The main entry point is src/main.js
. In there we first add a few new properties to our client, like client.commands
. Before we login, we loop over the src/commands
folder and add all the commands to the collection (Collection<string, Command>
). Additionally, when adding a new command, the registerCommands()
makes sure that:
- the command has a name, execute, etc.
- a command with the same name/alias hasn't been added yet
For the event handling: It is similar to
registerCommands()
,registerEvents()
will loop over thesrc/events
folder and bind all the exported methods to the corresponding event. (Both methods work perfectly fine if you have your files inside subfolders and both also support multiple directories to be loaded simultaneously, e.g.:registerCommands(client, '../commands', '../evenMoreCommands');
).\
For better IntelliCode completions, all client properties, command properties, collection types, etc. have been defined in the typings.d.js
file.\
The command handling all takes part in the message event (src/eventHandlers/message.js
). In there, we check whether or not the user wants to use a command. We then get the command from the client.commands
collection and start checking for all the command properties like needed user permissions, cooldowns, command arguments, etc. (including all feature-related checks like per server command permissions, cooldowns, ... )
All the possible command properties are documented in the prefab.js
file which is a copy-paste template to easily add new commands.\
WIP
WIP
E.g.: If you are making a command that requires the user to pass in a number, simply add
arguments: [
{
type: 'NUMBER',
prompt: 'Please enter a valid number',
id: 'num'
}
]
to your command and the function will take care of the rest. If the user didn't provide a number, it will send a message as defined in the prompt
and won't execute the command. If they do send a valid number, it will continue. The order of the arguments will be kept, that is:
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "Test command to demonstrate the arguments property.",
arguments: [
{
type: 'NUMBER',
prompt: 'Please enter a valid number.',
id: 'nums'
amount: 2
},
{
type: 'SOMETHING',
prompt: 'Please enter an operation',
words: ['add', 'sub', 'div', 'mul'],
id: 'op'
}
],
execute: async function({ client, message, flags }) {
console.log(flags)
}
}
If they do test 1 2 add
, it will log { nums: [ 1, 2 ], op: 'add' }.
\
First, require the function paginate
defined in src/utils/utils.js
. The function requires two parameters: message
and embeds
.
message
: just the message object (to send the paginated message, get the author to listen to the reactions to, etc.).
embeds
: an array of embed objects. Yes, a MessageEmbed is a plain old JavaScript object.
The function also has an optional parameter optional
: optional = { time: maxTimeInMs }
default time: 30000ms
The embeds
parameter from the paginate
function expects an array of embeds. The order does matter: The function will first send the first embed of the array and then switch between them depending on the reaction.
Now, a few examples on how you could create embeds and use the paginate
function.
const { paginate, CustomEmbed } = require('../utils/utils')
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "test command to demonstrate the pagination function",
execute: async function({ client, message }) {
const array = []
for (let i = 0; i < 10; i++) {
const embed = new CustomEmbed({ client: client, userID: message.author.id })
.setTitle('Test Embed')
.setTimestamp()
.setDescription(i)
array.push(embed)
}
paginate(message, array, { time: 15000 })
}
}
On a side note, if you try something similar yourself and do anything like:
const { paginate, CustomEmbed } = require('../utils/utils')
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "test command to demonstrate the pagination function",
execute: async function({ client, message }) {
const embed = new CustomEmbed({ client: client, userID: message.author.id })
.setTitle('Test Embed')
.setTimestamp()
const array = []
for (let i = 0; i < 10; i++) {
embed.setDescription(i)
array.push(embed)
}
paginate(message, array, { time: 15000 })
}
}
That will not work: All embeds inside array
will be the same because they are deep copies of embed
, which means, if you change embed
, it will change all existing copies of the object.
Another example:
const { paginate, CustomEmbed } = require('../utils/utils')
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "test command to demonstrate the pagination function",
execute: async function({ client, message }) {
const array = []
const embed1 = new CustomEmbed({ client: client, userID: message.author.id })
.setTitle('Test Embed')
.setTimestamp()
.setDescription('some text')
array.push(embed1)
const embed2 = new CustomEmbed({ client: client, userID: message.author.id })
.setTitle('Test Embed')
.setTimestamp()
.setDescription('some more meaningful text')
array.push(embed2)
const embed3 = new CustomEmbed({ client: client, userID: message.author.id })
.setTitle('Test Embed')
.setTimestamp()
.setDescription('even more very creative and meaningful text')
array.push(embed3)
paginate(message, array)
}
}
That isn't very pretty code, but it works.
A blacklisted user will not be able to use any command from your bot and will be totally ignored.
To blacklist someone, simply require the functions blacklist, whitelist
defined in src/utils/utils.js
.
const { blacklist } = require('../../utils/utils')
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "Test command to demonstrate blacklisting.",
arguments: [
{
type: 'MEMBER',
notBot: true,
notSelf: true,
prompt: 'Who do you want to blacklist?',
id: 'user'
}
],
execute: async function({ client, message, flags }) {
blacklist(client, flags.user.id)
}
}
It's that easy.
First, require the function getReply
defined in src/utils/utils.js
. The function requires one parameters: message
.
message
: just the message object (to listen to messages, get the author to listen to the messages to, etc.).
The function also has an optional parameter optional
:
{
time: number, // max time in ms, default: 30000
user: user, // the user to listen to messages to, default: message.author
words: string[] // array with strings of accepted words, default: [] (all words will be accepted)
regexp: RegExp // the message content should match this RegExp to be accepted
}
This function helps you await a users reply. Let's say you have a quiz command and want to see what the user replied, you could use this function to reduce duplicate code:
const { getReply } = require('../../utils/utils')
/**
* @type {import('../typings.d').Command}
*/
module.exports = {
name: "test",
description: "Test command to demonstrate \`getReply()\`",
execute: async function({ client, message }) {
message.channel.send('Are you sure?')
const reply = await getReply(message, { time: 10000, words: ['yes', 'y', 'n', 'no'] })
if (['yes', 'y'].includes(reply.content.toLowerCase())) message.channel.send(`${message.author.username} is sure about that.`)
else message.channel.send(`${message.author.username} isn't so sure about that.`)
}
}
randomRange(min, max)
const random = randomRange(10, 20)
console.log(random)
delay(ms)
await delay(ms)