fix: push the final text into the draft before sending it as a message

Драфт с последней фразой висел в чате ещё несколько секунд после того, как
уже ушло сообщение «✅ Обучение завершено»: содержимое драфта не совпадало
с финальным сообщением, и клиент не мог их слить.

Финальный текст теперь сначала пушится в драфт тем же draft_id, и только
потом уходит реальным сообщением с тем же текстом — драфт заменяется без
задержки. finish() без текста гасит драфт пустым пушем.

Проверено: 198 тестов OK (2 новых на совпадение текста драфта и сообщения).

VERSION: 0.1.0+50 -> 0.1.0+51
This commit is contained in:
admin committed 2026-10-11 15:34:15 +02:00
1 parent 635b78cb86
commit 7a630a974f
5 files changed
+69 -13

No files matched your search

+29 -5
View File
@@ -896,18 +896,22 @@ class OperationStatus:
async def finish(self, text: str | None = None) -> None:
"""Close the status out.
A live draft only disappears once the bot sends a real message, so the
final text must always go out through ``sendMessage`` — otherwise the
last activity phrase keeps hanging in the chat for the rest of the
draft's TTL.
A live draft only disappears once the bot sends a real message. Its
content must match that message, otherwise the client cannot merge
them: the draft keeps floating in the chat for a few more seconds
while already being stale. So the final text is pushed into the draft
first, then sent as a real message with the same text.
"""
await self.cancel()
self._closed = True
if text is None:
await self._clear_draft()
return
self._last_text = text
if self.message is not None and self._finished_draft_id is not None:
# Draft path: a real message replaces the draft in the chat.
# Bring the draft to the final text so the real message replaces it
# instead of racing a stale one.
await self._push_draft(self._finished_draft_id, text)
self.draft_id = None
try:
await self.message.answer(text)
@@ -916,6 +920,26 @@ class OperationStatus:
return
await self.update(text)
async def _push_draft(self, draft_id: int, text: str) -> None:
message = self.message
if message is None or message.bot is None:
return
try:
await message.bot.send_message_draft(
chat_id=message.chat.id,
draft_id=draft_id,
text=text,
)
except Exception as exc:
logger.debug("Draft push failed (%s): %s", type(exc).__name__, str(exc)[:120])
async def _clear_draft(self) -> None:
"""Drop the draft without leaving any text behind."""
draft_id = self._finished_draft_id
if draft_id is None:
return
await self._push_draft(draft_id, "")
async def finish_if_open(self, text: str) -> None:
"""Safety net: close the status only if nothing has closed it yet."""
if not self._closed: