diff --git a/README.md b/README.md index e887828..dccb4be 100644 --- a/README.md +++ b/README.md @@ -502,7 +502,8 @@ Uses **WebSocket** long connection — no public IP required. "appSecret": "xxx", "encryptKey": "", "verificationToken": "", - "allowFrom": ["ou_YOUR_OPEN_ID"] + "allowFrom": ["ou_YOUR_OPEN_ID"], + "groupPolicy": "mention" } } } @@ -510,6 +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. +> `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 160b9b4..780227a 100644 --- a/nanobot/channels/feishu.py +++ b/nanobot/channels/feishu.py @@ -352,6 +352,27 @@ class FeishuChannel(BaseChannel): self._running = False logger.info("Feishu bot stopped") + 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: + 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 _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).""" from lark_oapi.api.im.v1 import CreateMessageReactionRequest, CreateMessageReactionRequestBody, Emoji @@ -893,6 +914,10 @@ class FeishuChannel(BaseChannel): chat_type = message.chat_type msg_type = message.message_type + 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 1b26dd7..55e109e 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -48,6 +48,7 @@ class FeishuConfig(Base): react_emoji: str = ( "THUMBSUP" # Emoji type for message reactions (e.g. THUMBSUP, OK, DONE, SMILE) ) + group_policy: Literal["open", "mention"] = "mention" # "mention" responds when @mentioned, "open" responds to all class DingTalkConfig(Base):