03-oct: superguardado automático
This commit is contained in:
117
scripts/monitor_estado/config.py
Normal file
117
scripts/monitor_estado/config.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""config.py — Carga y valida la configuracion del monitor de estado.
|
||||
|
||||
La configuracion vive en `INFRAESTRUCTURA/estado/monitor/config.json` (runtime, no
|
||||
versionada). Si no existe, se crea a partir de la plantilla `config.example.json`
|
||||
que va junto a este modulo, de modo que el monitor arranca siempre con valores validos.
|
||||
|
||||
Las 5 fuentes de Telegram y sus identificadores se resolvieron desde la sesion real
|
||||
de Telegram (ver plan 005).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# El modulo vive en INFRAESTRUCTURA/scripts/monitor_estado/; su padre es scripts/,
|
||||
# donde esta infra_paths.py.
|
||||
_SCRIPTS_DIR = Path(__file__).resolve().parent.parent
|
||||
if str(_SCRIPTS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_SCRIPTS_DIR))
|
||||
|
||||
import infra_paths # noqa: E402
|
||||
|
||||
# Directorio raiz de datos runtime del monitor (gitignored).
|
||||
MONITOR_DIR = infra_paths.ESTADO_DIR / "monitor"
|
||||
CONFIG_PATH = MONITOR_DIR / "config.json"
|
||||
EXAMPLE_PATH = Path(__file__).resolve().parent / "config.example.json"
|
||||
|
||||
|
||||
def default_config() -> dict:
|
||||
"""Configuracion por defecto (equivale a la plantilla de ejemplo)."""
|
||||
return {
|
||||
"intervalo_minutos": 30,
|
||||
"retencion_dias": 30,
|
||||
"telegram": {
|
||||
# Sesion Telethon DEDICADA (no reutiliza la del watcher, que esta bloqueada).
|
||||
"sesion": "~/.telegram-agent/sessions/monitor",
|
||||
"telefono": "+34657866417",
|
||||
"fuentes": [
|
||||
{"nombre": "oscar_rodriguez", "id": 129020043},
|
||||
{"nombre": "isa", "id": 16182959},
|
||||
{"nombre": "daniel_garcia", "id": 8249301783},
|
||||
{"nombre": "antonio_romero", "id": 880864397},
|
||||
{"nombre": "desarrollo_software", "id": -1002693936231},
|
||||
],
|
||||
},
|
||||
"correo": {"cuenta": "jminguez@prolongo.es", "horas": 24},
|
||||
"jira": {"proyecto": "DS"},
|
||||
"ia": {"base_url": "http://localhost:20128/v1", "modelo": "antigravity/gemini-2.5-flash-lite"},
|
||||
"aviso": {"voz": True},
|
||||
}
|
||||
|
||||
|
||||
def _merge(base: dict, override: dict) -> dict:
|
||||
"""Mezcla recursiva de diccionarios (override gana; no borra claves base)."""
|
||||
result = copy.deepcopy(base)
|
||||
for clave, valor in override.items():
|
||||
if isinstance(valor, dict) and isinstance(result.get(clave), dict):
|
||||
result[clave] = _merge(result[clave], valor)
|
||||
else:
|
||||
result[clave] = valor
|
||||
return result
|
||||
|
||||
|
||||
def _escribir_plantilla(destino: Path) -> None:
|
||||
"""Escribe la config de ejemplo en `destino` (creando el directorio)."""
|
||||
destino.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(destino, "w", encoding="utf-8") as f:
|
||||
json.dump(default_config(), f, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
def cargar(crear_si_falta: bool = True) -> dict:
|
||||
"""Carga la configuracion, creando `config.json` desde la plantilla si no existe.
|
||||
|
||||
Devuelve siempre un dict completo (las claves que falten se toman por defecto).
|
||||
"""
|
||||
cfg = default_config()
|
||||
if not CONFIG_PATH.exists():
|
||||
if not crear_si_falta:
|
||||
return cfg
|
||||
_escribir_plantilla(CONFIG_PATH)
|
||||
try:
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
usuario = json.load(f)
|
||||
if isinstance(usuario, dict):
|
||||
cfg = _merge(cfg, usuario)
|
||||
except Exception as e: # config corrupta -> seguimos con los valores por defecto
|
||||
print(f"[monitor_estado] Aviso: no se pudo leer {CONFIG_PATH}: {e}", file=sys.stderr)
|
||||
return cfg
|
||||
|
||||
|
||||
def fuentes_telegram(cfg: dict | None = None) -> list[dict]:
|
||||
"""Devuelve la lista de fuentes de Telegram configuradas."""
|
||||
cfg = cfg or cargar()
|
||||
return list(cfg.get("telegram", {}).get("fuentes", []))
|
||||
|
||||
|
||||
def resumen_config(cfg: dict | None = None) -> str:
|
||||
"""Texto legible con las fuentes configuradas (para el CLI/estado)."""
|
||||
cfg = cfg or cargar()
|
||||
lineas = [
|
||||
f"Intervalo : {cfg.get('intervalo_minutos')} min",
|
||||
f"Retencion : {cfg.get('retencion_dias')} dias",
|
||||
f"Correo : {cfg.get('correo', {}).get('cuenta')} (ultimas {cfg.get('correo', {}).get('horas')} h)",
|
||||
f"Jira : proyecto {cfg.get('jira', {}).get('proyecto')}",
|
||||
"Telegram:",
|
||||
]
|
||||
for f in fuentes_telegram(cfg):
|
||||
lineas.append(f" - {f.get('nombre')} (id {f.get('id')})")
|
||||
return "\n".join(lineas)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(resumen_config())
|
||||
Reference in New Issue
Block a user