import time from datetime import datetime from aiogram import Bot from app import cfg from services.alert_mute import is_muted, is_auto_muted from services.incidents import log_incident _LAST_SENT: dict[str, float] = {} def _parse_hhmm(value: str) -> int | None: try: hours, minutes = value.strip().split(":", 1) h = int(hours) m = int(minutes) except Exception: return None if not (0 <= h <= 23 and 0 <= m <= 59): return None return h * 60 + m def _in_quiet_hours(alerts_cfg: dict) -> bool: quiet = alerts_cfg.get("quiet_hours", {}) if not quiet.get("enabled", False): return False start_min = _parse_hhmm(quiet.get("start", "23:00")) end_min = _parse_hhmm(quiet.get("end", "08:00")) if start_min is None or end_min is None: return False if start_min == end_min: return False now = datetime.now() now_min = now.hour * 60 + now.minute if start_min < end_min: return start_min <= now_min < end_min return now_min >= start_min or now_min < end_min async def notify( bot: Bot, chat_id: int, text: str, level: str = "info", key: str | None = None, category: str | None = None, ): alerts_cfg = cfg.get("alerts", {}) if category and is_muted(category): return if category and is_auto_muted(cfg, category): return if _in_quiet_hours(alerts_cfg): allow_critical = bool(alerts_cfg.get("quiet_hours", {}).get("allow_critical", True)) if not (allow_critical and level == "critical"): return dedup_sec = int(alerts_cfg.get("notify_cooldown_sec", alerts_cfg.get("cooldown_sec", 900))) if dedup_sec > 0: dedup_key = key or text now = time.time() last_time = _LAST_SENT.get(dedup_key, 0) if now - last_time < dedup_sec: return _LAST_SENT[dedup_key] = now try: await bot.send_message(chat_id, text) except Exception: pass try: log_incident(cfg, text) except Exception: pass