One handler, several commands
One handler class can answer several slash commands and branch on the route that ran. Covers subcommands, the options every route shares, keeping both lists on the same routes, and the arm every route needs.
Two commands that share most of their work become two handler classes, plus a helper you import into both. Instead, just handle both in a single handler class. The decorator and the generic both take the list.
import { SlashHandler, SlashRoute } from '@seedcord/gateway';
@SlashRoute('ban', 'kick')
export class Moderation extends SlashHandler<'ban' | 'kick'> {
public async execute(): Promise<void> {
await this.match({
ban: async (options) => {
const reason = options.getString('reason');
await this.reply(`Banned. ${reason ?? ''}`);
},
kick: async () => {
await this.reply('Kicked.');
}
});
}
}Each arm receives that route's own options, so reason is there in the ban arm even though kick never declares it.
Subcommands work the same way
A route with a slash in it goes in the list like any other. One handler can answer every leaf of a command.
@SlashRoute('role/add', 'role/remove')
export class Role extends SlashHandler<'role/add' | 'role/remove'> {
public async execute(): Promise<void> {
await this.match({
'role/add': (options) =>
this.reply(`Gave ${options.getRole('role').name}.`),
'role/remove': (options) =>
this.reply(`Took ${options.getRole('role').name}.`)
});
}
}Options every route shares
this.options on the class holds the options every route in the list declares. ban and kick both take target, so it's there.
const target = this.options.getUser('target');An option only one route declares stays out.
@SlashRoute('ban', 'kick')
class Moderation extends SlashHandler<'ban' | 'kick'> {
public async execute(): Promise<void> {
const reason = this.options.getString('reason'); }
}reason belongs to ban alone, so you read it inside that arm.
Both lists must carry the same routes
Adding a route to one list and forgetting the other is a compile error on the decorator line. TypeScript opens that error on a generic line about the decorator, and the sentence you want sits a couple of lines further in:
SlashHandler declares a route that the SlashRoute decorator does not listMost seedcord type errors carry a plain sentence like that one somewhere in the type noise. Read past the first line and look for it.
@SlashRoute('ban')class Moderation extends SlashHandler<'ban' | 'kick'> {
public async execute(): Promise<void> {}
}Every route needs an arm
Your editor lists the routes still waiting for one.
await this.match({ '- ban
- kick
Leaving one out is a compile error.
@SlashRoute('ban', 'kick')
class Moderation extends SlashHandler<'ban' | 'kick'> {
public async execute(): Promise<void> {
await this.match({ ban: async () => {
await this.reply('Banned.');
}
});
}
}