Files
AnonBot/handlers/errors.py

218 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Глобальная обработка ошибок
"""
import traceback
from aiogram import Router
from aiogram.types import ErrorEvent, Message, CallbackQuery
from aiogram.exceptions import TelegramBadRequest, TelegramNetworkError, TelegramRetryAfter
from config import config
from services.infrastructure.logger import get_logger
from services.infrastructure.metrics import get_metrics_service
from keyboards.reply import get_main_keyboard_for_user
logger = get_logger(__name__)
router = Router()
@router.error()
async def error_handler(event: ErrorEvent):
"""Глобальный обработчик ошибок"""
error = event.exception
update = event.update
# Записываем метрику ошибки
metrics_service = get_metrics_service()
metrics_service.increment_errors(type(error).__name__, "global_handler")
# Логируем ошибку
logger.error(f"💥 Ошибка в обработчике: {error}")
logger.error(f"🔍 Тип ошибки: {type(error).__name__}")
logger.error(f"📋 Детали: {traceback.format_exc()}")
# Определяем тип обновления
if update.message:
await handle_message_error(update.message, error)
elif update.callback_query:
await handle_callback_error(update.callback_query, error)
else:
logger.error(f"❓ Неизвестный тип обновления: {update}")
async def handle_message_error(message: Message, error: Exception):
"""Обработка ошибок в сообщениях"""
try:
# Определяем тип ошибки и отправляем соответствующее сообщение
if isinstance(error, TelegramRetryAfter):
# Ошибка rate limiting
await message.answer(
f"⏳ Слишком много запросов. Попробуйте через {error.retry_after} секунд.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
elif isinstance(error, TelegramBadRequest):
# Некорректный запрос
if "message is not modified" in str(error):
# Сообщение не изменилось - это не критическая ошибка
logger.warning("Сообщение не изменилось")
elif "chat not found" in str(error):
# Чат не найден
logger.warning(f"Чат не найден: {message.chat.id}")
else:
await message.answer(
"❌ Произошла ошибка при обработке запроса. Попробуйте позже.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
elif isinstance(error, TelegramNetworkError):
# Сетевая ошибка
await message.answer(
"🌐 Проблемы с сетью. Проверьте подключение к интернету.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
elif isinstance(error, ValueError):
# Ошибка валидации
await message.answer(
"❌ Некорректные данные. Проверьте введенную информацию.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
elif isinstance(error, KeyError):
# Ошибка ключа (обычно в FSM)
await message.answer(
"❌ Ошибка состояния. Попробуйте начать заново с команды /start.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
else:
# Неизвестная ошибка
if config.DEBUG:
# В режиме отладки показываем детали ошибки
error_text = f"🐛 <b>Ошибка отладки:</b>\n\n"
error_text += f"<code>{type(error).__name__}: {str(error)}</code>"
await message.answer(
error_text,
reply_markup=get_main_keyboard_for_user(message.from_user.id),
parse_mode="HTML"
)
else:
# В продакшене показываем общее сообщение
await message.answer(
"❌ Произошла неожиданная ошибка. Попробуйте позже или обратитесь к администратору.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
# Уведомляем администраторов о критических ошибках
if not isinstance(error, (TelegramRetryAfter, TelegramBadRequest)):
await notify_admins_about_error(error, message)
except Exception as e:
# Если даже обработка ошибки не удалась
logger.critical(f"Критическая ошибка в обработчике ошибок: {e}")
try:
await message.answer(
"❌ Критическая ошибка. Бот временно недоступен.",
reply_markup=get_main_keyboard_for_user(message.from_user.id)
)
except:
pass # Если даже это не удалось, ничего не делаем
async def handle_callback_error(callback: CallbackQuery, error: Exception):
"""Обработка ошибок в callback запросах"""
try:
# Определяем тип ошибки и отправляем соответствующее сообщение
if isinstance(error, TelegramRetryAfter):
# Ошибка rate limiting
await callback.answer(
f"⏳ Слишком много запросов. Попробуйте через {error.retry_after} секунд.",
show_alert=True
)
elif isinstance(error, TelegramBadRequest):
# Некорректный запрос
if "message is not modified" in str(error):
# Сообщение не изменилось - это не критическая ошибка
logger.warning("Сообщение не изменилось в callback")
elif "query is too old" in str(error):
# Запрос слишком старый
await callback.answer(
"⏰ Запрос устарел. Обновите страницу и попробуйте снова.",
show_alert=True
)
else:
await callback.answer(
"❌ Произошла ошибка при обработке запроса.",
show_alert=True
)
elif isinstance(error, TelegramNetworkError):
# Сетевая ошибка
await callback.answer(
"🌐 Проблемы с сетью. Проверьте подключение.",
show_alert=True
)
else:
# Неизвестная ошибка
if config.DEBUG:
# В режиме отладки показываем детали ошибки
error_text = f"🐛 <b>Ошибка отладки:</b>\n\n"
error_text += f"<code>{type(error).__name__}: {str(error)}</code>"
await callback.message.edit_text(
error_text,
parse_mode="HTML"
)
else:
# В продакшене показываем общее сообщение
await callback.answer(
"❌ Произошла неожиданная ошибка.",
show_alert=True
)
# Уведомляем администраторов о критических ошибках
if not isinstance(error, (TelegramRetryAfter, TelegramBadRequest)):
await notify_admins_about_error(error, callback.message)
except Exception as e:
# Если даже обработка ошибки не удалась
logger.critical(f"Критическая ошибка в обработчике callback ошибок: {e}")
try:
await callback.answer(
"❌ Критическая ошибка.",
show_alert=True
)
except:
pass # Если даже это не удалось, ничего не делаем
async def notify_admins_about_error(error: Exception, message: Message):
"""Уведомление администраторов об ошибке"""
if not config.ADMINS:
return
try:
error_text = f"🚨 <b>Ошибка в боте</b>\n\n"
error_text += f"🐛 <b>Тип:</b> {type(error).__name__}\n"
error_text += f"📝 <b>Сообщение:</b> {str(error)}\n"
error_text += f"👤 <b>Пользователь:</b> {message.from_user.id}\n"
error_text += f"💬 <b>Чат:</b> {message.chat.id}\n"
error_text += f"📅 <b>Время:</b> {message.date.strftime('%d.%m.%Y %H:%M:%S')}\n\n"
if config.DEBUG:
error_text += f"🔍 <b>Трассировка:</b>\n<code>{traceback.format_exc()}</code>"
# Отправляем уведомление всем администраторам
from dependencies import get_message_service
message_service = get_message_service()
for admin_id in config.ADMINS:
try:
await message_service.send_bot_message(
message.bot,
admin_id,
error_text
)
except Exception as e:
logger.error(f"Не удалось отправить уведомление админу {admin_id}: {e}")
except Exception as e:
logger.error(f"Ошибка при уведомлении администраторов: {e}")