From 7df4a26d03b773920edd690e7dcb8c655ad9bc08 Mon Sep 17 00:00:00 2001 From: Charlie Liao Date: Mon, 20 Apr 2026 13:27:53 +0800 Subject: [PATCH 1/2] feat(feishu): add require_mention config option for group chats Add a configurable require_mention option to FeishuAdapterSettings that allows users to opt out of the @mention requirement in group chats via config.yaml. When require_mention is set to false, the bot will respond to all group messages (subject to group policy) without needing to be @mentioned. Default is true (backward-compatible). --- gateway/platforms/feishu.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gateway/platforms/feishu.py b/gateway/platforms/feishu.py index 0531bff487..c47a9935e2 100644 --- a/gateway/platforms/feishu.py +++ b/gateway/platforms/feishu.py @@ -313,6 +313,7 @@ class FeishuAdapterSettings: admins: frozenset[str] = frozenset() default_group_policy: str = "" group_rules: Dict[str, FeishuGroupRule] = field(default_factory=dict) + require_mention: bool = True @dataclass @@ -1165,6 +1166,9 @@ class FeishuAdapter(BasePlatformAdapter): # Default group policy (for groups not in group_rules) default_group_policy = str(extra.get("default_group_policy", "")).strip().lower() + # Whether @mention is required in group chats (default: True, backward-compatible) + require_mention = str(extra.get("require_mention", "true")).strip().lower() != "false" + return FeishuAdapterSettings( app_id=str(extra.get("app_id") or os.getenv("FEISHU_APP_ID", "")).strip(), app_secret=str(extra.get("app_secret") or os.getenv("FEISHU_APP_SECRET", "")).strip(), @@ -1221,6 +1225,7 @@ class FeishuAdapter(BasePlatformAdapter): admins=admins, default_group_policy=default_group_policy, group_rules=group_rules, + require_mention=require_mention, ) def _apply_settings(self, settings: FeishuAdapterSettings) -> None: @@ -1251,6 +1256,7 @@ class FeishuAdapter(BasePlatformAdapter): self._ws_reconnect_interval = settings.ws_reconnect_interval self._ws_ping_interval = settings.ws_ping_interval self._ws_ping_timeout = settings.ws_ping_timeout + self._require_mention = settings.require_mention def _build_event_handler(self) -> Any: if EventDispatcherHandler is None: @@ -3277,6 +3283,9 @@ class FeishuAdapter(BasePlatformAdapter): def _should_accept_group_message(self, message: Any, sender_id: Any, chat_id: str = "") -> bool: """Require an explicit @mention before group messages enter the agent.""" + # If require_mention is False, skip mention check but still enforce group policy. + if not self._require_mention: + return self._allow_group_message(sender_id, chat_id) if not self._allow_group_message(sender_id, chat_id): return False # @_all is Feishu's @everyone placeholder — always route to the bot. From c62bcd9751e75fcf5e03ed1e09c28241be195de5 Mon Sep 17 00:00:00 2001 From: Charlie Liao Date: Mon, 20 Apr 2026 13:33:32 +0800 Subject: [PATCH 2/2] test(feishu): add TestRequireMentionConfig for require_mention option Tests cover: - require_mention=false accepts group message without @mention - require_mention=false still enforces group allowlist policy - require_mention=true (default) still requires @mention - require_mention=false combined with allowlist blocks non-allowed users --- tests/gateway/test_feishu.py | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/gateway/test_feishu.py b/tests/gateway/test_feishu.py index 21ef6a4276..54feff212e 100644 --- a/tests/gateway/test_feishu.py +++ b/tests/gateway/test_feishu.py @@ -3174,6 +3174,59 @@ class TestGroupMentionAtAll(unittest.TestCase): self.assertTrue(adapter._should_accept_group_message(message, allowed_sender, "")) +class TestRequireMentionConfig(unittest.TestCase): + """Tests for the require_mention config option in Feishu group chats.""" + + @patch.dict(os.environ, {"FEISHU_GROUP_POLICY": "open"}, clear=True) + def test_require_mention_false_accepts_message_without_mention(self): + """When require_mention=false, group messages without @mention are accepted (if policy allows).""" + from gateway.config import PlatformConfig + from gateway.platforms.feishu import FeishuAdapter + + adapter = FeishuAdapter(PlatformConfig(extra={"require_mention": False})) + message = SimpleNamespace(content='{"text":"hello"}', mentions=[]) + sender_id = SimpleNamespace(open_id="ou_anyone", user_id=None) + self.assertTrue(adapter._should_accept_group_message(message, sender_id, "")) + + @patch.dict(os.environ, {"FEISHU_GROUP_POLICY": "allowlist", "FEISHU_ALLOWED_USERS": "ou_allowed"}, clear=True) + def test_require_mention_false_still_requires_group_policy(self): + """require_mention=false bypasses mention check but NOT group allowlist policy.""" + from gateway.config import PlatformConfig + from gateway.platforms.feishu import FeishuAdapter + + adapter = FeishuAdapter(PlatformConfig(extra={"require_mention": False})) + message = SimpleNamespace(content='{"text":"hello"}', mentions=[]) + # Non-allowlisted user with allowlist policy — should be blocked. + sender_id = SimpleNamespace(open_id="ou_stranger", user_id=None) + self.assertFalse(adapter._should_accept_group_message(message, sender_id, "")) + + @patch.dict(os.environ, {"FEISHU_GROUP_POLICY": "open"}, clear=True) + def test_require_mention_true_default_requires_mention(self): + """Default require_mention=true still requires @mention (backward compatible).""" + from gateway.config import PlatformConfig + from gateway.platforms.feishu import FeishuAdapter + + adapter = FeishuAdapter(PlatformConfig(extra={"require_mention": True})) + message = SimpleNamespace(content='{"text":"hello"}', mentions=[]) + sender_id = SimpleNamespace(open_id="ou_anyone", user_id=None) + self.assertFalse(adapter._should_accept_group_message(message, sender_id, "")) + + @patch.dict(os.environ, {"FEISHU_GROUP_POLICY": "allowlist", "FEISHU_ALLOWED_USERS": "ou_allowed"}, clear=True) + def test_require_mention_false_with_allowlist_blocks_non_allowed_users(self): + """require_mention=false does not bypass allowlist policy.""" + from gateway.config import PlatformConfig + from gateway.platforms.feishu import FeishuAdapter + + adapter = FeishuAdapter(PlatformConfig(extra={"require_mention": False})) + message = SimpleNamespace(content='{"text":"hello"}', mentions=[]) + # Allowlisted user — should pass. + allowed_sender = SimpleNamespace(open_id="ou_allowed", user_id=None) + self.assertTrue(adapter._should_accept_group_message(message, allowed_sender, "")) + # Non-allowlisted user — should be blocked. + blocked_sender = SimpleNamespace(open_id="ou_stranger", user_id=None) + self.assertFalse(adapter._should_accept_group_message(message, blocked_sender, "")) + + @unittest.skipUnless(_HAS_LARK_OAPI, "lark-oapi not installed") class TestSenderNameResolution(unittest.TestCase): """Tests for _resolve_sender_name_from_api."""