Skip to content

Command mentions

Mention a slash command in your bot's messages, so a reader clicks it rather than retyping it. Covers the route keys, what an entry holds, why a command in two servers has no mention, when the list fills during startup, and context menu commands.

Discord turns </role remove:1287> into a clickable mention that runs the command. The number is the id Discord assigned when your bot deployed it, so writing that string by hand means fetching the id first.

Your bot already received that id at startup. You read it from Commands.

src/handlers/RoleAdd.ts
import {
    Commands,
    SlashHandler,
    SlashRoute
} from '@seedcord/gateway';

@SlashRoute('role/add')
export class RoleAdd extends SlashHandler<'role/add'> {
    public async execute(): Promise<void> {
        const member = this.options.getUser('member');
        const role = this.options.getRole('role');

        await this.reply(
            `Gave ${role.name} to ${member.username}. ` +
                `Undo with ${Commands['role/remove'].mention}.`
        );
    }
}

The keys are your routes

Commands is keyed by the routes seedcord codegen wrote, the same strings your handlers name. ping is a valid identifier so it takes a dot. role/add has a slash in it and needs brackets.

const ping = Commands.ping.mention; // '</ping:1234>'
const addRole = Commands['role/add'].mention; // '</role add:1287>'

</role add:1287> up there is the whole path with a space in it, which is the form Discord reads.

Typing the dot lists the routes that take one. These are the commands this guide uses in its examples.

Commands.
  • award
  • ban
  • feed
  • history
  • kick
  • leaderboard
  • maintenance
  • ping
  • probe
  • roles
  • search
  • whoami

What an entry holds

fieldwhat it holds
mentionThe clickable string, or plain /role add when the id did not resolve
idThe deployed command id. Absent for a command in two or more servers
routeThe full route, role/add
descriptionThe description on that route. A nested route takes it from the subcommand

Commands holds every route you registered, so a help command can read them all.

hover for typestap for types, arrow keys walk the tokens
const const listing: string[]listing = var Object: ObjectConstructorObject.
ObjectConstructor.values<CommandInfo>(o: {
    [s: string]: CommandInfo;
} | ArrayLike<CommandInfo>): CommandInfo[] (+1 overload)
values
(const Commands: InjectedCommandMapCommands).Array<CommandInfo>.map<string>(callbackfn: (value: CommandInfo, index: number, array: CommandInfo[]) => string, thisArg?: any): string[]map(
(command: CommandInfocommand) => `${command: CommandInfocommand.CommandInfo.mention: stringmention} ${command: CommandInfocommand.CommandInfo.description: stringdescription}` );

A command in two servers has no mention

Discord gives a guild command its own id in every server it deploys to, so no single id stands for it. id comes back undefined and mention falls back to plain text.

const id = Commands.ping.id;
const id: string | undefined

Your bot logs it once per command at startup.

ping is deployed to multiple guilds, falling back to plain text (not clickable).

A global command has one id everywhere, so it always gets a clickable mention.

Commands fills during startup

Your bot sends its commands to Discord after it connects, then Commands fills from what comes back. A read before that throws.

Commands.ping has no value yet. Commands fills during startup, and a read at the top of a file runs before that.

A ready event handler is early too, since the deploy runs after the client connects.

Tip

seedcord publishes a commandsDeployed bus event right after Commands fills. Subscribe to it to run something the moment your commands are live. It fires again every time a hot reload redeploys them.

Context menu commands

Discord doesn't give a right-click command any mention syntax, so ContextMenus carries the id and the name. The two kinds keep separate registries, the same way Discord scopes name uniqueness per kind.

import { ContextMenus } from '@seedcord/gateway';

const profile = ContextMenus.user['View Profile'].id;
const report = ContextMenus.message['Report Message'].id;

A context menu deployed to two or more servers comes back with id undefined, the same as a slash command.