Module 2

Slash Commands

Learn how to create slash commands that users can discover and use! Build interactive commands like /help, /ping, and more.

4 sections
Beginner

Learning Objectives

By the end of this module, you will be able to:

Define slash commands in a commands file
Create basic command handlers (/help, /ping)
Handle command parameters and user mentions
Register commands with Towns Protocol
1

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.

src/commands.ts
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 commands

Command 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

2

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.

/help command
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.

/ping command
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

3

Command Parameters

Handle arguments and user mentions in your commands.

User Mentions

Commands can accept user mentions like /stats @user

/stats with mentions and args
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

4

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. 1. Define commands in your commands.ts file
  2. 2. Register them with setSlashCommands()
  3. 3. Commands sync when the bot starts
  4. 4. Users see them in autocomplete when typing "/"

Complete Registration Example

Here's how commands are registered and handled in your bot:

src/index.ts
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!

What You've Built:

Command definitions file
Help and ping commands
Commands with user mentions
Command registration workflow

Next Up:

Handle messages and reactions
Welcome new members
Build interactive features