238 lines
8.8 KiB
Python
238 lines
8.8 KiB
Python
"""
|
||
Сервис для отправки сообщений
|
||
"""
|
||
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
|