LogoRobo.js

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

ping.ts!ping
ban.ts!ban
ban.ts!admin ban
kick.ts!admin kick

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.

src/prefixCommands/ping.ts
import type { PrefixCommandArgs } from '@robojs/discordjs'
import type { Message } from 'discord.js'

export default (message: Message, args: PrefixCommandArgs) => {
	return 'Pong!'
}
src/prefixCommands/ping.js
export default (message, args) => {
	return 'Pong!'
}

The return type (PrefixCommandResult) can be:

  • string -- replied as a text message via message.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

src/prefixCommands/ban.ts
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'}`
}
src/prefixCommands/ban.js
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]`
src/prefixCommands/kick.ts
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}`
}
src/prefixCommands/kick.js
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" hello

Produces:

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.

src/prefixCommands/help.ts
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!'
}
src/prefixCommands/help.js
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.

src/prefixCommands/purge.ts
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!'
}
src/prefixCommands/purge.js
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.

mod.ts!mod
ban.ts!mod ban
kick.ts!mod kick
set.ts!mod config set

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

config/plugins/robojs/discordjs.ts
import type { DiscordConfig } from '@robojs/discordjs'

export default {
	prefix: {
		value: '?',
		caseSensitive: false,
		ignoreBots: true
	}
} satisfies DiscordConfig
config/plugins/robojs/discordjs.mjs
export 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).

config/plugins/robojs/discordjs.ts
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 DiscordConfig
config/plugins/robojs/discordjs.mjs
import { 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'.

src/middleware/logger.ts
import type { MiddlewareData } from '@robojs/discordjs'

export default (data: MiddlewareData) => {
	if (data.record.type === 'discordjs:prefixCommands') {
		console.log(`Prefix command: ${data.record.key}`)
	}
}
src/middleware/logger.js
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.

Next steps

On this page