- 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.
130 lines
4.2 KiB
Rust
130 lines
4.2 KiB
Rust
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)
|
||
}
|
||
} |