Aplikasi billing & manajemen ISP (fiber optic) — PWA pelanggan, portal teknisi & kolektor, admin panel lengkap, multi-vendor OLT, RADIUS, WhatsApp/Telegram, pembayaran QRIS. Kredit & lisensi (ISC): - Base kode & repo: alijayanet/billing-rtrw oleh Alijaya Net — terima kasih banyak atas base kode dan repo yang bermanfaat. - Add-on: GoWA, LibreSpeed/OpenSpeedTest, Uptime Kuma, notification-forwarder. - Dikembangkan menjadi OPEN-ISP oleh oxpoe — https://github.com/oxpoe/open-isp
1157 lines
43 KiB
JavaScript
1157 lines
43 KiB
JavaScript
// 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
|
|
};
|