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;
+31
View File
@@ -177,6 +177,37 @@ class GenieACSApi {
return this._buscar({ [`Tags.${tag.toLowerCase()}`]: true }, instance, 'tag');
}
/**
* Cria uma task setParameterValues no dispositivo via NBI.
* Aguarda até 3 s pela conexão do CPE com o servidor (timeout=3000).
*
* @param {string} deviceId - _id do dispositivo
* @param {Array} parameterValues - Array de [paramName, value, xsdType]
* @param {Object} instance - Instância de GenieACSConfig
* @returns {Promise<Object>} - { success, error }
*/
async setParameterValues(deviceId, parameterValues, instance) {
const client = this._buildClient(instance);
const encodedId = encodeURIComponent(deviceId);
try {
await client.post(
`/devices/${encodedId}/tasks`,
{ name: 'setParameterValues', parameterValues },
{ params: { timeout: 3000 } }
);
logger.info(`[GenieACS] setParameterValues em ${deviceId}: ${JSON.stringify(parameterValues)}`);
return { success: true };
} catch (error) {
const status = error.response?.status;
const msg = status
? `Erro HTTP ${status} ao gravar parâmetros em ${deviceId} (${instance.name})`
: `Falha ao gravar parâmetros: ${error.message}`;
logger.error(`[GenieACS] ${msg}`);
return { success: false, error: msg };
}
}
/**
* Adiciona uma TAG a um dispositivo na NBI do GenieACS.
* Endpoint: POST /devices/{id}/tags/{tag}
+32
View File
@@ -115,6 +115,38 @@ class GenieACSManager {
const addResult = await this.api.adicionarTag(deviceId, tag, instance);
return { ...addResult, tag, deviceId };
}
/**
* Aplica um conjunto de setParameterValues a um dispositivo já conhecido pelo deviceId.
*
* @param {string} deviceId - _id do dispositivo no GenieACS
* @param {Array} paramValues - Array de [paramName, value, xsdType]
* @param {string} location - Texto de localização (opcional)
* @returns {Promise<Object>} - { success, error }
*/
async configurarRoteador(deviceId, paramValues, location = '') {
if (!this.isReady() || !deviceId) {
return { success: false, error: 'GenieACS não configurado ou deviceId ausente' };
}
const instance = this.config.getInstanceByLocation(location);
return this.api.setParameterValues(deviceId, paramValues, instance);
}
/**
* Relê um dispositivo pelo seu _id para obter dados atualizados.
*
* @param {string} deviceId - _id do dispositivo
* @param {string} location - Texto de localização (opcional)
* @returns {Promise<Object>} - { success, device, instance, searchedBy }
*/
async buscarRoteadorPorId(deviceId, location = '') {
if (!this.isReady() || !deviceId) {
return { success: false, error: 'GenieACS não configurado ou deviceId ausente' };
}
const instance = this.config.getInstanceByLocation(location);
const result = await this.api._buscar({ '_id': deviceId }, instance, 'id');
if (result.success) return { ...result, instance: instance.name };
return result;
}
}
module.exports = GenieACSManager;
+26 -1
View File
@@ -35,11 +35,14 @@ class NLPProcessor {
'fechar_suporte': ['fechar suporte', 'finalizar suporte', 'encerrar suporte', 'concluir suporte', 'fechar chamado', 'finalizar chamado', 'encerrar chamado', 'concluir chamado', 'fechar ticket', 'finalizar ticket', 'encerrar ticket', 'concluir atendimento', 'fechar atendimento', 'encerrar atendimento'],
'consultar_usuario': ['consultar usuario', 'consultar user', 'ver usuario', 'status usuario', 'status user', 'info usuario', 'info user', 'checar usuario', 'verificar usuario', 'pesquisar usuario', 'localizar usuario', 'buscar usuario', 'usuario conectado', 'user conectado', 'usuario online', 'user online', 'usuario ativo', 'user ativo'],
'diagnosticar': ['diagnosticar', 'diagnostico', 'diagnose', 'diagnosticar cliente', 'diagnostico cliente', 'checar cliente completo', 'verificar cliente completo', 'relatorio cliente', 'relatorio completo', 'status completo cliente', 'analisar cliente', 'inspecionar cliente', 'situacao do cliente', 'situação do cliente'],
'ver_roteador': ['ver roteador', 'info roteador', 'status roteador', 'roteador', 'cpe', 'ver cpe', 'info cpe', 'dados roteador', 'informações roteador', 'informacoes roteador', 'genieacs', 'ver device', 'info device', 'dispositivo cpe']
'ver_roteador': ['ver roteador', 'info roteador', 'status roteador', 'roteador', 'cpe', 'ver cpe', 'info cpe', 'dados roteador', 'informações roteador', 'informacoes roteador', 'genieacs', 'ver device', 'info device', 'dispositivo cpe'],
'configurar_roteador': ['configurar roteador', 'mudar roteador', 'alterar roteador', 'editar roteador', 'config roteador', 'configurar wifi', 'mudar wifi', 'alterar wifi', 'mudar ssid', 'alterar ssid', 'configurar cpe', 'mudar cpe', 'alterar cpe', 'configurar dns', 'mudar dns', 'configurar dhcp', 'mudar dhcp', 'alterar dhcp', 'mudar configuracoes roteador', 'mudar configurações roteador']
};
// Padrões de comandos
this.commandPatterns = [
// Padrões específicos para GenieACS – configurar roteador
/^(configurar|mudar|alterar|editar|config)\s+(roteador|cpe|wifi|ssid|dns|dhcp)(?:\s+(.+))?$/i,
// Padrões específicos para GenieACS – ver roteador
/^(ver|info|status|dados|informações|informacoes)\s+(roteador|cpe|device)\s+([A-Z0-9_-]+)$/i,
/^(roteador|cpe)\s+([A-Z0-9_-]+)$/i,
@@ -168,6 +171,17 @@ class NLPProcessor {
const fullMatch = match[0];
const action = match[1];
// Detectar comando de configurar roteador / CPE via GenieACS
if (/configurar|mudar|alterar|editar|config/i.test(action) && /roteador|cpe|wifi|ssid|dns|dhcp/i.test(fullMatch)) {
const termo = match[3] || match[2] || null;
return {
action: 'configurar_roteador',
target: termo ? termo.trim() : null,
confidence: 0.95,
type: 'genieacs_config_pattern'
};
}
// Detectar comando de ver roteador / CPE via GenieACS
if (/roteador|cpe|device/i.test(fullMatch) && !/sinal|signal|onu|ont/i.test(fullMatch)) {
// Padrão "ver roteador SERIAL" ou "roteador SERIAL"
@@ -681,6 +695,17 @@ class NLPProcessor {
extractFromKeywords(text) {
let bestMatch = { action: null, target: null, confidence: 0 };
// Detectar configurar roteador via palavras-chave
if (/configurar|mudar|alterar|editar/i.test(text) && /roteador|cpe|wifi|ssid|dns|dhcp/i.test(text)) {
const m = text.match(/(?:configurar|mudar|alterar|editar|config)\s+(?:roteador|cpe|wifi|ssid|dns|dhcp)\s*(.*)/i);
return {
action: 'configurar_roteador',
target: m && m[1] ? m[1].trim() : null,
confidence: 0.9,
type: 'keyword_configurar_roteador'
};
}
// Verificar primeiro se é um comando de desconectar usuário
if (/desconectar|kick|expulsar|remover/i.test(text)) {