import logging from typing import List import httpx from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings from app.models import User, Participant, Marathon logger = logging.getLogger(__name__) class TelegramNotifier: """Service for sending Telegram notifications.""" def __init__(self): self.bot_token = settings.TELEGRAM_BOT_TOKEN self.api_url = f"https://api.telegram.org/bot{self.bot_token}" async def send_message( self, chat_id: int, text: str, parse_mode: str = "HTML" ) -> bool: """Send a message to a Telegram chat.""" if not self.bot_token: logger.warning("Telegram bot token not configured") return False try: async with httpx.AsyncClient() as client: response = await client.post( f"{self.api_url}/sendMessage", json={ "chat_id": chat_id, "text": text, "parse_mode": parse_mode }, timeout=10.0 ) if response.status_code == 200: return True else: logger.error(f"Failed to send message: {response.text}") return False except Exception as e: logger.error(f"Error sending Telegram message: {e}") return False async def notify_user( self, db: AsyncSession, user_id: int, message: str ) -> bool: """Send notification to a user by user_id.""" result = await db.execute( select(User).where(User.id == user_id) ) user = result.scalar_one_or_none() if not user or not user.telegram_id: return False return await self.send_message(user.telegram_id, message) async def notify_marathon_participants( self, db: AsyncSession, marathon_id: int, message: str, exclude_user_id: int | None = None ) -> int: """Send notification to all marathon participants with linked Telegram.""" result = await db.execute( select(User) .join(Participant, Participant.user_id == User.id) .where( Participant.marathon_id == marathon_id, User.telegram_id.isnot(None) ) ) users = result.scalars().all() sent_count = 0 for user in users: if exclude_user_id and user.id == exclude_user_id: continue if await self.send_message(user.telegram_id, message): sent_count += 1 return sent_count # Notification templates async def notify_event_start( self, db: AsyncSession, marathon_id: int, event_type: str, marathon_title: str ) -> int: """Notify participants about event start.""" event_messages = { "golden_hour": f"🌟 Начался Golden Hour в «{marathon_title}»!\n\nВсе очки x1.5 в течение часа!", "jackpot": f"🎰 JACKPOT в «{marathon_title}»!\n\nОчки x3 за следующий сложный челлендж!", "double_risk": f"⚡ Double Risk в «{marathon_title}»!\n\nПоловина очков, но дропы бесплатны!", "common_enemy": f"👥 Common Enemy в «{marathon_title}»!\n\nВсе получают одинаковый челлендж. Первые 3 — бонус!", "swap": f"🔄 Swap в «{marathon_title}»!\n\nМожно поменяться заданием с другим участником!", "game_choice": f"🎲 Выбор игры в «{marathon_title}»!\n\nВыбери игру и один из 3 челленджей!" } message = event_messages.get( event_type, f"📌 Новое событие в «{marathon_title}»!" ) return await self.notify_marathon_participants(db, marathon_id, message) async def notify_event_end( self, db: AsyncSession, marathon_id: int, event_type: str, marathon_title: str ) -> int: """Notify participants about event end.""" event_names = { "golden_hour": "Golden Hour", "jackpot": "Jackpot", "double_risk": "Double Risk", "common_enemy": "Common Enemy", "swap": "Swap", "game_choice": "Выбор игры" } event_name = event_names.get(event_type, "Событие") message = f"⏰ {event_name} в «{marathon_title}» завершён" return await self.notify_marathon_participants(db, marathon_id, message) async def notify_marathon_start( self, db: AsyncSession, marathon_id: int, marathon_title: str ) -> int: """Notify participants about marathon start.""" message = ( f"🚀 Марафон «{marathon_title}» начался!\n\n" f"Время крутить колесо и получить первое задание!" ) return await self.notify_marathon_participants(db, marathon_id, message) async def notify_marathon_finish( self, db: AsyncSession, marathon_id: int, marathon_title: str ) -> int: """Notify participants about marathon finish.""" message = ( f"🏆 Марафон «{marathon_title}» завершён!\n\n" f"Зайди на сайт, чтобы увидеть итоговую таблицу!" ) return await self.notify_marathon_participants(db, marathon_id, message) async def notify_dispute_raised( self, db: AsyncSession, user_id: int, marathon_title: str, challenge_title: str ) -> bool: """Notify user about dispute raised on their assignment.""" message = ( f"⚠️ На твоё задание подан спор\n\n" f"Марафон: {marathon_title}\n" f"Задание: {challenge_title}\n\n" f"Зайди на сайт, чтобы ответить на спор." ) return await self.notify_user(db, user_id, message) async def notify_dispute_resolved( self, db: AsyncSession, user_id: int, marathon_title: str, challenge_title: str, is_valid: bool ) -> bool: """Notify user about dispute resolution.""" if is_valid: message = ( f"❌ Спор признан обоснованным\n\n" f"Марафон: {marathon_title}\n" f"Задание: {challenge_title}\n\n" f"Задание возвращено. Выполни его заново." ) else: message = ( f"✅ Спор отклонён\n\n" f"Марафон: {marathon_title}\n" f"Задание: {challenge_title}\n\n" f"Твоё выполнение засчитано!" ) return await self.notify_user(db, user_id, message) # Global instance telegram_notifier = TelegramNotifier()