// genieacs-commands.js - Module for handling GenieACS commands via WhatsApp const { logger } = require('./logger'); const genieacsApi = require('./genieacs'); // const responses = require('./responses'); // Store the WhatsApp socket instance let sock = null; // Set the WhatsApp socket instance function setSock(sockInstance) { sock = sockInstance; logger.info('WhatsApp socket set in genieacs-commands module'); } // Helper functions for device status and parameters function getDeviceStatus(lastInform) { if (!lastInform) return false; const now = Date.now(); const lastInformTime = new Date(lastInform).getTime(); const timeDiff = now - lastInformTime; // Consider device online if last inform was within 5 minutes return timeDiff < 5 * 60 * 1000; } function formatUptime(uptimeValue) { if (!uptimeValue || uptimeValue === 'N/A') return 'N/A'; // If already formatted (like "5d 04:50:18"), return as is if (typeof uptimeValue === 'string' && uptimeValue.includes('d ')) { return uptimeValue; } // If it's seconds, convert to formatted string if (!isNaN(uptimeValue)) { const seconds = parseInt(uptimeValue); const days = Math.floor(seconds / (24 * 3600)); const hours = Math.floor((seconds % (24 * 3600)) / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; let result = ''; if (days > 0) result += `${days}d `; if (hours > 0) result += `${hours}h `; if (minutes > 0) result += `${minutes}m `; if (secs > 0) result += `${secs}s`; return result.trim() || '0s'; } return uptimeValue; } function getParameterWithPaths(device, paths) { if (!device || !paths || !Array.isArray(paths)) return 'N/A'; for (const path of paths) { try { const value = getParameterValue(device, path); if (value && value !== 'N/A') { return value; } } catch (error) { // Continue to next path } } return 'N/A'; } function getParameterValue(device, path) { if (!device || !path) return 'N/A'; try { const pathParts = path.split('.'); let current = device; for (const part of pathParts) { if (current && typeof current === 'object') { current = current[part]; } else { return 'N/A'; } } // Handle GenieACS parameter format if (current && typeof current === 'object' && current._value !== undefined) { return current._value; } // Handle direct value if (current !== null && current !== undefined && current !== '') { return current; } return 'N/A'; } catch (error) { logger.debug(`Error getting parameter ${path}: ${error.message}`); return 'N/A'; } } // Parameter paths for different device parameters (updated with confirmed VirtualParameters) const parameterPaths = { rxPower: [ 'VirtualParameters.RXPower', // ✅ CONFIRMED: -19.74 'InternetGatewayDevice.WANDevice.1.WANPONInterfaceConfig.RXPower', 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.X_ALU-COM_RxPower', 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.RxPower', 'Device.Optical.Interface.1.RxPower' ], pppoeIP: [ 'VirtualParameters.pppoeIP', // ✅ CONFIRMED: 192.168.10.159 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.ExternalIPAddress', 'Device.PPP.Interface.1.IPCPExtensions.RemoteIPAddress' ], pppUsername: [ 'VirtualParameters.pppoeUsername', // ✅ CONFIRMED: leha 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.Username', 'Device.PPP.Interface.1.Username' ], uptime: [ 'VirtualParameters.getdeviceuptime', // ✅ CONFIRMED: 5d 04:50:18 'InternetGatewayDevice.DeviceInfo.UpTime', 'Device.DeviceInfo.UpTime' ], firmware: [ 'InternetGatewayDevice.DeviceInfo.SoftwareVersion', 'Device.DeviceInfo.SoftwareVersion' ], userConnected: [ 'InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.TotalAssociations', // ✅ SSID 1 (2.4GHz) only 'VirtualParameters.activedevices', // ✅ Fallback if needed 'Device.WiFi.AccessPoint.1.AssociatedDeviceNumberOfEntries' ], temperature: [ 'VirtualParameters.gettemp', // ✅ CONFIRMED: 48 'InternetGatewayDevice.DeviceInfo.TemperatureStatus.TemperatureValue', 'Device.DeviceInfo.TemperatureStatus.TemperatureValue' ], // Additional VirtualParameters yang tersedia serialNumber: [ 'VirtualParameters.getSerialNumber', // ✅ AVAILABLE: CIOT12E8C8B8 'InternetGatewayDevice.DeviceInfo.SerialNumber' ], ponMode: [ 'VirtualParameters.getponmode' // ✅ AVAILABLE: EPON ], pppUptime: [ 'VirtualParameters.getpppuptime' // ✅ AVAILABLE: 0d 08:46:43 ], ponMac: [ 'VirtualParameters.PonMac', // ✅ AVAILABLE (but might be empty) 'VirtualParameters.ponMac', // Alternative lowercase 'VirtualParameters.MacAddress', // Alternative name 'VirtualParameters.deviceMac', // Alternative name 'VirtualParameters.ontMac', // Alternative name 'InternetGatewayDevice.DeviceInfo.X_ALU-COM_MACAddress', 'InternetGatewayDevice.LANDevice.1.LANEthernetInterfaceConfig.1.MACAddress' ], wlanPassword: [ 'VirtualParameters.WlanPassword' // ✅ AVAILABLE ] }; // Menggunakan format pesan dari responses.js function formatResponse(message) { return message; // Tidak pakai header/footer lagi } // Get device by phone number async function getDeviceByNumber(phoneNumber) { try { return await genieacsApi.findDeviceByPhoneNumber(phoneNumber); } catch (error) { logger.error(`Error finding device with phone number ${phoneNumber}: ${error.message}`); return null; } } // Handler for WiFi info command async function handleWifiInfo(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get SSID information const ssid = getSSIDValue(device, '1') || 'N/A'; const ssid5G = getSSIDValue(device, '5') || 'N/A'; // Send WiFi information const wifiInfo = `📱 *INFORMASI WI-FI*\n\n`; wifiInfo += `• SSID 2.4G: ${ssid}\n`; wifiInfo += `• SSID 5G: ${ssid5G}\n`; await sock.sendMessage(remoteJid, { text: formatResponse(wifiInfo) }); } catch (error) { logger.error(`Error handling WiFi info: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mengambil informasi WiFi.') }); } } // Handler for change WiFi SSID command async function handleChangeWifiSSID(remoteJid, senderNumber, newSSID) { if (!sock) { logger.error('Sock instance not set'); return; } try { if (!newSSID || newSSID.length < 3 || newSSID.length > 32) { await sock.sendMessage(remoteJid, { text: formatResponse('Format SSID tidak valid. SSID harus antara 3-32 karakter.') }); return; } // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse(`Sedang mengubah SSID perangkat ke "${newSSID}"...`) }); // Set SSID parameter value (pass server ID from device) await genieacsApi.setParameterValues(device._id, { 'SSID': newSSID }, device._acs_server_id); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse(`SSID berhasil diubah ke "${newSSID}".`) }); } catch (error) { logger.error(`Error changing WiFi SSID: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mengubah SSID.') }); } } // Handler for change WiFi password command async function handleChangeWifiPassword(remoteJid, senderNumber, newPassword) { if (!sock) { logger.error('Sock instance not set'); return; } try { if (!newPassword || newPassword.length < 8 || newPassword.length > 63) { await sock.sendMessage(remoteJid, { text: formatResponse('Format password tidak valid. Password harus antara 8-63 karakter.') }); return; } // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse('Sedang mengubah password WiFi...') }); // Set password parameter value (pass server ID from device) await genieacsApi.setParameterValues(device._id, { 'KeyPassphrase': newPassword }, device._acs_server_id); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse('Password WiFi berhasil diubah.') }); } catch (error) { logger.error(`Error changing WiFi password: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mengubah password WiFi.') }); } } // Handler for device status command async function handleDeviceStatus(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get device status information const lastInform = device._lastInform; const isOnline = getDeviceStatus(lastInform); const serialNumber = device.InternetGatewayDevice?.DeviceInfo?.SerialNumber?._value || 'N/A'; const firmware = getParameterWithPaths(device, parameterPaths.firmware) || 'N/A'; const uptime = formatUptime(getParameterWithPaths(device, parameterPaths.uptime)) || 'N/A'; const rxPower = getParameterWithPaths(device, parameterPaths.rxPower) || 'N/A'; const pppoeIP = getParameterWithPaths(device, parameterPaths.pppoeIP) || 'N/A'; const pppUsername = getParameterWithPaths(device, parameterPaths.pppUsername) || 'N/A'; const ssid = getSSIDValue(device, '1') || 'N/A'; const ssid5G = getSSIDValue(device, '5') || 'N/A'; const connectedUsers = getParameterWithPaths(device, parameterPaths.userConnected) || '0'; // Format device status const statusMessage = `📱 *STATUS PERANGKAT*\n\n`; statusMessage += `• Status Online: ${isOnline ? '🟢 Online' : '🔴 Offline'}\n`; statusMessage += `• Serial Number: ${serialNumber}\n`; statusMessage += `• Firmware: ${firmware}\n`; statusMessage += `• Uptime: ${uptime}\n`; statusMessage += `• RX Power: ${rxPower} dBm\n`; statusMessage += `• PPPoE IP: ${pppoeIP}\n`; statusMessage += `• PPP Username: ${pppUsername}\n`; statusMessage += `• SSID 2.4G: ${ssid}\n`; statusMessage += `• SSID 5G: ${ssid5G}\n`; statusMessage += `• Pengguna Terhubung: ${connectedUsers}\n`; statusMessage += `• Last Inform: ${new Date(lastInform).toLocaleString()}\n`; await sock.sendMessage(remoteJid, { text: formatResponse(statusMessage) }); } catch (error) { logger.error(`Error handling device status: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mengambil status perangkat.') }); } } // Handler for restart device command async function handleRestartDevice(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Send confirmation message await sock.sendMessage(remoteJid, { text: formatResponse('Apakah Anda yakin ingin merestart perangkat ini? (ketik "confirm restart" untuk melanjutkan)') }); // Save restart confirmation status global.pendingRestarts = global.pendingRestarts || {}; global.pendingRestarts[senderNumber] = { deviceId: device._id, timestamp: Date.now() }; } catch (error) { logger.error(`Error preparing device restart: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mempersiapkan restart perangkat.') }); } } // Handler for restart confirmation async function handleRestartConfirmation(remoteJid, senderNumber, confirmed) { if (!sock) { logger.error('Sock instance not set'); return; } try { if (!global.pendingRestarts || !global.pendingRestarts[senderNumber]) { await sock.sendMessage(remoteJid, { text: formatResponse('Tidak ada permintaan restart yang pending.') }); return; } const { deviceId, timestamp } = global.pendingRestarts[senderNumber]; // Check if confirmation is still valid (within 5 minutes) if (Date.now() - timestamp > 5 * 60 * 1000) { await sock.sendMessage(remoteJid, { text: formatResponse('Permintaan restart sudah kadaluarsa.') }); delete global.pendingRestarts[senderNumber]; return; } if (confirmed) { // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse('Sedang merestart perangkat...') }); // Restart device (pass server ID from pending restart) const device = await genieacsApi.getDevice(deviceId); await genieacsApi.reboot(deviceId, device._acs_server_id); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat berhasil direstart.') }); } else { // Send cancellation message await sock.sendMessage(remoteJid, { text: formatResponse('Restart perangkat dibatalkan.') }); } // Delete restart confirmation status delete global.pendingRestarts[senderNumber]; } catch (error) { logger.error(`Error handling restart confirmation: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse('Terjadi kesalahan saat mengkonfirmasi restart.') }); // Delete restart confirmation status even if error if (global.pendingRestarts && global.pendingRestarts[senderNumber]) { delete global.pendingRestarts[senderNumber]; } } } // Helper function to get SSID value function getSSIDValue(device, configIndex) { try { // Try method 1: Using bracket notation for WLANConfiguration if (device.InternetGatewayDevice && device.InternetGatewayDevice.LANDevice && device.InternetGatewayDevice.LANDevice['1'] && device.InternetGatewayDevice.LANDevice['1'].WLANConfiguration && device.InternetGatewayDevice.LANDevice['1'].WLANConfiguration[configIndex] && device.InternetGatewayDevice.LANDevice['1'].WLANConfiguration[configIndex].SSID) { const ssidObj = device.InternetGatewayDevice.LANDevice['1'].WLANConfiguration[configIndex].SSID; if (ssidObj._value !== undefined) { return ssidObj._value; } } // Try method 2: Using getParameterWithPaths const ssidPath = `InternetGatewayDevice.LANDevice.1.WLANConfiguration.${configIndex}.SSID`; const ssidValue = getParameterWithPaths(device, [ssidPath]); if (ssidValue && ssidValue !== 'N/A') { return ssidValue; } // Try method 3: Search in entire object for (const key in device) { if (device[key]?.LANDevice?.['1']?.WLANConfiguration?.[configIndex]?.SSID?._value) { return device[key].LANDevice['1'].WLANConfiguration[configIndex].SSID._value; } } // Try method 4: Check in virtual parameters if (device.VirtualParameters?.SSID?._value) { return device.VirtualParameters.SSID._value; } if (configIndex === '5' && device.InternetGatewayDevice?.LANDevice?.['1']?.WLANConfiguration?.['2']?.SSID?._value) { return device.InternetGatewayDevice.LANDevice['1'].WLANConfiguration['2'].SSID._value; } return 'N/A'; } catch (error) { logger.error(`Error getting SSID for config ${configIndex}: ${error.message}`); return 'N/A'; } } // Handler untuk factory reset perangkat async function handleFactoryReset(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Send confirmation message await sock.sendMessage(remoteJid, { text: formatResponse(`⚠️ *PERINGATAN FACTORY RESET*\n\nAnda akan melakukan factory reset pada perangkat Anda.\nSemua pengaturan akan kembali ke default pabrik.\n\nKetik "confirm factory reset" untuk melanjutkan.`) }); // Save factory reset confirmation status global.pendingFactoryResets = global.pendingFactoryResets || {}; global.pendingFactoryResets[senderNumber] = { deviceId: device._id, timestamp: Date.now() }; } catch (error) { logger.error(`Error preparing factory reset: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk konfirmasi factory reset async function handleFactoryResetConfirmation(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { if (!global.pendingFactoryResets || !global.pendingFactoryResets[senderNumber]) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Tidak ada permintaan factory reset yang pending.`) }); return; } const { deviceId, timestamp } = global.pendingFactoryResets[senderNumber]; // Check if confirmation is still valid (within 5 minutes) if (Date.now() - timestamp > 5 * 60 * 1000) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Permintaan factory reset sudah expired. Silakan ulangi.`) }); delete global.pendingFactoryResets[senderNumber]; return; } // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse(`🔄 Melakukan factory reset perangkat...\nMohon tunggu beberapa menit.`) }); // Factory reset device (pass server ID) const device = await genieacsApi.getDevice(deviceId); await genieacsApi.factoryReset(deviceId, device._acs_server_id); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse(`✅ Factory reset berhasil dilakukan.\nPerangkat akan restart dan kembali ke pengaturan default pabrik.`) }); // Delete factory reset confirmation status delete global.pendingFactoryResets[senderNumber]; } catch (error) { logger.error(`Error handling factory reset confirmation: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); // Delete factory reset confirmation status even if error if (global.pendingFactoryResets && global.pendingFactoryResets[senderNumber]) { delete global.pendingFactoryResets[senderNumber]; } } } // Handler untuk melihat perangkat yang terhubung ke WiFi async function handleConnectedDevices(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get connected devices information // Get number of connected users const connectedUsers = getParameterWithPaths(device, parameterPaths.userConnected) || '0'; // Try to get detailed host information let hostInfo = ''; try { // Check for host entries in LANDevice if (device.InternetGatewayDevice?.LANDevice?.['1']?.Hosts?.Host) { const hosts = device.InternetGatewayDevice.LANDevice['1'].Hosts.Host; let hostCount = 0; for (const hostKey in hosts) { const host = hosts[hostKey]; if (host.Active?._value === 'true' || host.Active?._value === true) { hostCount++; if (hostCount <= 10) { // Limit to 10 devices const hostname = host.HostName?._value || 'Unknown'; const ip = host.IPAddress?._value || 'N/A'; const mac = host.MACAddress?._value || 'N/A'; hostInfo += `${hostCount}. ${hostname}\n IP: ${ip}\n MAC: ${mac}\n\n`; } } } if (hostCount > 10) { hostInfo += `... dan ${hostCount - 10} perangkat lainnya\n`; } } } catch (error) { logger.error(`Error getting detailed host info: ${error.message}`); } let message = `📱 *PERANGKAT TERHUBUNG*\n\n`; message += `Jumlah perangkat aktif: ${connectedUsers}\n\n`; if (hostInfo) { message += `Detail perangkat:\n${hostInfo}`; } else { message += `Detail perangkat tidak tersedia.`; } await sock.sendMessage(remoteJid, { text: formatResponse(message) }); } catch (error) { logger.error(`Error handling connected devices: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk admin - melihat detail lengkap perangkat async function handleAdminDeviceDetail(remoteJid, phoneNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by phone number const device = await getDeviceByNumber(phoneNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Perangkat dengan nomor ${phoneNumber} tidak ditemukan.`) }); return; } // Get device information const lastInform = device._lastInform; const isOnline = getDeviceStatus(lastInform); const serialNumber = device.InternetGatewayDevice?.DeviceInfo?.SerialNumber?._value || 'N/A'; const firmware = getParameterWithPaths(device, parameterPaths.firmware) || 'N/A'; const uptime = formatUptime(getParameterWithPaths(device, parameterPaths.uptime)) || 'N/A'; const rxPower = getParameterWithPaths(device, parameterPaths.rxPower) || 'N/A'; const pppoeIP = getParameterWithPaths(device, parameterPaths.pppoeIP) || 'N/A'; const pppUsername = getParameterWithPaths(device, parameterPaths.pppUsername) || 'N/A'; const ssid = getSSIDValue(device, '1') || 'N/A'; const ssid5G = getSSIDValue(device, '5') || 'N/A'; const connectedUsers = getParameterWithPaths(device, parameterPaths.userConnected) || '0'; const temperature = getParameterWithPaths(device, parameterPaths.temperature) || 'N/A'; // Additional VirtualParameters const ponMode = getParameterWithPaths(device, parameterPaths.ponMode) || 'N/A'; const pppUptime = getParameterWithPaths(device, parameterPaths.pppUptime) || 'N/A'; // Get device model and manufacturer const manufacturer = device.InternetGatewayDevice?.DeviceInfo?.Manufacturer?._value || 'N/A'; const model = device.InternetGatewayDevice?.DeviceInfo?.ModelName?._value || 'N/A'; const hardwareVersion = device.InternetGatewayDevice?.DeviceInfo?.HardwareVersion?._value || 'N/A'; let message = `🔍 *DETAIL PERANGKAT ADMIN*\n\n`; message += `📱 *Nomor:* ${phoneNumber}\n`; message += `🆔 *Device ID:* ${device._id}\n`; message += `📟 *Serial Number:* ${serialNumber}\n`; message += `🏭 *Manufacturer:* ${manufacturer}\n`; message += `📦 *Model:* ${model}\n`; message += `🔧 *Hardware Version:* ${hardwareVersion}\n`; message += `💾 *Firmware:* ${firmware}\n\n`; message += `🌐 *Status Koneksi:*\n`; message += `• Status: ${isOnline ? '🟢 Online' : '🔴 Offline'}\n`; message += `• Last Inform: ${new Date(lastInform).toLocaleString()}\n`; message += `• Device Uptime: ${uptime}\n`; message += `• PPP Uptime: ${pppUptime}\n`; message += `• PPPoE IP: ${pppoeIP}\n`; message += `• PPP Username: ${pppUsername}\n\n`; message += `📡 *Network Information:*\n`; message += `• PON Mode: ${ponMode}\n`; message += `• RX Power: ${rxPower} dBm\n`; message += `• Temperature: ${temperature}°C\n\n`; message += `📶 *WiFi Information:*\n`; message += `• SSID 2.4G: ${ssid}\n`; message += `• SSID 5G: ${ssid5G}\n`; message += `• Connected Devices: ${connectedUsers}\n\n`; // Get tags if (device._tags && device._tags.length > 0) { message += `🏷️ *Tags:* ${device._tags.join(', ')}\n`; } await sock.sendMessage(remoteJid, { text: formatResponse(message) }); } catch (error) { logger.error(`Error handling admin device detail: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk admin - restart perangkat pelanggan async function handleAdminRestartDevice(remoteJid, phoneNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by phone number const device = await getDeviceByNumber(phoneNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Perangkat dengan nomor ${phoneNumber} tidak ditemukan.`) }); return; } // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse(`🔄 Melakukan restart perangkat pelanggan ${phoneNumber}...\nMohon tunggu beberapa menit.`) }); // Restart device (pass server ID from device) await genieacsApi.reboot(device._id, device._acs_server_id); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse(`✅ Perintah restart berhasil dikirim ke perangkat pelanggan ${phoneNumber}.\nPerangkat akan restart dalam beberapa menit.`) }); } catch (error) { logger.error(`Error handling admin restart device: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk admin - factory reset perangkat pelanggan async function handleAdminFactoryReset(remoteJid, phoneNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by phone number const device = await getDeviceByNumber(phoneNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Perangkat dengan nomor ${phoneNumber} tidak ditemukan.`) }); return; } // Send confirmation message await sock.sendMessage(remoteJid, { text: formatResponse(`⚠️ *PERINGATAN FACTORY RESET ADMIN*\n\nAnda akan melakukan factory reset pada perangkat pelanggan ${phoneNumber}.\nSemua pengaturan akan kembali ke default pabrik.\n\nKetik "confirm admin factory reset ${phoneNumber}" untuk melanjutkan.`) }); // Save factory reset confirmation status global.pendingAdminFactoryResets = global.pendingAdminFactoryResets || {}; global.pendingAdminFactoryResets[phoneNumber] = { deviceId: device._id, timestamp: Date.now() }; } catch (error) { logger.error(`Error preparing admin factory reset: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk konfirmasi admin factory reset async function handleAdminFactoryResetConfirmation(remoteJid, phoneNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { if (!global.pendingAdminFactoryResets || !global.pendingAdminFactoryResets[phoneNumber]) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Tidak ada permintaan factory reset yang pending untuk nomor ${phoneNumber}.`) }); return; } const { deviceId, timestamp } = global.pendingAdminFactoryResets[phoneNumber]; // Check if confirmation is still valid (within 5 minutes) if (Date.now() - timestamp > 5 * 60 * 1000) { await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Permintaan factory reset untuk ${phoneNumber} sudah expired. Silakan ulangi.`) }); delete global.pendingAdminFactoryResets[phoneNumber]; return; } // Send processing message await sock.sendMessage(remoteJid, { text: formatResponse(`🔄 Melakukan factory reset perangkat pelanggan ${phoneNumber}...\nMohon tunggu beberapa menit.`) }); // Factory reset device await genieacsApi.factoryReset(deviceId); // Send success message await sock.sendMessage(remoteJid, { text: formatResponse(`✅ Factory reset berhasil dilakukan pada perangkat pelanggan ${phoneNumber}.\nPerangkat akan restart dan kembali ke pengaturan default pabrik.`) }); // Delete factory reset confirmation status delete global.pendingAdminFactoryResets[phoneNumber]; } catch (error) { logger.error(`Error handling admin factory reset confirmation: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); // Delete factory reset confirmation status even if error if (global.pendingAdminFactoryResets && global.pendingAdminFactoryResets[phoneNumber]) { delete global.pendingAdminFactoryResets[phoneNumber]; } } } // Handler untuk melihat speed test / bandwidth async function handleSpeedTest(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get bandwidth/speed information const { getParameterWithPaths } = require('./whatsapp'); // Try to get bandwidth information from various paths const downloadSpeed = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.DownstreamMaxBitRate', 'Device.IP.Interface.1.Stats.BytesReceived' ]) || 'N/A'; const uploadSpeed = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.UpstreamMaxBitRate', 'Device.IP.Interface.1.Stats.BytesSent' ]) || 'N/A'; // Get interface statistics const bytesReceived = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.Stats.BytesReceived', 'Device.IP.Interface.1.Stats.BytesReceived' ]) || 'N/A'; const bytesSent = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.Stats.BytesSent', 'Device.IP.Interface.1.Stats.BytesSent' ]) || 'N/A'; let message = `📊 *INFORMASI BANDWIDTH*\n\n`; message += `📥 *Download Speed:* ${downloadSpeed}\n`; message += `📤 *Upload Speed:* ${uploadSpeed}\n\n`; message += `📈 *Statistik Interface:*\n`; message += `• Bytes Received: ${formatBytes(bytesReceived)}\n`; message += `• Bytes Sent: ${formatBytes(bytesSent)}\n\n`; message += `💡 *Tips:* Untuk speed test yang akurat, gunakan aplikasi speed test di perangkat Anda.`; await sock.sendMessage(remoteJid, { text: formatResponse(message) }); } catch (error) { logger.error(`Error handling speed test: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk diagnostik jaringan async function handleNetworkDiagnostic(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get diagnostic information const { getDeviceStatus, getParameterWithPaths, parameterPaths } = require('./whatsapp'); const lastInform = device._lastInform; const isOnline = getDeviceStatus(lastInform); const rxPower = getParameterWithPaths(device, parameterPaths.rxPower) || 'N/A'; const pppoeIP = getParameterWithPaths(device, parameterPaths.pppoeIP) || 'N/A'; const temperature = getParameterWithPaths(device, parameterPaths.temperature) || 'N/A'; // Get connection status const wanStatus = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ConnectionStatus', 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.ConnectionStatus' ]) || 'N/A'; // Get DNS servers const dnsServers = getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.DNSServers', 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.DNSServers' ]) || 'N/A'; let message = `🔧 *DIAGNOSTIK JARINGAN*\n\n`; // Status koneksi message += `🌐 *Status Koneksi:*\n`; message += `• Device: ${isOnline ? '🟢 Online' : '🔴 Offline'}\n`; message += `• WAN Status: ${wanStatus}\n`; message += `• PPPoE IP: ${pppoeIP}\n`; message += `• Last Inform: ${new Date(lastInform).toLocaleString()}\n\n`; // Signal quality message += `📶 *Kualitas Signal:*\n`; message += `• RX Power: ${rxPower} dBm\n`; const rxPowerNum = parseFloat(rxPower); if (!isNaN(rxPowerNum)) { if (rxPowerNum >= -25) { message += `• Status: 🟢 Excellent\n`; } else if (rxPowerNum >= -27) { message += `• Status: 🟡 Good\n`; } else if (rxPowerNum >= -30) { message += `• Status: 🟠 Fair\n`; } else { message += `• Status: 🔴 Poor\n`; } } message += `• Temperature: ${temperature}°C\n\n`; // Network settings message += `⚙️ *Pengaturan Jaringan:*\n`; message += `• DNS Servers: ${dnsServers}\n\n`; // Recommendations message += `💡 *Rekomendasi:*\n`; if (!isOnline) { message += `• Perangkat offline, coba restart perangkat\n`; } if (!isNaN(rxPowerNum) && rxPowerNum < -27) { message += `• Signal lemah, hubungi teknisi\n`; } if (!isNaN(parseFloat(temperature)) && parseFloat(temperature) > 70) { message += `• Temperature tinggi, pastikan ventilasi baik\n`; } await sock.sendMessage(remoteJid, { text: formatResponse(message) }); } catch (error) { logger.error(`Error handling network diagnostic: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Handler untuk melihat riwayat koneksi async function handleConnectionHistory(remoteJid, senderNumber) { if (!sock) { logger.error('Sock instance not set'); return; } try { // Find device by sender's phone number const device = await getDeviceByNumber(senderNumber); if (!device) { await sock.sendMessage(remoteJid, { text: formatResponse('Perangkat tidak ditemukan.') }); return; } // Get connection history information const { formatUptime, getParameterWithPaths, parameterPaths } = require('./whatsapp'); const deviceUptime = formatUptime(getParameterWithPaths(device, parameterPaths.uptime)) || 'N/A'; const pppUptime = formatUptime(getParameterWithPaths(device, [ 'InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.UpTime', 'Device.PPP.Interface.1.UpTime' ])) || 'N/A'; const lastInform = device._lastInform; const firstInform = device._registered || device._created || 'N/A'; let message = `📊 *RIWAYAT KONEKSI*\n\n`; message += `⏰ *Waktu Aktif:*\n`; message += `• Device Uptime: ${deviceUptime}\n`; message += `• PPPoE Uptime: ${pppUptime}\n\n`; message += `📅 *Riwayat:*\n`; message += `• First Registered: ${firstInform !== 'N/A' ? new Date(firstInform).toLocaleString() : 'N/A'}\n`; message += `• Last Inform: ${new Date(lastInform).toLocaleString()}\n\n`; // Calculate connection stability const now = Date.now(); const lastInformTime = new Date(lastInform).getTime(); const timeDiff = now - lastInformTime; message += `🔄 *Status Koneksi:*\n`; if (timeDiff < 5 * 60 * 1000) { // 5 minutes message += `• Status: 🟢 Stabil (Last inform ${Math.round(timeDiff / 1000)} detik lalu)\n`; } else if (timeDiff < 30 * 60 * 1000) { // 30 minutes message += `• Status: 🟡 Normal (Last inform ${Math.round(timeDiff / 60000)} menit lalu)\n`; } else { message += `• Status: 🔴 Bermasalah (Last inform ${Math.round(timeDiff / 60000)} menit lalu)\n`; } await sock.sendMessage(remoteJid, { text: formatResponse(message) }); } catch (error) { logger.error(`Error handling connection history: ${error.message}`); await sock.sendMessage(remoteJid, { text: formatResponse(`❌ Terjadi kesalahan: ${error.message}`) }); } } // Helper function untuk format bytes function formatBytes(bytes) { if (bytes === 'N/A' || !bytes) return 'N/A'; const num = parseInt(bytes); if (isNaN(num)) return bytes; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (num === 0) return '0 Bytes'; const i = Math.floor(Math.log(num) / Math.log(1024)); return Math.round(num / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; } module.exports = { setSock, handleWifiInfo, handleChangeWifiSSID, handleChangeWifiPassword, handleDeviceStatus, handleRestartDevice, handleRestartConfirmation, handleFactoryReset, handleFactoryResetConfirmation, handleConnectedDevices, handleAdminDeviceDetail, handleAdminRestartDevice, handleAdminFactoryReset, handleAdminFactoryResetConfirmation, handleSpeedTest, handleNetworkDiagnostic, handleConnectionHistory };