""" Telegram bot for viral clip processing. Receives YouTube URLs, processes via main.py, sends progress updates. """ import asyncio import os import re import subprocess import sys import shutil import tempfile from datetime import datetime from pathlib import Path from dotenv import load_dotenv from telegram import Update from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes load_dotenv() BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"] CHAT_ID = int(os.environ["TELEGRAM_CHAT_ID"]) NEXTCLOUD_BASE = "/home/ren/nextcloud/html/data/renato97/files/viral_clips" SCRIPT_DIR = Path(__file__).parent queue = asyncio.Queue() current_job: str | None = None async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): if update.effective_chat.id != CHAT_ID: return await update.message.reply_text( "🎬 Mandame un link de YouTube y lo proceso.\n" "Los clips aparecen en Nextcloud automáticamente." ) async def status(update: Update, context: ContextTypes.DEFAULT_TYPE): if update.effective_chat.id != CHAT_ID: return qsize = queue.qsize() if current_job: name = current_job[:80] await update.message.reply_text(f"▶️ Procesando: {name}...\n⏳ En cola: {qsize}") elif qsize > 0: await update.message.reply_text(f"⏳ En cola: {qsize}") else: await update.message.reply_text("💤 Sin actividad.") async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): if update.effective_chat.id != CHAT_ID: return text = update.message.text.strip() if not re.search(r"(youtube\.com|youtu\.be)", text): await update.message.reply_text("❌ Mandame un link de YouTube.") return qsize = queue.qsize() await queue.put(text) if qsize == 0 and not current_job: await update.message.reply_text("🚀 Iniciando procesamiento...") else: await update.message.reply_text(f"⏳ Encolado ({qsize + 1}º en cola).") def get_video_title(url: str) -> str: try: r = subprocess.run( ["yt-dlp", "--print", "title", url], capture_output=True, text=True, timeout=30, ) return r.stdout.strip() except Exception: return "video" async def process_queue(bot): global current_job while True: url = await queue.get() current_job = url try: await process_video(bot, url) except Exception as e: try: await bot.send_message(CHAT_ID, f"❌ Error procesando: {e}") except Exception: pass finally: current_job = None queue.task_done() async def process_video(bot, url: str): title = get_video_title(url) safe = re.sub(r'[<>:"/\\|?*#\s]+', "_", title)[:80] folder = f"{safe}_{datetime.now():%Y-%m-%d}" target = f"{NEXTCLOUD_BASE}/{folder}" os.makedirs(target, exist_ok=True) msg = await bot.send_message(CHAT_ID, "🚀 Descargando...") env = os.environ.copy() env["NEXTCLOUD_TARGET"] = target temp_dir = tempfile.mkdtemp(prefix="viral_") total_clips = 0 cur_clip = 0 last_text = "" try: proc = await asyncio.create_subprocess_exec( sys.executable, "-u", str(SCRIPT_DIR / "main.py"), "-u", url, "-o", temp_dir, env=env, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, ) async for raw in proc.stdout: line = raw.decode("utf-8", errors="replace").strip() if not line: continue text = None if "Transcribing" in line: text = "🎙️ Transcribiendo..." elif "Calling local LLM" in line: text = "🤖 Analizando clips virales..." elif m := re.search(r"Found\s+(\d+)\s+viral", line, re.IGNORECASE): total_clips = int(m.group(1)) text = f"🔥 Encontrados {total_clips} clips" elif m := re.search(r"\bClip\s+(\d+)\]\s+Starting", line): cur_clip = int(m.group(1)) text = f"🎬 Clip {cur_clip}/?: reencuadrando + subs..." elif "Normalizing" in line and cur_clip > 0: text = f"📱 Clip {cur_clip}/{max(total_clips, cur_clip)}: normalizando..." elif "Copying to Nextcloud" in line and cur_clip > 0: text = f"📤 Clip {cur_clip}/{max(total_clips, cur_clip)}: subiendo a Nextcloud..." if text and text != last_text: last_text = text try: await msg.edit_text(text) except Exception: pass await proc.wait() finally: shutil.rmtree(temp_dir, ignore_errors=True) try: await msg.delete() except Exception: pass async def post_init(app: Application): asyncio.create_task(process_queue(app.bot)) def main(): app = ( Application.builder() .token(BOT_TOKEN) .post_init(post_init) .build() ) app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("status", status)) app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) print("🤖 Viral Telegram Bot iniciado.") app.run_polling(allowed_updates=Update.ALL_TYPES) if __name__ == "__main__": main()