PicoBot/src/command/handlers/switch_session.rs
ooodc 3591822145 feat: add /help command to display all supported commands
- Implemented HelpCommandHandler to handle the /help command.
- Added CommandMetadata struct to store command metadata.
- Registered new command handlers for GetCurrentSession, ListSessions, LoadSession, and SwitchSession.
- Updated existing command handlers to provide metadata for help command.
- Removed deprecated SessionQueryCommandHandler.
- Added new command handlers for listing sessions and loading sessions.
2026-05-16 19:48:39 +08:00

130 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::command::context::CommandContext;
use crate::command::handler::{CommandHandler, CommandMetadata};
use crate::command::response::{CommandError, CommandResponse, MessageKind};
use crate::command::Command;
use crate::gateway::session::SessionManager;
use crate::storage::SessionStore;
use async_trait::async_trait;
use std::sync::Arc;
/// 切换话题命令处理器
pub struct SwitchSessionCommandHandler {
store: Arc<SessionStore>,
session_manager: Option<SessionManager>,
}
impl SwitchSessionCommandHandler {
pub fn new(store: Arc<SessionStore>) -> Self {
Self { store, session_manager: None }
}
pub fn with_session_manager(mut self, session_manager: SessionManager) -> Self {
self.session_manager = Some(session_manager);
self
}
}
#[async_trait]
impl CommandHandler for SwitchSessionCommandHandler {
fn can_handle(&self, cmd: &Command) -> bool {
matches!(cmd, Command::SwitchSession { .. })
}
fn metadata(&self) -> Option<CommandMetadata> {
Some(CommandMetadata {
name: "use",
description: "切换到指定话题",
usage: "/use <session_id>",
})
}
async fn handle(
&self,
cmd: Command,
ctx: CommandContext,
) -> Result<CommandResponse, CommandError> {
match cmd {
Command::SwitchSession { session_id } => {
handle_switch_session(self, session_id, ctx).await
}
_ => unreachable!(),
}
}
}
async fn handle_switch_session(
handler: &SwitchSessionCommandHandler,
topic_id: String,
ctx: CommandContext,
) -> Result<CommandResponse, CommandError> {
let session_id = ctx.session_id.as_deref()
.ok_or_else(|| CommandError::new("NO_SESSION", "No active session"))?;
let chat_id = ctx.chat_id.as_deref()
.ok_or_else(|| CommandError::new("NO_CHAT_ID", "No chat_id in context"))?;
// 尝试解析为序号
let target_topic_id = if let Ok(index) = topic_id.parse::<usize>() {
let topics = handler
.store
.list_topics(session_id)
.map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?;
let index = index.saturating_sub(1);
if index >= topics.len() {
return Err(CommandError::new(
"INVALID_TOPIC_INDEX",
format!("Topic index {} is out of range (1-{})", index + 1, topics.len())
));
}
topics[index].id.clone()
} else {
topic_id
};
// 验证目标话题存在
let topic = handler
.store
.get_topic(&target_topic_id)
.map_err(|e| CommandError::new("SWITCH_TOPIC_ERROR", e.to_string()))?
.ok_or_else(|| CommandError::new("TOPIC_NOT_FOUND", format!("Topic not found: {}", target_topic_id)))?;
// 如果有 SessionManager实际切换话题历史
if let Some(ref session_manager) = handler.session_manager {
if let Some(session) = session_manager.get(&ctx.channel_name).await {
let mut session_guard = session.lock().await;
session_guard.switch_topic(chat_id, &target_topic_id)
.map_err(|e| CommandError::new("SWITCH_TOPIC_ERROR", e.to_string()))?;
}
}
let message = format!(
"✓ Switched to topic: {} ({} messages)",
topic.title, topic.message_count
);
Ok(CommandResponse::success(ctx.request_id)
.with_message(MessageKind::Notification, &message)
.with_metadata("topic_id", &topic.id)
.with_metadata("title", &topic.title)
.with_metadata("message_count", &topic.message_count.to_string()))
}
fn format_time_ago(timestamp_ms: i64) -> String {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as i64;
let diff_ms = now - timestamp_ms;
let diff_secs = diff_ms / 1000;
if diff_secs < 60 {
"just now".to_string()
} else if diff_secs < 3600 {
format!("{} mins ago", diff_secs / 60)
} else if diff_secs < 86400 {
format!("{} hours ago", diff_secs / 3600)
} else {
format!("{} days ago", diff_secs / 86400)
}
}