Внимание! Пакеты
python-telegram-bot
версии 13.x будут придерживаться многопоточной парадигмы программирования (*на данный момент актуальна версия 13.15). Пакеты версий 20.x и новее предоставляют чистый асинхронный Python интерфейс для Telegram Bot API. Дополнительно смотрите основные изменения в пакетеpython-telegram-bot
версии 20.x.
В python-telegram-bot
все ошибки, связанные с Telegram, инкапсулируются в класс исключения TelegramError
и его подклассы, расположенные в модуле telegram.error
.
Любая ошибка, включая TelegramError
, которая возникает в одном из обработчиков сообщений или при вызове Updater.get_updates()
, перенаправляется всем зарегистрированным обработчикам ошибок, чтобы можно было на них отреагировать.
Что бы зарегистрировать обработчик ошибок, необходимо вызвать Dispatcher.add_error_handler(callback)
, где обратный вызов callback
- это функция, которая принимает обновление update
и контекст context
. Объект update
будет обновлением, вызвавшим ошибку (или None
, если ошибка не была вызвана update
, например, для Jobs
), а context.error
- возникшей ошибкой.
Пример: пытаемся отправить сообщение, но пользователь заблокировал бота. Будет вызвано исключение Unauthorized
, подкласса TelegramError
, которое будет доставлено обработчику ошибок. В обработчике ошибок можно удалить этот контакт из списка контактов бота.
Примечание. Можно обрабатывать исключения по мере их возникновения. Обработчику ошибок python-telegram-bot
перенаправляются только неперехваченные исключения.
Очень простой пример того, как можно реализовать собственный обработчик ошибок.
import html import json import logging import traceback from telegram import Update, ParseMode from telegram.ext import Updater, CommandHandler logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) # Токен, который получен от @botfather при создании бота BOT_TOKEN = "TOKEN" # Это может быть ваш собственный идентификатор # или идентификатор группы/канала. # Можно использовать команду `/start` этого бота, # чтобы увидеть свой идентификатор чата. CHAT_ID = 123456789 # Функция-обработчик ошибок def error_handler(update, context): """ Регистрирует ошибку и уведомляет разработчика сообщением telegram. """ # Пишем ошибку, прежде чем что-то делать. Вдруг что-то сломается. logger.error(msg="Исключение при обработке сообщения:", exc_info=context.error) # `traceback.format_exception` возвращает обычное сообщение python # об исключении в виде списка строк, поэтому объединяем их вместе. tb_list = traceback.format_exception(None, context.error, context.error.__traceback__) tb_string = ''.join(tb_list) # Создаем сообщение с некоторой разметкой и дополнительной # информацией о том, что произошло. Возможно, придется добавить некоторую # логику для работы с сообщениями длиной более 4096 символов. update_str = update.to_dict() if isinstance(update, Update) else str(update) message = ( f'Возникло исключение при обработке сообщения.\n' f'<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}' '</pre>\n\n' f'<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n\n' f'<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n\n' f'<pre>{html.escape(tb_string)}</pre>' ) # Отправляем сообщение разработчику context.bot.send_message(chat_id=CHAT_ID, text=message, parse_mode=ParseMode.HTML) # объект `update` в функции не используется, заменяем его на `_` def bad_command(_, context): """Вызывает ошибку, чтобы вызвать обработчик ошибок.""" context.bot.wrong_method_name() # объект `context` в функции не используется, заменяем его на `_` def start(update, _): update.effective_message.reply_html( 'Принудительный вызов ошибки `/bad_command`\n' f'Ваш идентификатор чата <code>{update.effective_chat.id}</code>.' ) if __name__ == '__main__': updater = Updater(BOT_TOKEN) dispatcher = updater.dispatcher # Зарегистрируем команды... dispatcher.add_handler(CommandHandler('start', start)) dispatcher.add_handler(CommandHandler('bad_command', bad_command)) # ...и обработчик ошибок dispatcher.add_error_handler(error_handler) # Запускаем бота updater.start_polling() updater.idle()
Следующий фрагмент кода уведомляет пользователя, когда происходит ошибка, и уведомляет разработчиков об ошибке, включая трассировку и место ее возникновения. Комментарии в коде объясняют, что именно происходит, когда и почему, поэтому редактировать его в соответствии с особыми потребностями должно быть проще простого.
from telegram import ParseMode from telegram.utils.helpers import mention_html import sys import traceback # это общая функция обработчика ошибок. # Если нужна дополнительная информация о конкретном типе сообщения, # добавьте ее в полезную нагрузку в соответствующем предложении `if ...` def error(update, context): # добавьте все идентификаторы разработчиков в этот список. # Можно добавить идентификаторы каналов или групп. devs = [208589966] # Уведомление пользователя об этой проблеме. # Уведомления будут работать, только если сообщение НЕ является # обратным вызовом, встроенным запросом или обновлением опроса. # В случае, если это необходимо, то имейте в виду, что отправка # сообщения может потерпеть неудачу if update.effective_message: text = "К сожалению произошла ошибка в момент обработки сообщения. " \ "Мы уже работаем над этой проблемой." update.effective_message.reply_text(text) # Трассировка создается из `sys.exc_info`, которая возвращается в # как третье значение возвращаемого кортежа. Затем используется # `traceback.format_tb`, для получения `traceback` в виде строки. trace = "".join(traceback.format_tb(sys.exc_info()[2])) # попробуем получить как можно больше информации из обновления telegram payload = [] # обычно всегда есть пользователь. Если нет, то это # либо канал, либо обновление опроса. if update.effective_user: bad_user = mention_html(update.effective_user.id, update.effective_user.first_name) payload.append(f' с пользователем {bad_user}') # есть ситуаций, когда что то с чатом if update.effective_chat: payload.append(f' внутри чата <i>{update.effective_chat.title}</i>') if update.effective_chat.username: payload.append(f' (@{update.effective_chat.username})') # полезная нагрузка - опрос if update.poll: payload.append(f' с id опроса {update.poll.id}.') # Поместим это в 'хорошо' отформатированный текст text = f"Ошибка <code>{context.error}</code> случилась{''.join(payload)}. " \ f"Полная трассировка:\n\n<code>{trace}</code>" # и отправляем все разработчикам for dev_id in devs: context.bot.send_message(dev_id, text, parse_mode=ParseMode.HTML) # Необходимо снова вызывать ошибку, для того, чтобы модуль `logger` ее записал. # Если вы не используете этот модуль, то самое время задуматься. raise