05-sep: [parental] pausar y bloquear dispositivos por nombre

This commit is contained in:
juanjo
2026-09-05 19:00:52 +02:00
parent 5c9365d596
commit c35147188b
2 changed files with 80 additions and 18 deletions

View File

@@ -7,6 +7,7 @@ import unicodedata
import aiohttp
from asusrouter import AsusData, AsusRouter
from asusrouter.modules.parental_control import PCRuleType, ParentalControlRule
def _load_env():
@@ -71,6 +72,41 @@ async def _parental_data():
await session.close()
async def _set_block(mac, name, blocked):
username = _env("ASUS_USERNAME")
password = _env("ASUS_PASSWORD")
if not username or not password:
raise RuntimeError(
"Falta ASUS_USERNAME o ASUS_PASSWORD en el .env de la Raspberry"
)
session = aiohttp.ClientSession()
router = AsusRouter(
hostname=_env("ASUS_ROUTER_IP", "192.168.50.1"),
username=username,
password=password,
session=session,
)
try:
await router.async_connect()
rule = ParentalControlRule(
mac=mac,
name=name if blocked else None,
type=PCRuleType.BLOCK if blocked else PCRuleType.REMOVE,
)
return await router.async_set_state(rule)
finally:
await router.async_disconnect()
await session.close()
def parental_status():
data = asyncio.run(_parental_data())
return json.dumps(data, ensure_ascii=False, indent=2, default=str)
def cambiar_bloqueo(nombre, blocked):
device = resolver_dispositivo(nombre)
if not device:
raise ValueError(f"Dispositivo no reconocido: {nombre}")
asyncio.run(_set_block(device["mac"], device["name"], blocked))
return f"{'Bloqueado' if blocked else 'Activado'}: {device['name']}"

View File

@@ -15,6 +15,7 @@ ultimos_torrents = {} # chat_id -> {'titulo':..., 'torrents':[...]}
flujo_genero = {} # chat_id -> 'esperando_tipo' | 'esperando_genero' | None
flujo_genero_tipo = {} # chat_id -> 'movie' | 'series' (tipo ya elegido)
panel_descargas = {} # chat_id -> estado del panel editable
temporizadores_parental = {} # alias normalizado -> Timer de desbloqueo
BASE = os.path.dirname(os.path.abspath(__file__))
LOG = os.path.join(BASE, "bot.log")
@@ -458,23 +459,48 @@ def procesar(texto, chat_id):
else:
enviar("Acción de router no reconocida. Usa <code>/router ayuda</code>.", chat_id)
elif comando == "parental":
partes_parental = argumento.split()
accion_parental = partes_parental[0].lower() if partes_parental else "listar"
try:
from asus_api_control import cambiar_bloqueo, normalizar_nombre
if accion_parental == "listar":
from router_control import control_parental
enviar(
"👪 <b>RUTINAS PARENTALES</b>\n"
"Alias reconocidos: <code>Inés</code>, <code>INES</code>, <code>Carolina</code>.\n"
"Rutinas previstas:\n"
"<code>/parental listar</code>\n"
"<code>/parental activar &lt;rutina&gt;</code>\n"
"<code>/parental desactivar &lt;rutina&gt;</code>\n"
"<code>/parental pausa &lt;rutina&gt; &lt;minutos&gt;</code>\n\n"
"Las acciones por nombre quedan preparadas para la primera rutina.\n\n"
"Estado actual:\n" + control_parental(),
chat_id,
enviar("👪 <b>CONTROL PARENTAL ASUS</b>\n" + control_parental(), chat_id)
elif accion_parental in ("desactivar", "bloquear") and len(partes_parental) >= 2:
alias = " ".join(partes_parental[1:])
enviar(f"🔒 {cambiar_bloqueo(alias, True)}", chat_id)
elif accion_parental in ("activar", "desbloquear") and len(partes_parental) >= 2:
alias = " ".join(partes_parental[1:])
timer = temporizadores_parental.pop(normalizar_nombre(alias), None)
if timer:
timer.cancel()
enviar(f"🔓 {cambiar_bloqueo(alias, False)}", chat_id)
elif accion_parental == "pausa" and len(partes_parental) >= 3:
alias = " ".join(partes_parental[1:-1])
minutos = int(partes_parental[-1])
if minutos < 1 or minutos > 1440:
raise ValueError("Los minutos deben estar entre 1 y 1440")
cambiar_bloqueo(alias, True)
clave = normalizar_nombre(alias)
anterior = temporizadores_parental.pop(clave, None)
if anterior:
anterior.cancel()
timer = threading.Timer(
minutos * 60,
lambda: cambiar_bloqueo(alias, False),
)
timer.daemon = True
temporizadores_parental[clave] = timer
timer.start()
enviar(f"⏸️ Bloqueado {alias} durante {minutos} minutos.", chat_id)
else:
enviar(
"Uso: <code>/parental pausa Inés 60</code>, "
"<code>/parental desactivar Carolina</code> o "
"<code>/parental activar Inés</code>.", chat_id)
except Exception as e:
log(f"parental error: {e}")
enviar(f"❌ Error consultando parental: {e}", chat_id)
enviar(f"❌ Error en control parental: {e}", chat_id)
elif t in ("pelis", "/pelis"):
procesar_pelis(chat_id)
elif t in ("series", "/series"):
@@ -532,10 +558,10 @@ def procesar(texto, chat_id):
"━━━ 👪 CONTROL PARENTAL ━━━\n"
"<b>14.</b> <code>parental listar</code> — estado y dispositivos\n"
" ✏️ Ej: <code>/parental listar</code>\n"
"<b>15.</b> <code>parental activar|desactivar</code> — rutinas por nombre (preparado)\n"
" ✏️ Ej: <code>/parental activar noches_escolares</code>\n"
"<b>16.</b> <code>parental pausa</code> — pausa temporal (preparado)\n"
" ✏️ Ej: <code>/parental pausa noches_escolares 60</code>\n\n"
"<b>15.</b> <code>parental desactivar|activar</code> — bloquea o desbloquea\n"
" ✏️ Ej: <code>/parental desactivar Inés</code>\n"
"<b>16.</b> <code>parental pausa</code> — bloquea X minutos\n"
" ✏️ Ej: <code>/parental pausa Carolina 60</code>\n\n"
"━━━ 🛠️ SISTEMA ━━━\n"
"<b>17.</b> <code>estado</code> — estado de la Pi\n"
" ✏️ Ej: <code>/estado</code>\n"