Discord.js Command Registry

This is a data structure that lets you define Discord.js slash commands, register them with Discord's API, and route Discord.js Interaction events to your handler for that command.
Currently Discord.js separates slash command creation into three different, weirdly disjoined processes. They want you to: which is only used to construct the data of an HTTP call. which uses an entirely separate library that directly relies on the Discord API which still requires you to write your own router and juggle your own handlers
This library simplifies this process by letting you do this all in one place.
It also provides some bridging functionality to support additional option types:
- Application
- Emoji
Usage
Defining commands with the SlashCommandRegistry
This library adds a new builder SlashCommandRegistry
that serves as the
entry point for defining all of your commands. Existing builders from
@discordjs/builders
still work as you expect, but there's a new function added to all of them:
.setHandler(handler)
. The handler is a callback function that expects a
Discord.js Interaction
instance. The SlashCommandRegistry
will figure out
which handler to call based on the received Interaction
.const {
ApplicationCommandType,
SlashCommandRegistry,
// Can also import these directly, but you don't need them
// ContextMenuCommandBuilder,
// SlashCommandBuilder,
// SlashCommandSubcommandBuilder,
// SlashCommandSubcommandGroupBuilder,
} = require('discord-command-registry');
const commands = new SlashCommandRegistry()
.addDefaultHandler(interaction => interaction.reply("I can't do this yet"))
.addCommand(command => command
.setName('ping')
.setDescription('Ping pong command')
.setHandler(interaction => interaction.reply('pong'))
)
.addCommand(command => command
.setName('info')
.setDescription('Gets some info on something')
.addSubCommand(sub => sub
.setName('all')
.setDescription('Gets all the info ever created')
.setHandler(interaction => interaction.reply('All things'))
)
.addSubcommand(sub => sub
.setname('user')
.setDescription('Gets info for a user')
.addUserOption(opt => opt
.setName('user')
.setDescription('The user whose info to list')
.setHandler(interaction => interaction.reply('User info'))
)
)
)
.addContextMenuCommand(command => command
.setName('select')
.setType(ApplicationCommandType.Message)
.setHandler(interaction => interaction.reply('selected a message'))
);
Registering commands with Discord
TheSlashCommandRegistry
can register commands with Discord's API with a
single function call.commands.registerCommands({
application_id: 'your bot client ID',
token: 'your bot token here',
guild: 'a guild ID', // If provided, commands are registered for this guild.
// If omitted, commands are registered globally.
})
.then(res => console.log('Successfully registered', res))
.catch(err => console.error('Something went wrong', err));
Ok cool, but what if you need more control? You also can restrict this to register only a subset of commands.
await commands.registerCommands({
application_id: 'your bot client ID',
token: 'your bot token here',
commands: ['ping'],
});
await commands.registerCommands({
application_id: 'your bot client ID',
token: 'your bot token here',
guild: 'some guild ID',
commands: ['info']
});
You can also store the
application_id
and token
in the registry to avoid
repeating it:commands
.setApplicationID('your bot client ID')
.setToken('your bot token here');
commands.registerCommands({ commands: ['ping'] });
Executing commands
You can pipe Discord.js interaction events directly into aSlashCommandRegistry
's execute()
method.const Discord = require('discord.js');
const { SlashCommandRegistry } = require('discord-command-registry');
const client = new Discord.Client({...});
const commands = new SlashCommandRegistry();
// Additional setup omitted for brevity...
client.on(Discord.Constants.Events.INTERACTION_CREATE, (interaction) => {
commands.execute(interaction)
.then(result => console.log('Command returned this', result))
.catch(err => console.error('Command failed', err));
});
This library does not do anything with the
Interaction
object other than route
it to the appropriate handler function. It's up to you to extract relevant data
(such as options) from the Interaction
.Which handler gets called?
I added a handler to a subcommand, the group that subcommand belongs to, the command that group belongs to, and to the registry itself. Which one actually gets used when I execute an interaction?The
SlashCommandRegistry
picks the most specific handler it can find,
according to this priority list:- Subcommand
- Subcommand Group
- Top-level Command
- Registry's default
In other words, if your command and subcommand both have a handler, only the subcommand's handler will be called. Using a lower-priority handler can give you some flexibility if you have many commands that all use similar code.
Additional option types
Discord (and Discord.js) does not currently support command options for things like Applications. This library provides functions to approximate these additional option types:getApplication(interaction, option_name, required=false)
getEmoji(interaction, option_name, required=false)
To use these, register the option as a string option, then use the
Options.getX(...)
helpers to retrieve the value.For example, this is a functional example of an Application option:
const {
Options,
SlashCommandRegistry,
} = require('discord-command-registry');
const commands = new SlashCommandRegistry()
.addCommand(command => command
.addName('mycmd')
.addDescription('Example command that has an application option')
// Add your application option as a string option
.addStringOption(option => option
.setName('app')
.setDescription('An application ID')
)
.setHandler(async (interaction) => {
// Use this function to resolve that string option into an application.
// NOTE this makes an HTTP call and so returns a promise.
const app = await Options.getApplication(interaction, 'app');
return interaction.reply(`Application name: ${app.name}`);
});
);
Other stuff from @discordjs/builders
The Discord.js builders package has a lot of neat
helper functions. The
command registry passes all of these functions through, so they can be included
directly (preventing the need to add / import @discordjs/builders
).const { bold, hyperlink, time } = require('discord-command-registry');
Dependencies
This library is built using the following libraries. You will, of course, need Node and Discord.js, but you don't need any of the others. This library downloads these dependencies for you, and you interact with them through this library.- Node 16.6.0 (or above)
- discord.js 13.x
- @discordjs/builders
- @discordjs/rest 0.1.0-canary.0
- discord-api-types v9
License
Copyright 2021 MimickalThis code is licensed under the LGPL-3.0 license.
Basically, you are free to use this library in your closed source projects, but any modifications to this library must be made open source.