""" Глобальная обработка ошибок """ 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"🐛 Ошибка отладки:\n\n" error_text += f"{type(error).__name__}: {str(error)}" 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"🐛 Ошибка отладки:\n\n" error_text += f"{type(error).__name__}: {str(error)}" 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"🚨 Ошибка в боте\n\n" error_text += f"🐛 Тип: {type(error).__name__}\n" error_text += f"📝 Сообщение: {str(error)}\n" error_text += f"👤 Пользователь: {message.from_user.id}\n" error_text += f"💬 Чат: {message.chat.id}\n" error_text += f"📅 Время: {message.date.strftime('%d.%m.%Y %H:%M:%S')}\n\n" if config.DEBUG: error_text += f"🔍 Трассировка:\n{traceback.format_exc()}" # Отправляем уведомление всем администраторам 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}")