As a full-stack developer and Linux expert, I have created my fair share of Discord bots for various purposes. One fun and useful bot is a birthday bot that can track user birthdays and send notifications on the server. In this comprehensive guide, I‘ll walk through how to create and customize a birthday bot from scratch using JavaScript and Node.js.

Overview of Capabilities

A birthday bot can provide the following key features:

  • Store user birthday information entered through commands
  • Create birthday roles that get automatically assigned on a user‘s birthday
  • Send customizable birthday messages in announcement channels
  • Display user birthday information through commands
  • Provide server stats on birthdays and upcoming birthdays

By the end of this guide, you‘ll have a personalized birthday bot ready for your community. Let‘s get started!

Setting Up the Bot Project

The first step is initializing a new Node.js project. This will be home base for our bot‘s code.

mkdir birthday-bot
cd birthday-bot
npm init -y

Next, we need to install some key dependencies. The discord.js library handles interfacing with the Discord API. dotenv secures our bot token in an environment variable. And mongodb connects us to a NoSQL database to store birthdays.

npm install discord.js dotenv mongodb

Inside the project, create a .env file with your bot token:

DISCORD_TOKEN=XXXXXXXXXXXXXXXXXX

Then create an index.js file that initializes our Discord client:

require(‘dotenv‘).config();
const { Client } = require(‘discord.js‘);

const client = new Client({
  intents: 32767,
});

client.on(‘ready‘, () => {
  console.log(‘Birthday bot ready!‘); 
});

client.login(process.env.DISCORD_TOKEN);

This connects to Discord using your token and logs a ready message.

Registering Bot Slash Commands

Our bot will use Discord‘s slash command system for its user interface. Let‘s register some initial commands.

Create a commands folder with an index.js file:

const { SlashCommandBuilder } = require(‘discord.js‘);

module.exports = {
    data: new SlashCommandBuilder()
        .setName(‘setbirthday‘)
        .setDescription(‘Set your birthday‘)
    // Additional commands here
}

Then back in our main index.js, register the commands globally:

const { REST } = require(‘@discordjs/rest‘);
const { Routes } = require(‘discord-api-types/v9‘); 
const commands = []; // populated array

const rest = new REST({ version: ‘9‘ }).setToken(process.env.DISCORD_TOKEN);

(async () => {
  try {
    console.log(‘Started refreshing application (/) commands.‘);

    await rest.put(Routes.applicationCommands(clientId), { body: commands });
    console.log(‘Successfully reloaded application (/) commands.‘);
  } catch (error) {
    console.error(error);
  }
})();

This will handle any updates to commands as we build out functionality.

Storing Birthday Data with MongoDB

For persistent birthday storage, we‘ll use MongoDB Atlas for a managed cloud database. Sign up for a free account, then create a shared cluster.

Install the mongoose ODM for interacting in Node:

npm install mongoose

Then in index.js, connect to the database:

const mongoose = require(‘mongoose‘);

mongoose.connect(process.env.MONGODB_URL)
  .then(() => {
    console.log(‘Connected to MongoDB‘);
  })
  .catch((err) => console.error(err));

Don‘t forget to add your MongoDB connection string to .env.

We need a Birthday model for data storage:

const birthdaySchema = new mongoose.Schema({
  userId: { 
    type: String,
    required: true
  },
  date: {
    type: String, 
    required: true
  } 
});

module.exports = mongoose.model(‘Birthday‘, birthdaySchema);

This schema has a userId field to link the birthday to a Discord user, and a date field that holds the birthday in ‘MM-DD‘ format.

Implementing Birthday Setup Command

With our backend set up, let‘s implement the /setbirthday command for users to add their birthdays.

In commands/index.js:

module.exports = {
  data: new SlashCommandBuilder()
        .setName(‘setbirthday‘)
        .setDescription(‘Set your birthday‘)
    .addStringOption(option =>
      option.setName(‘date‘)
        .setDescription(‘Your birthday date (MM-DD)‘)
        .setRequired(true)),

  async execute(interaction) {
    await interaction.deferReply();

    const birthday = await Birthday.findOne({
      userId: interaction.user.id  
    });

    if (birthday) {
      // Birthday exists, update it
    } else {
      // Create new birthday 
    }

    const newBirthday = new Birthday({
      userId: interaction.user.id,
      date: interaction.options.get(‘date‘).value  
    });

    await newBirthday.save();

    await interaction.editReply(`Got it! Saved your birthday as ${interaction.options.get(‘date‘).value}`);
  }  
}  

This handles querying for an existing birthday, updating if needed, and inserting a new document if none exists.

Don‘t forget to load the model:

const Birthday = require(‘../models/Birthday‘);  

Try running the bot and using /setbirthday! The birthday should save in MongoDB.

Adding a Birthday Role

To make birthdays more visible, we can automatically assign a special role on a user‘s birthday.

First, create your "Birthday Boy/Girl" role in server settings. Give this a fun color.

Then in index.js, create a background task:

const checkBirthdays = async () => {

  // Get current date 
  const now = new Date();  

  // Check all birthday documents
  const birthdays = await Birthday.find();

  birthdays.forEach(async bday => {

    // Parse saved birthday 
    const dob = new Date(bday.date);  

    // Check if today‘s date matches 
    if (dob.getDate() === now.getDate() && 
        dob.getMonth() === now.getMonth()) {

      // Fetch user 
      const user = client.users.cache.get(bday.userId);

      // Get member on server
      const member = guild.members.cache.get(user.id);

      // Add the birthday role  
      member.roles.add(birthdayRole); 

      // You can also send custom birthday messages here

    }
  });
}

// Check for birthdays every hour 
setInterval(checkBirthdays, 3600000);

Now try advancing your system clock to your birthday! You should get the role automatically.

Sending Birthday Messages

Let‘s make our bot even more festive by sending birthday messages in announcement channels.

Create a messages folder with specific templates:

// messages/birthday-general.js

module.exports = {
  content: `<@{{userId}}> just levelled up to {{age}}! <:birthday:987654321987654321>

Happy birthday!! Have an awesome day :)` 
}
// messages/birthday-milestone.js 

module.exports = {
  content: `Woo, <@{{userId}}> just turned the big {{age}}! 🎉🥳

Hope you have a wonderful birthday!`
}

Then in our checker:

// If birthday

// Get template
const general = require(‘../messages/birthday-general‘);

const message = general.content
  .replace(‘{{userId}}‘, bday.userId)
  .replace(‘{{age}}‘, getAge(bday.date)); 

// Send message   
client.channels.cache.get(channelId).send(message);

You can customize templates for different ages, roles, etc. The sky‘s the limit!

Displaying Server Birthday Stats

Let‘s round out our bot‘s capabilities by adding some commands to display birthday data.

First, a helper function to calculate age from a date:

function getAge(dateString) {

  let today = new Date();
  let birthDate = new Date(dateString);
  let age = today.getFullYear() - birthDate.getFullYear();

  // Adjust age when today‘s date occurs before birthday  
  let m = today.getMonth() - birthDate.getMonth();
  if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
      age--;
  }

  return age;
}

Then stats slash commands:

{
  data: new SlashCommandBuilder()
    .setName(‘serverbirthdays‘)
    .setDescription(‘View server birthday stats‘),

  async execute(interaction) {
    const birthdays = await Birthday.find();

    let ageGroups = {
      ‘< 18‘: 0, 
      ‘18-24‘: 0,
      ‘25-35‘: 0, 
      ‘36+‘: 0    
    };

    birthdays.forEach(bday => {
      const age = getAge(bday.date); 

      if (age < 18) {
        ageGroups[‘< 18‘]++;  
      } else if (age >= 18 && age <= 24) { 
        ageGroups[‘18-24‘]++;
      // And so on
    });

    await interaction.reply(`**Age Groups**\n${Object.entries(ageGroups)
      .map(group => `${group[0]}: ${group[1]}`).join(‘\n‘)}`);
  }
}

You can embellish these stats with graphs, break downs by month, and more!

Final Touches

Some finishing bits:

  • Add permission checks so only admins can access sensitive commands
  • Implement database backups
  • Set up command parsing and groups
  • Deploy the bot to a service like Heroku or AWS

And that‘s it – you now have a fully-featured custom birthday bot for your server! The possibilites are endless for creative integrations into your community.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *