import hashlib import json import time from typing import Any from sqlalchemy.orm import Session from app.config import get_settings from app.homelab.netdata import NetdataClient from app.homelab.state import get_state, set_state ALERT_COOLDOWN_SEC = 1800 def _alarm_key(alarm: dict[str, Any]) -> str: return f"{alarm.get('name')}:{alarm.get('status')}" def check_netdata_alerts(db: Session) -> list[str]: settings = get_settings() if not settings.netdata_alerts_enabled: return [] result = NetdataClient().fetch_alarms() if not result.get("ok"): return [] alarms = result.get("alarms") or [] significant = [ a for a in alarms if (a.get("status") or "").lower() in ("warning", "critical", "raised") ] if not significant: return [] prev_raw = get_state(db, "netdata_alarm_hashes") or "{}" try: prev_map: dict[str, float] = json.loads(prev_raw) except json.JSONDecodeError: prev_map = {} now = time.time() notices: list[str] = [] new_map = dict(prev_map) for alarm in significant: key = _alarm_key(alarm) digest = hashlib.sha256(json.dumps(alarm, sort_keys=True).encode()).hexdigest()[:16] state_key = f"netdata:{key}:{digest}" last_sent = prev_map.get(state_key, 0) if now - last_sent < ALERT_COOLDOWN_SEC: continue host = alarm.get("host") or "server" value = alarm.get("value_string") or "" info = alarm.get("info") or alarm.get("name") or "алерт" status = alarm.get("status") or "alert" link = settings.netdata_public_url link_part = f" [Netdata]({link})" if link else "" notices.append( f"⚠️ **Netdata** · {host}: {info} — {status}" + (f" ({value})" if value else "") + link_part ) new_map[state_key] = now if notices: set_state(db, "netdata_alarm_hashes", json.dumps(new_map)) return notices