refactor: unify Telegram delivery and add quality checks
Some checks failed
quality / test (3.10) (push) Has been cancelled
quality / test (3.12) (push) Has been cancelled

This commit is contained in:
kandrusyak
2026-07-27 17:54:25 +03:00
parent c20d893255
commit 701cdd35d2
14 changed files with 123 additions and 82 deletions

View File

@@ -22,7 +22,6 @@ from .telegram_utils import (
get_tz,
get_voice_max_duration,
markdown_code,
reply_long,
reply_markdown,
require_user_id,
)
@@ -118,7 +117,7 @@ async def history_command(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
f"{str(row['content']) if row['role'] == 'assistant' else escape_markdown_text(row['content'])}"
for row in rows
)
await reply_long(update.message, text)
await reply_markdown(update.message, text)
async def ask_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:

View File

@@ -1,7 +1,7 @@
import asyncio
import logging
import re
from collections.abc import AsyncIterator
from collections.abc import AsyncIterator, Awaitable, Callable
from contextlib import asynccontextmanager, suppress
from uuid import uuid4
from zoneinfo import ZoneInfo
@@ -174,6 +174,30 @@ def _with_markdown_notice(markdown_v2: str) -> str:
async def reply_markdown(message: Message, markdown: str) -> Message:
async def send_formatted(text: str) -> Message:
return await message.reply_text(
text,
parse_mode=ParseMode.MARKDOWN_V2,
)
async def send_plain(text: str) -> Message:
return await message.reply_text(text)
return await _send_markdown_chunks(
markdown,
send_formatted,
send_plain,
)
MarkdownSender = Callable[[str], Awaitable[Message]]
async def _send_markdown_chunks(
markdown: str,
send_formatted: MarkdownSender,
send_plain: MarkdownSender,
) -> Message:
chunks, formatting_failed = _prepare_chunks(markdown)
first_message: Message | None = None
@@ -185,17 +209,14 @@ async def reply_markdown(message: Message, markdown: str) -> Message:
else markdown_v2
)
try:
sent = await message.reply_text(
formatted_text,
parse_mode=ParseMode.MARKDOWN_V2,
)
sent = await send_formatted(formatted_text)
except BadRequest:
logger.warning("Telegram rejected MarkdownV2; retrying as plain text")
formatting_failed = True
fallback_text = (
_with_fallback_notice(plain_text) if is_last else plain_text
)
sent = await message.reply_text(fallback_text)
sent = await send_plain(fallback_text)
if first_message is None:
first_message = sent
@@ -204,38 +225,25 @@ async def reply_markdown(message: Message, markdown: str) -> Message:
async def edit_markdown(message: Message, markdown: str) -> None:
await edit_or_reply(message, markdown)
await _edit_or_reply(message, markdown)
async def send_markdown(bot: Bot, chat_id: int, markdown: str) -> Message:
chunks, formatting_failed = _prepare_chunks(markdown)
first_message: Message | None = None
for index, (markdown_v2, plain_text) in enumerate(chunks):
is_last = index == len(chunks) - 1
formatted_text = (
_with_markdown_notice(markdown_v2)
if is_last and formatting_failed
else markdown_v2
async def send_formatted(text: str) -> Message:
return await bot.send_message(
chat_id=chat_id,
text=text,
parse_mode=ParseMode.MARKDOWN_V2,
)
try:
sent = await bot.send_message(
chat_id=chat_id,
text=formatted_text,
parse_mode=ParseMode.MARKDOWN_V2,
)
except BadRequest:
logger.warning("Telegram rejected MarkdownV2; retrying as plain text")
formatting_failed = True
fallback_text = (
_with_fallback_notice(plain_text) if is_last else plain_text
)
sent = await bot.send_message(chat_id=chat_id, text=fallback_text)
if first_message is None:
first_message = sent
assert first_message is not None
return first_message
async def send_plain(text: str) -> Message:
return await bot.send_message(chat_id=chat_id, text=text)
return await _send_markdown_chunks(
markdown,
send_formatted,
send_plain,
)
def make_article(
@@ -300,11 +308,7 @@ def require_user_id(update: Update) -> int | None:
return None
async def reply_long(message: Message, text: str) -> None:
await reply_markdown(message, text)
async def edit_or_reply(message: Message, text: str) -> None:
async def _edit_or_reply(message: Message, text: str) -> None:
chunks, formatting_failed = _prepare_chunks(text)
for index, (markdown_v2, plain_text) in enumerate(chunks):