From 230bfb9c6950f1fdb175c4bfc12a6a2e135e4d5f Mon Sep 17 00:00:00 2001 From: "ziyan.li" Date: Mon, 25 May 2026 12:12:52 +0800 Subject: [PATCH] feat(feishu-channel): add streaming switch and reaction reply feature 1. add new env/config item: TOOL_FEISHU_CHANNEL_REACTIONS 2. add reaction reply function that sends "OneSecond" emoji when receiving messages 3. support reading reaction config from both init params and env variables --- docs/docs/tools/feishu-channel.md | 3 ++ veadk/extensions/feishu_channel.py | 44 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/docs/docs/tools/feishu-channel.md b/docs/docs/tools/feishu-channel.md index b36bc2ff..fa1d2d39 100644 --- a/docs/docs/tools/feishu-channel.md +++ b/docs/docs/tools/feishu-channel.md @@ -29,6 +29,8 @@ pip install lark-oapi - `TOOL_FEISHU_CHANNEL_APP_ID` - `TOOL_FEISHU_CHANNEL_APP_SECRET` - `TOOL_FEISHU_CHANNEL_TRANSPORT`,默认 `ws` +- `TOOL_FEISHU_CHANNEL_STREAMING`,是否开启流式输出,默认 `false` +- `TOOL_FEISHU_CHANNEL_REACTIONS`,是否在收到消息时回复“收到”表情,默认 `false` 或在 `config.yaml` 中配置: @@ -39,6 +41,7 @@ tool: app_secret: xxx transport: ws streaming: true + reactions: true ``` ## 最小示例 diff --git a/veadk/extensions/feishu_channel.py b/veadk/extensions/feishu_channel.py index 30e70228..cde05d6d 100644 --- a/veadk/extensions/feishu_channel.py +++ b/veadk/extensions/feishu_channel.py @@ -60,6 +60,9 @@ class FeishuMessageContext: text: str +FEISHU_EMOJI_ONE_SECOND = "OneSecond" + + class FeishuChannelExtension: """Bridge a Feishu bot channel with a VeADK runner. @@ -85,6 +88,7 @@ def __init__( ignore_empty_messages: bool = True, channel_kwargs: dict[str, Any] | None = None, streaming: bool = False, + reactions: bool = False, ) -> None: self.runner = runner self.session_id_factory = session_id_factory or self.default_session_id_factory @@ -93,6 +97,10 @@ def __init__( self.response_formatter = response_formatter or self.default_response_formatter self.reply_in_thread = reply_in_thread self.ignore_empty_messages = ignore_empty_messages + self.reactions = ( + reactions + or str(os.getenv("TOOL_FEISHU_CHANNEL_REACTIONS", "")).lower() == "true" + ) self.streaming = ( streaming or str(os.getenv("TOOL_FEISHU_CHANNEL_STREAMING", "")).lower() == "true" @@ -172,6 +180,42 @@ async def _on_message(self, message: Any) -> None: context = self.build_message_context(message=message, text=text) + if self.reactions and context.message_id: + try: + import lark_oapi.api.im.v1 as lark_im + + emoji = ( + lark_im.Emoji.builder().emoji_type(FEISHU_EMOJI_ONE_SECOND).build() + ) + request = ( + lark_im.CreateMessageReactionRequest.builder() + .message_id(context.message_id) + .request_body( + lark_im.CreateMessageReactionRequestBody.builder() + .reaction_type(emoji) + .build() + ) + .build() + ) + + if hasattr(self.channel, "client"): + response = await self._maybe_await( + self.channel.client.im.v1.message_reaction.create(request) + ) + + if not response.success(): + logger.error( + f"Failed to add reaction to message {context.message_id}: {response.code} {response.msg}" + ) + else: + logger.warning( + "Channel has no client attribute, cannot send reaction" + ) + except Exception as e: + logger.error( + f"Failed to add reaction to message {context.message_id}: {e}" + ) + send_options = {} if self.reply_in_thread and context.message_id: send_options["reply_to"] = context.message_id