Files

905 lines
30 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""menu_idd.py - Menu de botones inline para el bot IDD (Telegram).
Capa nueva sobre pi_bot.py: los botones NO duplican logica, disparan los
mismos comandos de texto llamando a pi_bot.procesar(). Asi las integraciones
(NAS, router, camaras, TV, Tapo, parental, AiMesh, QoS, limites) siguen
exactamente igual. Se importa pi_bot de forma perezosa para evitar ciclos.
"""
import json
import sys
import time
API = "https://api.telegram.org/bot"
# Prefijo de los callback_data del menu: "m:<ruta>"
PREFIJO = "m:"
def _bot_token():
pi_bot = _pi()
return pi_bot.TOKEN
def _api(method):
return f"{API}{_bot_token()}/{method}"
def _pi():
"""Devuelve el modulo vivo de pi_bot. El bot corre como __main__, asi
que `import pi_bot` cargaria una SEGUNDA copia con estado vacio:
hay que usar sys.modules['__main__'] cuando sea el bot."""
main = sys.modules.get("__main__")
if main is not None and hasattr(main, "ultima_titulos"):
return main
import pi_bot
return pi_bot
def _log(linea):
pi_bot = _pi()
try:
pi_bot.log(f"menu: {linea}")
except Exception:
pass
def _txt(codigo, chat_id):
"""Recupera el estado de la ultima busqueda de pi_bot para este chat."""
pi_bot = _pi()
try:
if codigo == "ultimos_titulos":
estado = pi_bot.ultima_titulos.get(chat_id) or {}
return estado.get("results") or []
if codigo == "ultimos_torrents":
return pi_bot.ultimos_torrents.get(chat_id) or {}
except Exception:
return {} if codigo == "ultimos_torrents" else []
return [] if codigo == "ultimos_torrents" else {}
def _btn(texto, data):
return {"text": texto, "callback_data": data}
def _mk(filas):
return {"inline_keyboard": filas}
# ---------------------------------------------------------------- teclados --
FILA_VOLVER = [_btn("⬅️ Menú", PREFIJO + "ini")]
def teclado_principal():
return _mk([
[
_btn("🎬 Videoclub", PREFIJO + "cat:videoclub"),
_btn("📥 NAS/Descargas", PREFIJO + "cat:nas"),
],
[
_btn("📺 TV", PREFIJO + "cat:tv"),
_btn("👪 Control parental", PREFIJO + "cat:parental"),
_btn("🏠 Domótica", PREFIJO + "cat:domotica"),
],
[
_btn("🛠️ Sistema", PREFIJO + "cat:sistema"),
_btn("📚 Ayuda (texto)", PREFIJO + "acc:ayuda"),
],
])
def teclado_videoclub(chat_id):
filas = [
[
_btn("🔎 Buscar", PREFIJO + "txt:busca"),
_btn("🎬 Película", PREFIJO + "txt:pelicula"),
_btn("📺 Serie", PREFIJO + "txt:serie"),
],
[
_btn("🎭 Por género", PREFIJO + "acc:genero"),
_btn("🆕 Novedades", PREFIJO + "acc:novedades"),
],
]
titulos = _txt("ultimos_titulos", chat_id)
if titulos:
filas.append([
_btn("🧲 Torrents…", PREFIJO + "sub:torrents"),
])
torrents = _txt("ultimos_torrents", chat_id)
if torrents and torrents.get("torrents"):
filas.append([
_btn("⬇️ Bajar…", PREFIJO + "sub:bajar"),
])
filas.append(FILA_VOLVER)
return _mk(filas)
def teclado_nas():
return _mk([
[
_btn("📥 Panel descargas", PREFIJO + "acc:descargas"),
_btn("➕ Añadir enlace", PREFIJO + "txt:descargar"),
],
FILA_VOLVER,
])
def teclado_domotica():
return _mk([
[
_btn("🤖 Aspiradora", PREFIJO + "cat:aspi"),
_btn("🔌 Termo", PREFIJO + "cat:termo"),
],
[
_btn("🖥️ Pantalla", PREFIJO + "cat:pantalla"),
_btn("🛏️ Mesillas", PREFIJO + "cat:mesillas"),
],
FILA_VOLVER,
])
def teclado_aspi():
return _mk([
[
_btn("📊 Estado", PREFIJO + "acc:aspiradora:estado"),
_btn("🧹 Aspirar", PREFIJO + "acc:aspiradora:start"),
],
[
_btn("⏸️ Pausar", PREFIJO + "acc:aspiradora:pause"),
_btn("🏠 A la base", PREFIJO + "acc:aspiradora:home"),
],
[
_btn("🗑️ Vaciar", PREFIJO + "acc:aspiradora:vaciar"),
_btn("📊 Stats", PREFIJO + "acc:aspiradora:stats"),
],
[
_btn("⚡ Potencia…", PREFIJO + "cat:potencia"),
_btn("🔧 Consumibles", PREFIJO + "acc:aspiradora:consumibles"),
],
[_btn("⬅️ Domótica", PREFIJO + "cat:domotica")],
])
def teclado_potencia():
return _mk([
[
_btn("🔈 Silencioso", PREFIJO + "acc:potencia:0"),
_btn("🔉 Estándar", PREFIJO + "acc:potencia:1"),
],
[
_btn("🔊 Medio", PREFIJO + "acc:potencia:2"),
_btn("📢 Fuerte", PREFIJO + "acc:potencia:3"),
],
[_btn("⬅️ Aspiradora", PREFIJO + "cat:aspi")],
])
def teclado_termo():
return _mk([
[
_btn("🔌 ON", PREFIJO + "acc:termo:on"),
_btn("🔌 OFF", PREFIJO + "acc:termo:off"),
_btn("📊 Estado", PREFIJO + "acc:termo:estado"),
],
[_btn("⬅️ Domótica", PREFIJO + "cat:domotica")],
])
def teclado_pantalla():
return _mk([
[
_btn("🖥️ ON", PREFIJO + "acc:pantalla:on"),
_btn("🖥️ OFF", PREFIJO + "acc:pantalla:off"),
_btn("📊 Estado", PREFIJO + "acc:pantalla:estado"),
],
[_btn("⬅️ Domótica", PREFIJO + "cat:domotica")],
])
def teclado_mesillas():
return _mk([
[_btn("📊 Estado mesillas", PREFIJO + "acc:mesilla:estado")],
[
_btn("🛏️ Juanjo ON", PREFIJO + "acc:mesilla:juanjo:on"),
_btn("🛏️ Juanjo OFF", PREFIJO + "acc:mesilla:juanjo:off"),
],
[
_btn("🛏️ Nahid ON", PREFIJO + "acc:mesilla:nahid:on"),
_btn("🛏️ Nahid OFF", PREFIJO + "acc:mesilla:nahid:off"),
],
[_btn("⬅️ Domótica", PREFIJO + "cat:domotica")],
])
def teclado_tv():
return _mk([
[
_btn("📺 Mensaje", PREFIJO + "txt:tv"),
_btn("🚨 Alarma", PREFIJO + "txt:alarma"),
],
[_btn("▶️ YouTube", PREFIJO + "txt:youtube")],
FILA_VOLVER,
])
def teclado_parental():
return _mk([
[
_btn("🔒 Bloquear tablets", PREFIJO + "acc:tablets:bloquear"),
_btn("🔓 Desbloquear tablets", PREFIJO + "acc:tablets:desbloquear"),
],
[_btn("📋 Listar estado", PREFIJO + "acc:parental_listar")],
[_btn("📺 Bloquear dispositivo…", PREFIJO + "acc:dispositivos")],
[
_btn("🔒 Desactivar…", PREFIJO + "sub:desactivar"),
_btn("🔓 Activar…", PREFIJO + "sub:activar"),
],
[_btn("⏸️ Pausa…", PREFIJO + "sub:pausa")],
FILA_VOLVER,
])
def teclado_sistema():
return _mk([
[
_btn("📊 Estado", PREFIJO + "acc:estado"),
_btn("🌡️ Temperatura", PREFIJO + "acc:temperatura"),
],
[
_btn("📡 Router…", PREFIJO + "sub:router"),
_btn("🌐 AiMesh", PREFIJO + "acc:aimesh"),
],
[
_btn("⚙️ QoS", PREFIJO + "acc:qos"),
_btn("🚦 Límites", PREFIJO + "acc:limite"),
_btn("🛡️ Seguridad", PREFIJO + "acc:seguridad"),
],
[
_btn("📸 Panorama", PREFIJO + "acc:panorama"),
_btn("🧹 Limpieza", PREFIJO + "acc:limpieza"),
],
[_btn("🗑️ Limpiar pantalla", PREFIJO + "acc:limpiar")],
FILA_VOLVER,
])
def teclado_router():
return _mk([
[
_btn("📊 Estado", PREFIJO + "acc:router:estado"),
_btn("📶 Clientes", PREFIJO + "acc:router:clientes"),
_btn("📡 Wi-Fi", PREFIJO + "acc:router:wifi"),
],
[
_btn("📋 Inventario", PREFIJO + "acc:router:inventario"),
_btn("👪 Parental", PREFIJO + "acc:router:parental"),
],
[
_btn("🔄 Reiniciar", PREFIJO + "acc:router:reiniciar"),
_btn("🔁 Reservar IPs", PREFIJO + "acc:router:reservar"),
],
[_btn("⬅️ Sistema", PREFIJO + "cat:sistema")],
])
def teclado_nombres(accion):
return _mk([
[_btn("👧 Inés", PREFIJO + f"sub:{accion}:Inés"),
_btn("👩 Carolina", PREFIJO + f"sub:{accion}:Carolina")],
[_btn("✏️ Otro…", PREFIJO + f"txt:{accion}")],
[_btn("⬅️ Control parental", PREFIJO + "cat:parental")],
])
def teclado_minutos_pausa(nombre):
return _mk([
[
_btn("15 min", PREFIJO + "pausa_min:15"),
_btn("30 min", PREFIJO + "pausa_min:30"),
_btn("60 min", PREFIJO + "pausa_min:60"),
],
[_btn("✏️ Otro…", PREFIJO + "pausa_min:otro")],
[_btn("⬅️ Nombres", PREFIJO + "cat:parental")],
])
# Snapshot de la lista de dispositivos por chat (para toggle por numero).
menu_dispositivos = {}
def teclado_dispositivos(lista):
filas = []
for i, d in enumerate(lista, start=1):
marca = "🔒" if d["bloqueado"] else "🔓"
extra = f" {d['ip']}" if d.get("ip") else ""
filas.append([_btn(f"{i}. {marca} {d['tipo']} {d['nombre'][:28]}{extra}",
PREFIJO + f"acc:blqdev:{i}")])
filas.append([_btn("⬅️ Control parental", PREFIJO + "cat:parental")])
return _mk(filas)
def _mostrar_dispositivos(chat_id, message_id):
pi_bot = _pi()
try:
from asus_api_control import listar_dispositivos
lista = listar_dispositivos()
except Exception as e:
_enviar_nuevo(chat_id, f"❌ Error listando dispositivos: {e}", None)
return
if not lista:
_enviar_nuevo(chat_id, "No hay dispositivos en red.", None)
return
menu_dispositivos[chat_id] = lista
_enviar_edit(chat_id, message_id,
"📺 <b>Bloquear dispositivo</b>\nPulsa el número para "
"bloquear/desbloquear:",
teclado_dispositivos(lista))
def teclado_resultados(lista, prefijo):
filas = []
for i, item in enumerate(lista[:12], start=1):
nombre = item.get("name") or item.get("filename") or item.get("title") or item.get("clean") or f"Opción {i}"
filas.append([_btn(f"{i}. {nombre[:40]}", PREFIJO + f"acc:{prefijo}:{i}")])
filas.append([_btn("⬅️ Videoclub", PREFIJO + "cat:videoclub")])
return _mk(filas)
def teclado_cancelar():
return _mk([[_btn("❌ Cancelar", PREFIJO + "cancelar")]])
# --------------------------------------------------------------- helpers ---
def _enviar_edit(chat_id, message_id, texto, teclado):
import requests
try:
r = requests.post(_api("editMessageText"), data={
"chat_id": chat_id,
"message_id": message_id,
"text": texto,
"parse_mode": "HTML",
"reply_markup": json.dumps(teclado),
}, timeout=10)
d = r.json()
if not d.get("ok"):
_log(f"edit {message_id} fallo: {d.get('description')}")
except Exception as e:
_log(f"edit error: {e}")
def _enviar_nuevo(chat_id, texto, teclado=None):
import requests
data = {
"chat_id": chat_id,
"text": texto,
"parse_mode": "HTML",
}
if teclado:
data["reply_markup"] = json.dumps(teclado)
try:
requests.post(_api("sendMessage"), data=data, timeout=10)
except Exception as e:
_log(f"send error: {e}")
def _cerrar_menu(chat_id, message_id):
"""Quita los botones del menu (lo deja cerrado como texto estatico)."""
import requests
try:
requests.post(_api("editMessageReplyMarkup"),
data={"chat_id": chat_id, "message_id": message_id},
timeout=10)
except Exception as e:
_log(f"cerrar menu error: {e}")
def _disp(chat_id, message_id, comando, categoria=None):
"""Ejecuta un comando, cierra el menu viejo y devuelve al menu."""
pi_bot = _pi()
pi_bot.procesar(comando, chat_id)
_cerrar_menu(chat_id, message_id)
_reenviar_menu(chat_id, categoria)
def _fin_accion(chat_id, categoria=None):
"""Tras una accion terminal: deja ver el resultado, limpia toda la
pantalla y devuelve al menu de la categoria."""
import time as _time
pi_bot = _pi()
_time.sleep(4)
try:
pi_bot.limpiar_pantalla(chat_id)
except Exception:
pass
_reenviar_menu(chat_id, categoria)
def _reabrir(chat_id, message_id, categoria, comando):
"""Para consultas (estado, listar): ejecuta, borra los mensajes viejos
dejando solo el ultimo resultado y muestra de nuevo el menu de la
categoria (con su tecla de volver operativa)."""
pi_bot = _pi()
pi_bot.procesar(comando, chat_id)
try:
hist = pi_bot.historial_chat.get(chat_id, []) or []
for mid in hist[:-1]:
_borrar(chat_id, mid)
pi_bot.historial_chat[chat_id] = hist[-1:] if hist else []
ent = pi_bot.entrantes_chat.get(chat_id, []) or []
for mid in ent:
_borrar(chat_id, mid)
pi_bot.entrantes_chat[chat_id] = []
except Exception:
pass
_borrar(chat_id, message_id)
_reenviar_menu(chat_id, categoria)
# ----------------------------------------------------------------- menu ----
def enviar_menu(chat_id):
_enviar_nuevo(
chat_id,
"🧭 <b>Menú IDD</b>\nElige una categoría:",
teclado_principal(),
)
def _header(categoria):
nombres = {
"videoclub": "🎬 <b>Videoclub</b>",
"nas": "📥 <b>NAS / Descargas</b>",
"tv": "📺 <b>TV</b>",
"parental": "👪 <b>Control parental</b>",
"domotica": "🏠 <b>Domótica</b>",
"aspi": "🤖 <b>Aspiradora</b>",
"potencia": "⚡ <b>Potencia aspiradora</b>",
"termo": "🔌 <b>Termo</b>",
"pantalla": "🖥️ <b>Pantalla</b>",
"mesillas": "🛏️ <b>Mesillas</b>",
"sistema": "🛠️ <b>Sistema</b>",
"router": "📡 <b>Router ASUS</b>",
}
return nombres.get(categoria, "🧭 <b>Menú IDD</b>")
# Ultima categoria visitada por chat: a ella se vuelve tras cada accion.
menu_cat = {}
def _record_cat(chat_id, categoria):
menu_cat[chat_id] = categoria
def _reenviar_menu(chat_id, categoria=None):
"""Envia de nuevo el menu de la categoria (con su volver operativo)."""
pi_bot = _pi()
cat = categoria or menu_cat.get(chat_id)
try:
nt = len((pi_bot.ultima_titulos.get(chat_id) or {}).get("results", []))
nb = len((pi_bot.ultimos_torrents.get(chat_id) or {}).get("torrents", []))
_log(f"reenviar cat={cat} chat={chat_id} titulos={nt} torrents={nb}")
except Exception:
pass
if not cat:
_enviar_nuevo(chat_id, "🧭 <b>Menú IDD</b>\nElige una categoría:",
teclado_principal())
return
teclado = _teclado_categoria(chat_id, cat)
if teclado is None:
_enviar_nuevo(chat_id, "🧭 <b>Menú IDD</b>\nElige una categoría:",
teclado_principal())
return
_enviar_nuevo(chat_id, _header(cat), teclado)
def volver_al_menu(chat_id, categoria=None):
"""API para pi_bot: tras un texto libre del menu, devuelve al menu."""
_reenviar_menu(chat_id, categoria)
def categoria_de(comando):
"""Categoria del menu a la que pertenece un comando de texto."""
mapa = {
"busca": "videoclub", "pelicula": "videoclub",
"serie": "videoclub", "novedades": "videoclub",
"torrents": "videoclub", "bajar": "videoclub",
"genero": "videoclub",
"tv": "tv", "alarma": "tv", "youtube": "tv",
"descargar": "nas", "descargas": "nas", "borrar": "nas",
"parental": "parental",
"termo": "termo", "pantalla": "pantalla",
"aspiradora": "aspi", "meross": "mesillas",
"router": "router", "aimesh": "sistema", "qos": "sistema",
"limite": "sistema", "seguridad": "sistema",
"estado": "sistema", "temperatura": "sistema",
"panorama": "sistema", "limpieza": "sistema",
"limpiar": "sistema",
}
return mapa.get((comando or "").lower().strip())
def _teclado_categoria(chat_id, categoria):
teclados = {
"videoclub": lambda: teclado_videoclub(chat_id),
"nas": teclado_nas,
"tv": teclado_tv,
"parental": teclado_parental,
"domotica": teclado_domotica,
"aspi": teclado_aspi,
"potencia": teclado_potencia,
"termo": teclado_termo,
"pantalla": teclado_pantalla,
"mesillas": teclado_mesillas,
"sistema": teclado_sistema,
"router": teclado_router,
}
fn = teclados.get(categoria)
return fn() if fn else None
def _abrir_categoria(chat_id, message_id, categoria):
_record_cat(chat_id, categoria)
teclado = _teclado_categoria(chat_id, categoria)
if teclado is None:
return False
_enviar_edit(chat_id, message_id, _header(categoria), teclado)
return True
def procesar_callback(data, chat_id, message_id):
"""data llega sin el prefijo 'm:'. Gestiona navegacion y acciones."""
partes = data.split(":")
ruta = partes[0]
_log(f"callback {data} chat={chat_id}")
if ruta == "ini":
_enviar_edit(chat_id, message_id, "🧭 <b>Menú IDD</b>\nElige una categoría:", teclado_principal())
return
if ruta == "cat":
return _abrir_categoria(chat_id, message_id, partes[1])
if ruta == "sub":
# sub:torrents / sub:bajar / sub:router / sub:parental:<acc>
sub = partes[1]
if sub in ("router",):
_record_cat(chat_id, "router")
_enviar_edit(chat_id, message_id, _header("router"), teclado_router())
return
if sub in ("torrents", "bajar"):
_record_cat(chat_id, "videoclub")
return _mostrar_resultados(chat_id, message_id, sub)
if len(partes) == 2 and sub in ("desactivar", "activar", "pausa"):
_record_cat(chat_id, "parental")
_enviar_edit(chat_id, message_id,
f"¿Sobre qué dispositivo? ({sub})",
teclado_nombres(sub))
return
# sub:<accion>:<nombre> -> accion parental con nombre directo
if len(partes) >= 3 and sub in ("desactivar", "activar", "pausa"):
return _ejecutar_parental(chat_id, message_id, sub, partes[2])
_log(f"sub desconocida: {data}")
return
if ruta == "txt":
# pide un dato por teclado: lo captura el siguiente mensaje
accion = partes[1]
_guardar_pendiente(chat_id, accion)
CAT_TXT = {
"busca": "videoclub", "pelicula": "videoclub", "serie": "videoclub",
"tv": "tv", "alarma": "tv", "youtube": "tv",
"descargar": "nas",
"desactivar": "parental", "activar": "parental",
}
_record_cat(chat_id, CAT_TXT.get(accion, "sistema"))
preguntas = {
"busca": "🔎 ¿Qué busco? Escribe el nombre.",
"pelicula": "🎬 ¿Qué película busco?",
"serie": "📺 ¿Qué serie busco?",
"tv": "📺 ¿Qué mensaje envío a la tele?",
"alarma": "🚨 ¿Qué mensaje de alarma lanzo?",
"youtube": "▶️ Pega la URL de YouTube.",
"descargar": "🧲 Pega el enlace magnet o URL a descargar.",
"desactivar": "🔒 ¿A quién bloqueo? Escribe el nombre.",
"activar": "🔓 ¿A quién desbloqueo? Escribe el nombre.",
}
_enviar_nuevo(chat_id, preguntas.get(accion, "Escribe el dato:"),
teclado_cancelar())
_cerrar_menu(chat_id, message_id)
return
if ruta == "pausa_min":
nombre = _get_pausa_nombre(chat_id)
if not nombre:
_enviar_nuevo(chat_id, "Empieza de nuevo: /menu", teclado_cancelar())
_cerrar_menu(chat_id, message_id)
return
if partes[1] == "otro":
_guardar_pendiente(chat_id, "pausa_minutos")
_enviar_nuevo(chat_id, "⏱️ ¿Cuántos minutos? (1-1440)", teclado_cancelar())
_cerrar_menu(chat_id, message_id)
return
minutos = partes[1]
pi_bot = _pi()
pi_bot.procesar(f"parental pausa {nombre} {minutos}", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "parental")
return
if ruta == "acc":
return _ejecutar_accion(chat_id, message_id, partes[1:])
if ruta == "cancelar":
_limpiar_pendiente(chat_id)
_borrar(chat_id, message_id)
_reenviar_menu(chat_id)
return
def _mostrar_resultados(chat_id, message_id, tipo):
titulos = _txt("ultimos_titulos", chat_id)
if tipo == "torrents":
if titulos:
_enviar_edit(chat_id, message_id,
"🧲 Elige el título para ver sus torrents:",
teclado_resultados(titulos, "torrents"))
else:
_enviar_nuevo(chat_id, "Primero busca arriba con <b>🔎 Buscar</b>, "
"<b>🎬 Película</b>, <b>📺 Serie</b> o "
"<b>🆕 Novedades</b> y luego pulsa "
"<b>🧲 Torrents…</b>.", None)
return
torrents = _txt("ultimos_torrents", chat_id)
if torrents:
_enviar_edit(chat_id, message_id,
f"⬇️ Elige torrent de «{torrents.get('titulo')}»:",
teclado_resultados(torrents.get("torrents", []), "bajar"))
else:
_enviar_nuevo(chat_id, "Primero elige un título en <b>🧲 Torrents…</b> "
"y luego pulsa <b>⬇️ Bajar…</b>.", None)
def _ejecutar_parental(chat_id, message_id, accion, nombre):
if accion == "pausa":
_record_cat(chat_id, "parental")
_set_pausa_nombre(chat_id, nombre)
_enviar_edit(chat_id, message_id,
f"⏸️ Pausa para <b>{nombre}</b>. ¿Cuántos minutos?",
teclado_minutos_pausa(nombre))
return
comando = f"parental {'desactivar' if accion == 'desactivar' else 'activar'} {nombre}"
pi_bot = _pi()
pi_bot.procesar(comando, chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "parental")
def _ejecutar_accion(chat_id, message_id, partes):
pi_bot = _pi()
accion = partes[0]
sub = partes[1] if len(partes) > 1 else ""
directos = {
"ayuda": "ayuda",
"genero": "genero",
"novedades": "novedades",
"descargas": "descargas",
"estado": "estado",
"temperatura": "temperatura",
"panorama": "panorama",
"limpieza": "limpieza",
"limpiar": "limpiar",
"aimesh": "aiMesh estado",
"qos": "qos estado",
"limite": "limite listar",
"seguridad": "seguridad estado",
}
if accion == "parental_listar":
_reabrir(chat_id, message_id, "parental", "parental listar")
return
CAT_ACC = {
"ayuda": None, "novedades": "videoclub",
"descargas": "nas", "estado": "sistema", "temperatura": "temperatura",
"panorama": "sistema", "limpieza": "sistema", "limpiar": "limpiar",
"aimesh": "sistema", "qos": "sistema", "limite": "limite listar",
"seguridad": "sistema",
}
if accion == "genero":
flujo_genero[chat_id] = "esperando_tipo"
flujo_genero_tipo.pop(chat_id, None)
enviar("🎭 Catálogo por <b>género</b> (30 más descargadas). Primero elige el tipo:\n"
"<b>1.</b> 🎬 Películas\n"
"<b>2.</b> 📺 Series\n\nResponde el <b>1</b> o el <b>2</b>.", chat_id)
return
if accion in directos:
_disp(chat_id, message_id, directos[accion], CAT_ACC.get(accion))
return
if accion in ("termo", "pantalla"):
if sub == "estado":
_reabrir(chat_id, message_id, accion, f"{accion} estado")
elif sub in ("on", "off"):
pi_bot.procesar(f"{accion} {sub}", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, accion)
return
if accion == "aspiradora":
if sub in ("estado", "stats", "consumibles"):
_reabrir(chat_id, message_id, "aspi", f"aspiradora {sub}")
elif sub in ("start", "stop", "home", "pause", "locate", "vaciar"):
pi_bot.procesar(f"aspiradora {sub}", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "aspi")
return
if accion == "potencia":
if sub.isdigit() and int(sub) in (0, 1, 2, 3):
pi_bot.procesar(f"aspiradora potencia {sub}", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "aspi")
return
if accion == "dispositivos":
_record_cat(chat_id, "parental")
_mostrar_dispositivos(chat_id, message_id)
return
if accion == "blqdev":
_record_cat(chat_id, "parental")
lista = menu_dispositivos.get(chat_id) or []
numero = int(sub) if sub.isdigit() else 0
if not lista and numero:
try:
from asus_api_control import listar_dispositivos as _ld
lista = _ld()
menu_dispositivos[chat_id] = lista
except Exception:
lista = []
if 1 <= numero <= len(lista):
d = lista[numero - 1]
try:
from asus_api_control import bloquear_mac, listar_dispositivos
bloquear_mac(d["mac"], not d["bloqueado"], d["nombre"])
lista = listar_dispositivos()
menu_dispositivos[chat_id] = lista
except Exception as e:
_enviar_nuevo(chat_id, f"❌ Error: {e}", None)
return
_enviar_edit(chat_id, message_id,
"📺 <b>Bloquear dispositivo</b>\nPulsa el número para "
"bloquear/desbloquear:",
teclado_dispositivos(lista))
else:
_mostrar_dispositivos(chat_id, message_id)
return
if accion == "mesilla":
# sub: estado | nombre | nombre:accion
rest = ":".join(partes[1:])
trozos = rest.split(":")
if len(trozos) == 1 and trozos[0] == "estado":
_reabrir(chat_id, message_id, "mesillas", "meross estado")
elif len(trozos) == 2 and trozos[1] in ("on", "off"):
pi_bot.procesar(f"meross {trozos[0]} {trozos[1]}", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "mesillas")
return
if accion == "tablets":
if sub in ("bloquear", "desbloquear"):
verbo = "desactivar" if sub == "bloquear" else "activar"
pi_bot.procesar(f"parental {verbo} Inés", chat_id)
pi_bot.procesar(f"parental {verbo} Carolina", chat_id)
_cerrar_menu(chat_id, message_id)
_fin_accion(chat_id, "parental")
return
if accion == "router":
mapeo = {
"estado": "router estado",
"clientes": "router clientes",
"wifi": "router wifi",
"inventario": "router inventario",
"parental": "router parental",
"reiniciar": "router reiniciar",
"reservar": "router reservar",
}
_disp(chat_id, message_id, mapeo.get(sub, "router ayuda"), "router")
return
if accion in ("torrents", "bajar"):
numero = int(sub) if sub.isdigit() else 0
if numero:
_disp(chat_id, message_id, f"{accion} {numero}", "videoclub")
return
# --------------------------------------------- pendiente de texto libre ----
def _pend(chat_id):
pi_bot = _pi()
return pi_bot.menu_pendiente
def _guardar_pendiente(chat_id, accion):
_pend(chat_id)[chat_id] = accion
def _limpiar_pendiente(chat_id):
_pend(chat_id).pop(chat_id, None)
def pendiente_activo(chat_id):
return chat_id in _pend(chat_id)
def cancelar_pendiente(chat_id):
_limpiar_pendiente(chat_id)
def construir_comando(chat_id, texto):
"""Devuelve el texto del comando para el texto libre recibido, o None."""
accion = _pend(chat_id).get(chat_id)
if not accion:
return None
texto = texto.strip()
if accion in ("pausa_minutos",):
if not texto.isdigit():
return "INVALIDO"
return None # manejado aparte
prefijos = {
"busca": "busca",
"pelicula": "pelicula",
"serie": "serie",
"tv": "tv",
"alarma": "alarma",
"youtube": "youtube",
"descargar": "descargar",
"desactivar": "parental desactivar",
"activar": "parental activar",
}
prefijo = prefijos.get(accion)
if prefijo is None:
return None
return f"{prefijo} {texto}"
def resolver_pausa_minutos(chat_id, texto):
"""Si el pendiente es pausa_minutos y el texto es valido, lanza la pausa."""
accion = _pend(chat_id).get(chat_id)
if accion != "pausa_minutos":
return False
if not texto.strip().isdigit():
return False
minutos = int(texto.strip())
if minutos < 1 or minutos > 1440:
return False
nombre = _get_pausa_nombre(chat_id)
_limpiar_pendiente(chat_id)
if not nombre:
return True
pi_bot = _pi()
pi_bot.procesar(f"parental pausa {nombre} {minutos}", chat_id)
_reenviar_menu(chat_id, "parental")
return True
def pausa_estado(chat_id):
pi_bot = _pi()
return pi_bot.menu_pausa_nombre.get(chat_id)
def _set_pausa_nombre(chat_id, nombre):
pi_bot = _pi()
pi_bot.menu_pausa_nombre[chat_id] = nombre
def _get_pausa_nombre(chat_id):
pi_bot = _pi()
return pi_bot.menu_pausa_nombre.get(chat_id)
def _borrar(chat_id, message_id):
import requests
try:
requests.post(_api("deleteMessage"),
data={"chat_id": chat_id, "message_id": message_id},
timeout=10)
except Exception:
pass