Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion gateway/platforms/feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ class FeishuGroupRule:
policy: str # "open" | "allowlist" | "blacklist" | "admin_only" | "disabled"
allowlist: set[str] = field(default_factory=set)
blacklist: set[str] = field(default_factory=set)
# When False, bot processes every message in this group (no @mention required).
# Only use this for solo bot-user groups or dedicated topic channels where you
# want Robocat to respond to every message. Default True preserves the safe
# mention-gated behaviour for team/multi-person groups.
require_mention: bool = True


@dataclass
Expand Down Expand Up @@ -1104,6 +1109,7 @@ def _load_settings(extra: Dict[str, Any]) -> FeishuAdapterSettings:
policy=str(rule_cfg.get("policy", "open")).strip().lower(),
allowlist=set(str(u).strip() for u in rule_cfg.get("allowlist", []) if str(u).strip()),
blacklist=set(str(u).strip() for u in rule_cfg.get("blacklist", []) if str(u).strip()),
require_mention=bool(rule_cfg.get("require_mention", True)),
)

# Bot-level admins
Expand Down Expand Up @@ -3061,9 +3067,20 @@ def _allow_group_message(self, sender_id: Any, chat_id: str = "") -> bool:
return bool(sender_ids and (sender_ids & self._allowed_group_users))

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."""
"""Require an explicit @mention before group messages enter the agent.

Groups with ``require_mention: false`` configured in ``group_rules`` bypass
the mention gate entirely — every message in that group is routed to the
bot (subject to the policy/allowlist/blacklist check above). This is
intended for solo bot-user groups or dedicated topic channels where the
bot should act like a DM participant.
"""
if not self._allow_group_message(sender_id, chat_id):
return False
# Per-group opt-out: skip the mention gate entirely.
rule = self._group_rules.get(chat_id) if chat_id else None
if rule and not rule.require_mention:
return True
# @_all is Feishu's @everyone placeholder — always route to the bot.
raw_content = getattr(message, "content", "") or ""
if "@_all" in raw_content:
Expand Down
54 changes: 54 additions & 0 deletions tests/gateway/test_feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,60 @@ def test_per_group_admin_only_policy_requires_admin(self):
)
)

def test_per_group_require_mention_false_bypasses_mention_gate(self):
"""Groups opted in via ``require_mention: false`` process every message."""
from gateway.config import PlatformConfig
from gateway.platforms.feishu import FeishuAdapter

config = PlatformConfig(
extra={
"group_rules": {
"oc_solo_topic": {
"policy": "open",
"require_mention": False,
},
"oc_team_chat": {
"policy": "open",
# require_mention defaults to True
},
}
}
)
adapter = FeishuAdapter(config)
adapter._bot_open_id = "ou_bot"

# A plain message with NO mentions — the normal mention gate would drop it.
no_mention = SimpleNamespace(mentions=[], content="")
sender = SimpleNamespace(open_id="ou_jerry", user_id=None)

# Solo topic group — should accept without mention.
self.assertTrue(
adapter._should_accept_group_message(no_mention, sender, "oc_solo_topic")
)

# Regular team group — still requires a mention, so this is dropped.
self.assertFalse(
adapter._should_accept_group_message(no_mention, sender, "oc_team_chat")
)

# Policy gate still applies — a blacklist hit on the opt-out group is still dropped.
config_with_blacklist = PlatformConfig(
extra={
"group_rules": {
"oc_solo_topic": {
"policy": "blacklist",
"blacklist": ["ou_jerry"],
"require_mention": False,
}
}
}
)
adapter2 = FeishuAdapter(config_with_blacklist)
adapter2._bot_open_id = "ou_bot"
self.assertFalse(
adapter2._should_accept_group_message(no_mention, sender, "oc_solo_topic")
)

def test_per_group_disabled_policy_blocks_all(self):
from gateway.config import PlatformConfig
from gateway.platforms.feishu import FeishuAdapter
Expand Down