Prefix Commands
Text-based command routing with configurable prefix, arguments, and cooldowns.
Prefix commands respond to messages starting with a configurable prefix (default !), using file-based routing in src/prefixCommands/.
File structure
Prefix commands support up to 3 levels of nesting. The separator between tokens is a space (e.g., !admin ban).
Handler signature
The default export receives the Discord.js Message and a PrefixCommandArgs object. Return a string, MessageReplyOptions, or void.
import type { PrefixCommandArgs } from '@robojs/discordjs'
import type { Message } from 'discord.js'
export default (message: Message, args: PrefixCommandArgs) => {
return 'Pong!'
}export default (message, args) => {
return 'Pong!'
}The return type (PrefixCommandResult) can be:
string-- replied as a text message viamessage.reply()MessageReplyOptions-- replied with embeds, components, etc.void-- you handle the reply yourself
Arguments
The args parameter provides parsed arguments from the message content.
Prop
Type
Command config
Export a config object to configure the command. All fields are optional.
Prop
Type
import type { PrefixCommandConfig, PrefixCommandArgs } from '@robojs/discordjs'
import type { Message } from 'discord.js'
export const config: PrefixCommandConfig = {
description: 'Ban a user from the server',
args: [
{ name: 'user', description: 'The user to ban', required: true },
{ name: 'reason', description: 'Reason for the ban' }
],
aliases: ['b'],
cooldown: 10000,
requiredPermissions: 'BanMembers'
}
export default (message: Message, args: PrefixCommandArgs) => {
return `Banned ${args.params.user} for: ${args.params.reason ?? 'No reason provided'}`
}export const config = {
description: 'Ban a user from the server',
args: [
{ name: 'user', description: 'The user to ban', required: true },
{ name: 'reason', description: 'Reason for the ban' }
],
aliases: ['b'],
cooldown: 10000,
requiredPermissions: 'BanMembers'
}
export default (message, args) => {
return `Banned ${args.params.user} for: ${args.params.reason ?? 'No reason provided'}`
}Named arguments
Define named arguments with config.args to map positional tokens to named parameters.
Prop
Type
When a required argument is missing, the handler auto-replies with the missing argument names and a usage message showing <required> and [optional] placeholders:
Missing required argument(s): user
Usage: `ban <user> [reason]`import type { PrefixCommandConfig, PrefixCommandArgs } from '@robojs/discordjs'
import type { Message } from 'discord.js'
export const config: PrefixCommandConfig = {
args: [
{ name: 'user', required: true },
{ name: 'reason' }
]
}
export default (message: Message, args: PrefixCommandArgs) => {
// If "user" is missing, the handler never runs -- an auto-reply is sent instead
return `Kicked ${args.params.user}`
}export const config = {
args: [
{ name: 'user', required: true },
{ name: 'reason' }
]
}
export default (message, args) => {
return `Kicked ${args.params.user}`
}Quoted strings
Both double-quoted and single-quoted strings in arguments are preserved as single tokens. For example:
!greet "John Doe" helloProduces:
args.raw // ['John Doe', 'hello']
args.params // { name: 'John Doe', greeting: 'hello' } (if args are configured)
args.content // '"John Doe" hello'Aliases
Use config.aliases to define alternative names for a command. Alias resolution is case-insensitive by default.
import type { PrefixCommandConfig } from '@robojs/discordjs'
import type { Message } from 'discord.js'
export const config: PrefixCommandConfig = {
description: 'Show help information',
aliases: ['h', 'info', '?']
}
export default (message: Message) => {
return 'Here is the help information!'
}export const config = {
description: 'Show help information',
aliases: ['h', 'info', '?']
}
export default (message) => {
return 'Here is the help information!'
}Cooldowns
Cooldowns are tracked per guild + user + command combination. The cooldown timer only starts after successful execution -- if the handler throws an error, no cooldown is applied.
When a user triggers a command while on cooldown, they receive an automatic reply:
Please wait Xs before using this command again.export const config = {
cooldown: 10000 // 10 seconds between uses
}Permissions
Use requiredPermissions to restrict a command to users with specific Discord permissions. Accepts a single permission string or an array.
When a user lacks the required permissions, the command auto-replies with the list of missing permissions.
import type { PrefixCommandConfig, PrefixCommandArgs } from '@robojs/discordjs'
import type { Message } from 'discord.js'
export const config: PrefixCommandConfig = {
requiredPermissions: ['ManageMessages', 'ManageChannels']
}
export default (message: Message, args: PrefixCommandArgs) => {
return 'Messages purged!'
}export const config = {
requiredPermissions: ['ManageMessages', 'ManageChannels']
}
export default (message, args) => {
return 'Messages purged!'
}DM support
Prefix commands are allowed in DMs by default. Set dmPermission: false to silently ignore DM messages for a command.
export const config = {
dmPermission: false // this command only works in servers
}Subcommands
Nested directories create subcommands. The router uses longest-match-first resolution, checking up to 3 space-separated tokens.
When a user types !mod ban @user, the router first tries to match mod/ban, then falls back to mod if no match is found. This means you can have both a base command and subcommands for the same prefix.
Sage mode
Prefix commands have their own Sage mode, separate from slash command Sage. Export config.sage to configure it.
Prop
Type
Unlike slash command Sage, prefix command Sage does not auto-defer interactions. Instead, when typing is enabled, it calls message.channel.sendTyping() to show a typing indicator. Error replies are only shown in development mode when errorReplies is true.
Set sage: false to disable both typing and error replies:
export const config = {
sage: false
}Plugin configuration
Configure prefix command behavior globally in your plugin config.
Prop
Type
import type { DiscordConfig } from '@robojs/discordjs'
export default {
prefix: {
value: '?',
caseSensitive: false,
ignoreBots: true
}
} satisfies DiscordConfigexport default {
prefix: {
value: '?',
caseSensitive: false,
ignoreBots: true
}
}Dynamic prefix
Use a function to return different prefixes per guild. The function receives the guild ID (or null for DMs).
import { Flashcore } from 'robo.js'
import type { DiscordConfig } from '@robojs/discordjs'
export default {
prefix: {
value: async (guildId: string | null) => {
if (!guildId) return '!'
return (await Flashcore.get<string>(`prefix:${guildId}`)) ?? '!'
}
}
} satisfies DiscordConfigimport { Flashcore } from 'robo.js'
export default {
prefix: {
value: async (guildId) => {
if (!guildId) return '!'
return (await Flashcore.get(`prefix:${guildId}`)) ?? '!'
}
}
}Bot mention as prefix
Allow users to trigger commands by mentioning the bot instead of typing a prefix:
export default {
prefix: {
mentionAsPrefix: true
}
}With this enabled, both !ping and @Bot ping will trigger the ping command.
Intent requirements
Prefix commands require the GuildMessages, DirectMessages, and MessageContent gateway intents. MessageContent is a privileged intent that must be enabled in the Discord Developer Portal. These intents are auto-inferred at build time when prefix command files are detected.
See Intents & permissions for details on intent configuration.
Middleware integration
Prefix commands pass through the same middleware chain as slash commands. In middleware, the record.type for prefix commands is 'discordjs:prefixCommands'.
import type { MiddlewareData } from '@robojs/discordjs'
export default (data: MiddlewareData) => {
if (data.record.type === 'discordjs:prefixCommands') {
console.log(`Prefix command: ${data.record.key}`)
}
}export default (data) => {
if (data.record.type === 'discordjs:prefixCommands') {
console.log(`Prefix command: ${data.record.key}`)
}
}See Middleware for more on how the middleware chain works.
