fix(feishu): match bot's own open_id in _is_bot_mentioned to prevent cross-bot false positives

Previously, _is_bot_mentioned used a heuristic (no user_id + open_id
prefix "ou_") which caused other bots in the same group to falsely
think they were mentioned. Now fetches the bot's own open_id via
GET /open-apis/bot/v3/info at startup and does an exact match.
This commit is contained in:
有泉 2026-04-01 23:23:23 +08:00 committed by Xubin Ren
parent 424b9fc262
commit 1b368a33dc

View File

@ -298,6 +298,7 @@ class FeishuChannel(BaseChannel):
self._processed_message_ids: OrderedDict[str, None] = OrderedDict() # Ordered dedup cache
self._loop: asyncio.AbstractEventLoop | None = None
self._stream_bufs: dict[str, _FeishuStreamBuf] = {}
self._bot_open_id: str | None = None
@staticmethod
def _register_optional_event(builder: Any, method_name: str, handler: Any) -> Any:
@ -378,6 +379,15 @@ class FeishuChannel(BaseChannel):
self._ws_thread = threading.Thread(target=run_ws, daemon=True)
self._ws_thread.start()
# Fetch bot's own open_id for accurate @mention matching
self._bot_open_id = await asyncio.get_running_loop().run_in_executor(
None, self._fetch_bot_open_id
)
if self._bot_open_id:
logger.info("Feishu bot open_id: {}", self._bot_open_id)
else:
logger.warning("Could not fetch bot open_id; @mention matching may be inaccurate")
logger.info("Feishu bot started with WebSocket long connection")
logger.info("No public IP required - using WebSocket to receive events")
@ -396,6 +406,20 @@ class FeishuChannel(BaseChannel):
self._running = False
logger.info("Feishu bot stopped")
def _fetch_bot_open_id(self) -> str | None:
"""Fetch the bot's own open_id via GET /open-apis/bot/v3/info."""
from lark_oapi.api.bot.v3 import GetBotInfoRequest
try:
request = GetBotInfoRequest.builder().build()
response = self._client.bot.v3.bot_info.get(request)
if response.success() and response.data and response.data.bot:
return getattr(response.data.bot, "open_id", None)
logger.warning("Failed to get bot info: code={}, msg={}", response.code, response.msg)
return None
except Exception as e:
logger.warning("Error fetching bot info: {}", e)
return None
def _is_bot_mentioned(self, message: Any) -> bool:
"""Check if the bot is @mentioned in the message."""
raw_content = message.content or ""
@ -406,8 +430,8 @@ class FeishuChannel(BaseChannel):
mid = getattr(mention, "id", None)
if not mid:
continue
# Bot mentions have no user_id (None or "") but a valid open_id
if not getattr(mid, "user_id", None) and (getattr(mid, "open_id", None) or "").startswith("ou_"):
mention_open_id = getattr(mid, "open_id", None) or ""
if self._bot_open_id and mention_open_id == self._bot_open_id:
return True
return False