Skip to content

Creating your first command

Gerald McAlister edited this page Jan 30, 2023 · 7 revisions

Creating the Command Itself

In this tutorial, we will be creating an add command for the bot. This command will take in two variables (a and b), add these numbers together, and then output the result. You can see the final sample code as a commit here.

Step 1: Create the Command File

To begin, let's create a file called add.ts in the commands folder. This is where we will create the command itself. Open up this file, and let's add the following code:

import {
    IDiscordRequestData,
    IDiscordResponseData,
} from 'discord-bot-cdk-construct';
import { ApplicationCommandOptionType } from 'slash-commands';
import { CustomCommand, SlashCommand } from './command-list';

These are the various imports that we will need. The first 2 imports are required for handling inputs and outputs to our command. The 3rd import is required to tell Discord what type of inputs we want for our command. The last two imports are how we will structure our commands to tell the bot what our command is and what it should do.

Step 2: Define our Command

Let's begin by creating our commands definition so that Discord understands how our command should work. Go ahead and add the following bit of code to your file:

const addCommandInfo: SlashCommand = {
    name: 'add',
    description: 'Adds two numbers together.',
    options: [
        {
            name: 'a',
            description: 'The first number to add',
            type: ApplicationCommandOptionType.INTEGER,
            required: true,
        },
        {
            name: 'b',
            description: 'The second number to add',
            type: ApplicationCommandOptionType.INTEGER,
            required: true,
        },
    ],
};

This structure tells Discord everything it needs to know about our command. Specifically, it's name, a description of it, and the options users must give to it to run it. Note that variables a and b must be integers, and are required for the command to function. While Discord should do its best to sanitize the inputs to the command, we will still want to validate the inputs, as you'll see in this next section.

Step 3: Implement our Command

This next step is the meat of our command: the implementation! It's a fairly straightforward command, with most of the code just being to sanitize the inputs a bit more:

/**
 * A simple add command, just responds with the addition of two given numbers.
 * @param {IDiscordRequestData} requestData The incoming request data.
 * @return {IDiscordResponseData} Returns the result of the inputs added if given.
 */
function addCommand(requestData?: IDiscordRequestData): IDiscordResponseData {
    let message: string = 'No numbers found to add!';
    // Make sure we were given options.
    if (requestData?.options) {
        // Parse out the values and add them.
        const a: number = Number(requestData?.options[0].value);
        const b: number = Number(requestData?.options[1].value);
        const result: number = a + b;
        message = `${a} + ${b} = ${result}`;
    }
    // Return our message finally.
    return {
        tts: false,
        content: message,
        embeds: [],
        allowedMentions: [],
    };
}

The code here is pretty simple: We parse out a and b as number types if we have options given, then add them and store it in a result constant of type number. Finally, we update our response message and return it! The response structured is based on Discord's interaction callback data structure here. The Discord Bot CDK Construct library has this structured defined here if you want to see the additional inputs.

Step 4: Exporting the Command

Exporting the command is simple: Just add the following bit to the end of your add.ts file:

export const AddCommand: CustomCommand = {
    slashCommand: addCommandInfo,
    callback: addCommand,
};

Then open up command-list.ts and update the list of commands to look like so:

export const Commands: CustomCommand[] = [
    HelloCommand,
    PokeCommand,
    WhoAreYouCommand,
    AddCommand,
];

At this stage, your command is all hooked up! Now you can run the command locally with the following:

# Build first so your code is compiled!
npm run build
# Now test the command
npm run command '{"name": "add", "options": [{"name": "a", "value": "1"}, {"name": "b", "value": "1"}]}'

You should see something like this as your output: image

Step 5: Unit Testing the Command

At this stage, we now should add a quick set of unit tests to validate our code! Go ahead and create a file called add.test.ts in the test/commands folder. Add the following code to it:

import { AddCommand } from '../../src/commands/add';

describe('Test add command', () => {
    test('Command info test', () => {
        expect(AddCommand.slashCommand.name).toEqual('add');
    });

    test('Callback test - Valid', () => {
        const response = AddCommand.callback({
            id: '0',
            name: 'add',
            options: [
                {
                    name: 'a',
                    value: '2',
                },
                {
                    name: 'b',
                    value: '3',
                },
            ],
        });
        expect(response).toEqual({
            tts: false,
            content: `2 + 3 = 5`,
            embeds: [],
            allowedMentions: [],
        });
    });

    test('Callback test - Invalid', () => {
        const response = AddCommand.callback({
            id: '0',
            name: 'add',
        });
        expect(response).toEqual({
            tts: false,
            content: 'No numbers found to add!',
            embeds: [],
            allowedMentions: [],
        });
    });
});

There are 3 tests in this as you can see:

  • The first test simply validates the command's definition.
  • The second test passes in the values 2 and 3 for a and b respectively, and confirms we are able to add the numbers together.
  • The final test passes in no values, and confirms we display an error message when this occurs.

There are of course other tests that should be done here (such as what if we only pass in a value for a but not b), and the additional tests are left up to the reader to implement 😉