Prefix Commands¶
rustycord provides a powerful prefix-based command system that allows you to easily create and manage bot commands with specific prefixes (like !
, ?
, or custom prefixes).
Overview¶
The prefix command system consists of:
PrefixListener
- Manages commands for a specific prefixPrefixCommand
trait - Define your own commands- Built-in commands (
HelpCommand
,PingCommand
,EchoPrefixCommand
) - Integration with the existing message handler system
Basic Usage¶
1. Create a Prefix Listener¶
use rustycord::prefix::PrefixListener;
use std::sync::Arc;
// Create a listener for "!" prefix
let listener = Arc::new(PrefixListener::new("!"));
// For case-sensitive commands
let case_listener = Arc::new(PrefixListener::new_case_sensitive("!"));
2. Register Built-in Commands¶
use rustycord::prefix::{HelpCommand, PingCommand, EchoPrefixCommand};
// Register built-in commands
listener.register_command("help", Box::new(HelpCommand::new(listener.clone()))).await;
listener.register_command("ping", Box::new(PingCommand)).await;
listener.register_command("echo", Box::new(EchoPrefixCommand)).await;
3. Create Custom Commands¶
use async_trait::async_trait;
use rustycord::prefix::PrefixCommand;
use rustycord::message::ChannelMessage;
struct InfoCommand;
#[async_trait]
impl PrefixCommand for InfoCommand {
async fn execute(&self, message: &ChannelMessage, _args: Vec<&str>)
-> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>
{
let response = format!(
"**Server Information**\\n\\
Channel ID: {}\\n\\
Your ID: {}",
message.channel_id,
message.author.id
);
Ok(Some(response))
}
fn description(&self) -> &str {
"Show information about the current server and channel"
}
fn aliases(&self) -> Vec<&str> {
vec!["server", "guild"]
}
}
// Register the custom command
listener.register_command("info", Box::new(InfoCommand)).await;
4. Integrate with Message Handler¶
use rustycord::handlers::message_handler::MessageHandler;
struct PrefixMessageHandler {
listener: Arc<PrefixListener>,
}
impl PrefixMessageHandler {
fn new(listener: Arc<PrefixListener>) -> Self {
Self { listener }
}
}
#[async_trait]
impl MessageHandler for PrefixMessageHandler {
async fn on_message_create(&self, message: &ChannelMessage, client: &Client)
-> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
// Skip messages from bots
if message.author.bot.unwrap_or(false) {
return Ok(());
}
// Try to handle the message with the prefix listener
if let Some(response) = self.listener.handle_message(message).await? {
client.send_text_message(&message.channel_id, &response).await?;
}
Ok(())
}
}
5. Register with Event Dispatcher¶
// Register with the bot's event dispatcher
if let Some(client) = &bot.client {
let event_dispatcher = client.get_event_dispatcher();
let message_handlers = event_dispatcher.get_message_handlers();
message_handlers.add_handler(PrefixMessageHandler::new(listener.clone())).await;
}
Built-in Commands¶
Help Command¶
The HelpCommand
automatically lists all registered commands and provides detailed help.
// Usage in Discord:
// !help - Lists all commands
// !help <command> - Shows help for specific command
Ping Command¶
A simple responsiveness test command.
Echo Command¶
Echoes back the provided text.
Advanced Examples¶
Math Calculator Command¶
struct MathCommand;
#[async_trait]
impl PrefixCommand for MathCommand {
async fn execute(&self, _message: &ChannelMessage, args: Vec<&str>)
-> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>
{
if args.len() < 3 {
return Ok(Some("Usage: `!math <number> <operator> <number>`\\nExample: `!math 5 + 3`".to_string()));
}
let num1: f64 = match args[0].parse() {
Ok(n) => n,
Err(_) => return Ok(Some("Invalid first number".to_string())),
};
let operator = args[1];
let num2: f64 = match args[2].parse() {
Ok(n) => n,
Err(_) => return Ok(Some("Invalid second number".to_string())),
};
let result = match operator {
"+" => num1 + num2,
"-" => num1 - num2,
"*" | "x" => num1 * num2,
"/" => {
if num2 == 0.0 {
return Ok(Some("Cannot divide by zero!".to_string()));
}
num1 / num2
}
"%" => num1 % num2,
"^" | "**" => num1.powf(num2),
_ => return Ok(Some("Unknown operator. Supported: +, -, *, /, %, ^".to_string())),
};
Ok(Some(format!("{} {} {} = {}", num1, operator, num2, result)))
}
fn description(&self) -> &str {
"Perform basic math calculations"
}
fn aliases(&self) -> Vec<&str> {
vec!["calc", "calculate"]
}
}
User Information Command¶
struct UserCommand;
#[async_trait]
impl PrefixCommand for UserCommand {
async fn execute(&self, message: &ChannelMessage, _args: Vec<&str>)
-> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>
{
let user = &message.author;
let response = format!(
"**User Information**\\n\\
Username: {}#{}\\n\\
ID: {}\\n\\
Bot: {}\\n\\
MFA Enabled: {}",
user.name,
user.discriminator,
user.id,
user.bot.unwrap_or(false),
user.mfa_enabled
);
Ok(Some(response))
}
fn description(&self) -> &str {
"Show information about a user (defaults to yourself)"
}
fn aliases(&self) -> Vec<&str> {
vec!["whois", "profile"]
}
}
Complete Example¶
See the prefix commands example for a complete working bot that demonstrates all these features.
API Reference¶
PrefixListener¶
new(prefix: &str)
- Create a new case-insensitive prefix listenernew_case_sensitive(prefix: &str)
- Create a case-sensitive prefix listenerregister_command(name: &str, command: Box<dyn PrefixCommand>)
- Register a commandunregister_command(name: &str)
- Remove a commandhandle_message(message: &ChannelMessage)
- Process a message for commandslist_commands()
- Get all registered command namesget_command_help(command_name: &str)
- Get help for a specific commandprefix()
- Get the prefix being used
PrefixCommand Trait¶
execute(message: &ChannelMessage, args: Vec<&str>)
- Execute the commanddescription()
- Get command description for helpaliases()
- Get command aliases (optional)
Best Practices¶
- Use descriptive command names - Make commands easy to discover
- Provide good help text - Users should understand what commands do
- Handle errors gracefully - Return helpful error messages
- Use aliases for common commands - Make frequently used commands easy to type
- Skip bot messages - Always check
message.author.bot
to avoid infinite loops - Validate arguments - Check argument count and types before processing
- Use case-insensitive commands - Unless you specifically need case sensitivity
Error Handling¶
Commands should return Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>
:
Ok(Some(response))
- Command executed successfully with a responseOk(None)
- Command executed successfully but no response neededErr(error)
- Command failed with an error
The prefix system will log errors and continue processing other messages.
Limitations¶
- Commands are processed sequentially, not in parallel
- Aliases are logged but not currently stored (feature limitation)
- No built-in cooldown or rate limiting (implement in your command)
- No built-in permission system (implement in your command)
Migration from Simple Message Handlers¶
If you're currently using simple message handlers with manual prefix checking:
// Old approach
if message.content.starts_with("!ping") {
client.send_text_message(&message.channel_id, "Pong!").await?;
}
// New approach with prefix system
struct PingCommand;
#[async_trait]
impl PrefixCommand for PingCommand {
async fn execute(&self, _message: &ChannelMessage, _args: Vec<&str>)
-> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>
{
Ok(Some("Pong!".to_string()))
}
fn description(&self) -> &str {
"Test if the bot is responding"
}
}
The prefix system provides better organization, automatic help generation, and easier command management.