diff --git a/homelab-ai-bot/monitor.py b/homelab-ai-bot/monitor.py index bc02a4cd..02adf53f 100644 --- a/homelab-ai-bot/monitor.py +++ b/homelab-ai-bot/monitor.py @@ -2,11 +2,24 @@ import sys import os +import json +import hashlib import requests +from datetime import datetime, timezone sys.path.insert(0, os.path.dirname(__file__)) from core import config, loki_client, proxmox_client, mail_client +ALERT_STATE_FILE = os.path.join(os.path.dirname(__file__), ".alert_state.json") +ALERT_COOLDOWN_MINUTES = { + "mail": 120, + "container": 30, + "ram": 30, + "panic": 60, + "silence": 60, + "default": 60, +} + def _get_tokens(cfg): tokens = {} @@ -126,6 +139,63 @@ def format_report() -> str: return "\n".join(lines) +def _load_alert_state() -> dict: + try: + with open(ALERT_STATE_FILE, "r") as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return {} + + +def _save_alert_state(state: dict): + try: + with open(ALERT_STATE_FILE, "w") as f: + json.dump(state, f) + except Exception: + pass + + +def _alert_key(alert_text: str) -> str: + return hashlib.md5(alert_text.encode()).hexdigest() + + +def _alert_category(alert_text: str) -> str: + if "📧" in alert_text or "Mail" in alert_text: + return "mail" + if "CT " in alert_text and "ist " in alert_text: + return "container" + if "RAM " in alert_text: + return "ram" + if "panic" in alert_text.lower() or "fatal" in alert_text.lower(): + return "panic" + if "Keine Logs" in alert_text: + return "silence" + return "default" + + +def _filter_new_alerts(alerts: list[str]) -> list[str]: + """Filtert bereits gemeldete Alerts. Gibt nur neue/fällige zurück.""" + state = _load_alert_state() + now = datetime.now(timezone.utc).timestamp() + new_alerts = [] + + for alert in alerts: + key = _alert_key(alert) + cat = _alert_category(alert) + cooldown = ALERT_COOLDOWN_MINUTES.get(cat, 60) * 60 + + last_sent = state.get(key, {}).get("ts", 0) + if now - last_sent > cooldown: + new_alerts.append(alert) + state[key] = {"ts": now, "text": alert[:80], "cat": cat} + + cutoff = now - 86400 + state = {k: v for k, v in state.items() if v.get("ts", 0) > cutoff} + + _save_alert_state(state) + return new_alerts + + def send_alert(token: str, chat_id: str, message: str): """Sendet eine Nachricht via Telegram.""" requests.post( @@ -144,8 +214,9 @@ def run_check_and_alert(): return alerts = check_all() - if alerts: - msg = "🔧 Hausmeister-Check\n\n" + "\n".join(alerts) + new_alerts = _filter_new_alerts(alerts) + if new_alerts: + msg = "🔧 Hausmeister-Check\n\n" + "\n".join(new_alerts) send_alert(token, chat_id, msg)