from __future__ import annotations import json import logging import time import urllib.parse import urllib.request from datetime import datetime from config import Config from state import State log = logging.getLogger(__name__) ICONS = {"CRITICAL": "πŸ”΄", "WARNING": "🟑", "INFO": "🟒"} class Alerter: def __init__(self, config: Config, state: State) -> None: self.config = config self.state = state # ── Persistent Alerting ─────────────────────────────────── # Gleiche Logik wie im Bash-Script: # - Neuer Fehler β†’ sofort melden, runs = 0 # - Fehler bleibt β†’ runs++, melden wenn runs >= repeat_runs # - Fehler weg β†’ Entwarnung, State lΓΆschen def should_alert(self, key: str, level: str) -> bool: repeat = ( self.config.alert.critical_repeat_runs if level == "CRITICAL" else self.config.alert.warning_repeat_runs ) active = self.state.get(f"{key}_active") runs: int = self.state.get(f"{key}_runs", 0) if active != "1": self.state.set(f"{key}_active", "1") self.state.set(f"{key}_runs", 0) return True runs += 1 self.state.set(f"{key}_runs", runs) if repeat == 0: return False if runs >= repeat: self.state.set(f"{key}_runs", 0) return True return False def clear_alert(self, key: str) -> bool: """Clear state and return True if the alert was previously active.""" was_active = self.state.get(f"{key}_active") == "1" self.state.delete(f"{key}_active", f"{key}_runs") return was_active # ── Sending ─────────────────────────────────────────────── def send_alert(self, level: str, title: str, body: str, key: str = "") -> None: icon = ICONS.get(level, "βšͺ") suffix = "" if key: runs: int = self.state.get(f"{key}_runs", 0) if runs > 0: suffix = f" (Erinnerung #{runs})" ts = datetime.now().strftime("%d.%m.%Y %H:%M:%S") msg = ( f"{icon} [{level}] {self.config.hostname}{suffix}\n" f"{title}\n\n" f"{body}\n\n" f"πŸ• {ts}" ) log.info("ALERT %s: %s", level, title) self._send_telegram(msg) def _send_telegram(self, text: str) -> bool: url = f"https://api.telegram.org/bot{self.config.telegram.token}/sendMessage" data = urllib.parse.urlencode({ "chat_id": self.config.telegram.chat_id, "text": text, "parse_mode": "HTML", }).encode() for attempt in range(1, 4): try: with urllib.request.urlopen(url, data=data, timeout=10) as resp: result = json.loads(resp.read()) if result.get("ok"): return True except Exception as e: log.warning("Telegram Fehler (Versuch %d/3): %s", attempt, e) if attempt < 3: time.sleep(attempt * 2) log.error("Telegram: Alert konnte nicht gesendet werden!") return False