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.