diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 8ff929961..112b232d0 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -1916,9 +1916,20 @@ class TelegramAdapter(BasePlatformAdapter): ) # 9) Convert blockquotes: > at line start → protect > from escaping + # Handle both regular blockquotes (> text) and expandable blockquotes + # (Telegram MarkdownV2: **> for expandable start, || to end the quote) + def _convert_blockquote(m): + prefix = m.group(1) # >, >>, >>>, **>, or **>> etc. + content = m.group(2) + # Check if content ends with || (expandable blockquote end marker) + # In this case, preserve the trailing || unescaped for Telegram + if prefix.startswith('**') and content.endswith('||'): + return _ph(f'{prefix} {_escape_mdv2(content[:-2])}||') + return _ph(f'{prefix} {_escape_mdv2(content)}') + text = re.sub( - r'^(>{1,3}) (.+)$', - lambda m: _ph(m.group(1) + ' ' + _escape_mdv2(m.group(2))), + r'^((?:\*\*)?>{1,3}) (.+)$', + _convert_blockquote, text, flags=re.MULTILINE, ) diff --git a/tests/gateway/test_telegram_format.py b/tests/gateway/test_telegram_format.py index 7a50aded4..1bd889b7c 100644 --- a/tests/gateway/test_telegram_format.py +++ b/tests/gateway/test_telegram_format.py @@ -408,6 +408,27 @@ class TestFormatMessageBlockquote: result = adapter.format_message("5 > 3") assert "\\>" in result + def test_expandable_blockquote(self, adapter): + """Expandable blockquote prefix **> and trailing || must NOT be escaped.""" + result = adapter.format_message("**> Hidden content||") + assert "**>" in result + assert "||" in result + assert "\\*" not in result # asterisks in prefix must not be escaped + assert "\\>" not in result # > in prefix must not be escaped + + def test_single_asterisk_gt_not_blockquote(self, adapter): + """Single asterisk before > should not be treated as blockquote prefix.""" + result = adapter.format_message("*> not a quote") + assert "\\*" in result + assert "\\>" in result + + def test_regular_blockquote_with_pipes_escaped(self, adapter): + """Regular blockquote ending with || should escape the pipes.""" + result = adapter.format_message("> not expandable||") + assert "> not expandable" in result + assert "\\|" in result + assert "\\>" not in result + # ========================================================================= # format_message - mixed/complex