Telegram bot's own messages treated as inbound or unread
Cluster 52363 · gateway. Data as of
Nothing open on GitHub. Fix merged in #53571 on 27 Jun 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #53571. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- 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.
Open issues0
Open PRs0
Fixes on record
-
#53571 fix(telegram): ignore bot-authored self-messages to stop self-ingestion (#11905)
files:
plugins/platforms/telegram/adapter.py,tests/gateway/test_telegram_group_gating.pyShow the diff
-
plugins/platforms/telegram/adapter.py@@ -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): @@ -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. @@ -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@@ -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()
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#52667 fix(telegram): filter out bot's own messages from inbound processing · merged via another change →
6fb25f8,#53571,#11905
Closed issues · 1
- Closed#52363Telegram: outbound messages counted as incoming unread (regression 0.16 → 0.17)
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Subsystem
- gateway
- Merged PRs
- #53571
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 27 Jun 2026