Command Structure
All commands follow a consistent pattern with two main functions:register()- Defines the slash command structure for Discordrun()- Executes the command logic when invoked
Creating a Simple Command
Let’s create a/ping style command as an example.
1
Create the Command Module
Create a new file in For complex commands, create a directory:
src/commands/:2
Implement register() Function
Define the slash command structure that Discord will display to users:This creates a basic command with no options.
src/commands/example.rs
3
Implement run() Function
Add the command execution logic:
src/commands/example.rs
4
Export in mod.rs
Add your new command to
src/commands/mod.rs:src/commands/mod.rs
5
Register in main.rs ready Event
Add your command to the registration list in See
src/main.rs at the ready event handler:src/main.rs
src/main.rs for the full implementation.6
Add Match Arm in interaction_create
Route the command to your handler in See
src/main.rs at the interaction_create event:src/main.rs
src/main.rs for the full implementation.Adding Command Options
Commands can accept user input through options:Accessing Option Values
Extract option values in yourrun() function:
src/commands/anime/command.rs:66-80 for a real-world example.
Deferring Long Operations
Discord requires responses within 3 seconds. For long-running operations, defer the response immediately:Calling external services
External API clients use asyncreqwest::Client. Await those requests directly after deferring the Discord interaction:
spawn_blocking only for synchronous work that can block the Tokio runtime, such as the current Redis cache helper calls.
See src/utils/requests/anilist.rs for the AniList HTTP client and src/commands/recommend/command.rs for a bounded spawn_blocking cache example.
Adding Instrumentation
Use the#[instrument] attribute for tracing and observability:
name- Span name for filteringskip- Fields to exclude from tracing (avoid logging sensitive data)
Code Style Conventions
Follow these conventions when writing commands:Logging
Use
tracing macros: info!, debug!, error!Never use println! or eprintln!Error Handling
Prefer
? operator over .unwrap()Handle errors gracefully with user-friendly messagesPrivacy
Hash user IDs before logging:
Sentry
Configure Sentry scope for commands:
Testing Your Command
Unit Tests
Add tests in the same file:src/commands/ping.rs:50-78 for a complete test example.
Running Tests
Manual Testing
-
Run the bot locally:
-
Use the command in Discord:
-
Check logs for tracing output:
Example: Complete Command
Here’s a complete command implementation fromsrc/commands/ping.rs:
File Organization Patterns
Depending on complexity, organize your command:Simple Command (Single File)
Complex Command (Module)
- GraphQL queries
- Multiple helper functions
- Complex business logic
src/commands/anime/ for a complete example.
Next Steps
Architecture
Understand the project structure
Database
Add database models for persistent data
Testing
Write comprehensive tests
