alert_relay.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #!/usr/bin/env python3
  2. """HyperDX alert-webhook → Telegram relay (ohmu).
  3. Võtab vastu HyperDX "generic" webhook POST-i ja saadab sisu Telegrami.
  4. Kasutab ainult standardteeki (ei vaja pip-pakette).
  5. HyperDX generic-webhook saadab tavaliselt: {"text": "..."} (Slack-laadne).
  6. Varasem versioon ootas välju name/state/message, mida generic payload ei sisalda
  7. -> sõnumis oli "Seis: ?" ja toor-JSON. Nüüd saadetakse `text` otse.
  8. Env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID (vaikimisi 5036685258)
  9. Kuulab: 0.0.0.0:9099
  10. """
  11. from __future__ import annotations
  12. import json
  13. import os
  14. import urllib.parse
  15. import urllib.request
  16. from http.server import BaseHTTPRequestHandler, HTTPServer
  17. TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "").strip()
  18. CHAT = os.environ.get("TELEGRAM_CHAT_ID", "5036685258")
  19. PORT = int(os.environ.get("PORT", "9099"))
  20. # HyperDX generic payload võib olla pikk; Telegram lubab 4096 tähemärki.
  21. MAX_LEN = 4000
  22. def send(text: str) -> None:
  23. if not TOKEN:
  24. print("[relay] TELEGRAM_BOT_TOKEN puudub", flush=True)
  25. return
  26. # Ilma parse_mode'ita (plain text) — HyperDX tekst sisaldab Markdown-erimärke
  27. # (nt [ ] * _), mis muidu põhjustaksid Telegrami "can't parse entities" vea.
  28. data = urllib.parse.urlencode({"chat_id": CHAT, "text": text[:MAX_LEN]}).encode()
  29. req = urllib.request.Request(f"https://api.telegram.org/bot{TOKEN}/sendMessage", data=data)
  30. try:
  31. with urllib.request.urlopen(req, timeout=20) as r:
  32. r.read()
  33. except Exception as e: # noqa: BLE001
  34. print("[relay] Telegram viga:", str(e)[:200], flush=True)
  35. def format_msg(data: dict) -> str:
  36. # 1) HyperDX generic webhook -> {"text": "..."} — saada otse.
  37. text = data.get("text")
  38. if isinstance(text, str) and text.strip():
  39. return text.strip()
  40. # 2) Muud payload'id: proovi tuntud välju.
  41. def pick(*keys):
  42. for k in keys:
  43. v = data.get(k) or (data.get("alert") or {}).get(k)
  44. if v not in (None, ""):
  45. return v
  46. return None
  47. name = pick("name", "alertName", "title")
  48. state = pick("state", "status", "event")
  49. msg = pick("message", "note", "description")
  50. lines: list[str] = []
  51. if name:
  52. lines.append(str(name))
  53. if state:
  54. icon = {"ALERT": "🔴", "alert": "🔴", "RESOLVED": "✅", "resolved": "✅", "OK": "✅"}.get(str(state), "⚠️")
  55. lines.append(f"{icon} Seis: {state}")
  56. if msg:
  57. lines.append(str(msg)[:1500])
  58. if not lines:
  59. lines.append(json.dumps(data, ensure_ascii=False)[:1200])
  60. return "\n".join(lines)
  61. class H(BaseHTTPRequestHandler):
  62. def do_POST(self): # noqa: N802
  63. n = int(self.headers.get("Content-Length", "0") or 0)
  64. raw = self.rfile.read(n) if n else b"{}"
  65. try:
  66. data = json.loads(raw or b"{}")
  67. except Exception: # noqa: BLE001
  68. data = {"text": raw.decode("utf-8", "replace")[:1000]}
  69. send(format_msg(data if isinstance(data, dict) else {"text": str(data)}))
  70. self.send_response(200)
  71. self.send_header("Content-Type", "application/json")
  72. self.end_headers()
  73. self.wfile.write(b'{"ok":true}')
  74. def do_GET(self): # noqa: N802
  75. self.send_response(200)
  76. self.end_headers()
  77. self.wfile.write(b"alert-relay ok")
  78. def log_message(self, *args): # vaikimisi logi maha
  79. return
  80. if __name__ == "__main__":
  81. print(f"[relay] kuulan :{PORT} -> Telegram {CHAT}", flush=True)
  82. HTTPServer(("0.0.0.0", PORT), H).serve_forever()