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:
2025-09-06 18:35:12 +03:00
parent 50be010026
commit 596a2fa813
111 changed files with 16847 additions and 65 deletions

View File

@@ -0,0 +1,237 @@
"""
Сервис для отправки сообщений
"""
from typing import Optional, Union
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, ReplyKeyboardMarkup
from aiogram import Bot
from services.infrastructure.logger import get_logger
from services.rate_limiting.rate_limit_service import RateLimitService
from services.infrastructure.logging_decorators import log_function_call, log_business_event
logger = get_logger(__name__)
class MessageService:
"""Сервис для отправки сообщений"""
def __init__(self, rate_limit_service: Optional[RateLimitService] = None):
self.rate_limit_service = rate_limit_service
@log_business_event("send_message", log_params=True, log_result=True)
async def send_message(
self,
target: Union[Message, CallbackQuery, Bot],
text: str,
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
parse_mode: str = "HTML"
) -> bool:
"""
Отправка сообщения
Args:
target: Целевой объект (Message, CallbackQuery или Bot)
text: Текст сообщения
reply_markup: Клавиатура
parse_mode: Режим парсинга
Returns:
True если успешно отправлено
"""
try:
logger.info(f"📤 Отправка сообщения с клавиатурой: {type(reply_markup).__name__ if reply_markup else 'None'}")
if isinstance(target, Message):
await target.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
elif isinstance(target, CallbackQuery):
await target.message.answer(text, reply_markup=reply_markup, parse_mode=parse_mode)
elif isinstance(target, Bot):
# Для Bot нужен chat_id, который должен быть передан отдельно
logger.error("Для Bot нужен chat_id")
return False
return True
except Exception as e:
logger.error(f"Ошибка при отправке сообщения: {e}")
return False
@log_business_event("edit_message", log_params=True, log_result=True)
async def edit_message(
self,
target: Union[Message, CallbackQuery],
text: str,
reply_markup: Optional[InlineKeyboardMarkup] = None,
parse_mode: str = "HTML"
) -> bool:
"""
Редактирование сообщения
Args:
target: Целевой объект (Message или CallbackQuery)
text: Новый текст сообщения
reply_markup: Новая клавиатура
parse_mode: Режим парсинга
Returns:
True если успешно отредактировано
"""
try:
if isinstance(target, Message):
await target.edit_text(text, reply_markup=reply_markup, parse_mode=parse_mode)
elif isinstance(target, CallbackQuery):
await target.message.edit_text(text, reply_markup=reply_markup, parse_mode=parse_mode)
return True
except Exception as e:
logger.error(f"Ошибка при редактировании сообщения: {e}")
return False
@log_function_call(log_params=True, log_result=True)
async def send_callback_answer(
self,
callback: CallbackQuery,
text: Optional[str] = None,
show_alert: bool = False
) -> bool:
"""
Отправка ответа на callback query
Args:
callback: CallbackQuery объект
text: Текст ответа
show_alert: Показывать ли alert
Returns:
True если успешно отправлено
"""
try:
await callback.answer(text, show_alert=show_alert)
return True
except Exception as e:
logger.error(f"Ошибка при отправке callback answer: {e}")
return False
@log_business_event("send_bot_message", log_params=True, log_result=True)
async def send_bot_message(
self,
bot: Bot,
chat_id: int,
text: str,
reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
parse_mode: str = "HTML"
) -> bool:
"""
Отправка сообщения через бота с rate limiting
Args:
bot: Экземпляр бота
chat_id: ID чата
text: Текст сообщения
reply_markup: Клавиатура
parse_mode: Режим парсинга
Returns:
True если успешно отправлено
"""
try:
if self.rate_limit_service:
# Используем rate limiting
await self.rate_limit_service.send_with_rate_limit(
bot.send_message,
chat_id,
text=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
else:
# Отправляем без rate limiting
await bot.send_message(
chat_id=chat_id,
text=text,
reply_markup=reply_markup,
parse_mode=parse_mode
)
return True
except Exception as e:
logger.error(f"Ошибка при отправке сообщения через бота в чат {chat_id}: {e}")
return False
@log_business_event("send_notification", log_params=True, log_result=True)
async def send_notification(
self,
bot: Bot,
user_id: int,
title: str,
message: str,
reply_markup: Optional[InlineKeyboardMarkup] = None
) -> bool:
"""
Отправка уведомления пользователю с rate limiting
Args:
bot: Экземпляр бота
user_id: ID пользователя
title: Заголовок уведомления
message: Текст уведомления
reply_markup: Клавиатура
Returns:
True если успешно отправлено
"""
try:
notification_text = f"🔔 <b>{title}</b>\n\n{message}"
if self.rate_limit_service:
# Используем rate limiting
await self.rate_limit_service.send_with_rate_limit(
bot.send_message,
user_id,
text=notification_text,
reply_markup=reply_markup,
parse_mode="HTML"
)
else:
# Отправляем без rate limiting
await bot.send_message(
chat_id=user_id,
text=notification_text,
reply_markup=reply_markup,
parse_mode="HTML"
)
logger.info(f"Уведомление отправлено пользователю {user_id}")
return True
except Exception as e:
logger.error(f"Ошибка при отправке уведомления пользователю {user_id}: {e}")
return False
@log_business_event("send_error_message", log_params=True, log_result=True)
async def send_error_message(
self,
target: Union[Message, CallbackQuery],
error_text: str = "❌ Произошла ошибка. Попробуйте позже."
) -> bool:
"""
Отправка сообщения об ошибке
Args:
target: Целевой объект
error_text: Текст ошибки
Returns:
True если успешно отправлено
"""
try:
if isinstance(target, CallbackQuery):
await target.answer(error_text, show_alert=True)
else:
await self.send_message(target, error_text)
return True
except Exception as e:
logger.error(f"Ошибка при отправке сообщения об ошибке: {e}")
return False