Skip to content

refactor(telegram): extract DM-topic machinery into TelegramDmTopicMixin (adapter slice A1, −661 lines) - #78949

Closed
andrexibiza wants to merge 10 commits into
NousResearch:mainfrom
andrexibiza:fix/tg-dm-topics-mixin
Closed

refactor(telegram): extract DM-topic machinery into TelegramDmTopicMixin (adapter slice A1, −661 lines)#78949
andrexibiza wants to merge 10 commits into
NousResearch:mainfrom
andrexibiza:fix/tg-dm-topics-mixin

Conversation

@andrexibiza

Copy link
Copy Markdown
Contributor

Summary

Adapter god-file slice A1 of the Telegram decomposition (epic #78791, decomposition plan #78792): the DM-topic machinery cluster moves out of plugins/platforms/telegram/adapter.py into a focused mixin.

Module Cluster adapter.py net delta
telegram_dm_topics.py (727 ln) DM-topic metadata/thread-id helpers, reply-anchor send routing + retry, topic creation/persistence, stale-binding pruning, config reload/cache trio −661 (2 insertions: import + inheritance)

TelegramAdapter inherits TelegramDmTopicMixin (MRO: polling → ingest → text-delivery → rich → dm-topics → base). Every moved method resolves through the adapter namespace to the same function object (seam identity, classmethod-safe __func__ comparison) — the pattern proven by the landed #75742/#75747 mixins and the wave-1 slices (#78866).

Why this wave

Operator policy: all god-files get sharded — model first, agree on the graph, then slice; one PR per cluster, suite green. Wave 1 (#78866) took the four hardest clusters (inbound, delivery, rich, polling). A1 continues the same gates on the DM-topic cluster so private-chat topic machinery gets the same focused-module treatment. Pure extraction — zero behavior change.

Verification

  • Seam identity: all 23 moved methods getattr(TelegramAdapter, n) resolve to the identical __func__ object from the mixin — [] failures. AST-level comparison vs the wave-1 base: bodies byte-identical, the only deltas are the two prescribed lazy imports of _redact_telegram_error_text (monkeypatch-surface shim, same as wave-1 slices).
  • No stragglers: zero def _setup_dm_topics|_create_dm_topic|ensure_dm_topic|rename_dm_topic|_persist_dm_topic_thread_id|_thread_kwargs_for_send|_prune_stale_dm_topic_binding|create_handoff_thread|_metadata_thread_id|_send_with_dm_topic_reply_anchor_retry|_reload_dm_topics_from_config|_get_dm_topic_info|_cache_dm_topic_from_message left in adapter.py.
  • Regression tests: test_dm_topic_mixin_seam_identity (new) asserts mixin-in-MRO, mixin ahead of BasePlatformAdapter, __func__ identity for all 23 names, no adapter __dict__ shadowing, plus behavioral spot-checks — RED on the pre-slice tree, GREEN here.
  • Suite: test_dm_topics.py 15 passed; dm-topic-adjacent gateway files (prune-stale-binding, reply-mode, thread-fallback) 43 passed; full tests/gateway/test_telegram*.py 509 passed (blind reviewer run). Isolated basetemp (Windows host, known WinError-5 teardown bug worked around with --basetemp).
  • Gates: git diff --check clean; utf8-gate PASS; windows-footguns scan of both slice files CLEAN (three pre-existing bare open() calls in test_dm_topics.py — live violations of the blocking footgun gate — fixed with explicit encoding="utf-8" in commit 472523ec9d); worktree health-check HEALTHY before/after; DCO sign-off on every commit.

Merge order / composition (declared, no work lost)

Commits (this slice, oldest → newest)

  1. a8890d9486 refactor(telegram): extract DM-topic machinery into TelegramDmTopicMixin (adapter god-file slice A1)
  2. 472523ec9d test(telegram): explicit utf-8 on config round-trips in dm-topics fixtures
  3. 7279039c60 fix(telegram): strip trailing blank line at EOF in telegram_dm_topics.py
  4. 8207af5ec6 style(telegram): drop stray blank lines after mixin docstring

Why this matters to users

Nothing changes in behavior — that is the point. The adapter is a 10,000-line file where every Telegram feature lives; slicing it into focused mixins means future fixes (topic handling, reply anchors, retries) land in a module a reviewer can actually read, and regressions get caught by seam tests instead of hiding in a wall of monolith. For users: same Telegram experience, a codebase that keeps getting safer to change — which is what keeps the bot from breaking on the next Bot API update.

How to test

cd <worktree>
C:/Users/andre/AppData/Local/hermes/hermes-agent/venv/Scripts/python -m pytest tests/gateway/test_dm_topics.py -q --no-header -p no:cacheprovider --basetemp=C:/tmp/pytest-tg-a1   # 15 passed
C:/Users/andre/AppData/Local/hermes/hermes-agent/venv/Scripts/python -m pytest tests/gateway/test_telegram_prune_stale_topic_binding_31501.py tests/gateway/test_telegram_reply_mode.py tests/gateway/test_telegram_thread_fallback.py -q --no-header -p no:cacheprovider --basetemp=C:/tmp/pytest-tg-a1   # 43 passed
git diff fix/tg-adapter-shard-wave1...HEAD --check   # clean

Platforms tested

Windows 11 (native, git-bash), Python 3.11 (main-checkout venv), isolated pytest basetemp. The moved code is byte-identical to code already running on all platforms via main.

Part of #78792 (adapter decomposition) · Epic #78791 · Supersedes nothing — pure continuation of #78866.

Base note (merge order)

Base is main. This branch is stacked on the wave-1 slice (#78866, branch fix/tg-adapter-shard-wave1) — until #78866 merges, the diff includes its four mixin files (same commits as that PR). The A1-only delta is the four commits listed above. Merge #78866 first, then this PR — after #78866 lands, this diff collapses to the three A1 files automatically and stays clean.

…amIngestMixin (adapter god-file slice)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
…eliveryMixin (adapter god-file slice)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>

# Conflicts:
#	plugins/platforms/telegram/adapter.py
…xin (adapter god-file slice)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>

# Conflicts:
#	plugins/platforms/telegram/adapter.py
…llingMixin (adapter god-file slice)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>

# Conflicts:
#	plugins/platforms/telegram/adapter.py
… mixin

The webhook-start block (and its GHSA-3vpc-7q5r-276h secret guard) moved
from TelegramAdapter.connect() into TelegramPollingMixin._start_webhook
during the adapter god-file slice. The source-level pin now scans both
adapter.py and telegram_polling.py so the invariant survives either
layout, and the polling-branch check anchors on the new
``if not webhook_started:`` dispatch instead of the old else-branch.

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
The inbound mixin called the media cache via its own module global; the
gateway test suite monkeypatches plugins.platforms.telegram.adapter.
cache_image_from_bytes (media-group batching), so the patch missed and the
real guard rejected the fake payloads, breaking photo-burst buffering.
Function-local lazy imports route resolution through the adapter module,
matching the landed slices' shim pattern.

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
…xin (adapter god-file slice A1)

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
…tures

Three bare open() calls in test_dm_topics.py violate the blocking
windows-footguns gate (platform-default encoding on Windows is cp1252).
Explicit encoding="utf-8" on both write and read sides of the same
round-trip — zero behavior change for the fixtures, closes the live
gate violations in a file this PR already touches.

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change comp/plugins Plugin system and bundled plugins platform/telegram Telegram bot adapter P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Aug 4, 2026
@andrexibiza

Copy link
Copy Markdown
Contributor Author

Superseded by the wave-2 consolidation PR (epic #78791). A1 (DM-topic mixin) is one of six adapter slices that ship together as a single decomposed layer; the consolidated PR carries this exact commit plus A2-A6 and the integration fixes. Closing this standalone to keep ONE PR per class (CCC doctrine). No work lost — the commit lives on in the consolidated branch.

@andrexibiza

Copy link
Copy Markdown
Contributor Author

Superseded by wave-2 consolidated PR. See epic #78791.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/telegram Telegram bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants