Files
viral/openshorts/telegram_bot.py
T

186 lines
5.4 KiB
Python

"""
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()