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