Files
vps_monitoring/scripts/sync_keenetic_import.py
Андрей Бобырев 746ecfc343 fix(keenetic): preserve URL scheme/ports and IPv4 polling
Use web_url scheme and host:port for API base URL instead of
forcing https. Force IPv4 in aiohttp to avoid IPv6 hangs on
KeenDNS. Sync canonical 24-router list with Russian names.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 00:52:11 +03:00

93 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Sync keenetic.json with canonical router list (24 entries, exact URLs)."""
import json
import sys
from datetime import datetime
from pathlib import Path
from urllib.parse import urlparse
# Authoritative list: Russian name, exact web URL, AnyDesk (optional)
IMPORT = [
("Москва Сити Асланян", "https://moscowcity.netcraze.pro:5443", ""),
("Вымпел Асланян", "https://vimpel.netcraze.link", ""),
("Асланян Квартира", "https://eropkinskii.netcraze.club", ""),
("Новоглаголево Лилиана", "http://91.77.164.164", ""),
("Лилиана Фотиева", "https://fotieva.netcraze.link", ""),
("Лилиана Лофт", "https://loftliliana.netcraze.pro", "1020687391"),
("Артем vtb126", "https://vtb126.netcraze.club", ""),
("Артем Квартира Подмосковный", "http://95.165.93.46:777", "1527495291"),
("Артем Квартира 2", "https://pomidor.netcraze.pro:5443", ""),
("Артем Малаховка", "https://malahovka1.netcraze.pro:5083", ""),
("Арсен Квартира", "https://arsen53.netcraze.link", "1633258458"),
("Вишневый Сад", "https://visheviisad.netcraze.link", "135128054"),
("Антоновка", "https://antonovka.netcraze.pro:5083", "1356582394"),
("Цехомский Николай Дом", "https://peredelkino15.netcraze.link", "1726497286"),
("Цехомский Домик Истра", "https://utrodomik.netcraze.club:5443", "451325164"),
("Маршала Жукова Квартира", "https://marshalaszukova.netcraze.pro", "1677737879"),
("Ломакин Квартира", "https://lomakinkvartira.netcraze.pro", ""),
("Ломакин Дача", "https://lomakindacha.netcraze.pro:8443/", "815142240"),
("КАСТАНАЕВСКАЯ", "https://kastanaevskaya.netcraze.link", "1627649255"),
("Чиверево Меламед", "https://chiverevo.netcraze.pro", "846367657"),
("Рав Гедалья Меламед", "https://ravged.netcraze.pro", ""),
("Кургин Дом", "https://kurgin.netcraze.link", "709243112"),
("Таланова Дом", "https://talanovadom.netcraze.pro", "951049627"),
("Загорье Дом", "https://zagorie.netcraze.link", "249788953"),
]
def normalize_web_url(url: str) -> str:
url = (url or "").strip().rstrip("/")
if not url:
return ""
if not url.startswith(("http://", "https://")):
url = "https://" + url
return url
def host_from_url(url: str) -> str:
parsed = urlparse(normalize_web_url(url))
return parsed.netloc or url.replace("https://", "").replace("http://", "").rstrip("/")
def make_device(name, keenetic_url, login, password, anydesk="", added=None):
keenetic_url = normalize_web_url(keenetic_url)
host = host_from_url(keenetic_url)
return {
"name": name,
"host": host,
"web_url": keenetic_url,
"anydesk": (anydesk or "").strip(),
"login": login,
"password": password,
"added": added or datetime.now().isoformat(),
}
def sync_file(path: Path, default_password: str):
with open(path) as f:
old = json.load(f)
by_name = {d.get("name", ""): d for d in old}
by_host = {d.get("host", ""): d for d in old}
path.with_suffix(".json.bak").write_text(
json.dumps(old, indent=2, ensure_ascii=False), encoding="utf-8"
)
result = []
for name, url, ad in IMPORT:
host = host_from_url(url)
prev = by_name.get(name) or by_host.get(host, {})
login = prev.get("login", "admin")
password = prev.get("password") or default_password
added = prev.get("added")
result.append(make_device(name, url, login, password, ad, added))
path.write_text(json.dumps(result, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Wrote {len(result)} devices (canonical import)")
if __name__ == "__main__":
p = Path(sys.argv[1] if len(sys.argv) > 1 else "/opt/vps-monitoring/data/keenetic.json")
pwd = sys.argv[2] if len(sys.argv) > 2 else "Ipadipad1"
sync_file(p, pwd)