genieacs: improvements

Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
rodrigo committed 2026-04-02 13:32:20 -03:00
1 parent 31926ecf80
commit 8dd2680838
4 files changed
+456 -1

No files matched your search

+367
View File
@@ -259,6 +259,11 @@ class BotHandler {
await this.handleVerRoteador(chatId, target);
break;
case 'configurar_roteador':
console.log(`⚙️ Executando handleConfigurarRoteador com target: ${target}`);
await this.handleConfigurarRoteador(chatId, userId, target);
break;
case 'abrir_suporte':
case 'adicionar_suporte':
case 'criar_suporte':
@@ -1660,6 +1665,12 @@ Digite /help para ver todos os comandos disponíveis!
// Verificar se é sessão baseada em action (Fiberhome ONU)
if (session.action) {
// Sessões de configuração de roteador (ação dinâmica com prefixo)
if (session.action.startsWith('router_cfg_wait_')) {
await this._processarInputConfigurarRoteador(chatId, userId, session, text);
return;
}
switch (session.action) {
case 'adicionar_onu_fiberhome_step1':
// Recebeu o nome do usuário, adicionar sufixo e perguntar pelo tipo da ONU
@@ -2015,6 +2026,9 @@ Digite /help para ver todos os comandos disponíveis!
const serialNumber = data.replace('fiberhome_details_', '');
await this.bot.answerCallbackQuery(callbackQuery.id);
await this.handleStatusONU(chatId, serialNumber);
} else if (data.startsWith('router_cfg_')) {
console.log(`🔍 Condição router_cfg_ atendida`);
await this.handleRouterCfgCallback(callbackQuery, data);
} else if (data.startsWith('diag_cliente_')) {
console.log(`🔍 Condição diag_cliente_ atendida`);
await this.handleDiagClienteCallback(callbackQuery, data);
@@ -7469,6 +7483,359 @@ Digite /help para ver todos os comandos disponíveis!
await this.bot.sendMessage(chatId, `❌ Erro ao consultar o GenieACS: ${error.message}`);
}
}
// ─────────────────────────────────────────────────────────────────────────
// CONFIGURAR ROTEADOR VIA GENIEACS
// ─────────────────────────────────────────────────────────────────────────
/**
* Mapa de campos configuráveis: chave curta → detalhes do parâmetro TR-069.
* Centralizado aqui para ser usado tanto no menu quanto nos callbacks.
*/
_routerParamMap() {
return {
ssid24: { label: 'SSID 2.4GHz', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID', type: 'xsd:string', toggle: false },
ssid5: { label: 'SSID 5GHz', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.2.SSID', type: 'xsd:string', toggle: false },
ch24: { label: 'Canal 2.4GHz', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Channel', type: 'xsd:unsignedInt', toggle: false },
ch5: { label: 'Canal 5GHz', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.2.Channel', type: 'xsd:unsignedInt', toggle: false },
auto24: { label: 'Auto-Canal 2.4', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.AutoChannelEnable', type: 'xsd:boolean', toggle: true },
auto5: { label: 'Auto-Canal 5', param: 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.2.AutoChannelEnable', type: 'xsd:boolean', toggle: true },
dnswan: { label: 'DNS WAN', param: 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.DNSServers', type: 'xsd:string', toggle: false },
dnslan: { label: 'DNS LAN', param: 'InternetGatewayDevice.LANDevice.1.LANHostConfigManagement.DNSServers', type: 'xsd:string', toggle: false },
dhcpstart: { label: 'DHCP Início', param: 'InternetGatewayDevice.LANDevice.1.LANHostConfigManagement.MinAddress', type: 'xsd:string', toggle: false },
dhcpend: { label: 'DHCP Fim', param: 'InternetGatewayDevice.LANDevice.1.LANHostConfigManagement.MaxAddress', type: 'xsd:string', toggle: false },
};
}
/**
* Comando "configurar roteador <serial|tag>".
* Busca o dispositivo e exibe o menu de configurações com inline keyboard.
*/
async handleConfigurarRoteador(chatId, userId, termo) {
if (!termo) {
await this.bot.sendMessage(chatId,
'⚙️ *Configurar Roteador*\n\n' +
'Informe o serial ou a TAG (login TopSapp) do dispositivo.\n' +
'Exemplos:\n' +
'• `configurar roteador ABCD12345678`\n' +
'• `configurar roteador joao.silva`',
{ parse_mode: 'Markdown' }
);
return;
}
await this.bot.sendMessage(chatId, `🔍 Buscando dispositivo \`${termo}\` no GenieACS...`, { parse_mode: 'Markdown' });
try {
const resultado = await this.genieacs.buscarRoteador(termo);
if (!resultado.success) {
await this.bot.sendMessage(chatId,
`❌ *Dispositivo não encontrado*\n\n🔍 Busca: \`${termo}\`\n⚠️ ${resultado.error || 'Nenhum resultado'}`,
{ parse_mode: 'Markdown' }
);
return;
}
// Salva contexto na sessão para os callbacks seguintes
this.userSessions.set(userId, {
action: 'router_cfg',
deviceId: resultado.device.deviceId,
device: resultado.device,
instance: resultado.instance || '',
timestamp: Date.now()
});
await this._enviarMenuConfigurarRoteador(chatId, resultado.device, resultado.instance || '');
} catch (error) {
logger.error('Erro em handleConfigurarRoteador:', error);
await this.bot.sendMessage(chatId, `❌ Erro ao consultar o GenieACS: ${error.message}`);
}
}
/**
* Envia/atualiza a mensagem de menu de configuração do roteador.
*/
async _enviarMenuConfigurarRoteador(chatId, device, instance) {
const esc = (v) => (v === null || v === undefined || v === '') ? 'N/A' : String(v).replace(/[_*`\[]/g, '\\$&');
const auto24On = device.autoChannel24 === 'Sim';
const auto5On = device.autoChannel5 === 'Sim';
const sumario =
`⚙️ *Configurar Roteador*\n` +
`📌 ${esc(instance)} — \`${esc(device.serial)}\`\n\n` +
`📶 *Wi-Fi 2.4 GHz*\n` +
`├ SSID: ${esc(device.ssid24)}\n` +
`├ Canal: ${esc(device.channel24)} (Auto: ${esc(device.autoChannel24)})\n\n` +
`📶 *Wi-Fi 5 GHz*\n` +
`├ SSID: ${esc(device.ssid5)}\n` +
`├ Canal: ${esc(device.channel5)} (Auto: ${esc(device.autoChannel5)})\n\n` +
`🌐 *WAN/PPPoE*\n` +
`├ DNS WAN: ${esc(device.dnsWAN)}\n\n` +
`🏠 *LAN/DHCP*\n` +
`├ DNS LAN: ${esc(device.dnsLAN)}\n` +
`├ DHCP Início: ${esc(device.dhcpInicio)}\n` +
`└ DHCP Fim: ${esc(device.dhcpFim)}\n\n` +
`_Selecione o que deseja alterar:_`;
const keyboard = {
inline_keyboard: [
[
{ text: '📶 SSID 2.4GHz', callback_data: 'router_cfg_ssid24' },
{ text: '📶 SSID 5GHz', callback_data: 'router_cfg_ssid5' },
],
[
{ text: `${auto24On ? '✅' : '⬜'} Auto-Canal 2.4`, callback_data: 'router_cfg_auto24' },
{ text: `${auto5On ? '✅' : '⬜'} Auto-Canal 5`, callback_data: 'router_cfg_auto5' },
],
[
{ text: '📡 Canal 2.4GHz', callback_data: 'router_cfg_ch24' },
{ text: '📡 Canal 5GHz', callback_data: 'router_cfg_ch5' },
],
[
{ text: '🌐 DNS WAN', callback_data: 'router_cfg_dnswan' },
{ text: '🏠 DNS LAN', callback_data: 'router_cfg_dnslan' },
],
[
{ text: '▶ DHCP Início', callback_data: 'router_cfg_dhcpstart' },
{ text: '◀ DHCP Fim', callback_data: 'router_cfg_dhcpend' },
],
[
{ text: '🔄 Atualizar leitura', callback_data: 'router_cfg_refresh' },
{ text: '❌ Fechar', callback_data: 'cancel_action' },
],
]
};
await this.bot.sendMessage(chatId, sumario, { parse_mode: 'Markdown', reply_markup: keyboard });
}
/**
* Dispatcher de todos os callbacks com prefixo "router_cfg_".
*/
async handleRouterCfgCallback(callbackQuery, data) {
const chatId = callbackQuery.message.chat.id;
const userId = callbackQuery.from.id;
const msgId = callbackQuery.message.message_id;
await this.bot.answerCallbackQuery(callbackQuery.id);
const session = this.userSessions.get(userId);
if (!session || session.action !== 'router_cfg') {
await this.bot.sendMessage(chatId, '⚠️ Sessão expirada. Use `configurar roteador <serial>` novamente.', { parse_mode: 'Markdown' });
return;
}
const field = data.replace('router_cfg_', ''); // ex.: "ssid24", "auto24", "ch24_6", "refresh"
// ── Atualizar leitura ────────────────────────────────────────────────
if (field === 'refresh') {
await this.bot.sendMessage(chatId, '🔄 Relendo dados do dispositivo...');
const fresh = await this.genieacs.buscarRoteadorPorId(session.deviceId, session.instance);
if (!fresh.success) {
await this.bot.sendMessage(chatId, `❌ Falha ao reler dispositivo: ${fresh.error}`);
return;
}
session.device = fresh.device;
this.userSessions.set(userId, session);
await this._enviarMenuConfigurarRoteador(chatId, fresh.device, session.instance);
return;
}
// ── Seleção de canal vinda do picker inline (ch24_N ou ch5_N) ───────
const chPickMatch = field.match(/^(ch24|ch5)_(\d+)$/);
if (chPickMatch) {
const band = chPickMatch[1]; // 'ch24' ou 'ch5'
const canal = parseInt(chPickMatch[2], 10);
const paramMap = this._routerParamMap();
const meta = paramMap[band];
await this.bot.sendMessage(chatId, `⏳ Aplicando ${meta.label} → canal ${canal}...`);
const res = await this.genieacs.configurarRoteador(
session.deviceId,
[[meta.param, canal, 'xsd:unsignedInt']],
session.instance
);
if (!res.success) {
await this.bot.sendMessage(chatId, `❌ Falha: ${res.error}`);
} else {
if (band === 'ch24') session.device.channel24 = String(canal);
else session.device.channel5 = String(canal);
this.userSessions.set(userId, session);
await this.bot.sendMessage(chatId, `✅ *${meta.label}* definido para o canal *${canal}*.`, { parse_mode: 'Markdown' });
}
await this._enviarMenuConfigurarRoteador(chatId, session.device, session.instance);
return;
}
const paramMap = this._routerParamMap();
// ── Toggle de auto-canal ─────────────────────────────────────────────
if (field === 'auto24' || field === 'auto5') {
const meta = paramMap[field];
const atualSim = field === 'auto24'
? session.device.autoChannel24 === 'Sim'
: session.device.autoChannel5 === 'Sim';
const novoValor = !atualSim; // inverte
await this.bot.sendMessage(chatId, `⏳ ${atualSim ? 'Desativando' : 'Ativando'} ${meta.label}...`);
const res = await this.genieacs.configurarRoteador(
session.deviceId,
[[meta.param, novoValor, 'xsd:boolean']],
session.instance
);
if (!res.success) {
await this.bot.sendMessage(chatId, `❌ Falha: ${res.error}`);
return;
}
// Atualiza o device local
if (field === 'auto24') session.device.autoChannel24 = novoValor ? 'Sim' : 'Não';
else session.device.autoChannel5 = novoValor ? 'Sim' : 'Não';
this.userSessions.set(userId, session);
await this.bot.sendMessage(chatId, `✅ ${meta.label} ${novoValor ? 'ativado' : 'desativado'} com sucesso.`);
await this._enviarMenuConfigurarRoteador(chatId, session.device, session.instance);
return;
}
// ── Picker de canal com botões inline ────────────────────────────────
if (field === 'ch24' || field === 'ch5') {
const meta = paramMap[field];
const canalAtual = field === 'ch24' ? (session.device.channel24 || '') : (session.device.channel5 || '');
const is24 = field === 'ch24';
const canais24 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
const canais5 = [36, 40, 44, 48, 52, 60, 64, 149, 153, 157, 161];
const canais = is24 ? canais24 : canais5;
// Monta rows de 4 botões por linha
const rows = [];
for (let i = 0; i < canais.length; i += 4) {
rows.push(
canais.slice(i, i + 4).map(ch => ({
text: String(ch) === String(canalAtual) ? `✅ ${ch}` : String(ch),
callback_data: `router_cfg_${field}_${ch}`
}))
);
}
rows.push([{ text: '↩️ Voltar', callback_data: 'router_cfg_refresh' }]);
await this.bot.sendMessage(chatId,
`📡 *${meta.label}*\nCanal atual: \`${canalAtual || 'N/A'}\`\n\nEscolha o novo canal:`,
{ parse_mode: 'Markdown', reply_markup: { inline_keyboard: rows } }
);
return;
}
// ── Campos de texto/número: solicita input ───────────────────────────
const meta = paramMap[field];
if (!meta) {
await this.bot.sendMessage(chatId, `❓ Campo desconhecido: \`${field}\``, { parse_mode: 'Markdown' });
return;
}
// Valor atual para mostrar ao usuário
const fieldToCurrent = {
ssid24: 'ssid24', ssid5: 'ssid5',
dnswan: 'dnsWAN', dnslan: 'dnsLAN',
dhcpstart: 'dhcpInicio', dhcpend: 'dhcpFim',
};
const atualKey = fieldToCurrent[field];
const valorAtual = atualKey ? (session.device[atualKey] || 'N/A') : 'N/A';
let hint = '';
if (field.startsWith('dns')) hint = ' (ex.: 8.8.8.8,8.8.4.4)';
else if (field.startsWith('dhcp')) hint = ' (ex.: 192.168.1.100)';
// Promove o estado de sessão para aguardar o valor
session.action = `router_cfg_wait_${field}`;
this.userSessions.set(userId, session);
await this.bot.sendMessage(chatId,
`⚙️ *Alterar ${meta.label}*\n\n` +
`Valor atual: \`${valorAtual}\`\n\n` +
`Digite o novo valor${hint}:\n` +
`_(ou envie /cancelar para abortar)_`,
{ parse_mode: 'Markdown' }
);
}
/**
* Processa o valor digitado pelo usuário para alterar um parâmetro do roteador.
* Chamado por handleUserSessionInput quando session.action começa com "router_cfg_wait_".
*/
async _processarInputConfigurarRoteador(chatId, userId, session, text) {
const field = session.action.replace('router_cfg_wait_', ''); // ex.: "ssid24"
const paramMap = this._routerParamMap();
const meta = paramMap[field];
if (!meta) {
this.userSessions.delete(userId);
await this.bot.sendMessage(chatId, '❌ Campo desconhecido. Operação cancelada.');
return;
}
// Cancela se o usuário enviou /cancelar
if (text.trim().toLowerCase() === '/cancelar') {
session.action = 'router_cfg';
this.userSessions.set(userId, session);
await this.bot.sendMessage(chatId, '🚫 Alteração cancelada.');
await this._enviarMenuConfigurarRoteador(chatId, session.device, session.instance);
return;
}
// Converte para o tipo correto
let valorFinal;
if (meta.type === 'xsd:unsignedInt') {
const n = parseInt(text.trim(), 10);
if (isNaN(n) || n < 0) {
await this.bot.sendMessage(chatId, `❌ Valor inválido. Informe um número inteiro positivo.`);
return; // mantém sessão aguardando nova tentativa
}
valorFinal = n;
} else {
valorFinal = text.trim();
if (!valorFinal) {
await this.bot.sendMessage(chatId, `❌ Valor não pode ser vazio.`);
return;
}
}
await this.bot.sendMessage(chatId, `⏳ Aplicando ${meta.label}...`);
const res = await this.genieacs.configurarRoteador(
session.deviceId,
[[meta.param, valorFinal, meta.type]],
session.instance
);
if (!res.success) {
await this.bot.sendMessage(chatId, `❌ Falha ao aplicar: ${res.error}`);
session.action = 'router_cfg';
this.userSessions.set(userId, session);
return;
}
await this.bot.sendMessage(chatId, `✅ *${meta.label}* atualizado para: \`${valorFinal}\``, { parse_mode: 'Markdown' });
// Atualiza o device local com o novo valor para o menu refletir
const fieldToDeviceKey = {
ssid24: 'ssid24', ssid5: 'ssid5',
ch24: 'channel24', ch5: 'channel5',
dnswan: 'dnsWAN', dnslan: 'dnsLAN',
dhcpstart: 'dhcpInicio', dhcpend: 'dhcpFim',
};
const devKey = fieldToDeviceKey[field];
if (devKey) session.device[devKey] = String(valorFinal);
session.action = 'router_cfg';
this.userSessions.set(userId, session);
await this._enviarMenuConfigurarRoteador(chatId, session.device, session.instance);
}
}
module.exports = BotHandler;