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.
Note that this doesn't create subcommands. We'll cover those in Plugin Subcommands
Note that by default Sapphire does not load the listeners required for processing message commands. You will need to
construct your SapphireClient
with the loadMessageCommandListeners
option set to true
. For more information see
the SapphireClientOptions#loadMessageCommandListeners
interface.
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:
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class PingCommand extends Command {
constructor(context, options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
}
module.exports = {
PingCommand
};
import { Command } from '@sapphire/framework';
export class PingCommand extends Command {
constructor(context, options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
}
import { Command } from '@sapphire/framework';
export class PingCommand extends Command {
public constructor(context: Command.LoaderContext, 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 thePiece
class (whichCommand
extends) in order to function.name
: by default, the name of the file without the extension, i.e.ping.js
becomesping
, 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
If you want to use message commands in your Sapphire bot, you have to make sure you meet the following prerequisites:
- Enable the
Message Content Intent
underPrivileged Gateway Intents
on the Discord Developer Portal for your application. - In your Sapphire client options, set the
intents
property to include the intentsGatewayIntentBits.GuildMessages
,GatewayIntentBits.Guilds
, andGatewayIntentBits.MessageContent
. - In your Sapphire client options set
loadMessageCommandListeners
totrue
.
All in all your code should look something like this:
- CommonJS
- ESM
- TypeScript
const { SapphireClient } = require('@sapphire/framework');
const { GatewayIntentBits } = require('discord.js');
const client = new SapphireClient({
intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
loadMessageCommandListeners: true
});
import { SapphireClient } from '@sapphire/framework';
import { GatewayIntentBits } from 'discord.js';
const client = new SapphireClient({
intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
loadMessageCommandListeners: true
});
import { SapphireClient } from '@sapphire/framework';
import { GatewayIntentBits } from 'discord.js';
const client = new SapphireClient({
intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
loadMessageCommandListeners: true
});
Commands have a messageRun
method, which executes the command logic when it is invoked from a message. Define this
below the command's constructor:
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class PingCommand extends Command {
constructor(context, options) {
// ...
}
async messageRun(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);
}
}
module.exports = {
PingCommand
};
import { Command } from '@sapphire/framework';
export class PingCommand extends Command {
constructor(context, options) {
// ...
}
async messageRun(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);
}
}
import { Command } from '@sapphire/framework';
import type { Message } from 'discord.js';
export class PingCommand extends Command {
public constructor(context: Command.LoaderContext, 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:
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class PingCommand extends Command {
constructor(context, options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
async messageRun(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);
}
}
module.exports = {
PingCommand
};
import { Command } from '@sapphire/framework';
export class PingCommand extends Command {
constructor(context, options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
async messageRun(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);
}
}
import { Command } from '@sapphire/framework';
import type { Message } from 'discord.js';
export class PingCommand extends Command {
public constructor(context: Command.LoaderContext, 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);
}
}
Creating subcommands
For handling subcommands, please refer to the Sapphire Plugin Subcommands documentation.