Files
rodrigonet/src/bot/handler.js
T
rodrigo 1337e1c2d0 datacom: fix pedro afonso
Signed-off-by: Rodrigo B. de Sousa Martins <rodrigo.sousa.577@gmail.com>
2025-08-12 19:56:32 -03:00

4586 lines
186 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 <equipamento> - 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 <equipamento> - 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 <ID>" ou "ssh <ID>"';
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;