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().
This commit is contained in:
teknium1 2026-06-27 03:43:57 -07:00 committed by Teknium
parent 6fb25f86ac
commit 08e131f77c

View file

@ -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()