4.6 KiB
sidebar_position
| sidebar_position |
|---|
| 12 |
Slash Commands API
The Commands API lets plugins register / slash commands. When a user types / in the chat composer, Toju shows a Discord-style autocomplete menu of available commands. Selecting a command (click, Enter, or Tab) runs it — either immediately when it declares no options, or after the user types the requested arguments.
Required Capabilities
| Method | Capability |
|---|---|
commands.register(id, command) |
ui.commands |
commands.list() |
ui.commands |
Every registration returns a disposable. Push it into context.subscriptions so the command is removed when the plugin unloads.
Command Scope
A command's scope controls where it appears:
| Scope | Available in |
|---|---|
global (default) |
Chat servers and direct messages |
server |
Only while a chat server is the active surface |
Use global for commands that work without a server context (e.g. /help, /shrug). Use server for commands that act on the current server, channel, or members.
Options and Argument Parsing
Declare options to describe the arguments a command accepts. Toju parses what the user typed after the command name and passes the result to run as context.args, keyed by option name.
interface PluginApiSlashCommandOption {
description?: string;
name: string;
required?: boolean;
// 'rest' captures all remaining text; otherwise a single whitespace-delimited token
type?: 'string' | 'number' | 'boolean' | 'rest';
}
- Positional options are filled left-to-right from whitespace-delimited tokens.
- A
restoption captures all remaining text verbatim (use it last, for free-form text). - Missing positional values are passed as empty strings.
- The autocomplete menu shows required options as
<name>and optional ones as[name].
Values arrive as strings; convert number/boolean types yourself inside run.
Command Context
run receives a context that extends the standard action context (source: 'slashCommand') with the invocation details:
interface PluginApiSlashCommandContext extends PluginApiActionContext {
args: Record<string, string>; // parsed values keyed by option name
command: string; // invoked name without the leading slash
rawArgs: string; // raw text typed after the command name
// inherited: server, textChannel, voiceChannel, user, source
}
Register a Command
export function activate(context) {
const api = context.api;
// Server-scoped command with a free-form message argument.
context.subscriptions.push(
api.commands.register('announce', {
name: 'announce',
description: 'Post an announcement to the current channel',
icon: '📢',
scope: 'server',
options: [{ name: 'message', type: 'rest', required: true }],
run: (slash) => {
api.messages.send(`📢 ${slash.args.message}`, slash.textChannel?.id);
}
})
);
// Global command that works in servers and DMs.
context.subscriptions.push(
api.commands.register('shrug', {
name: 'shrug',
description: 'Append the shrug emoticon',
scope: 'global',
run: () => api.messages.send('¯\\_(ツ)_/¯')
})
);
}
api.messages.send requires the messages.send capability, so the example above declares both ui.commands and messages.send in its manifest.
List Registered Commands
const allCommands = context.api.commands.list();
Returns every slash command currently registered across all active plugins, including their scope and options.
Built-in Commands
Toju ships first-party commands that are always available without any plugin, such as /lenny (posts ( ͡° ͜ʖ ͡°)). They appear in the same autocomplete menu tagged as Built-in. Plugin commands are listed alongside them; if a plugin registers a command with the same name as a built-in, both appear and the user can pick either.
How Input Is Handled
- Typing
/opens the menu; typing more characters filters by command name (prefix matches rank first). - Picking a command without options runs it immediately and clears the composer.
- Picking a command with options fills
/nameso the user can type arguments, thenEnterruns it. - Slash input is intercepted and never posted as a chat message. Text that starts with
/but matches no registered command falls through and is sent as a normal message.