This guide explains how to create a basic addon for our bots. Addons allow users to extend the bot's functionality without modifying the main codebase.
What is an Addon?
An addon is a file or group of files inside the addons/ folder that can:
Listen to events: React to things happening in the bot (e.g., when a message is sent or a member joins).
Add slash commands: Create commands that users can type into Discord starting with /.
You can choose to use only events, only commands, or both in an addon. It's up to you!
Addon File Requirements
Event Listener Files:
These files can be named anything (e.g., myEvents.js).
Use event listeners to react to bot activities, like a user sending a message.
Slash Command Files:
These files must start with cmd_ (e.g., cmd_exampleCommand.js).
Use slash commands to create commands users can trigger with /.
How the Addon System Works
Event Handlers:
All events (like messageCreate or guildMemberAdd) are centralized in the bot using something called an eventHandler.
You can use on to listen for these events in your addon.
Slash Command Handlers:
If you include a file that starts with cmd_, the bot will automatically load it as a slash command.
Step 1: Creating an Event Listener (Optional)
To listen to events, create a new file in the addons/ folder. The file name can be anything, like myEventAddon.js.
Example: Listening to messageCreate
Here’s how you can listen for messages and respond when someone says !hello:
module.exports.register= ({ on }) => {on('messageCreate', (message) => {if (message.content ==='hello') {message.channel.send('Hello! This message is from my custom addon.'); } });};
Step 2: Creating a Slash Command (Optional)
If you want to create a slash command, the file must start with cmd_, for example: cmd_greet.js.
Example: A Simple Slash Command
Here’s how you can create a /greet command:
const { SlashCommandBuilder } =require('@discordjs/builders');module.exports= { data:newSlashCommandBuilder().setName('greet').setDescription('Sends a greeting message.'),asyncexecute(interaction, client) {awaitinteraction.reply('Hello! This is a slash command from my addon.'); },};
Step 3: Combining Events and Commands
You can create both an event listener and a slash command in the same addon. Just use a separate file for each.
module.exports.register= ({ on }) => {on('messageCreate', (message) => {if (message.content ==='!ping') {message.channel.send('Pong! This message is from the event listener in my addon.'); } });on('guildMemberAdd', (member) => {console.log(`${member.user.tag} joined the server.`); });};
Example: cmd_greet.js
const { SlashCommandBuilder } =require('@discordjs/builders');module.exports= { data:newSlashCommandBuilder().setName('greet').setDescription('Sends a greeting message.'),asyncexecute(interaction, client) {awaitinteraction.reply('Hello from the /greet command!'); },};
Step 4: Loading Your Addon
Place your addon files in the addons/ folder.
The bot will automatically detect and load event listener files and slash commands when it starts.
Advanced Example: Fully Featured Addon
Here’s an example addon that listens for events and includes a slash command:
This file contains event listeners for various bot events.
const { ActionRowBuilder,ButtonBuilder,EmbedBuilder } =require('discord.js');module.exports.register= ({ on }) => {// Example: Respond to a specific messageon('messageCreate', (message) => {if (message.content ==='!info') {constembed=newEmbedBuilder().setColor('#0099ff').setTitle('Bot Information').setDescription('This is an example addon showcasing embeds and buttons.').setFooter({ text:'Example Addon', iconURL:'https://example.com/icon.png' });constrow=newActionRowBuilder().addComponents(newButtonBuilder().setCustomId('info_button').setLabel('Click Me!').setStyle('Primary') );message.channel.send({ embeds: [embed], components: [row] }); } });// Example: Handle a button clickon('interactionCreate',async (interaction) => {if (!interaction.isButton()) return;if (interaction.customId ==='info_button') {awaitinteraction.reply({ content:'You clicked the button! This response is from the example addon.', ephemeral:true, }); } });// Example: Log when the bot is readyon('ready', () => {console.log('The advanced example addon is loaded and ready!'); });};
File: cmd_advanced.js
This file contains a slash command definition and its execution logic.
Integrating MongoDB with your addon is simple and allows you to store and retrieve data from the same database used by the main bot. This guide will show you how to set up a MongoDB model in your addon and use it effectively.
Step 1: Create a MongoDB Model
Add your MongoDB models in the same folder as your addon. For example, you can create a file called exampleModel.js inside your addon folder.
File: exampleModel.js
Here’s an example model for storing example data:
constmongoose=require('mongoose');// Define the schemaconstExampleSchema=newmongoose.Schema({ userId: { type: String, required:true },// The user's ID points: { type: Number, default:0 },// Example: Points the user has createdAt: { type: Date, default:Date.now },// When the entry was created});// Export the modelmodule.exports=mongoose.model('Example', ExampleSchema);
Step 2: Use the Model in Your Addon
To use the model in your addon, import it just like you would in any other Node.js project. You can now interact with the database using MongoDB methods.
Here’s an example slash command to interact with the database:
const { SlashCommandBuilder } =require('@discordjs/builders');constExampleModel=require('./exampleModel'); // Import the modelmodule.exports= { data:newSlashCommandBuilder().setName('setpoints').setDescription('Set points for a user.').addUserOption(option =>option.setName('target').setDescription('The user to set points for').setRequired(true)).addIntegerOption(option =>option.setName('points').setDescription('The number of points to set').setRequired(true)),asyncexecute(interaction) {consttargetUser=interaction.options.getUser('target');constpoints=interaction.options.getInteger('points');if (points <0) {returninteraction.reply('Points must be a non-negative number.'); }// Update or create the user in the databaselet user =awaitExampleModel.findOne({ userId:targetUser.id });if (!user) { user =newExampleModel({ userId:targetUser.id, points }); } else {user.points = points; }awaituser.save(); // Save the changesinteraction.reply(`${targetUser.username} now has ${user.points} points.`); },};
Full Example Addon with MongoDB integration
File Structure
/addons/ mongodbExample/ events.js <-- Handles bot events cmd_example.js <-- Handles a slash command exampleModel.js <-- MongoDB model
constExampleModel=require('./exampleModel');module.exports.register= ({ on }) => {on('messageCreate',async (message) => {if (message.content.startsWith('!addpoints')) {constuserId=message.author.id;constpointsToAdd=parseInt(message.content.split(' ')[1]) ||0;let user =awaitExampleModel.findOne({ userId });if (!user) user =newExampleModel({ userId, points: pointsToAdd });elseuser.points += pointsToAdd;awaituser.save();message.channel.send(`${message.author.username} now has ${user.points} points!`); }if (message.content.startsWith('!checkpoints')) {constuserId=message.author.id;constuser=awaitExampleModel.findOne({ userId });message.channel.send(user ?`You have ${user.points} points.`:'You have no points yet!'); } });};
cmd_example.js:
const { SlashCommandBuilder } =require('@discordjs/builders');constExampleModel=require('./exampleModel');module.exports= { data:newSlashCommandBuilder().setName('setpoints').setDescription('Set points for a user.').addUserOption(option =>option.setName('target').setDescription('The user to set points for').setRequired(true)).addIntegerOption(option =>option.setName('points').setDescription('The number of points to set').setRequired(true)),asyncexecute(interaction) {consttargetUser=interaction.options.getUser('target');constpoints=interaction.options.getInteger('points');let user =awaitExampleModel.findOne({ userId:targetUser.id });if (!user) user =newExampleModel({ userId:targetUser.id, points });elseuser.points = points;awaituser.save();interaction.reply(`${targetUser.username} now has ${user.points} points.`); },};