From 60015c097220ab2e3d6c0eaf25e608a62182eeb3 Mon Sep 17 00:00:00 2001 From: Gustavo Date: Mon, 24 Aug 2026 15:18:15 -0300 Subject: [PATCH] feat(v2): add short message and coordination digest builders Co-Authored-By: Claude Sonnet 5 --- logic/message.js | 52 +++++++++++++++++++++++++++++++++++++++++++ logic/message.test.js | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 logic/message.js create mode 100644 logic/message.test.js diff --git a/logic/message.js b/logic/message.js new file mode 100644 index 0000000..25a9c82 --- /dev/null +++ b/logic/message.js @@ -0,0 +1,52 @@ +const ACTION_LABELS = { + UNASSIGNED_ACTIONABLE: 'Atribuir técnico', + ACTIONABLE_NOW: 'Validar e executar', + WAITING_APPROVAL: 'Cobrar aprovação', + INSUFFICIENT_CONTEXT: 'Completar informação', +}; + +function shortActionLabel(aiClassification) { + if (aiClassification.is_fallback) return 'Revisar chamado'; + const fromMap = ACTION_LABELS[aiClassification.classification]; + if (fromMap) return fromMap; + const raw = String(aiClassification.next_action || 'Revisar chamado').trim(); + return raw.length > 40 ? `${raw.slice(0, 37)}...` : raw; +} + +function tituloCurto(texto) { + const limpo = String(texto || 'Sem titulo').replace(/\s+/g, ' ').trim(); + return limpo.length > 60 ? `${limpo.slice(0, 57)}...` : limpo; +} + +function signalLine(ticket) { + const parts = []; + parts.push(ticket.sem_tecnico ? 'SEM TÉCNICO' : ticket.tecnico_atribuido); + if (ticket.sla_vencido) parts.push('SLA vencido'); + parts.push(`parado ${ticket.dias_sem_interacao}d`); + return parts.join(' • '); +} + +function buildTopMessage(selected, dateStr, config) { + if (!selected.length) { + return `@time Coordenador GLPI - ${dateStr}\n\nNenhum chamado acionável encontrado hoje.`; + } + const header = `@time ${selected.length} chamados para atenção hoje — ${dateStr}`; + const blocks = selected.map((item, index) => { + const t = item.ticket; + return [ + `${index + 1}. #${t.id} • ${tituloCurto(t.titulo)}`, + signalLine(t), + `→ ${shortActionLabel(item.aiClassification)}`, + t.link_glpi, + ].join('\n'); + }); + return [header, '', ...blocks].join('\n\n'); +} + +function buildCoordinationDigest(changedBlocked, dateStr) { + if (!changedBlocked.length) return null; + const lines = changedBlocked.map((item) => `#${item.ticket.id} • ${(item.aiClassification && item.aiClassification.reason) || item.exclusionReason}`); + return [`Coordenação — bloqueios novos/alterados — ${dateStr}`, '', ...lines].join('\n'); +} + +module.exports = { shortActionLabel, tituloCurto, signalLine, buildTopMessage, buildCoordinationDigest }; diff --git a/logic/message.test.js b/logic/message.test.js new file mode 100644 index 0000000..a31d288 --- /dev/null +++ b/logic/message.test.js @@ -0,0 +1,46 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { buildTopMessage, buildCoordinationDigest, shortActionLabel, tituloCurto } = require('./message'); + +function selectedItem(overrides) { + return Object.assign({ + ticket: { + id: 38611, titulo: 'Colocar etiqueta sala 03', tecnico_atribuido: 'Sem tecnico atribuido', + sem_tecnico: true, sla_vencido: false, dias_sem_interacao: 2, link_glpi: 'https://glpi/x?id=38611', + }, + aiClassification: { classification: 'UNASSIGNED_ACTIONABLE', next_action: 'Atribuir e executar', is_fallback: false }, + }, overrides); +} + +test('mensagem final contem cabecalho, id, link e acao curta', () => { + const msg = buildTopMessage([selectedItem({})], '24/08', {}); + assert.ok(msg.includes('#38611')); + assert.ok(msg.includes('https://glpi/x?id=38611')); + assert.ok(msg.includes('→')); + assert.ok(msg.includes('24/08')); +}); + +test('mensagem NAO contem score, confianca ou justificativa longa', () => { + const msg = buildTopMessage([selectedItem({})], '24/08', {}); + assert.ok(!/confian(c|ç)a/i.test(msg)); + assert.ok(!/score/i.test(msg)); +}); + +test('linha de acao e curta (limite de tamanho)', () => { + const label = shortActionLabel({ classification: 'ACTIONABLE_NOW', next_action: 'x'.repeat(100), is_fallback: false }); + assert.ok(label.length <= 40); +}); + +test('tituloCurto limita a 60 caracteres', () => { + const curto = tituloCurto('a'.repeat(100)); + assert.ok(curto.length <= 60); +}); + +test('digest de coordenacao vazio retorna null (nao manda lista igual todo dia)', () => { + assert.equal(buildCoordinationDigest([], '24/08'), null); +}); + +test('digest de coordenacao lista apenas os itens passados (novos/alterados)', () => { + const digest = buildCoordinationDigest([{ ticket: { id: 37691 }, aiClassification: { reason: 'aguardando definicao' }, exclusionReason: 'needs_coordination' }], '24/08'); + assert.ok(digest.includes('#37691')); +});