/ping in the tour. Now write a command of your own, /dice, which rolls a die. It is four lines of logic, and it is a chance to understand each part of a command file: what it declares, what run receives, and what happens when something is wrong.
The file
Createsrc/commands/dice.ts:
src/commands/dice.ts
/dice in your test server. It is there, with its description under its name, and it answers.
Line by line
import { command } from 'chapterjs'. The function that makes a command. It takes an object describing the command and returns something the framework recognises as one.
export default. The file hands its command to the framework by exporting it. The framework loads every file of src/ and keeps what they export made with command(), event(), button()…: a default export, or a named one (export const dice = command(...)), it does not matter. What is exported and is none of these is yours.
description. The text Discord shows under the name in the / menu, 1 to 100 characters. Besides run, it is the only thing a command needs. (A later chapter moves it to the language files.)
async run({ interaction, user }). The function that runs when someone uses the command. It receives one object, and you take out of it what you need: here interaction, the use of the command, and user, the person who used it. The next pages go through everything else that object holds.
interaction.reply(...). The answer. Discord shows it under the command, with the name of the person who used it.
await. Sending the answer is a request to Discord, which takes time. await waits for it, and lets the framework know when your function is really done. Always await what you ask Discord to do: an error in a request you did not await is reported without the line of your code that caused it.
The command has to answer
Discord waits for an answer to every use of a command, and tells the person “The application did not respond” when none comes. Try it: comment out thereply line, save, and use /dice.
What is checked before Discord sees it
Discord has rules for commands: a name is lowercase, with letters, digits,- and _ only, 32 characters at most; a description is 1 to 100 characters; and so on. Breaking one would make Discord refuse the whole list of your commands. So the framework checks each file when it loads, and explains in your terms.
Change name to 'Dice' and save:
description and save:
command() only accepts what exists.
Why slash commands and not messages
An older kind of bot reads every message and reacts to the ones that start with!, like !dice. ChapterJS does not do that for commands, and Discord discourages it, for good reasons:
- A slash command is discoverable: people type
/and see what the bot can do, with descriptions and options, without reading a help page. - Discord validates what people type before sending it to the bot: a missing option or a wrong value never reaches your code.
- Reading every message needs the Message Content Intent, a privileged one, while slash commands need nothing.
- Discord shows a command only to the people who have the permissions it asks for.
Recap
- A command is
command({ name, description, run }), exported from any file ofsrc/. runreceives one object; takeinteraction,userand the rest out of it.await interaction.reply(...)answers; a command that does not answer is a warning with the file.- Every rule of Discord is checked when the file loads, with a message saying what to write.
Options
Let people choose the number of sides, and receive it typed.
Guide: Commands
Subcommands, permissions, private messages, in short.