fix(telegram): handle channel senders and fix guest reply ordering - #49116
Closed
elphamale wants to merge 6 commits into
Closed
fix(telegram): handle channel senders and fix guest reply ordering#49116elphamale wants to merge 6 commits into
elphamale wants to merge 6 commits into
Conversation
…member chats) PTB 22.6 doesn't natively support the `guest_message` update type or `answerGuestQuery` method introduced in Telegram Bot API 10.0. This patch adds full guest bot support as a backward-compatible layer on top of the existing adapter. Changes: - Register a `TypeHandler(Update, ...)` in group=1 to intercept `guest_message` updates, which PTB passes through via `update.api_kwargs` since they're unknown to its typed layer. - Add `"guest_message"` to `allowed_updates` in all three start-polling / webhook paths so Telegram actually delivers the updates. - Parse the raw `guest_message` payload via `Message.de_json()` and route it through the existing text-processing pipeline. - Store the `guest_query_id` per chat in `_pending_guest_queries` and mark the chat in `_guest_only_chats` for the duration of the request. - In `send()`, `send_draft()`, and `send_or_update_status()`, silently buffer/suppress all outgoing content for guest chats rather than attempting `sendMessage` (which would fail — the bot is not a member). `send()` keeps only the most recent write (final answer overwrites any earlier thinking/tool-progress text). - In `on_processing_complete()`, flush the buffered reply via `answerGuestQuery` using an `InlineQueryResultArticle`-shaped payload, then clean up all per-chat guest state. Flushing at completion means the user receives the full, coherent final answer rather than a mid-processing fragment. The Telegram API imposes no documented timeout on `guest_query_id` (unlike `answerInlineQuery`), so deferral until completion is safe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… tests Fix a stale-state bug: when _should_process_message() rejects a guest message, _pending_guest_queries was popped but _guest_only_chats was not discarded. Any subsequent send() to that chat (e.g. after the bot joins the group later) would be silently suppressed forever. Add 12 unit tests covering the four scenarios raised in PR review: - guest_query_id extraction and state setup in _handle_guest_message_update - send() buffering instead of sendMessage for guest chats - on_processing_complete answerGuestQuery flush and full state cleanup - denial path: both _pending_guest_queries and _guest_only_chats cleared Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In P2P private chats, Telegram cannot post a bot message into the conversation, so it surfaces the answerGuestQuery reply in the bot's DM thread with the mentioning user instead of the original chat. This is Telegram API behaviour; our call is identical for groups and private chats. Add a code comment so future maintainers don't mistake the private-chat routing for a bug in this implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
send_voice / send_video / send_image_file / send_document: add the same
guest-chat guard already present on send() / send_draft() /
send_or_update_status(). Without it, the adapter attempts bot.send_voice()
on a chat the bot is not a member of, getting a Telegram API rejection.
Also fix edit_message: return early on the "__no_edit__" stream_consumer
sentinel instead of crashing int("__no_edit__").
Add _resolve_workspace_path() to translate /workspace/<file> Docker container
paths to their host equivalents before os.path.exists() is called.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously send_audio/send_image/send_document/send_video returned success=True (silent no-op) when the chat was a guest chat. The agent saw "success", reported "sent silently", and the file never arrived in the group. When the user complained the agent would explain the limitation, but the initial false-success response was confusing. Now these methods return success=False with an explicit error message instructing the agent to send to the user's private DMs and notify them in the group chat. The agent can then proactively communicate the guest-mode media limitation before the user has to ask. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two related fixes for group chat message handling: 1. sender_chat identity: when a user posts "as a channel" in a group, from_user is None and sender_chat carries the channel's id/title/username. Previously this set user_id=None, silently failing the auth check. Now we fall back to sender_chat.id so allow-lists can authorize the channel and the session key is stable across messages. 2. Guest reply ordering: the Bot API 10.0 guest buffer check was placed after the rich-message fast-path, causing send() to attempt sendMessage (Forbidden for non-member bots) before falling back to answerGuestQuery. Move the guest check to the top of send() so it short-circuits before any network call is made. Follows up on NousResearch#43049 (@elphamale). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced Jun 19, 2026
Closed
elphamale
pushed a commit
to elphamale/hermes-agent
that referenced
this pull request
Jun 20, 2026
Upstream commit 5600105 moved gateway/platforms/telegram.py → plugins/platforms/telegram/adapter.py. This applies the equivalent of PRs NousResearch#43049 / NousResearch#49116 / NousResearch#49186 to the new path: - NousResearch#43049 (guest mode): _pending_guest_queries / _guest_only_chats / _guest_reply_buffer state; send() buffer block; TypeHandler registration; _handle_guest_message_update(); on_processing_complete() answerGuestQuery flush; media-method guards for send_voice / send_image_file / send_document / send_video / send_image. - NousResearch#49116 (sender_chat): _build_message_event() uses sender_chat.id / .title when from_user is None (channel-as-user posts in groups). - NousResearch#49186 (buffer quality): tool-progress drops (expect_edits/notify flags), cursor-strip before buffering, startswith-accumulation so cumulative streaming frames replace rather than double-append. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
19 tasks
Author
|
Closing as superseded by #56476. Both fixes verified present in the rebuild: (1) the guest-buffer check is ordered before the rich/legacy send paths in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #43049 — two fixes for group chat message handling discovered during testing of the guest mode feature:
Channel sender identity: when a user posts "as a channel" in a group,
from_userisNoneandsender_chatcarries the channel'sid/title/username. Previously this setuser_id=None, silently failing the auth check with no error surfaced to the user. Nowsender_chat.idis used as theuser_idfallback, so allow-lists can authorize the channel and the session key is stable across messages from the same channel.Guest reply ordering: the Bot API 10.0 guest buffer check in
send()was placed after thesendRichMessagefast-path (added in feat(telegram): implement Bot API 10.0 guest mode (@mention from non-member chats) #43049 for the legacy MarkdownV2 path). This causedsend()to attemptsendMessage— which Telegram rejects withForbidden: bot is not a memberfor non-member bots — before falling back toanswerGuestQuery. Moving the guest check to the top ofsend()short-circuits before any network call is made, eliminating the spurious delivery-failure notification.Test plan
answerGuestQuerysender_chat.idis used asuser_id, auth passes if channel ID is in the allowlist, bot respondsrich_messagesis enabled🤖 Generated with Claude Code