#!/usr/bin/env python3 """Beszel Webhook Receiver — Cria tickets Desk CRM a partir de alertas Beszel.""" import json, os, sys, logging import string, random from http.server import HTTPServer, BaseHTTPRequestHandler from datetime import datetime import pymysql PORT = int(sys.argv[sys.argv.index("--port") + 1]) if "--port" in sys.argv else 8650 DB_CONFIG = {"host": "server.descomplicar.pt", "port": 3306, "user": "ealmeida_desk24", "password": "9qPRdCGGqM4o", "database": "ealmeida_desk24", "charset": "utf8mb4"} # Configuração Desk CRM — Tickets DEPARTMENT_ID = 7 # Tecnologia PROJECT_ID = 65 # DES Stack Workflow ASSIGNED_STAFF_ID = 28 # Izito PRIORITY = 3 # Alta LOG = "/root/logs/beszel-webhook.log" os.makedirs(os.path.dirname(LOG), exist_ok=True) logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[logging.FileHandler(LOG, encoding="utf-8"), logging.StreamHandler()]) log = logging.getLogger("beszel-webhook") def get_db(): return pymysql.connect(**DB_CONFIG, cursorclass=pymysql.cursors.DictCursor) def ticket_key(): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=32)) def ticket_existe(cur, padrao): cur.execute( "SELECT ticketid FROM tbltickets WHERE subject LIKE %s AND status NOT IN (2,5) AND department=%s LIMIT 1", (padrao, DEPARTMENT_ID) ) return cur.fetchone() def criar_ticket(cur, assunto, mensagem): now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") key = ticket_key() cur.execute( "INSERT INTO tbltickets (subject, message, department, priority, status, date, project_id, assigned, ticketkey, adminread, clientread, userid) VALUES (%s, %s, %s, %s, 1, %s, %s, %s, %s, 1, 0, 0)", (assunto, mensagem, DEPARTMENT_ID, PRIORITY, now, PROJECT_ID, ASSIGNED_STAFF_ID, key) ) tid = cur.lastrowid log.info(f"Ticket #{tid} criado — {assunto}") return tid def fechar_ticket(cur, tid, nota): cur.execute("UPDATE tbltickets SET status=2, lastreply=NOW() WHERE ticketid=%s", (tid,)) now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") cur.execute( "INSERT INTO tblticket_replies (ticketid, message, date, staffid, admin) VALUES (%s, %s, %s, %s, 1)", (tid, f"

Auto-fecho (Beszel): {nota}

", now, ASSIGNED_STAFF_ID) ) log.info(f"Ticket #{tid} fechado: {nota}") class Handler(BaseHTTPRequestHandler): def do_POST(self): if self.path != "/beszel-alert": self.send_response(404); self.end_headers(); return body = self.rfile.read(int(self.headers.get("Content-Length", 0))) try: data = json.loads(body) except Exception: self.send_response(400); self.end_headers(); return titulo = data.get("title", "") mensagem = data.get("message", "") log.info(f"Webhook: {titulo} — {mensagem}") is_down = "down" in (mensagem + titulo).lower() is_up = "up" in mensagem.lower() or "recovered" in mensagem.lower() sname = data.get("system", "") or data.get("name", "") or data.get("host", "") if not sname: parts = mensagem.split(":") if len(parts) > 1: sname = parts[1].strip().split("(")[0].strip() try: db = get_db(); cur = db.cursor() if is_down and sname: if not ticket_existe(cur, f"[MONIT] {sname}%"): criar_ticket(cur, f"[MONIT] {sname} — sistema DOWN", f"

Beszel — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

{mensagem}

") elif is_up and sname: ex = ticket_existe(cur, f"[MONIT] {sname}%") if ex: fechar_ticket(cur, ex["ticketid"], f"{sname} voltou ao normal") db.commit(); cur.close(); db.close() except Exception as e: log.error(f"Erro Desk CRM: {e}") self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers() self.wfile.write(b'{"status":"ok"}') def log_message(self, fmt, *args): log.info(f"{self.client_address[0]} - {fmt % args}") if __name__ == "__main__": srv = HTTPServer(("0.0.0.0", PORT), Handler) log.info(f"Beszel Webhook Receiver a escutar na porta {PORT}") srv.serve_forever()