Skip to main content

Creating a basic command

In order to start registering commands you need to create a subdirectory called commands (lowercase!) in your entry point directory. If you were following Getting Started, it'll be src/commands.

Normally, you'd export one command per file to keep things organized. However, as we support various ways of exporting commands from the file (export class ..., export default class, module.exports = class ... and exports.MyCommand = class ...) you can export multiple commands from the same file.

info

Note that this doesn't create subcommands. We'll cover those in Plugin Subcommands

Create a ping.js file in your commands folder, which will send a message and then edit it with the elapsed time. Arguments and other features are covered in other pages.

Creating a command class

Let's start by creating the file for the command class. In your commands folder, create a file named ping.js (or ping.mjs / ping.ts if you're using ESM or TypeScript respectively). This command will send a message, and then edit it with the bot's WebSocket latency.

Your project directory should now look something like this:

โ”œโ”€โ”€ node_modules
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ src
โ”œโ”€โ”€ commands
โ”‚ โ””โ”€โ”€ ping.js
โ””โ”€โ”€ index.js

With the file created, we can start writing our commands by extending the Command class, from which all commands must be derived.

That said, here is an example of a ping command:

import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
}

Let's go over what is defined in the constructor in this code:

  • context: an object that contains file metadata required by the Piece class (which Command extends) in order to function.
  • name: by default, the name of the file without the extension, i.e. ping.js becomes ping, so there's no need to define it.
  • aliases: other ways users can call the command. You can have as many as you want!
  • description: some text that you can use to display when a "help" command is used.

There are many other properties available, all of which can be seen in the [CommandOptions] interface, but will also be explained in upcoming sections.

Creating the messageRun method

Commands have a messageRun method, which executes the command logic when it is invoked from a message. Define this below the command's constructor:

import { Command } from '@sapphire/framework';
import type { Message } from 'discord.js';

export class PingCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
// ...
}

public async messageRun(message: Message) {
const msg = await message.channel.send('Ping?');

const content = `Pong from JavaScript! Bot Latency ${Math.round(this.container.client.ws.ping)}ms. API Latency ${
msg.createdTimestamp - message.createdTimestamp
}ms.`;

return msg.edit(content);
}
}

Any discord.js code can be executed here since the Sapphire Framework is an extension of it. The command can be triggered with @bot ping or @bot pong (custom prefixes are mentioned in other documents).

Resulting code

Once you've set up the constructor and the messageRun method, your code should look like this:

import { Command } from '@sapphire/framework';
import type { Message } from 'discord.js';

export class PingCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}

public async messageRun(message: Message) {
const msg = await message.channel.send('Ping?');

const content = `Pong from JavaScript! Bot Latency ${Math.round(this.container.client.ws.ping)}ms. API Latency ${
msg.createdTimestamp - message.createdTimestamp
}ms.`;

return msg.edit(content);
}
}