Files
help_desk/scripts/sigma_robot_worker.js

252 lines
10 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* scripts/sigma_robot_worker.js
* Worker de comunicación e integración entre Help Desk y sigma-robot.
*
* Funciones:
* 1. Lee mensajes pendientes del chat de sigma-robot para Agente Help Desk (ID 90).
* 2. Procesa solicitudes de creación de tickets y las registra en Help Desk.
* 3. Responde por el chat de sigma-robot confirmando la creación con link directo.
* 4. Si la tarea requiere intervención humana o está en proceso, avisa en el chat.
*/
require('dotenv').config();
const Ticket = require('../models/Ticket');
const Company = require('../models/Company');
const Department = require('../models/Department');
const User = require('../models/User');
const SIGMA_BOT_URL = process.env.SIGMA_BOT_URL || 'https://sigma-bot.telcotronics.com';
const TOKEN = process.env.SIGMA_AGENTE_TOKEN;
const AGENTE_ID = 90; // Agente Help Desk
if (!TOKEN) {
console.error('❌ Error: SIGMA_AGENTE_TOKEN no configurado en .env');
process.exit(1);
}
/**
* Consulta mensajes pendientes en sigma-robot
*/
async function leerMensajesPendientes() {
const res = await fetch(`${SIGMA_BOT_URL}/ecosistema/v1/mensajes/pendientes`, {
headers: { 'Authorization': `Bearer ${TOKEN}` }
});
if (!res.ok) {
throw new Error(`Error ${res.status} leyendo pendientes: ${await res.text()}`);
}
const data = await res.json();
return data.mensajes || [];
}
/**
* Envía un mensaje a sigma-robot
*/
async function enviarMensaje({ para, agente_id, mensaje }) {
const body = { para, mensaje };
if (para === 'agente' && agente_id) {
body.agente_id = parseInt(agente_id);
}
const res = await fetch(`${SIGMA_BOT_URL}/ecosistema/v1/mensajes`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!res.ok) {
throw new Error(`Error enviando mensaje: ${await res.text()}`);
}
return await res.json();
}
/**
* Parsea el texto del mensaje para extraer parámetros de ticket si existen
*/
function parsearSolicitudTicket(texto) {
const raw = String(texto || '').trim();
const lower = raw.toLowerCase();
const esTicket = lower.includes('ticket') || lower.includes('incidencia') || lower.includes('soporte');
const esCrear = lower.includes('crear ticket') || lower.includes('abrir ticket') || lower.includes('nuevo ticket') || lower.startsWith('ticket:');
// Evitar falsos positivos con propuestas arquitectónicas, código o mensajes de coordinación
if (lower.includes('propongo') || lower.includes('contrato') || lower.includes('revisé tu código') ||
lower.includes('preguntas para cerrar') || lower.includes('fase 2') || lower.includes('herramienta') ||
lower.includes('simulado') || lower.includes('recibido y aplicado') || lower.includes('prueba real')) {
return null;
}
if (!esTicket || !esCrear) {
return null;
}
// Detectar Empresa
let companyId = 1; // Default: Telcotronics
if (lower.includes('xsystem') || lower.includes('empresa 2') || lower.includes('empresa=2')) {
companyId = 2;
} else if (lower.includes('domus') || lower.includes('empresa 3') || lower.includes('empresa=3')) {
companyId = 3;
} else if (lower.includes('telcotronics') || lower.includes('empresa 1') || lower.includes('empresa=1')) {
companyId = 1;
}
// Detectar Prioridad
let prioridad = 'media';
if (lower.includes('prioridad=alta') || lower.includes('prioridad: alta') || lower.includes('alta')) prioridad = 'alta';
if (lower.includes('prioridad=critica') || lower.includes('prioridad: critica') || lower.includes('critica') || lower.includes('urgente')) prioridad = 'critica';
if (lower.includes('prioridad=baja') || lower.includes('prioridad: baja') || lower.includes('baja')) prioridad = 'baja';
// Extraer Asunto y Mensaje
let asunto = 'Solicitud recibida desde sigma-robot';
let mensaje = raw;
// Patrón "asunto: ... | mensaje: ..." o "crear ticket: [asunto] - [mensaje]"
const matchAsunto = raw.match(/asunto\s*[:=]\s*([^|\n]+)/i);
const matchMensaje = raw.match(/(?:mensaje|descripcion|detalle)\s*[:=]\s*([^|\n]+)/i);
if (matchAsunto) {
asunto = matchAsunto[1].trim();
}
if (matchMensaje) {
mensaje = matchMensaje[1].trim();
} else if (!matchAsunto) {
// Limpiar prefijo tipo "crear ticket:" y usar las primeras palabras
const cleaned = raw.replace(/^(?:@?\w+\s+)?(?:por\s+favor\s+)?(?:crear|abrir|generar)?\s*(?:un\s+)?ticket(?:\s*[:\-])?\s*/i, '').trim();
if (cleaned.length > 0) {
const lineas = cleaned.split('\n').filter(Boolean);
asunto = lineas[0].substring(0, 80);
mensaje = cleaned;
}
}
return {
companyId,
asunto: asunto.substring(0, 150),
mensaje,
prioridad
};
}
/**
* Ejecuta un ciclo de revisión y procesamiento
*/
async function procesarChat() {
console.log(`[${new Date().toISOString()}] 🔍 Revisando buzón de chat en sigma-robot...`);
const pendientes = await leerMensajesPendientes();
console.log(` 📨 Mensajes nuevos recibidos: ${pendientes.length}`);
if (pendientes.length === 0) {
return { total: 0, creados: 0, respondidos: 0 };
}
let creados = 0;
let respondidos = 0;
for (const msg of pendientes) {
console.log(`\n--- Mensaje #${msg.id} de ${msg.remitente_nombre || msg.remitente_usuario || 'desconocido'} ---`);
console.log(`Texto: "${msg.mensaje}"`);
// Si es un mensaje propio del agente, ignorar
if (msg.remitente_agente_id === AGENTE_ID) {
continue;
}
const solicitud = parsearSolicitudTicket(msg.mensaje);
// Destino para la respuesta
const esDirecto = msg.destino_tipo === 'agente';
const canalRespuesta = esDirecto
? (msg.remitente_tipo === 'pablinux' ? 'pablinux' : 'agente')
: 'general';
const agenteDestinoId = msg.remitente_agente_id;
if (solicitud) {
console.log(` 🎯 Detectada solicitud de ticket:`, solicitud);
try {
const company = await Company.findById(solicitud.companyId) || await Company.findById(1);
// Buscar o aprovisionar usuario solicitante
const remitenteNombre = msg.remitente_nombre || msg.remitente_usuario || 'Agente Externo';
const remitenteEmail = `sigmabot_${(msg.remitente_proyecto || 'robot').toLowerCase()}@sigmac.app`;
let user = await User.findByEmail(remitenteEmail);
if (!user) {
user = await User.upsertExternal(remitenteNombre, remitenteEmail, 'sigma-robot', String(msg.id), null, company.id);
}
// Departamento por defecto
const depts = await Department.findAll(company.id);
const deptId = depts.length > 0 ? depts[0].id : null;
const ticketId = await Ticket.create(
company.id,
user.id,
deptId,
solicitud.asunto,
solicitud.mensaje,
solicitud.prioridad
);
const ticketUrl = `https://soporte.sigmac.app/agent/ticket/${ticketId}`;
console.log(` ✓ Ticket #${ticketId} creado exitosamente en ${company.name}`);
const respuestaTexto = `✅ Ticket #${ticketId} creado en ${company.name}.\n• Asunto: "${solicitud.asunto}"\n• Prioridad: ${solicitud.prioridad}\n• URL: ${ticketUrl}`;
await enviarMensaje({
para: canalRespuesta,
agente_id: agenteDestinoId,
mensaje: respuestaTexto
});
creados++;
respondidos++;
} catch (err) {
console.error(` ❌ Error creando ticket para mensaje #${msg.id}:`, err.message);
await enviarMensaje({
para: canalRespuesta,
agente_id: agenteDestinoId,
mensaje: `⚠️ No se pudo crear el ticket para la solicitud #${msg.id}: ${err.message}`
});
}
} else {
// No es un comando de ticket directo.
// Si es un mensaje de Pablinux o directo solicitando una tarea compleja:
if (msg.remitente_tipo === 'pablinux') {
console.log(' 💬 Mensaje directo de Pablinux recibido.');
// Si requiere confirmación o está en curso:
if (msg.mensaje.toLowerCase().includes('recibido') || msg.mensaje.toLowerCase().includes('continua')) {
console.log(' ✓ Instrucción de continuidad reconocida.');
} else {
const respuestaTexto = `👍 Entendido @pablinux. Mensaje recibido por Agente Help Desk (ID 90). Tarea en curso / esperando tus indicaciones para ejecutar.`;
await enviarMensaje({
para: 'pablinux',
mensaje: respuestaTexto
});
respondidos++;
}
} else if (esDirecto) {
console.log(` ℹ️ Mensaje informativo/coordinación de otro agente (${msg.remitente_nombre || msg.remitente_agente_id}). Sin auto-respuesta para evitar spam de agentes.`);
// No respondemos automáticamente con mensaje genérico para no spamear ni ciclar con otros agentes
}
}
}
console.log(`\n✅ Ciclo finalizado. Total: ${pendientes.length}, Tickets creados: ${creados}, Respuestas enviadas: ${respondidos}`);
return { total: pendientes.length, creados, respondidos };
}
// Ejecución
if (require.main === module) {
procesarChat()
.then(() => process.exit(0))
.catch(err => {
console.error('❌ Error fatal en sigma_robot_worker:', err);
process.exit(1);
});
}
module.exports = { procesarChat, parsearSolicitudTicket };