Implement user-specific question numbering and update database schema. Added triggers for automatic question numbering and adjustments upon deletion. Enhanced CRUD operations to manage user_question_number effectively.
This commit is contained in:
217
handlers/errors.py
Normal file
217
handlers/errors.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
Глобальная обработка ошибок
|
||||
"""
|
||||
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}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user