diff --git a/nanobot/channels/feishu.py b/nanobot/channels/feishu.py index 60e59a3ba..bac14cb84 100644 --- a/nanobot/channels/feishu.py +++ b/nanobot/channels/feishu.py @@ -1,6 +1,7 @@ """Feishu/Lark channel implementation using lark-oapi SDK with WebSocket long connection.""" import asyncio +import importlib.util import json import os import re @@ -11,18 +12,15 @@ from collections import OrderedDict from dataclasses import dataclass from typing import Any, Literal -from lark_oapi.api.im.v1.model import P2ImMessageReceiveV1, MentionEvent - +from lark_oapi.api.im.v1.model import MentionEvent, P2ImMessageReceiveV1 from loguru import logger +from pydantic import Field from nanobot.bus.events import OutboundMessage from nanobot.bus.queue import MessageBus from nanobot.channels.base import BaseChannel from nanobot.config.paths import get_media_dir from nanobot.config.schema import Base -from pydantic import Field - -import importlib.util FEISHU_AVAILABLE = importlib.util.find_spec("lark_oapi") is not None @@ -292,11 +290,13 @@ class FeishuChannel(BaseChannel): return FeishuConfig().model_dump(by_alias=True) def __init__(self, config: Any, bus: MessageBus): + import lark_oapi as lark + if isinstance(config, dict): config = FeishuConfig.model_validate(config) super().__init__(config, bus) self.config: FeishuConfig = config - self._client: Any = None + self._client: lark.Client = None self._ws_client: Any = None self._ws_thread: threading.Thread | None = None self._processed_message_ids: OrderedDict[str, None] = OrderedDict() # Ordered dedup cache @@ -340,6 +340,9 @@ class FeishuChannel(BaseChannel): builder = self._register_optional_event( builder, "register_p2_im_message_reaction_created_v1", self._on_reaction_created ) + builder = self._register_optional_event( + builder, "register_p2_im_message_reaction_deleted_v1", self._on_reaction_deleted + ) builder = self._register_optional_event( builder, "register_p2_im_message_message_read_v1", self._on_message_read ) @@ -365,6 +368,7 @@ class FeishuChannel(BaseChannel): # "This event loop is already running" errors. def run_ws(): import time + import lark_oapi.ws.client as _lark_ws_client ws_loop = asyncio.new_event_loop() @@ -418,9 +422,10 @@ class FeishuChannel(BaseChannel): import lark_oapi as lark request = ( - lark.RawRequest.builder() + lark.BaseRequest.builder() .http_method(lark.HttpMethod.GET) .uri("/open-apis/bot/v3/info") + .token_types({lark.AccessTokenType.APP}) .build() ) response = self._client.request(request) @@ -455,12 +460,12 @@ class FeishuChannel(BaseChannel): if not key or key not in text: continue - userID = mention.id or None - if not userID: + user_id_obj = mention.id or None + if not user_id_obj: continue - open_id = userID.open_id - user_id = userID.user_id + open_id = user_id_obj.open_id + user_id = user_id_obj.user_id name = mention.name or key # Format: @姓名 (open_id, user_id: xxx) @@ -1576,7 +1581,6 @@ class FeishuChannel(BaseChannel): "thread_id": thread_id, }, ) - await self._del_reaction(message_id, reaction_id) except Exception as e: logger.error("Error processing Feishu message: {}", e) @@ -1585,6 +1589,10 @@ class FeishuChannel(BaseChannel): """Ignore reaction events so they do not generate SDK noise.""" pass + def _on_reaction_deleted(self, data: Any) -> None: + """Ignore reaction deleted events so they do not generate SDK noise.""" + pass + def _on_message_read(self, data: Any) -> None: """Ignore read events so they do not generate SDK noise.""" pass