const NLPProcessor = require('../nlp/processor'); const SSHManager = require('../ssh/manager'); const { TopSappManager } = require('../topsapp'); const FiberhomeManager = require('../fiberhome/manager'); const config = require('../config/config'); const logger = require('../utils/logger'); class BotHandler { constructor(bot) { this.bot = bot; this.nlp = new NLPProcessor(); this.ssh = new SSHManager(); this.topsapp = new TopSappManager(); this.fiberhome = new FiberhomeManager(); this.userSessions = new Map(); // Armazena sessões de usuário 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 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; // 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 '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 '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 = `📋 COMANDOS DISPONÍVEIS: 🔧 COMANDOS BÁSICOS: /start - Inicia o bot /help - Mostra esta ajuda /equipamentos - Lista equipamentos 🔐 SSH E CONEXÕES: /ssh - Conecta via SSH • "ssh juniper" • "acessar concentrador" 👥 JUNIPER - GERENCIAR USUÁRIOS: • "desconectar cliente USUARIO" • "kick usuário USUARIO" • "remover cliente USUARIO" • "listar usuários conectados" 📡 OLT - VERIFICAR SINAL DE ONU: • "ver sinal DACM91569513" • "verificar sinal ABCD12345678" • "sinal SERIAL_NUMBER" • Suporte para OLT Fiberhome + OLTs Datacom • Interface unificada para todas as OLTs ➕ OLT - ADICIONAR ONU: • "adicionar onu DACM91569513" • "cadastrar ABCD12345678" • "add onu SERIAL_NUMBER" 🗑️ OLT - REMOVER ONU: • "remover onu DACM91569513" • "deletar ABCD12345678" • "remover SERIAL_NUMBER" 🔄 OLT - REINICIAR ONU: • Após verificar o sinal, use o botão "🔌 Reiniciar ONU" • Comando confirma antes de reiniciar • Processo automático com confirmação ⬆️ OLT - ATUALIZAR FIRMWARE ONU: • Após verificar o sinal, use o botão "⬆️ Atualizar ONU" • Detecta automaticamente o modelo (DM986-100/DM986-414) • Oferece opções de firmware específicas para cada modelo • Processo seguro com confirmação 🏢 TOPSAPP - GESTÃO DE CLIENTES: • "buscar cliente João da Silva" • "buscar cliente 123.456.789-10" • "buscar cliente Guarai João" • "dados cliente João Silva" (dados cadastrais completos) • "dados cadastrais 123.456.789-10" • "ver faturas 123.456.789-10" • "listar serviços 123.456.789-10" • "gerar boleto fatura123" • "bloquear 123.456.789-10" • "liberar 123.456.789-10" • "status topsapp" 📋 DADOS CADASTRAIS COMPLETOS: • Informações pessoais do cliente • CPF, telefone, endereço completo • Status atual do cliente (ativo/inativo) • Dados de todos os serviços contratados • Login, senha, IP fixo (quando aplicável) • Plano contratado, valor mensalidade • Dia de vencimento, status do serviço • Dados FTTH: OLT, PON, VLAN, Serial ONU • Modelo da ONU e níveis de sinal RX/TX 🔍 BUSCA INTELIGENTE: • Busca por nome (com ou sem acentos) • Busca por CPF (formatado ou não) • Busca por ID do cliente • Múltiplos resultados com seleção • Interface com botões para ações rápidas 🔧 SERVIÇOS INDIVIDUAIS: • Use "listar serviços CLIENTE" para ver todos os serviços • Clique nos botões para bloquear/liberar serviços específicos • Controle granular por serviço do cliente 📊 STATUS E MONITORAMENTO: /status - Verifica status • "verificar concentrador" • "status do juniper" 🗣️ LINGUAGEM NATURAL: • "Conectar no concentrador" • "Desconectar cliente joao123" • "Ver sinal DACM91569513" • "Adicionar onu DACM91569513" • "Remover onu DACM91569513" • "Listar usuários conectados" • "Reiniciar juniper" • "Ping 131.0.192.254" • "Buscar cliente João da Silva" • "Ver faturas do CPF 123.456.789-10" ✅ EXEMPLOS PRÁTICOS: • "desconectar cliente user123" • "ver sinal DACM91569513" • "adicionar onu DACM91569513" • "remover onu DACM91569513" • "buscar cliente João Pedro Afonso" • "ver faturas cliente 123.456.789-10" • "gerar boleto fatura456" • "bloquear cliente 789" • "listar clientes conectados" • "verificar status concentrador" 🏢 OLTs DISPONÍVEIS: • OLT FIBERHOME (131.0.192.9) - Via TL1 • 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) 🛡️ COMANDOS ADMINISTRATIVOS: • "admin" - Painel administrativo • "usuarios autorizados" - Ver usuários com acesso • "meu id" - Ver seu ID do Telegram 🔶 FIBERHOME - COMANDOS ESPECÍFICOS: • "status onu FHTT12345678" - Status detalhado de ONU específica • "verificar onu FHTT12345678" - Verificar ONU por serial • "onu status DeviceName" - Status por nome do dispositivo • "fiberhome status" - Status das conexões Fiberhome • Use "ver sinal" para interface unificada �🔒 SEGURANÇA: 🛡️ Bot restrito a usuários autorizados 🆔 Para solicitar acesso, forneça seu ID`; await this.bot.sendMessage(chatId, helpMessage); } /** * 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) { let message = `📡 *Sinal da ONU ${serialNumber}*\n\n`; message += `🏢 **OLT:** ${olt.name} (${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' } ]); await this.bot.sendMessage(chatId, `🗑️ **Remover ONU ${serialNumber}**\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' } ]); await this.bot.sendMessage(chatId, `➕ **Adicionar ONU ${serialNumber}**\n\n` + '🏢 Selecione a OLT onde deseja adicionar a ONU:', { reply_markup: keyboard, 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_')) { 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_')) { console.log(`🔍 Chamando handleAddOnuCallback`); await this.handleAddOnuCallback(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 === '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'); 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 { 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); } /** * Lida com input de usuário durante sessões específicas */ async handleUserSessionInput(chatId, text, userId, session) { switch (session.type) { case 'waiting_custom_command': await this.executeCustomCommand(chatId, session.connectionId, text); break; case 'waiting_username_disconnect': await this.handleDisconnectUser(chatId, text.trim()); break; default: await this.bot.sendMessage(chatId, '❓ Estado de sessão desconhecido. Tente novamente.'); } // Limpar sessão this.userSessions.delete(userId); } /** * 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) { await this.bot.sendMessage(chatId, `🔍 Procurando ONU ${serialNumber} 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 ${serialNumber}*\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}\n`; message += `📤 **TX Power:** ${resultado.txPower}\n`; message += `📏 **Distância:** ${resultado.distance}\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 result = await this.ssh.executeCommand(connection.connectionId, configCommand); this.ssh.disconnect(connection.connectionId); if (result.success) { let message = `📋 *Configuração da ONU*\n`; message += `📍 Slot: ${slot}, ONU: ${onuId}\n\n`; message += `\`\`\`\n${result.output}\n\`\`\``; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } else { await this.bot.sendMessage(chatId, `❌ Erro ao obter configuração: ${result.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 atualizar 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 const keyboard = { inline_keyboard: [ [ { text: '✅ CONFIRMAR REMOÇÃO', callback_data: `confirm_remove_${oltId}_${result.slot.replace(/\//g, '-')}_${result.onuId}_${result.servicePort}_${serialNumber}` } ], [ { text: '❌ Cancelar', callback_data: 'cancel_action' } ] ] }; await this.bot.editMessageText( `⚠️ **CONFIRMAÇÃO DE REMOÇÃO**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${result.slot}, ONU ${result.onuId}\n` + `🔌 **Service-port:** ${result.servicePort}\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}`); const parts = data.split('_'); // confirm_remove_olt_dito_[slot]_[onuId]_[servicePort]_[serialNumber] console.log(`🔍 Parts: ${JSON.stringify(parts)}`); let oltId, slot, onuId, servicePort, serialNumber; if (parts.length >= 9 && parts[3] === 'pedro' && parts[4] === 'afonso') { // Caso: confirm_remove_olt_pedro_afonso_slot_onuId_servicePort_serialNumber 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]; servicePort = parts[7]; serialNumber = parts.slice(8).join('_'); // Serial pode ter underscores } else if (parts.length >= 8) { // Caso: confirm_remove_olt_dito_slot_onuId_servicePort_serialNumber oltId = parts[2] + '_' + parts[3]; // "olt_dito" slot = parts[4].replace(/-/g, '/'); // Converter de volta: "1-1-3" -> "1/1/3" onuId = parts[5]; servicePort = parts[6]; serialNumber = parts.slice(7).join('_'); // Serial pode ter underscores } else { // Fallback - caso não siga o padrão esperado oltId = parts[2]; slot = parts[3].replace(/-/g, '/'); // Converter de volta também no fallback onuId = parts[4]; servicePort = parts[5]; serialNumber = parts.slice(6).join('_'); } console.log(`🔍 Parsed - oltId: ${oltId}, slot: ${slot}, onuId: ${onuId}, servicePort: ${servicePort}, 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` + `🔌 **Service-port:** ${servicePort}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `⏳ Executando comandos de remoção...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); try { const result = await this.ssh.removeOnu(oltId, slot, onuId, servicePort); if (result.success) { await this.bot.editMessageText( `✅ **ONU Removida com Sucesso!**\n\n` + `📱 **Serial:** ${serialNumber}\n` + `📍 **Localização:** Slot ${slot}, ONU ${onuId}\n` + `🔌 **Service-port:** ${servicePort}\n` + `🏢 **OLT:** ${oltId.replace('olt_', '').replace(/_/g, '\\_').toUpperCase()}\n\n` + `✅ **Status:** ONU removida completamente\n` + `🗑️ **Ações realizadas:**\n` + `• Removida interface ONU\n` + `• Removido service-port\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` + `🔌 **Service-port:** ${servicePort}\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 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 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' } ); } } /** * 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}`, { parse_mode: 'Markdown' } ); 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`; if (cliente.cpf) { message += `📄 CPF: \`${cliente.cpf}\`\n`; } if (cliente.rg) { message += `📄 RG: \`${cliente.rg}\`\n`; } message += `📧 Email: ${cliente.email || 'Não informado'}\n`; message += `📞 Telefone: ${cliente.telefone || 'Não informado'}\n`; message += `📱 Celular: ${cliente.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}` } ] ]; await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } 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' } ); } } /** * Ver faturas do cliente */ async handleVerFaturas(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `📄 **Ver Faturas**\n\n` + `💡 **Como usar:**\n` + `• "ver faturas 123.456.789-10"\n` + `• "ver faturas cliente123"\n` + `• "ver faturas Guarai 123.456.789-10"` ); return; } try { await this.bot.sendMessage(chatId, '📄 Buscando faturas...'); // Detectar se especificou uma localização let location = ''; let clienteId = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; clienteId = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; clienteId = target.replace(/pedro afonso/gi, '').trim(); } // Se é CPF ou nome, buscar o cliente primeiro let idCliente = null; // Verificar se é número puro (CPF sem formatação) if (/^\d{11}$/.test(clienteId)) { // É CPF sem formatação const busca = await this.topsapp.buscarCliente(clienteId, location); if (busca.success && busca.clientes && busca.clientes.length > 0) { idCliente = busca.clientes[0].id; location = busca.instancia; } else { await this.bot.sendMessage(chatId, `❌ **Cliente não encontrado**\n\n` + `📝 CPF: "${clienteId}"`, { parse_mode: 'Markdown' } ); return; } } else if (clienteId.includes('.') || clienteId.includes('-') || isNaN(clienteId)) { // É CPF formatado ou nome const busca = await this.topsapp.buscarCliente(clienteId, location); if (busca.success && busca.clientes && busca.clientes.length > 0) { idCliente = busca.clientes[0].id; location = busca.instancia; } else { await this.bot.sendMessage(chatId, `❌ **Cliente não encontrado**\n\n` + `📝 CPF/Nome: "${clienteId}"`, { parse_mode: 'Markdown' } ); return; } } else { // É ID numérico idCliente = clienteId; } const resultado = await this.topsapp.obterBoletos(idCliente, location); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar faturas**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } if (!resultado.boletos || resultado.boletos.length === 0) { await this.bot.sendMessage(chatId, `📄 **Nenhuma fatura encontrada**\n\n` + `👤 Cliente ID: ${idCliente}\n` + `🌐 Instância: ${resultado.instancia || location}`, { parse_mode: 'Markdown' } ); return; } let message = `📄 **Faturas do Cliente**\n\n`; message += `📊 **Total**: ${resultado.total} fatura(s)\n`; message += `🌐 **Instância**: ${resultado.instancia}\n\n`; resultado.boletos.slice(0, 10).forEach((boleto, index) => { message += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; message += `💸 **${index + 1}. Fatura #${boleto.id}**\n`; message += `💰 **Valor:** R$ ${boleto.valor}\n`; message += `📅 **Vencimento:** ${this.formatarData(boleto.vencimento)}\n`; message += `📊 **Status:** ${this.getStatusFatura(boleto.status)}\n`; if (boleto.descricao) { message += `📝 **Descrição:** ${boleto.descricao}\n`; } if (boleto.periodo) { message += `📆 **Período:** ${boleto.periodo}\n`; } if (boleto.linhaDigitavel) { message += `\n🔢 **Linha Digitável:**\n\`${boleto.linhaDigitavel}\`\n`; } if (boleto.pixCopiaCola) { message += `\n💳 **PIX Copia e Cola:**\n\`${boleto.pixCopiaCola}\`\n`; } message += '\n'; }); if (resultado.total > 10) { message += `📋 *Mostrando apenas as primeiras 10 faturas de ${resultado.total}*\n\n`; } if (resultado.boletos.length > 0) { message += `💡 **Gerar boleto:**\n`; message += `• "gerar boleto ${resultado.boletos[0].id}"`; } await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); } 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 PDF */ async handleGerarBoleto(chatId, target) { if (!target) { await this.bot.sendMessage(chatId, `💸 **Gerar Boleto**\n\n` + `💡 **Como usar:**\n` + `• "gerar boleto 12345"\n` + `• "gerar boleto fatura123"` ); return; } try { await this.bot.sendMessage(chatId, '💸 Gerando boleto PDF...'); const resultado = await this.topsapp.gerarBoletoPDF(target); if (!resultado.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao gerar boleto**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; } // Enviar o PDF await this.bot.sendDocument(chatId, resultado.buffer, { filename: resultado.filename, caption: `💸 **Boleto Gerado**\n\n` + `📄 Arquivo: ${resultado.filename}\n` + `🌐 Instância: ${resultado.instancia}`, parse_mode: 'Markdown' }); await this.bot.sendMessage(chatId, `✅ **Boleto enviado com sucesso!**\n\n` + `📄 Arquivo: ${resultado.filename}\n` + `🌐 Instância: ${resultado.instancia}`, { parse_mode: 'Markdown' } ); } 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' } ); } } /** * Bloquear cliente */ async handleBloquearCliente(chatId, target) { console.log(`🔍 handleBloquearCliente iniciado com target:`, target, `(tipo: ${typeof target})`); if (!target) { await this.bot.sendMessage(chatId, `🔒 **Bloquear Cliente**\n\n` + `💡 **Como usar:**\n` + `• "bloquear 123.456.789-10"\n` + `• "bloquear cliente João"\n` + `• "bloquear Guarai 123.456.789-10"` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização let location = ''; let clienteQuery = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; clienteQuery = target.replace(/pedro afonso/gi, '').trim(); } // Buscar cliente primeiro let idCliente = null; let nomeCliente = ''; if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { // É CPF ou nome const busca = await this.topsapp.buscarCliente(clienteQuery, location); if (busca.success && busca.clientes && busca.clientes.length > 0) { idCliente = busca.clientes[0].id; nomeCliente = busca.clientes[0].nome; location = busca.instancia; } else { await this.bot.sendMessage(chatId, `❌ **Cliente não encontrado**\n\n` + `📝 CPF/Nome: "${clienteQuery}"`, { parse_mode: 'Markdown' } ); return; } } else { // É ID numérico idCliente = clienteQuery; } // Buscar serviços do cliente para mostrar opções const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } if (!servicosResult.servicos || servicosResult.servicos.length === 0) { await this.bot.sendMessage(chatId, `📋 **Nenhum serviço encontrado**\n\n` + `👤 Cliente: ${nomeCliente}\n` + `🆔 ID: ${idCliente}`, { parse_mode: 'Markdown' } ); return; } // Verificar se há serviços liberados const servicosLiberados = servicosResult.servicos.filter(s => s.status === 'liberado' || s.statusNumerico === 1 ); if (servicosLiberados.length === 0) { await this.bot.sendMessage(chatId, `🔒 **Nenhum serviço liberado**\n\n` + `👤 Cliente: ${nomeCliente}\n` + `🆔 ID: ${idCliente}\n` + `📊 Todos os ${servicosResult.servicos.length} serviços já estão bloqueados.`, { parse_mode: 'Markdown' } ); return; } // Criar menu de seleção de serviços let message = `🔒 **Selecionar Serviços para Bloquear**\n\n`; message += `👤 **Cliente**: ${nomeCliente}\n`; message += `🆔 **ID**: ${idCliente}\n`; message += `🌐 **Instância**: ${location}\n\n`; message += `✅ **Serviços liberados** (${servicosLiberados.length}):\n\n`; const keyboard = []; servicosLiberados.forEach((servico, index) => { message += `${index + 1}. **${servico.nome}**\n`; message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; message += ` 🆔 ID: ${servico.id}\n\n`; // Botão para bloquear este serviço específico keyboard.push([{ text: `🔒 Bloquear ${servico.nome}`, callback_data: `confirm_block_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` }]); }); // Botão para bloquear todos if (servicosLiberados.length > 1) { keyboard.push([{ text: `🔒 Bloquear TODOS os serviços (${servicosLiberados.length})`, callback_data: `confirm_block_all_${idCliente}_${location.replace(/\s+/g, '_')}` }]); } // Botão cancelar keyboard.push([{ 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 preparar bloqueio:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao preparar bloqueio**\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 123.456.789-10"\n` + `• "liberar cliente João"\n` + `• "liberar Guarai 123.456.789-10"` ); return; } try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização let location = ''; let clienteQuery = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; clienteQuery = target.replace(/pedro afonso/gi, '').trim(); } // Buscar cliente primeiro let idCliente = null; let nomeCliente = ''; if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { // É CPF ou nome const busca = await this.topsapp.buscarCliente(clienteQuery, location); if (busca.success && busca.clientes && busca.clientes.length > 0) { idCliente = busca.clientes[0].id; nomeCliente = busca.clientes[0].nome; location = busca.instancia; } else { await this.bot.sendMessage(chatId, `❌ **Cliente não encontrado**\n\n` + `📝 CPF/Nome: "${clienteQuery}"`, { parse_mode: 'Markdown' } ); return; } } else { // É ID numérico idCliente = clienteQuery; } // Buscar serviços do cliente para mostrar opções const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location); if (!servicosResult.success) { await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { parse_mode: 'Markdown' } ); return; } if (!servicosResult.servicos || servicosResult.servicos.length === 0) { await this.bot.sendMessage(chatId, `📋 **Nenhum serviço encontrado**\n\n` + `👤 Cliente: ${nomeCliente}\n` + `🆔 ID: ${idCliente}`, { parse_mode: 'Markdown' } ); return; } // Verificar se há serviços bloqueados const servicosBloqueados = servicosResult.servicos.filter(s => s.status === 'bloqueado' || s.statusNumerico === 2 ); if (servicosBloqueados.length === 0) { await this.bot.sendMessage(chatId, `✅ **Nenhum serviço bloqueado**\n\n` + `👤 Cliente: ${nomeCliente}\n` + `🆔 ID: ${idCliente}\n` + `📊 Todos os ${servicosResult.servicos.length} serviços estão liberados.`, { parse_mode: 'Markdown' } ); return; } // Criar menu de seleção de serviços let message = `🔓 **Selecionar Serviços para Liberar**\n\n`; message += `👤 **Cliente**: ${nomeCliente}\n`; message += `🆔 **ID**: ${idCliente}\n`; message += `🌐 **Instância**: ${location}\n\n`; message += `🔒 **Serviços bloqueados** (${servicosBloqueados.length}):\n\n`; const keyboard = []; servicosBloqueados.forEach((servico, index) => { message += `${index + 1}. **${servico.nome}**\n`; message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; message += ` 🆔 ID: ${servico.id}\n\n`; // Botão para liberar este serviço específico keyboard.push([{ text: `🔓 Liberar ${servico.nome}`, callback_data: `confirm_unblock_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` }]); }); // Botão para liberar todos if (servicosBloqueados.length > 1) { keyboard.push([{ text: `🔓 Liberar TODOS os serviços (${servicosBloqueados.length})`, callback_data: `confirm_unblock_all_${idCliente}_${location.replace(/\s+/g, '_')}` }]); } // Botão cancelar keyboard.push([{ 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 preparar liberação:', error); await this.bot.sendMessage(chatId, `❌ **Erro ao preparar liberação**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Utilitário para obter status da fatura */ getStatusFatura(status) { // Se já é uma string formatada, retornar diretamente if (typeof status === 'string') { switch (status.toLowerCase()) { case 'pago': return '✅ Pago'; case 'pendente': return '⏳ Pendente'; case 'em aberto': return '⏳ Pendente'; case 'vencida': return '❌ Vencida'; case 'processando': return '🔄 Em processamento'; default: return `📊 ${status}`; } } // Números (legado) switch (status) { case 1: return '✅ Pago'; case 2: return '⏳ Pendente'; case 3: return '❌ Vencida'; case 4: return '🔄 Em processamento'; default: return '❓ Status desconhecido'; } } /** * Callback para confirmar bloqueio de cliente */ async handleConfirmBlockCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; try { // Extrair dados: confirm_block_ID_LOCATION const parts = data.replace('confirm_block_', '').split('_'); const clienteId = parts[0]; const location = parts[1] ? parts[1].replace(/_/g, ' ') : ''; await this.bot.answerCallbackQuery(callbackQuery.id, 'Processando bloqueio...'); await this.bot.editMessageText( `🔒 **Bloqueando cliente ID: ${clienteId}**\n\n⏳ Aguarde...`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); const resultado = await this.topsapp.bloquearCliente(clienteId, location); if (!resultado.success) { await this.bot.editMessageText( `❌ **Erro ao bloquear cliente**\n\n` + `🆔 Cliente ID: ${clienteId}\n` + `🌐 Instância: ${location}\n` + `⚠️ Erro: ${resultado.error}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); return; } let message = `🔒 **Cliente bloqueado com sucesso!**\n\n`; message += `🆔 Cliente ID: ${clienteId}\n`; message += `🌐 Instância: ${resultado.instancia}\n`; message += `📊 Serviços bloqueados: ${resultado.total}\n\n`; if (resultado.servicosBloqueados && resultado.servicosBloqueados.length > 0) { message += `📋 **Detalhes dos bloqueios:**\n`; resultado.servicosBloqueados.forEach((servico, index) => { message += `${index + 1}. ${servico.descricao}: ${servico.resultado.success ? '✅ Bloqueado' : '❌ Erro'}\n`; }); } await this.bot.editMessageText(message, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' }); } catch (error) { console.error('❌ Erro ao bloquear cliente:', error); await this.bot.editMessageText( `❌ **Erro interno ao bloquear cliente**\n\n` + `🆔 Cliente ID: ${clienteId}\n` + `⚠️ ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Callback para confirmar liberação de cliente */ async handleConfirmUnblockCallback(callbackQuery, data) { const chatId = callbackQuery.message.chat.id; try { // Extrair dados: confirm_unblock_ID_LOCATION const parts = data.replace('confirm_unblock_', '').split('_'); const clienteId = parts[0]; const location = parts[1] ? parts[1].replace(/_/g, ' ') : ''; await this.bot.answerCallbackQuery(callbackQuery.id, 'Carregando opções...'); // Usar a mesma lógica do comando NLP para mostrar opções de serviços // Em vez de liberar tudo diretamente, mostrar menu de seleção const servicosResult = await this.topsapp.obterServicosCliente(clienteId, location); if (!servicosResult.success) { await this.bot.editMessageText( `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${servicosResult.error}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); return; } if (!servicosResult.servicos || servicosResult.servicos.length === 0) { await this.bot.editMessageText( `📋 **Nenhum serviço encontrado**\n\n` + `🆔 Cliente ID: ${clienteId}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); return; } // Verificar se há serviços bloqueados const servicosBloqueados = servicosResult.servicos.filter(s => s.status === 'bloqueado' || s.statusNumerico === 2 ); if (servicosBloqueados.length === 0) { await this.bot.editMessageText( `✅ **Nenhum serviço bloqueado**\n\n` + `🆔 Cliente ID: ${clienteId}\n` + `📊 Todos os ${servicosResult.servicos.length} serviços estão liberados.`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); return; } // Criar menu de seleção de serviços let message = `🔓 **Selecionar Serviços para Liberar**\n\n`; message += `🆔 **Cliente ID**: ${clienteId}\n`; message += `🌐 **Instância**: ${location}\n\n`; message += `� **Serviços bloqueados** (${servicosBloqueados.length}):\n\n`; const keyboard = []; servicosBloqueados.forEach((servico, index) => { message += `${index + 1}. **${servico.nome}**\n`; message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; message += ` 🆔 ID: ${servico.id}\n\n`; // Botão para liberar este serviço específico keyboard.push([{ text: `🔓 Liberar ${servico.nome}`, callback_data: `confirm_unblock_service_${servico.id}_${clienteId}_${location.replace(/\s+/g, '_')}` }]); }); // Botão para liberar todos if (servicosBloqueados.length > 1) { keyboard.push([{ text: `🔓 Liberar TODOS os serviços (${servicosBloqueados.length})`, callback_data: `confirm_unblock_all_${clienteId}_${location.replace(/\s+/g, '_')}` }]); } // Botão cancelar keyboard.push([{ text: '❌ Cancelar', callback_data: 'cancel_action' }]); await this.bot.editMessageText(message, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao preparar liberação:', error); await this.bot.editMessageText( `❌ **Erro ao preparar liberação**\n\n` + `🆔 Cliente ID: ${clienteId}\n` + `⚠️ ${error.message}`, { chat_id: chatId, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Comando para listar serviços do cliente */ async handleListarServicos(chatId, target) { logger.botCommand(chatId, 'listar_servicos', { target }); if (!target) { await this.bot.sendMessage(chatId, `📋 **Listar Serviços**\n\n` + `💡 **Como usar:**\n` + `• "listar serviços 123.456.789-10"\n` + `• "ver serviços cliente123"\n` + `• "mostrar serviços Guarai 123.456.789-10"` ); return; } try { await this.bot.sendMessage(chatId, '📋 Buscando serviços...'); // Detectar se especificou uma localização let location = ''; let clienteQuery = target; logger.debug('Processando busca de serviços', { originalTarget: target, chatId }); if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { location = 'Guarai/Colmeia'; clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); } else if (target.toLowerCase().includes('pedro afonso')) { location = 'Pedro Afonso'; clienteQuery = target.replace(/pedro afonso/gi, '').trim(); } logger.debug('Detectada localização e query', { location, clienteQuery, isNumeric: !isNaN(clienteQuery), isCPF: /^\d{11}$/.test(clienteQuery) }); // Buscar cliente primeiro let idCliente = null; let nomeCliente = ''; if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { // É CPF ou nome logger.debug('Buscando por CPF/Nome', { clienteQuery }); const busca = await this.topsapp.buscarCliente(clienteQuery, location); logger.debug('Resultado da busca de cliente', busca); if (busca.success && busca.clientes && busca.clientes.length > 0) { idCliente = busca.clientes[0].id; nomeCliente = busca.clientes[0].nome; location = busca.instancia; logger.debug('Cliente encontrado', { idCliente, nomeCliente, location }); } else { logger.warn('Cliente não encontrado', { clienteQuery, location }); await this.bot.sendMessage(chatId, `❌ **Cliente não encontrado**\n\n` + `📝 CPF/Nome: "${clienteQuery}"`, { parse_mode: 'Markdown' } ); return; } } else { // É ID numérico idCliente = clienteQuery; logger.debug('Usando ID direto', { idCliente: clienteQuery }); } logger.debug('Chamando obterServicosCliente', { idCliente, location, tipoIdCliente: typeof idCliente }); const resultado = await this.topsapp.obterServicosCliente(idCliente, location); logger.topsappOperation('obterServicosCliente', idCliente, resultado); if (!resultado.success) { logger.error('Erro ao buscar serviços', resultado.error); await this.bot.sendMessage(chatId, `❌ **Erro ao buscar serviços**\n\n` + `⚠️ ${resultado.error}`, { parse_mode: 'Markdown' } ); return; return; } // Debug: verificar se houve problema na filtragem if (resultado.debug && resultado.debug.totalRetornadoAPI > 100) { await this.bot.sendMessage(chatId, `⚠️ **DEBUG: Possível problema na API**\n\n` + `🔍 Total retornado pela API: ${resultado.debug.totalRetornadoAPI}\n` + `🎯 Total filtrado para cliente ${resultado.debug.clienteId}: ${resultado.debug.totalFiltrado}\n` + `🌐 Instância: ${resultado.instancia}\n\n` + `💡 Se este número ainda está alto, há problema na API do TopSapp.`, { parse_mode: 'Markdown' } ); } if (!resultado.servicos || resultado.servicos.length === 0) { await this.bot.sendMessage(chatId, `📋 **Nenhum serviço encontrado**\n\n` + `👤 Cliente ID: ${idCliente}\n` + `🌐 Instância: ${resultado.instancia}`, { parse_mode: 'Markdown' } ); return; } // Criar mensagem com lista de serviços let message = `📋 **Serviços do Cliente**\n\n`; if (nomeCliente) { message += `👤 **Cliente**: ${nomeCliente}\n`; } message += `🆔 **ID**: ${idCliente}\n`; message += `📊 **Total**: ${resultado.total} serviço(s)\n`; message += `🌐 **Instância**: ${resultado.instancia}\n\n`; // Criar botões inline para cada serviço const keyboard = []; resultado.servicos.forEach((servico, index) => { message += `🔧 **${index + 1}. ${servico.nome}**\n`; message += `🆔 ID: ${servico.id}\n`; message += `📊 Status: ${this.getStatusServico(servico.status)}\n`; message += `💰 Valor: R$ ${servico.valorMensal}\n`; if (servico.dataAtivacao) { message += `📅 Ativado em: ${servico.dataAtivacao}\n`; } message += '\n'; // Adicionar botões de ação baseados no status const row = []; if (servico.status === 'liberado') { row.push({ text: `🔒 Bloquear ${servico.nome}`, callback_data: `block_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` }); } else { row.push({ text: `🔓 Liberar ${servico.nome}`, callback_data: `unblock_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` }); } keyboard.push(row); }); await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown', reply_markup: { inline_keyboard: keyboard } }); } catch (error) { console.error('❌ Erro ao listar serviços:', error); await this.bot.sendMessage(chatId, `❌ **Erro interno ao listar serviços**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Utilitário para obter status do serviço */ getStatusServico(status) { if (typeof status === 'string') { switch (status.toLowerCase()) { case 'liberado': return '✅ Liberado'; case 'bloqueado': return '🔒 Bloqueado'; case 'desativado': return '❌ Desativado'; default: return `📊 ${status}`; } } switch (status) { case 1: return '✅ Liberado'; case 2: return '🔒 Bloqueado'; case 3: return '❌ Desativado'; default: return '❓ Status desconhecido'; } } async handleConfirmBlockServiceCallback(callbackQuery, data) { try { // Extrair dados: confirm_block_service_SERVICEID_CLIENTEID_LOCATION const parts = data.replace('confirm_block_service_', '').split('_'); const serviceId = parts[0]; const idCliente = parts[1]; const location = parts.slice(2).join('_').replace(/_/g, ' '); logger.debug('handleConfirmBlockServiceCallback', { serviceId, idCliente, location, originalData: data }); const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText( '⏳ Bloqueando serviço...', { chat_id: chatId, message_id: callbackQuery.message.message_id } ); const resultado = await this.topsapp.alterarStatusServico(serviceId, 'bloquear', location); let texto; if (resultado.success) { texto = '✅ Serviço bloqueado com sucesso!'; } else { texto = `❌ Erro ao bloquear serviço: ${resultado.error}`; } await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id } ); } catch (error) { logger.botError(callbackQuery.message.chat.id, error, 'handleConfirmBlockServiceCallback'); console.error('Erro no handleConfirmBlockServiceCallback:', error); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); } } async handleConfirmUnblockServiceCallback(callbackQuery, data) { try { // Extrair dados: confirm_unblock_service_SERVICEID_CLIENTEID_LOCATION const parts = data.replace('confirm_unblock_service_', '').split('_'); const serviceId = parts[0]; const idCliente = parts[1]; const location = parts.slice(2).join('_').replace(/_/g, ' '); logger.debug('handleConfirmUnblockServiceCallback', { serviceId, idCliente, location, originalData: data }); const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText( '⏳ Liberando serviço...', { chat_id: chatId, message_id: callbackQuery.message.message_id } ); const resultado = await this.topsapp.alterarStatusServico(serviceId, 'liberar', location); let texto; if (resultado.success) { texto = '✅ Serviço liberado com sucesso!'; } else { texto = `❌ Erro ao liberar serviço: ${resultado.error}`; } await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id } ); } catch (error) { console.error('Erro no handleConfirmUnblockServiceCallback:', error); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); } } async handleConfirmBlockAllCallback(callbackQuery, data) { try { const clienteId = data.replace('confirm_block_all_', ''); const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText( '⏳ Bloqueando todos os serviços...', { chat_id: chatId, message_id: callbackQuery.message.message_id } ); const resultado = await this.topsapp.bloquearCliente(clienteId); let texto; if (resultado.sucesso) { texto = '✅ Todos os serviços foram bloqueados com sucesso!'; } else { texto = `❌ Erro ao bloquear serviços: ${resultado.erro}`; } await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id } ); } catch (error) { console.error('Erro no handleConfirmBlockAllCallback:', error); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); } } async handleConfirmUnblockAllCallback(callbackQuery, data) { try { const clienteId = data.replace('confirm_unblock_all_', ''); const chatId = callbackQuery.message.chat.id; await this.bot.editMessageText( '⏳ Liberando todos os serviços...', { chat_id: chatId, message_id: callbackQuery.message.message_id } ); const resultado = await this.topsapp.liberarCliente(clienteId); let texto; if (resultado.sucesso) { texto = '✅ Todos os serviços foram liberados com sucesso!'; } else { texto = `❌ Erro ao liberar serviços: ${resultado.erro}`; } await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id } ); } catch (error) { console.error('Erro no handleConfirmUnblockAllCallback:', error); await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); } } async handleBlockServiceSelection(callbackQuery, data) { try { // Extrair dados: block_service_ID_CLIENTE_LOCATION const parts = data.replace('block_service_', '').split('_'); const serviceId = parts[0]; const clienteId = parts[1]; const location = parts.slice(2).join('_').replace(/_/g, ' '); const chatId = callbackQuery.message.chat.id; // Criar mensagem de confirmação const texto = `🔸 **Confirmar Bloqueio de Serviço**\n\n` + `🆔 **ID do Serviço:** ${serviceId}\n` + `👤 **Cliente:** ${clienteId}\n` + `📍 **Localização:** ${location}\n\n` + `⚠️ Tem certeza que deseja bloquear este serviço?`; const keyboard = { inline_keyboard: [ [ { text: '✅ Sim, bloquear', callback_data: `confirm_block_service_${serviceId}_${clienteId}_${location.replace(/\s+/g, '_')}` }, { text: '❌ Cancelar', callback_data: 'cancel' } ] ] }; await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id, reply_markup: keyboard, parse_mode: 'Markdown' } ); } catch (error) { console.error('❌ Erro ao selecionar serviço para bloqueio:', error); await this.bot.editMessageText( `❌ **Erro interno ao selecionar serviço**\n\n` + `⚠️ ${error.message}`, { chat_id: callbackQuery.message.chat.id, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } async handleUnblockServiceSelection(callbackQuery, data) { try { // Extrair dados: unblock_service_ID_CLIENTE_LOCATION const parts = data.replace('unblock_service_', '').split('_'); const serviceId = parts[0]; const clienteId = parts[1]; const location = parts.slice(2).join('_').replace(/_/g, ' '); const chatId = callbackQuery.message.chat.id; // Criar mensagem de confirmação const texto = `🔸 **Confirmar Liberação de Serviço**\n\n` + `🆔 **ID do Serviço:** ${serviceId}\n` + `👤 **Cliente:** ${clienteId}\n` + `📍 **Localização:** ${location}\n\n` + `✅ Tem certeza que deseja liberar este serviço?`; const keyboard = { inline_keyboard: [ [ { text: '✅ Sim, liberar', callback_data: `confirm_unblock_service_${serviceId}_${clienteId}_${location.replace(/\s+/g, '_')}` }, { text: '❌ Cancelar', callback_data: 'cancel' } ] ] }; await this.bot.editMessageText( texto, { chat_id: chatId, message_id: callbackQuery.message.message_id, reply_markup: keyboard, parse_mode: 'Markdown' } ); } catch (error) { console.error('❌ Erro ao selecionar serviço para liberação:', error); await this.bot.editMessageText( `❌ **Erro interno ao selecionar serviço**\n\n` + `⚠️ ${error.message}`, { chat_id: callbackQuery.message.chat.id, message_id: callbackQuery.message.message_id, parse_mode: 'Markdown' } ); } } /** * Visualizar logs recentes */ async handleViewLogs(chatId) { const userId = chatId; if (!this.isAuthorized(userId)) { await this.bot.sendMessage(chatId, '🚫 Acesso negado.'); return; } try { const recentLogs = logger.getRecentLogs(50); if (recentLogs.length === 0) { await this.bot.sendMessage(chatId, `📋 **Logs do Sistema**\n\n` + `ℹ️ Nenhum log encontrado ou modo debug desabilitado.`, { parse_mode: 'Markdown' } ); return; } // Dividir logs em mensagens menores (Telegram tem limite de caracteres) const maxLength = 4000; let currentMessage = `📋 **Logs Recentes (${recentLogs.length} linhas)**\n\n\`\`\`\n`; for (const line of recentLogs) { const newLine = line + '\n'; if ((currentMessage + newLine + '```').length > maxLength) { // Enviar mensagem atual e começar nova currentMessage += '```'; await this.bot.sendMessage(chatId, currentMessage, { parse_mode: 'Markdown' }); currentMessage = '```\n' + newLine; } else { currentMessage += newLine; } } // Enviar último bloco if (currentMessage.length > 10) { currentMessage += '```'; await this.bot.sendMessage(chatId, currentMessage, { parse_mode: 'Markdown' }); } } catch (error) { logger.botError(userId, error, 'handleViewLogs'); await this.bot.sendMessage(chatId, `❌ **Erro ao visualizar logs**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Limpar logs do sistema */ async handleClearLogs(chatId) { const userId = chatId; if (!this.isAuthorized(userId)) { await this.bot.sendMessage(chatId, '🚫 Acesso negado.'); return; } try { logger.clearLogs(); logger.info('Logs limpos pelo usuário administrativo', { userId }); await this.bot.sendMessage(chatId, `🧹 **Logs Limpos**\n\n` + `✅ Todos os logs foram removidos com sucesso.`, { parse_mode: 'Markdown' } ); } catch (error) { logger.botError(userId, error, 'handleClearLogs'); await this.bot.sendMessage(chatId, `❌ **Erro ao limpar logs**\n\n` + `⚠️ ${error.message}`, { parse_mode: 'Markdown' } ); } } /** * Callback para mostrar dados cadastrais do cliente */ async handleDadosClienteCallback(callbackQuery, data) { await this.bot.answerCallbackQuery(callbackQuery.id); const parts = data.split('_'); const idCliente = parts[2]; const location = parts[3] || ''; const chatId = callbackQuery.message.chat.id; await this.handleDadosCliente(chatId, idCliente, location); } /** * Callback para mostrar faturas do cliente */ async handleFaturasClienteCallback(callbackQuery, data) { await this.bot.answerCallbackQuery(callbackQuery.id); const parts = data.split('_'); const idCliente = parts[2]; const location = parts[3] || ''; const chatId = callbackQuery.message.chat.id; await this.handleVerFaturasById(chatId, idCliente, location); } /** * Callback para mostrar serviços do cliente */ async handleServicosClienteCallback(callbackQuery, data) { await this.bot.answerCallbackQuery(callbackQuery.id); const parts = data.split('_'); const idCliente = parts[2]; const location = parts[3] || ''; const chatId = callbackQuery.message.chat.id; await this.handleListarServicos(chatId, idCliente); } /** * Ver faturas do cliente por ID com organização por serviço */ async handleVerFaturasById(chatId, idCliente, location = '') { try { await this.bot.sendMessage(chatId, '📄 Buscando faturas detalhadas...'); 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; } if (!resultado.faturasPorServico || resultado.faturasPorServico.length === 0) { await this.bot.sendMessage(chatId, `📄 **Nenhuma fatura encontrada**\n\n` + `👤 Cliente ID: ${idCliente}\n` + `🌐 Instância: ${resultado.instancia || location}\n\n` + `✅ O cliente está em dia com os pagamentos!`, { parse_mode: 'Markdown' } ); return; } // Cabeçalho principal let headerMessage = `📄 **FATURAS EM ABERTO POR SERVIÇO**\n\n`; headerMessage += `👤 **Cliente ID**: ${idCliente}\n`; headerMessage += `📊 **Total de serviços**: ${resultado.totalServicos}\n`; headerMessage += `💳 **Total de faturas**: ${resultado.totalFaturas}\n`; headerMessage += `🌐 **Instância**: ${resultado.instancia || location}\n\n`; // Enviar cabeçalho await this.bot.sendMessage(chatId, headerMessage, { parse_mode: 'Markdown' }); // Processar cada serviço separadamente for (let i = 0; i < resultado.faturasPorServico.length; i++) { const servicoData = resultado.faturasPorServico[i]; const servico = servicoData.servico; const faturas = servicoData.faturas; // Informações do serviço let servicoMessage = `🔧 **SERVIÇO ${i + 1}: ${servico.nome}**\n`; servicoMessage += `🏷️ Base: ${servico.base}\n`; servicoMessage += `⚙️ Tecnologia: ${servico.tecnologia}\n`; if (servico.valor_mensal && servico.valor_mensal > 0) { servicoMessage += `💰 Valor mensal: R$ ${parseFloat(servico.valor_mensal).toFixed(2)}\n`; } if (servico.vencimento_dia !== 'Variável' && servico.vencimento_dia !== 'Não definido') { servicoMessage += `📅 Vencimento: Todo dia ${servico.vencimento_dia}\n`; } servicoMessage += `📊 Status do serviço: ${servico.status}\n\n`; // Enviar informações do serviço await this.bot.sendMessage(chatId, servicoMessage, { parse_mode: 'Markdown' }); // Dividir faturas em grupos menores para evitar overflow const faturasPorMensagem = 3; // Máximo 3 faturas por mensagem for (let j = 0; j < faturas.length; j += faturasPorMensagem) { const faturasGrupo = faturas.slice(j, j + faturasPorMensagem); let faturaMessage = ''; faturasGrupo.forEach((fatura, index) => { const valor = parseFloat(fatura.valor || 0); const faturaIndex = j + index + 1; // Destacar a data de vencimento const dataVencimento = this.formatarData(fatura.vencimento); const isVencida = new Date(fatura.vencimento) < new Date(); const statusVencimento = isVencida ? '🔴 VENCIDA' : '🟡 EM ABERTO'; faturaMessage += `📄 **Fatura ${faturaIndex}**\n`; faturaMessage += `🆔 ID: \`${fatura.id}\`\n`; faturaMessage += `💰 **Valor: R$ ${valor.toFixed(2)}**\n`; faturaMessage += `⏰ **VENCIMENTO: ${dataVencimento} ${statusVencimento}**\n`; if (fatura.periodo) { faturaMessage += `📅 Período: ${fatura.periodo}\n`; } if (fatura.descricao) { faturaMessage += `📝 Descrição: ${fatura.descricao}\n`; } faturaMessage += `📊 Status: ${this.getStatusFatura(fatura.status)}\n`; // Linha digitável if (fatura.linhaDigitavel) { faturaMessage += `\n📋 **Linha Digitável:**\n\`${fatura.linhaDigitavel}\`\n`; } // PIX Copia e Cola if (fatura.pixCopiaCola) { faturaMessage += `\n💲 **PIX Copia e Cola:**\n\`${fatura.pixCopiaCola}\`\n`; } faturaMessage += `\n`; }); // Enviar grupo de faturas await this.bot.sendMessage(chatId, faturaMessage, { parse_mode: 'Markdown' }); } } // Enviar botões de ação na mensagem final const keyboard = [ [ { text: '📋 Dados Cadastrais', callback_data: `dados_cliente_${idCliente}_${location}` }, { text: '⚙️ Ver Serviços', callback_data: `servicos_cliente_${idCliente}_${location}` } ] ]; if (resultado.faturasPorServico.length > 0 && 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, '🔧 **Ações disponíveis:**', { 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' } ); } } /** for (let i = 0; i < resultado.faturasPorServico.length; i++) { const servicoData = resultado.faturasPorServico[i]; const servico = servicoData.servico; const faturas = servicoData.faturas; message += `🔧 **SERVIÇO ${i + 1}: ${servico.nome}**\n`; message += `🏷️ Base: ${servico.base}\n`; message += `⚙️ Tecnologia: ${servico.tecnologia}\n`; if (servico.valor_mensal && servico.valor_mensal > 0) { message += `💰 Valor mensal: R$ ${parseFloat(servico.valor_mensal).toFixed(2)}\n`; } if (servico.vencimento_dia !== 'Variável' && servico.vencimento_dia !== 'Não definido') { message += `📅 Vencimento: Todo dia ${servico.vencimento_dia}\n`; } message += `📊 Status do serviço: ${servico.status}\n\n`; let valorTotalServico = 0; // Processar faturas do serviço faturas.forEach((fatura, index) => { const valor = parseFloat(fatura.valor || 0); valorTotalServico += valor; valorTotalGeral += valor; // Destacar a data de vencimento const dataVencimento = this.formatarData(fatura.vencimento); const isVencida = new Date(fatura.vencimento) < new Date(); const statusVencimento = isVencida ? '🔴 VENCIDA' : '🟡 EM ABERTO'; message += `� **Fatura ${index + 1}**\n`; message += `🆔 ID: \`${fatura.id}\`\n`; message += `💰 **Valor: R$ ${valor.toFixed(2)}**\n`; message += `⏰ **VENCIMENTO: ${dataVencimento} ${statusVencimento}**\n`; if (fatura.periodo) { message += `📅 Período: ${fatura.periodo}\n`; } if (fatura.descricao) { message += `📝 Descrição: ${fatura.descricao}\n`; } message += `📊 Status: ${this.getStatusFatura(fatura.status)}\n`; // Linha digitável if (fatura.linhaDigitavel) { message += `\n📋 **Linha Digitável:**\n\`${fatura.linhaDigitavel}\`\n`; } // PIX Copia e Cola if (fatura.pixCopiaCola) { message += `\n💲 **PIX Copia e Cola:**\n\`${fatura.pixCopiaCola}\`\n`; } message += `\n`; }); message += `💰 **Subtotal do serviço: R$ ${valorTotalServico.toFixed(2)}**\n`; message += `${'-'.repeat(30)}\n\n`; } message += `💰 **TOTAL GERAL EM ABERTO: R$ ${valorTotalGeral.toFixed(2)}**\n\n`; // Botões de ação const keyboard = [ [ { text: '📋 Dados Cadastrais', callback_data: `dados_cliente_${idCliente}_${location}` }, { text: '⚙️ Ver Serviços', callback_data: `servicos_cliente_${idCliente}_${location}` } ] ]; // Adicionar botão para gerar PDF da primeira fatura if (resultado.faturasPorServico.length > 0 && 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) { // Escapar caracteres especiais para evitar problemas no Telegram const errorMsg = resultado.error ? resultado.error.replace(/[_*\[\]()~`>#+=|{}.!-]/g, '\\$&') : 'Erro desconhecido'; await this.bot.sendMessage(chatId, `❌ **Erro ao obter status da ONU**\n\n` + `🔍 **Busca:** \`${target}\`\n` + `⚠️ **Erro:** ${errorMsg}`, { parse_mode: 'Markdown' } ); return; } // 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 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' }); } 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' } ); } } } module.exports = BotHandler;