mod common;
use botrs::{Client, Context, EventHandler, Intents, Message, Ready, Token};
use common::{Config, init_logging};
use std::env;
use tracing::{info, warn};
type CommandHandlerFn = fn(&str) -> Option<String>;
struct CommandRegistry {
commands: Vec<(Vec<String>, CommandHandlerFn)>,
}
impl CommandRegistry {
fn new() -> Self {
Self {
commands: Vec::new(),
}
}
fn register(&mut self, aliases: Vec<&str>, handler: CommandHandlerFn) {
let aliases: Vec<String> = aliases.iter().map(|s| s.to_string()).collect();
self.commands.push((aliases, handler));
}
fn try_execute(&self, content: &str) -> Option<String> {
let trimmed = content.trim();
for (aliases, handler) in &self.commands {
for alias in aliases {
if trimmed.starts_with(alias) {
let params = if trimmed.len() > alias.len() {
trimmed[alias.len()..].trim()
} else {
""
};
return handler(params);
}
}
}
None
}
}
fn hello_command(params: &str) -> Option<String> {
info!("Hello command executed with params: {}", params);
Some(if params.is_empty() {
"Hello! 你好!".to_string()
} else {
format!("Hello! 你好!参数: {params}")
})
}
fn good_night_command(params: &str) -> Option<String> {
info!("Good night command executed with params: {}", params);
Some(if params.is_empty() {
"Good night! 晚安!".to_string()
} else {
format!("Good night! 晚安!参数: {params}")
})
}
struct AtReplyCommandHandler {
registry: CommandRegistry,
}
impl AtReplyCommandHandler {
fn new() -> Self {
let mut registry = CommandRegistry::new();
registry.register(vec!["你好", "hello"], hello_command);
registry.register(vec!["晚安"], good_night_command);
Self { registry }
}
}
#[async_trait::async_trait]
impl EventHandler for AtReplyCommandHandler {
async fn ready(&self, _ctx: Context, ready: Ready) {
info!("robot 「{}」 on_ready!", ready.user.username);
}
async fn message_create(&self, ctx: Context, message: Message) {
let content = match &message.content {
Some(content) => content,
None => return,
};
info!("Received message: {}", content);
if let Some(response) = self.registry.try_execute(content) {
match message.reply(&ctx.api, &ctx.token, &response).await {
Ok(_) => info!("Successfully sent reply via message.reply"),
Err(e) => warn!("Failed to send reply via message.reply: {}", e),
}
let params = botrs::models::message::MessageParams {
content: Some(response),
msg_id: message.id.clone(),
..Default::default()
};
match ctx
.api
.post_message_with_params(
&ctx.token,
message.channel_id.as_ref().unwrap_or(&String::new()),
params,
)
.await
{
Ok(_) => info!("Successfully sent message via api.post_message"),
Err(e) => warn!("Failed to send message via api.post_message: {}", e),
}
} else {
let default_response = "收到消息,但没有匹配的命令。可用命令: 你好/hello, 晚安";
match message.reply(&ctx.api, &ctx.token, default_response).await {
Ok(_) => info!("Successfully sent default reply"),
Err(e) => warn!("Failed to send default reply: {}", e),
}
}
}
async fn error(&self, error: botrs::BotError) {
warn!("Event handler error: {}", error);
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_logging();
info!("Starting AT reply command demo...");
let config = Config::load_with_fallback(
Some("examples/config.toml"),
env::args().nth(1), env::args().nth(2), )?;
info!("Configuration loaded successfully");
let token = Token::new(config.bot.app_id, config.bot.secret);
if let Err(e) = token.validate() {
panic!("Invalid token: {e}");
}
info!("Token validated successfully");
let intents = Intents::default().with_public_guild_messages();
info!("Configured intents: {}", intents);
let handler = AtReplyCommandHandler::new();
let mut client = Client::new(token, intents, handler, true)?;
info!("Client created, starting bot...");
client.start().await?;
info!("Bot stopped");
Ok(())
}