From 08e131f77cd956dca4cc52f4b12188e23b3b195f Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Sat, 27 Jun 2026 03:43:57 -0700 Subject: [PATCH] test(telegram): cover bot self-message ingestion guard (#11905) Regression tests for the self-author guard added in the salvaged fix: - bot-authored DM-topic watcher echo is dropped (the exact #11905 symptom) - bot self-messages dropped in groups/supergroups too - other bots in the same chat are still processed (self-id, not is_bot) - observe-unmentioned sibling path also rejects self-messages - missing from_user does not crash Test scaffolding ported from @cola-runner's PR #12817 and adapted to the current plugins/platforms/telegram/adapter.py and _is_own_message(). --- tests/gateway/test_telegram_group_gating.py | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/gateway/test_telegram_group_gating.py b/tests/gateway/test_telegram_group_gating.py index 02362db91ec..5e0ff6e5f44 100644 --- a/tests/gateway/test_telegram_group_gating.py +++ b/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()