fix(beszel): webhook cria tickets em tbltickets, dept 7 Tecnologia

- Tabela correcta: tbltickets (não tbltasks)
- department: 7 (Tecnologia)
- project_id: 65 (DES Stack Workflow)
- assigned: Izito (staff 28)
- userid: 0 (interno)
- Auto-fecho via tblticket_replies
This commit is contained in:
2026-06-24 06:03:49 +01:00
parent ab3384c961
commit e11b237a1e
+36 -61
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Beszel Webhook Receiver — Cria tarefas Desk CRM a partir de alertas Beszel.""" """Beszel Webhook Receiver — Cria tickets Desk CRM a partir de alertas Beszel."""
import json, os, sys, logging import json, os, sys, logging
import string, random
from http.server import HTTPServer, BaseHTTPRequestHandler from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime from datetime import datetime
import pymysql import pymysql
@@ -10,11 +11,10 @@ PORT = int(sys.argv[sys.argv.index("--port") + 1]) if "--port" in sys.argv else
DB_CONFIG = {"host": "server.descomplicar.pt", "port": 3306, "user": "ealmeida_desk24", DB_CONFIG = {"host": "server.descomplicar.pt", "port": 3306, "user": "ealmeida_desk24",
"password": "9qPRdCGGqM4o", "database": "ealmeida_desk24", "charset": "utf8mb4"} "password": "9qPRdCGGqM4o", "database": "ealmeida_desk24", "charset": "utf8mb4"}
# Configuração Desk CRM # Configuração Desk CRM — Tickets
DEPARTMENT_ID = 7 # Tecnologia
PROJECT_ID = 65 # DES Stack Workflow PROJECT_ID = 65 # DES Stack Workflow
MILESTONE_ID = 355 # Sistemas de Apoio (Tecnologia) ASSIGNED_STAFF_ID = 28 # Izito
ASSIGNEE_STAFF_ID = 28 # Izito
CREATOR_STAFF_ID = 25 # Claude/AIkTop
PRIORITY = 3 # Alta PRIORITY = 3 # Alta
LOG = "/root/logs/beszel-webhook.log" LOG = "/root/logs/beszel-webhook.log"
@@ -26,95 +26,70 @@ log = logging.getLogger("beszel-webhook")
def get_db(): def get_db():
return pymysql.connect(**DB_CONFIG, cursorclass=pymysql.cursors.DictCursor) return pymysql.connect(**DB_CONFIG, cursorclass=pymysql.cursors.DictCursor)
def tarefa_existe(cur, padrao): def ticket_key():
"""Verifica se já existe tarefa aberta com o padrão indicado.""" return ''.join(random.choices(string.ascii_lowercase + string.digits, k=32))
def ticket_existe(cur, padrao):
cur.execute( cur.execute(
"SELECT id FROM tbltasks WHERE name LIKE %s AND status NOT IN (5,6) AND rel_type='project' AND rel_id=%s LIMIT 1", "SELECT ticketid FROM tbltickets WHERE subject LIKE %s AND status NOT IN (2,5) AND department=%s LIMIT 1",
(padrao, PROJECT_ID) (padrao, DEPARTMENT_ID)
) )
return cur.fetchone() return cur.fetchone()
def criar_tarefa(cur, nome, descricao): def criar_ticket(cur, assunto, mensagem):
"""Cria tarefa Desk CRM no projecto 65, milestone 355, atribuída a Izito."""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
key = ticket_key()
cur.execute( cur.execute(
"""INSERT INTO tbltasks "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)",
(name, description, priority, status, rel_type, rel_id, milestone, (assunto, mensagem, DEPARTMENT_ID, PRIORITY, now, PROJECT_ID, ASSIGNED_STAFF_ID, key)
addedfrom, startdate, dateadded)
VALUES (%s, %s, %s, 1, %s, %s, %s, %s, %s, %s)""",
(nome, descricao, PRIORITY, "project", PROJECT_ID, MILESTONE_ID,
CREATOR_STAFF_ID, now[:10], now)
) )
tid = cur.lastrowid tid = cur.lastrowid
cur.execute( log.info(f"Ticket #{tid} criado — {assunto}")
"INSERT INTO tbltask_assigned (taskid, staffid, assigned_from) VALUES (%s, %s, 0)",
(tid, ASSIGNEE_STAFF_ID)
)
log.info(f"Tarefa #{tid} criada — {nome}")
return tid return tid
def fechar_tarefa(cur, tid, nota): def fechar_ticket(cur, tid, nota):
"""Fecha tarefa (status 5 = Terminado).""" cur.execute("UPDATE tbltickets SET status=2, lastreply=NOW() WHERE ticketid=%s", (tid,))
cur.execute("UPDATE tbltasks SET status=5, datefinished=NOW() WHERE id=%s", (tid,)) now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log.info(f"Tarefa #{tid} fechada: {nota}") cur.execute(
"INSERT INTO tblticket_replies (ticketid, message, date, staffid, admin) VALUES (%s, %s, %s, %s, 1)",
(tid, f"<p>Auto-fecho (Beszel): {nota}</p>", now, ASSIGNED_STAFF_ID)
)
log.info(f"Ticket #{tid} fechado: {nota}")
class Handler(BaseHTTPRequestHandler): class Handler(BaseHTTPRequestHandler):
def do_POST(self): def do_POST(self):
if self.path != "/beszel-alert": if self.path != "/beszel-alert":
self.send_response(404) self.send_response(404); self.end_headers(); return
self.end_headers() body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
return
body = self.rfile.read(int(self.headers.get("Content-Type-Length", 0)))
try: try:
data = json.loads(body) data = json.loads(body)
except Exception: except Exception:
self.send_response(400) self.send_response(400); self.end_headers(); return
self.end_headers()
return
titulo = data.get("title", "") titulo = data.get("title", "")
mensagem = data.get("message", "") mensagem = data.get("message", "")
log.info(f"Webhook: {titulo}{mensagem}") log.info(f"Webhook: {titulo}{mensagem}")
is_down = "down" in (mensagem + titulo).lower() is_down = "down" in (mensagem + titulo).lower()
is_up = "up" in mensagem.lower() or "recovered" in mensagem.lower() is_up = "up" in mensagem.lower() or "recovered" in mensagem.lower()
# Extrair nome do sistema
sname = data.get("system", "") or data.get("name", "") or data.get("host", "") sname = data.get("system", "") or data.get("name", "") or data.get("host", "")
if not sname: if not sname:
parts = mensagem.split(":") parts = mensagem.split(":")
if len(parts) > 1: if len(parts) > 1:
sname = parts[1].strip().split("(")[0].strip() sname = parts[1].strip().split("(")[0].strip()
try: try:
db = get_db() db = get_db(); cur = db.cursor()
cur = db.cursor()
if is_down and sname: if is_down and sname:
if not tarefa_existe(cur, f"[MONIT] {sname}%"): if not ticket_existe(cur, f"[MONIT] {sname}%"):
criar_tarefa( criar_ticket(cur, f"[MONIT] {sname} — sistema DOWN",
cur, f"<p><strong>Beszel — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</strong></p><p>{mensagem}</p>")
f"[MONIT] {sname} — sistema DOWN",
f"<p><strong>Beszel — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</strong></p>"
f"<p>{mensagem}</p>"
)
elif is_up and sname: elif is_up and sname:
existente = tarefa_existe(cur, f"[MONIT] {sname}%") ex = ticket_existe(cur, f"[MONIT] {sname}%")
if existente: if ex:
fechar_tarefa(cur, existente["id"], f"{sname} voltou ao normal") fechar_ticket(cur, ex["ticketid"], f"{sname} voltou ao normal")
db.commit(); cur.close(); db.close()
db.commit()
cur.close()
db.close()
except Exception as e: except Exception as e:
log.error(f"Erro Desk CRM: {e}") log.error(f"Erro Desk CRM: {e}")
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(b'{"status":"ok"}') self.wfile.write(b'{"status":"ok"}')
def log_message(self, fmt, *args): def log_message(self, fmt, *args):
log.info(f"{self.client_address[0]} - {fmt % args}") log.info(f"{self.client_address[0]} - {fmt % args}")