Slash Commands
Learn how to create slash commands that users can discover and use! Build interactive commands like /help, /ping, and more.
Learning Objectives
By the end of this module, you will be able to:
Command Definitions
Create a file that defines all your bot's slash commands.
Creating src/commands.ts
This file tells Towns Protocol what commands your bot has. Each command needs a name and description.
import type { PlainMessage, SlashCommand } from '@towns-protocol/proto'
const commands = [
{
name: 'help',
description: 'Show bot help and available commands',
},
{
name: 'ping',
description: 'Check if the bot is responsive',
},
{
name: 'stats',
description: 'Show community or user stats',
},
] as const satisfies PlainMessage<SlashCommand>[]
export default commandsCommand Naming Tips
- • Use lowercase, single words (help, ping, stats)
- • Keep descriptions short and clear
- • Commands appear in autocomplete when users type "/"
Challenge: Define 3 Commands
Add help, ping, and info commands
Basic Command Handlers
Implement handlers that respond when users type your commands.
1. Help Command
The /help command shows users what your bot can do.
bot.onSlashCommand('help', async (handler, { channelId, spaceId, userId }) => {
try {
// IMPORTANT: Each command execution is stateless
// No access to message history or previous interactions
await handler.sendMessage(channelId, `
🤖 **Bot Help**
**Available Commands:**
• \`/help\` - Show this help message
• \`/ping\` - Check bot status
• \`/stats\` - View community stats
Type \`/\` to see all commands!
`)
} catch (error) {
console.error('Error in /help:', error)
}
})2. Ping Command
The /ping command lets users check if your bot is working.
bot.onSlashCommand('ping', async (handler, { channelId, spaceId, userId, args }) => {
try {
// args contains any text after the command: /ping hello world
// args would be ['hello', 'world']
const message = args?.length ?
`🏓 Pong! You said: ${args.join(' ')}` :
'🏓 Pong! Bot is online.'
await handler.sendMessage(channelId, message)
} catch (error) {
console.error('Error in /ping:', error)
}
})Error Handling
Always wrap commands in try/catch. This prevents one failing command from crashing your entire bot!
Challenge: Create a Help Command
Make it list /help, /ping, and /stats commands
Command Parameters
Handle arguments and user mentions in your commands.
User Mentions
Commands can accept user mentions like /stats @user
bot.onSlashCommand('stats', async (handler, { channelId, spaceId, userId, args, mentions }) => {
try {
// IMPORTANT: Each command is stateless - no access to previous data
// You would need to use a database (Module 5) to store actual stats
if (mentions && mentions.length > 0) {
// User mentioned someone: /stats @user
const targetUser = mentions[0]
await handler.sendMessage(channelId, `
📊 **Stats for <@${targetUser.userId}>**
💬 Messages: 150 (example data)
👍 Reactions: 45 (example data)
📅 Member since: 2 weeks ago
`)
} else if (args && args[0] === 'server') {
// /stats server - show server stats
await handler.sendMessage(channelId, `
📊 **Space Stats**
🏛️ Space: <#${spaceId}>
👥 Members: 1,234
💬 Messages Today: 567
🔥 Most Active: Alice
`)
} else {
// No args: show user's own stats
await handler.sendMessage(channelId, `
📊 **Your Stats**
👤 User: <@${userId}>
💬 Messages: 89 (example data)
👍 Reactions Given: 23 (example data)
`)
}
} catch (error) {
console.error('Error in /stats:', error)
}
})Command Parameters
- •
args- Array of text arguments after the command - •
mentions- Array of mentioned users - •
userId- Who ran the command - •
channelId- Where it was run - •
spaceId- Which space it's in
Challenge: Stats with Mentions
Show user stats if mentioned, otherwise show community stats
Command Registration
Register your commands so users can discover them in Towns.
Automatic Registration
Commands are automatically registered when your bot connects to Towns Protocol:
- 1. Define commands in your commands.ts file
- 2. Register them with setSlashCommands()
- 3. Commands sync when the bot starts
- 4. Users see them in autocomplete when typing "/"
Complete Registration Example
Here's how commands are registered and handled in your bot:
import { makeTownsBot } from '@towns-protocol/bot'
import commands from './commands.js'
// Initialize bot with base64-encoded credentials
const bot = makeTownsBot({
privateData: process.env.APP_PRIVATE_DATA!, // Must be base64 encoded
webhookSecret: process.env.JWT_SECRET!,
mnemonic: process.env.MNEMONIC!, // 12-word recovery phrase
port: parseInt(process.env.PORT || '5123', 10)
})
// Register slash commands (automatically synced to Towns)
bot.setSlashCommands(commands)
// Handle each command (stateless - no history access)
bot.onSlashCommand('help', async (handler, event) => {
// Each command execution is isolated
await handler.sendMessage(event.channelId, 'Help message here')
})
// Start the webhook server
bot.startWebhook()
console.log(`✅ Bot running with ${commands.length} commands`)Auto-Discovery
Users can type "/" in Towns and see your commands in autocomplete! They can discover what your bot does without asking.
Updating Commands
If you add, remove, or change commands, simply restart your bot. Commands are automatically synced on every restart!
Module 2 Complete!
Awesome! You've learned how to create slash commands. Users can now type "/" and discover your bot's features through an intuitive command interface!