const qrcode = require('qrcode-terminal'); const { Client, LocalAuth } = require('whatsapp-web.js'); const admin = require('firebase-admin'); const inquirer = require('inquirer').default; const fs = require('fs'); const path = require('path'); admin.initializeApp({ credential: admin.credential.cert(require('./serviceAccountKey.json')), databaseURL: "databaseURL" }); const db = admin.database(); let senderNumber = null; let recipientNumbers = []; let lastMessagesSent = {}; function resetMessageFlag(message) { delete lastMessagesSent[message]; } async function loadNumbers() { try { const snapshot = await db.ref('/whatsappNumbers').once('value'); const data = snapshot.val() || {}; senderNumber = data.sender || null; recipientNumbers = data.recipients || []; console.log('✅ Data nomor berhasil dimuat dari Firebase'); } catch (error) { console.error('❌ Gagal memuat data nomor:', error); } } async function saveNumbers() { try { await db.ref('/whatsappNumbers').set({ sender: senderNumber, recipients: recipientNumbers }); console.log('✅ Data nomor berhasil disimpan ke Firebase'); } catch (error) { console.error('❌ Gagal menyimpan data nomor:', error); } } function clearWhatsAppSession() { const sessionPath = path.join(__dirname, '.wwebjs_auth'); if (fs.existsSync(sessionPath)) { fs.rmSync(sessionPath, { recursive: true, force: true }); console.log('✅ Sesi WhatsApp lama dihapus.'); } } function createNewClient() { return new Client({ authStrategy: new LocalAuth(), puppeteer: { headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] } }); } const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function broadcastWhatsApp(client, message) { if (lastMessagesSent[message]) return; for (const num of recipientNumbers) { try { const start = Date.now(); await client.sendMessage(num, message); const timeTaken = ((Date.now() - start) / 1000).toFixed(2); console.log(`✅ Pesan terkirim ke ${num} dalam ${timeTaken} detik`); console.log(` ➤ Isi Pesan: ${message}`); await delay(1000); } catch (e) { console.error(`❌ Gagal kirim ke ${num}:`, e.message || e); } } lastMessagesSent[message] = true; } async function startMonitoring() { if (!senderNumber || recipientNumbers.length === 0) { console.log('\n❗ Pengirim atau penerima belum diatur.'); return; } console.log('\n⏳ Menunggu koneksi WhatsApp...'); const client = createNewClient(); client.on('ready', () => { console.log('✅ WhatsApp Client siap.'); const notifRef = db.ref('/notif'); const pompaRef = db.ref('/pompa/status'); const sensorApiRef = db.ref('/sensor/api'); const sensorAsapRef = db.ref('/sensor/asap'); const sensorTanahRef = db.ref('/sensor/tanah'); let lastStates = { notif_kebakaran: '', notif_tanah: '', notif_pompa_off: false, kebakaran_teratasi: false, pompaStatus: '', apiStatus: '', kelembabanTanah: '', }; let lastAsapApiNotif = ''; let pompaOnWithFireStart = null; let problemPompaSent = false; async function handleKebakaran(kebakaranStatus) { const fireMsg = "🔥 Kebakaran Terdeteksi! Segera ambil tindakan!"; const apiClearMsg = "✅ Api berhasil dipadamkan, Kondisi aman."; const prevKebakaran = lastStates.notif_kebakaran; lastStates.notif_kebakaran = kebakaranStatus; if (prevKebakaran === "iya" && kebakaranStatus === "tidak") { await broadcastWhatsApp(client, apiClearMsg); lastStates.kebakaran_teratasi = true; } if (kebakaranStatus === "iya" && prevKebakaran !== "iya") { await broadcastWhatsApp(client, fireMsg); resetMessageFlag(apiClearMsg); lastStates.kebakaran_teratasi = false; } if (kebakaranStatus !== "iya") { resetMessageFlag(fireMsg); } } async function handleTanah(tanahStatus) { const kelembaban = lastStates.kelembabanTanah; const persen = kelembaban != null && !isNaN(kelembaban) ? ` dengan kelembaban ${kelembaban}%` : ''; const kering = `💧 Tanah terlalu kering${persen}, Segera lakukan irigasi.`; const basah = `🌊 Tanah terlalu basah${persen}, Segera lakukan drainase.`; const ideal = `✅ Kelembaban tanah sudah ideal${persen}.`; if (tanahStatus !== lastStates.notif_tanah) { if (tanahStatus === "kering") { await broadcastWhatsApp(client, kering); } else if (tanahStatus === "basah") { await broadcastWhatsApp(client, basah); } else if (tanahStatus === "ideal") { await broadcastWhatsApp(client, ideal); } else { resetMessageFlag(kering); resetMessageFlag(basah); resetMessageFlag(ideal); } lastStates.notif_tanah = tanahStatus; } } async function handlePompa() { const apiClearMsg = "✅ Api berhasil dipadamkan, Kondisi aman."; const problemPompaMsg = "⚠️ Terjadi masalah pada pompa, Segera lakukan penanganan manual!"; const { notif_kebakaran, pompaStatus, apiStatus } = lastStates; const transisiApiPadam = lastStates.prev_kebakaran === "iya" && notif_kebakaran === "tidak"; const kondisiApiBenarBenarPadam = apiStatus === "tidak"; if (transisiApiPadam && kondisiApiBenarBenarPadam && !lastStates.kebakaran_teratasi) { await broadcastWhatsApp(client, apiClearMsg); lastStates.kebakaran_teratasi = true; } if (notif_kebakaran === "iya" && pompaStatus === "on") { if (!pompaOnWithFireStart) { pompaOnWithFireStart = Date.now(); problemPompaSent = false; console.log("⏱️ Mulai hitung durasi masalah pompa..."); } else { const duration = (Date.now() - pompaOnWithFireStart) / 1000; if (duration >= 3) { if (!problemPompaSent) { problemPompaSent = true; await broadcastWhatsApp(client, problemPompaMsg); } } } } else { if (pompaOnWithFireStart) { console.log("🔁 Reset durasi masalah pompa (status berubah)"); } pompaOnWithFireStart = null; problemPompaSent = false; resetMessageFlag(problemPompaMsg); } lastStates.prev_kebakaran = notif_kebakaran; } async function handleAsapApi(asapStatus, apiStatus) { const asapMsg = "🌫️ Terdeteksi asap berlebihan pada lahan, Namun tidak berpotensi kebakaran."; const asapApiMsg = "🔥🌫️ Terdeteksi asap dan api pada lahan, Namun tidak berpotensi kebakaran."; const key = `${asapStatus}_${apiStatus}`; if (lastAsapApiNotif !== key) { if (asapStatus === "iya" && apiStatus !== "iya") { await broadcastWhatsApp(client, asapMsg); } else if (asapStatus === "iya" && apiStatus === "iya") { await broadcastWhatsApp(client, asapApiMsg); } else { resetMessageFlag(asapMsg); resetMessageFlag(asapApiMsg); } lastAsapApiNotif = key; } } notifRef.on('value', async snapshot => { const notif = snapshot.val() || {}; await handleKebakaran(notif.kebakaran); await handlePompa(); }); pompaRef.on('value', async snapshot => { lastStates.pompaStatus = snapshot.val() || "off"; await handlePompa(); }); sensorApiRef.on('value', async snapshot => { lastStates.apiStatus = (snapshot.val() || "").trim().toLowerCase(); await handlePompa(); const asapSnapshot = await sensorAsapRef.once('value'); const asapStatus = (asapSnapshot.val() || "").trim().toLowerCase(); await handleAsapApi(asapStatus, lastStates.apiStatus); }); sensorAsapRef.on('value', async snapshot => { const asapStatus = (snapshot.val() || "").trim().toLowerCase(); const apiStatus = lastStates.apiStatus; await handleAsapApi(asapStatus, apiStatus); }); sensorTanahRef.on('value', async snapshot => { const value = snapshot.val(); const kelembaban = typeof value === 'number' ? value : parseFloat(value); lastStates.kelembabanTanah = kelembaban; let tanahStatus = ''; if (kelembaban <= 50) { tanahStatus = 'kering'; } else if (kelembaban >= 70) { tanahStatus = 'basah'; } else { tanahStatus = 'ideal'; } await handleTanah(tanahStatus); }); setInterval(async () => { await handlePompa(); }, 1000); }); client.initialize(); } async function showMainMenu() { while (true) { const { action } = await inquirer.prompt({ type: 'list', name: 'action', message: 'Menu Utama:', choices: [ { name: 'Tambah Nomor Pengirim', value: 'addSender' }, { name: 'Lihat Nomor Pengirim', value: 'viewSender' }, { name: 'Hapus Nomor Pengirim', value: 'deleteSender' }, { name: 'Tambah Nomor Penerima', value: 'addRecipient' }, { name: 'Lihat Nomor Penerima', value: 'viewRecipients' }, { name: 'Hapus Nomor Penerima', value: 'deleteRecipients' }, { name: 'Mulai Monitoring', value: 'startMonitoring' }, { name: 'Keluar', value: 'exit' } ] }); switch (action) { case 'addSender': await addSenderNumber(); break; case 'viewSender': viewSenderNumber(); break; case 'deleteSender': await deleteSenderNumber(); break; case 'addRecipient': await addRecipientNumber(); break; case 'viewRecipients': viewRecipientNumbers(); break; case 'deleteRecipients': await deleteRecipientNumbers(); break; case 'startMonitoring': await startMonitoring(); return; case 'exit': process.exit(0); } } } async function addSenderNumber() { if (senderNumber) { const { confirm } = await inquirer.prompt({ type: 'confirm', name: 'confirm', message: 'Sudah ada nomor pengirim. Tambah baru akan hapus yang lama. Lanjutkan?', default: false }); if (!confirm) return; clearWhatsAppSession(); senderNumber = null; await saveNumbers(); } console.log('\nScan QR code berikut untuk menambahkan nomor pengirim:'); const tempClient = createNewClient(); return new Promise((resolve, reject) => { let isAuthenticated = false; tempClient.once('qr', (qr) => qrcode.generate(qr, { small: true })); tempClient.once('authenticated', () => { console.log('✅ Autentikasi berhasil!'); isAuthenticated = true; }); tempClient.once('ready', async () => { if (isAuthenticated) { try { senderNumber = tempClient.info.me._serialized; await saveNumbers(); console.log(`Nomor pengirim berhasil ditambahkan: ${senderNumber}`); await tempClient.destroy(); resolve(); } catch (err) { reject(err); } } }); tempClient.initialize(); }); } function viewSenderNumber() { console.log(senderNumber ? `\nNomor Pengirim: ${senderNumber}` : '\nBelum ada nomor pengirim.'); } async function deleteSenderNumber() { if (!senderNumber) return console.log('\nTidak ada nomor pengirim untuk dihapus.'); const { confirm } = await inquirer.prompt({ type: 'confirm', name: 'confirm', message: `Hapus nomor pengirim ${senderNumber}?`, default: false }); if (confirm) { senderNumber = null; await saveNumbers(); console.log('\nNomor pengirim dihapus.'); } } async function addRecipientNumber() { const { number } = await inquirer.prompt({ type: 'input', name: 'number', message: 'Masukkan nomor penerima (format: 628xxxx):', validate: input => input.startsWith('62') && input.length >= 10 ? true : 'Nomor tidak valid.' }); const formatted = `${number}@c.us`; if (recipientNumbers.includes(formatted)) return console.log('\nNomor sudah ada.'); recipientNumbers.push(formatted); await saveNumbers(); console.log(`\nNomor penerima ditambahkan: ${formatted}`); } function viewRecipientNumbers() { if (recipientNumbers.length === 0) return console.log('\nBelum ada nomor penerima.'); console.log('\nDaftar Nomor Penerima:'); recipientNumbers.forEach((num, i) => console.log(`${i + 1}. ${num}`)); } async function deleteRecipientNumbers() { if (recipientNumbers.length === 0) return console.log('\nTidak ada nomor untuk dihapus.'); const { choices } = await inquirer.prompt({ type: 'checkbox', name: 'choices', message: 'Pilih nomor untuk dihapus:', choices: recipientNumbers.map((num, i) => ({ name: `${i + 1}. ${num}`, value: num })) }); if (choices.length === 0) return; recipientNumbers = recipientNumbers.filter(num => !choices.includes(num)); await saveNumbers(); console.log('\nNomor berhasil dihapus.'); } (async function init() { await loadNumbers(); console.log('\n📋 Status Awal:'); console.log(` ➤ Nomor Pengirim: ${senderNumber || 'Belum ada'}`); console.log(` ➤ Jumlah Penerima: ${recipientNumbers.length}`); await showMainMenu(); })();