Skip to content

Latest commit

 

History

History
256 lines (236 loc) · 26.3 KB

docs.md

File metadata and controls

256 lines (236 loc) · 26.3 KB

Contents

How the prefab works

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 the src/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.\

Command handling

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.\

Command properties

WIP

Command arguments

WIP

Examples

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' }.\

Others

Pagination

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.

Global black-/whitelisting

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.

getReply()

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.`)
    }
}

Misc.

randomRange(min, max)

const random = randomRange(10, 20)
console.log(random)

delay(ms)

await delay(ms)