Skip to main content

Getting Started

Introduction

Subcommands are a way to split 1 command into multiple. This can in particular be very useful for configuration commands with subcommands such as set, reset and remove.

Originally for message commands, the plugin now supports Discord's native Subcommands and Subcommand Groups interface for Chat Input Commands (Slash Commands).

Installation

npm install @sapphire/plugin-subcommands @sapphire/framework @sapphire/utilities discord.js@14.x

Message Command Usage

const { Subcommand } = require('@sapphire/plugin-subcommands');

// Extend `Subcommand` instead of `Command`
class UserCommand extends Subcommand {
constructor(context, options) {
super(context, {
...options,
name: 'vip',
subcommands: [
{
name: 'list',
messageRun: 'messageList',
default: true
},
{
name: 'add',
messageRun: 'messageAdd'
},
{
name: 'remove',
messageRun: 'messageRemove'
}
]
});
}

async messageList(message, args) {}

async messageAdd(message, args) {}

async messageRemove(message, args) {}
}
module.exports = {
UserCommand
};

In this example command, "vip list" will run messageList, "vip add" will run messageAdd, and "vip remove" will run messageRemove. You can map the value of messageRun to the name of any method in the class, but we recommend you use a naming convention to keep things organized. Our preferred convention is prefixing the method name with the type of command (messageAdd, chatInputAdd, and contextMenuAdd for Message Commands, Chat Input Commands, and Context Menu Commands respectively).

Including default: true will run that subcommand when no subcommand is provided. In this case, invoking the command with "vip" will run messageList just like "vip list".

Chat Input Command (Slash Command) Usage

warning

To use subcommands with Chat Input Commands (Slash Commands), we recommend you learn how to register and use regular Chat Input Commands first in order to understand registerApplicationCommands(). Please read Registering Chat Input Commands for details.

const { Subcommand } = require('@sapphire/plugin-subcommands');

// Extend `Subcommand` instead of `Command`
class UserCommand extends Subcommand {
constructor(context, options) {
super(context, {
...options,
name: 'vip',
subcommands: [
{
name: 'list',
chatInputRun: 'chatInputList'
},
{
name: 'add',
chatInputRun: 'chatInputAdd'
},
{
name: 'remove',
chatInputRun: 'chatInputRemove'
}
]
});
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder
.setName('vip')
.setDescription('Vip command') // Needed even though base command isn't displayed to end user
.addSubcommand((command) => command.setName('list').setDescription('List vips'))
.addSubcommand((command) =>
command
.setName('add')
.setDescription('Add a vip')
.addUserOption((option) =>
option.setName('user').setDescription('user to add to vip list').setRequired(true)
)
)
.addSubcommand((command) =>
command
.setName('remove')
.setDescription('Remove a vip')
.addUserOption((option) =>
option.setName('user').setDescription('user to remove from vip list').setRequired(true)
)
)
);
}

async chatInputList(interaction) {}

async chatInputAdd(interaction) {}

async chatInputRemove(interaction) {}
}
module.exports = {
UserCommand
};

The code above registers three slash commands: /vip list, /vip add, and /vip remove. These run the respective class methods mapped in the subcommands array.

info

When using subcommand or subcommand groups for application commands, Discord will make your base command unusable. In the example above, this is why Sapphire does not register a /vip command for you, and also why providing a default option will not do anything for Chat Input Subcommands.

Mixing Message Command and Chat Input Command Subcommands

You can use both kinds of subcommands at the same time if needed. This may be useful in situations such as migrating Message Command subcommands to Chat Input Command subcommands over time.

const { Subcommand } = require('@sapphire/plugin-subcommands');

// Extend `Subcommand` instead of `Command`
class UserCommand extends Subcommand {
constructor(context, options) {
super(context, {
...options,
name: 'vip',
subcommands: [
{
name: 'list',
messageRun: 'messageList',
default: true,
chatInputRun: 'chatInputList'
},
{
name: 'add',
messageRun: 'messageAdd',
chatInputRun: 'chatInputAdd'
},
{
name: 'remove',
messageRun: 'messageRemove',
chatInputRun: 'chatInputRemove'
}
]
});
} // Register Application Commands and implement methods below...
}
module.exports = {
UserCommand
};

Keep in mind that default is only functional for Message Command Subcommands as mentioned in the previous section.