Security News
Node.js EOL Versions CVE Dubbed the "Worst CVE of the Year" by Security Experts
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
discordbot-framework
Advanced tools
Welcome to the wonderful world of Discord bots. Through creating my own Discord bot, I have put together this basic framework to enable the process.
The purpose of this project is to make it so that anyone can spin up their own simple Discord bot fairly easily. Simply follow the below instructions and you'll be on the way to your own Discord bot in no time.
This project provides a basic wrapper around functionality presented by discord.js project.
The first step to get started is to load the scaffolding.
const DiscordBot = require('discordbot-framework');
let bot = new DiscordBot();
Configuration for the bot is provided in the form of a basic object.
How you decide to get the object is at your discretion.
const config = {
'secret_key' : 'my_discord_provided_secret_key'
};
bot.configure(config);
The only option that is required for configuration is secret_key
but the full list of possible options is as follows:
Configuration Key | Data Type | Default Value | Note |
---|---|---|---|
secret_key | integer | (none) | The client secret key/token provided on the Discord Developer page. The bot will fail to boot without this. |
command_prefix | string | '!' | The prefix used for commands, e.g. !syn |
allowed_channels | array | [ ] | The channels that the bot is allowed to respond in; an empty array means all channels. |
respond_to_bots | boolean | false | Whether or not the bot is allowed to respond to other bots. |
playing_msg | string | false | The "Playing" message for the bot, if false will skip this feature. |
boot_msg | string | "Connected!" | This is just the message that shows up on the command line when you boot the bot up, only really shown to the person starting the bot. |
enable_cmds | boolean | true | Setting this to false will tell the bot to skip adding the commands handler; useful if you want to have a non-command based bot. |
Note: If there is no default value, the framework will throw an Error if one isn't specified
We can add event listeners to the bot.
bot.observe('message', (msg) => console.log(`${msg.author.username} sent a message in #${msg.channel.name}`));
The observe function takes a string for the first parameter, where the string is one of the events defined by the discordjs Client
. The second parameter is the callback to fire when the event is triggered.
Note: You can refer to discord.js's Client API documentation here for the supported events
Two event listeners are added automatically as part of the framework; one for 'ready'
as it's required for discord.js
to start, and the other for message
which runs all of the message handlers (including commands, provided you don't disable them).
As event listeners can be added multiple times for the same event, these two event listeners should not affect the code you write for the bot.
We can also add commands to the bot.
bot.bind('syn', {
'callback' : (msg) => msg.channel.sendMessage('Ack!'),
'help_message' : 'Is the bot listening? \n\tUsage: `!syn`',
'allow_dm' : true
});
The bind function takes in two parameters.
syn
=> !syn
).Parameter | Data Type | Default Value | Note |
---|---|---|---|
callback | function | (none) | The function to call when the command is called. |
rate_limit | integer | 3 | The number of times per minute the command can be called by a user. |
allow_dm | boolean | false | Whether or not the bot will respond to this command if it's in a direct message |
help_message | string | "[undocumented]" | The help message for this command. |
Note: If there is no default value, the system will Error if one isn't specified
The callback registered for a command is passed two parameters. The first parameter is a reference to the Message object generated by the DiscordJS message
event. The second parameter is a reference to the framework instance itself which allows your command to interact with data stored as part of the instance (such as the task scheduler or Client).
I've found convenience in writing my callbacks into their own module and then importing from there. This gives the callback a complete closure to work with.
const {isup} = require('./commands/status.js');
bot.bind('isup', {
'callback' : isup,
'help_message' : "Check if a server is online.\n\tUsage: `!isup <webpage>`"
});
The system makes no implications for what you can do with the help message parameter, which is why it's optional.
There is a command on the bot framework getCmdHelp()
that will return an array that used in an Embed
message
Here is an example implementation:
bot.bind('help', {
'help_message' : 'This message.\n\tUsage: `!help`',
'callback' : (msg, bot) => {
let help = bot.getCmdHelp();
msg.author.sendEmbed({
'color' : 0x229922,
'title' : "My Bot's Help",
'fields' : help,
'timestamp' : new Date()
});
},
'allow_dm' : true
});
We can write custom handlers for messages to perform actions that aren't related to commands.
There are two methods related to this.
addHandler
will create a new one and expects a name for the handler as well as the handler itself. It also supports passing in a DI object/context parameter.removeHandler
can be used to delete a handler for a given name.The first parameter of a handler will always be a Message object from the Discordjs message
Client event. The second parameter will be the DI object/context.
Here's a basic example...
let counter = 0;
bot.addHandler('counter', (msg) => {
counter += 1;
console.log(`I have received ${counter} messages so far!`);
});
bot.removeHandler('counter');
And here's an example where we use the context object. Instead of passing in a function directly, we want to pass an object with two keys, callback
and context
. The callback defined will receive too parameters. The first parameter is the message object itself, the second parameter is the context provided.
const customHandler = require('./my-handler.js');
const DB = require('./my-db.js');
bot.addHandler('myCustomHandler', {
'callback': customHandler,
'context' : DB
});
In the above example, my-handler.js
exports a function that starts like...
module.exports = (msg, context) => {
// ...
And here's one more example that is a modification of the counter example from earlier to use the context parameter. I create a simple object to store my simple counter in and pass that as my context, rather than relying on the function closure.
let counter = 0;
bot.addHandler('counter', {
'callback' : (msg, ctx) => {
ctx.counter += 1;
console.log(`I have seen ${ctx.counter} messages so far!`);
},
'context' : { counter }
});
We can schedule functions to run at specific times.
This is convenient if we want something to happen on a specific schedule.
bot.schedule({
'name' : 'server-list',
'frequency' : 'hourly',
'callback' : (instance) => {
let servers = instance.getGuilds().reduce((list, guild) => { list.push(guild.name + "|" + guild.id); return list; }, []);
console.log('I am connected to the following servers: ' + servers.join(', '));
}
});
By default, the parameter sent in to the callback is a reference to the framework itself, but this can be specified as one of the parameters as seen in the below (fairly useless) example.
// Create some data we want to send in
let days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
bot.schedule({
'name' : 'hourly-notice',
'frequency' : 'hourly',
'start_of' : 'hour',
'context' : {bot, days}, // Short hand object notation, we still want to send the bot instance, but we want to also send in the data we created
'callback' : (context) => {
context.bot.getGuilds().first().defaultChannel.sendMessage(`Hello! I am only available on the following days: ${context.days.join(', ')}`);
}
});
Parameter | Data Type | Default Value | Note |
---|---|---|---|
name | String | (none) | (Required) The name of the task for scheduling purposes. Names must be unique. |
frequency | String | (none) | (Required) The timeframe for which to fire the event; see the supported schedules table below. |
callback | function | (none) | (Required) The callback to trigger on the schedule. |
begin_at | String/momentjs Timestamp | now | A timestamp at which point to start this task; can be string or momentjs instance. |
start_of | String | (none) | This is used to jump your task the start of the next schedule. e.g., hour means start of next hour, start at 3:44 -> 4:00 -> 5:00 . If omitted, it will just schedule for the next increment. e.g., 3:44 -> 4:44 -> 5:44 . |
context | Any | Framework | This is the value that will be passed into the callback parameter, the default is the instance of the framework but this would allow you to pass in anything. |
immediate | Boolean | false | This will fire the function once before scheduling it |
once | Boolean | false | Whether or not to reschedule the task after it has run the first time (not including the immediate run, so once + immediate = two executions) |
The following frequencies are defined as within the limitations of NodeJS's setTimeout
/ setInterval
maximum supported delay.
Frequency | Definition |
---|---|
deciminute | Every ten seconds* |
minute | Every minute |
hourly | Every hour |
daily | Every day |
weekly | Every 7 days |
biweekly | Every 14 days |
* deciminute
was created for testing, but the option was left because there's probably a use case for it. Highly, highly, highly recommend AGAINST hitting the Discord API every ten seconds.
The following start_of
options are supported.
start_of Options |
---|
year |
month |
quarter |
week |
isoweek |
day |
date |
hour |
minute |
second |
This is handled using the momentjs
startOf
function. For examples of what specifically these options mean, see the MomentJS documentation regarding the function.
Now that our bot is configured, has it's listeners, and commands added, we can start up the bot.
bot.connect();
And if everything went according to plan, your bot should log in to Discord successfully.
Here is a working example bot that was set up using the framework.
// Load the module
const DiscordBot = require('discordbot-framework');
let bot = new DiscordBot();
// Get the configuration
// Please never ever commit your secret key to a git repository
// See DotEnv in the references
const configData = {
'secret_key' : 'thisisreallynotasecrettoken',
'command_prefix' : '@',
'respond_to_bots' : true,
'boot_msg' : 'I have connected!',
'playing_msg' : 'I am a bot!'
};
// Load the configuration into the bot
bot.configure(configData);
// Add a command
bot.bind('syn', {
'callback' : msg => msg.channel.sendMessage('Ack!'),
'rate_limit': 1,
'allow_dm' : true
});
// Add a listener
bot.observe('guildMemberAdd', (guildMember) => {
const nickname = guildMember.nickname || guildMember.user.username;
guildMember.guild.defaultChannel.sendMessage(`Welcome to the ${guildMember.guild.name} party, ${nickname}!`);
});
// Add an event to the schedule
bot.schedule({
'name' : 'server-list',
'frequency' : 'hourly',
'callback' : (instance) => {
let servers = instance.getGuilds().reduce((list, guild) => { list.push(guild.name + "|" + guild.id); return list; }, []);
console.log('I am connected to the following servers: ' + servers.join(', '));
}
});
bot.bind('help', {
'help_message' : 'This message.\n\tUsage: `!help`',
'callback' : (msg, bot) => {
let help = bot.getCmdHelp();
msg.author.sendEmbed({
'color' : 0x229922,
'title' : "My Bot's Help",
'fields' : help,
'timestamp' : new Date()
});
},
'allow_dm' : true
});
let counter = 0;
bot.addHandler('counter', (msg) => {
counter += 1;
console.log(`I have seen ${counter} messages so far!`);
});
// Tell the bot to connect to Discord
bot.connect();
Link | Description |
---|---|
Discord.js Documentation | Main repository for documentation on the Discord.js API |
momentJS | The library used in the task scheduler |
NodeJS DotEnv | Project designed for the purpose of loading configurations into projects |
discord.js
to 11.2.1
uws@0.14.5
(optional requirement for discord.js@11.2.1
), this should solve issues related to the bot randomly disconnecting.uws
.playing_msg
and boot_msg
allow_dm
setting wasn't respectedFAQs
A simple framework for creating Discord bots
We found that discordbot-framework demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Security News
cURL and Go security teams are publicly rejecting CVSS as flawed for assessing vulnerabilities and are calling for more accurate, context-aware approaches.
Security News
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.