diff --git a/bot.py b/bot.py index bd61372..fdd8fd6 100644 --- a/bot.py +++ b/bot.py @@ -8,7 +8,7 @@ from pathlib import Path from telegram import InputFile, Update from telegram.constants import ChatAction, ChatType -from telegram.error import BadRequest, TelegramError +from telegram.error import BadRequest, NetworkError, TelegramError, TimedOut from telegram.ext import ( Application, CommandHandler, @@ -47,11 +47,11 @@ class MpakoBot: builder = ( Application.builder() .token(self._settings.bot_token) - .connect_timeout(10) - .read_timeout(30) - .write_timeout(30) - .get_updates_connect_timeout(10) - .get_updates_read_timeout(30) + .connect_timeout(20) + .read_timeout(60) + .write_timeout(60) + .get_updates_connect_timeout(20) + .get_updates_read_timeout(60) .post_init(self._post_init) .post_shutdown(self._post_shutdown) ) @@ -251,7 +251,15 @@ class MpakoBot: await message.reply_text("Не удалось поставить задачу. Попробуй позже.") return False - status_message = await message.reply_text(self._ack_text(position)) + try: + status_message = await self._retry( + message.reply_text, self._ack_text(position) + ) + except TelegramError: + logger.exception( + "Не удалось отправить подтверждение по запросу %s", request.id + ) + return True self._status_messages[request.id] = ( status_message.chat_id, status_message.message_id, @@ -303,6 +311,23 @@ class MpakoBot: event.error or "Не удалось сгенерировать изображение.", ) + async def _retry(self, func, *args, attempts: int = 3, **kwargs): + delay = 1.0 + for attempt in range(1, attempts + 1): + try: + return await func(*args, **kwargs) + except (TimedOut, NetworkError): + if attempt >= attempts: + raise + logger.warning( + "Telegram timeout (%s/%s), повтор через %.0f с", + attempt, + attempts, + delay, + ) + await asyncio.sleep(delay) + delay *= 2 + async def _edit_status( self, request_id: int, @@ -328,7 +353,8 @@ class MpakoBot: chat_id, message_id = target try: - await self._bot.edit_message_text( + await self._retry( + self._bot.edit_message_text, chat_id=chat_id, message_id=message_id, text=text, @@ -366,22 +392,26 @@ class MpakoBot: await self._finish_failed(request, "Файл результата не читается.") return try: - await self._bot.send_photo( + await self._retry( + self._bot.send_photo, chat_id=chat_id, photo=InputFile(content, filename=filename), caption=caption, read_timeout=120, write_timeout=120, + attempts=2, ) except TelegramError: logger.exception("Не удалось отправить фото по запросу %s", request.id) try: - await self._bot.send_document( + await self._retry( + self._bot.send_document, chat_id=chat_id, document=InputFile(content, filename=filename), caption=caption, read_timeout=120, write_timeout=120, + attempts=2, ) except TelegramError: await self._finish_failed( @@ -397,7 +427,8 @@ class MpakoBot: text = f"Не получилось: {error}" if target is not None and self._bot is not None: try: - await self._bot.edit_message_text( + await self._retry( + self._bot.edit_message_text, chat_id=target[0], message_id=target[1], text=text, @@ -406,7 +437,9 @@ class MpakoBot: logger.debug("Не удалось показать ошибку по запросу %s", request.id) if self._bot is not None: try: - await self._bot.send_message(chat_id=chat_id, text=text) + await self._retry( + self._bot.send_message, chat_id=chat_id, text=text + ) except TelegramError: logger.exception( "Не удалось отправить ошибку по запросу %s", request.id @@ -416,7 +449,9 @@ class MpakoBot: if self._bot is not None: try: - await self._bot.send_message(chat_id=chat_id, text=text) + await self._retry( + self._bot.send_message, chat_id=chat_id, text=text + ) except TelegramError: logger.exception("Не удалось отправить ошибку по запросу %s", request.id) @@ -425,8 +460,11 @@ class MpakoBot: self._last_edit.pop(request_id, None) if delete and target is not None and self._bot is not None: try: - await self._bot.delete_message( - chat_id=target[0], message_id=target[1] + await self._retry( + self._bot.delete_message, + chat_id=target[0], + message_id=target[1], + attempts=2, ) except TelegramError: logger.debug("Не удалось удалить статус %s", request_id)