bot prime refactor. Notification events & messages

This commit is contained in:
2025-08-03 21:42:07 +09:00
parent 842710fe5c
commit becf4f5c99
22 changed files with 431 additions and 139 deletions

View File

@@ -1,105 +1,131 @@
# notifications.py
import logging
from asgiref.sync import sync_to_async
from bot.utils import send_event_message_async
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
class NotificationService:
def __init__(self, bot):
"""
Инициализация сервиса уведомлений.
:param bot: экземпляр telegram.Bot, используемый для отправки сообщений.
"""
self.bot = bot
self.logger = logging.getLogger(__name__)
self.logger = logging.getLogger("NotificationService")
self.logger.setLevel(logging.DEBUG)
if not self.logger.handlers:
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
self.logger.addHandler(handler)
@sync_to_async
def _get_all_clients(self):
from webapp.models import Client
# Возвращаем только клиентов, у которых telegram_id заполнен корректно
return list(Client.objects.exclude(telegram_id__in=[None, "", "NULL"]))
clients = list(Client.objects.exclude(telegram_id__in=[None, "", "NULL"]))
self.logger.info(f"Найдено клиентов для уведомления: {len(clients)}")
return clients
@sync_to_async
def _prepare_results(self, results):
# Принудительно загружаем связанные объекты через select_related
return list(results.select_related('prize', 'participant__invoice'))
async def notify_draw_start(self, lottery):
"""
Уведомляет всех клиентов о запуске розыгрыша.
:param lottery: объект розыгрыша (например, модель Lottery)
"""
message_text = f"🎉 *Розыгрыш '{lottery.name}' начался!* Следите за обновлениями."
clients = await self._get_all_clients()
for client in clients:
if not client.telegram_id:
continue
try:
await self.bot.send_message(
chat_id=client.telegram_id,
text=message_text,
parse_mode="Markdown"
self.logger.debug(f"Отправка 'draw_started' клиенту {client.telegram_id}")
await send_event_message_async(
event="draw_started",
bot=self.bot,
chat_id=int(client.telegram_id),
context_vars={"lottery": lottery, "client": client}
)
except Exception as e:
self.logger.error(f"Ошибка отправки уведомления о запуске розыгрыша пользователю {client.telegram_id}: {e}")
self.logger.error(f"Ошибка отправки draw_started: {e}")
async def notify_draw_results(self, lottery, results):
"""
Отправляет результаты розыгрыша всем клиентам.
:param lottery: объект розыгрыша (Lottery)
:param results: QuerySet с результатами розыгрыша (например, объекты DrawResult)
"""
# Принудительно загружаем связанные объекты
# async def notify_draw_results(self, lottery, results):
# results_list = await self._prepare_results(results)
# clients = await self._get_all_clients()
# for client in clients:
# if not client.telegram_id:
# continue
# try:
# self.logger.debug(f"Отправка 'draw_finished' клиенту {client.telegram_id}")
# await send_event_message_async(
# event="draw_finished",
# bot=self.bot,
# chat_id=int(client.telegram_id),
# context_vars={"lottery": lottery, "client": client, "results": results_list}
# )
# except Exception as e:
# self.logger.error(f"Ошибка отправки draw_finished: {e}")
async def notify_draw_results(self, lottery, results, context_vars=None):
results_list = await self._prepare_results(results)
message_text = f"🎉 *Результаты розыгрыша '{lottery.name}':*\n"
for result in results_list:
status = "✅ Подтвержден" if result.confirmed else "В ожидании подтверждения"
prize = result.prize.reward if result.prize and hasattr(result.prize, "reward") else "неизвестно"
account = (str(result.participant.invoice).split('/')[0].strip()
if result.participant and hasattr(result.participant, "invoice") else "неизвестно")
message_text += f"• Приз: *{prize}*, Счет: _{account}_, Статус: *{status}*\n"
clients = await self._get_all_clients()
# 🔢 Подготовка суммы и списка победителей
total_reward = sum(
r.prize.reward for r in results_list if r.prize and r.prize.reward
)
winners_lines = []
for r in results_list:
try:
account_id = r.participant.invoice.ext_id
prize_amount = r.prize.reward
winners_lines.append(f"• Счёт {account_id}{prize_amount:,.2f}")
except Exception:
continue
winners_list = "\n".join(winners_lines)
for client in clients:
if not client.telegram_id:
continue
try:
await self.bot.send_message(
chat_id=client.telegram_id,
text=message_text,
parse_mode="Markdown"
self.logger.debug(f"Отправка 'draw_finished' клиенту {client.telegram_id}")
await send_event_message_async(
event="draw_finished",
bot=self.bot,
chat_id=int(client.telegram_id),
context_vars={
**(context_vars or {}),
"lottery": lottery,
"client": client,
"results": results_list,
"total_reward": f"{total_reward:,.2f}",
"winners_list": winners_list
}
)
except Exception as e:
self.logger.error(f"Ошибка отправки результатов розыгрыша пользователю {client.telegram_id}: {e}")
self.logger.error(f"Ошибка отправки draw_finished: {e}")
async def notify_prize_status_update(self, client, result):
"""
Отправляет конкретному пользователю обновление по статусу подтверждения приза.
:param client: объект клиента (Client)
:param result: объект результата розыгрыша (DrawResult)
"""
status = "✅ Подтвержден" if result.confirmed else "В ожидании подтверждения"
prize = result.prize.reward if result.prize and hasattr(result.prize, "reward") else "неизвестно"
message_text = f"🎉 *Обновление розыгрыша:*\nВаш приз *{prize}* имеет статус: *{status}*."
try:
if client.telegram_id:
await self.bot.send_message(
chat_id=client.telegram_id,
text=message_text,
parse_mode="Markdown"
self.logger.debug(f"Отправка 'winner_announced' клиенту {client.telegram_id}")
await send_event_message_async(
event="winner_announced",
bot=self.bot,
chat_id=int(client.telegram_id),
context_vars={"client": client, "result": result, "prize": result.prize}
)
except Exception as e:
self.logger.error(f"Ошибка отправки обновления статуса приза пользователю {client.telegram_id}: {e}")
self.logger.error(f"Ошибка отправки winner_announced: {e}")
async def notify_binding_complete(self, client):
"""
Уведомляет пользователя об окончании привязки клубной карты к Telegram ID.
:param client: объект клиента (Client)
"""
message_text = "✅ *Привязка завершена!* Ваша клубная карта успешно привязана к Telegram. Теперь вы можете участвовать в розыгрышах и чате."
try:
if client.telegram_id:
await self.bot.send_message(
chat_id=client.telegram_id,
text=message_text,
parse_mode="Markdown"
self.logger.debug(f"Отправка 'binding_complete' клиенту {client.telegram_id}")
await send_event_message_async(
event="binding_complete",
bot=self.bot,
chat_id=int(client.telegram_id),
context_vars={"client": client}
)
except Exception as e:
self.logger.error(f"Ошибка отправки уведомления о привязке пользователю {client.telegram_id}: {e}")
self.logger.error(f"Ошибка отправки binding_complete: {e}")