05-sep: [vigilancia] comandos /aiMesh /qos /limite /seguridad para el ASUS desde Telegram

This commit is contained in:
juanjo
2026-09-05 19:38:03 +02:00
parent e15204a5e8
commit 15f19a7900
7 changed files with 987 additions and 10 deletions

View File

@@ -82,6 +82,42 @@ Desde Telegram, el bot consulta el ASUS mediante la clave de servicio de la Rasp
El reinicio exige la palabra `confirmar`. Las consultas actuales no cambian la configuracion. El reinicio exige la palabra `confirmar`. Las consultas actuales no cambian la configuracion.
## Funcionalidades Mesh / QoS / Limite / Seguridad implementadas (05-sep-2026)
Cuatro comandos nuevos del bot, desplegados y verificados:
```text
/aiMesh estado # router + nodos, señal y roaming
/aiMesh reiniciar <nodo> confirmar # reinicia un nodo por nombre
/aiMesh reconstruir confirmar # reconstruye la malla
/qos estado
/qos adaptive confirmar # auto gaming/streaming (QoS)
/qos tradicional confirmar # prioridades manuales
/qos apagar confirmar # restaura aceleracion NAT
/limite listar # reglas activas (Mbps)
/limite poner <dispositivo> <bajada> <subida> # en Mbps
/limite quitar <dispositivo>
/seguridad estado # AiProtection + filtro de palabras
/seguridad bloquear <palabra> # p. ej. tiktok
/seguridad desbloquear <palabra>
```
Detalles tecnicos:
- **AiMesh** se lee con la libreria `asusrouter` (v1.21.3 ya presente en la Pi) via `AsusData.AIMESH`. Los nodos se resuelven por alias tolerante a tildes/minusculas (`bedroom`, `living room`). Las acciones usan `AsusSystem.NODE_REBOOT` (con `device_list=<MAC>`), `AIMESH_REBUILD` y `AIMESH_REBOOT`.
- **Estado actual detectado:** router principal RT-AX88U PRO (fw 3.0.0.6.102) + nodos `Bedroom` (192.168.50.76, enlace 2.4GHz rssi -59) y `Living Room` (192.168.50.13, enlace 2.4GHz rssi -69), ambos ZenWiFi AX Mini.
- **Bandwidth limiter y QoS** se gestionan por NVRAM via SSH. Formato real de `qos_bw_rulelist` (confirmado en vivo): reglas `enable>MAC>dl_kbps>ul_kbps>indice` separadas por `<`, sin `<` inicial. Al cambiar reglas se aplica `service restart_qos`.
- **El router ya tenia el limiter activo con 7 reglas** (Caroline-tablet 25 Mbps, y 6 dispositivos a 30 Mbps). `/limite listar` las muestra. El modulo reconstruye el payload sin perder ninguna.
- Conversiones en `Mbps x 1024` (coherentes con la UI del router: 30720 kbps = 30 Mbps).
- **QoS y bandwidth limiter desactivan la aceleracion NAT** (aviso incluido en la respuesta del bot).
- `/seguridad` usa el filtro de contenidos `keyword_rulelist` (AiProtection) con `service restart_bwdpi`. El bloqueo por categorias Trend Micro requiere la base web del router y queda como posible evolucion.
- `asusrouter==1.21.3` y `aiohttp` anadidos a `vigilancia/requirements.txt`.
Las acciones destructivas (reiniciar nodo, reconstruir, cambiar QoS, quitar ultima regla) piden `confirmar` como en el patron `router reiniciar`.
## Capacidades del RT-AX88U PRO ## Capacidades del RT-AX88U PRO
El router puede centralizar muchas funciones para Telegram: El router puede centralizar muchas funciones para Telegram:
@@ -205,6 +241,8 @@ Cada unidad debe tener un registro con hostname, MAC, IP Ethernet, IP Wi-Fi, ser
Catalogo de capacidades del RT-AX88U PRO consultado con el usuario, agrupado por area, para implementar desde Telegram cuando se pida. Ninguna de estas se ha implementado todavia salvo que se indique. Catalogo de capacidades del RT-AX88U PRO consultado con el usuario, agrupado por area, para implementar desde Telegram cuando se pida. Ninguna de estas se ha implementado todavia salvo que se indique.
> **Actualizacion 05-sep-2026 (tarde):** los comandos `/aiMesh`, `/seguridad`, `/limite` y `/qos` ya estan implementados y desplegados. Ver seccion «Funcionalidades Mesh / QoS / Limite / Seguridad implementadas» mas abajo.
**Red y Wi-Fi** **Red y Wi-Fi**
- Apagar/encender cada banda Wi-Fi (2.4/5 GHz) por separado. - Apagar/encender cada banda Wi-Fi (2.4/5 GHz) por separado.
- Ajustar canales y potencia de senal para optimizar cobertura. - Ajustar canales y potencia de senal para optimizar cobertura.

View File

@@ -0,0 +1,60 @@
# Videoclub — 2026-09-05
**Total disponibles: 51** | Novedades: 0
## Películas (37)
- Backrooms 2160p 4K WEB x265 10bit AAC5 1 (2026) `https://real-debrid.com/d/WC7MIGNKYMMEAFED`
- Disclosure Day 1080p ViruseProject (2026) `https://real-debrid.com/d/7AKRWAMVG2A7OFA6`
- Disclosure Day NO-ADS 1080p x265 MD-AAC5 1+2 0 ITA-ENG MmTRaXx DD-CREW-IlCosaro (2026) `https://real-debrid.com/d/ZWPJNGU5EKGA61D4`
- Downloads
- El dia de la revelacion Disclosure Day (2026) `https://real-debrid.com/d/AHBLKPLNL56KOBBC`
- El día de la revelacion Disclosure Day (2026) `https://real-debrid.com/d/NSHXB2IFNLNEK7C8`
- El Pasajero Nocturno (2026) `https://real-debrid.com/d/52MMX6GHV6U5639B`
- El Pasajero Nocturno (2026) `https://real-debrid.com/d/MBLS4EMXARKVM5C4`
- Fiasco total La tragedia del festival Astroworld (2025) `https://real-debrid.com/d/YCBMS3HYQPLU27E1`
- Fiasco total La tragedia del festival Astroworld (2025) `https://real-debrid.com/d/YCBMS3HYQPLU27E1`
- Fiasco total La tragedia del festival Astroworld (2025) `https://real-debrid.com/d/YCBMS3HYQPLU27E1`
- Greenland `https://real-debrid.com/d/LUZ3G5KBQZYES397`
- Greenland 2 `https://real-debrid.com/d/FS4OAHZSNYHFK568`
- Greenland El Ultimo Refugio (2020) `https://real-debrid.com/d/MJR44YRCTRDMY2C9`
- He-Man y los Masters del Universo (2026) `https://real-debrid.com/d/APGDE3Z7ADR3U58B`
- Iron Lung 2026 1080p `https://real-debrid.com/d/I6BVHXX7IFSUE1DB`
- La maldicion de Shelby Oaks (2026) `https://real-debrid.com/d/T4FFNHSB2XBV4D92`
- La momia de Lee Cronin Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p `https://real-debrid.com/d/QDPUO2UWMDWX6C6E`
- Luger (2026) `https://real-debrid.com/d/VWUUZIQGVLO76357`
- Obsession 2160p UHD x265 10bit DV HDR r00t (2026) `https://real-debrid.com/d/XFJULZ3HFQ6VG8A4`
- Obsession 2160p UHD x265 10bit DV HDR TrueHD Atmos 7 1 r00t (2026) `https://real-debrid.com/d/LMJHM23CEYXZOA66`
- Obsession Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p `https://real-debrid.com/d/LESH2XGOLRWYKEC8`
- Posesion infernal En llamas (2026) `https://real-debrid.com/d/RF2IRCGKIKISI174`
- Project Hail Mary 2160p KinoRip h265 En Es Dual YG (2026) `https://real-debrid.com/d/OMCP4MAMM775Q17A`
- Project Hail Mary 2160p KinoRip h265 En Es Dual YG (2026) `https://real-debrid.com/d/OMCP4MAMM775Q17A`
- Project Hail Mary 2160p KinoRip h265 En Es Dual YG (2026) `https://real-debrid.com/d/OMCP4MAMM775Q17A`
- Resident Evil `https://real-debrid.com/d/6VMIEUSOVVIXYBD2`
- Salvacion (2026) `https://real-debrid.com/d/BF3FGPVABGL6A610`
- Salvacion (2026) `https://real-debrid.com/d/4Q6AFXH6KSKRO2AF`
- Scary Movie (2026) `https://real-debrid.com/d/7FHGHGOXV7ELY3E6`
- Spider-Man No Way Home 4Kremux2160 atomixhq link `https://real-debrid.com/d/S7GR3CYFAWOWC7AC`
- Steven Spielberg el rey midas de Hollywood wolfmax4k com (2024) `https://real-debrid.com/d/WWCMHPUY4FPFAF67`
- Super Mario Galaxy La película (2026) `https://real-debrid.com/d/VNTK5JCJGRHJOF0E`
- super mario galaxy la película Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p `https://real-debrid.com/d/3GQUITYMNB4TUE1C`
- The End of Oak Street 1080p HC V2 x264-DKS (2026) `https://real-debrid.com/d/JI3KPXS4HEY7Y234`
- TheAdventuresOfTomSawyer `https://real-debrid.com/d/XJVUAD7LIIMFKDCC`
- Twilight Saga 2008-2012 2160p H265 HDR10 AC3 5 1 ITA ENG sub ita eng Sp33dy94-MIRCrew `https://real-debrid.com/d/II6EYH6KIMNUI10A`
## Series (14)
- Euphoria `https://real-debrid.com/d/YOFHJHFSKN6HOB11`
- From - S02E01 - Strangers in a Strange Land - WEBDL-1080p - Spanish 2x01 - audio - sub `https://real-debrid.com/d/B64L3AL7NWVN41D1`
- Heidi Girl of the Alps Alps no Shoujo Heidi Season 1 BD 1080p HEVC x265 10bit Eng-Subs -Judas `https://real-debrid.com/d/3V5DNKCHKQOPGF89`
- Monarch El Legado De Los Monstruos wolfmax4k com `https://real-debrid.com/d/LRB4AEEJCYOCU1E0`
- Star Trek Strange New Worlds `https://real-debrid.com/d/UCVH7W3HSXDKS4E5`
- Stargate Sg-1 05x09 Entre Dos Fuegos `https://real-debrid.com/d/TYLADWGS6AEMQ28C`
- Stargate Sg-1 05x10 2001 `https://real-debrid.com/d/LHQ7LEN7VCRSS1C4`
- Stargate Sg-1 05x12 Agujero X-Tremo `https://real-debrid.com/d/7BTWUVXH2KH7KCC5`
- Supergirl (2026) `https://real-debrid.com/d/65RUKRSI3YERQDCB`
- Supergirl (2026) `https://real-debrid.com/d/ZKB3MUWMEM52G885`
- The Studio `https://real-debrid.com/d/6AQCY2YCPY6FE9A9`
- The Studio `https://real-debrid.com/d/L7DDTFXUCCPF47A5`
- The Studio `https://real-debrid.com/d/P3J5H2OCFG76WC67`
- The Studio wolfmax4k com `https://real-debrid.com/d/KHLUHJ4W7JMKA663`

View File

@@ -0,0 +1,413 @@
{
"fecha": "2026-09-05",
"items": [
{
"id": "realdebrid:Downloads",
"name": "Downloads",
"clean": "Downloads",
"year": null,
"tipo": "pelicula",
"enlace": null
},
{
"id": "realdebrid:O47SZPHDEIJD4",
"name": "Posesion infernal En llamas (2026) [4k 2160p][Esp].mkv",
"clean": "Posesion infernal En llamas",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/RF2IRCGKIKISI174"
},
{
"id": "realdebrid:5DLXRYNWASICQ",
"name": "The.End.of.Oak.Street.2026.1080p.HC.V2.x264-DKS.mkv",
"clean": "The End of Oak Street 1080p HC V2 x264-DKS",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/JI3KPXS4HEY7Y234"
},
{
"id": "realdebrid:TEGQKYCCQBXXO",
"name": "Supergirl (2026) [Bluray 720p][Esp].mkv",
"clean": "Supergirl",
"year": "2026",
"tipo": "serie",
"enlace": "https://real-debrid.com/d/65RUKRSI3YERQDCB"
},
{
"id": "realdebrid:HARMOP47F3AP2",
"name": "Obsession Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5.1 BD-Rip hd 1080p.mkv",
"clean": "Obsession Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/LESH2XGOLRWYKEC8"
},
{
"id": "realdebrid:V7XUZQ6APSG4Q",
"name": "Scary Movie (2026) [4k 2160p][Esp].mkv",
"clean": "Scary Movie",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/7FHGHGOXV7ELY3E6"
},
{
"id": "realdebrid:MBGEYHZC4MKYU",
"name": "Star Trek Strange New Worlds [HDTV 1080p][Cap.401].mkv",
"clean": "Star Trek Strange New Worlds",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/UCVH7W3HSXDKS4E5"
},
{
"id": "realdebrid:HEIP5O2XB5V66",
"name": "He-Man y los Masters del Universo (2026) [Bluray 1080p][Esp].mkv",
"clean": "He-Man y los Masters del Universo",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/APGDE3Z7ADR3U58B"
},
{
"id": "realdebrid:SLJ2UJGPDFZLW",
"name": "Supergirl (2026) [Bluray 1080p][Esp].mkv",
"clean": "Supergirl",
"year": "2026",
"tipo": "serie",
"enlace": "https://real-debrid.com/d/ZKB3MUWMEM52G885"
},
{
"id": "realdebrid:FC5FZS2J4HUFS",
"name": "El dia de la revelacion (Disclosure Day) (2026) [Bluray 1080p][Esp].mkv",
"clean": "El dia de la revelacion Disclosure Day",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/AHBLKPLNL56KOBBC"
},
{
"id": "realdebrid:A5XIYLPNAZNZM",
"name": "El día de la revelacion (Disclosure Day) (2026) [4k 2160p][Esp].mkv",
"clean": "El día de la revelacion Disclosure Day",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/NSHXB2IFNLNEK7C8"
},
{
"id": "realdebrid:7XL55PFQOE4OO",
"name": "El Pasajero Nocturno (2026) [Bluray 1080p][Esp].mkv",
"clean": "El Pasajero Nocturno",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/52MMX6GHV6U5639B"
},
{
"id": "realdebrid:5IHX4PFCFP5QY",
"name": "El Pasajero Nocturno (2026) [Bluray][Esp].avi",
"clean": "El Pasajero Nocturno",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/MBLS4EMXARKVM5C4"
},
{
"id": "realdebrid:S52E6GZH2FSNK",
"name": "Disclosure.Day.(2026).(NO-ADS).1080p.x265.MD-AAC5.1+2.0.ITA-ENG.MmTRaXx(DD-CREW-IlCosaro).mkv",
"clean": "Disclosure Day NO-ADS 1080p x265 MD-AAC5 1+2 0 ITA-ENG MmTRaXx DD-CREW-IlCosaro",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/ZWPJNGU5EKGA61D4"
},
{
"id": "realdebrid:FOTT5OF2CNCQO",
"name": "Disclosure.Day.2026.1080p.CAMRip.ViruseProject.mkv",
"clean": "Disclosure Day 1080p ViruseProject",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/7AKRWAMVG2A7OFA6"
},
{
"id": "realdebrid:FEWY5OUINFE6I",
"name": "Obsession (2026) (2160p UHD BluRay x265 10bit DV HDR r00t).mkv",
"clean": "Obsession 2160p UHD x265 10bit DV HDR r00t",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/XFJULZ3HFQ6VG8A4"
},
{
"id": "realdebrid:GE3KLARBGACYC",
"name": "Obsession (2026) (2160p UHD BluRay x265 10bit DV HDR TrueHD Atmos 7.1 r00t)",
"clean": "Obsession 2160p UHD x265 10bit DV HDR TrueHD Atmos 7 1 r00t",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/LMJHM23CEYXZOA66"
},
{
"id": "realdebrid:REQOOWL7R3X5M",
"name": "Backrooms.2026.2160p.4K.WEB.x265.10bit.AAC5.1-[YTS.GG - YTS.BZ].mkv",
"clean": "Backrooms 2160p 4K WEB x265 10bit AAC5 1",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/WC7MIGNKYMMEAFED"
},
{
"id": "realdebrid:HEVMDYWAXLSLM",
"name": "Luger (2026) [Bluray 720p][Esp].mkv",
"clean": "Luger",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/VWUUZIQGVLO76357"
},
{
"id": "realdebrid:MBSDHRJMTC3S2",
"name": "Spider-Man No Way Home 4Kremux2160.atomixhq.link.mkv",
"clean": "Spider-Man No Way Home 4Kremux2160 atomixhq link",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/S7GR3CYFAWOWC7AC"
},
{
"id": "realdebrid:YVLXIF6ILFL2U",
"name": "Iron Lung 2026 1080p.mkv",
"clean": "Iron Lung 2026 1080p",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/I6BVHXX7IFSUE1DB"
},
{
"id": "realdebrid:DLUAAXNZJ2ZHW",
"name": "From - S02E01 - Strangers in a Strange Land - WEBDL-1080p - Spanish 2x01 - audio [ES+EN] - sub [ES+EN].mkv",
"clean": "From - S02E01 - Strangers in a Strange Land - WEBDL-1080p - Spanish 2x01 - audio - sub",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/B64L3AL7NWVN41D1"
},
{
"id": "realdebrid:UP7DPBWVMH7JS",
"name": "La maldicion de Shelby Oaks (2026) [4k 2160p][Esp].mkv",
"clean": "La maldicion de Shelby Oaks",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/T4FFNHSB2XBV4D92"
},
{
"id": "realdebrid:PMBQI365H7PU6",
"name": "Steven Spielberg el rey midas de Hollywood (2024) [HDTV 1080p][Esp](wolfmax4k.com).mkv",
"clean": "Steven Spielberg el rey midas de Hollywood wolfmax4k com",
"year": "2024",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/WWCMHPUY4FPFAF67"
},
{
"id": "realdebrid:OWWSTBTFG4ZFO",
"name": "Fiasco total La tragedia del festival Astroworld (2025) [HDTV 1080p][Esp].mkv",
"clean": "Fiasco total La tragedia del festival Astroworld",
"year": "2025",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/YCBMS3HYQPLU27E1"
},
{
"id": "realdebrid:M4UVGSNUKTLA6",
"name": "Fiasco total La tragedia del festival Astroworld (2025) [HDTV 1080p][Esp].mkv",
"clean": "Fiasco total La tragedia del festival Astroworld",
"year": "2025",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/YCBMS3HYQPLU27E1"
},
{
"id": "realdebrid:7HQN7ELMLWTDI",
"name": "Fiasco total La tragedia del festival Astroworld (2025) [HDTV 1080p][Esp].mkv",
"clean": "Fiasco total La tragedia del festival Astroworld",
"year": "2025",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/YCBMS3HYQPLU27E1"
},
{
"id": "realdebrid:BRNWEZP2TGOEQ",
"name": "La momia de Lee Cronin Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5.1 BD-Rip hd 1080p.mkv",
"clean": "La momia de Lee Cronin Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/QDPUO2UWMDWX6C6E"
},
{
"id": "realdebrid:3MNHGJ3UM73VY",
"name": "super mario galaxy la película Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5.1 BD-Rip hd 1080p.mkv",
"clean": "super mario galaxy la película Castellano-Ingles+Subt Castellano-Ingles H264 E-AC3 5 1 BD-Rip hd 1080p",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/3GQUITYMNB4TUE1C"
},
{
"id": "realdebrid:PKZ3B7VEZXWZQ",
"name": "Super Mario Galaxy La película (2026) [4k 2160p][Esp].mkv",
"clean": "Super Mario Galaxy La película",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/VNTK5JCJGRHJOF0E"
},
{
"id": "realdebrid:6RE4EY5ZP2ILA",
"name": "Proyecto Salvacion (2026) [Bluray 1080p][Esp].mkv",
"clean": "Salvacion",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/BF3FGPVABGL6A610"
},
{
"id": "realdebrid:ASX6FSY2ZZD36",
"name": "Heidi Girl of the Alps (Alps no Shoujo Heidi) (Season 1) (BD 1080p)(HEVC x265 10bit)(Eng-Subs)-Judas[TGx]",
"clean": "Heidi Girl of the Alps Alps no Shoujo Heidi Season 1 BD 1080p HEVC x265 10bit Eng-Subs -Judas",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/3V5DNKCHKQOPGF89"
},
{
"id": "realdebrid:K4CJIWAJF3NKS",
"name": "Greenland El Ultimo Refugio (2020) [BluRay Rip][AC3 5.1 Castellano][www.PctFenix.com].avi",
"clean": "Greenland El Ultimo Refugio",
"year": "2020",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/MJR44YRCTRDMY2C9"
},
{
"id": "realdebrid:KDWFL34NA3MXE",
"name": "Greenland [4K UHDremux][2160p][HDR10][AC3 5.1 Castellano-True HD 7.1-Ingles+Subs][ES-EN]",
"clean": "Greenland",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/LUZ3G5KBQZYES397"
},
{
"id": "realdebrid:KW4WNKPELJM5O",
"name": "Greenland 2 [4k 2160p][Esp].mkv",
"clean": "Greenland 2",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/FS4OAHZSNYHFK568"
},
{
"id": "realdebrid:WEFMCETRL5BZM",
"name": "Resident Evil [FullUHD 2160p][Castellano AC3 5.1-Ingles DolbyTrueHD 7.1+Subs]",
"clean": "Resident Evil",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/6VMIEUSOVVIXYBD2"
},
{
"id": "realdebrid:CFPM3BI4C7EXO",
"name": "Proyecto Salvacion (2026) [4k 2160p][Esp].mkv",
"clean": "Salvacion",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/4Q6AFXH6KSKRO2AF"
},
{
"id": "realdebrid:5W6E6YBRA7V22",
"name": "Project.Hail.Mary.2026.2160p.KinoRip.h265.En.Es.Dual YG.mkv",
"clean": "Project Hail Mary 2160p KinoRip h265 En Es Dual YG",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/OMCP4MAMM775Q17A"
},
{
"id": "realdebrid:3GJHZMZQUQ544",
"name": "Project.Hail.Mary.2026.2160p.KinoRip.h265.En.Es.Dual YG.mkv",
"clean": "Project Hail Mary 2160p KinoRip h265 En Es Dual YG",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/OMCP4MAMM775Q17A"
},
{
"id": "realdebrid:YAFM2WGO4IR64",
"name": "Project.Hail.Mary.2026.2160p.KinoRip.h265.En.Es.Dual YG.mkv",
"clean": "Project Hail Mary 2160p KinoRip h265 En Es Dual YG",
"year": "2026",
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/OMCP4MAMM775Q17A"
},
{
"id": "realdebrid:RBESCUG7LBCH2",
"name": "Twilight Saga (2008-2012) 2160p H265 HDR10 AC3 5.1 ITA.ENG sub ita.eng Sp33dy94-MIRCrew",
"clean": "Twilight Saga 2008-2012 2160p H265 HDR10 AC3 5 1 ITA ENG sub ita eng Sp33dy94-MIRCrew",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/II6EYH6KIMNUI10A"
},
{
"id": "realdebrid:ZYLMP5J4H3VE6",
"name": "Monarch El Legado De Los Monstruos [4k 2160p][Cap.101](wolfmax4k.com).mkv",
"clean": "Monarch El Legado De Los Monstruos wolfmax4k com",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/LRB4AEEJCYOCU1E0"
},
{
"id": "realdebrid:37ZFJ63K6W7PM",
"name": "Stargate_Sg-1_05x12_Agujero_X-Tremo.avi",
"clean": "Stargate Sg-1 05x12 Agujero X-Tremo",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/7BTWUVXH2KH7KCC5"
},
{
"id": "realdebrid:6JJIFVQ5MZQEI",
"name": "TheAdventuresOfTomSawyer",
"clean": "TheAdventuresOfTomSawyer",
"year": null,
"tipo": "pelicula",
"enlace": "https://real-debrid.com/d/XJVUAD7LIIMFKDCC"
},
{
"id": "realdebrid:5KFSC2OVN37MO",
"name": "The Studio [HDTV 1080p][Cap.105]",
"clean": "The Studio",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/6AQCY2YCPY6FE9A9"
},
{
"id": "realdebrid:3AS7OJ3EXXKMC",
"name": "Stargate_Sg-1_05x10_2001.avi",
"clean": "Stargate Sg-1 05x10 2001",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/LHQ7LEN7VCRSS1C4"
},
{
"id": "realdebrid:UYIBLKOIL5HD6",
"name": "The Studio [HDTV 1080p][Cap.104]",
"clean": "The Studio",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/L7DDTFXUCCPF47A5"
},
{
"id": "realdebrid:CQ5CWIAKCM3ZS",
"name": "The Studio [HDTV 1080p][Cap.103]",
"clean": "The Studio",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/P3J5H2OCFG76WC67"
},
{
"id": "realdebrid:TMZRDVHVHUHRQ",
"name": "The Studio [HDTV 1080p][cap.102](wolfmax4k.com).mkv",
"clean": "The Studio wolfmax4k com",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/KHLUHJ4W7JMKA663"
},
{
"id": "realdebrid:S5HO3BW6ADQYI",
"name": "Euphoria [4k 2160p][Cap.302].mkv",
"clean": "Euphoria",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/YOFHJHFSKN6HOB11"
},
{
"id": "realdebrid:CCDJGDZJK3FRA",
"name": "Stargate_Sg-1_05x09_Entre_Dos_Fuegos.avi",
"clean": "Stargate Sg-1 05x09 Entre Dos Fuegos",
"year": null,
"tipo": "serie",
"enlace": "https://real-debrid.com/d/TYLADWGS6AEMQ28C"
}
]
}

View File

@@ -1,6 +1,7 @@
"""Lectura segura de datos ASUSWRT mediante la API web del router.""" """Lectura segura de datos ASUSWRT mediante la API web del router."""
import asyncio import asyncio
import html
import json import json
import os import os
import unicodedata import unicodedata
@@ -8,6 +9,7 @@ import unicodedata
import aiohttp import aiohttp
from asusrouter import AsusData, AsusRouter from asusrouter import AsusData, AsusRouter
from asusrouter.modules.parental_control import PCRuleType, ParentalControlRule from asusrouter.modules.parental_control import PCRuleType, ParentalControlRule
from asusrouter.modules.system import AsusSystem
def _load_env(): def _load_env():
@@ -110,3 +112,115 @@ def cambiar_bloqueo(nombre, blocked):
raise ValueError(f"Dispositivo no reconocido: {nombre}") raise ValueError(f"Dispositivo no reconocido: {nombre}")
asyncio.run(_set_block(device["mac"], device["name"], blocked)) asyncio.run(_set_block(device["mac"], device["name"], blocked))
return f"{'Bloqueado' if blocked else 'Activado'}: {device['name']}" return f"{'Bloqueado' if blocked else 'Activado'}: {device['name']}"
async def _aimesh_data():
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()
return await router.async_get_data(AsusData.AIMESH, force=True)
finally:
await router.async_disconnect()
await session.close()
async def _aimesh_action(cmd, mac=None):
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()
if cmd == "reconstruir":
return await router.async_set_state(AsusSystem.AIMESH_REBUILD)
if cmd == "reiniciar_todo":
return await router.async_set_state(AsusSystem.AIMESH_REBOOT)
return await router.async_set_state(
AsusSystem.NODE_REBOOT, device_list=mac
)
finally:
await router.async_disconnect()
await session.close()
def _nodo_texto(node):
tipo = "ROUTER" if getattr(node, "type", "") == "router" else "NODO"
alias = node.alias or node.model or "Sin nombre"
lineas = [f"{tipo} · {alias} ({node.model})"]
estado = "conectado" if node.status else "apagado"
lineas.append(f" estado: {estado}")
if node.ip:
lineas.append(f" ip: {node.ip}")
if node.fw:
lineas.append(f" firmware: {node.fw}")
if node.mac:
lineas.append(f" mac: {node.mac}")
parent = node.parent or {}
if parent:
rssi = parent.get("rssi", "?")
conexion = parent.get("connection", "?")
lineas.append(f" enlace: {conexion} · rssi={rssi}")
for banda, ssid in (node.ap or {}).items():
lineas.append(f" wifi {banda}: {ssid}")
return "\n".join(lineas)
def aimesh_estado():
data = asyncio.run(_aimesh_data()) or {}
if not data:
return "<pre>Sin datos AiMesh</pre>"
nodos = list(data.values())
nodos.sort(key=lambda n: getattr(n, "level", 0))
texto = "\n\n".join(_nodo_texto(n) for n in nodos)
return f"<pre>{html.escape(texto)}</pre>"
def _resolver_nodo(nombre):
data = asyncio.run(_aimesh_data()) or {}
clave = normalizar_nombre(nombre)
for mac, node in data.items():
if mac and mac.lower() == nombre.lower().strip():
return node
alias = normalizar_nombre(node.alias or "")
if clave and (alias == clave or clave in alias or alias in clave):
return node
return None
def aimesh_reiniciar_nodo(nombre):
node = _resolver_nodo(nombre)
if not node:
raise ValueError(f"Nodo AiMesh no encontrado: {nombre}")
asyncio.run(_aimesh_action("nodo", mac=node.mac))
return f"🔄 Nodo reiniciado: {node.alias or node.model} ({node.mac})"
def aimesh_reconstruir():
asyncio.run(_aimesh_action("reconstruir"))
return "🛠️ AiMesh reconstruida. Los nodos se reconectarán en unos minutos."
def aimesh_reiniciar_todo():
asyncio.run(_aimesh_action("reiniciar_todo"))
return "⚠️ Router y todos los nodos se están reiniciando..."

View File

@@ -497,10 +497,115 @@ def procesar(texto, chat_id):
enviar( enviar(
"Uso: <code>/parental pausa Inés 60</code>, " "Uso: <code>/parental pausa Inés 60</code>, "
"<code>/parental desactivar Carolina</code> o " "<code>/parental desactivar Carolina</code> o "
"<code>/parental activar Inés</code>.", chat_id) "<code>/parental activar Inés</code>.", chat_id)
except Exception as e: except Exception as e:
log(f"parental error: {e}") log(f"parental error: {e}")
enviar(f"❌ Error en control parental: {e}", chat_id) enviar(f"❌ Error en control parental: {e}", chat_id)
elif comando == "aiMesh":
partes_mesh = argumento.split()
accion_mesh = partes_mesh[0].lower() if partes_mesh else "estado"
try:
from asus_api_control import (
aimesh_estado,
aimesh_reconstruir,
aimesh_reiniciar_nodo,
)
if accion_mesh in ("estado", "nodos", "listar"):
enviar("🌐 <b>AIMESH ASUS</b>\n" + aimesh_estado(), chat_id)
elif accion_mesh == "reiniciar" and len(partes_mesh) >= 2:
if partes_mesh[-1] != "confirmar":
enviar("Para reiniciar un nodo responde: <code>/aiMesh reiniciar &lt;nodo&gt; confirmar</code>", chat_id)
else:
nodo = " ".join(partes_mesh[1:-1])
enviar(f"🔄 Reiniciando el nodo <b>{nodo}</b>...", chat_id)
enviar(aimesh_reiniciar_nodo(nodo), chat_id)
elif accion_mesh == "reconstruir":
if len(partes_mesh) < 2 or partes_mesh[-1] != "confirmar":
enviar("Para reconstruir la malla responde: <code>/aiMesh reconstruir confirmar</code>", chat_id)
else:
enviar("⚠️ Reconstruyendo la malla AiMesh...", chat_id)
enviar(aimesh_reconstruir(), chat_id)
else:
enviar(
"Uso:\n<code>/aiMesh estado</code> — nodos y señal\n"
"<code>/aiMesh reiniciar &lt;nodo&gt; confirmar</code>\n"
"<code>/aiMesh reconstruir confirmar</code>", chat_id)
except Exception as e:
log(f"aiMesh error: {e}")
enviar(f"❌ Error en AiMesh: {e}", chat_id)
elif comando == "qos":
partes_qos = argumento.lower().split()
accion_qos = partes_qos[0] if partes_qos else "estado"
try:
from router_control import qos_apagar, qos_estado, qos_set
if accion_qos == "estado":
enviar("⚙️ <b>QOS ASUS</b>\n" + qos_estado(), chat_id)
elif accion_qos in ("adaptive", "tradicional") and len(partes_qos) >= 2 and partes_qos[-1] == "confirmar":
enviar("⏳ Aplicando QoS...", chat_id)
enviar(qos_set(accion_qos), chat_id)
elif accion_qos == "apagar" and len(partes_qos) >= 2 and partes_qos[-1] == "confirmar":
enviar("⏳ Apagando QoS...", chat_id)
enviar(qos_apagar(), chat_id)
else:
enviar(
"Uso:\n<code>/qos estado</code>\n"
"<code>/qos adaptive confirmar</code> — auto gaming/streaming\n"
"<code>/qos tradicional confirmar</code> — prioridades manuales\n"
"<code>/qos apagar confirmar</code>", chat_id)
except Exception as e:
log(f"qos error: {e}")
enviar(f"❌ Error en QoS: {e}", chat_id)
elif comando == "limite":
partes_lim = argumento.split()
accion_lim = partes_lim[0].lower() if partes_lim else "listar"
try:
from router_control import limite_listar, limite_poner, limite_quitar
if accion_lim == "listar":
enviar("🚦 <b>LÍMITES DE VELOCIDAD</b>\n" + limite_listar(), chat_id)
elif accion_lim in ("poner", "set") and len(partes_lim) >= 4:
try:
dl = float(partes_lim[-2])
ul = float(partes_lim[-1])
except ValueError:
enviar("Las velocidades deben ser números en Mbps.", chat_id)
else:
dispositivo = " ".join(partes_lim[1:-2]) or partes_lim[1]
enviar(limite_poner(dispositivo, dl, ul), chat_id)
elif accion_lim in ("quitar", "del", "remove") and len(partes_lim) >= 2:
dispositivo = " ".join(partes_lim[1:])
enviar(limite_quitar(dispositivo), chat_id)
else:
enviar(
"Uso:\n<code>/limite listar</code>\n"
"<code>/limite poner &lt;dispositivo&gt; &lt;bajada&gt; &lt;subida&gt;</code> (Mbps)\n"
" ✏️ Ej: <code>/limite poner NAS 50 20</code>\n"
"<code>/limite quitar &lt;dispositivo&gt;</code>", chat_id)
except Exception as e:
log(f"limite error: {e}")
enviar(f"❌ Error en límites: {e}", chat_id)
elif comando == "seguridad":
partes_seg = argumento.lower().split()
accion_seg = partes_seg[0] if partes_seg else "estado"
try:
from router_control import (
seguridad_estado,
seguridad_keyword,
)
if accion_seg == "estado":
enviar("🛡️ <b>SEGURIDAD ASUS</b>\n" + seguridad_estado(), chat_id)
elif accion_seg in ("bloquear", "desbloquear") and len(partes_seg) >= 2:
palabra = " ".join(partes_seg[1:])
activar = accion_seg == "bloquear"
enviar(seguridad_keyword(palabra, activar), chat_id)
else:
enviar(
"Uso (filtro de contenidos AiProtection):\n"
"<code>/seguridad estado</code>\n"
"<code>/seguridad bloquear &lt;palabra&gt;</code> — p. ej. tiktok\n"
"<code>/seguridad desbloquear &lt;palabra&gt;</code>", chat_id)
except Exception as e:
log(f"seguridad error: {e}")
enviar(f"❌ Error en seguridad: {e}", chat_id)
elif t in ("pelis", "/pelis"): elif t in ("pelis", "/pelis"):
procesar_pelis(chat_id) procesar_pelis(chat_id)
elif t in ("series", "/series"): elif t in ("series", "/series"):
@@ -579,15 +684,15 @@ def procesar(texto, chat_id):
" ✏️ Ej: <code>/reboot</code> → confirmar con <code>/confirmar-reboot</code>\n" " ✏️ Ej: <code>/reboot</code> → confirmar con <code>/confirmar-reboot</code>\n"
"<b>24.</b> <code>ayuda</code> — este índice\n" "<b>24.</b> <code>ayuda</code> — este índice\n"
" ✏️ Ej: <code>/ayuda</code>\n\n" " ✏️ Ej: <code>/ayuda</code>\n\n"
"━━━ 🆕 ROUTER ASUS — PRÓXIMAMENTE ━━━\n" "━━━ 🌐 ROUTER ASUS — MESH / QoS / SEGURIDAD ━━━\n"
"<b>25.</b> <code>qos</code> — prioridad de ancho de banda por tipo de tráfico\n" "<b>25.</b> <code>qos estado</code> — ancho de banda con prioridades\n"
" ✏️ Ej: <code>/qos modo adaptive</code> — gaming/streaming con prioridad\n" " ✏️ Ej: <code>/qos adaptive confirmar</code> — auto gaming/streaming\n"
"<b>26.</b> <code>aiMesh</code> — red en malla con tus 2 módulos ASUS\n" "<b>26.</b> <code>aiMesh estado</code> — nodos, señal y roaming unificado\n"
" ✏️ Ej: <code>/aiMesh estado</code> — nodos, señal y roaming unificado\n" " ✏️ Ej: <code>/aiMesh reiniciar nodo confirmar</code>\n"
"<b>27.</b> <code>seguridad</code> — AiProtection: bloqueo de webs por categoría\n" "<b>27.</b> <code>seguridad estado</code> — AiProtection: filtro de webs\n"
" ✏️ Ej: <code>/seguridad bloquear Inés redes sociales</code>\n" " ✏️ Ej: <code>/seguridad bloquear tiktok</code>\n"
"<b>28.</b> <code>limite</code> — límite de velocidad por dispositivo\n" "<b>28.</b> <code>limite listar</code> — límite de velocidad por dispositivo\n"
" ✏️ Ej: <code>/limite poner NAS 50 20</code> — 50/20 Mbps", " ✏️ Ej: <code>/limite poner NAS 50 20</code> — 50/20 Mbps",
chat_id) chat_id)
elif t in ("servicios", "/servicios"): elif t in ("servicios", "/servicios"):
estado_svc = sh("systemctl is-active pi-bot tv-api autossh-tunel") estado_svc = sh("systemctl is-active pi-bot tv-api autossh-tunel")

View File

@@ -2,3 +2,5 @@ opencv-python
requests requests
python-dotenv python-dotenv
ultralytics ultralytics
asusrouter==1.21.3
aiohttp

View File

@@ -143,6 +143,245 @@ def reiniciar():
return _run("reboot") return _run("reboot")
QOS_TIPOS = {
"0": "Traditional (por prioridades)",
"1": "Adaptive (auto gaming/streaming)",
"2": "Bandwidth limiter",
"3": "GeForce Now",
}
def _set_nvram(entradas, servicio=None):
partes = [
f"nvram set {clave}={shlex.quote(str(valor))}"
for clave, valor in entradas
]
partes.append("nvram commit")
if servicio:
partes.append(f"service {servicio}")
return _run("; ".join(partes))
def _nombre_por_mac(mac):
_devices, _by_mac = _inventario()
for ip, m, nombre in _devices:
if m == mac.upper():
return nombre
try:
from asus_api_control import DEVICE_ALIASES
for _alias, info in DEVICE_ALIASES.items():
if info["mac"].upper() == mac.upper():
return info["name"]
except Exception:
pass
return f"unknown-{mac}"
def _resolver_mac(dispositivo):
valor = str(dispositivo or "").strip()
if valor.lower().count(":") == 5:
return valor.upper()
try:
from asus_api_control import normalizar_nombre, resolver_dispositivo
except Exception:
raise ValueError(f"Dispositivo no reconocido: {dispositivo}")
info = resolver_dispositivo(valor)
if info:
return info["mac"].upper()
_devices, _by_mac = _inventario()
clave = normalizar_nombre(valor)
for ip, m, nombre in _devices:
n = normalizar_nombre(nombre)
if clave and (n == clave or clave in n or n in clave):
return m
raise ValueError(
f"Dispositivo no reconocido: {dispositivo}. Usa MAC o un alias."
)
def qos_estado():
enable = (_run("nvram get qos_enable") or "").strip()
tipo = (_run("nvram get qos_type") or "").strip()
activado = "sí" if enable == "1" else "no"
modo = QOS_TIPOS.get(tipo, f"desconocido ({tipo})")
texto = f"Activado: {activado}\nModo: {modo}"
return f"<pre>{html.escape(texto)}</pre>"
def qos_set(modo):
conf = {
"adaptive": ("1", "1"),
"tradicional": ("0", "0"),
}
if modo not in conf:
raise ValueError("Modo no válido: usa adaptive o tradicional")
qos_enable, qos_type = conf[modo]
resultado = _set_nvram(
[("qos_enable", qos_enable), ("qos_type", qos_type)], "restart_qos"
)
if resultado.startswith("Error "):
return resultado
nombre = QOS_TIPOS[qos_type]
return (
f"QoS activado en modo <b>{nombre}</b>. "
"Ten en cuenta que QoS desactiva la aceleración NAT."
)
def qos_apagar():
resultado = _set_nvram([("qos_enable", "0")], "restart_qos")
if resultado.startswith("Error "):
return resultado
return "QoS desactivado. Aceleración NAT restaurada."
def _limite_parse():
raw = _run("nvram get qos_bw_rulelist") or ""
reglas = []
for parte in str(raw).split("<"):
parte = parte.strip()
if not parte:
continue
campos = parte.rstrip(">").split(">")
if len(campos) < 4:
continue
en, mac, dl, ul = campos[:4]
if str(mac).count(":") != 5:
continue
reglas.append({
"enable": en,
"mac": mac.upper(),
"dl": dl,
"ul": ul,
})
return raw, reglas
def _limite_payload(reglas):
partes = [
f"{r['enable']}>{r['mac']}>{r['dl']}>{r['ul']}>{idx}"
for idx, r in enumerate(reglas)
]
return "<".join(partes)
def _limite_guardar(reglas):
payload = _limite_payload(reglas)
if reglas:
entradas = [
("qos_enable", "1"),
("qos_type", "2"),
("qos_bw_rulelist", payload),
]
else:
entradas = [
("qos_enable", "0"),
("qos_bw_rulelist", payload),
]
return _set_nvram(entradas, "restart_qos")
def limite_listar():
_raw, reglas = _limite_parse()
if not reglas:
return "<pre>No hay reglas de límite activas.</pre>"
lineas = ["ACTIVADO DISPOSITIVO/MAC BAJADA SUBIDA"]
for r in reglas:
nombre = _nombre_por_mac(r["mac"])
dl = f"{int(r['dl']) / 1024:.0f}" if r["dl"].isdigit() else r["dl"]
ul = f"{int(r['ul']) / 1024:.0f}" if r["ul"].isdigit() else r["ul"]
lineas.append(
f"{'sí' if r['enable'] == '1' else 'no':<9} "
f"{nombre:<20} {dl:>7} {ul:>7} Mbps"
)
return f"<pre>{html.escape(chr(10).join(lineas))}</pre>"
def limite_poner(dispositivo, dl_mbps, ul_mbps):
mac = _resolver_mac(dispositivo)
if dl_mbps <= 0 or ul_mbps <= 0:
raise ValueError("Las velocidades deben ser mayores que 0")
_raw, reglas = _limite_parse()
reglas = [r for r in reglas if r["mac"] != mac]
reglas.append({
"enable": "1",
"mac": mac,
"dl": str(int(dl_mbps * 1024)),
"ul": str(int(ul_mbps * 1024)),
})
resultado = _limite_guardar(reglas)
if resultado.startswith("Error "):
return resultado
return (
f"Límite aplicado a <b>{_nombre_por_mac(mac)}</b>: "
f"{dl_mbps:.0f} Mbps bajada / {ul_mbps:.0f} Mbps subida. "
"El bandwidth limiter desactiva la aceleración NAT."
)
def limite_quitar(dispositivo):
mac = _resolver_mac(dispositivo)
_raw, reglas = _limite_parse()
if not any(r["mac"] == mac for r in reglas):
return f"No hay límite activo para {dispositivo}."
reglas = [r for r in reglas if r["mac"] != mac]
resultado = _limite_guardar(reglas)
if resultado.startswith("Error "):
return resultado
if not reglas:
return f"Límite retirado de <b>{_nombre_por_mac(mac)}</b>. "
return f"Límite retirado de <b>{_nombre_por_mac(mac)}</b>."
def seguridad_estado():
claves = (
"bwdpi_enable",
"yadns_enable_x",
"yadns_mode",
)
lineas = []
for clave in claves:
lineas.append(f"{clave}={_run(f'nvram get {clave}')}")
keywords = _run("nvram get keyword_rulelist") or ""
palabras = [p for p in str(keywords).split("<") if p.strip()]
lineas.append(
"palabras filtradas: "
+ (", ".join(palabras) if palabras else "(ninguna)")
)
return f"<pre>{html.escape(chr(10).join(lineas))}</pre>"
def _keywords_actuales():
raw = _run("nvram get keyword_rulelist") or ""
return [p.strip().lower() for p in str(raw).split("<") if p.strip()]
def seguridad_keyword(palabra, activar):
palabra = palabra.strip().lower()
palabras = _keywords_actuales()
if activar:
if palabra in palabras:
return f"La palabra <b>{palabra}</b> ya estaba en el filtro."
palabras.append(palabra)
else:
if palabra not in palabras:
return f"La palabra <b>{palabra}</b> no estaba en el filtro."
palabras = [p for p in palabras if p != palabra]
payload = "".join(f"<{p}>" for p in palabras)
entradas = [("keyword_rulelist", payload)]
if palabras:
entradas.insert(0, ("bwdpi_enable", "1"))
resultado = _set_nvram(entradas, "restart_bwdpi")
if resultado.startswith("Error "):
return resultado
accion = "bloqueada" if activar else "desbloqueada"
if palabras:
resto = f" Filtro activo: {', '.join(palabras)}."
else:
resto = " Filtro de contenidos vacío."
return f"Palabra <b>{palabra}</b> {accion}.{resto}"
if __name__ == "__main__": if __name__ == "__main__":
action = sys.argv[1] if len(sys.argv) > 1 else "estado" action = sys.argv[1] if len(sys.argv) > 1 else "estado"
actions = { actions = {
@@ -153,5 +392,11 @@ if __name__ == "__main__":
"reservar": reservar_dhcp, "reservar": reservar_dhcp,
"reservas": reservas, "reservas": reservas,
"parental": control_parental, "parental": control_parental,
"qos_estado": qos_estado,
"qos_adaptive": lambda: qos_set("adaptive"),
"qos_tradicional": lambda: qos_set("tradicional"),
"qos_apagar": qos_apagar,
"limite_listar": limite_listar,
"seguridad_estado": seguridad_estado,
} }
print(actions.get(action, estado)()) print(actions.get(action, estado)())