| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- #!/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()
|