05-sep: [vigilancia] comandos /aiMesh /qos /limite /seguridad para el ASUS desde Telegram
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
60
videoclub/listados/2026-09-05.md
Normal file
60
videoclub/listados/2026-09-05.md
Normal 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`
|
||||||
413
videoclub/snapshots/2026-09-05.json
Normal file
413
videoclub/snapshots/2026-09-05.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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..."
|
||||||
|
|||||||
@@ -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 <nodo> 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 <nodo> 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 <dispositivo> <bajada> <subida></code> (Mbps)\n"
|
||||||
|
" ✏️ Ej: <code>/limite poner NAS 50 20</code>\n"
|
||||||
|
"<code>/limite quitar <dispositivo></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 <palabra></code> — p. ej. tiktok\n"
|
||||||
|
"<code>/seguridad desbloquear <palabra></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,14 +684,14 @@ 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"):
|
||||||
|
|||||||
@@ -2,3 +2,5 @@ opencv-python
|
|||||||
requests
|
requests
|
||||||
python-dotenv
|
python-dotenv
|
||||||
ultralytics
|
ultralytics
|
||||||
|
asusrouter==1.21.3
|
||||||
|
aiohttp
|
||||||
|
|||||||
@@ -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)())
|
||||||
|
|||||||
Reference in New Issue
Block a user