06-sep: [videoclub] flujo unificado torrents/bajar en novedades+pelicula+serie, sin URLs, /limpiar borra todo el chat (IDD+OpenCode), parental CAROLINA con fallback MAC en vivo

This commit is contained in:
juanjo
2026-09-06 12:21:19 +02:00
parent c82737cb13
commit bdc6cf8f48
7 changed files with 591 additions and 26 deletions

View File

@@ -19,6 +19,7 @@ temporizadores_parental = {} # alias normalizado -> Timer de desbloqueo
menu_pendiente = {} # chat_id -> accion de texto libre esperada (menu botones)
menu_pausa_nombre = {} # chat_id -> nombre elegido en flujo de pausa
historial_chat = {} # chat_id -> [message_id...] enviados por el bot (para /limpiar)
entrantes_chat = {} # chat_id -> [message_id...] enviados por el usuario (para /limpiar)
borrar_pend = {} # chat_id -> lista de tareas NAS esperando numero a borrar
BASE = os.path.dirname(os.path.abspath(__file__))
@@ -278,8 +279,10 @@ def borrar(chat_id, message_id):
def limpiar_pantalla(chat_id):
"""Borra los mensajes que el bot ha enviado en este chat (/limpiar)."""
"""Borra TODO el chat visible: mensajes del bot y del usuario (/limpiar).
Limite de Telegram: solo mensajes de menos de 48h se pueden borrar."""
lista = historial_chat.pop(chat_id, None) or []
lista += entrantes_chat.pop(chat_id, None) or []
borrados = 0
for mid in reversed(lista):
try:
@@ -292,7 +295,7 @@ def limpiar_pantalla(chat_id):
borrados += 1
except Exception:
pass
aviso = enviar(f"🧹 Pantalla limpia ({borrados} mensajes borrados).", chat_id)
aviso = enviar(f"🧹 Chat limpio ({borrados} mensajes borrados).", chat_id)
if aviso:
threading.Timer(6.0, borrar, args=(chat_id, aviso)).start()
@@ -943,6 +946,8 @@ def procesar_buscador_pelicula(nombre, chat_id):
for m in mensajes:
enviar(m, chat_id)
if resultados:
ultima_titulos[chat_id] = {"query": nombre, "results": resultados,
"origen": "catalogo"}
log(f"BUSCADOR pelis '{nombre}': {len(resultados)} resultados")
@@ -967,6 +972,9 @@ def procesar_buscador_serie(nombre, chat_id):
enviar(m, chat_id)
if grupos:
total_eps = sum(len(v) for v in grupos.values())
ultima_titulos[chat_id] = {"query": nombre,
"results": vcb.aplanar_series(grupos),
"origen": "catalogo"}
log(f"BUSCADOR series '{nombre}': {len(grupos)} series, {total_eps} capitulos")
@@ -989,6 +997,10 @@ def procesar_novedades(chat_id):
return
for m in mensajes:
enviar(m, chat_id)
if items:
ultima_titulos[chat_id] = {"query": "novedades", "results": items,
"origen": "catalogo"}
log(f"NOVEDADES: {len(items)} items")
def _int_arg(texto):
@@ -1071,11 +1083,31 @@ def _mostrar_catalogo_genero(chat_id, tipo, genero, categoria):
def procesar_torrents(n, chat_id):
"""Muestra los torrents (Torrentio) del titulo numero n de la ultima busqueda."""
"""Muestra las opciones de descarga del numero n de la ultima busqueda
(/busca, /pelicula, /serie, /novedades o /genero)."""
estado = ultima_titulos.get(chat_id)
if not estado or n is None or n < 1 or n > len(estado["results"]):
enviar("Primero usa <b>/busca &lt;nombre&gt;</b> y luego "
"<b>/torrents &lt;numero&gt;</b>.", chat_id)
enviar("Primero usa <b>/busca</b>, <b>/pelicula</b>, <b>/serie</b>, "
"<b>/novedades</b> o <b>/genero</b> y luego "
"<b>/torrents &lt;numero&gt;</b>.", chat_id)
return
r = estado["results"][n - 1]
if not r.get("imdb_id") and r.get("enlace"):
# Item de catalogo: ya trae enlace directo, sin paso por Torrentio.
nombre = r.get("clean") or r.get("name") or "titulo"
ultimos_torrents[chat_id] = {
"titulo": nombre,
"torrents": [{"filename": nombre, "calidad": "directo",
"size": None, "enlace": r["enlace"]}],
}
enviar(f"🧲 <b>{nombre}</b> — enlace directo disponible:\n"
"<b>1.</b> Descarga directa al NAS\n\n"
"Responde <b>/bajar 1</b> para descargarlo.", chat_id)
log(f"TORRENTS catalogo '{nombre}': enlace directo")
return
if not r.get("imdb_id"):
enviar("❌ Ese resultado no tiene enlace de descarga. "
"Prueba con <b>/busca &lt;nombre&gt;</b>.", chat_id)
return
try:
gol = _import_global_buscar()
@@ -1083,7 +1115,6 @@ def procesar_torrents(n, chat_id):
log(f"global_buscar import error: {e}")
enviar("❌ No se pudo cargar el modulo", chat_id)
return
r = estado["results"][n - 1]
enviar(f"🧲 Buscando torrents de <b>{r['name']}</b>...", chat_id)
try:
torrents = gol.torrents_de(r["imdb_id"], r["type"])
@@ -1104,8 +1135,25 @@ def procesar_bajar(n, chat_id):
"""Descarga el torrent numero n al NAS (Download Station via magnet)."""
estado = ultimos_torrents.get(chat_id)
if not estado or n is None or n < 1 or n > len(estado["torrents"]):
enviar("Primero usa <b>/busca &lt;nombre&gt;</b> y luego "
"<b>/torrents &lt;numero&gt;</b>.", chat_id)
enviar("Primero usa <b>/busca</b>, <b>/pelicula</b>, <b>/serie</b>, "
"<b>/novedades</b> o <b>/genero</b>, luego "
"<b>/torrents &lt;numero&gt;</b>.", chat_id)
return
t = estado["torrents"][n - 1]
if t.get("enlace") and not t.get("info_hash"):
# Enlace directo de catalogo: al NAS sin magnet.
try:
import nas_dl
ok = nas_dl.add_torrent(t["enlace"])
except Exception as e:
log(f"bajar error: {e}")
enviar(f"❌ Error al anadir al NAS: {e}", chat_id)
return
if ok:
enviar(f"📥 Añadido al NAS: <b>{t.get('filename') or estado['titulo']}</b>.",
chat_id)
else:
enviar("❌ No se pudo añadir al NAS.", chat_id)
return
try:
gol = _import_global_buscar()
@@ -1113,7 +1161,6 @@ def procesar_bajar(n, chat_id):
log(f"global_buscar import error: {e}")
enviar("❌ No se pudo cargar el modulo", chat_id)
return
t = estado["torrents"][n - 1]
magnet = gol.magnet_de(t)
log(f"BAJAR '{estado['titulo']}' torrent {n}: {magnet[:60]}...")
try:
@@ -1167,6 +1214,11 @@ def _atender_update(upd):
return
texto = msg.get("text")
chat_id = msg["chat"]["id"]
if msg.get("message_id"):
lista = entrantes_chat.setdefault(chat_id, [])
lista.append(msg["message_id"])
if len(lista) > 300:
entrantes_chat[chat_id] = lista[-300:]
if texto:
procesar(texto, chat_id)
except Exception as e: