Just this month, Discord has released their Slash Commands and Interactions API, which introduces inline commands with suggestion, validation, and everything else you need built in.
a
a
Talk about a UX improvement.
Included is the ability to handle all incoming commands without a running gateway bot. This is awesome! I wanted to start working with this right away. Problem is: none of the big Discord bot frameworks (Discord.js and others) handle these commands (yet, as of this writing on 3/26/2021).
So I wrote my own!
Here’s an example Gouge (like a slash, but deeper) bot with a single command, bagel. This command has two arguments:
- kind, for which kind of bagel to order. It should only ever be one of these: Plain, Blueberry, Poppy, Cinnamon.
- amount, an integer for the amount of bagels to order.
import { GougeClient, command } from 'gouge' const client = new GougeClient({ id: process.env.CLIENT_ID, key: process.env.PUBLIC_KEY, secret: process.env.CLIENT_SECRET, token: process.env.BOT_TOKEN, }) client.with( command('bagel', 'Order a number of bagels') .string('kind', 'The kind of bagel to order', true, [ 'Plain', 'Blueberry', 'Poppy', 'Cinnamon', ]) .integer('amount', 'The amount of bagels to order', true) .handler(async (client, respond, [kind, amount]) => { await respond( 'You ordered ' + amount + ' ' + kind + ' bagels.' ) }) ) client.start(3000)
Just this code does the following:
- Registers the bagel command with Discord’s Interactions API. This makes the command show up in the list when a user types a forward slash in the chat box.
- Starts up an express server that listens on port 3000 for incoming Discord webhooks.
- When it receives an interaction that matches up with one of its commands, it runs that handler.
A call from a user like this:
/bagel kind:Plain amount:34
Is responded to with:
You ordered 34 Plain bagels.
Slick!
But probably the most ergonomic feature is that a command’s handler arguments are typed. Take this example command, with three arguments of types User, Channel, and Role.
a
That array [user, channel, role]? It’s type is [IUser, IChannel, IRole], determined automatically by the preceding arguments.
a
a
a
I did this with some pretty hideous Typescript cajoling in the background, but it’s all hidden from the developer, with the result being a perfectly type-safe handler-writing experience.
(Also: if an argument is labeled as optional, it’ll type to being | undefined in the handler!)
There’s also support for all the other features that writing a slash command bot might need
- Subcommands
- Subcommand Groups
- Auto-deleting global commands on restart, if they aren’t defined in your code any longer.
- Optional raw-handling of interactions without a registered handler, for rolling your own method.
There’s also a pretty neat auto-generating docs, and a work in progress wiki that’ll contain tutorials for coders old and new (new especially - in my experience, Discord Bot is a very common first-timer coding project). This is my first real shot at building an open source library for wider use, I want to make sure the developer experience is at least on par with the high-quality mainline stuff I’ve used like Discord.js.
The whole thing is a major work in progress, but I’m hoping to at least get it to a level of stability and testing that I can use it in production for one of my own discord bots.
