Skip to content

fix(telegram): escape MarkdownV2 reserved parens in chunk indicators (#74004) - #74028

Open
kyssta-exe wants to merge 1 commit into
NousResearch:mainfrom
kyssta-exe:auto-fix/issue-74004
Open

fix(telegram): escape MarkdownV2 reserved parens in chunk indicators (#74004)#74028
kyssta-exe wants to merge 1 commit into
NousResearch:mainfrom
kyssta-exe:auto-fix/issue-74004

Conversation

@kyssta-exe

Copy link
Copy Markdown
Contributor

Problem

When a Telegram message is sent via the standalone _send_telegram() path and is long enough to be chunked (>4096 UTF-16 units), BasePlatformAdapter.truncate_message() appends chunk indicators like (1/3) to each chunk. These parentheses are reserved characters in Telegram's MarkdownV2 format and cause the API to reject the message, falling back to plain text — silently stripping all formatting.

Fix

After chunking, escape the parentheses in chunk indicators when the parse mode is MarkdownV2. The pattern r' (\d+)/(\d+)\)$' targets only the (N/M) suffix appended by truncate_message() at the end of each chunk and wraps the parentheses with MarkdownV2 escapes (\(, \)).

Testing

The fix was verified with a Python unit test:

  • Input: **bold** message (1/3) → Output: **bold** message \(1/3\)
  • Multiple chunks all get proper escaping
  • Chunks without indicators are unaffected

Closes #74004

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 29, 2026
@isak-ialogics

Copy link
Copy Markdown
Contributor

One remaining parity gap with the live adapter: this copies the indicator escaping but omits _separate_chunk_indicator_from_fence(). For a >4096-character fenced block, truncate_message() closes each chunk as (1/2)``; this PR turns that into (1/2)`` on the same line, while the live adapter moves the marker to the next line because Telegram does not treat the former as a clean closing fence. I reproduced the branch transformation yielding \n``` \\(1/2\\) versus the live path's `\n```\n\(1/2\)`. Could you apply the same helper here and add a behavior regression in `tests/tools/test_send_message_tool.py` using a long fenced MarkdownV2 message? (The current PR changes only the implementation, despite the body mentioning a unit test.)

@Adolanium

Copy link
Copy Markdown
Contributor

The fix works for the main case, and the approach checks out: this is the same regex the gateway adapter already uses for this exact bug (plugins/platforms/telegram/adapter.py, the send path around line 4372).

Two things before this merges:

  1. The gateway version does one more step this PR misses: _separate_chunk_indicator_from_fence(). When a chunk boundary lands inside a fenced code block, truncate_message closes the fence and the indicator ends up on the same line, like ``` (1/2). Escaped or not, Telegram rejects that as a closing fence, so the chunk still falls back to plain text. Any long message with a code block over 4096 units hits this, which is the same silent formatting loss this PR is fixing. Worth porting that helper too. Since _send_telegram already imports from plugins.platforms.telegram.adapter in this function, maybe just reuse it instead of duplicating the regex.

  2. The PR body mentions a unit test but no test file is included. The repo asks for tests with bug fixes, and this one is easy to pin down: chunk a MarkdownV2 string with an indicator, assert the parens come out escaped, plus the fence case if you add it.

No regressions on my read. The change is scoped to MarkdownV2 multi-chunk sends, and a false positive can't really happen since user parens are already escaped by the time the indicator is appended. Small nit: re is already imported at the top of the file, so the local import re as _re can go.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for addressing the standalone sender path. The ordinary MarkdownV2 suffix escape is the right direction, but this needs one parity fix before it is complete.

Problems

  • The live adapter applies _separate_chunk_indicator_from_fence() after escaping its marker at plugins/platforms/telegram/adapter.py:4372-4376. This PR only applies the regex in tools/send_message_tool.py, so an oversized fenced block can still produce a closing-fence line such as ````` \(1/2\)``. The helper documents that Telegram rejects that form at plugins/platforms/telegram/adapter.py:470-480.
  • No standalone regression test is included. The existing assertion at tests/gateway/test_telegram_format.py:459-480 covers TelegramAdapter.send(), not _send_telegram().

Suggested changes

  • Reuse the adapter helper immediately after the suffix substitution, matching the live send path.
  • Add _send_telegram() coverage in tests/tools/test_send_message_tool.py for both ordinary multi-chunk markers and a split fenced block.

Automated hermes-sweeper review.

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

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram Markdown formatting silently lost on chunked messages in standalone send path (_send_telegram)

5 participants