# NousResearch/hermes-agent · Telegram bot's own messages treated as inbound or unread

## Open now

- Nothing open on GitHub.

These items concern Telegram messages sent by the bot being counted as incoming unread or processed as inbound input. One is a regression report (0.16 → 0.17), and two are fix proposals that filter or ignore bot-authored self-messages.

## 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 #53571. Verify it addresses your case.
- **Recommended:** read PR #53571 (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: `unresolved`. 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

- #52363: Telegram: outbound messages counted as incoming unread (regression 0.16 → 0.17)

## Prior & competing PRs

- **#53571: fix(telegram): ignore bot-authored self-messages to stop self-ingestion (#11905)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `plugins/platforms/telegram/adapter.py`, `tests/gateway/test_telegram_group_gating.py`
  - fix hunks:
  - `plugins/platforms/telegram/adapter.py`

    ```diff
    @@ -5863,6 +5863,8 @@ def _clean_bot_trigger_text(self, text: Optional[str]) -> Optional[str]:
     
         def _should_observe_unmentioned_group_message(self, message: Message) -> bool:
             """Return True when a group message should be stored but not dispatched."""
    +        if self._is_own_message(message):
    +            return False
             if not self._telegram_observe_unmentioned_group_messages():
                 return False
             if not self._is_group_chat(message):
    ```

    ```diff
    @@ -6132,6 +6134,23 @@ def _observe_unmentioned_group_message(
                 adapter_name = getattr(self, "name", "telegram")
                 logger.warning("[%s] Failed to observe Telegram group message: %s", adapter_name, exc)
     
    +    def _is_own_message(self, message: Message) -> bool:
    +        """Return True when the message was sent by this bot itself.
    +
    +        In some Telegram environments (groups, supergroups where the bot can
    +        see its own messages), getUpdates returns the bot's own outgoing
    +        messages as updates.  These must be filtered out so they are not
    +        counted as incoming unread messages in the Hermes inbox.
    +        """
    +        if not self._bot:
    +            return False
    +        from_user = getattr(message, "from_user", None)
    +        if from_user is None:
    +            return False
    +        bot_id = getattr(self._bot, "id", None)
    +        user_id = getattr(from_user, "id", None)
    +        return bot_id is not None and user_id is not None and bot_id == user_id
    +
         def _should_process_message(self, message: Message, *, is_command: bool = False) -> bool:
             """Apply Telegram group trigger rules.
     
    ```

    ```diff
    @@ -6154,6 +6173,13 @@ def _should_process_message(self, message: Message, *, is_command: bool = False)
             mentioning the bot (``@botname /command``), both of which are
             recognised as mentions by :meth:`_message_mentions_bot`.
             """
    +        # Filter out the bot's own messages (returned by getUpdates in some
    +        # environments like groups/supergroups where the bot can see its own
    +        # messages).  Without this, outbound messages are counted as incoming
    +        # unread in the Hermes inbox (#52363).
    +        if self._is_own_message(message):
    +            return False
    +
             if not self._is_group_chat(message):
                 return True
     
    ```
  - `tests/gateway/test_telegram_group_gating.py`

    ```diff
    @@ -636,6 +636,66 @@ def test_invalid_regex_patterns_are_ignored():
         assert adapter._should_process_message(_group_message("hello everyone")) is False
     
     
    +def test_bot_self_messages_are_ignored_in_dm_and_group():
    +    """Bot-authored messages must not re-enter as fresh user turns (issue #11905).
    +
    +    Telegram echoes the bot's own outbound messages back through getUpdates.
    +    Without a self-author guard, those echoes — including
    +    ``[SYSTEM: Background process ...]`` watcher notifications — get ingested
    +    as new inbound turns, producing the "haunted topic" loop. The guard keys
    +    on ``from_user.id == self._bot.id`` (bot id is 999 in ``_make_adapter``).
    +    """
    +    adapter = _make_adapter(require_mention=False)
    +
    +    # Control: a real user in the same group IS processed.
    +    assert adapter._should_process_message(_group_message("hi", chat_id=-100)) is True
    +
    +    # The exact reported symptom: a bot-authored DM-topic watcher echo.
    +    self_dm = _group_message(
    +        "[SYSTEM: Background process matched watch pattern ...]",
    +        chat_id=555,
    +        from_user_id=999,
    +    )
    +    self_dm.chat.type = "private"
    +    assert adapter._should_process_message(self_dm) is False
    +
    +    # Same guard applies in groups/supergroups.
    +    self_group = _group_message("status tick", chat_id=-100, from_user_id=999)
    +    assert adapter._should_process_message(self_group) is False
    +
    +
    +def test_other_bots_are_still_processed():
    +    """A different bot's message must not be over-filtered.
    +
    +    Distinguishes the self-id guard from a blanket ``from_user.is_bot`` check,
    +    which would incorrectly drop unrelated bots (weather, music, etc.) sharing
    +    the same chat.
    +    """
    +    adapter = _make_adapter(require_mention=False)
    +    other_bot = _group_message("weather update", chat_id=-100, from_user_id=555)
    +    other_bot.from_user = SimpleNamespace(id=555, is_bot=True)
    +    assert adapter._should_process_message(other_bot) is True
    +
    +
    +def test_self_message_guard_skips_observe_path():
    +    """Bot-authored messages are not stored via the observe-unmentioned path.
    +
    +    When ``_should_process_message`` rejects a message, dispatch falls through
    +    to ``_should_observe_unmentioned_group_message``; the self-guard must also
    +    sit there so a self-echo is neither dispatched nor stored.
    +    """
    +    adapter = _make_adapter(require_mention=True, observe_unmentioned_group_messages=True)
    +    self_group = _group_message("status tick", chat_id=-100, from_user_id=999)
    +    assert adapter._should_observe_unmentioned_group_message(self_group) is False
    +
    +
    +def test_missing_from_user_does_not_crash():
    +    adapter = _make_adapter(require_mention=False)
    +    anon = _group_message("channel post", chat_id=-100)
    +    anon.from_user = None
    +    assert adapter._should_process_message(anon) is True
    +
    +
     def test_config_bridges_telegram_group_settings(monkeypatch, tmp_path):
         hermes_home = tmp_path / ".hermes"
         hermes_home.mkdir()
    ```
- **#52667: fix(telegram): filter out bot's own messages from inbound processing**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `6fb25f86a`, `#53571`, `#11905`

## Fix status

- Merged to main in PR #53571 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-27`.
- Subsystem: `gateway`
