Skip to content
Merged
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
18 changes: 16 additions & 2 deletions gateway/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,11 +846,25 @@ def load_gateway_config() -> GatewayConfig:
if yaml_key in allow_mentions_cfg and not os.getenv(env_key):
os.environ[env_key] = str(allow_mentions_cfg[yaml_key]).lower()

# Bridge top-level require_mention to Telegram when the telegram: section
# does not already provide one. Users often write "require_mention: true"
# at the top level alongside group_sessions_per_user, expecting it to work
# the same way (#3979).
_tl_require_mention = yaml_cfg.get("require_mention")
if _tl_require_mention is not None:
_tg_section = yaml_cfg.get("telegram") or {}
if "require_mention" not in _tg_section:
_tg_plat = platforms_data.setdefault(Platform.TELEGRAM.value, {})
_tg_extra = _tg_plat.setdefault("extra", {})
_tg_extra.setdefault("require_mention", _tl_require_mention)

# Telegram settings → env vars (env vars take precedence)
telegram_cfg = yaml_cfg.get("telegram", {})
if isinstance(telegram_cfg, dict):
if "require_mention" in telegram_cfg and not os.getenv("TELEGRAM_REQUIRE_MENTION"):
os.environ["TELEGRAM_REQUIRE_MENTION"] = str(telegram_cfg["require_mention"]).lower()
# Prefer telegram.require_mention; fall back to the top-level shorthand.
_effective_rm = telegram_cfg.get("require_mention", yaml_cfg.get("require_mention"))
if _effective_rm is not None and not os.getenv("TELEGRAM_REQUIRE_MENTION"):
os.environ["TELEGRAM_REQUIRE_MENTION"] = str(_effective_rm).lower()
if "mention_patterns" in telegram_cfg and not os.getenv("TELEGRAM_MENTION_PATTERNS"):
os.environ["TELEGRAM_MENTION_PATTERNS"] = json.dumps(telegram_cfg["mention_patterns"])
frc = telegram_cfg.get("free_response_chats")
Expand Down
51 changes: 51 additions & 0 deletions tests/gateway/test_telegram_group_gating.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,57 @@ def test_group_allow_from_is_enforced_by_gateway_authorization_not_trigger_gate(
assert adapter._should_process_message(_group_message("hello", from_user_id=333)) is True


def test_top_level_require_mention_bridges_to_telegram(monkeypatch, tmp_path):
"""require_mention at the config.yaml top level (alongside group_sessions_per_user)
must behave identically to telegram.require_mention: true (#3979).
"""
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
# Intentionally no "telegram:" section — keys are at the top level.
(hermes_home / "config.yaml").write_text(
"require_mention: true\n"
"group_sessions_per_user: true\n",
encoding="utf-8",
)

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

config = load_gateway_config()

assert config is not None
assert __import__("os").environ.get("TELEGRAM_REQUIRE_MENTION") == "true"

# The adapter's extra dict must also carry the setting so that
# _telegram_require_mention() works even without the env var.
tg_cfg = config.platforms.get(__import__("gateway.config", fromlist=["Platform"]).Platform.TELEGRAM)
if tg_cfg is not None:
assert tg_cfg.extra.get("require_mention") is True


def test_top_level_require_mention_does_not_override_telegram_section(monkeypatch, tmp_path):
"""When telegram.require_mention is explicitly set, top-level require_mention
must not override it (platform-specific config takes precedence).
"""
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text(
"require_mention: true\n"
"telegram:\n"
" require_mention: false\n",
encoding="utf-8",
)

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

config = load_gateway_config()

assert config is not None
# The telegram-specific "false" must win over the top-level "true".
assert __import__("os").environ.get("TELEGRAM_REQUIRE_MENTION") == "false"


def test_config_bridges_telegram_ignored_threads(monkeypatch, tmp_path):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
Expand Down
2 changes: 1 addition & 1 deletion website/docs/user-guide/messaging/telegram.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ Hermes Agent works in Telegram group chats with a few considerations:
- `TELEGRAM_ALLOWED_USERS` still applies — only authorized users can trigger the bot, even in groups
- You can keep the bot from responding to ordinary group chatter with `telegram.require_mention: true`
- With `telegram.require_mention: true`, group messages are accepted when they are:
- slash commands
- replies to one of the bot's messages
- `@botusername` mentions
- `/command@botusername` (Telegram's bot-menu command form that includes the bot name)
- matches for one of your configured regex wake words in `telegram.mention_patterns`
- Use `telegram.ignored_threads` to keep Hermes silent in specific Telegram forum topics, even when the group would otherwise allow free responses or mention-triggered replies
- If `telegram.require_mention` is left unset or false, Hermes keeps the previous open-group behavior and responds to normal group messages it can see
Expand Down
Loading