const NLPProcessor = require('../nlp/processor'); const SSHManager = require('../ssh/manager'); const { TopSappManager } = require('../topsapp'); const FiberhomeManager = require('../fiberhome/manager'); const FiberhomeTL1Manager = require('../fiberhome/manager_tl1'); const config = require('../config/config'); const logger = require('../utils/logger'); const DateProcessor = require('../utils/dateProcessor'); class BotHandler { constructor(bot) { this.bot = bot; this.nlp = new NLPProcessor(); this.ssh = new SSHManager(); this.topsapp = new TopSappManager(); this.fiberhome = new FiberhomeManager(); this.fiberhomeTL1 = new FiberhomeTL1Manager(); this.dateProcessor = new DateProcessor(); this.userSessions = new Map(); // Armazena sessões de usuário this.suporteSessions = new Map(); // Armazena sessões de suporte em andamento logger.info('BotHandler inicializado'); console.log('🎯 BotHandler inicializado'); } /** * Verifica se o usuário tem permissão */ isAuthorized(userId) { if (config.bot.allowedUsers.length === 0) { return true; // Se não há lista de usuários, permite todos } return config.bot.allowedUsers.includes(userId); } /** * Processa mensagens recebidas */ async handleMessage(msg) { const chatId = msg.chat.id; const userId = msg.from.id; const text = msg.text; // Log da mensagem if (config.bot.debug) { console.log(`📨 Mensagem de ${msg.from.first_name} (${userId}): ${text}`); } // Verificar autorização if (!this.isAuthorized(userId)) { await this.bot.sendMessage(chatId, `🚫 **Acesso Negado**\n\n` + `❌ Você não tem permissão para usar este bot.\n` + `🆔 Seu ID: \`${userId}\`\n\n` + `📞 Entre em contato com o administrador para solicitar acesso.`, { parse_mode: 'Markdown' } ); console.log(`🚫 Acesso negado para usuário ${msg.from.first_name} (${userId})`); return; } try { // Verificar se há uma sessão de suporte ativa const processouSuporte = await this.processarMensagemSuporte(msg); if (processouSuporte) { return; // Mensagem foi processada pelo fluxo de suporte } // Verificar se o usuário está em uma sessão esperando input const userSession = this.userSessions.get(userId); if (userSession) { await this.handleUserSessionInput(chatId, text, userId, userSession); return; } // Processar a mensagem com NLP const intent = this.nlp.processMessage(text); if (config.bot.debug) { console.log('🧠 Intent detectado:', intent); } // Executar ação baseada na intenção await this.executeAction(chatId, intent, msg.from); } catch (error) { console.error('❌ Erro ao processar mensagem:', error); await this.bot.sendMessage(chatId, '❌ Ocorreu um erro ao processar sua mensagem. Tente novamente.'); } } /** * Executa ação baseada na intenção detectada */ async executeAction(chatId, intent, user) { const { action, target, confidence, type } = intent; const userId = user.id; // Extrair userId do objeto user // Se a confiança é muito baixa, pedir esclarecimento if (confidence < 0.3 && action) { const suggestions = this.nlp.suggestCorrection(action); let message = '🤔 Não entendi muito bem. '; if (suggestions.length > 0) { message += `Você quis dizer: ${suggestions.join(', ')}?`; } else { message += 'Digite /help para ver os comandos disponíveis.'; } await this.bot.sendMessage(chatId, message); return; } // Executar ação específica try { switch (action) { case 'start': await this.handleStart(chatId, user); break; case 'help': await this.handleHelp(chatId); break; case 'ssh': await this.handleSSH(chatId, target); break; case 'status': await this.handleStatus(chatId, target); break; case 'lista': case 'equipamentos': await this.handleListEquipments(chatId); break; case 'listar_usuarios': await this.handleListUsers(chatId, target); break; case 'admin': case 'administracao': await this.handleAdmin(chatId); break; case 'logs': await this.handleViewLogs(chatId); break; case 'limpar_logs': await this.handleClearLogs(chatId); break; case 'usuarios_autorizados': await this.handleListAuthorizedUsers(chatId); break; case 'meu_id': case 'meu_user_id': await this.handleMyId(chatId, userId); break; case 'ver_sinal': console.log(`🔍 Executando handleVerSinal com target: ${target}`); await this.handleVerSinal(chatId, target); break; case 'remover_onu': console.log(`🗑️ Executando handleRemoverOnu com target: ${target}`); await this.handleRemoverOnu(chatId, target); break; case 'adicionar_onu': console.log(`➕ Executando handleAdicionarOnu com target: ${target}`); await this.handleAdicionarOnu(chatId, target); break; case 'adicionar_onu_vlan': console.log(`➕ Executando handleAdicionarOnuVlan com target: ${target}, vlanId: ${intent.vlanId}`); await this.handleAdicionarOnuVlan(chatId, target, intent.vlanId); break; case 'adicionar_onu_fiberhome': console.log(`➕ Executando handleAdicionarOnuFiberhome com target: ${target}`); await this.handleAdicionarOnuFiberhome(chatId, target, userId); break; case 'remover_onu_fiberhome': console.log(`🗑️ Executando handleRemoverOnuFiberhome com target: ${target}`); await this.handleRemoverOnuFiberhome(chatId, target, userId); break; case 'buscar_cliente': console.log(`🔍 Executando handleBuscarCliente com target: ${target}`); await this.handleBuscarCliente(chatId, target); break; case 'ver_faturas': console.log(`📄 Executando handleVerFaturas com target: ${target}`); await this.handleVerFaturas(chatId, target); break; case 'gerar_boleto': console.log(`💸 Executando handleGerarBoleto com target: ${target}`); await this.handleGerarBoleto(chatId, target); break; case 'bloquear_cliente': console.log(`🔒 Executando handleBloquearCliente com target: ${target}`); await this.handleBloquearCliente(chatId, target); break; case 'liberar_cliente': console.log(`🔓 Executando handleLiberarCliente com target: ${target}`); await this.handleLiberarCliente(chatId, target); break; case 'listar_servicos': console.log(`📋 Executando handleListarServicos com target: ${target}`); await this.handleListarServicos(chatId, target); break; case 'dados_cliente': case 'dados_cadastrais': console.log(`📋 Executando handleDadosClientePorBusca com target: ${target}`); await this.handleDadosClientePorBusca(chatId, target); break; case 'status_topsapp': console.log(`🏢 Executando handleStatusTopSapp`); await this.handleStatusTopSapp(chatId); break; case 'status_onu': console.log(`📡 Executando handleStatusONU com target: ${target}`); await this.handleStatusONU(chatId, target); break; case 'fiberhome_status': console.log(`🔌 Executando handleFiberhomeStatus`); await this.handleFiberhomeStatus(chatId); break; case 'abrir_suporte': case 'adicionar_suporte': case 'criar_suporte': console.log(`🎫 Iniciando processo de abertura de suporte`); await this.iniciarProcessoSuporte(chatId, user.id, target); break; case 'conectar': await this.handleConnect(chatId, target); break; case 'desconectar': await this.handleDisconnect(chatId, target); break; case 'desconectar_usuario': await this.handleDisconnectUser(chatId, target); break; case 'reiniciar': await this.handleRestart(chatId, target); break; case 'ping': await this.handlePing(chatId, target); break; default: if (!action) { await this.handleUnknown(chatId, intent.originalText || ''); } else { await this.bot.sendMessage(chatId, `❓ Ação '${action}' não implementada ainda.`); } } } catch (actionError) { console.error(`❌ Erro ao executar ação '${action}':`, actionError); await this.bot.sendMessage(chatId, `❌ Erro ao executar ação: ${actionError.message}`); } } /** * Comando /start */ async handleStart(chatId, user) { const welcomeMessage = ` 🤖 *Bem-vindo ao Bot de Ajuda!* Olá ${user.first_name}! Agora você não precisa mandar mensagem pro Rodrigo ou no grupo da ID pra ficar vendo coisas triviais: 🔐 *Conexões SSH* - Acesso remoto a equipamentos 🌐 *Status de rede* - Verificação de conectividade ⚙️ *Comandos remotos* - Execução de tarefas *Exemplos do que você pode dizer:* • "Conectar no router1" • "Verificar status do switch1" • "Reiniciar equipamento" • "Lista de equipamentos" Digite /help para ver todos os comandos disponíveis! `; await this.bot.sendMessage(chatId, welcomeMessage, { parse_mode: 'Markdown' }); } /** * Comando /help */ async handleHelp(chatId) { const helpMessage = `🤖 **BOT DE AJUDA TÉCNICA** 🔧 **COMANDOS PRINCIPAIS:** • /start - Inicia o bot • /help - Esta ajuda • /equipamentos - Lista equipamentos 🏢 **GESTÃO DE CLIENTES:** • "buscar cliente João Silva" • "dados cliente 123.456.789-10" • "ver faturas cliente" • "abrir suporte" 📡 **OLT/ONU:** • "ver sinal DACM91569513" • "adicionar onu SERIAL" • "adicionar onu fiberhome SERIAL" • "remover onu SERIAL" � **JUNIPER:** • "desconectar cliente USER" • "listar usuários conectados" 🔐 **SSH:** • "ssh juniper" • "acessar concentrador" 📋 **Para ver comandos detalhados, use:**`; const keyboard = [ [ { text: '🏢 TopSapp', callback_data: 'help_topsapp' }, { text: '� OLT/ONU', callback_data: 'help_olt' } ], [ { text: '👥 Juniper', callback_data: 'help_juniper' }, { text: '🔐 SSH', callback_data: 'help_ssh' } ], [ { text: '🎫 Suporte', callback_data: 'help_suporte' }, { text: '🛡️ Admin', callback_data: 'help_admin' } ] ]; await this.bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } /** * Handler para callbacks de help detalhado */ async handleHelpDetailCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const helpType = data.replace('help_', ''); await this.bot.answerCallbackQuery(callbackQuery.id); let helpMessage = ''; let backButton = { text: '⬅️ Voltar', callback_data: 'help_main' }; switch (helpType) { case 'main': await this.handleHelp(chatId); return; case 'topsapp': helpMessage = `🏢 **TOPSAPP - GESTÃO DE CLIENTES** **🔍 BUSCAR CLIENTES:** • "buscar cliente João Silva" • "buscar cliente 123.456.789-10" • "buscar cliente Guarai João" **📋 DADOS CADASTRAIS:** • "dados cliente João Silva" • "dados cadastrais 123.456.789-10" **📄 FATURAS:** • "ver faturas 123.456.789-10" • "gerar boleto fatura123" **⚙️ SERVIÇOS:** • "listar serviços 123.456.789-10" **🔒 BLOQUEIO/LIBERAÇÃO:** • "bloquear 123.456.789-10" • "liberar 123.456.789-10" **📊 STATUS:** • "status topsapp"`; break; case 'olt': helpMessage = `📡 **OLT/ONU - COMANDOS** **📶 VERIFICAR SINAL:** • "ver sinal DACM91569513" • "verificar sinal ABCD12345678" • "sinal SERIAL_NUMBER" **➕ ADICIONAR ONU:** • "adicionar onu DACM91569513" • "cadastrar ABCD12345678" • "add onu SERIAL_NUMBER" **➕ ADICIONAR ONU COM VLAN CUSTOMIZADA:** • "adicionar onu vlan 603 DACM91569513" • "add onu vlan 700 ABCD12345678" • "adicionar vlan 603 SERIAL_NUMBER" **➕ ADICIONAR ONU FIBERHOME VIA TL1:** • "adicionar onu fiberhome FHTT12345678" • "fiberhome onu FHTT12345678" • Processo interativo com: - Nome/alias do usuário - Nome da OLT - Porta PON (ex: 7-1) - Tipo da ONU (AN5506-02-B ou AN5506-01-A) - VLAN (padrão: 1002) • Configura automaticamente: - WAN Services - Login Web (admin/clicknet10) - Reinicia a ONU **🗑️ REMOVER ONU:** • "remover onu DACM91569513" • "deletar ABCD12345678" • "remover SERIAL_NUMBER" **🔄 REINICIAR ONU:** • Após verificar sinal, use botão "🔌 Reiniciar ONU" **⬆️ ATUALIZAR FIRMWARE:** • Após verificar sinal, use botão "⬆️ Atualizar ONU" **🏢 OLTs DISPONÍVEIS:** • OLT FIBERHOME (131.0.192.9) • OLT DITO (192.168.190.7) • OLT COLMEIA (192.168.190.8) • OLT CANAA (192.168.190.9) • OLT CANAA 2 (192.168.190.10) • OLT PEDRO AFONSO 1 (192.168.100.2) • OLT CANAVIEIRAS (192.168.100.4)`; break; case 'juniper': helpMessage = `👥 **JUNIPER - GERENCIAR USUÁRIOS** **🚫 DESCONECTAR:** • "desconectar cliente USUARIO" • "kick usuário USUARIO" • "remover cliente USUARIO" **📊 LISTAR:** • "listar usuários conectados" **🔧 STATUS:** • "verificar concentrador" • "status do juniper" **🌐 PING:** • "ping 131.0.192.254" **🔄 REINICIAR:** • "reiniciar juniper"`; break; case 'ssh': helpMessage = `🔐 **SSH E CONEXÕES** **📡 CONECTAR:** • /ssh • "ssh juniper" • "acessar concentrador" **📋 LISTAR:** • /equipamentos **🔧 EXEMPLOS:** • "conectar no concentrador" • "acessar juniper" • "ssh olt"`; break; case 'suporte': helpMessage = `🎫 **SISTEMA DE SUPORTE** **🆕 ABRIR SUPORTE:** • "abrir suporte" • "criar suporte para João Silva" • "novo chamado" • "suporte cliente 123.456.789-10" **🔄 PROCESSO INTERATIVO:** 1️⃣ Busca do cliente (nome ou CPF) 2️⃣ Seleção do serviço 3️⃣ Escolha da categoria do problema 4️⃣ Seleção do técnico responsável 5️⃣ Agendamento de data/hora 6️⃣ Descrição detalhada 7️⃣ Confirmação e protocolo **📅 EXEMPLOS DE DATA/HORA:** • "amanhã às 14h" • "sexta-feira 09:00" • "15/08 às 16h30" • "hoje 18h" • "em 3 dias às 10h" • "segunda-feira 08:30"`; break; case 'admin': helpMessage = `🛡️ **COMANDOS ADMINISTRATIVOS** **👥 USUÁRIOS:** • "admin" • "usuarios autorizados" • "meu id" **📊 STATUS:** • "fiberhome status" • "status topsapp" **🔧 FIBERHOME ESPECÍFICOS:** • "status onu FHTT12345678" • "verificar onu FHTT12345678" • "onu status DeviceName" **🔒 SEGURANÇA:** • Bot restrito a usuários autorizados • Para solicitar acesso, forneça seu ID`; break; default: helpMessage = '❌ Seção de ajuda não encontrada.'; } await this.bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: [[backButton]] } }); } /** * Listar equipamentos */ async handleListEquipments(chatId) { const equipments = this.ssh.listEquipments(); if (equipments.length === 0) { await this.bot.sendMessage(chatId, '📋 Nenhum equipamento configurado.'); return; } let message = '📋 *Equipamentos Disponíveis:*\n\n'; equipments.forEach((eq, index) => { message += `${index + 1}. *${eq.name}*\n`; message += ` 📍 ${eq.host}:${eq.port}\n`; message += ` 🔑 ID: \`${eq.id}\`\n\n`; }); message += '💡 *Como usar:* Digite "conectar " ou "ssh "'; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } /** * Conexão SSH */ async handleSSH(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, '❓ Especifique o equipamento. Exemplo: "ssh router1"'); return; } await this.bot.sendMessage(chatId, `🔄 Conectando em ${target}...`); try { const result = await this.ssh.connect(target); if (result.success) { await this.bot.sendMessage(chatId, `✅ ${result.message}`); // Oferecer opções de comandos const keyboard = { inline_keyboard: [ [ { text: '📊 Status', callback_data: `cmd_status_${result.connectionId}` }, { text: '🔄 Reiniciar', callback_data: `cmd_reboot_${result.connectionId}` } ], [ { text: '📋 Comando personalizado', callback_data: `cmd_custom_${result.connectionId}` }, { text: '❌ Desconectar', callback_data: `cmd_disconnect_${result.connectionId}` } ] ] }; await this.bot.sendMessage(chatId, '🎛️ O que você gostaria de fazer?', { reply_markup: keyboard }); } else { await this.bot.sendMessage(chatId, `❌ Falha na conexão: ${result.message}`); } } catch (error) { await this.bot.sendMessage(chatId, `❌ Erro: ${error.message}`); } } /** * Verificar status */ async handleStatus(chatId, target) { if (!target) { // Mostrar status geral const connections = this.ssh.getActiveConnections(); if (connections.length === 0) { await this.bot.sendMessage(chatId, '📊 Nenhuma conexão ativa no momento.'); return; } let message = '📊 *Conexões Ativas:*\n\n'; connections.forEach((conn, index) => { message += `${index + 1}. ${conn.equipment} (${conn.host})\n`; message += ` ⏰ Conectado: ${conn.connectedAt.toLocaleString()}\n\n`; }); await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); return; } await this.bot.sendMessage(chatId, `🔍 Verificando status de ${target}...`); try { const result = await this.ssh.testConnection(target); if (result.success) { await this.bot.sendMessage(chatId, `✅ ${result.message}`); } else { await this.bot.sendMessage(chatId, `❌ ${result.message}`); } } catch (error) { await this.bot.sendMessage(chatId, `❌ Erro ao verificar status: ${error.message}`); } } /** * Conectar a um equipamento */ async handleConnect(chatId, target) { await this.handleSSH(chatId, target); } /** * Desconectar */ async handleDisconnect(chatId, target) { if (!target || target === 'todas' || target === 'all') { // Desconectar todas this.ssh.closeAllConnections(); await this.bot.sendMessage(chatId, '🔌 Todas as conexões foram encerradas.'); return; } // Implementar lógica para desconectar equipamento específico await this.bot.sendMessage(chatId, `🔌 Desconectando ${target}...`); } /** * Reiniciar equipamento */ async handleRestart(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, '❓ Especifique qual equipamento reiniciar.'); return; } await this.bot.sendMessage(chatId, `⚠️ Reiniciar ${target}? Esta ação pode causar indisponibilidade.`, { reply_markup: { inline_keyboard: [ [ { text: '✅ Confirmar', callback_data: `confirm_restart_${target}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] } }); } /** * Ping */ async handlePing(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, '❓ Especifique o IP ou hostname para fazer ping.'); return; } await this.bot.sendMessage(chatId, `🏓 Fazendo ping para ${target}...`); // Implementar ping usando comandos do sistema // Por enquanto, simular resposta setTimeout(async () => { await this.bot.sendMessage(chatId, `🏓 Ping para ${target}: 12ms - Conexão OK`); }, 2000); } /** * Desconectar usuário específico no concentrador Juniper */ async handleDisconnectUser(chatId, username) { if (!username) { await this.bot.sendMessage(chatId, '❓ Especifique o usuário a ser desconectado. Exemplo: "desconectar cliente joao123"'); return; } await this.bot.sendMessage(chatId, `🔄 Desconectando usuário ${username} do concentrador...`); try { // Usar concentrador Juniper como padrão const result = await this.ssh.disconnectJuniperUser('juniper', username); if (result.success) { await this.bot.sendMessage(chatId, `✅ ${result.message}`); if (result.output && result.output.trim()) { const outputMessage = `📋 *Saída do comando:*\n\`\`\`\n${result.output}\n\`\`\``; await this.bot.sendMessage(chatId, outputMessage, { parse_mode: 'Markdown' }); } } else { await this.bot.sendMessage(chatId, `❌ ${result.message}`); if (result.error && result.error.trim()) { const errorMessage = `⚠️ *Detalhes do erro:*\n\`\`\`\n${result.error}\n\`\`\``; await this.bot.sendMessage(chatId, errorMessage, { parse_mode: 'Markdown' }); } } } catch (error) { console.error('Erro ao desconectar usuário:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Listar usuários conectados no concentrador */ async handleListUsers(chatId, target = 'juniper') { await this.bot.sendMessage(chatId, '🔍 Listando usuários conectados no concentrador...'); try { const result = await this.ssh.listJuniperUsers(target); if (result.success) { let message = '📋 *Usuários Conectados:*\n\n'; if (result.output && result.output.trim()) { // Processar saída para extrair informações úteis const lines = result.output.split('\n'); let userCount = 0; for (const line of lines) { if (line.trim() && !line.includes('Total') && !line.includes('---')) { userCount++; message += `${userCount}. \`${line.trim()}\`\n`; } } if (userCount === 0) { message += '🟢 Nenhum usuário conectado no momento.\n'; } else { message += `\n👥 Total: ${userCount} usuário(s) conectado(s)`; } } else { message += '🟢 Nenhum usuário conectado no momento.'; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); // Oferecer opções para desconectar usuários se houver algum conectado if (result.output && result.output.trim() && !result.output.includes('Total: 0')) { const keyboard = { inline_keyboard: [ [ { text: '🔄 Atualizar lista', callback_data: 'refresh_users' }, { text: '❌ Desconectar usuário', callback_data: 'disconnect_user_prompt' } ] ] }; await this.bot.sendMessage(chatId, '🎛️ O que você gostaria de fazer?', { reply_markup: keyboard }); } } else { await this.bot.sendMessage(chatId, `❌ ${result.message}`); } } catch (error) { console.error('Erro ao listar usuários:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Verificar sinal de ONU */ async handleVerSinal(chatId, serialNumber) { console.log(`🎯 handleVerSinal chamado - chatId: ${chatId}, serialNumber: ${serialNumber}`); if (!serialNumber) { console.log('❌ Serial number não fornecido'); await this.bot.sendMessage(chatId, '❓ Especifique o serial number da ONU. Exemplo: "ver sinal DACM91569513"'); return; } // Obter lista de OLTs disponíveis const olts = this.ssh.listOLTs(); console.log(`📋 OLTs Datacom encontradas: ${olts.length}`); // Criar menu de seleção de OLT const keyboard = { inline_keyboard: [] }; // Adicionar OLT Fiberhome como primeira opção keyboard.inline_keyboard.push([ { text: '🔶 OLT FIBERHOME', callback_data: `check_signal_fiberhome_${serialNumber}` } ]); // Adicionar separador visual if (olts.length > 0) { keyboard.inline_keyboard.push([ { text: '➖ OLTs DATACOM ➖', callback_data: 'separator' } ]); // Adicionar botões para cada OLT Datacom (2 por linha) for (let i = 0; i < olts.length; i += 2) { const row = []; row.push({ text: olts[i].name, callback_data: `check_signal_${olts[i].id}_${serialNumber}` }); if (i + 1 < olts.length) { row.push({ text: olts[i + 1].name, callback_data: `check_signal_${olts[i + 1].id}_${serialNumber}` }); } keyboard.inline_keyboard.push(row); } } // Adicionar botão de cancelar keyboard.inline_keyboard.push([ { text: '❌ Cancelar', callback_data: 'cancel_action' } ]); console.log(`📤 Enviando mensagem com Fiberhome + ${olts.length} OLTs Datacom para seleção`); await this.bot.sendMessage(chatId, `🔍 *Verificar sinal da ONU*\nSerial: \`${serialNumber}\`\n\nSelecione onde deseja pesquisar:`, { parse_mode: 'Markdown', reply_markup: keyboard }); console.log(`✅ Mensagem de seleção de OLT enviada com sucesso`); } /** * Executa verificação de sinal em uma OLT específica */ async handleCheckSignalOLT(chatId, oltId, serialNumber) { const olt = this.ssh.equipamentos.get(oltId); if (!olt) { await this.bot.sendMessage(chatId, `❌ OLT ${oltId} não encontrada.`); return; } await this.bot.sendMessage(chatId, `🔍 Procurando ONU ${serialNumber} na ${olt.name}...`); try { const result = await this.ssh.checkOnuSignalBySerial(oltId, serialNumber); if (result.success) { // Escapar caracteres especiais do Markdown const oltNameEscaped = olt.name.replace(/_/g, '\\_'); const serialEscaped = serialNumber.replace(/_/g, '\\_'); let message = `📡 *Sinal da ONU ${serialEscaped}*\n\n`; message += `🏢 *OLT:* ${oltNameEscaped} (${olt.host})\n`; message += `📍 *Localização:* Slot ${result.slot}, ONU ${result.onuId}\n\n`; message += `📊 *Informações do Sinal:*\n`; message += `\`\`\`\n${result.signalOutput}\n\`\`\``; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); // Oferecer opções adicionais const keyboard = { inline_keyboard: [ [ { text: '🔄 Atualizar sinal', callback_data: `check_signal_${oltId}_${serialNumber}` }, { text: '📋 Ver configuração', callback_data: `show_config_${oltId}_${result.slot}_${result.onuId}` } ], [ { text: '🔌 Reiniciar ONU', callback_data: `reset_onu_${oltId}_${result.slot}_${result.onuId}` }, { text: '⬆️ Atualizar ONU', callback_data: `update_onu_${oltId}_${result.slot}_${result.onuId}` } ], [ { text: '🔍 Pesquisar em outra OLT', callback_data: `ver_sinal_${serialNumber}` } ] ] }; await this.bot.sendMessage(chatId, '🎛️ O que você gostaria de fazer?', { reply_markup: keyboard }); } else { await this.bot.sendMessage(chatId, `❌ ${result.message}`); // Oferecer pesquisar em outras OLTs const keyboard = { inline_keyboard: [ [ { text: '🔍 Pesquisar em outra OLT', callback_data: `ver_sinal_${serialNumber}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.sendMessage(chatId, 'ONU não encontrada nesta OLT. Deseja pesquisar em outra?', { reply_markup: keyboard }); } } catch (error) { console.error('Erro ao verificar sinal:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Comando para remover ONU */ async handleRemoverOnu(chatId, serialNumber) { if (!serialNumber) { await this.bot.sendMessage(chatId, '❌ Por favor, informe o serial da ONU que deseja remover.\n\nExemplo: `remover FHTT12345678`', { parse_mode: 'Markdown' }); return; } // Perguntar em qual OLT buscar const oltOptions = this.ssh.listOLTs(); if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; } let keyboard = { inline_keyboard: oltOptions.map(olt => [ { text: `🏢 ${olt.name}`, callback_data: `remove_onu_${olt.id}_${serialNumber}` } ]) }; // Adicionar opção de cancelar keyboard.inline_keyboard.push([ { text: '❌ Cancelar', callback_data: 'cancel_action' } ]); // Escapar caracteres especiais do serial const serialEscaped = serialNumber.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `🗑️ *Remover ONU ${serialEscaped}*\n\n` + '🏢 Selecione a OLT onde a ONU está configurada:', { reply_markup: keyboard, parse_mode: 'Markdown' } ); } /** * Comando para adicionar ONU */ async handleAdicionarOnu(chatId, serialNumber) { if (!serialNumber) { await this.bot.sendMessage(chatId, '❌ Por favor, informe o serial da ONU que deseja adicionar.\n\nExemplo: `adicionar onu FHTT12345678`', { parse_mode: 'Markdown' }); return; } // Perguntar em qual OLT adicionar const oltOptions = this.ssh.listOLTs(); if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; } let keyboard = { inline_keyboard: oltOptions.map(olt => [ { text: `🏢 ${olt.name}`, callback_data: `add_onu_${olt.id}_${serialNumber}` } ]) }; // Adicionar opção de cancelar keyboard.inline_keyboard.push([ { text: '❌ Cancelar', callback_data: 'cancel_action' } ]); // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `➕ *Adicionar ONU ${serialEscaped}*\n\n` + '🏢 Selecione a OLT onde deseja adicionar a ONU:', { reply_markup: keyboard, parse_mode: 'Markdown' } ); } /** * Comando para adicionar ONU com VLAN customizada */ async handleAdicionarOnuVlan(chatId, serialNumber, vlanId) { if (!serialNumber) { await this.bot.sendMessage(chatId, '❌ Por favor, informe o serial da ONU.\n\nExemplo: `adicionar onu vlan 603 FHTT12345678`', { parse_mode: 'Markdown' }); return; } if (!vlanId || isNaN(vlanId) || vlanId < 1 || vlanId > 4094) { await this.bot.sendMessage(chatId, '❌ VLAN ID inválida. Deve ser um número entre 1 e 4094.\n\nExemplo: `adicionar onu vlan 603 FHTT12345678`', { parse_mode: 'Markdown' }); return; } // Perguntar em qual OLT adicionar const oltOptions = this.ssh.listOLTs(); if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; } let keyboard = { inline_keyboard: oltOptions.map(olt => [ { text: `🏢 ${olt.name}`, callback_data: `add_onu_vlan_${olt.id}_${vlanId}_${serialNumber}` } ]) }; // Adicionar opção de cancelar keyboard.inline_keyboard.push([ { text: '❌ Cancelar', callback_data: 'cancel_action' } ]); // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `➕ *Adicionar ONU com VLAN Customizada*\n\n` + `📱 *Serial:* ${serialEscaped}\n` + `🏷️ *VLAN:* ${vlanId}\n\n` + '🏢 Selecione a OLT onde deseja adicionar a ONU:', { reply_markup: keyboard, parse_mode: 'Markdown' } ); } /** * Comando para adicionar ONU Fiberhome via TL1 */ async handleAdicionarOnuFiberhome(chatId, serialNumber, userId) { if (!serialNumber) { await this.bot.sendMessage(chatId, '❌ Por favor, informe o serial da ONU Fiberhome.\n\n' + 'Exemplo: `adicionar onu fiberhome FHTT12345678`', { parse_mode: 'Markdown' } ); return; } console.log(`📝 Criando sessão para usuário ${userId}, serial: ${serialNumber}`); // Criar sessão para coletar informações this.userSessions.set(userId, { action: 'adicionar_onu_fiberhome_step1', serial: serialNumber, timestamp: Date.now() }); console.log(`✅ Sessão criada. Sessões ativas:`, this.userSessions.size); // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); // Perguntar o alias/nome do usuário await this.bot.sendMessage(chatId, `➕ *Adicionar ONU Fiberhome*\n\n` + `📱 *Serial:* ${serialEscaped}\n` + `🔍 A porta PON será descoberta automaticamente\n\n` + '👤 Digite o *nome/alias* do usuário:', { parse_mode: 'Markdown' } ); } /** * Processar input do usuário durante sessões */ async handleUserSessionInput(chatId, text, userId, session) { try { console.log(`🔄 Processando input de sessão. Action: ${session.action}, Type: ${session.type}, Text: ${text}`); // Verificar se é sessão baseada em action (Fiberhome ONU) if (session.action) { switch (session.action) { case 'adicionar_onu_fiberhome_step1': // Recebeu o nome do usuário, adicionar sufixo e perguntar pelo tipo da ONU const usuarioBase = text.trim(); session.usuario = usuarioBase.includes('@') ? usuarioBase : `${usuarioBase}@clicknetguarai.com.br`; session.action = 'adicionar_onu_fiberhome_step2'; this.userSessions.set(userId, session); const keyboard = { inline_keyboard: [ [{ text: '🔌 AN5506-02-B (2 portas)', callback_data: 'fh_onu_type_AN5506-02-B' }], [{ text: '🔌 AN5506-01-A (1 porta)', callback_data: 'fh_onu_type_AN5506-01-A' }], [{ text: '❌ Cancelar', callback_data: 'cancel_action' }] ] }; await this.bot.sendMessage(chatId, `👤 **Usuário:** ${session.usuario}\n\n` + '🔌 Selecione o **tipo da ONU**:', { reply_markup: keyboard, parse_mode: 'Markdown' } ); return; case 'adicionar_onu_fiberhome_step3': // Recebeu confirmação de VLAN customizada const vlanInput = text.trim(); if (vlanInput && !isNaN(vlanInput)) { const vlan = parseInt(vlanInput); if (vlan >= 1 && vlan <= 4094) { session.vlan = vlan; } else { await this.bot.sendMessage(chatId, '❌ VLAN inválida. Usando VLAN padrão 1002.'); session.vlan = 1002; } } else { session.vlan = 1002; } // Limpar sessão this.userSessions.delete(userId); // Executar adição da ONU await this.executarAdicaoOnuFiberhome(chatId, session); return; } } // Verificar se é sessão baseada em type (outros fluxos) if (session.type) { switch (session.type) { case 'waiting_custom_command': await this.executeCustomCommand(chatId, session.connectionId, text); this.userSessions.delete(userId); return; case 'waiting_username_disconnect': await this.handleDisconnectUser(chatId, text.trim()); this.userSessions.delete(userId); return; } } // Estado desconhecido console.log(`❓ Estado de sessão desconhecido: action=${session.action}, type=${session.type}`); this.userSessions.delete(userId); await this.bot.sendMessage(chatId, '❓ Estado de sessão desconhecido. Tente novamente.'); } catch (error) { console.error('❌ Erro ao processar input da sessão:', error); this.userSessions.delete(userId); await this.bot.sendMessage(chatId, '❌ Erro ao processar sua resposta. Tente novamente.'); } } /** * Executar adição da ONU Fiberhome */ async executarAdicaoOnuFiberhome(chatId, session) { try { const { serial, usuario, vlan, onuType } = session; // Escapar caracteres especiais const serialEscaped = serial.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `⏳ Adicionando ONU Fiberhome...\n\n` + `🔍 Descobrindo porta PON automaticamente...\n` + `👤 *Usuário:* ${usuario}\n` + `📱 *Serial:* ${serialEscaped}\n` + `🔌 *Tipo:* ${onuType}\n` + `🏷️ *VLAN:* ${vlan}`, { parse_mode: 'Markdown' } ); // Executar comando TL1 via Fiberhome Manager const resultado = await this.fiberhomeTL1.adicionarOnuFiberhome( serial, usuario, vlan, onuType ); if (resultado.success) { // Escapar caracteres especiais do Markdown const oltEscaped = resultado.details.olt.replace(/_/g, '\\_'); const serialEscaped = serial.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `✅ *ONU Fiberhome Adicionada com Sucesso!*\n\n` + `👤 *Usuário:* ${usuario}\n` + `🏢 *OLT:* ${oltEscaped}\n` + `📡 *Porta descoberta:* Slot ${resultado.details.slot}, PON ${resultado.details.ponPort}\n` + `📱 *Serial:* ${serialEscaped}\n` + `🔌 *Tipo:* ${onuType}\n` + `🏷️ *VLAN:* ${vlan}\n` + `⚙️ *Portas configuradas:* ${resultado.details.portas}\n\n` + `🔐 Login Web: \`admin / clicknet10\`\n\n` + `⚡ A ONU foi reiniciada e estará disponível em alguns instantes.`, { parse_mode: 'Markdown' } ); } else { await this.bot.sendMessage(chatId, `❌ *Erro ao adicionar ONU Fiberhome*\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); } } catch (error) { console.error('❌ Erro ao executar adição de ONU Fiberhome:', error); await this.bot.sendMessage(chatId, `❌ *Erro ao adicionar ONU Fiberhome*\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Handler para remover ONU Fiberhome via TL1 */ async handleRemoverOnuFiberhome(chatId, serialNumber, userId) { try { // Validar serial if (!serialNumber || serialNumber.length !== 12) { await this.bot.sendMessage(chatId, '❌ Serial inválido. O serial deve conter 12 caracteres.\n\n' + 'Exemplo: `FHTT199cf3c8`' ); return; } // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); // Solicitar confirmação const keyboard = { inline_keyboard: [ [ { text: '✅ Sim, remover', callback_data: `confirm_remove_fh_${serialNumber}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.sendMessage(chatId, `🗑️ *Remover ONU Fiberhome*\n\n` + `📱 *Serial:* ${serialEscaped}\n\n` + `⚠️ Esta ação irá remover a ONU e todas as suas configurações.\n\n` + `Deseja confirmar a remoção?`, { reply_markup: keyboard, parse_mode: 'Markdown' } ); } catch (error) { console.error('❌ Erro ao preparar remoção de ONU Fiberhome:', error); await this.bot.sendMessage(chatId, `❌ *Erro ao preparar remoção*\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Comando não reconhecido */ async handleUnknown(chatId, text) { const suggestions = this.nlp.suggestCorrection(text); let message = '❓ Não entendi o comando. '; if (suggestions.length > 0) { message += `Talvez você quis dizer: ${suggestions.join(', ')}?\n\n`; } message += 'Digite /help para ver todos os comandos disponíveis.'; await this.bot.sendMessage(chatId, message); } /** * Processa callbacks de botões inline */ async handleCallbackQuery(callbackQuery) { const chatId = callbackQuery.message.chat.id; const data = callbackQuery.data; const userId = callbackQuery.from.id; // Verificar autorização if (!this.isAuthorized(userId)) { await this.bot.answerCallbackQuery(callbackQuery.id, '🚫 Acesso negado'); console.log(`🚫 Callback negado para usuário (${userId})`); return; } try { console.log(`🔍 Callback recebido: ${data}`); console.log(`🔍 Verifica confirm_remove_: ${data.startsWith('confirm_remove_')}`); if (data.startsWith('cmd_')) { console.log(`🔍 Condição cmd_ atendida`); await this.handleCommandCallback(callbackQuery, data); } else if (data.startsWith('confirm_remove_fh_')) { console.log(`🔍 Chamando handleConfirmRemoveFiberhomeCallback`); try { await this.handleConfirmRemoveFiberhomeCallback(callbackQuery, data); } catch (confirmError) { console.error(`❌ Erro em handleConfirmRemoveFiberhomeCallback:`, confirmError); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro na confirmação'); } } else if (data.startsWith('confirm_remove_')) { console.log(`🔍 Chamando handleConfirmRemoveCallback`); try { await this.handleConfirmRemoveCallback(callbackQuery, data); } catch (confirmError) { console.error(`❌ Erro em handleConfirmRemoveCallback:`, confirmError); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro na confirmação'); } } else if (data.startsWith('confirm_add_')) { console.log(`🔍 Chamando handleConfirmAddCallback`); try { await this.handleConfirmAddCallback(callbackQuery, data); } catch (confirmError) { console.error(`❌ Erro em handleConfirmAddCallback:`, confirmError); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro na confirmação'); } } else if (data.startsWith('confirm_block_service_')) { console.log(`🔍 Chamando handleConfirmBlockServiceCallback`); await this.handleConfirmBlockServiceCallback(callbackQuery, data); } else if (data.startsWith('confirm_unblock_service_')) { console.log(`🔍 Chamando handleConfirmUnblockServiceCallback`); await this.handleConfirmUnblockServiceCallback(callbackQuery, data); } else if (data.startsWith('confirm_block_all_')) { console.log(`🔍 Chamando handleConfirmBlockAllCallback`); await this.handleConfirmBlockAllCallback(callbackQuery, data); } else if (data.startsWith('confirm_unblock_all_')) { console.log(`🔍 Chamando handleConfirmUnblockAllCallback`); await this.handleConfirmUnblockAllCallback(callbackQuery, data); } else if (data.startsWith('confirm_block_')) { console.log(`🔍 Chamando handleConfirmBlockCallback`); await this.handleConfirmBlockCallback(callbackQuery, data); } else if (data.startsWith('confirm_unblock_')) { console.log(`🔍 Chamando handleConfirmUnblockCallback`); await this.handleConfirmUnblockCallback(callbackQuery, data); } else if (data.startsWith('block_service_')) { console.log(`🔍 Chamando handleBlockServiceSelection`); await this.handleBlockServiceSelection(callbackQuery, data); } else if (data.startsWith('unblock_service_')) { console.log(`🔍 Chamando handleUnblockServiceCallback`); await this.handleUnblockServiceCallback(callbackQuery, data); } else if (data.startsWith('confirm_')) { console.log(`🔍 Condição confirm_ atendida`); await this.handleConfirmCallback(callbackQuery, data); } else if (data.startsWith('check_signal_')) { console.log(`🔍 Condição check_signal_ atendida`); await this.handleCheckSignalCallback(callbackQuery, data); } else if (data.startsWith('ver_sinal_')) { console.log(`🔍 Condição ver_sinal_ atendida`); const serialNumber = data.replace('ver_sinal_', ''); await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleVerSinal(chatId, serialNumber); } else if (data.startsWith('show_config_')) { console.log(`🔍 Condição show_config_ atendida`); await this.handleShowConfigCallback(callbackQuery, data); } else if (data.startsWith('reset_onu_')) { console.log(`🔍 Condição reset_onu_ atendida`); await this.handleResetOnuCallback(callbackQuery, data); } else if (data.startsWith('update_onu_')) { console.log(`🔍 Condição update_onu_ atendida`); await this.handleUpdateOnuCallback(callbackQuery, data); } else if (data.startsWith('firmware_')) { console.log(`🔍 Condição firmware_ atendida`); await this.handleFirmwareCallback(callbackQuery, data); } else if (data.startsWith('remove_onu_')) { console.log(`🔍 Chamando handleRemoveOnuCallback`); await this.handleRemoveOnuCallback(callbackQuery, data); } else if (data.startsWith('add_onu_vlan_')) { console.log(`🔍 Chamando handleAddOnuVlanCallback`); await this.handleAddOnuVlanCallback(callbackQuery, data); } else if (data.startsWith('add_onu_')) { console.log(`🔍 Chamando handleAddOnuCallback`); await this.handleAddOnuCallback(callbackQuery, data); } else if (data.startsWith('fh_onu_type_')) { console.log(`🔍 Chamando handleFiberhomeOnuTypeCallback`); await this.handleFiberhomeOnuTypeCallback(callbackQuery, data); } else if (data.startsWith('fh_vlan_')) { console.log(`🔍 Chamando handleFiberhomeVlanCallback`); await this.handleFiberhomeVlanCallback(callbackQuery, data); } else if (data.startsWith('fiberhome_details_')) { console.log(`🔍 Condição fiberhome_details_ atendida`); const serialNumber = data.replace('fiberhome_details_', ''); await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleStatusONU(chatId, serialNumber); } else if (data.startsWith('dados_cliente_')) { console.log(`🔍 Condição dados_cliente_ atendida`); await this.handleDadosClienteCallback(callbackQuery, data); } else if (data.startsWith('faturas_cliente_')) { console.log(`🔍 Condição faturas_cliente_ atendida`); await this.handleFaturasClienteCallback(callbackQuery, data); } else if (data.startsWith('servicos_cliente_')) { console.log(`🔍 Condição servicos_cliente_ atendida`); await this.handleServicosClienteCallback(callbackQuery, data); } else if (data.startsWith('help_')) { console.log(`🔍 Condição help_ atendida`); await this.handleHelpDetailCallback(callbackQuery, data); } else if (data === 'separator') { // Ignorar clique no separador visual await this.bot.answerCallbackQuery(callbackQuery.id); } else if (data === 'cancel_action') { await this.bot.answerCallbackQuery(callbackQuery.id, 'Ação cancelada'); // Limpar sessão do usuário se existir if (this.userSessions.has(userId)) { this.userSessions.delete(userId); } await this.bot.editMessageText('❌ Ação cancelada.', { chat_id: chatId, message_id: callbackQuery.message.message_id }); } else if (data === 'refresh_users') { await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleListUsers(chatId); } else if (data === 'disconnect_user_prompt') { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText('👤 Digite o nome do usuário que deseja desconectar:', { chat_id: chatId, message_id: callbackQuery.message.message_id }); // Armazenar estado para próxima mensagem this.userSessions.set(callbackQuery.from.id, { type: 'waiting_username_disconnect' }); } else if (data.startsWith('suporte_cliente_')) { console.log(`🔍 Condição suporte_cliente_ atendida`); await this.handleSuporteClienteCallback(callbackQuery, data); } else if (data.startsWith('suporte_servico_')) { console.log(`🔍 Condição suporte_servico_ atendida`); await this.handleSuporteServicoCallback(callbackQuery, data); } else if (data.startsWith('linha_')) { console.log(`🔍 Condição linha_ atendida`); await this.handleLinhaDigitavelCallback(callbackQuery, data); } else if (data.startsWith('pix_')) { console.log(`🔍 Condição pix_ atendida`); await this.handlePixCallback(callbackQuery, data); } else { console.log(`❓ Callback não reconhecido: ${data}`); await this.bot.answerCallbackQuery(callbackQuery.id, 'Comando não reconhecido'); } } catch (error) { console.error('❌ Erro no callback:', error); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno'); } } /** * Processa callbacks de comandos */ async handleCommandCallback(callbackQuery, data) { const parts = data.split('_'); const action = parts[1]; const connectionId = parts.slice(2).join('_'); await this.bot.answerCallbackQuery(callbackQuery.id); switch (action) { case 'status': await this.executeSSHCommand(callbackQuery, connectionId, 'uptime'); break; case 'reboot': await this.confirmRebootCallback(callbackQuery, connectionId); break; case 'disconnect': await this.disconnectCallback(callbackQuery, connectionId); break; case 'custom': await this.requestCustomCommand(callbackQuery, connectionId); break; } } /** * Executa comando SSH e mostra resultado */ async executeSSHCommand(callbackQuery, connectionId, command) { const chatId = callbackQuery.message.chat.id; try { const result = await this.ssh.executeCommand(connectionId, command); let message = `💻 *Comando:* \`${command}\`\n\n`; if (result.success) { message += `✅ *Resultado:*\n\`\`\`\n${result.output}\`\`\``; } else { message += `❌ *Erro:*\n\`\`\`\n${result.error}\`\`\``; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { await this.bot.sendMessage(chatId, `❌ Erro ao executar comando: ${error.message}`); } } /** * Confirma reinicialização */ async confirmRebootCallback(callbackQuery, connectionId) { const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText('⚠️ Tem certeza que deseja reiniciar o equipamento?', { chat_id: chatId, message_id: callbackQuery.message.message_id, reply_markup: { inline_keyboard: [ [ { text: '✅ SIM, reiniciar', callback_data: `confirm_reboot_${connectionId}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] } }); } /** * Desconecta conexão SSH */ async disconnectCallback(callbackQuery, connectionId) { const chatId = callbackQuery.message.chat.id; const success = this.ssh.disconnect(connectionId); if (success) { await this.bot.editMessageText('🔌 Conexão encerrada com sucesso.', { chat_id: chatId, message_id: callbackQuery.message.message_id }); } else { await this.bot.editMessageText('❌ Conexão não encontrada ou já encerrada.', { chat_id: chatId, message_id: callbackQuery.message.message_id }); } } /** * Solicita comando personalizado */ async requestCustomCommand(callbackQuery, connectionId) { const chatId = callbackQuery.message.chat.id; // Armazenar estado para próxima mensagem this.userSessions.set(callbackQuery.from.id, { type: 'waiting_custom_command', connectionId: connectionId }); await this.bot.editMessageText('💻 Digite o comando que deseja executar:', { chat_id: chatId, message_id: callbackQuery.message.message_id }); } /** * Processa callbacks de confirmação */ async handleConfirmCallback(callbackQuery, data) { const parts = data.split('_'); const action = parts[1]; if (action === 'restart' || action === 'reboot') { const target = parts.slice(2).join('_'); await this.executeRestart(callbackQuery, target); } else if (action === 'reset' && parts[2] === 'onu') { // Formato: confirm_reset_onu_oltid_slot_onuid await this.handleConfirmResetOnu(callbackQuery, data); } else if (action === 'firmware') { // Formato: confirm_firmware_filename_oltid_slot_onuid await this.handleConfirmFirmware(callbackQuery, data); } } /** * Executa reinicialização confirmada */ async executeRestart(callbackQuery, target) { const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText(`🔄 Reiniciando ${target}...`, { chat_id: chatId, message_id: callbackQuery.message.message_id }); // Simular reinicialização (implementar comando real aqui) setTimeout(async () => { await this.bot.sendMessage(chatId, `✅ ${target} foi reiniciado com sucesso.`); }, 3000); } /** * MÉTODO REMOVIDO - handleUserSessionInput duplicado foi mesclado acima */ /** * Executa comando personalizado em uma conexão SSH */ async executeCustomCommand(chatId, connectionId, command) { await this.bot.sendMessage(chatId, `💻 Executando: \`${command}\``, { parse_mode: 'Markdown' }); try { const result = await this.ssh.executeCommand(connectionId, command); let message = `💻 *Comando:* \`${command}\`\n\n`; if (result.success) { message += `✅ *Resultado:*\n\`\`\`\n${result.output || 'Comando executado com sucesso'}\`\`\``; } else { message += `❌ *Erro:*\n\`\`\`\n${result.error || 'Erro desconhecido'}\`\`\``; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { await this.bot.sendMessage(chatId, `❌ Erro ao executar comando: ${error.message}`); } } /** * Processa callback de verificação de sinal */ async handleCheckSignalCallback(callbackQuery, data) { const parts = data.split('_'); const chatId = callbackQuery.message.chat.id; // Verificar se é Fiberhome (formato: check_signal_fiberhome_SERIAL) if (parts.length === 4 && parts[2] === 'fiberhome') { const serialNumber = parts[3]; await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleCheckSignalFiberhome(chatId, serialNumber); return; } // Formato padrão Datacom: check_signal_olt_dito_SERIAL ou check_signal_olt_pedro_afonso_SERIAL let oltId, serialNumber; if (parts.length === 6 && parts[2] === 'olt' && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: check_signal_olt_pedro_afonso_SERIAL oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" serialNumber = parts[5]; } else if (parts.length === 5) { // Caso: check_signal_olt_dito_SERIAL oltId = parts[2] + '_' + parts[3]; // "olt_dito" serialNumber = parts[4]; } else if (parts.length === 4) { // Caso: check_signal_oltid_SERIAL (se existir formato mais simples) oltId = parts[2]; serialNumber = parts[3]; } else { // Tentar reconstruir dinamicamente oltId = parts.slice(2, -1).join('_'); // Tudo exceto o último (que é o serial) serialNumber = parts[parts.length - 1]; // Último elemento } await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleCheckSignalOLT(chatId, oltId, serialNumber); } /** * Executa verificação de sinal na OLT Fiberhome */ async handleCheckSignalFiberhome(chatId, serialNumber) { // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); await this.bot.sendMessage(chatId, `🔍 Procurando ONU ${serialEscaped} na OLT Fiberhome...`); try { // Usar a mesma lógica do handleStatusONU let resultado; if (serialNumber.length === 12 && /^[A-Z0-9]+$/.test(serialNumber)) { // É um serial resultado = await this.fiberhome.obterStatusONU(serialNumber); } else { // Pode ser device name resultado = await this.fiberhome.obterStatusONUPorNome(serialNumber); } if (resultado.success) { // Formatar resposta similar às OLTs Datacom let message = `📡 *Sinal da ONU ${serialEscaped}*\n\n`; message += `🏢 *OLT:* Fiberhome UNM2000 (131.0.192.9)\n`; message += `📍 *Localização:* Slot ${resultado.slot}, PON ${resultado.pon}, ONU ${resultado.onuId}\n`; message += `🔌 *Status:* ${resultado.online ? '🟢 Online' : '🔴 Offline'}\n\n`; message += `📊 *Informações do Sinal:*\n`; message += `📡 *RX Power:* ${resultado.rxPower} _(uplink)_\n`; message += `📤 *TX Power:* ${resultado.txPower}\n`; message += `📏 *Distância:* ${resultado.distance}\n\n`; // Nota sobre leitura de potência message += `_💡 Valores medidos pela OLT via protocolo TL1_\n\n`; // Status das portas Ethernet (resumido) if (resultado.portasEthernet.length > 0) { const portasAtivas = resultado.portasEthernet.filter(p => p.status === 'active' || p.status === 'up').length; message += `🔗 *Portas Ethernet:* ${portasAtivas}/${resultado.portasEthernet.length} ativas\n`; } // Serviços WAN if (resultado.servicosWAN.length > 0) { const servicosAtivos = resultado.servicosWAN.filter(s => s.status === 'active').length; message += `🌐 *Serviços WAN:* ${servicosAtivos} ativo(s)\n`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); // Oferecer opções adicionais (similar às OLTs Datacom) const keyboard = { inline_keyboard: [ [ { text: '🔄 Atualizar sinal', callback_data: `check_signal_fiberhome_${serialNumber}` }, { text: '📋 Detalhes completos', callback_data: `fiberhome_details_${serialNumber}` } ], [ { text: '🔍 Pesquisar em OLT Datacom', callback_data: `ver_sinal_${serialNumber}` } ] ] }; await this.bot.sendMessage(chatId, '🎛️ O que você gostaria de fazer?', { reply_markup: keyboard }); } else { await this.bot.sendMessage(chatId, `❌ ONU ${serialNumber} não encontrada na OLT Fiberhome.`); // Oferecer pesquisar em OLTs Datacom const keyboard = { inline_keyboard: [ [ { text: '🔍 Pesquisar em OLT Datacom', callback_data: `ver_sinal_${serialNumber}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.sendMessage(chatId, 'ONU não encontrada na Fiberhome. Deseja pesquisar nas OLTs Datacom?', { reply_markup: keyboard }); } } catch (error) { console.error('Erro ao verificar sinal Fiberhome:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Processa callback de mostrar configuração */ async handleShowConfigCallback(callbackQuery, data) { const parts = data.split('_'); // Formato: show_config_olt_dito_slot_onuid ou show_config_olt_pedro_afonso_slot_onuid let oltId, slot, onuId; if (parts.length === 7 && parts[2] === 'olt' && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: show_config_olt_pedro_afonso_slot_onuid oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" slot = parts[5]; onuId = parts[6]; } else if (parts.length === 6) { // Caso: show_config_olt_dito_slot_onuid oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4]; onuId = parts[5]; } else if (parts.length === 5) { // Caso: show_config_oltid_slot_onuid (se existir formato mais simples) oltId = parts[2]; slot = parts[3]; onuId = parts[4]; } else { // Tentar reconstruir dinamicamente // Os dois últimos são sempre slot e onuId onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(2, -2).join('_'); // Tudo entre o terceiro e os dois últimos } const chatId = callbackQuery.message.chat.id; await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.sendMessage(chatId, `📋 Buscando configuração da ONU ${onuId} no slot ${slot}...`); try { const connection = await this.ssh.connect(oltId); if (!connection.success) { await this.bot.sendMessage(chatId, `❌ Erro na conexão: ${connection.message}`); return; } const configCommand = `show running-config interface gpon ${slot} onu ${onuId}`; const spCommand = `show running-config service-port | context-match "gpon ${slot} onu ${onuId}"`; const [configResult, spResult] = await Promise.all([ this.ssh.executeCommand(connection.connectionId, configCommand), this.ssh.executeCommand(connection.connectionId, spCommand) ]); this.ssh.disconnect(connection.connectionId); if (configResult.success) { // Extrair service-ports const servicePorts = spResult.success ? this.ssh.parseServicePorts(spResult.output) : []; let message = `📋 *Configuração da ONU*\n`; message += `📍 Slot: ${slot}, ONU: ${onuId}\n`; if (servicePorts.length > 0) { message += `🔌 *Service-port${servicePorts.length > 1 ? 's' : ''}:* ${servicePorts.join(', ')}\n`; } else { message += `🔌 *Service-port:* não encontrado\n`; } message += `\n\`\`\`\n${configResult.output}\n\`\`\``; if (spResult.success && spResult.output && spResult.output.trim()) { message += `\n\n*Service-port(s):*\n\`\`\`\n${spResult.output.trim()}\n\`\`\``; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } else { await this.bot.sendMessage(chatId, `❌ Erro ao obter configuração: ${configResult.error}`); } } catch (error) { console.error('Erro ao mostrar configuração:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Processa callback de reiniciar ONU */ async handleResetOnuCallback(callbackQuery, data) { console.log(`🔌 handleResetOnuCallback chamado com data: ${data}`); const parts = data.split('_'); console.log(`📝 Parts: ${JSON.stringify(parts)}`); // Formato: reset_onu_olt_dito_slot_onuid ou reset_onu_olt_pedro_afonso_slot_onuid let oltId, slot, onuId; if (parts.length === 7 && parts[2] === 'olt' && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: reset_onu_olt_pedro_afonso_slot_onuid oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" slot = parts[5]; onuId = parts[6]; } else if (parts.length === 6) { // Caso: reset_onu_olt_dito_slot_onuid oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4]; onuId = parts[5]; } else if (parts.length === 5) { // Caso: reset_onu_oltid_slot_onuid (se existir formato mais simples) oltId = parts[2]; slot = parts[3]; onuId = parts[4]; } else { // Tentar reconstruir dinamicamente // Os dois últimos são sempre slot e onuId onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(2, -2).join('_'); // Tudo entre o terceiro e os dois últimos } console.log(`🎯 Parsed - OLT: ${oltId}, Slot: ${slot}, ONU: ${onuId}`); const chatId = callbackQuery.message.chat.id; // Primeiro mostrar confirmação await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `⚠️ **Confirmação de Reinicialização**\n\n` + `Tem certeza que deseja reiniciar a ONU?\n\n` + `📍 **Slot:** ${slot}\n` + `🔌 **ONU ID:** ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `⚠️ Esta ação irá desconectar temporariamente o cliente.`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: '✅ SIM, Reiniciar', callback_data: `confirm_reset_onu_${oltId}_${slot}_${onuId}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] } } ); } /** * Processa confirmação de reinicialização de ONU */ async handleConfirmResetOnu(callbackQuery, data) { console.log(`🔄 handleConfirmResetOnu chamado com data: ${data}`); const parts = data.replace('confirm_reset_onu_', '').split('_'); console.log(`📝 Parts após parsing: ${JSON.stringify(parts)}`); // Formato após remoção do prefixo: olt_dito_slot_onuid let oltId, slot, onuId; if (parts.length === 4) { // Caso: olt_dito_slot_onuid oltId = parts[0] + '_' + parts[1]; // "olt_dito" slot = parts[2]; onuId = parts[3]; } else if (parts.length === 3) { // Caso: oltid_slot_onuid oltId = parts[0]; slot = parts[1]; onuId = parts[2]; } else { // Tentar reconstruir dinamicamente onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(0, -2).join('_'); } console.log(`🎯 Parsed - OLT: ${oltId}, Slot: ${slot}, ONU: ${onuId}`); const chatId = callbackQuery.message.chat.id; await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `🔄 **Reiniciando ONU...**\n\n` + `📍 Slot: ${slot}\n` + `🔌 ONU ID: ${onuId}\n` + `🏢 OLT: ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `⏳ Aguarde, isso pode levar alguns segundos...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.resetOnuAlternative(oltId, slot, onuId); if (result.success) { await this.bot.editMessageText( `✅ **ONU Reiniciada com Sucesso!**\n\n` + `📍 Slot: ${slot}\n` + `🔌 ONU ID: ${onuId}\n` + `🏢 OLT: ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `🔄 A ONU deve voltar a funcionar em alguns instantes.\n` + `📊 Recomenda-se verificar o sinal novamente em 1-2 minutos.`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } else { await this.bot.editMessageText( `❌ **Erro ao Reiniciar ONU**\n\n` + `📍 Slot: ${slot}\n` + `🔌 ONU ID: ${onuId}\n` + `🏢 OLT: ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `❌ Erro: ${result.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('Erro ao reiniciar ONU:', error); await this.bot.editMessageText( `❌ **Erro Interno**\n\n` + `Não foi possível reiniciar a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Processa callback de atualização de firmware da ONU */ async handleUpdateOnuCallback(callbackQuery, data) { const parts = data.split('_'); // Formato: update_onu_olt_dito_slot_onuid ou update_onu_olt_pedro_afonso_slot_onuid let oltId, slot, onuId; if (parts.length === 7 && parts[2] === 'olt' && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: update_onu_olt_pedro_afonso_slot_onuid oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" slot = parts[5]; onuId = parts[6]; } else if (parts.length === 6) { // Caso: update_onu_olt_dito_slot_onuid oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4]; onuId = parts[5]; } else if (parts.length === 5) { // Caso: update_onu_oltid_slot_onuid (se existir formato mais simples) oltId = parts[2]; slot = parts[3]; onuId = parts[4]; } else { // Tentar reconstruir dinamicamente // Os dois últimos são sempre slot e onuId onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(2, -2).join('_'); // Tudo entre o terceiro e os dois últimos } const chatId = callbackQuery.message.chat.id; await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.sendMessage(chatId, `🔍 Identificando modelo da ONU ${onuId} no slot ${slot}...`); try { // Obter detalhes da ONU para identificar o modelo const details = await this.ssh.getOnuDetails(oltId, slot, onuId); if (!details.success) { await this.bot.sendMessage(chatId, `❌ Erro ao obter detalhes da ONU: ${details.message}`); return; } const equipmentId = details.equipmentId; console.log(`🔍 Equipment ID detectado: ${equipmentId}`); // Determinar opções de firmware baseado no modelo let firmwareOptions = []; if (equipmentId.includes('DM986-100')) { firmwareOptions = [ { text: '📦 FW INTERMEDIÁRIA', callback_data: `firmware_DM986-100-inter.tar_${oltId}_${slot}_${onuId}` }, { text: '📦 FW ATUAL (v56)', callback_data: `firmware_DM986-100-v56.tar_${oltId}_${slot}_${onuId}` }, { text: '📦 FW ROUTER (R300)', callback_data: `firmware_DM986-100-R300.tar_${oltId}_${slot}_${onuId}` } ]; } else if (equipmentId.includes('DM986-414')) { firmwareOptions = [ { text: '📦 FW INTERMEDIÁRIA', callback_data: `firmware_DM986-414-inter.tar_${oltId}_${slot}_${onuId}` }, { text: '📦 FW ATUAL (v70)', callback_data: `firmware_DM986-414-v70.tar_${oltId}_${slot}_${onuId}` } ]; } else { await this.bot.sendMessage(chatId, `❌ **Modelo não suportado**\n\n` + `📍 Slot: ${slot}, ONU: ${onuId}\n` + `🔍 Equipment ID: ${equipmentId}\n\n` + `⚠️ Apenas DM986-100 e DM986-414 são suportados para atualização.` ); return; } // Criar teclado com opções de firmware const keyboard = { inline_keyboard: [ ...firmwareOptions.map(option => [option]), [{ text: '❌ Cancelar', callback_data: 'cancel_action' }] ] }; await this.bot.sendMessage(chatId, `⬆️ **Atualização de Firmware**\n\n` + `📍 **Slot:** ${slot}\n` + `🔌 **ONU ID:** ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n` + `🔍 **Modelo:** ${equipmentId}\n\n` + `📦 Selecione a versão do firmware:`, { parse_mode: 'Markdown', reply_markup: keyboard } ); } catch (error) { console.error('Erro ao processar atualização de ONU:', error); await this.bot.sendMessage(chatId, `❌ Erro interno: ${error.message}`); } } /** * Processa callback de seleção de firmware */ async handleFirmwareCallback(callbackQuery, data) { // Formato: firmware_DM986-100-v56.tar_olt_dito_slot_onuid const parts = data.replace('firmware_', '').split('_'); let firmwareFile, oltId, slot, onuId; // O arquivo de firmware é sempre o primeiro elemento firmwareFile = parts[0]; // Processar o resto baseado no número de partes if (parts.length === 5) { // Caso: DM986-100-v56.tar_olt_dito_slot_onuid oltId = parts[1] + '_' + parts[2]; // "olt_dito" slot = parts[3]; onuId = parts[4]; } else if (parts.length === 4) { // Caso: DM986-100-v56.tar_oltid_slot_onuid oltId = parts[1]; slot = parts[2]; onuId = parts[3]; } else { // Tentar reconstruir dinamicamente onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(1, -2).join('_'); } const chatId = callbackQuery.message.chat.id; // Mostrar confirmação await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `⚠️ **Confirmação de Atualização de Firmware**\n\n` + `📦 **Firmware:** ${firmwareFile}\n` + `📍 **Slot:** ${slot}\n` + `🔌 **ONU ID:** ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `⚠️ **ATENÇÃO:**\n` + `• A atualização pode demorar alguns minutos\n` + `• A ONU ficará offline durante o processo\n` + `• Não interrompa a operação\n\n` + `Tem certeza que deseja continuar?`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: { inline_keyboard: [ [ { text: '✅ SIM, Atualizar', callback_data: `confirm_firmware_${firmwareFile}_${oltId}_${slot}_${onuId}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] } } ); } /** * Processa confirmação de atualização de firmware */ async handleConfirmFirmware(callbackQuery, data) { // Formato: confirm_firmware_DM986-100-v56.tar_olt_dito_slot_onuid const parts = data.replace('confirm_firmware_', '').split('_'); let firmwareFile, oltId, slot, onuId; firmwareFile = parts[0]; if (parts.length === 5) { oltId = parts[1] + '_' + parts[2]; slot = parts[3]; onuId = parts[4]; } else if (parts.length === 4) { oltId = parts[1]; slot = parts[2]; onuId = parts[3]; } else { onuId = parts[parts.length - 1]; slot = parts[parts.length - 2]; oltId = parts.slice(1, -2).join('_'); } const chatId = callbackQuery.message.chat.id; await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `⬆️ **Atualizando Firmware...**\n\n` + `📦 Firmware: ${firmwareFile}\n` + `📍 Slot: ${slot}\n` + `🔌 ONU ID: ${onuId}\n` + `🏢 OLT: ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `⏳ Enviando comando de atualização...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.updateOnuFirmware(oltId, slot, onuId, firmwareFile); if (result.success) { await this.bot.editMessageText( `✅ **Comando de Atualização Enviado!**\n\n` + `📦 **Firmware:** ${firmwareFile}\n` + `📍 **Slot:** ${slot}\n` + `🔌 **ONU ID:** ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `✅ **Status:** Comando enviado com sucesso\n` + `⏳ **Processamento:** A atualização está sendo processada pela OLT\n` + `🕒 **Tempo estimado:** 5-15 minutos\n` + `📡 **Importante:** Monitore o status da ONU\n\n` + `💡 *A ONU ficará offline durante a atualização*`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } else { await this.bot.editMessageText( `❌ **Erro ao Enviar Comando**\n\n` + `📦 **Firmware:** ${firmwareFile}\n` + `📍 **Slot:** ${slot}\n` + `🔌 **ONU ID:** ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `❌ **Erro:** ${result.message}\n\n` + `🔄 Tente novamente ou verifique a conexão`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('Erro ao atualizar firmware:', error); await this.bot.editMessageText( `❌ **Erro Interno**\n\n` + `Não foi possível atualizar o firmware.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para iniciar processo de remoção de ONU */ async handleRemoveOnuCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.split('_'); // remove_onu_olt_dito_[serialNumber] ou remove_onu_olt_pedro_afonso_[serialNumber] let oltId, serialNumber; if (parts.length >= 6 && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: remove_onu_olt_pedro_afonso_SERIAL oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" serialNumber = parts.slice(5).join('_'); // Serial pode ter underscores } else if (parts.length >= 5) { // Caso: remove_onu_olt_dito_SERIAL ou outras OLTs de duas palavras oltId = parts[2] + '_' + parts[3]; // "olt_dito" serialNumber = parts.slice(4).join('_'); // Serial pode ter underscores } else { // Fallback - caso não siga o padrão esperado oltId = parts[2]; serialNumber = parts.slice(3).join('_'); } await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `🔍 **Buscando ONU ${serialNumber}**\n\n` + `🏢 OLT: ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n` + `📱 Serial: ${serialNumber}\n\n` + `⏳ Localizando ONU e service-port...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.removeOnuBySerial(oltId, serialNumber); if (result.success && result.readyForRemoval) { // ONU encontrada - mostrar informações e pedir confirmação // O service-port NÃO é incluído no callback_data pois removeOnu busca todos automaticamente const keyboard = { inline_keyboard: [ [ { text: '✅ CONFIRMAR REMOÇÃO', callback_data: `confirm_remove_${oltId}_${result.slot.replace(/\//g, '-')}_${result.onuId}_${serialNumber}` } ], [ { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; const servicePortsInfo = result.servicePorts && result.servicePorts.length > 1 ? `🔌 **Service-ports:** ${result.servicePorts.join(', ')} *(${result.servicePorts.length} encontrados — todos serão removidos)*` : `🔌 **Service-port:** ${result.servicePort}`; await this.bot.editMessageText( `⚠️ **CONFIRMAÇÃO DE REMOÇÃO**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${result.slot}, ONU ${result.onuId}\n` + `${servicePortsInfo}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `⚠️ **ATENÇÃO:** Esta ação não pode ser desfeita!\n` + `A ONU será completamente removida da OLT.\n\n` + `🔴 **Confirme que deseja remover esta ONU:**`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: keyboard } ); } else { await this.bot.editMessageText( `❌ **ONU não encontrada**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `❌ **Erro:** ${result.message}\n\n` + `💡 Verifique se o serial está correto ou se a ONU existe nesta OLT`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('Erro ao buscar ONU para remoção:', error); await this.bot.editMessageText( `❌ **Erro interno**\n\n` + `Não foi possível buscar a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para confirmar remoção de ONU */ async handleConfirmRemoveCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; console.log(`🔍 handleConfirmRemoveCallback chamado com data: ${data}`); // Novo formato (sem servicePort): confirm_remove_olt_dito_[slot]_[onuId]_[serialNumber] const parts = data.split('_'); console.log(`🔍 Parts: ${JSON.stringify(parts)}`); let oltId, slot, onuId, serialNumber; if (parts.length >= 8 && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: confirm_remove_olt_pedro_afonso_slot_onuId_serialNumber oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" slot = parts[5].replace(/-/g, '/'); onuId = parts[6]; serialNumber = parts.slice(7).join('_'); } else if (parts.length >= 7) { // Caso: confirm_remove_olt_dito_slot_onuId_serialNumber oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4].replace(/-/g, '/'); onuId = parts[5]; serialNumber = parts.slice(6).join('_'); } else { // Fallback oltId = parts[2]; slot = parts[3].replace(/-/g, '/'); onuId = parts[4]; serialNumber = parts.slice(5).join('_'); } console.log(`🔍 Parsed - oltId: ${oltId}, slot: ${slot}, onuId: ${onuId}, serialNumber: ${serialNumber}`); await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `🗑️ **Removendo ONU...**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `⏳ Buscando e removendo todos os service-ports...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.removeOnu(oltId, slot, onuId); if (result.success) { const portsRemovidos = result.servicePorts && result.servicePorts.length > 0 ? result.servicePorts.join(', ') : 'N/A'; const portsLabel = result.servicePorts && result.servicePorts.length > 1 ? `• Removidos ${result.servicePorts.length} service-ports (${portsRemovidos})` : `• Removido service-port ${portsRemovidos}`; await this.bot.editMessageText( `✅ **ONU Removida com Sucesso!**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `✅ **Status:** ONU removida completamente\n` + `🗑️ **Ações realizadas:**\n` + `• Removida interface ONU\n` + `${portsLabel}\n` + `• Configuração commitada\n\n` + `💡 A ONU pode ser reconfigurada quando necessário`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } else { await this.bot.editMessageText( `❌ **Erro na Remoção**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `❌ **Erro:** ${result.message}\n\n` + `🔄 Verifique o status da ONU ou tente novamente`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('Erro ao remover ONU:', error); await this.bot.editMessageText( `❌ **Erro Interno**\n\n` + `Não foi possível remover a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para confirmar remoção de ONU Fiberhome via TL1 */ async handleConfirmRemoveFiberhomeCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; console.log(`🔍 handleConfirmRemoveFiberhomeCallback chamado com data: ${data}`); // Formato: confirm_remove_fh_SERIAL const serialNumber = data.replace('confirm_remove_fh_', '').toUpperCase(); console.log(`🔍 Serial a remover: ${serialNumber}`); await this.bot.answerCallbackQuery(callbackQuery.id); // Escapar caracteres especiais const serialEscaped = serialNumber.replace(/_/g, '\\_'); await this.bot.editMessageText( `🗑️ *Removendo ONU Fiberhome...*\n\n` + `📱 *Serial:* ${serialEscaped}\n\n` + `⏳ Aguarde...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { // Executar comando TL1 de remoção const resultado = await this.fiberhomeTL1.removerOnuFiberhome(serialNumber); if (resultado.success) { await this.bot.editMessageText( `✅ *ONU Fiberhome Removida com Sucesso!*\n\n` + `📱 *Serial:* ${serialEscaped}\n` + `🏢 *OLT:* ${resultado.details.olt.replace(/_/g, '\\_')}\n` + `📡 *Porta:* ${resultado.details.porta}\n\n` + `🔄 A ONU foi removida do sistema.`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } else { await this.bot.editMessageText( `❌ *Erro na Remoção*\n\n` + `📱 *Serial:* ${serialEscaped}\n\n` + `⚠️ *Erro:* ${resultado.error}\n\n` + `🔄 Verifique o status da ONU ou tente novamente`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('❌ Erro ao remover ONU Fiberhome:', error); await this.bot.editMessageText( `❌ *Erro Interno*\n\n` + `📱 *Serial:* ${serialEscaped}\n\n` + `⚠️ ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para iniciar processo de adição de ONU */ async handleAddOnuCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.split('_'); // add_onu_olt_dito_[serialNumber] ou add_onu_olt_pedro_afonso_[serialNumber] let oltId, serialNumber; if (parts.length >= 6 && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: add_onu_olt_pedro_afonso_SERIAL oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" serialNumber = parts.slice(5).join('_'); // Serial pode ter underscores } else if (parts.length >= 5) { // Caso: add_onu_olt_dito_SERIAL ou outras OLTs de duas palavras oltId = parts[2] + '_' + parts[3]; // "olt_dito" serialNumber = parts.slice(4).join('_'); // Serial pode ter underscores } else { // Fallback - caso não siga o padrão esperado oltId = parts[2]; serialNumber = parts.slice(3).join('_'); } await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `🔍 **Analisando ONU ${serialNumber}**\n\n` + `🏢 OLT: ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n` + `📱 Serial: ${serialNumber}\n\n` + `⏳ Descobrindo slot e próximo ONU ID disponível...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.addOnuBySerial(oltId, serialNumber); if (result.success && result.readyForAddition) { // ONU analisada - mostrar informações e pedir confirmação const keyboard = { inline_keyboard: [ [ { text: '✅ CONFIRMAR ADIÇÃO', callback_data: `confirm_add_${oltId}_${result.slot.replace(/\//g, '-')}_${result.onuId}_${result.vlanId}_${serialNumber}` } ], [ { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.editMessageText( `✅ **CONFIRMAÇÃO DE ADIÇÃO**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Slot descoberto:** ${result.slot}\n` + `🔢 **ONU ID a usar:** ${result.onuId}\n` + `🏷️ **VLAN ID:** ${result.vlanId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `📋 **ONU IDs em uso no slot:** ${result.usedIds.join(', ')}\n\n` + `⚡ **Configurações que serão aplicadas:**\n` + `• Line-profile: DM986-100\n` + `• Ethernet: porta 1\n` + `• Native VLAN: ${result.vlanId}\n` + `• Service-port: novo (auto)\n\n` + `✅ **Confirme que deseja adicionar esta ONU:**`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: keyboard } ); } else { let errorMessage = `❌ **Não foi possível adicionar a ONU**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n`; if (result.message) { errorMessage += `❌ **Erro:** ${result.message}\n\n`; } if (result.message && result.message.includes('discovered-onus')) { errorMessage += `💡 **Dica:** Verifique se a ONU está ligada e descoberta pela OLT.\n` + `Use o comando: \`show interface gpon discovered-onus\``; } else if (result.message && result.message.includes('todos os ONU IDs')) { errorMessage += `💡 **Dica:** O slot está lotado. Considere usar outro slot ou remover ONUs inutilizadas.`; } else { errorMessage += `💡 Verifique a conectividade com a OLT ou se a ONU está descoberta.`; } await this.bot.editMessageText(errorMessage, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' }); } } catch (error) { console.error('Erro ao analisar ONU para adição:', error); await this.bot.editMessageText( `❌ **Erro interno**\n\n` + `Não foi possível analisar a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para iniciar processo de adição de ONU com VLAN customizada */ async handleAddOnuVlanCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; // Formato: add_onu_vlan_olt_dito_603_SERIAL ou add_onu_vlan_olt_pedro_afonso_603_SERIAL // Índices: [0]_[1]_[2]_[3]_[4]_[5]_[6] const parts = data.split('_'); let oltId, vlanId, serialNumber; if (parts.length >= 9 && parts[4] === 'pedro' && parts[5] === 'afonso') { // Caso: add_onu_vlan_olt_pedro_afonso_VLANID_SERIAL oltId = parts[3] + '_' + parts[4] + '_' + parts[5]; // "olt_pedro_afonso" vlanId = parts[6]; serialNumber = parts.slice(7).join('_'); // Serial pode ter underscores } else if (parts.length >= 7) { // Caso: add_onu_vlan_olt_dito_VLANID_SERIAL ou add_onu_vlan_olt_colmeia_VLANID_SERIAL oltId = parts[3] + '_' + parts[4]; // "olt_dito" ou "olt_colmeia" vlanId = parts[5]; serialNumber = parts.slice(6).join('_'); // Serial pode ter underscores } else { // Fallback oltId = parts[3]; vlanId = parts[4]; serialNumber = parts.slice(5).join('_'); } await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `🔍 **Analisando ONU ${serialNumber}**\n\n` + `🏢 OLT: ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n` + `📱 Serial: ${serialNumber}\n` + `🏷️ VLAN customizada: ${vlanId}\n\n` + `⏳ Descobrindo slot e próximo ONU ID disponível...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { // Usar a mesma função addOnuBySerial mas sem a vlanId (vamos usar a customizada) const result = await this.ssh.addOnuBySerial(oltId, serialNumber); if (result.success && result.readyForAddition) { // ONU analisada - mostrar informações e pedir confirmação const keyboard = { inline_keyboard: [ [ { text: '✅ CONFIRMAR ADIÇÃO', callback_data: `confirm_add_${oltId}_${result.slot.replace(/\//g, '-')}_${result.onuId}_${vlanId}_${serialNumber}` } ], [ { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.editMessageText( `✅ **CONFIRMAÇÃO DE ADIÇÃO COM VLAN CUSTOMIZADA**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Slot descoberto:** ${result.slot}\n` + `🔢 **ONU ID a usar:** ${result.onuId}\n` + `🏷️ **VLAN ID customizada:** ${vlanId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `📋 **ONU IDs em uso no slot:** ${result.usedIds.join(', ')}\n\n` + `⚡ **Configurações que serão aplicadas:**\n` + `• Line-profile: DM986-100\n` + `• Ethernet: porta 1\n` + `• Native VLAN: ${vlanId} (customizada)\n` + `• Service-port: novo (auto) com VLAN ${vlanId}\n\n` + `✅ **Confirme que deseja adicionar esta ONU:**`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: keyboard } ); } else { let errorMessage = `❌ **Não foi possível adicionar a ONU**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `🏷️ **VLAN:** ${vlanId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n`; if (result.message) { errorMessage += `❌ **Erro:** ${result.message}\n\n`; } if (result.message && result.message.includes('discovered-onus')) { errorMessage += `💡 **Dica:** Verifique se a ONU está ligada e descoberta pela OLT.\n` + `Use o comando: \`show interface gpon discovered-onus\``; } else if (result.message && result.message.includes('todos os ONU IDs')) { errorMessage += `💡 **Dica:** O slot está lotado. Considere usar outro slot ou remover ONUs inutilizadas.`; } else { errorMessage += `💡 Verifique a conectividade com a OLT ou se a ONU está descoberta.`; } await this.bot.editMessageText(errorMessage, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' }); } } catch (error) { console.error('Erro ao analisar ONU para adição com VLAN customizada:', error); await this.bot.editMessageText( `❌ **Erro interno**\n\n` + `Não foi possível analisar a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para tipo de ONU Fiberhome */ async handleFiberhomeOnuTypeCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const userId = callbackQuery.from.id; // Formato: fh_onu_type_AN5506-02-B ou fh_onu_type_AN5506-01-A const onuType = data.replace('fh_onu_type_', ''); // Recuperar sessão const session = this.userSessions.get(userId); if (!session || session.action !== 'adicionar_onu_fiberhome_step2') { await this.bot.answerCallbackQuery(callbackQuery.id, '❌ Sessão expirada'); await this.bot.sendMessage(chatId, '❌ Sessão expirada. Por favor, comece novamente.'); this.userSessions.delete(userId); return; } session.onuType = onuType; session.action = 'adicionar_onu_fiberhome_step3'; this.userSessions.set(userId, session); await this.bot.answerCallbackQuery(callbackQuery.id); // Perguntar sobre VLAN const keyboard = { inline_keyboard: [ [{ text: '✅ Usar VLAN 1002 (padrão)', callback_data: 'fh_vlan_1002' }], [{ text: '🔧 Usar outra VLAN', callback_data: 'fh_vlan_custom' }], [{ text: '❌ Cancelar', callback_data: 'cancel_action' }] ] }; await this.bot.editMessageText( `🔌 **Tipo da ONU:** ${onuType}\n\n` + `🏷️ **VLAN:**\n` + `A VLAN padrão é 1002. Deseja usar esta VLAN ou configurar outra?`, { chat_id: chatId, message_id: callbackQuery.message.message_id, reply_markup: keyboard, parse_mode: 'Markdown' } ); } /** * Handle callback para VLAN Fiberhome */ async handleFiberhomeVlanCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const userId = callbackQuery.from.id; // Recuperar sessão const session = this.userSessions.get(userId); if (!session || session.action !== 'adicionar_onu_fiberhome_step3') { await this.bot.answerCallbackQuery(callbackQuery.id, '❌ Sessão expirada'); await this.bot.sendMessage(chatId, '❌ Sessão expirada. Por favor, comece novamente.'); this.userSessions.delete(userId); return; } await this.bot.answerCallbackQuery(callbackQuery.id); if (data === 'fh_vlan_1002') { // Usar VLAN padrão 1002 session.vlan = 1002; // Limpar sessão this.userSessions.delete(userId); // Executar adição da ONU await this.executarAdicaoOnuFiberhome(chatId, session); } else if (data === 'fh_vlan_custom') { // Pedir VLAN customizada - manter sessão no step3 para receber input do usuário await this.bot.editMessageText( `🏷️ **VLAN Customizada**\n\n` + `Digite o número da VLAN que deseja usar (1-4094):`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Handle callback para confirmar adição de ONU */ async handleConfirmAddCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; console.log(`🔍 handleConfirmAddCallback chamado com data: ${data}`); // Parse do formato: confirm_add_olt_dito_1-1-3_0_603_DACM915E0F76 const parts = data.split('_'); console.log(`🔍 Parts: ${JSON.stringify(parts)}`); let oltId, slot, onuId, vlanId, serialNumber; if (parts.length >= 8 && parts[2] === 'olt' && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: confirm_add_olt_pedro_afonso_1-1-3_0_603_DACM915E0F76 oltId = parts[2] + '_' + parts[3] + '_' + parts[4]; // "olt_pedro_afonso" slot = parts[5].replace(/-/g, '/'); // Converter de volta: "1-1-3" -> "1/1/3" onuId = parts[6]; vlanId = parts[7]; serialNumber = parts.slice(8).join('_'); // Serial pode ter underscores } else if (parts.length >= 7) { // Caso padrão: confirm_add_olt_dito_1-1-3_0_603_DACM915E0F76 oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4].replace(/-/g, '/'); // Converter de volta: "1-1-3" -> "1/1/3" onuId = parts[5]; vlanId = parts[6]; serialNumber = parts.slice(7).join('_'); // Serial pode ter underscores } else if (parts.length >= 6) { // Fallback para OLTs com nome simples oltId = parts[2]; // "olt" slot = parts[3].replace(/-/g, '/'); // Converter de volta onuId = parts[4]; vlanId = parts[5]; serialNumber = parts.slice(6).join('_'); } else { console.error(`❌ Formato de callback inválido: ${data}`); await this.bot.answerCallbackQuery(callbackQuery.id, { text: 'Erro no formato do callback' }); return; } console.log(`🔍 Parsed - oltId: ${oltId}, slot: ${slot}, onuId: ${onuId}, vlanId: ${vlanId}, serialNumber: ${serialNumber}`); await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText( `➕ **Adicionando ONU...**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏷️ **VLAN:** ${vlanId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `⏳ Executando comandos de configuração...\n` + `⚡ Este processo pode levar até 90 segundos`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.addOnu(oltId, slot, onuId, serialNumber, parseInt(vlanId)); if (result.success) { const servicePortInfo = result.servicePortId ? `🔌 **Service-port:** ${result.servicePortId} (Auto-gerado)` : `🔌 **Service-port:** Criado automaticamente`; await this.bot.editMessageText( `✅ **ONU Adicionada com Sucesso!**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏷️ **VLAN:** ${vlanId}\n` + `${servicePortInfo}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `✅ **Configurações aplicadas:**\n` + `• Line-profile: DM986-100\n` + `• Serial number: ${serialNumber}\n` + `• Ethernet: porta 1\n` + `• Native VLAN: ${vlanId}\n` + `• Service-port: ${result.servicePortId || 'auto-gerado'} criado e configurado\n` + `• Configuração commitada\n\n` + `🎉 A ONU está pronta para uso!`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } else { await this.bot.editMessageText( `❌ **Erro na Adição**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🏷️ **VLAN:** ${vlanId}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').toUpperCase()}\n\n` + `❌ **Erro:** ${result.message}\n\n` + `🔄 Verifique a conectividade com a OLT ou tente novamente`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } catch (error) { console.error('Erro ao adicionar ONU:', error); await this.bot.editMessageText( `❌ **Erro Interno**\n\n` + `Não foi possível adicionar a ONU.\n` + `Erro: ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Painel administrativo */ async handleAdmin(chatId) { const userId = chatId; // Para chats individuais, chatId = userId // Verificar se é usuário autorizado if (!this.isAuthorized(userId)) { await this.bot.sendMessage(chatId, '🚫 Acesso negado ao painel administrativo.'); return; } await this.bot.sendMessage(chatId, `🛡️ **Painel Administrativo**\n\n` + `👥 **Gestão de Usuários:**\n` + `• \`usuarios autorizados\` - Ver lista de usuários\n` + `• \`meu id\` - Ver seu ID do Telegram\n\n` + `📊 **Sistema:**\n` + `• \`status\` - Status do bot\n` + `• \`equipamentos\` - Lista de equipamentos\n` + `• \`logs\` - Ver logs recentes\n` + `• \`limpar logs\` - Limpar logs do sistema\n\n` + `🔧 **Comandos disponíveis:**\n` + `• Ver sinal, adicionar ONU, remover ONU\n` + `• Resetar ONU, atualizar firmware\n` + `• Gerenciar usuários Juniper`, { parse_mode: 'Markdown' } ); } /** * Listar usuários autorizados */ async handleListAuthorizedUsers(chatId) { const userId = chatId; if (!this.isAuthorized(userId)) { await this.bot.sendMessage(chatId, '🚫 Acesso negado.'); return; } const authorizedUsers = config.bot.allowedUsers; if (authorizedUsers.length === 0) { await this.bot.sendMessage(chatId, `👥 **Usuários Autorizados**\n\n` + `⚠️ Nenhuma restrição configurada\n` + `🌐 Bot está aberto para todos os usuários\n\n` + `💡 Configure ALLOWED_USERS no .env para restringir` ); } else { const userList = authorizedUsers.map((id, index) => `${index + 1}. ID: \`${id}\`` ).join('\n'); await this.bot.sendMessage(chatId, `👥 **Usuários Autorizados**\n\n` + `📊 Total: ${authorizedUsers.length} usuário(s)\n\n` + `${userList}\n\n` + `🆔 Seu ID: \`${userId}\``, { parse_mode: 'Markdown' } ); } } /** * Exibir ID do usuário */ async handleMyId(chatId, userId) { await this.bot.sendMessage(chatId, `🆔 **Sua Identificação**\n\n` + `👤 User ID: \`${userId}\`\n` + `💬 Chat ID: \`${chatId}\`\n\n` + `📋 Use estes IDs para configurar permissões`, { parse_mode: 'Markdown' } ); } /** * Status do TopSapp */ async handleStatusTopSapp(chatId) { try { await this.bot.sendMessage(chatId, '🔄 Verificando status do TopSapp...'); const status = this.topsapp.getConfigStatus(); const instances = this.topsapp.getInstances(); let message = `🏢 **STATUS TOPSAPP**\n\n`; if (status.ready) { message += `✅ **Configuração**: OK\n`; message += `📊 **Instâncias configuradas**: ${instances.length}\n\n`; instances.forEach(instance => { message += `🌐 **${instance.location}**\n`; message += `└ ${instance.host}\n`; message += `└ Status: ${status.instances.find(i => i.location === instance.location)?.configured ? '✅ Configurado' : '❌ Não configurado'}\n\n`; }); message += `💡 Use os comandos:\n`; message += `• "buscar cliente João"\n`; message += `• "ver faturas 123.456.789-10"\n`; message += `• "gerar boleto fatura123"\n`; message += `• "bloquear cliente 456"`; } else { message += `❌ **Configuração**: Incompleta\n\n`; message += `⚠️ **Problemas encontrados**:\n`; status.instances.forEach(instance => { if (!instance.configured) { message += `• ${instance.location}: Credenciais não configuradas\n`; } }); message += `\n📝 **Configure no arquivo .env**:\n`; message += `• TOPSAPP_GUARAI_USER\n`; message += `• TOPSAPP_GUARAI_PASS\n`; message += `• TOPSAPP_PEDRO_AFONSO_USER\n`; message += `• TOPSAPP_PEDRO_AFONSO_PASS`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao verificar status TopSapp:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao verificar status TopSapp**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Buscar cliente no TopSapp */ async handleBuscarCliente(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `🔍 **Buscar Cliente**\n\n` + `💡 **Como usar:**\n` + `• "buscar cliente João da Silva"\n` + `• "buscar cliente 123.456.789-10"\n` + `• "buscar cliente Guarai João" (específica instância)\n` + `• "buscar cliente Pedro Afonso Maria"` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const resultado = await this.topsapp.buscarCliente(query, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } if (!resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"\n` + `🌐 Instância: ${resultado.instancia || 'Todas'}`, { parse_mode: 'Markdown' } ); return; } // Se encontrou mais de um cliente, mostrar lista para seleção if (resultado.clientes.length > 1) { let message = `🔍 **${resultado.clientes.length} clientes encontrados**\n\n`; message += `🌐 **Instância**: ${resultado.instancia}\n\n`; const keyboard = []; resultado.clientes.slice(0, 10).forEach((cliente, index) => { message += `👤 **${index + 1}. ${cliente.nome}**\n`; message += `🆔 ID: \`${cliente.id}\`\n`; if (cliente.cpf) { message += `📄 CPF: \`${cliente.cpf}\`\n`; } message += `📞 ${cliente.telefone || 'Telefone não informado'}\n`; message += `📊 Status: ${cliente.status === 1 ? '✅ Ativo' : '❌ Inativo'}\n\n`; // Adicionar botões para ações keyboard.push([ { text: `📋 Dados ${index + 1}`, callback_data: `dados_cliente_${cliente.id}_${location}` }, { text: `📄 Faturas ${index + 1}`, callback_data: `faturas_cliente_${cliente.id}_${location}` } ]); }); if (resultado.clientes.length > 10) { message += `📋 *Mostrando apenas os primeiros 10 resultados de ${resultado.clientes.length}*\n\n`; } message += `💡 **Selecione uma opção abaixo:**`; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); return; } // Se encontrou apenas um cliente, mostrar dados detalhados // Se encontrou apenas um cliente, mostrar dados detalhados const cliente = resultado.clientes[0]; let message = `👤 **Cliente Encontrado**\n\n`; message += `🆔 **ID**: \`${cliente.id}\`\n`; message += `👤 **Nome**: ${cliente.nome}\n`; if (cliente.cpf) { message += `📄 **CPF**: \`${cliente.cpf}\`\n`; } message += `📧 **Email**: ${cliente.email || 'Não informado'}\n`; message += `📞 **Telefone**: ${cliente.telefone || 'Não informado'}\n`; message += `📍 **Endereço**: ${cliente.endereco || 'Não informado'}\n`; message += `🏙️ **Cidade**: ${cliente.cidade || 'Não informado'}\n`; message += `📊 **Status**: ${cliente.status === 1 ? '✅ Ativo' : '❌ Inativo'}\n`; message += `🌐 **Instância**: ${resultado.instancia}\n\n`; // Botões de ação const keyboard = [ [ { text: '📋 Dados Cadastrais', callback_data: `dados_cliente_${cliente.id}_${location}` }, { text: '📄 Ver Faturas', callback_data: `faturas_cliente_${cliente.id}_${location}` } ], [ { text: '🔒 Bloquear', callback_data: `confirm_block_${cliente.id}_${location}` }, { text: '🔓 Liberar', callback_data: `confirm_unblock_${cliente.id}_${location}` } ] ]; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao buscar cliente:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar cliente**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Bloquear cliente */ async handleBloquearCliente(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `🔒 **Bloquear Cliente**\n\n` + `💡 **Como usar:**\n` + `• "bloquear cliente João da Silva"\n` + `• "bloquear cliente 123.456.789-10"\n` + `• "bloquear Guarai João" (específica instância)` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const resultado = await this.topsapp.buscarCliente(query, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } if (!resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"\n` + `🌐 Instância: ${resultado.instancia || 'Todas'}`, { parse_mode: 'Markdown' } ); return; } const cliente = resultado.clientes[0]; // Obter serviços do cliente para bloquear const servicosResult = await this.topsapp.obterServicos(cliente.id, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } const servicos = servicosResult.servicos || servicosResult.data?.dados || servicosResult.data?.servicos || []; const servicosAtivos = servicos.filter(s => { // id_status já vem normalizado da API return s.id_status === 1 || (s.status && s.status.toLowerCase() === 'liberado'); }); if (servicosAtivos.length === 0) { await this.bot.sendMessage(chatId, `⚠️ **Nenhum serviço ativo**\n\n` + `O cliente **${cliente.nome}** não possui serviços ativos para bloquear.`, { parse_mode: 'Markdown' } ); return; } let message = `🔒 **Bloquear Cliente**\n\n`; message += `👤 **Cliente**: ${cliente.nome}\n`; message += `🆔 **ID**: ${cliente.id}\n`; message += `📊 **Serviços ativos**: ${servicosAtivos.length}\n\n`; message += `⚠️ **Confirma o bloqueio de todos os serviços?**`; const keyboard = [ [ { text: '✅ Confirmar Bloqueio', callback_data: `confirm_block_all_${cliente.id}_${location}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ]; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao bloquear cliente:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao bloquear cliente**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Liberar cliente */ async handleLiberarCliente(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `🔓 **Liberar Cliente**\n\n` + `💡 **Como usar:**\n` + `• "liberar cliente João da Silva"\n` + `• "liberar cliente 123.456.789-10"\n` + `• "liberar Guarai João" (específica instância)` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const resultado = await this.topsapp.buscarCliente(query, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } if (!resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"\n` + `🌐 Instância: ${resultado.instancia || 'Todas'}`, { parse_mode: 'Markdown' } ); return; } const cliente = resultado.clientes[0]; // Obter serviços do cliente para liberar const servicosResult = await this.topsapp.obterServicos(cliente.id, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } const servicos = servicosResult.servicos || servicosResult.data?.dados || servicosResult.data?.servicos || []; // DEBUG: Verificar estrutura dos serviços console.log('🔍 DEBUG - Total de serviços recebidos:', servicos.length); if (servicos.length > 0) { console.log('🔍 DEBUG - Primeiro serviço:', JSON.stringify(servicos[0], null, 2)); } const servicosBloqueados = servicos.filter(s => { // id_status já vem normalizado da API const isBloqueado = s.id_status === 2 || (s.status && s.status.toLowerCase() === 'bloqueado'); console.log(`🔍 DEBUG - Serviço ${s.id_servico || s.id}: id_status=${s.id_status}, status="${s.status}", bloqueado=${isBloqueado}`); return isBloqueado; }); console.log('🔍 DEBUG - Serviços bloqueados encontrados:', servicosBloqueados.length); if (servicosBloqueados.length === 0) { await this.bot.sendMessage(chatId, `⚠️ **Nenhum serviço bloqueado**\n\n` + `O cliente **${cliente.nome}** não possui serviços bloqueados para liberar.`, { parse_mode: 'Markdown' } ); return; } let message = `🔓 **Liberar Cliente**\n\n`; message += `👤 **Cliente**: ${cliente.nome}\n`; message += `🆔 **ID**: ${cliente.id}\n`; message += `📊 **Serviços bloqueados**: ${servicosBloqueados.length}\n\n`; message += `⚠️ **Confirma a liberação de todos os serviços?**`; const keyboard = [ [ { text: '✅ Confirmar Liberação', callback_data: `confirm_unblock_all_${cliente.id}_${location}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ]; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao liberar cliente:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao liberar cliente**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Listar serviços do cliente */ async handleListarServicos(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `📋 **Listar Serviços**\n\n` + `💡 **Como usar:**\n` + `• "listar serviços João da Silva"\n` + `• "serviços do cliente 123.456.789-10"\n` + `• "serviços Guarai João" (específica instância)` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando serviços...'); // Detectar se especificou uma localização let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const resultado = await this.topsapp.buscarCliente(query, location); if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"`, { parse_mode: 'Markdown' } ); return; } const cliente = resultado.clientes[0]; // Obter serviços do cliente const servicosResult = await this.topsapp.obterServicos(cliente.id, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } const servicos = servicosResult.servicos || servicosResult.data?.dados || servicosResult.data?.servicos || []; if (servicos.length === 0) { await this.bot.sendMessage(chatId, `📋 **Nenhum serviço encontrado**\n\n` + `O cliente **${cliente.nome}** não possui serviços cadastrados.`, { parse_mode: 'Markdown' } ); return; } // Separar serviços por status (id_status já vem normalizado da API) const servicosAtivos = servicos.filter(s => { return s.id_status === 1 || (s.status && s.status.toLowerCase() === 'liberado'); }); const servicosBloqueados = servicos.filter(s => { return s.id_status === 2 || (s.status && s.status.toLowerCase() === 'bloqueado'); }); const servicosDesativados = servicos.filter(s => { return s.id_status === 3 || (s.status && s.status.toLowerCase() === 'desativado'); }); let message = `📋 **SERVIÇOS DO CLIENTE**\n\n`; message += `👤 **Cliente**: ${cliente.nome}\n`; message += `🆔 **ID**: ${cliente.id}\n\n`; // Exibir serviços ativos if (servicosAtivos.length > 0) { message += `✅ **SERVIÇOS ATIVOS** (${servicosAtivos.length})\n\n`; servicosAtivos.forEach((servico, index) => { message += `${index + 1}. 🟢 **${servico.descricao || servico.plano || 'Serviço'}**\n`; message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; if (servico.usuario) { message += ` 👤 Login: \`${servico.usuario}\`\n`; } if (servico.servico_valor || servico.valor) { const valor = parseFloat(servico.servico_valor || servico.valor); message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; } if (servico.vencimento) { message += ` 📅 Vencimento: dia ${servico.vencimento}\n`; } message += `\n`; }); } // Exibir serviços bloqueados if (servicosBloqueados.length > 0) { message += `\n🔒 **SERVIÇOS BLOQUEADOS** (${servicosBloqueados.length})\n\n`; servicosBloqueados.forEach((servico, index) => { message += `${index + 1}. 🔴 **${servico.descricao || servico.plano || 'Serviço'}**\n`; message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; if (servico.usuario) { message += ` 👤 Login: \`${servico.usuario}\`\n`; } if (servico.servico_valor || servico.valor) { const valor = parseFloat(servico.servico_valor || servico.valor); message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; } if (servico.data_bloqueio && servico.data_bloqueio !== '0000-00-00') { message += ` 🕒 Bloqueado em: ${servico.data_bloqueio}\n`; } message += `\n`; }); } // Exibir serviços desativados if (servicosDesativados.length > 0) { message += `\n❌ **SERVIÇOS DESATIVADOS** (${servicosDesativados.length})\n\n`; servicosDesativados.forEach((servico, index) => { message += `${index + 1}. ⚫ **${servico.descricao || servico.plano || 'Serviço'}**\n`; message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; if (servico.data_desativacao && servico.data_desativacao !== '0000-00-00') { message += ` 🕒 Desativado em: ${servico.data_desativacao}\n`; } message += `\n`; }); } // Botões de ação const keyboard = []; if (servicosAtivos.length > 0) { keyboard.push([ { text: '🔒 Bloquear Todos', callback_data: `confirm_block_all_${cliente.id}_${location}` } ]); } if (servicosBloqueados.length > 0) { keyboard.push([ { text: '🔓 Liberar Todos', callback_data: `confirm_unblock_all_${cliente.id}_${location}` } ]); } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: keyboard.length > 0 ? { inline_keyboard: keyboard } : undefined }); } catch (error) { console.error('❌ Erro ao listar serviços:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao listar serviços**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Ver faturas do cliente */ async handleVerFaturas(chatId, targetOrId, location = '') { try { let idCliente = targetOrId; // Se não for um número, buscar cliente primeiro if (isNaN(targetOrId)) { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); const resultado = await this.topsapp.buscarCliente(targetOrId, location); if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${targetOrId}"`, { parse_mode: 'Markdown' } ); return; } idCliente = resultado.clientes[0].id; location = location || resultado.instancia; } await this.bot.sendMessage(chatId, '📄 Buscando faturas...'); const resultado = await this.topsapp.obterFaturasDetalhadas(idCliente, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar faturas**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } const faturasPorServico = resultado.faturasPorServico || []; if (faturasPorServico.length === 0) { await this.bot.sendMessage(chatId, `📄 **Nenhuma fatura encontrada**\n\n` + `✅ O cliente não possui faturas cadastradas.`, { parse_mode: 'Markdown' } ); return; } // Contar total de faturas let totalFaturas = 0; faturasPorServico.forEach(s => { totalFaturas += s.faturas.length; }); let message = `📄 **FATURAS DO CLIENTE**\n\n`; message += `🆔 **ID Cliente**: ${idCliente}\n`; message += `📊 **Total de faturas**: ${totalFaturas}\n\n`; faturasPorServico.forEach((servicoInfo, sIndex) => { if (servicoInfo.faturas.length > 0) { message += `📋 **Serviço**: ${servicoInfo.servico.descricao || 'Serviço'}\n`; message += `🆔 **ID**: ${servicoInfo.servico.id}\n\n`; servicoInfo.faturas.forEach((fatura, fIndex) => { const isPago = fatura.status === 'Pago'; const statusEmoji = isPago ? '✅' : '🔴'; message += `${fIndex + 1}. ${statusEmoji} **Fatura #${fatura.id}**\n`; if (fatura.valor) { const valor = parseFloat(fatura.valor); message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; } if (fatura.vencimento) { message += ` 📅 Vencimento: ${fatura.vencimento}\n`; } message += ` 📊 Status: ${fatura.status}\n`; if (fatura.descricao) { message += ` 📝 Descrição: ${fatura.descricao}\n`; } if (fatura.periodo) { message += ` 📆 Período: ${fatura.periodo}\n`; } message += `\n`; }); message += `\n`; } }); // Criar botões para cada fatura com linha digitável/PIX const keyboard = []; faturasPorServico.forEach((servicoInfo) => { servicoInfo.faturas.forEach((fatura) => { if (fatura.status !== 'Pago') { const row = []; if (fatura.linhaDigitavel) { row.push({ text: `📋 Linha Digitável #${fatura.id}`, callback_data: `linha_${fatura.id}_${location}` }); } if (fatura.pixCopiaCola) { row.push({ text: `💳 PIX #${fatura.id}`, callback_data: `pix_${fatura.id}_${location}` }); } if (row.length > 0) { keyboard.push(row); } } }); }); await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: keyboard.length > 0 ? { inline_keyboard: keyboard } : undefined }); } catch (error) { console.error('❌ Erro ao buscar faturas:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar faturas**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Gerar boleto */ async handleGerarBoleto(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `💸 **Gerar Boleto**\n\n` + `💡 **Como usar:**\n` + `• "gerar boleto João da Silva"\n` + `• "boleto do cliente 123.456.789-10"` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const resultado = await this.topsapp.buscarCliente(query, location); if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"`, { parse_mode: 'Markdown' } ); return; } const cliente = resultado.clientes[0]; await this.handleVerFaturas(chatId, cliente.id, location); } catch (error) { console.error('❌ Erro ao gerar boleto:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao gerar boleto**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para confirmar bloqueio de todos os serviços */ async handleConfirmBlockAllCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('confirm_block_all_', '').split('_'); const idCliente = parts[0]; const location = parts.slice(1).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText('⏳ Bloqueando serviços...', { chat_id: chatId, message_id: callbackQuery.message.message_id }); // Obter serviços do cliente const servicosResult = await this.topsapp.obterServicos(idCliente, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } const servicos = servicosResult.servicos || servicosResult.data?.dados || servicosResult.data?.servicos || []; const servicosAtivos = servicos.filter(s => { // id_status já vem normalizado da API return s.id_status === 1 || (s.status && s.status.toLowerCase() === 'liberado'); }); let sucesso = 0; let falhas = 0; for (const servico of servicosAtivos) { const resultado = await this.topsapp.bloquearServico(servico.id_servico || servico.id, idCliente, location); if (resultado.success) { sucesso++; } else { falhas++; } } let message = `🔒 **Bloqueio Concluído**\n\n`; message += `✅ **Bloqueados**: ${sucesso}\n`; if (falhas > 0) { message += `❌ **Falhas**: ${falhas}\n`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao bloquear serviços:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao bloquear serviços**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para confirmar liberação de todos os serviços */ async handleConfirmUnblockAllCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('confirm_unblock_all_', '').split('_'); const idCliente = parts[0]; const location = parts.slice(1).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText('⏳ Liberando serviços...', { chat_id: chatId, message_id: callbackQuery.message.message_id }); // Obter serviços do cliente const servicosResult = await this.topsapp.obterServicos(idCliente, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } const servicos = servicosResult.servicos || servicosResult.data?.dados || servicosResult.data?.servicos || []; const servicosBloqueados = servicos.filter(s => { // id_status já vem normalizado da API return s.id_status === 2 || (s.status && s.status.toLowerCase() === 'bloqueado'); }); let sucesso = 0; let falhas = 0; for (const servico of servicosBloqueados) { const resultado = await this.topsapp.liberarServico(servico.id_servico || servico.id, idCliente, location); if (resultado.success) { sucesso++; } else { falhas++; } } let message = `🔓 **Liberação Concluída**\n\n`; message += `✅ **Liberados**: ${sucesso}\n`; if (falhas > 0) { message += `❌ **Falhas**: ${falhas}\n`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao liberar serviços:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao liberar serviços**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para confirmar bloqueio de cliente individual */ async handleConfirmBlockCallback(callbackQuery, data) { await this.handleConfirmBlockAllCallback(callbackQuery, data.replace('confirm_block_', 'confirm_block_all_')); } /** * Callback para confirmar liberação de cliente individual */ async handleConfirmUnblockCallback(callbackQuery, data) { await this.handleConfirmUnblockAllCallback(callbackQuery, data.replace('confirm_unblock_', 'confirm_unblock_all_')); } /** * Callback para confirmar bloqueio de serviço individual */ async handleConfirmBlockServiceCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; // Formato: confirm_block_service_idServico_idCliente_location const parts = data.replace('confirm_block_service_', '').split('_'); const idServico = parts[0]; const idCliente = parts[1]; const location = parts.slice(2).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText('⏳ Bloqueando serviço...', { chat_id: chatId, message_id: callbackQuery.message.message_id }); const resultado = await this.topsapp.bloquearServico(idServico, idCliente, location); if (resultado.success) { await this.bot.sendMessage(chatId, `✅ **Serviço bloqueado com sucesso!**\n\n` + `🆔 **ID do Serviço**: ${idServico}`, { parse_mode: 'Markdown' } ); } else { await this.bot.sendMessage(chatId, `❌ **Erro ao bloquear serviço**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); } } catch (error) { console.error('❌ Erro ao bloquear serviço:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao bloquear serviço**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para confirmar liberação de serviço individual */ async handleConfirmUnblockServiceCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; // Formato: confirm_unblock_service_idServico_idCliente_location const parts = data.replace('confirm_unblock_service_', '').split('_'); const idServico = parts[0]; const idCliente = parts[1]; const location = parts.slice(2).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.editMessageText('⏳ Liberando serviço...', { chat_id: chatId, message_id: callbackQuery.message.message_id }); const resultado = await this.topsapp.liberarServico(idServico, idCliente, location); if (resultado.success) { await this.bot.sendMessage(chatId, `✅ **Serviço liberado com sucesso!**\n\n` + `🆔 **ID do Serviço**: ${idServico}`, { parse_mode: 'Markdown' } ); } else { await this.bot.sendMessage(chatId, `❌ **Erro ao liberar serviço**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); } } catch (error) { console.error('❌ Erro ao liberar serviço:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao liberar serviço**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para dados do cliente */ async handleDadosClienteCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('dados_cliente_', '').split('_'); const idCliente = parts[0]; const location = parts.slice(1).join('_'); await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleDadosCliente(chatId, idCliente, location); } /** * Callback para faturas do cliente */ async handleFaturasClienteCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('faturas_cliente_', '').split('_'); const idCliente = parts[0]; const location = parts.slice(1).join('_'); await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleVerFaturas(chatId, idCliente, location); } /** * Callback para serviços do cliente */ async handleServicosClienteCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('servicos_cliente_', '').split('_'); const idCliente = parts[0]; const location = parts.slice(1).join('_'); await this.bot.answerCallbackQuery(callbackQuery.id); await this.handleListarServicos(chatId, idCliente); } /** * Callback para exibir linha digitável */ async handleLinhaDigitavelCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('linha_', '').split('_'); const idFatura = parts[0]; const location = parts.slice(1).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.sendMessage(chatId, '⏳ Buscando linha digitável...'); // Buscar dados da fatura const resultado = await this.topsapp.obterDadosFatura(idFatura, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar linha digitável**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } const dadosFatura = resultado.data?.fatura || resultado.data?.dados || resultado.data; if (!dadosFatura || !dadosFatura.linha_digitavel) { await this.bot.sendMessage(chatId, `⚠️ **Linha digitável não disponível**\n\n` + `A fatura #${idFatura} não possui linha digitável.`, { parse_mode: 'Markdown' } ); return; } let message = `📋 **LINHA DIGITÁVEL**\n\n`; message += `🆔 **Fatura**: #${idFatura}\n`; message += `💰 **Valor**: R$ ${parseFloat(dadosFatura.valor || dadosFatura.valor_atualizado || 0).toFixed(2)}\n`; message += `📅 **Vencimento**: ${dadosFatura.vencimento || dadosFatura.vencimento_atualizado || 'N/A'}\n\n`; message += `📋 **Linha Digitável**:\n\`${dadosFatura.linha_digitavel}\`\n\n`; message += `💡 _Copie a linha digitável acima para pagar pelo app do banco_`; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao buscar linha digitável:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar linha digitável**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para exibir PIX copia e cola */ async handlePixCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; const parts = data.replace('pix_', '').split('_'); const idFatura = parts[0]; const location = parts.slice(1).join('_'); try { await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.sendMessage(chatId, '⏳ Buscando código PIX...'); // Buscar dados da fatura const resultado = await this.topsapp.obterDadosFatura(idFatura, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar código PIX**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } const dadosFatura = resultado.data?.fatura || resultado.data?.dados || resultado.data; const pixCode = dadosFatura?.qrcode || dadosFatura?.pix_copia_cola || dadosFatura?.codigo_pix; if (!dadosFatura || !pixCode) { await this.bot.sendMessage(chatId, `⚠️ **PIX não disponível**\n\n` + `A fatura #${idFatura} não possui código PIX.`, { parse_mode: 'Markdown' } ); return; } let message = `💳 **PIX COPIA E COLA**\n\n`; message += `🆔 **Fatura**: #${idFatura}\n`; message += `💰 **Valor**: R$ ${parseFloat(dadosFatura.valor || dadosFatura.valor_atualizado || 0).toFixed(2)}\n`; message += `📅 **Vencimento**: ${dadosFatura.vencimento || dadosFatura.vencimento_atualizado || 'N/A'}\n\n`; message += `💳 **PIX Copia e Cola**:\n\`${pixCode}\`\n\n`; message += `💡 _Copie o código acima e cole no app do banco na opção PIX Copia e Cola_`; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao buscar código PIX:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar código PIX**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Escapar caracteres problemáticos para Markdown do Telegram */ escapeMarkdown(text) { if (!text) return text; // Escapar caracteres especiais que podem causar problemas no Markdown return text.toString() .replace(/\\/g, '\\\\') // Escape backslash .replace(/\*/g, '\\*') // Escape asterisco .replace(/_/g, '\\_') // Escape underscore .replace(/\[/g, '\\[') // Escape colchete .replace(/\]/g, '\\]') // Escape colchete .replace(/\(/g, '\\(') // Escape parêntese .replace(/\)/g, '\\)') // Escape parêntese .replace(/~/g, '\\~') // Escape til .replace(/>/g, '\\>') // Escape maior que .replace(/#/g, '\\#') // Escape hashtag .replace(/\+/g, '\\+') // Escape mais .replace(/-/g, '\\-') // Escape hífen .replace(/=/g, '\\=') // Escape igual .replace(/\|/g, '\\|') // Escape pipe .replace(/\{/g, '\\{') // Escape chaves .replace(/\}/g, '\\}') // Escape chaves .replace(/\./g, '\\.') // Escape ponto .replace(/!/g, '\\!') // Escape exclamação .replace(/@/g, '\\@'); // Escape arroba } /** * Mostrar dados cadastrais completos do cliente */ async handleDadosCliente(chatId, idCliente, location = '') { try { await this.bot.sendMessage(chatId, '📋 Carregando dados cadastrais...'); const resultado = await this.topsapp.obterDadosCadastrais(idCliente, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ Erro ao buscar dados\n\n` + `⚠️ ${resultado.error}` ); return; } const cliente = resultado.cliente; const servicos = resultado.servicos || []; let message = `📋 Dados Cadastrais Completos\n\n`; // Dados do cliente message += `👤 CLIENTE\n`; message += `🆔 ID: ${cliente.id}\n`; message += `👤 Nome: ${cliente.nome || 'Não informado'}\n`; if (cliente.cpf) { message += `📄 CPF: \`${cliente.cpf}\`\n`; } if (cliente.rg) { message += `📄 RG: ${cliente.rg}\n`; } message += `📧 Email: ${cliente.email || 'Não informado'}\n`; if (cliente.telefone && cliente.telefone !== 'Não informado') { message += `📞 Telefone: \`${cliente.telefone}\`\n`; } else { message += `📞 Telefone: Não informado\n`; } if (cliente.celular && cliente.celular !== 'Não informado') { message += `📱 Celular: \`${cliente.celular}\`\n`; } else { message += `📱 Celular: Não informado\n`; } // Endereço if (cliente.endereco) { message += `📍 Endereço: ${cliente.endereco}`; if (cliente.numero) { message += `, ${cliente.numero}`; } message += `\n`; } if (cliente.complemento) { message += `📝 Complemento: ${cliente.complemento}\n`; } message += `🏘️ Bairro: ${cliente.bairro || 'Não informado'}\n`; message += `🏙️ Cidade: ${cliente.cidade || 'Não informado'}`; if (cliente.uf) { message += ` - ${cliente.uf}`; } message += `\n`; message += `📮 CEP: ${cliente.cep || 'Não informado'}\n`; message += `📊 Status Geral: ${cliente.status === 1 ? '✅ Ativo' : '❌ Inativo'}\n`; if (cliente.data_cadastro) { message += `📅 Cadastro: ${this.formatarData(cliente.data_cadastro)}\n`; } if (cliente.observacoes) { message += `📝 Observações: ${cliente.observacoes}\n`; } message += `\n🌐 Instância: ${resultado.instancia}\n\n`; // Dados dos serviços if (servicos.length > 0) { message += `⚙️ **SERVIÇOS CONTRATADOS**\n\n`; servicos.forEach((servico, index) => { message += `📦 Serviço ${index + 1}\n`; message += `🏷️ Plano: ${servico.planoNome}\n`; message += `📊 Status: ${servico.statusFormatado}\n`; if (servico.login) { message += `👤 Login: ${servico.login}\n`; } if (servico.senha) { message += `🔑 Senha: ${servico.senha}\n`; } if (servico.ip) { message += `🌐 IP: ${servico.ip}\n`; } if (servico.vencimento) { message += `📅 Vencimento: Dia ${servico.vencimento}\n`; } if (servico.valor) { message += `💰 Valor: R$ ${parseFloat(servico.valor).toFixed(2)}\n`; } message += `🔧 Tecnologia: ${servico.tecnologia}\n`; if (servico.velocidade_down) { message += `⬇️ Download: ${servico.velocidade_down} Mbps\n`; } if (servico.velocidade_up) { message += `⬆️ Upload: ${servico.velocidade_up} Mbps\n`; } // Dados FTTH - usando campos reais da API if (servico.tecnologia === 'FTTH' || servico.pon || servico.serial) { message += `\n📡 Dados FTTH:\n`; if (servico.base || servico.id_ap) { message += `🔌 Base/AP: ${servico.base || `AP ${servico.id_ap}`}\n`; } if (servico.pon) { message += `🔗 PON: ${servico.pon}\n`; } if (servico.perfil) { message += `⚙️ Perfil: ${servico.perfil}\n`; } if (servico.serial) { message += `📟 Serial ONU: \`${servico.serial}\`\n`; } if (servico.serviceport) { message += `🔌 Service Port: ${servico.serviceport}\n`; } if (servico.ultimo_rx_power) { message += `📶 Último RX: ${servico.ultimo_rx_power} dBm\n`; } if (servico.ultimo_tx_power) { message += `📡 Último TX: ${servico.ultimo_tx_power} dBm\n`; } if (servico.data_ultimo_sinal) { message += `📅 Último sinal: ${this.formatarData(servico.data_ultimo_sinal)} ${servico.hora_ultimo_sinal || ''}\n`; } if (servico.conexao) { const statusConexao = servico.conexao === 'online' ? '🟢 Online' : '🔴 Offline'; message += `🔌 Conexão: ${statusConexao}\n`; } if (servico.ultimo_acesso) { message += `🕐 Último acesso: ${servico.ultimo_acesso}\n`; } if (servico.ultimo_ip) { message += `🌐 Último IP: ${servico.ultimo_ip}\n`; } } // Dados de instalação if (servico.instalacao_endereco) { message += `\n📍 Endereço de Instalação:\n`; message += `🏠 ${servico.instalacao_endereco}, ${servico.instalacao_numero || 'S/N'}\n`; if (servico.instalacao_complemento) { message += `📝 Complemento: ${servico.instalacao_complemento}\n`; } message += `🏘️ ${servico.instalacao_bairro}, ${servico.instalacao_cidade}/${servico.instalacao_uf}\n`; if (servico.instalacao_cep) { message += `📮 CEP: ${servico.instalacao_cep}\n`; } if (servico.latitude && servico.longitude) { message += `🗺️ Coordenadas: ${servico.latitude}, ${servico.longitude}\n`; } } message += `\n`; }); } else { message += `⚙️ SERVIÇOS: Nenhum serviço encontrado\n\n`; } // Botões de ação const keyboard = [ [ { text: '📄 Ver Faturas', callback_data: `faturas_cliente_${idCliente}_${location}` }, { text: '⚙️ Listar Serviços', callback_data: `servicos_cliente_${idCliente}_${location}` } ], [ { text: '🔒 Bloquear Cliente', callback_data: `confirm_block_${idCliente}_${location}` }, { text: '🔓 Liberar Cliente', callback_data: `confirm_unblock_${idCliente}_${location}` } ] ]; // Adicionar botão para gerar PDF da primeira fatura (se houver) if (resultado.faturasPorServico && resultado.faturasPorServico.length > 0 && resultado.faturasPorServico[0].faturas && resultado.faturasPorServico[0].faturas.length > 0) { const primeiraFatura = resultado.faturasPorServico[0].faturas[0]; keyboard.push([ { text: '📄 Gerar Boleto PDF', callback_data: `boleto_pdf_${primeiraFatura.id}_${location}` } ]); } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao buscar faturas detalhadas:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar faturas**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Buscar e mostrar dados cadastrais do cliente */ async handleDadosClientePorBusca(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `📋 **Dados Cadastrais**\n\n` + `💡 **Como usar:**\n` + `• "dados cliente João da Silva"\n` + `• "dados cadastrais 123.456.789-10"\n` + `• "dados cliente 12345" (ID do cliente)\n` + `• "dados cliente Guarai João" (específica instância)` ); return; } try { // Primeiro, buscar o cliente let location = ''; let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; query = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; query = target.replace(/pedro afonso/gi, '').trim(); } const busca = await this.topsapp.buscarCliente(query, location); if (!busca.success) { await this.bot.sendMessage(chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${busca.error}`, { parse_mode: 'Markdown' } ); return; } if (!busca.clientes || busca.clientes.length === 0) { await this.bot.sendMessage(chatId, `🔍 **Nenhum cliente encontrado**\n\n` + `📝 Termo buscado: "${query}"\n` + `🌐 Instância: ${busca.instancia || 'Todas'}`, { parse_mode: 'Markdown' } ); return; } // Se encontrou mais de um, mostrar lista if (busca.clientes.length > 1) { let message = `📋 **${busca.clientes.length} clientes encontrados**\n\n`; message += `🌐 **Instância**: ${busca.instancia}\n\n`; message += `💡 **Selecione um cliente para ver os dados cadastrais:**\n\n`; const keyboard = []; busca.clientes.slice(0, 10).forEach((cliente, index) => { message += `👤 **${index + 1}. ${cliente.nome}**\n`; message += `🆔 ID: \`${cliente.id}\`\n`; if (cliente.cpf) { message += `📄 CPF: \`${cliente.cpf}\`\n`; } message += `📊 Status: ${cliente.status === 1 ? '✅ Ativo' : '❌ Inativo'}\n\n`; keyboard.push([ { text: `📋 Ver Dados ${index + 1}`, callback_data: `dados_cliente_${cliente.id}_${location}` } ]); }); await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); return; } // Se encontrou apenas um, mostrar dados diretamente const cliente = busca.clientes[0]; await this.handleDadosCliente(chatId, cliente.id, location || busca.instancia); } catch (error) { console.error('❌ Erro ao buscar dados do cliente:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar dados do cliente**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Formatardata para DD/MM/YYYY */ formatarData(data) { if (!data) return 'Data não informada'; // Se já está no formato DD/MM/YYYY, retornar como está if (data.match(/^\d{2}\/\d{2}\/\d{4}$/)) { return data; } // Se está no formato YYYY-MM-DD, converter if (data.match(/^\d{4}-\d{2}-\d{2}$/)) { const [ano, mes, dia] = data.split('-'); return `${dia}/${mes}/${ano}`; } // Tentar converter outros formatos try { const dataObj = new Date(data); if (!isNaN(dataObj.getTime())) { const dia = String(dataObj.getDate()).padStart(2, '0'); const mes = String(dataObj.getMonth() + 1).padStart(2, '0'); const ano = dataObj.getFullYear(); return `${dia}/${mes}/${ano}`; } } catch (error) { console.warn('Erro ao formatar data:', data, error); } // Se não conseguiu formatar, retornar original return data; } /** * Verificar status de ONU Fiberhome */ async handleStatusONU(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `📡 **Status ONU Fiberhome**\n\n` + `💡 **Como usar:**\n` + `• "status onu FHTT12345678"\n` + `• "verificar onu FHTT12345678"\n` + `• "onu status FHTT12345678"\n\n` + `📝 **Nota:** Informe o serial da ONU (12 caracteres)`, { parse_mode: 'Markdown' } ); return; } try { await this.bot.sendMessage(chatId, '📡 Conectando à OLT Fiberhome...'); logger.botCommand(chatId, 'status_onu', { target }); // Verificar se é serial ou device name let resultado; if (target.length === 12 && /^[A-Z0-9]+$/.test(target)) { // É um serial resultado = await this.fiberhome.obterStatusONU(target); } else { // Pode ser device name resultado = await this.fiberhome.obterStatusONUPorNome(target); } if (resultado.success) { // Formatar informações da ONU let message = `📡 **Status ONU Fiberhome**\n\n`; message += `🆔 **Serial:** ${resultado.serial}\n`; message += `📋 **Device Name:** ${resultado.deviceName}\n`; message += `📍 **Posição:** Slot ${resultado.slot}, PON ${resultado.pon}, ONU ${resultado.onuId}\n`; message += `🔌 **Status:** ${resultado.online ? '🟢 Online' : '🔴 Offline'}\n\n`; // Informações de sinal message += `📶 **Informações de Sinal:**\n`; message += `📡 **RX Power:** ${resultado.rxPower}\n`; message += `📤 **TX Power:** ${resultado.txPower}\n`; message += `📏 **Distância:** ${resultado.distance}\n\n`; // Status das portas Ethernet (resumido) if (resultado.portasEthernet.length > 0) { message += `🔗 **Portas Ethernet:**\n`; resultado.portasEthernet.forEach(porta => { const statusIcon = porta.status === 'up' ? '🟢' : '🔴'; message += ` ${statusIcon} **Porta ${porta.porta}:** ${porta.status}\n`; }); message += '\n'; } // Serviços WAN if (resultado.servicosWAN.length > 0) { message += `🌐 **Serviços WAN:**\n`; resultado.servicosWAN.forEach(servico => { const statusIcon = servico.status === 'active' ? '🟢' : '🔴'; message += ` ${statusIcon} **Serviço ${servico.id}:** ${servico.status}\n`; if (servico.ip !== 'N/A') { message += ` 📍 **IP:** ${servico.ip}\n`; } }); } else { message += `🌐 **Serviços WAN:** Nenhum configurado\n`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } else { await this.bot.sendMessage(chatId, `❌ ONU ${target} não encontrada na OLT Fiberhome.`); // Oferecer pesquisar em OLTs Datacom const keyboard = { inline_keyboard: [ [ { text: '🔍 Pesquisar em OLT Datacom', callback_data: `ver_sinal_${target}` }, { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.sendMessage(chatId, 'ONU não encontrada na Fiberhome. Deseja pesquisar nas OLTs Datacom?', { reply_markup: keyboard }); } } catch (error) { logger.botError(chatId, error, 'handleStatusONU'); // Escapar caracteres especiais para evitar problemas no Telegram const errorMsg = error.message ? error.message.replace(/[_*\[\]()~`>#+=|{}.!-]/g, '\\$&') : 'Erro desconhecido'; await this.bot.sendMessage(chatId, `❌ **Erro ao verificar status da ONU**\n\n` + `⚠️ ${errorMsg}`, { parse_mode: 'Markdown' } ); } } /** * Verificar status das conexões Fiberhome */ async handleFiberhomeStatus(chatId) { try { await this.bot.sendMessage(chatId, '🔌 Diagnosticando conexões Fiberhome...'); logger.botCommand(chatId, 'fiberhome_status'); // Fazer diagnóstico completo const diagnostico = await this.fiberhome.diagnosticarConectividade(); let message = `🔌 **Diagnóstico Fiberhome**\n\n`; // Status OLT message += `📡 **OLT (${diagnostico.olt.host}:${diagnostico.olt.port}):**\n`; message += ` 🌐 **Conectividade:** ${diagnostico.olt.conectividade ? '🟢 OK' : '🔴 Falhou'}\n`; message += ` � **Telnet:** ${diagnostico.olt.telnet ? '🟢 Conectado' : '🔴 Falhou'}\n`; if (diagnostico.olt.erro) { message += ` ⚠️ **Erro:** ${diagnostico.olt.erro}\n`; } message += '\n'; // Status UNM2000 message += `�️ **UNM2000 (${diagnostico.unm2000.host}:${diagnostico.unm2000.port}):**\n`; message += ` 🌐 **Conectividade:** ${diagnostico.unm2000.conectividade ? '🟢 OK' : '🔴 Falhou'}\n`; message += ` � **TL1:** ${diagnostico.unm2000.tl1 ? '🟢 Conectado' : '🔴 Falhou'}\n`; if (diagnostico.unm2000.erro) { message += ` ⚠️ **Erro:** ${diagnostico.unm2000.erro}\n`; } message += '\n'; // Configurações message += `⚙️ **Configurações:**\n`; message += ` 📡 **OLT:** GEPON/GEPON\n`; message += ` 🖥️ **UNM2000:** 1/1\n\n`; // Comandos disponíveis message += `💡 **Comandos disponíveis:**\n`; message += `• \`status onu FHTT12345678\` - Status de ONU específica\n`; message += `• \`verificar onu FHTT12345678\` - Verificar ONU\n`; message += `• \`onu status DeviceName\` - Status por nome do dispositivo\n\n`; // Status geral const oltOk = diagnostico.olt.conectividade && diagnostico.olt.telnet; const unm2000Ok = diagnostico.unm2000.conectividade && diagnostico.unm2000.tl1; if (oltOk && unm2000Ok) { message += `✅ **Status:** Sistema funcionando normalmente`; } else if (oltOk || unm2000Ok) { message += `⚠️ **Status:** Funcionamento parcial`; } else { message += `❌ **Status:** Sistema indisponível`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } catch (error) { logger.botError(chatId, error, 'handleFiberhomeStatus'); await this.bot.sendMessage(chatId, `❌ **Erro ao diagnosticar Fiberhome**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Iniciar processo interativo de abertura de suporte */ async iniciarProcessoSuporte(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; } // Criar sessão de suporte const sessionId = `${userId}_${chatId}_${Date.now()}`; const suporteSession = { id: sessionId, chatId, userId, etapa: 'buscar_cliente', dados: {}, iniciado: new Date() }; this.suporteSessions.set(sessionId, suporteSession); await this.bot.sendMessage(chatId, `🎫 **Abertura de Suporte**\n\n` + `🔍 **Etapa 1:** Identificar Cliente\n\n` + `Por favor, digite o **nome** ou **CPF** do cliente para quem deseja abrir o suporte:`, { parse_mode: 'Markdown' } ); // Se foi fornecido um target, usar como busca inicial if (target && target.trim()) { await this.processarEtapaSuporteBuscarCliente(sessionId, target.trim()); } } /** * Processar mensagem dentro do fluxo de suporte */ async processarMensagemSuporte(msg) { const chatId = msg.chat.id; const userId = msg.from.id; const text = msg.text; // Encontrar sessão ativa para este usuário const sessaoAtiva = Array.from(this.suporteSessions.values()).find( session => session.chatId === chatId && session.userId === userId ); if (!sessaoAtiva) { return false; // Não há sessão ativa } try { switch (sessaoAtiva.etapa) { case 'buscar_cliente': await this.processarEtapaSuporteBuscarCliente(sessaoAtiva.id, text); break; case 'selecionar_cliente': await this.processarEtapaSelecionarCliente(sessaoAtiva.id, text); break; case 'selecionar_servico': await this.processarEtapaSelecionarServico(sessaoAtiva.id, text); break; case 'selecionar_categoria': await this.processarEtapaSelecionarCategoria(sessaoAtiva.id, text); break; case 'selecionar_tecnico': await this.processarEtapaSelecionarTecnico(sessaoAtiva.id, text); break; case 'definir_data_hora': await this.processarEtapaDefinirDataHora(sessaoAtiva.id, text); break; case 'adicionar_descricao': await this.processarEtapaAdicionarDescricao(sessaoAtiva.id, text); break; case 'confirmar': await this.finalizarCriacaoSuporte(sessaoAtiva.id, text); break; default: await this.bot.sendMessage(chatId, '❌ Etapa desconhecida. Reiniciando processo...'); this.suporteSessions.delete(sessaoAtiva.id); break; } return true; // Mensagem foi processada pelo fluxo de suporte } catch (error) { logger.error('Erro no processamento de suporte:', error); await this.bot.sendMessage(chatId, `❌ **Erro no processamento**\n\n` + `⚠️ ${error.message}\n\n` + `🔄 Reiniciando processo de suporte...` ); this.suporteSessions.delete(sessaoAtiva.id); return true; } } /** * Etapa 1: Buscar cliente */ async processarEtapaSuporteBuscarCliente(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) { await this.bot.sendMessage(session.chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${resultado.error}\n\n` + `Por favor, tente novamente com outro nome ou CPF:` ); return; } if (!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) { // Apenas um cliente encontrado, selecionar automaticamente session.dados.clienteSelecionado = resultado.clientes[0]; session.etapa = 'selecionar_servico'; await this.processarEtapaSelecionarServico(sessionId); } else { // Múltiplos clientes, deixar usuário escolher session.etapa = 'selecionar_cliente'; await this.mostrarOpcoesClientes(session); } } catch (error) { await this.bot.sendMessage(session.chatId, `❌ **Erro na busca**\n\n` + `⚠️ ${error.message}\n\n` + `Por favor, tente novamente:` ); } } /** * Mostrar opções de clientes encontrados */ async mostrarOpcoesClientes(session) { let message = `👥 **Clientes encontrados:**\n\n`; const buttons = []; session.dados.clientesEncontrados.forEach((cliente, index) => { const status = this.topsapp.interpretarStatusCliente ? this.topsapp.interpretarStatusCliente(cliente) : cliente.status; const statusEmoji = status === 1 ? '🟢' : '🔴'; message += `**${index + 1}.** ${statusEmoji} ${cliente.nome}\n`; message += ` 📄 CPF: ${cliente.cpf || 'N/A'}\n`; message += ` 🆔 ID: ${cliente.id}\n\n`; // Adicionar botão para seleção direta buttons.push([{ text: `${index + 1}. ${cliente.nome}`, callback_data: `suporte_cliente_${index}` }]); }); message += `Selecione o cliente clicando no botão ou digite o **número** (1-${session.dados.clientesEncontrados.length}):`; const keyboard = { inline_keyboard: buttons }; await this.bot.sendMessage(session.chatId, message, { parse_mode: 'Markdown', reply_markup: keyboard }); } /** * Etapa 2: Selecionar cliente específico */ async processarEtapaSelecionarCliente(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**\n\n` + `Por favor, digite um número entre 1 e ${session.dados.clientesEncontrados.length}:` ); return; } session.dados.clienteSelecionado = session.dados.clientesEncontrados[numero - 1]; session.etapa = 'selecionar_servico'; await this.processarEtapaSelecionarServico(sessionId); } /** * Etapa 3: Selecionar serviço */ async processarEtapaSelecionarServico(sessionId, input = null) { const session = this.suporteSessions.get(sessionId); if (!session) return; const cliente = session.dados.clienteSelecionado; if (!input) { // Primeira vez nesta etapa - buscar serviços await this.bot.sendMessage(session.chatId, `🔍 Buscando serviços do cliente **${cliente.nome}**...`, { parse_mode: 'Markdown' } ); try { const resultado = await this.topsapp.obterServicosCliente(cliente.id); console.log(`🔍 Handler - Resultado completo:`, { success: resultado.success, servicosCount: resultado.servicos?.length, total: resultado.total, debug: resultado.debug }); if (!resultado.success) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${resultado.error}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } if (!resultado.servicos || resultado.servicos.length === 0) { await this.bot.sendMessage(session.chatId, `❌ **Nenhum serviço encontrado**\n\n` + `O cliente não possui serviços cadastrados.\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } session.dados.servicos = resultado.servicos; await this.mostrarOpcoesServicos(session); } catch (error) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${error.message}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); } } else { // Usuário selecionou um serviço const numero = parseInt(input.trim()); if (isNaN(numero) || numero < 1 || numero > session.dados.servicos.length) { await this.bot.sendMessage(session.chatId, `❌ **Número inválido**\n\n` + `Por favor, digite um número entre 1 e ${session.dados.servicos.length}:` ); return; } session.dados.servicoSelecionado = session.dados.servicos[numero - 1]; session.etapa = 'selecionar_categoria'; await this.processarEtapaSelecionarCategoria(sessionId); } } /** * Mostrar opções de serviços */ async mostrarOpcoesServicos(session) { let message = `📋 **Serviços disponíveis:**\n\n`; const buttons = []; session.dados.servicos.forEach((servico, index) => { const statusEmoji = servico.statusNumerico === 1 ? '🟢' : '🔴'; message += `**${index + 1}.** ${statusEmoji} ${servico.nome}\n`; message += ` 🆔 ID: ${servico.id}\n`; if (servico.valorMensal) message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; message += `\n`; // Adicionar botão para seleção direta buttons.push([{ text: `${index + 1}. ${servico.nome}`, callback_data: `suporte_servico_${index}` }]); }); message += `Selecione o serviço clicando no botão ou digite o **número** (1-${session.dados.servicos.length}):`; const keyboard = { inline_keyboard: buttons }; await this.bot.sendMessage(session.chatId, message, { parse_mode: 'Markdown', reply_markup: keyboard }); } /** * Etapa 4: Selecionar categoria */ async processarEtapaSelecionarCategoria(sessionId, input = null) { const session = this.suporteSessions.get(sessionId); if (!session) return; if (!input) { // Primeira vez nesta etapa - buscar categorias await this.bot.sendMessage(session.chatId, `🔍 Buscando categorias de suporte...`); try { const resultado = await this.topsapp.obterSuporteCategorias(); console.log(`🔍 Handler - Resultado categorias:`, { success: resultado.success, dataType: typeof resultado.data, dataLength: resultado.data?.dados?.length, error: resultado.error }); if (!resultado.success) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar categorias**\n\n` + `⚠️ ${resultado.error}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } if (!resultado.data?.dados || resultado.data.dados.length === 0) { await this.bot.sendMessage(session.chatId, `❌ **Nenhuma categoria encontrada**\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } session.dados.categorias = resultado.data.dados; await this.mostrarOpcoesCategorias(session); } catch (error) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar categorias**\n\n` + `⚠️ ${error.message}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); } } else { // Usuário selecionou uma categoria const numero = parseInt(input.trim()); if (isNaN(numero) || numero < 1 || numero > session.dados.categorias.length) { await this.bot.sendMessage(session.chatId, `❌ **Número inválido**\n\n` + `Por favor, digite um número entre 1 e ${session.dados.categorias.length}:` ); return; } session.dados.categoriaSelecionada = session.dados.categorias[numero - 1]; session.etapa = 'selecionar_tecnico'; await this.processarEtapaSelecionarTecnico(sessionId); } } /** * Mostrar opções de categorias */ async mostrarOpcoesCategorias(session) { let message = `📁 **Categorias de suporte:**\n\n`; // Verificar se categorias é um array válido if (!Array.isArray(session.dados.categorias)) { console.error(`❌ session.dados.categorias não é um array:`, { type: typeof session.dados.categorias, value: session.dados.categorias }); throw new Error(`Dados de categorias inválidos: ${typeof session.dados.categorias}`); } session.dados.categorias.forEach((categoria, index) => { message += `**${index + 1}.** ${categoria.descricao}\n`; }); message += `\nDigite o **número** da categoria do suporte (1-${session.dados.categorias.length}):`; await this.bot.sendMessage(session.chatId, message, { parse_mode: 'Markdown' }); } /** * Etapa 5: Selecionar técnico */ async processarEtapaSelecionarTecnico(sessionId, input = null) { const session = this.suporteSessions.get(sessionId); if (!session) return; if (!input) { // Primeira vez nesta etapa - buscar tipos de atendimento await this.bot.sendMessage(session.chatId, `🔍 Buscando tipos de atendimento disponíveis...`); try { // Usar tipos de atendimento ao invés de técnicos let resultado = await this.topsapp.obterSuporteTiposAtendimento(); if (!resultado.success || !resultado.data?.dados || resultado.data.dados.length === 0) { // Fallback para técnicos se tipos de atendimento não funcionarem console.log('🔄 Fallback para técnicos...'); resultado = await this.topsapp.obterTecnicos(); if (!resultado.success) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar tipos de atendimento**\n\n` + `⚠️ ${resultado.error}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } } if (!resultado.data?.dados || resultado.data.dados.length === 0) { await this.bot.sendMessage(session.chatId, `❌ **Nenhum tipo de atendimento encontrado**\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); return; } session.dados.tecnicos = resultado.data.dados; await this.mostrarOpcoesTecnicos(session); } catch (error) { await this.bot.sendMessage(session.chatId, `❌ **Erro ao buscar tipos de atendimento**\n\n` + `⚠️ ${error.message}\n\n` + `🔄 Reiniciando processo...` ); this.suporteSessions.delete(sessionId); } } else { // Usuário selecionou um técnico const numero = parseInt(input.trim()); console.log(`🔍 Técnico - Input do usuário: "${input}", Número convertido: ${numero}`); console.log(`🔍 Técnico - Lista completa de técnicos:`, session.dados.tecnicos); if (isNaN(numero) || numero < 1 || numero > session.dados.tecnicos.length) { await this.bot.sendMessage(session.chatId, `❌ **Número inválido**\n\n` + `Por favor, digite um número entre 1 e ${session.dados.tecnicos.length}:` ); return; } const tecnicoSelecionado = session.dados.tecnicos[numero - 1]; console.log(`🎯 Técnico selecionado - Índice: ${numero - 1}, Dados:`, tecnicoSelecionado); session.dados.tecnicoSelecionado = tecnicoSelecionado; session.etapa = 'definir_data_hora'; await this.processarEtapaDefinirDataHora(sessionId); } } /** * Mostrar opções de técnicos/tipos de atendimento */ async mostrarOpcoesTecnicos(session) { let message = `👨‍🔧 **Tipos de atendimento disponíveis:**\n\n`; // Validar se técnicos é um array if (!Array.isArray(session.dados.tecnicos)) { console.error(`❌ session.dados.tecnicos não é um array:`, { type: typeof session.dados.tecnicos, value: session.dados.tecnicos }); throw new Error(`Dados de tipos de atendimento inválidos: ${typeof session.dados.tecnicos}`); } session.dados.tecnicos.forEach((tecnico, index) => { // Mostrar nome ou descrição do tipo de atendimento const nome = tecnico.nome || tecnico.descricao || tecnico.tipo || tecnico.tecnico; message += `**${index + 1}.** ${nome}\n`; if (tecnico.email) message += ` 📧 ${tecnico.email}\n`; message += `\n`; }); message += `Digite o **número** do tipo de atendimento (1-${session.dados.tecnicos.length}):`; await this.bot.sendMessage(session.chatId, message, { parse_mode: 'Markdown' }); } /** * Etapa 6: Definir data e hora */ async processarEtapaDefinirDataHora(sessionId, input = null) { const session = this.suporteSessions.get(sessionId); if (!session) return; if (!input) { // Primeira vez nesta etapa await this.bot.sendMessage(session.chatId, `📅 **Data e horário para visita**\n\n` + `Digite quando você gostaria que o técnico fizesse a visita.\n\n` + `**Exemplos:**\n` + `• "amanhã às 14h"\n` + `• "sexta-feira 09:00"\n` + `• "15/08 às 16h30"\n` + `• "hoje 18h"\n` + `• "em 3 dias às 10h"\n\n` + `📝 Digite a data e horário:`, { parse_mode: 'Markdown' } ); } else { // Processar texto de data/hora const resultado = this.dateProcessor.processarTexto(input); if (!resultado.success) { await this.bot.sendMessage(session.chatId, `❌ **Não consegui entender a data/hora**\n\n` + `⚠️ ${resultado.error}\n\n` + `**Exemplos válidos:**\n` + `• "amanhã às 14h"\n` + `• "sexta-feira 09:00"\n` + `• "15/08 às 16h30"\n\n` + `Por favor, tente novamente:` ); return; } // Verificar se a data não é no passado if (resultado.data < new Date()) { await this.bot.sendMessage(session.chatId, `❌ **Data no passado**\n\n` + `A data especificada (${this.dateProcessor.formatarData(resultado.data)}) já passou.\n\n` + `Por favor, especifique uma data futura:` ); return; } session.dados.dataHora = resultado.data; session.dados.dataHoraTexto = input; session.etapa = 'adicionar_descricao'; await this.bot.sendMessage(session.chatId, `✅ **Data/hora confirmada:**\n${this.dateProcessor.formatarData(resultado.data)}\n\n` + `📝 **Última etapa:** Descrição do problema\n\n` + `Digite uma descrição detalhada do problema ou serviço que precisa ser realizado:`, { parse_mode: 'Markdown' } ); } } /** * Etapa 7: Adicionar descrição */ async processarEtapaAdicionarDescricao(sessionId, descricao) { const session = this.suporteSessions.get(sessionId); if (!session) return; if (!descricao || descricao.trim().length < 10) { await this.bot.sendMessage(session.chatId, `❌ **Descrição muito curta**\n\n` + `Por favor, forneça uma descrição mais detalhada (mínimo 10 caracteres):` ); return; } session.dados.descricao = descricao.trim(); // Mostrar resumo e confirmar await this.mostrarResumoSuporte(session); } /** * Mostrar resumo do suporte antes de criar */ async mostrarResumoSuporte(session) { const dados = session.dados; let resumo = `📋 **Resumo do Suporte**\n\n`; resumo += `👤 **Cliente:** ${dados.clienteSelecionado.nome}\n`; resumo += `🆔 **ID Cliente:** ${dados.clienteSelecionado.id}\n`; resumo += `📋 **Serviço:** ${dados.servicoSelecionado.descricao || dados.servicoSelecionado.plano}\n`; resumo += `🆔 **ID Serviço:** ${dados.servicoSelecionado.id}\n`; resumo += `📁 **Categoria:** ${dados.categoriaSelecionada.descricao}\n`; resumo += `👨‍🔧 **Técnico:** ${dados.tecnicoSelecionado.nome || dados.tecnicoSelecionado.tecnico}\n`; resumo += `📅 **Data/Hora:** ${this.dateProcessor.formatarData(dados.dataHora)}\n`; resumo += `📝 **Descrição:** ${dados.descricao}\n\n`; resumo += `Confirma a abertura do suporte? (sim/não)`; await this.bot.sendMessage(session.chatId, resumo, { parse_mode: 'Markdown' }); session.etapa = 'confirmar'; } /** * Finalizar criação do suporte */ async finalizarCriacaoSuporte(sessionId, confirmacao) { const session = this.suporteSessions.get(sessionId); if (!session) return; const confirmar = confirmacao.toLowerCase().trim(); if (!['sim', 's', 'yes', 'y', 'confirmar', 'ok'].includes(confirmar)) { await this.bot.sendMessage(session.chatId, `❌ **Suporte cancelado**\n\n` + `O processo foi cancelado. Use o comando "abrir suporte" para tentar novamente.` ); this.suporteSessions.delete(sessionId); return; } await this.bot.sendMessage(session.chatId, `⏳ Criando suporte...`); try { const dados = session.dados; const dadosSuporte = { idCliente: dados.clienteSelecionado.id, idServico: dados.servicoSelecionado.id, idCategoria: dados.categoriaSelecionada.id, descricao: `${dados.descricao}\n\nData/Hora agendada: ${this.dateProcessor.formatarData(dados.dataHora)}\nTécnico: ${dados.tecnicoSelecionado.nome || dados.tecnicoSelecionado.tecnico}` }; // Adicionar campos opcionais se existirem if (dados.tecnicoSelecionado?.id) { dadosSuporte.idTecnico = dados.tecnicoSelecionado.id; } if (dados.clienteSelecionado?.nome) { dadosSuporte.contato = dados.clienteSelecionado.nome; } if (dados.clienteSelecionado?.telefone) { dadosSuporte.contatoTelefone = dados.clienteSelecionado.telefone; } dadosSuporte.outrasInformacoes = `Criado via Bot Telegram\nData original: ${dados.dataHoraTexto}`; console.log('🎫 Dados finais para criação do suporte:', dadosSuporte); const resultado = await this.topsapp.adicionarSuporte(dadosSuporte); if (resultado.success) { const protocolo = resultado.data?.protocolo || 'N/A'; await this.bot.sendMessage(session.chatId, `✅ **Suporte criado com sucesso!**\n\n` + `🎫 **Protocolo:** ${protocolo}\n` + `👤 **Cliente:** ${dados.clienteSelecionado.nome}\n` + `📋 **Serviço:** ${dados.servicoSelecionado.descricao || dados.servicoSelecionado.plano}\n` + `📁 **Categoria:** ${dados.categoriaSelecionada.descricao}\n` + `👨‍🔧 **Técnico:** ${dados.tecnicoSelecionado.nome || dados.tecnicoSelecionado.tecnico}\n` + `📅 **Data/Hora:** ${this.dateProcessor.formatarData(dados.dataHora)}\n\n` + `🎯 O suporte foi registrado no sistema!`, { parse_mode: 'Markdown' } ); logger.info('Suporte criado via bot', { protocolo, cliente: dados.clienteSelecionado.nome, usuario: session.userId }); } else { await this.bot.sendMessage(session.chatId, `❌ **Erro ao criar suporte**\n\n` + `⚠️ ${resultado.error}\n\n` + `Tente novamente mais tarde.` ); } } catch (error) { logger.error('Erro ao criar suporte:', error); await this.bot.sendMessage(session.chatId, `❌ **Erro ao criar suporte**\n\n` + `⚠️ ${error.message}\n\n` + `Tente novamente mais tarde.` ); } // Limpar sessão this.suporteSessions.delete(sessionId); } } module.exports = BotHandler;