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:
@@ -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,12 +11,11 @@ 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
|
||||||
PROJECT_ID = 65 # DES Stack Workflow
|
DEPARTMENT_ID = 7 # Tecnologia
|
||||||
MILESTONE_ID = 355 # Sistemas de Apoio (Tecnologia)
|
PROJECT_ID = 65 # DES Stack Workflow
|
||||||
ASSIGNEE_STAFF_ID = 28 # Izito
|
ASSIGNED_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"
|
||||||
os.makedirs(os.path.dirname(LOG), exist_ok=True)
|
os.makedirs(os.path.dirname(LOG), exist_ok=True)
|
||||||
@@ -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}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user