fix(telegram): respect rich_messages config for pipe table routing

Remove the pipe-table bypass from _rich_delivery_enabled() so that
rich_messages: false is fully honoured.  Previously, pipe tables were
auto-routed to sendRichMessage regardless of the config flag, breaking
delivery on clients without Bot API 10.1 support (AyuGram, Telegram
Web, some desktop clients).

Fixes #53824
This commit is contained in:
liuhao1024 2026-06-28 04:49:48 +08:00 committed by Teknium
parent c084085a3e
commit 34d07732dd
2 changed files with 22 additions and 25 deletions

View file

@ -1482,11 +1482,12 @@ class TelegramAdapter(BasePlatformAdapter):
def _content_is_pipe_table_primary(self, content: str) -> bool:
"""True when pipe tables are the only rich construct in *content*.
Tables are auto-routed to ``sendRichMessage`` even when the full
``rich_messages`` opt-in is off MarkdownV2 has no table syntax and
the legacy path rewrites them into bullet lists, which reads like a
regression when users enable Telegram Topics and expect native tables.
Task lists, ``<details>``, and block math still require the full opt-in.
Used downstream to decide rendering strategy *after* the
``rich_messages`` config gate has already approved rich delivery.
MarkdownV2 has no table syntax the legacy path rewrites them into
bullet lists so callers may choose native table rendering when the
opt-in is active. Task lists, ``<details>``, and block math indicate
composite rich content and return ``False``.
"""
if not content or not any(
_TABLE_SEPARATOR_RE.match(line) for line in content.splitlines()
@ -1504,7 +1505,6 @@ class TelegramAdapter(BasePlatformAdapter):
"""Whether rich delivery is allowed for this payload."""
return bool(
getattr(self, "_rich_messages_enabled", True)
or self._content_is_pipe_table_primary(content)
)
def _rich_eligible(self, content: str) -> bool:

View file

@ -582,21 +582,20 @@ async def test_notification_opt_in_drops_disable_flag():
@pytest.mark.asyncio
async def test_table_only_uses_rich_when_rich_messages_opt_out():
"""Pipe tables auto-route to sendRichMessage even without the full opt-in."""
async def test_table_only_uses_legacy_when_rich_messages_opt_out():
"""Pipe tables respect the rich_messages: false config and stay on legacy."""
adapter = _make_adapter(extra={"rich_messages": False})
result = await adapter.send("12345", TABLE_ONLY_CONTENT)
assert result.success is True
api_kwargs = _rich_api_kwargs(adapter)
assert api_kwargs["rich_message"]["markdown"] == TABLE_ONLY_CONTENT
adapter._bot.send_message.assert_not_called()
adapter._bot.do_api_request.assert_not_called()
adapter._bot.send_message.assert_awaited()
@pytest.mark.asyncio
async def test_table_only_uses_rich_with_default_config():
"""Default config keeps task lists on legacy but upgrades bare tables."""
async def test_table_only_uses_legacy_with_default_config():
"""Default config (rich_messages unset → False) keeps tables on legacy path."""
config = PlatformConfig(enabled=True, token="fake-token")
adapter = TelegramAdapter(config)
bot = MagicMock()
@ -608,13 +607,13 @@ async def test_table_only_uses_rich_with_default_config():
result = await adapter.send("12345", TABLE_ONLY_CONTENT)
assert result.success is True
bot.do_api_request.assert_awaited_once()
bot.send_message.assert_not_called()
bot.do_api_request.assert_not_called()
bot.send_message.assert_awaited()
@pytest.mark.asyncio
async def test_dm_topic_resumed_send_uses_rich_for_table_without_reply_anchor():
"""Resumed/synthetic DM-topic sends route tables via direct_messages_topic_id."""
async def test_dm_topic_resumed_send_uses_legacy_for_table_when_opt_out():
"""Resumed DM-topic sends respect rich_messages: false for tables."""
adapter = _make_adapter(extra={"rich_messages": False})
result = await adapter.send(
@ -628,14 +627,13 @@ async def test_dm_topic_resumed_send_uses_rich_for_table_without_reply_anchor():
)
assert result.success is True
api_kwargs = _rich_api_kwargs(adapter)
assert api_kwargs["direct_messages_topic_id"] == 20189
assert "reply_parameters" not in api_kwargs
assert api_kwargs["rich_message"]["markdown"] == TABLE_ONLY_CONTENT
adapter._bot.do_api_request.assert_not_called()
adapter._bot.send_message.assert_awaited()
@pytest.mark.asyncio
async def test_finalize_edit_rich_includes_forum_topic_routing():
async def test_finalize_edit_legacy_includes_forum_topic_routing():
"""With rich_messages: false, table edits use legacy path with topic routing."""
adapter = _make_adapter(extra={"rich_messages": False})
result = await adapter.edit_message(
@ -647,9 +645,8 @@ async def test_finalize_edit_rich_includes_forum_topic_routing():
)
assert result.success is True
api_kwargs = _rich_edit_kwargs(adapter)
assert api_kwargs["message_thread_id"] == 5
assert api_kwargs["rich_message"]["markdown"] == TABLE_ONLY_CONTENT
adapter._bot.do_api_request.assert_not_called()
adapter._bot.edit_message_text.assert_awaited()
@pytest.mark.asyncio