# NousResearch/hermes-agent · Telegram/Slack mention gating and bot filtering

## Open now

- Nothing open on GitHub.

These issues and PRs address how require_mention rules are enforced in Telegram group chats and Slack group DMs, including slash command handling, bot-originated message filtering, and a proposed TELEGRAM_ALLOW_BOTS configuration option.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #16379. Verify it addresses your case.
- **Recommended:** read PR #16379 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-16379`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #15415: Telegram bot-menu slash commands (\`/cmd@botname\`) dropped in groups with require_mention=true

## Prior & competing PRs

- **#16379: fix(telegram): accept /cmd@botname from bot menu in groups**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `gateway/platforms/telegram.py`, `tests/gateway/test_telegram_group_gating.py`
  - fix hunks:
  - `gateway/platforms/telegram.py`

    ```diff
    @@ -2353,6 +2353,26 @@ def _iter_sources():
                         user = getattr(entity, "user", None)
                         if user and getattr(user, "id", None) == bot_id:
                             return True
    +                elif entity_type == "bot_command" and expected:
    +                    # Telegram's official group-disambiguation form for slash
    +                    # commands (``/cmd@botname``) is emitted as a single
    +                    # ``bot_command`` entity covering the whole span — there
    +                    # is no accompanying ``mention`` entity. Treat it as a
    +                    # direct address to this bot when the ``@botname`` suffix
    +                    # matches. This is the form Telegram's own command menu
    +                    # autocomplete produces in groups, so dropping it at the
    +                    # mention gate would break /new, /reset, /help, ... for
    +                    # every group that has ``require_mention`` enabled (#15415).
    +                    offset = int(getattr(entity, "offset", -1))
    +                    length = int(getattr(entity, "length", 0))
    +                    if offset < 0 or length <= 0:
    +                        continue
    +                    command_text = source_text[offset:offset + length]
    +                    at_index = command_text.find("@")
    +                    if at_index < 0:
    +                        continue
    +                    if command_text[at_index:].strip().lower() == expected:
    +                        return True
             return False
     
         def _message_matches_mention_patterns(self, message: Message) -> bool:
    ```
  - `tests/gateway/test_telegram_group_gating.py`

    ```diff
    @@ -59,6 +59,17 @@ def _mention_entity(text, mention="@hermes_bot"):
         return SimpleNamespace(type="mention", offset=offset, length=len(mention))
     
     
    +def _bot_command_entity(text, command):
    +    """Entity Telegram emits for a ``/cmd`` or ``/cmd@botname`` token.
    +
    +    Telegram parses slash commands server-side. For ``/cmd@botname`` the
    +    client does NOT emit a separate ``mention`` entity — the whole span
    +    is a single ``bot_command`` entity.
    +    """
    +    offset = text.index(command)
    +    return SimpleNamespace(type="bot_command", offset=offset, length=len(command))
    +
    +
     def test_group_messages_can_be_opened_via_config():
         adapter = _make_adapter(require_mention=False)
     
    ```

    ```diff
    @@ -73,12 +84,34 @@ def test_group_messages_can_require_direct_trigger_via_config():
         assert adapter._should_process_message(_group_message("replying", reply_to_bot=True)) is True
         # Commands must also respect require_mention when it is enabled
         assert adapter._should_process_message(_group_message("/status"), is_command=True) is False
    -    # But commands with @mention still pass (Telegram emits a MENTION entity
    -    # for /cmd@botname — the bot menu and python-telegram-bot's CommandHandler
    -    # rely on this same mechanism)
    +    # Telegram's group command menu sends ``/cmd@botname`` as a single
    +    # ``bot_command`` entity spanning the whole token (no separate mention
    +    # entity). We must accept it so the menu works when require_mention is on.
         assert adapter._should_process_message(
    -        _group_message("/status@hermes_bot", entities=[_mention_entity("/status@hermes_bot")])
    +        _group_message(
    +            "/status@hermes_bot",
    +            entities=[_bot_command_entity("/status@hermes_bot", "/status@hermes_bot")],
    +        ),
    +        is_command=True,
         ) is True
    +    # A bot_command entity addressed at a different bot must not satisfy
    +    # the mention gate — Telegram groups can host multiple bots that
    +    # register the same command name.
    +    assert adapter._should_process_message(
    +        _group_message(
    +            "/status@other_bot",
    +            entities=[_bot_command_entity("/status@other_bot", "/status@other_bot")],
    +        ),
    +        is_command=True,
    +    ) is False
    +    # Bare ``/status`` (no @botname) must still be dropped in groups with
    +    # require_mention=True — Telegram delivers it only when the bot's
    +    # privacy mode is off, and even then we should not respond unless the
    +    # user explicitly addressed the bot.
    +    assert adapter._should_process_message(
    +        _group_message("/status", entities=[_bot_command_entity("/status", "/status")]),
    +        is_command=True,
    +    ) is False
         # And commands still pass unconditionally when require_mention is disabled
         adapter_no_mention = _make_adapter(require_mention=False)
         assert adapter_no_mention._should_process_message(_group_message("/status"), is_command=True) is True
    ```
- **#15417: fix(telegram): accept /cmd@botname from bot menu in groups**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `3ff3dfb5a`, `#16379`, `#15415`

## Fix status

- Merged to main in PR #16379 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-27`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
