feat(plugins): let plugins register Telegram PTB handlers (ctx.register_telegram_handler) - #59159
feat(plugins): let plugins register Telegram PTB handlers (ctx.register_telegram_handler)#59159teknium1 wants to merge 1 commit into
Conversation
…ister_telegram_handler Mirrors the Slack precedent (register_slack_action_handler): plugins queue a factory at register() time; the Telegram adapter invokes each factory with (application, adapter) at connect() time, before the core handlers register, so pattern-scoped plugin handlers take precedence for their own updates while everything else falls through unchanged. Factories are isolated — a raising plugin cannot prevent Telegram from connecting. Unblocks standalone plugins that need PTB update types the core adapter doesn't route (Telegram Business API secretary bots, custom callback prefixes, chat-member events) without touching core files.
GottZ
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
Summary
Two open PRs address the same missing Telegram plugin-extension surface by adding a lazy (application, adapter) handler-factory API and wiring factories before core PTB handlers. #59159 also adds public plugin-author documentation, while #61576 carries the consolidated API with broader registration-order, validation, and failure-isolation tests.
Related pull requests
- #59159
related— (+326/-1) — duplicate with useful documentation: addsregister_telegram_handler(factory), connect-time pre-core wiring, per-factory isolation, tests, and the plugin-guide section; its implementation now substantially matches #61576. - #61576
duplicate— (+341/-0) — preferred consolidation path: after the keep_open review on #61576 identified the incompatible PTBhandlevalidation and competing API shape, the diff adopts #59159's lazy factory contract, eliminating that validator issue and adding broader ordering, copy-accessor, invalid-input, manager-failure, and per-factory-isolation coverage.
Duplicates
#59159 and #61576 now implement essentially the same ctx.register_telegram_handler(factory) API and connect-time Telegram adapter wiring; #59159 additionally contains the public plugin-guide documentation.
Suggested consolidation
Merge #61576 after carrying over #59159's plugin-guide documentation (with the corrected application.bot guidance), then close #59159 as superseded by #61576. This follows rather than overrides the keep_open review on #61576: its blocking PTB-contract concern is removed in the current diff by replacing handler duck-typing with the shared lazy factory API.
Complex graph
flowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
subgraph Dup59159 ["PRs duplicating each other"]
P59159["PR #59159 (open)"]
P61576["PR #61576 (open)"]
end
class P59159 open
class P61576 open
class P59159 target
click P59159 "https://github.com/NousResearch/hermes-agent/pull/59159"
click P61576 "https://github.com/NousResearch/hermes-agent/pull/61576"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed or no verify verdict yet (state tag in the node label).
Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 34 kB of PR diffs, 6 kB of issue/PR text, 6 kB of discussion (5 comments), 1 verify verdict. verdicts reflect diff content, not PR titles. Part of an automated triage batch.
… shape Rebases the plugin API onto NousResearch#59159's factory shape per review feedback ("Consolidate with NousResearch#59159's chosen API shape"): - register_telegram_handler(factory): the adapter invokes each factory with (application, adapter) at connect time, right after the PTB Application is built and BEFORE the core handlers are added — so pattern-scoped plugin handlers take precedence while everything else falls through to core. Plugins construct their own PTB handlers inside the factory, so the check_update/handle_update duck-type validation and the group param are gone; PTB's own contract applies at add_handler. - PluginManager: _telegram_handler_factories / get_telegram_handler_factories (was the (handler, group, plugin_name) tuple queue). - TelegramAdapter._wire_plugin_handlers: invokes factory(self._app, self); load-failure and per-factory exceptions isolated so one bad plugin can't break connect. NousResearch#59159 can now close as superseded. Tests rewritten for the factory shape: queuing/validation, the connect-path wiring (factories invoked with (app, adapter)), and isolation (load failure + a raising factory don't block others). Also fixes a doc bug carried over from NousResearch#59159: the Notes bullet advertised an adapter.bot attribute that doesn't exist (only self._bot); corrected to point plugin authors at application.bot (PTB). Ran code-review (high) + simplify before pushing.
Summary
Plugins can now attach their own python-telegram-bot handlers to the running Telegram adapter via
ctx.register_telegram_handler(factory)— the Telegram counterpart of the existingregister_slack_action_handlersurface.This unblocks standalone plugins that need PTB update types the core adapter doesn't route (Telegram Business API secretary bots, custom inline-button callback prefixes, chat-member events) without touching core files. It is the enabling hook for shipping Telegram Business Mode (#30055 and its duplicate cluster #26654 / #35342 / #46728) as a standalone plugin instead of 2,000+ LOC in the core adapter.
How it works
register()time; the manager stores(factory, plugin_name).connect()time, right after the PTBApplicationis built and before the core handlers register,TelegramAdapter._wire_plugin_handlers()invokes each factory with(application, adapter).CallbackQueryHandler(..., pattern=r"^bd:")) takes precedence for its own updates while everything else falls through to the core handlers unchanged.telegram/telegram.extlazily inside the factory body, so pluginregister()works when PTB isn't installed.discover_and_load(force=True)clears queued factories like every other plugin registration.Changes
hermes_cli/plugins.py:PluginContext.register_telegram_handler(), manager-side queue +get_telegram_handler_factories()accessor, force-rediscovery clear.plugins/platforms/telegram/adapter.py:_wire_plugin_handlers()invoked inconnect()before core handler registration.tests/gateway/test_telegram_plugin_handlers.py(new): 9 tests — validation, queuing, accessor copy semantics, multi-plugin ordering, force-clear, factory invocation args, fault isolation, manager-load failure.website/docs/guides/build-a-hermes-plugin.md: "Register Telegram (PTB) handlers" section alongside the Slack one.Validation
tests/gateway/test_telegram_plugin_handlers.pytests/hermes_cli/test_plugins.py+ Slack action handlers + Telegram approval buttons + thread fallbackInfographic