From bd1ce8f1440311d42dcc22c60153964f64d27a94 Mon Sep 17 00:00:00 2001 From: Re-bin Date: Thu, 12 Mar 2026 04:45:57 +0000 Subject: [PATCH] Simplify feishu group_policy: default to mention, clean up mention detection --- README.md | 15 +------ nanobot/channels/feishu.py | 91 ++++++++------------------------------ nanobot/config/schema.py | 3 +- 3 files changed, 22 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 155920f..dccb4be 100644 --- a/README.md +++ b/README.md @@ -503,7 +503,7 @@ Uses **WebSocket** long connection — no public IP required. "encryptKey": "", "verificationToken": "", "allowFrom": ["ou_YOUR_OPEN_ID"], - "groupPolicy": "open" + "groupPolicy": "mention" } } } @@ -511,18 +511,7 @@ Uses **WebSocket** long connection — no public IP required. > `encryptKey` and `verificationToken` are optional for Long Connection mode. > `allowFrom`: Add your open_id (find it in nanobot logs when you message the bot). Use `["*"]` to allow all users. - -**Group Chat Policy** (optional): - -| Option | Values | Default | Description | -|--------|--------|---------|-------------| -| `groupPolicy` | `"open"` | `"open"` | Respond to all group messages (backward compatible) | -| | `"mention"` | | Only respond when @mentioned | - -> [!NOTE] -> - `"open"`: Respond to all messages in all groups -> - `"mention"`: Only respond when @mentioned in any group -> - Private chats are unaffected (always respond) +> `groupPolicy`: `"mention"` (default — respond only when @mentioned), `"open"` (respond to all group messages). Private chats always respond. **3. Run** diff --git a/nanobot/channels/feishu.py b/nanobot/channels/feishu.py index 4919e3c..780227a 100644 --- a/nanobot/channels/feishu.py +++ b/nanobot/channels/feishu.py @@ -352,73 +352,26 @@ class FeishuChannel(BaseChannel): self._running = False logger.info("Feishu bot stopped") - def _get_bot_open_id_sync(self) -> str | None: - """Get bot's own open_id for mention detection. - - 飞书 SDK 没有直接的 bot info API,从配置或缓存获取。 - """ - # 尝试从配置获取 open_id(用户可以在配置中指定) - if hasattr(self.config, 'open_id') and self.config.open_id: - return self.config.open_id - - return None - - def _is_bot_mentioned(self, message: Any, bot_open_id: str | None) -> bool: - """Check if bot is mentioned in the message. - - 飞书 mentions 数组包含被@的对象。匹配策略: - 1. 如果配置了 bot_open_id,则匹配 open_id - 2. 否则,检查 mentions 中是否有空的 user_id(bot 的特征) - - Handles: - - Direct mentions in message.mentions - - @all mentions - """ - # Check @all + def _is_bot_mentioned(self, message: Any) -> bool: + """Check if the bot is @mentioned in the message.""" raw_content = message.content or "" if "@_all" in raw_content: - logger.debug("Feishu: @_all mention detected") return True - - # Check mentions array - mentions = message.mentions if hasattr(message, 'mentions') and message.mentions else [] - if mentions: - if bot_open_id: - # 策略 1: 匹配配置的 open_id - for mention in mentions: - if mention.id: - open_id = getattr(mention.id, 'open_id', None) - if open_id == bot_open_id: - logger.debug("Feishu: bot mention matched") - return True - else: - # 策略 2: 检查 bot 特征 - user_id 为空且 open_id 存在 - for mention in mentions: - if mention.id: - user_id = getattr(mention.id, 'user_id', None) - open_id = getattr(mention.id, 'open_id', None) - # Bot 的特征:user_id 为空字符串,open_id 存在 - if user_id == '' and open_id and open_id.startswith('ou_'): - logger.debug("Feishu: bot mention matched") - return True - + + for mention in getattr(message, "mentions", None) or []: + mid = getattr(mention, "id", None) + if not mid: + continue + # Bot mentions have an empty user_id with a valid open_id + if getattr(mid, "user_id", None) == "" and (getattr(mid, "open_id", None) or "").startswith("ou_"): + return True return False - def _should_respond_in_group( - self, - chat_id: str, - mentioned: bool - ) -> tuple[bool, str]: - """Determine if bot should respond in a group chat. - - Returns: - (should_respond, reason) - """ - # Check mention requirement - if self.config.group_policy == "mention" and not mentioned: - return False, "not mentioned in group" - - return True, "" + def _is_group_message_for_bot(self, message: Any) -> bool: + """Allow group messages when policy is open or bot is @mentioned.""" + if self.config.group_policy == "open": + return True + return self._is_bot_mentioned(message) def _add_reaction_sync(self, message_id: str, emoji_type: str) -> None: """Sync helper for adding reaction (runs in thread pool).""" @@ -961,16 +914,10 @@ class FeishuChannel(BaseChannel): chat_type = message.chat_type msg_type = message.message_type - # Check group policy and mention requirement - if chat_type == "group": - bot_open_id = self._get_bot_open_id_sync() - mentioned = self._is_bot_mentioned(message, bot_open_id) - should_respond, reason = self._should_respond_in_group(chat_id, mentioned) - - if not should_respond: - logger.debug("Feishu: ignoring group message - {}", reason) - return - + if chat_type == "group" and not self._is_group_message_for_bot(message): + logger.debug("Feishu: skipping group message (not mentioned)") + return + # Add reaction await self._add_reaction(message_id, self.config.react_emoji) diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index 592a93c..55e109e 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -48,8 +48,7 @@ class FeishuConfig(Base): react_emoji: str = ( "THUMBSUP" # Emoji type for message reactions (e.g. THUMBSUP, OK, DONE, SMILE) ) - # Group chat settings - group_policy: Literal["open", "mention"] = "open" # Group response policy (default: open for backward compatibility) + group_policy: Literal["open", "mention"] = "mention" # "mention" responds when @mentioned, "open" responds to all class DingTalkConfig(Base):