Slash Commands
Slash Commands are Discord's modern command system. They provide a native UI, autocomplete, permissions, and work on all platforms.
What are Slash Commands?
Unlike prefix commands (like !help), slash commands:
- Show a native Discord UI with descriptions
- Support typed arguments (strings, numbers, users, etc.)
- Provide autocomplete suggestions
- Have built-in permission controls
- Work on mobile and desktop
Creating Slash Commands
Basic Command
bot.slash('hello', 'Say hello to someone') do |cmd|
# Define options
cmd.string('name', 'The person to greet', required: true)
# Handle the command
cmd.handler do |interaction|
name = interaction.option('name')
interaction.respond(content: "Hello, #{name}!")
end
end
Command with Multiple Options
bot.slash('ban', 'Ban a user from the server') do |cmd|
cmd.user('user', 'User to ban', required: true)
cmd.string('reason', 'Reason for ban')
cmd.integer('days', 'Days of messages to delete', min_value: 0, max_value: 7)
# Set default permissions
cmd.default_permissions(:ban_members)
cmd.handler do |interaction|
user = interaction.option('user')
reason = interaction.option('reason') || 'No reason provided'
days = interaction.option('days') || 0
# Ban the user
bot.create_guild_ban(
interaction.guild_id,
user.id,
delete_message_days: days,
reason: reason
)
interaction.respond(
content: "Banned #{user.username}",
ephemeral: true # Only visible to command user
)
end
end
Option Types
DiscordRDA supports all Discord option types:
String
cmd.string('message', 'The message to send',
required: true,
min_length: 1,
max_length: 2000
)
Integer
cmd.integer('age', 'Your age',
min_value: 13,
max_value: 120
)
Number (Decimal)
cmd.number('price', 'Item price',
min_value: 0.01,
max_value: 999.99
)
Boolean
cmd.boolean('ephemeral', 'Show response only to you?')
User
cmd.user('target', 'User to target', required: true)
Channel
cmd.channel('destination', 'Target channel',
channel_types: [0, 5] # Text and news channels only
)
Role
cmd.role('rank', 'Role to assign')
Mentionable
cmd.mentionable('who', 'User or role to mention')
Attachment
cmd.attachment('file', 'File to upload')
Command Options
Choices (Predefined Options)
cmd.string('color', 'Pick a color',
choices: [
{ name: 'Red', value: '#FF0000' },
{ name: 'Green', value: '#00FF00' },
{ name: 'Blue', value: '#0000FF' }
]
)
Optional Options
cmd.string('note', 'Optional note', required: false)
Required Options
cmd.string('name', 'Required name', required: true)
Guild vs Global Commands
Global Commands
Available in all guilds (with 1-hour propagation delay):
bot.slash('help', 'Show help') do |cmd|
cmd.handler { |i| i.respond(content: 'Help text') }
end
Guild Commands
Available immediately in specific guilds:
bot.slash('admin', 'Admin command', guild_id: '123456789') do |cmd|
cmd.default_permissions(:administrator)
cmd.handler { |i| i.respond(content: 'Admin stuff') }
end
Multiple Guilds
ADMIN_GUILDS = ['123456789', '987654321']
ADMIN_GUILDS.each do |guild_id|
bot.slash('beta', 'Beta command', guild_id: guild_id) do |cmd|
cmd.handler { |i| i.respond(content: 'Beta feature') }
end
end
Subcommands
Organize complex commands with subcommands:
bot.slash('settings', 'Server settings') do |cmd|
# Subcommand: view
cmd.subcommand('view', 'View current settings') do |sub|
sub.handler do |interaction|
# Get guild settings
settings = get_settings(interaction.guild_id)
interaction.respond(content: format_settings(settings))
end
end
# Subcommand: set
cmd.subcommand('set', 'Update settings') do |sub|
sub.string('key', 'Setting to change', required: true)
sub.string('value', 'New value', required: true)
sub.handler do |interaction|
key = interaction.option('key')
value = interaction.option('value')
update_setting(interaction.guild_id, key, value)
interaction.respond(content: "Set #{key} to #{value}")
end
end
# Subcommand: reset
cmd.subcommand('reset', 'Reset to defaults') do |sub|
sub.handler do |interaction|
reset_settings(interaction.guild_id)
interaction.respond(content: 'Settings reset!')
end
end
end
Subcommand Groups
For even more organization:
bot.slash('mod', 'Moderation commands') do |cmd|
# Group: user
cmd.group('user', 'User management') do |group|
group.subcommand('ban', 'Ban a user') do |sub|
sub.user('target', required: true)
sub.string('reason')
sub.handler do |i|
# Handle ban
end
end
group.subcommand('kick', 'Kick a user') do |sub|
sub.user('target', required: true)
sub.string('reason')
sub.handler do |i|
# Handle kick
end
end
end
# Group: channel
cmd.group('channel', 'Channel management') do |group|
group.subcommand('lock', 'Lock a channel') do |sub|
sub.handler do |i|
# Handle lock
end
end
group.subcommand('unlock', 'Unlock a channel') do |sub|
sub.handler do |i|
# Handle unlock
end
end
end
end
Permissions
Default Permissions
bot.slash('ban', 'Ban a user') do |cmd|
# Only users with ban_members can see/use this
cmd.default_permissions(:ban_members, :kick_members)
cmd.handler { |i| /* ... */ }
end
DM Permissions
bot.slash('help', 'Get help') do |cmd|
cmd.dm_permission(true) # Allow in DMs (default: true)
# or
cmd.dm_permission(false) # Guild-only command
cmd.handler { |i| /* ... */ }
end
Deferred Responses
For slow commands, defer the response first:
bot.slash('process', 'Process data') do |cmd|
cmd.handler do |interaction|
# Show "Bot is thinking..." immediately
interaction.defer(ephemeral: true)
# Do slow work
result = heavy_processing()
# Update the response
interaction.edit_original(content: "Done! Result: #{result}")
end
end
Ephemeral Responses
Private responses only visible to the command user:
bot.slash('secret', 'Secret command') do |cmd|
cmd.handler do |interaction|
interaction.respond(
content: 'This is a secret!',
ephemeral: true # Only you can see this
)
end
end
Rich Responses
With Embeds
bot.slash('serverinfo', 'Server information') do |cmd|
cmd.handler do |interaction|
guild = bot.guild(interaction.guild_id)
interaction.respond do |builder|
builder.embed do |embed|
embed.title = guild.name
embed.description = guild.description
embed.color = 0x00ff00
embed.thumbnail = guild.icon_url
embed.add_field(name: 'Members', value: guild.member_count.to_s, inline: true)
embed.add_field(name: 'Created', value: guild.created_at.strftime('%Y-%m-%d'), inline: true)
embed.add_field(name: 'Owner', value: "<@#{guild.owner_id}>", inline: true)
embed.timestamp = Time.now
embed.footer = { text: 'Server Info' }
end
end
end
end
With Components
bot.slash('confirm', 'Confirm action') do |cmd|
cmd.handler do |interaction|
interaction.respond(
content: 'Are you sure?',
components: [
{
type: 1, # Action row
components: [
{
type: 2, # Button
style: 3, # Success (green)
label: 'Yes',
custom_id: 'confirm_yes'
},
{
type: 2,
style: 4, # Danger (red)
label: 'No',
custom_id: 'confirm_no'
}
]
}
]
)
end
end
Bulk Registration
Register multiple commands at once:
commands = []
commands << DiscordRDA::CommandBuilder.new('ping', 'Ping!') do |cmd|
cmd.handler { |i| i.respond(content: 'Pong!') }
end
commands << DiscordRDA::CommandBuilder.new('info', 'Bot info') do |cmd|
cmd.handler { |i| i.respond(content: 'Bot v1.0') }
end
commands << DiscordRDA::CommandBuilder.new('help', 'Show help') do |cmd|
cmd.handler { |i| i.respond(content: 'Help text') }
end
# Register all at once
bot.bulk_register_commands(commands)
Deleting Commands
# Delete global command
bot.delete_global_command(command_id)
# Delete guild command
bot.delete_guild_command(guild_id, command_id)
# Delete all global commands (careful!)
bot.bulk_register_commands([])
Best Practices
- Use descriptive names - Commands should be self-explanatory
- Provide descriptions - Required by Discord, helps users
- Set appropriate permissions - Don't expose admin commands
- Defer slow commands - Prevent timeouts
- Use subcommands - Organize complex commands
- Test in guild first - Guild commands update immediately
Complete Example
require 'discord_rda'
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
application_id: ENV['DISCORD_APP_ID'],
intents: [:guilds]
)
# Simple command
bot.slash('ping', 'Check bot latency') do |cmd|
cmd.handler do |interaction|
interaction.respond(content: 'Pong! 🏓')
end
end
# Command with options
bot.slash('echo', 'Repeat a message') do |cmd|
cmd.string('message', 'Message to echo', required: true)
cmd.boolean('ephemeral', 'Private response?')
cmd.handler do |interaction|
message = interaction.option('message')
ephemeral = interaction.option('ephemeral') || false
interaction.respond(
content: message,
ephemeral: ephemeral
)
end
end
# Command with subcommands
bot.slash('moderation', 'Moderation tools') do |cmd|
cmd.subcommand('warn', 'Warn a user') do |sub|
sub.user('user', required: true)
sub.string('reason', required: true)
sub.handler do |i|
user = i.option('user')
reason = i.option('reason')
# Implement warning logic
i.respond(content: "Warned #{user.mention}: #{reason}")
end
end
end
# Admin command with permissions
bot.slash('purge', 'Delete messages', guild_id: ADMIN_GUILD) do |cmd|
cmd.integer('amount', 'Messages to delete',
required: true,
min_value: 1,
max_value: 100
)
cmd.default_permissions(:manage_messages)
cmd.handler do |interaction|
amount = interaction.option('amount')
# Get messages and delete
messages = bot.channel_messages(interaction.channel_id, limit: amount)
bot.bulk_delete_messages(interaction.channel_id, messages.map(&:id))
interaction.respond(
content: "Deleted #{amount} messages",
ephemeral: true
)
end
end
bot.run
Next Steps
- Learn about Context Menus - Right-click commands
- Build Components - Buttons and selects
- Add Autocomplete - Smart suggestions