#!/usr/bin/env python3 """HyperDX alert-webhook → Telegram relay (ohmu). Võtab vastu HyperDX "generic" webhook POST-i ja saadab sisu Telegrami. Kasutab ainult standardteeki (ei vaja pip-pakette). HyperDX generic-webhook saadab tavaliselt: {"text": "..."} (Slack-laadne). Varasem versioon ootas välju name/state/message, mida generic payload ei sisalda -> sõnumis oli "Seis: ?" ja toor-JSON. Nüüd saadetakse `text` otse. Env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID (vaikimisi 5036685258) Kuulab: 0.0.0.0:9099 """ from __future__ import annotations import json import os import re from datetime import datetime, timezone import urllib.parse import urllib.request from http.server import BaseHTTPRequestHandler, HTTPServer TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "").strip() CHAT = os.environ.get("TELEGRAM_CHAT_ID", "5036685258") PORT = int(os.environ.get("PORT", "9099")) # HyperDX generic payload võib olla pikk; Telegram lubab 4096 tähemärki. MAX_LEN = 4000 try: from zoneinfo import ZoneInfo TZ = ZoneInfo("Europe/Tallinn") except Exception: # noqa: BLE001 TZ = None _TR_UTC = re.compile(r"Time Range \(UTC\): \[(.+?) - (.+?)\)") def _parse_utc(s): s = s.strip() for fmt in ("%b %d %I:%M:%S %p", "%b %d %H:%M:%S", "%Y-%m-%d %H:%M:%S"): try: dt = datetime.strptime(s, fmt) except ValueError: continue # HyperDX jätab aasta ära -> strptime annab 1900; kasuta jooksvat aastat, # muidu tuleb Tallinna ajalooline LMT-siire (+1:39) ja kellaaeg on vale. if dt.year == 1900: dt = dt.replace(year=datetime.now(timezone.utc).year) return dt.replace(tzinfo=timezone.utc) return None def _fmt_local(s): dt = _parse_utc(s) if dt is None: return s loc = dt.astimezone(TZ) if TZ else dt return loc.strftime("%b %d %H:%M:%S") def localize_times(text: str) -> str: """HyperDX esitab aja UTC-na -> näita Tallinna ajas (nagu teised teenused).""" def repl(m): return "Aeg (Tallinn): [%s - %s)" % (_fmt_local(m.group(1)), _fmt_local(m.group(2))) return _TR_UTC.sub(repl, text) def send(text: str) -> None: if not TOKEN: print("[relay] TELEGRAM_BOT_TOKEN puudub", flush=True) return # Ilma parse_mode'ita (plain text) — HyperDX tekst sisaldab Markdown-erimärke # (nt [ ] * _), mis muidu põhjustaksid Telegrami "can't parse entities" vea. data = urllib.parse.urlencode({"chat_id": CHAT, "text": text[:MAX_LEN]}).encode() req = urllib.request.Request(f"https://api.telegram.org/bot{TOKEN}/sendMessage", data=data) try: with urllib.request.urlopen(req, timeout=20) as r: r.read() except Exception as e: # noqa: BLE001 print("[relay] Telegram viga:", str(e)[:200], flush=True) def format_msg(data: dict) -> str: # 1) HyperDX generic webhook -> {"text": "..."} — saada otse. text = data.get("text") if isinstance(text, str) and text.strip(): return localize_times(text.strip()) # 2) Muud payload'id: proovi tuntud välju. def pick(*keys): for k in keys: v = data.get(k) or (data.get("alert") or {}).get(k) if v not in (None, ""): return v return None name = pick("name", "alertName", "title") state = pick("state", "status", "event") msg = pick("message", "note", "description") lines: list[str] = [] if name: lines.append(str(name)) if state: icon = {"ALERT": "🔴", "alert": "🔴", "RESOLVED": "✅", "resolved": "✅", "OK": "✅"}.get(str(state), "⚠️") lines.append(f"{icon} Seis: {state}") if msg: lines.append(str(msg)[:1500]) if not lines: lines.append(json.dumps(data, ensure_ascii=False)[:1200]) return localize_times("\n".join(lines)) class H(BaseHTTPRequestHandler): def do_POST(self): # noqa: N802 n = int(self.headers.get("Content-Length", "0") or 0) raw = self.rfile.read(n) if n else b"{}" try: data = json.loads(raw or b"{}") except Exception: # noqa: BLE001 data = {"text": raw.decode("utf-8", "replace")[:1000]} send(format_msg(data if isinstance(data, dict) else {"text": str(data)})) self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(b'{"ok":true}') def do_GET(self): # noqa: N802 self.send_response(200) self.end_headers() self.wfile.write(b"alert-relay ok") def log_message(self, *args): # vaikimisi logi maha return if __name__ == "__main__": print(f"[relay] kuulan :{PORT} -> Telegram {CHAT}", flush=True) HTTPServer(("0.0.0.0", PORT), H).serve_forever()