📜 Commands
Slash Commands are one of the primary ways to use your Discord Bot. They allow users to interact with your bot by typing /
followed by the command name, maybe some options, and hitting enter.
Building Slash Commands is streamlined with Robo.js, which also handles automatic registration.
Creating Slash Commands
Create a file in your project's src/commands
folder named after your Slash Command.
- Javascript
- Typescript
export default (interaction) => {
interaction.reply('Pong!')
}
import type { ChatInputCommandInteraction } from 'discord.js'
import type { CommandResult } from 'robo.js'
export default (interaction: ChatInputCommandInteraction): CommandResult => {
interaction.reply('Pong!')
}
That's it! Your /ping
command is fully set up—give it a try!
Don't see your new command? Try restarting your Discord Client to refresh your cache.
Alternatively, you can also return your reply directly.
- Javascript
- Typescript
export default async () => {
return 'Pong!'
}
import type { CommandResult } from 'robo.js'
export default async (): Promise<CommandResult> => {
return 'Pong!'
}
This kicks in Sage Mode—a hidden power that handles interactions and defers slow async functions!
Subcommands and Subcommand Groups
Wanna go deeper? Make a folder inside src/commands
with the parent command name and its own files.
- Javascript
- Typescript
export default async (interaction) => {
const everyoneRole = interaction.guild.roles.everyone
await interaction.channel.permissionOverwrites.edit(everyoneRole, {
SendMessages: false,
AddReactions: false
})
return 'This channel is now locked!'
}
import type { ChatInputCommandInteraction } from 'discord.js'
import type { CommandResult } from 'robo.js'
export default async (interaction: ChatInputCommandInteraction): Promise<CommandResult> => {
const everyoneRole = interaction.guild.roles.everyone
await interaction.channel.permissionOverwrites.edit(everyoneRole, {
SendMessages: false,
AddReactions: false
})
return 'This channel is now locked!'
}
We can go even deeper with Subcommand Groups. Just add another folder inside the parent command folder.
- Javascript
- Typescript
import { client } from 'robo.js'
export default async (interaction) => {
await client.user.setPresence({
status: 'idle'
})
return "I'm now idle!"
}
import { client } from 'robo.js'
import type { ChatInputCommandInteraction } from 'discord.js'
import type { CommandResult } from 'robo.js'
export default async (interaction: ChatInputCommandInteraction): Promise<CommandResult> => {
await client.user.setPresence({
status: 'idle'
})
return "I'm now idle!"
}
Pretty cool, right? Literally just folders and files!
src
└── commands
├── channel
│ └── lock.js
└── bot
└── status
└── idle.js
Customizing Commands
Give your commands some context with descriptions. You can do this by exporting a config
object from your command file.
export const config = {
description: 'Responds with Pong!'
}
For TypeScript users, you can add typings for both the config
object and the command result.
import type { CommandConfig, CommandResult } from 'robo.js'
export const config: CommandConfig = {
description: 'Responds with Pong!'
}
export default (): CommandResult => {
return 'Pong!'
}
The `` object also lets you customize stuff like locale translations, Sage options, and command timeouts. To understand more about the available uration options, check out the configuration section.
Command Options
Robo.js allows you to further customize your commands with options. You can define these options in your config
object and then access their values in your command function.
- Javascript
- Typescript
export const config = {
description: 'Responds with Pong!',
options: [
{
name: 'loud',
description: 'Respond loudly?',
type: 'boolean'
}
]
}
export default (interaction) => {
const loud = interaction.options.get('loud')?.value as boolean
return loud ? 'PONG!!!' : 'Pong!'
}
import type { CommandConfig, CommandResult } from 'robo.js'
import type { CommandInteraction } from 'discord.js'
export const config: CommandConfig = {
description: 'Responds with Pong!',
options: [
{
name: 'loud',
description: 'Respond loudly?',
type: 'boolean'
}
]
}
export default (interaction: CommandInteraction): CommandResult => {
const loud = interaction.options.get('loud')?.value as boolean
return loud ? 'PONG!!!' : 'Pong!'
}
You can also use a second parameter next to the interaction object to access the options directly. These are automatically parsed and passed to your command function, with full type support too!
- Javascript
- Typescript
export const config = {
description: 'Responds with Pong!',
options: [
{
name: 'loud',
description: 'Respond loudly?',
type: 'boolean'
}
]
}
export default (interaction, options) => {
return options.loud ? 'PONG!!!' : 'Pong!'
}
import { createCommandConfig } from 'robo.js'
import type { CommandOptions, CommandResult } from 'robo.js'
import type { CommandInteraction } from 'discord.js'
export const config = createCommandConfig({
description: 'Responds with Pong!',
options: [
{
name: 'loud',
description: 'Respond loudly?',
type: 'boolean'
}
]
} as const)
export default (interaction: CommandInteraction, options: CommandOptions<typeof config>): CommandResult => {
return options.loud ? 'PONG!!!' : 'Pong!'
}
Heads up!
createCommandConfig
andas const
are important for TypeScript!createCommandConfig
creates a command configuration object with the correct type, which tells your editor which options are available for your command for better autocompletion and type checking.
Want to explore more options? Check the configuration section.
DM Permission
Control whether your command is accessible in direct messages with dmPermission
. Setting this to true
allows users to use the command in DMs with the bot, while false
restricts it.
export const config = {
// ... other configuration options
dmPermission: false // Restricts this command in DMs
}
Default Member Permissions
Use defaultMemberPermissions
to define server-based permissions for your command. This field accepts PermissionFlagsBits
from Discord.js, allowing you to specify which roles or permissions are needed to access the command in a server context.
import { PermissionFlagsBits } from 'discord.js'
export const config = {
// ... other configuration options
defaultMemberPermissions: PermissionFlagsBits.KickMembers // Only users who can kick members can use this command
}
Remember, server admins can adjust these default permissions for their own servers. Also, due to a Discord quirk, default permissions might not apply as expected to subcommands.
Autocomplete
Autocomplete can take your commands to the next level by providing suggestions as users type. You can implement autocomplete by exporting an autocomplete
function in your command file.
- Javascript
- Typescript
export const config = {
description: 'Chooses a color',
options: [
{
name: 'color',
description: 'Your favorite color',
type: 'string',
autocomplete: true
}
]
}
const colors = ['red', 'green', 'blue', 'yellow', 'black', 'white', 'pink', 'purple', 'brown']
export default (interaction) => {
return `You chose ${interaction.options.get('color')?.value}`
}
export const autocomplete = (interaction) => {
const colorQuery = interaction.options.get('color')?.value
const filtered = colors.filter((color) => color.startsWith(colorQuery))
return filtered.map((colors) => ({ name: colors, value: colors }))
}
import type { CommandConfig, CommandResult } from 'robo.js'
import type { CommandInteraction, AutocompleteInteraction } from 'discord.js'
export const config: CommandConfig = {
description: 'Chooses a color',
options: [
{
name: 'color',
description: 'Your favorite color',
type: 'string',
autocomplete: true
}
]
}
const colors: Array<string> = ['red', 'green', 'blue', 'yellow', 'black', 'white', 'pink', 'purple', 'brown']
export default (interaction: CommandInteraction): CommandResult => {
return `You chose ${interaction.options.get('color')?.value}`
}
export const autocomplete = (interaction: AutocompleteInteraction) => {
const colorQuery = interaction.options.get('color')?.value as string
const filtered = colors.filter((color) => color.startsWith(colorQuery))
return filtered.map((colors) => ({ name: colors, value: colors }))
};
In this example, the autocomplete
function returns an array of colors that start with the user's input, providing a dynamic and responsive user experience.
Note: the type of the Interaction is: AutocompleteInteraction
Command Registration
The cherry on top? You don't need to manually register your commands. Robo.js handles it for you when you run robo dev
or robo build
, automatically! However, if things go sideways for some reason, you can use the --force
flag to force registration.
npx robo build --force
This will also clean up any commands that are no longer in your commands
directory. Pretty neat, right?
User Installs
Robo.js now supports commands for user-installed apps! You will need to set experimental userInstall
to true
in your config file to enable this feature.
export default {
// ... other config options
experimental: {
userInstall: true
}
}
With this enabled, users can install your app and use its commands anywhere!
Make sure you update your install settings in the Discord Developer Portal to allow user installs.