import logging from typing import Optional import aiohttp from db.config import settings logger = logging.getLogger(__name__) class TelegramNotifier: def __init__(self): self.bot_token = settings.telegram_bot_token self.chat_id = settings.telegram_chat_id self.base_url = f"https://api.telegram.org/bot{self.bot_token}" self.enabled = bool(self.bot_token and self.chat_id) async def send_contribution_notification( self, meta_id: str, title: str, meta_type: str, poster: str, uploader: str, info_hash: str, torrent_type: str, size: str, torrent_name: str, seasons_and_episodes: Optional[dict] = None, catalogs: Optional[list] = None, languages: Optional[list] = None, ): """Send notification about new contribution to Telegram channel""" if not self.enabled: logger.warning("Telegram notifications are disabled. Check bot token.") return # Create block URL - will open scraper page with block_torrent action and info hash block_url = ( f"{settings.host_url}/scraper?" f"action=block_torrent&" f"info_hash={info_hash}" ) meta_id_data = ( f"*IMDb*: [{meta_id}](https://www.imdb.com/title/{meta_id}/)\n" if meta_id.startswith("tt") else f"Meta ID: `{meta_id}`\n" ) # Build the message message = ( f"🎬 New Contribution\n\n" f"*Title*: {title}\n" f"*Type*: {meta_type.title()}\n" f"{meta_id_data}" f"*Uploader*: {uploader}\n" f"*Size*: {size}\n" f"*Torrent Name*: `{torrent_name}`\n" f"*Info Hash*: `{info_hash}`\n" f"*Type*: {torrent_type}\n" f"*Poster*: [View]({poster})\n" f"*Stremio Links*:\n" f" - *APP*: `stremio:///detail/{meta_type}/{meta_id}/{meta_id}`\n" f" - *WEB*: [View](https://web.stremio.com/#/detail/{meta_type}/{meta_id}/{meta_id})" ) if catalogs: # Escape underscores and use pipe with spaces escaped_catalogs = [cat.replace("_", "\\_") for cat in catalogs] message += f"\n*Catalogs*: {', '.join(escaped_catalogs)}" if languages: # Use pipe with spaces for languages message += f"\n*Languages*: {', '.join(languages)}" # Add season/episode info for series if meta_type == "series" and seasons_and_episodes: message += "\n*Seasons*: " for season, episodes in seasons_and_episodes.items(): message += f"\n- Season {season}: " if len(episodes) == 1: message += f"{episodes[0]}" else: message += f"{min(episodes)} - {max(episodes)}" message += "\n" # Add block link message += f"\n\n[🚫 Block/Delete Torrent]({block_url})" await self._send_photo_message(poster, message) async def send_block_notification( self, info_hash: str, action: str, meta_id: str, title: str, meta_type: str, poster: str, torrent_name: str, ): """Send notification when a torrent is blocked""" if not self.enabled: logger.warning("Telegram notifications are disabled. Check bot token.") return meta_id_data = ( f"*IMDb*: [{meta_id}](https://www.imdb.com/title/{meta_id}/)\n" if meta_id.startswith("tt") else f"Meta ID: {meta_id}\n" ) message = ( f"🚫 Torrent {'Blocked' if action == 'block' else 'Deleted'}\n\n" f"*Title*: {title}\n" f"*Type*: {meta_type.title()}\n" f"{meta_id_data}" f"*Torrent Name*: `{torrent_name}`\n" f"*Info Hash*: `{info_hash}`\n" f"*Poster*: [View]({poster})" ) await self._send_photo_message(poster, message) async def send_migration_notification( self, old_id: str, new_id: str, title: str, meta_type: str, poster: str, ): """Send notification when an ID is migrated""" if not self.enabled: logger.warning("Telegram notifications are disabled. Check bot token.") return message = ( f"🔄 ID Migration Complete\n\n" f"*Title*: {title}\n" f"*Type*: {meta_type.title()}\n" f"*Old ID*: `{old_id}`\n" f"*New IMDb ID*: [{new_id}](https://www.imdb.com/title/{new_id}/)\n" f"*Poster*: [View]({poster})" ) await self._send_photo_message(poster, message) async def send_image_update_notification( self, meta_id: str, title: str, meta_type: str, poster: str, old_poster: Optional[str] = None, new_poster: Optional[str] = None, old_background: Optional[str] = None, new_background: Optional[str] = None, old_logo: Optional[str] = None, new_logo: Optional[str] = None, ): """Send notification when images are updated""" if not self.enabled: logger.warning("Telegram notifications are disabled. Check bot token.") return meta_id_data = ( f"*IMDb*: [{meta_id}](https://www.imdb.com/title/{meta_id}/)\n" if meta_id.startswith("tt") else f"Meta ID: {meta_id}\n" ) message = ( f"🖼️ Images Updated\n\n" f"*Title*: {title}\n" f"*Type*: {meta_type.title()}\n" f"{meta_id_data}" f"*Old Poster*: [View]({old_poster})\n" f"*Old Background*: [View]({old_background})\n" f"*Old Logo*: [View]({old_logo})\n" ) # Add details about what was updated if new_poster: message += f"\n*Poster*: Updated ✅ [View]({new_poster})" if new_background: message += f"\n*Background*: Updated ✅ [View]({new_background})" if new_logo: message += f"\n*Logo*: Updated ✅ [View]({new_logo})" message += f"\n\n*Preview*: [View]({poster})" await self._send_photo_message(poster, message) async def _send_photo_message(self, photo_url: str, message: str): """Send a message with photo, falling back to text-only if photo fails""" try: async with aiohttp.ClientSession() as session: async with session.post( f"{self.base_url}/sendPhoto", json={ "chat_id": self.chat_id, "photo": photo_url, "caption": message, "parse_mode": "Markdown", }, ) as response: if not response.ok: error_data = await response.json() logger.error( f"Failed to send Telegram notification: {error_data}" ) # Fallback to text-only message if photo fails await self._send_text_only_message(message) return await response.json() except Exception as e: logger.error(f"Error sending Telegram notification: {e}") # Fallback to text-only message if there's an error await self._send_text_only_message(message) async def _send_text_only_message(self, message: str): """Fallback method to send text-only message if photo sending fails""" try: async with aiohttp.ClientSession() as session: await session.post( f"{self.base_url}/sendMessage", json={ "chat_id": self.chat_id, "text": message, "parse_mode": "Markdown", "disable_web_page_preview": False, }, ) except Exception as e: logger.error(f"Error sending fallback text message: {e}") async def send_file_annotation_request(self, info_hash: str, torrent_name: str): """Send notification to request episode annotations""" if not self.enabled: logger.warning("Telegram notifications are disabled. Check bot token.") return message = ( f"📝 Failed to identify the episodes. Require to annotate.\n\n" f"*Info Hash*: `{info_hash}`\n" f"*Torrent Name*: `{torrent_name}`\n" f"Please review and annotate the episodes manually." ) await self._send_text_only_message(message) # Create a singleton instance telegram_notifier = TelegramNotifier()