TopSapp: Suporte: Função de fechar suporte

Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
rodrigo committed 2026-04-01 16:58:23 -03:00
1 parent 2a939def1d
commit 6dc11e1ffe
4 files changed
+400 -1

No files matched your search

+351 -1
View File
@@ -265,6 +265,11 @@ class BotHandler {
console.log(`🎫 Iniciando processo de abertura de suporte`);
await this.iniciarProcessoSuporte(chatId, user.id, target);
break;
case 'fechar_suporte':
console.log(`🔒 Iniciando processo de fechamento de suporte`);
await this.iniciarProcessoFecharSuporte(chatId, user.id, target);
break;
case 'conectar':
await this.handleConnect(chatId, target);
@@ -531,7 +536,12 @@ Digite /help para ver todos os comandos disponíveis!
• "novo chamado"
• "suporte cliente 123.456.789-10"
**🔄 PROCESSO INTERATIVO:**
**� FECHAR SUPORTE:**
• "fechar suporte"
• "finalizar chamado"
• "encerrar suporte João Silva"
**�🔄 PROCESSO INTERATIVO:**
1️⃣ Busca do cliente (nome ou CPF)
2️⃣ Seleção do serviço
3️⃣ Escolha da categoria do problema
@@ -2037,6 +2047,12 @@ Digite /help para ver todos os comandos disponíveis!
} else if (data.startsWith('suporte_servico_')) {
console.log(`🔍 Condição suporte_servico_ atendida`);
await this.handleSuporteServicoCallback(callbackQuery, data);
} else if (data.startsWith('fechar_suporte_cli_')) {
await this.bot.answerCallbackQuery(callbackQuery.id);
await this.handleFecharSuporteClienteCallback(callbackQuery, data);
} else if (data.startsWith('fechar_suporte_id_')) {
await this.bot.answerCallbackQuery(callbackQuery.id);
await this.handleFecharSuporteIdCallback(callbackQuery, data);
} else if (data.startsWith('linha_')) {
console.log(`🔍 Condição linha_ atendida`);
await this.handleLinhaDigitavelCallback(callbackQuery, data);
@@ -5862,6 +5878,23 @@ Digite /help para ver todos os comandos disponíveis!
case 'confirmar':
await this.finalizarCriacaoSuporte(sessaoAtiva.id, text);
break;
// Etapas para fechar suporte
case 'fechar_buscar_cliente':
await this.processarEtapaFecharBuscarCliente(sessaoAtiva.id, text);
break;
case 'fechar_selecionar_cliente':
await this.processarEtapaFecharSelecionarCliente(sessaoAtiva.id, text);
break;
case 'fechar_selecionar_suporte':
await this.processarEtapaFecharSelecionarSuporte(sessaoAtiva.id, text);
break;
case 'fechar_aguardando_descricao':
await this.processarEtapaFecharDescricao(sessaoAtiva.id, text);
break;
default:
await this.bot.sendMessage(chatId, '❌ Etapa desconhecida. Reiniciando processo...');
@@ -6500,6 +6533,251 @@ Digite /help para ver todos os comandos disponíveis!
this.suporteSessions.delete(sessionId);
}
// ─────────────────────────────────────────────────────────────────────────
// Fechar Suporte
// ─────────────────────────────────────────────────────────────────────────
/**
* Iniciar processo de fechamento de suporte
*/
async iniciarProcessoFecharSuporte(chatId, userId, target = '') {
if (!this.topsapp.isReady()) {
await this.bot.sendMessage(chatId,
`❌ **TopSapp não configurado**\n\n` +
`⚠️ O sistema TopSapp não está configurado. Entre em contato com o administrador.`
);
return;
}
const sessionId = `${userId}_${chatId}_${Date.now()}`;
const suporteSession = {
id: sessionId,
chatId,
userId,
etapa: 'fechar_buscar_cliente',
dados: {},
iniciado: new Date()
};
this.suporteSessions.set(sessionId, suporteSession);
await this.bot.sendMessage(chatId,
`🔒 **Fechamento de Suporte**\n\n` +
`🔍 Digite o **nome** ou **CPF** do cliente cujo suporte deseja fechar:`,
{ parse_mode: 'Markdown' }
);
if (target && target.trim()) {
await this.processarEtapaFecharBuscarCliente(sessionId, target.trim());
}
}
/**
* Etapa 1 (fechar): Buscar cliente
*/
async processarEtapaFecharBuscarCliente(sessionId, query) {
const session = this.suporteSessions.get(sessionId);
if (!session) return;
await this.bot.sendMessage(session.chatId, `🔍 Buscando cliente: "${query}"...`);
try {
const resultado = await this.topsapp.buscarCliente(query);
if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) {
await this.bot.sendMessage(session.chatId,
`❌ **Nenhum cliente encontrado**\n\n` +
`🔍 Não foi encontrado nenhum cliente com: "${query}"\n\n` +
`Por favor, tente novamente com outro nome ou CPF:`
);
return;
}
session.dados.clientesEncontrados = resultado.clientes;
if (resultado.clientes.length === 1) {
session.dados.clienteSelecionado = resultado.clientes[0];
session.dados.location = resultado.instancia || '';
await this.processarEtapaFecharBuscarSuportes(sessionId);
} else {
session.etapa = 'fechar_selecionar_cliente';
let message = `👥 **Clientes encontrados:**\n\n`;
const buttons = [];
resultado.clientes.forEach((cliente, index) => {
message += `**${index + 1}.** ${cliente.nome} — CPF: ${cliente.cpf || 'N/A'}\n`;
buttons.push([{ text: `${index + 1}. ${cliente.nome}`, callback_data: `fechar_suporte_cli_${sessionId}_${index}` }]);
});
message += `\nDigite o **número** do cliente:`;
await this.bot.sendMessage(session.chatId, message, {
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: buttons }
});
}
} catch (error) {
await this.bot.sendMessage(session.chatId, `❌ **Erro na busca**\n\n⚠️ ${error.message}\n\nTente novamente:`);
}
}
/**
* Etapa 2 (fechar): Selecionar cliente quando há múltiplos
*/
async processarEtapaFecharSelecionarCliente(sessionId, input) {
const session = this.suporteSessions.get(sessionId);
if (!session) return;
const numero = parseInt(input.trim());
if (isNaN(numero) || numero < 1 || numero > session.dados.clientesEncontrados.length) {
await this.bot.sendMessage(session.chatId,
`❌ Número inválido. Digite entre 1 e ${session.dados.clientesEncontrados.length}:`
);
return;
}
session.dados.clienteSelecionado = session.dados.clientesEncontrados[numero - 1];
session.dados.location = '';
await this.processarEtapaFecharBuscarSuportes(sessionId);
}
/**
* Buscar suportes abertos do cliente selecionado
*/
async processarEtapaFecharBuscarSuportes(sessionId) {
const session = this.suporteSessions.get(sessionId);
if (!session) return;
const cliente = session.dados.clienteSelecionado;
await this.bot.sendMessage(session.chatId, `🔍 Buscando suportes abertos de **${cliente.nome}**...`, { parse_mode: 'Markdown' });
try {
const resultado = await this.topsapp.obterSuportesAbertos(cliente.id, session.dados.location || '');
const suportes = resultado.success ? (resultado.data?.dados || []) : [];
if (suportes.length === 0) {
await this.bot.sendMessage(session.chatId,
`ℹ️ **Nenhum suporte aberto**\n\n` +
`O cliente **${cliente.nome}** não possui suportes abertos no momento.`,
{ parse_mode: 'Markdown' }
);
this.suporteSessions.delete(sessionId);
return;
}
session.dados.suportesAbertos = suportes;
if (suportes.length === 1) {
session.dados.suporteSelecionado = suportes[0];
session.etapa = 'fechar_aguardando_descricao';
const s = suportes[0];
const enderecoCompleto = [s.endereco, s.numero, s.bairro, s.cidade].filter(Boolean).join(', ');
await this.bot.sendMessage(session.chatId,
`🎫 **Suporte encontrado:**\n\n` +
`🆔 ID: ${s.id}\n` +
`📝 Problema: ${s.defeito || 'N/A'}\n` +
`📍 Endereço: ${enderecoCompleto || 'N/A'}\n` +
`📅 Data: ${s.data || 'N/A'}\n\n` +
`✏️ Digite a **descrição do fechamento** do suporte:`,
{ parse_mode: 'Markdown' }
);
} else {
session.etapa = 'fechar_selecionar_suporte';
let message = `🎫 **Suportes abertos de ${cliente.nome}:**\n\n`;
const buttons = [];
suportes.forEach((s, index) => {
const enderecoCompleto = [s.endereco, s.numero, s.bairro].filter(Boolean).join(', ');
message += `**${index + 1}.** ID #${s.id}\n`;
message += ` 📝 ${s.defeito || 'Sem descrição'}\n`;
if (enderecoCompleto) message += ` 📍 ${enderecoCompleto}\n`;
message += ` 📅 ${s.data || ''}\n\n`;
buttons.push([{ text: `${index + 1}. #${s.id} — ${(s.defeito || 'Sem descrição').substring(0, 40)}`, callback_data: `fechar_suporte_id_${sessionId}_${index}` }]);
});
message += `Selecione qual suporte deseja fechar:`;
await this.bot.sendMessage(session.chatId, message, {
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: buttons }
});
}
} catch (error) {
await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar suportes**\n\n⚠️ ${error.message}`);
this.suporteSessions.delete(sessionId);
}
}
/**
* Etapa 3 (fechar): Selecionar suporte quando há múltiplos
*/
async processarEtapaFecharSelecionarSuporte(sessionId, input) {
const session = this.suporteSessions.get(sessionId);
if (!session) return;
const numero = parseInt(input.trim());
if (isNaN(numero) || numero < 1 || numero > session.dados.suportesAbertos.length) {
await this.bot.sendMessage(session.chatId,
`❌ Número inválido. Digite entre 1 e ${session.dados.suportesAbertos.length}:`
);
return;
}
session.dados.suporteSelecionado = session.dados.suportesAbertos[numero - 1];
session.etapa = 'fechar_aguardando_descricao';
const s = session.dados.suporteSelecionado;
const enderecoCompleto = [s.endereco, s.numero, s.bairro, s.cidade].filter(Boolean).join(', ');
await this.bot.sendMessage(session.chatId,
`🎫 **Suporte selecionado:**\n\n` +
`🆔 ID: ${s.id}\n` +
`📝 Problema: ${s.defeito || 'N/A'}\n` +
`📍 Endereço: ${enderecoCompleto || 'N/A'}\n\n` +
`✏️ Digite a **descrição do fechamento** do suporte:`,
{ parse_mode: 'Markdown' }
);
}
/**
* Etapa 4 (fechar): Receber descrição e executar fechamento
*/
async processarEtapaFecharDescricao(sessionId, descricao) {
const session = this.suporteSessions.get(sessionId);
if (!session) return;
if (!descricao || descricao.trim().length < 3) {
await this.bot.sendMessage(session.chatId,
`❌ A descrição é obrigatória e deve ter ao menos 3 caracteres.\n\nDigite a descrição do fechamento:`
);
return;
}
const s = session.dados.suporteSelecionado;
const cliente = session.dados.clienteSelecionado;
await this.bot.sendMessage(session.chatId, `⏳ Finalizando suporte #${s.id}...`);
try {
const resultado = await this.topsapp.finalizarSuporte(s.id, descricao.trim(), session.dados.location || '');
if (resultado.success) {
await this.bot.sendMessage(session.chatId,
`✅ **Suporte finalizado com sucesso!**\n\n` +
`🎫 ID do Suporte: #${s.id}\n` +
`👤 Cliente: ${cliente.nome}\n` +
`📝 Problema: ${s.defeito || 'N/A'}\n` +
`✏️ Fechamento: ${descricao.trim()}`,
{ parse_mode: 'Markdown' }
);
logger.info('Suporte finalizado via bot', { idSuporte: s.id, cliente: cliente.nome, usuario: session.userId });
} else {
await this.bot.sendMessage(session.chatId,
`❌ **Erro ao finalizar suporte**\n\n⚠️ ${resultado.error}\n\nTente novamente mais tarde.`
);
}
} catch (error) {
logger.error('Erro ao finalizar suporte:', error);
await this.bot.sendMessage(session.chatId, `❌ **Erro ao finalizar suporte**\n\n⚠️ ${error.message}`);
}
this.suporteSessions.delete(sessionId);
}
// ─────────────────────────────────────────────────────────────────────────
// TopSapp — Vinculação de ONU após adição
// ─────────────────────────────────────────────────────────────────────────
@@ -6957,6 +7235,78 @@ Digite /help para ver todos os comandos disponíveis!
await this.processarEtapaSelecionarCategoria(sessaoAtiva.id);
}
/**
* Callback: seleção de cliente no fluxo de fechar suporte
* formato: fechar_suporte_cli_<sessionId>_<index>
*/
async handleFecharSuporteClienteCallback(callbackQuery, data) {
const chatId = callbackQuery.message.chat.id;
const userId = callbackQuery.from.id;
// fechar_suporte_cli_<sessionId>_<index>
const withoutPrefix = data.replace('fechar_suporte_cli_', '');
const lastUnder = withoutPrefix.lastIndexOf('_');
const sessionId = withoutPrefix.substring(0, lastUnder);
const index = parseInt(withoutPrefix.substring(lastUnder + 1));
const session = this.suporteSessions.get(sessionId);
if (!session || session.chatId !== chatId || session.userId !== userId) {
await this.bot.sendMessage(chatId, `❌ Sessão expirada. Use "fechar suporte" para iniciar novamente.`);
return;
}
const clientes = session.dados.clientesEncontrados;
if (!clientes || index < 0 || index >= clientes.length) {
await this.bot.sendMessage(chatId, `❌ Seleção inválida. Tente novamente.`);
return;
}
session.dados.clienteSelecionado = clientes[index];
session.dados.location = '';
await this.processarEtapaFecharBuscarSuportes(sessionId);
}
/**
* Callback: seleção de suporte no fluxo de fechar suporte
* formato: fechar_suporte_id_<sessionId>_<index>
*/
async handleFecharSuporteIdCallback(callbackQuery, data) {
const chatId = callbackQuery.message.chat.id;
const userId = callbackQuery.from.id;
// fechar_suporte_id_<sessionId>_<index>
const withoutPrefix = data.replace('fechar_suporte_id_', '');
const lastUnder = withoutPrefix.lastIndexOf('_');
const sessionId = withoutPrefix.substring(0, lastUnder);
const index = parseInt(withoutPrefix.substring(lastUnder + 1));
const session = this.suporteSessions.get(sessionId);
if (!session || session.chatId !== chatId || session.userId !== userId) {
await this.bot.sendMessage(chatId, `❌ Sessão expirada. Use "fechar suporte" para iniciar novamente.`);
return;
}
const suportes = session.dados.suportesAbertos;
if (!suportes || index < 0 || index >= suportes.length) {
await this.bot.sendMessage(chatId, `❌ Seleção inválida. Tente novamente.`);
return;
}
session.dados.suporteSelecionado = suportes[index];
session.etapa = 'fechar_aguardando_descricao';
const s = session.dados.suporteSelecionado;
const enderecoCompleto = [s.endereco, s.numero, s.bairro, s.cidade].filter(Boolean).join(', ');
await this.bot.sendMessage(session.chatId,
`🎫 **Suporte selecionado:**\n\n` +
`🆔 ID: ${s.id}\n` +
`📝 Problema: ${s.defeito || 'N/A'}\n` +
`📍 Endereço: ${enderecoCompleto || 'N/A'}\n\n` +
`✏️ Digite a **descrição do fechamento** do suporte:`,
{ parse_mode: 'Markdown' }
);
}
/**
* Exibe informações do roteador/CPE via GenieACS.
* Uso: "ver roteador <serial>" ou "ver roteador <tag/login-topsapp>"