From 30b68599a472cac5e3eb3476babe3372fe14bcf7 Mon Sep 17 00:00:00 2001 From: xiaoxixi Date: Tue, 28 Apr 2026 20:58:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E9=A3=9E=E4=B9=A6=E6=B8=A0=E9=81=93?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=9C=E6=9D=A0=E5=91=BD=E4=BB=A4=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重写 FeishuChannel 的 handle_and_publish 方法,在发布消息前 检查是否为斜杠命令。如果是,则通过控制平面执行命令并返回结果。 --- src/channels/feishu.rs | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) 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(