Skip to content
Open
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
4 changes: 4 additions & 0 deletions gateway/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,8 @@ def load_gateway_config() -> GatewayConfig:
bridged["reply_in_thread"] = platform_cfg["reply_in_thread"]
if "require_mention" in platform_cfg:
bridged["require_mention"] = platform_cfg["require_mention"]
if "ignore_mention_all" in platform_cfg:
bridged["ignore_mention_all"] = platform_cfg["ignore_mention_all"]
if plat == Platform.TELEGRAM and "allowed_chats" in platform_cfg:
bridged["allowed_chats"] = platform_cfg["allowed_chats"]
if plat == Platform.TELEGRAM and "group_allowed_chats" in platform_cfg:
Expand Down Expand Up @@ -1118,6 +1120,8 @@ def load_gateway_config() -> GatewayConfig:
if isinstance(feishu_cfg, dict):
if "allow_bots" in feishu_cfg and not os.getenv("FEISHU_ALLOW_BOTS"):
os.environ["FEISHU_ALLOW_BOTS"] = str(feishu_cfg["allow_bots"]).lower()
if "ignore_mention_all" in feishu_cfg and not os.getenv("FEISHU_IGNORE_MENTION_ALL"):
os.environ["FEISHU_IGNORE_MENTION_ALL"] = str(feishu_cfg["ignore_mention_all"]).lower()

except Exception as e:
logger.warning(
Expand Down
7 changes: 6 additions & 1 deletion gateway/platforms/feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ class FeishuAdapterSettings:
group_rules: Dict[str, FeishuGroupRule] = field(default_factory=dict)
allow_bots: str = "none" # "none" | "mentions" | "all"
require_mention: bool = True
ignore_mention_all: bool = False


@dataclass
Expand Down Expand Up @@ -1569,6 +1570,9 @@ def _load_settings(extra: Dict[str, Any]) -> FeishuAdapterSettings:
require_mention=_to_boolean(
extra.get("require_mention", os.getenv("FEISHU_REQUIRE_MENTION", "true"))
),
ignore_mention_all=_to_boolean(
os.getenv("FEISHU_IGNORE_MENTION_ALL", extra.get("ignore_mention_all", "false"))
),
)

def _apply_settings(self, settings: FeishuAdapterSettings) -> None:
Expand Down Expand Up @@ -1601,6 +1605,7 @@ def _apply_settings(self, settings: FeishuAdapterSettings) -> None:
self._ws_ping_timeout = settings.ws_ping_timeout
self._allow_bots = settings.allow_bots
self._require_mention = settings.require_mention
self._ignore_mention_all = settings.ignore_mention_all

def _build_event_handler(self) -> Any:
if EventDispatcherHandler is None:
Expand Down Expand Up @@ -4093,7 +4098,7 @@ def _allow_group_message(
def _mentions_self(self, message: Any) -> bool:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adapter path was moved to plugins/platforms/feishu/adapter.py by 5600105478ffde29d7566b45421b100eaa29c4ef; please port this change to the plugin adapter, where current main's _mentions_self() is at lines 4331-4345.

# @_all is Feishu's @everyone placeholder.
raw_content = getattr(message, "content", "") or ""
if "@_all" in raw_content:
if "@_all" in raw_content and not self._ignore_mention_all:
return True
mentions = getattr(message, "mentions", None) or []
if mentions and self._message_mentions_bot(mentions):
Expand Down
2 changes: 2 additions & 0 deletions tests/gateway/feishu_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def make_adapter_skeleton(
bot_user_id: str = "",
allow_bots: str = "none",
require_mention: bool = True,
ignore_mention_all: bool = False,
group_policy: str = "allowlist",
) -> Any:
from gateway.platforms.feishu import FeishuAdapter
Expand All @@ -49,6 +50,7 @@ def make_adapter_skeleton(
adapter._allowed_group_users = frozenset()
adapter._allow_bots = allow_bots
adapter._require_mention = require_mention
adapter._ignore_mention_all = ignore_mention_all
return adapter


Expand Down
39 changes: 39 additions & 0 deletions tests/gateway/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,45 @@ def test_feishu_allow_bots_env_takes_precedence_over_config_yaml(self, tmp_path,

assert os.environ.get("FEISHU_ALLOW_BOTS") == "none"

def test_bridges_feishu_ignore_mention_all_from_config_yaml_to_env(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(
"feishu:\n ignore_mention_all: true\n",
encoding="utf-8",
)

monkeypatch.setenv("HERMES_HOME", str(hermes_home))
monkeypatch.delenv("FEISHU_IGNORE_MENTION_ALL", raising=False)

config = load_gateway_config()

assert os.environ.get("FEISHU_IGNORE_MENTION_ALL") == "true"
assert config.platforms[Platform.FEISHU].extra["ignore_mention_all"] is True

def test_feishu_ignore_mention_all_env_overrides_config_yaml(self, tmp_path, monkeypatch):
from gateway.platforms.feishu import FeishuAdapter

hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(
"feishu:\n ignore_mention_all: true\n",
encoding="utf-8",
)

monkeypatch.setenv("HERMES_HOME", str(hermes_home))
monkeypatch.setenv("FEISHU_APP_ID", "cli_test")
monkeypatch.setenv("FEISHU_APP_SECRET", "secret_test")
monkeypatch.setenv("FEISHU_IGNORE_MENTION_ALL", "false")

config = load_gateway_config()
settings = FeishuAdapter._load_settings(extra=config.platforms[Platform.FEISHU].extra)

assert config.platforms[Platform.FEISHU].extra["ignore_mention_all"] is True
assert settings.ignore_mention_all is False

def test_invalid_quick_commands_in_config_yaml_are_ignored(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
Expand Down
53 changes: 53 additions & 0 deletions tests/gateway/test_feishu_bot_admission.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,29 @@ def test_feishu_load_settings_require_mention(monkeypatch, env_value, extra, exp
assert settings.require_mention is expected


@pytest.mark.parametrize(
"env_value, extra, expected",
[
(None, {}, False),
("false", {}, False),
("true", {}, True),
("false", {"ignore_mention_all": True}, False),
],
)
def test_feishu_load_settings_ignore_mention_all(monkeypatch, env_value, extra, expected):
from gateway.platforms.feishu import FeishuAdapter

monkeypatch.setenv("FEISHU_APP_ID", "cli_test")
monkeypatch.setenv("FEISHU_APP_SECRET", "secret_test")
if env_value is None:
monkeypatch.delenv("FEISHU_IGNORE_MENTION_ALL", raising=False)
else:
monkeypatch.setenv("FEISHU_IGNORE_MENTION_ALL", env_value)

settings = FeishuAdapter._load_settings(extra=extra)
assert settings.ignore_mention_all is expected


def test_feishu_load_settings_parses_per_group_require_mention(monkeypatch):
from gateway.platforms.feishu import FeishuAdapter

Expand Down Expand Up @@ -148,6 +171,36 @@ def test_sender_identity_handles_missing_sender_id():
assert _sender_identity(SimpleNamespace()) == frozenset()


def test_mentions_self_treats_all_hands_as_mention_by_default():
adapter = make_adapter_skeleton(bot_open_id="ou_self")
message = make_message(mentions=[])
message.content = '{"text":"@_all please look"}'

assert adapter._mentions_self(message) is True


def test_mentions_self_ignores_all_hands_when_configured():
adapter = make_adapter_skeleton(bot_open_id="ou_self", ignore_mention_all=True)
message = make_message(mentions=[])
message.content = '{"text":"@_all please look"}'

assert adapter._mentions_self(message) is False


def test_mentions_self_explicit_bot_mention_unaffected_when_ignoring_all_hands():
adapter = make_adapter_skeleton(bot_open_id="ou_self", ignore_mention_all=True)
message = make_message(mentions=[
SimpleNamespace(
key="@_user_1",
id=SimpleNamespace(open_id="ou_self", user_id=""),
name="Hermes",
)
])
message.content = '{"text":"@_all @_user_1 please look"}'

assert adapter._mentions_self(message) is True


@pytest.mark.parametrize("sender_type", ["bot", "app"])
def test_is_bot_sender_treats_bot_and_app_as_bot_origin(sender_type):
from gateway.platforms.feishu import _is_bot_sender
Expand Down
10 changes: 9 additions & 1 deletion website/docs/user-guide/messaging/feishu.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,18 @@ FEISHU_GROUP_POLICY=allowlist # default
In all modes, the bot must be explicitly @mentioned (or @all) in the group before the message is processed. Direct messages always bypass this gate.

Set `FEISHU_REQUIRE_MENTION=false` to let Hermes read all group traffic without requiring an @mention:

```bash
FEISHU_REQUIRE_MENTION=false
```

To prevent workspace-wide `@all` / `@everyone` broadcasts from waking Hermes, set:

```bash
FEISHU_IGNORE_MENTION_ALL=true
```

This only changes group admission: explicit bot mentions still work, and processed messages still render Feishu `@all` mentions normally. You can also configure it as `feishu.ignore_mention_all` in `config.yaml` (env wins when both are set).

For per-chat control, set `require_mention` on a `group_rules` entry — see [Per-Group Access Control](#per-group-access-control) below.

### Bot Identity
Expand Down Expand Up @@ -490,6 +497,7 @@ Inbound messages are deduplicated using message IDs with a 24-hour TTL. The dedu
| `FEISHU_ALLOWED_USERS` | — | _(empty)_ | Comma-separated open_id list for user allowlist |
| `FEISHU_ALLOW_BOTS` | — | `none` | Accept messages from other bots: `none`, `mentions`, or `all` |
| `FEISHU_REQUIRE_MENTION` | — | `true` | Whether group messages must @mention the bot |
| `FEISHU_IGNORE_MENTION_ALL` | — | `false` | Ignore group `@all` / `@everyone` as a bot mention trigger |
| `FEISHU_HOME_CHANNEL` | — | — | Chat ID for cron/notification output |
| `FEISHU_ENCRYPT_KEY` | — | _(empty)_ | Encrypt key for webhook signature verification |
| `FEISHU_VERIFICATION_TOKEN` | — | _(empty)_ | Verification token for webhook payload auth |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,18 @@ FEISHU_GROUP_POLICY=allowlist # 默认
在所有模式下,消息处理前机器人必须被明确 @提及(或 @all)。私信始终绕过此限制。

设置 `FEISHU_REQUIRE_MENTION=false` 可让 Hermes 读取所有群消息而无需 @提及:

```bash
FEISHU_REQUIRE_MENTION=false
```

如需防止全工作区 `@all` / `@everyone` 广播唤醒 Hermes,请设置:

```bash
FEISHU_IGNORE_MENTION_ALL=true
```

这只影响群聊准入:明确 @提及机器人仍然有效,已处理消息中的飞书 `@all` 提及仍会正常渲染。也可在 `config.yaml` 中配置为 `feishu.ignore_mention_all`(两者同时设置时,环境变量优先)。

如需按群控制,在 `group_rules` 条目中设置 `require_mention`——参见下方[按群访问控制](#per-group-access-control)。

### 机器人身份
Expand Down Expand Up @@ -490,6 +497,7 @@ platforms:
| `FEISHU_ALLOWED_USERS` | — | _(空)_ | 用户白名单的逗号分隔 open_id 列表 |
| `FEISHU_ALLOW_BOTS` | — | `none` | 接受其他机器人消息:`none`、`mentions` 或 `all` |
| `FEISHU_REQUIRE_MENTION` | — | `true` | 群消息是否必须 @提及 机器人 |
| `FEISHU_IGNORE_MENTION_ALL` | — | `false` | 忽略群聊 `@all` / `@everyone` 作为机器人提及触发 |
| `FEISHU_HOME_CHANNEL` | — | — | cron/通知输出的聊天 ID |
| `FEISHU_ENCRYPT_KEY` | — | _(空)_ | webhook 签名验证的加密密钥 |
| `FEISHU_VERIFICATION_TOKEN` | — | _(空)_ | webhook payload 认证的验证 token |
Expand Down