import os import logging from typing import Optional from telethon import TelegramClient, events from telethon.tl.types import MessageMediaPhoto, MessageMediaDocument from db.repository import Repository from core.dedup import compute_content_hash, compute_file_hash from services.ai_processor import AIProcessor from core.metrics import COLLECTED_POSTS_TOTAL from core.proxy import get_telegram_proxy logger = logging.getLogger(__name__) SESSION_DIR = os.getenv("SESSION_DIR", "/app/sessions" if os.path.exists("/app") else "/projects/telegram-bots/copykar/sessions") MEDIA_DIR = os.getenv("MEDIA_DIR", "/app/data/media" if os.path.exists("/app") else "/projects/telegram-bots/copykar/data/media") class CollectorService: def __init__( self, repo: Repository, ai_processor: AIProcessor, api_id: Optional[int] = None, api_hash: Optional[str] = None, phone: Optional[str] = None, session_name: Optional[str] = None, ): self.repo = repo self.ai_processor = ai_processor self.api_id = api_id or int(os.getenv("API_ID", "0")) self.api_hash = api_hash or os.getenv("API_HASH", "") self.phone = phone or os.getenv("PHONE") self.session_name = session_name or os.path.join(SESSION_DIR, "collector.session") os.makedirs(os.path.dirname(self.session_name), exist_ok=True) os.makedirs(MEDIA_DIR, exist_ok=True) self.client = TelegramClient(self.session_name, self.api_id, self.api_hash, proxy=get_telegram_proxy()) async def start(self): logger.info("Starting Collector Userbot...") if self.phone: await self.client.start(phone=self.phone) else: await self.client.start() logger.info("Collector Userbot connected successfully.") @self.client.on(events.NewMessage) async def on_new_message(event: events.NewMessage.Event): await self._handle_message(event) async def _handle_message(self, event: events.NewMessage.Event): try: chat_id = event.chat_id source = await self.repo.get_source_by_channel_id(chat_id) if not source or not source.is_active: return raw_text = event.raw_text or "" media_path = None media_type = None media_hash = None if event.message.media: if isinstance(event.message.media, MessageMediaPhoto): media_type = "photo" elif isinstance(event.message.media, MessageMediaDocument): media_type = "document" else: media_type = "other" filename = f"{chat_id}_{event.message.id}" download_target = os.path.join(MEDIA_DIR, filename) downloaded_file = await event.message.download_media(file=download_target) if downloaded_file: media_path = downloaded_file media_hash = compute_file_hash(downloaded_file) content_hash = compute_content_hash(raw_text, media_hash) post_id = await self.repo.create_raw_post( source_channel_id=chat_id, source_message_id=event.message.id, raw_text=raw_text, media_path=media_path, media_type=media_type, content_hash=content_hash, ) if post_id: COLLECTED_POSTS_TOTAL.labels(source_channel_id=str(chat_id)).inc() logger.info(f"Collected new post ID {post_id} from channel {chat_id}") await self.ai_processor.process_post(post_id) except Exception as e: logger.error(f"Error handling message from {event.chat_id}: {e}", exc_info=True) async def stop(self): if self.client.is_connected(): await self.client.disconnect() logger.info("Collector Userbot disconnected.")