diff --git a/src/channels/feishu.rs b/src/channels/feishu.rs index cc3a9ae..2dba571 100644 --- a/src/channels/feishu.rs +++ b/src/channels/feishu.rs @@ -12,7 +12,9 @@ use tokio::sync::{broadcast, RwLock}; use crate::bus::{MessageBus, MediaItem, OutboundMessage}; use crate::channels::base::{Channel, ChannelError}; +use crate::channels::slash_command::parse_slash_command; use crate::config::FeishuChannelConfig; +use crate::session::{SessionCommand, SessionEvent}; const FEISHU_API_BASE: &str = "https://open.feishu.cn/open-apis"; const FEISHU_WS_BASE: &str = "https://open.feishu.cn"; @@ -1995,6 +1997,84 @@ impl Channel for FeishuChannel { "feishu" } + /// Handle an inbound message: check for slash commands first, then publish to bus + async fn handle_and_publish( + &self, + bus: &Arc, + msg: &crate::bus::InboundMessage, + ) -> Result<(), ChannelError> { + use tokio::sync::mpsc; + + if !self.is_allowed(&msg.sender_id) { + tracing::warn!( + channel = %self.name(), + sender = %msg.sender_id, + "Access denied" + ); + return Ok(()); + } + + // Check for slash command + if let Some((cmd_name, cmd_args)) = parse_slash_command(&msg.content) { + tracing::info!(cmd = %cmd_name, "Feishu slash command detected"); + let (reply_tx, mut reply_rx) = mpsc::channel(1); + bus.publish_control(crate::bus::ControlMessage { + op: SessionCommand::ExecuteSlashCommand { + command: cmd_name.to_string(), + args: if cmd_args.is_empty() { None } else { Some(cmd_args.to_string()) }, + channel: msg.channel.clone(), + chat_id: msg.chat_id.clone(), + current_session_id: None, + }, + reply_tx, + }).await?; + + // Handle response + if let Some(result) = reply_rx.recv().await { + match result { + Ok(SessionEvent::SlashCommandExecuted { message, .. }) => { + let outbound = crate::bus::OutboundMessage { + channel: msg.channel.clone(), + chat_id: msg.chat_id.clone(), + content: message, + reply_to: None, + media: vec![], + metadata: msg.forwarded_metadata.clone(), + }; + bus.publish_outbound(outbound).await?; + } + Ok(SessionEvent::Error { message, .. }) => { + let outbound = crate::bus::OutboundMessage { + channel: msg.channel.clone(), + chat_id: msg.chat_id.clone(), + content: message, + reply_to: None, + media: vec![], + metadata: msg.forwarded_metadata.clone(), + }; + bus.publish_outbound(outbound).await?; + } + Err(e) => { + let outbound = crate::bus::OutboundMessage { + channel: msg.channel.clone(), + chat_id: msg.chat_id.clone(), + content: format!("Error: {}", e), + reply_to: None, + media: vec![], + metadata: msg.forwarded_metadata.clone(), + }; + bus.publish_outbound(outbound).await?; + } + _ => {} + } + } + return Ok(()); + } + + bus.publish_inbound(msg.clone()).await?; + Ok(()) + } + async fn start(&self, bus: Arc) -> Result<(), ChannelError> { if self.config.app_id.is_empty() || self.config.app_secret.is_empty() { return Err(ChannelError::ConfigError(