const express = require('express') const { default: makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion, makeCacheableSignalKeyStore, jidDecode, generateWAMessageFromContent, delay } = require("baileys") const pino = require('pino') const cors = require('cors') const path = require('path') const fs = require('fs') const os = require('os') const axios = require('axios') const chalk = require('chalk') const cookieParser = require('cookie-parser') const { exec } = require('child_process') const db = require('./src/database') const { AccessRequired, AccessSecret } = require('./middleware/ValidateKeys') const uploadToTmpFiles = require('./lib/TempUpld.js') const app = express() const Port = 7860 const SessionDir = 'SenderAuth' app.use(cors()) app.use(express.json()) app.use(express.urlencoded({ extended: true })) app.use(cookieParser()) app.use((req, res, next) => { res.on('finish', () => { const ignorePaths = ['/stats', '/', '/akses'] if (!ignorePaths.includes(req.path) && res.statusCode === 200) { db.addRequest(req.path) } }) next() }) const makeInMemoryStore = () => { const messages = {} return { messages, bind: (ev) => { ev.on('messages.upsert', (data) => { const { messages: newMessages } = data for (const msg of newMessages) { const jid = msg.key.remoteJid if (!messages[jid]) messages[jid] = [] messages[jid].push(msg) } }) }, loadMessage: async (jid, id) => { if (!messages[jid]) return undefined return messages[jid].find(m => m.key.id === id) } } } let sock let isConnected = false const store = makeInMemoryStore() async function startBot() { const { state, saveCreds } = await useMultiFileAuthState(SessionDir) const { version } = await fetchLatestBaileysVersion() sock = makeWASocket({ version, logger: pino({ level: "silent" }), printQRInTerminal: false, auth: { creds: state.creds, keys: makeCacheableSignalKeyStore(state.keys, pino({ level: "silent" }).child({ level: "fatal" })), }, browser: ["Ubuntu", "Chrome", "20.0.04"], markOnlineOnConnect: true, generateHighQualityLinkPreview: true, syncFullHistory: false, getMessage: async (key) => { if (store) { const msg = await store.loadMessage(key.remoteJid, key.id) return msg.message || undefined } return { conversation: "Hello, I'm Alokkk" } }, connectTimeoutMs: 60000, keepAliveIntervalMs: 30000, emitOwnEvents: true }) store.bind(sock.ev) sock.ev.on('creds.update', saveCreds) sock.ev.on("connection.update", async ({ connection, lastDisconnect }) => { if (connection === "close") { isConnected = false const reason = (lastDisconnect?.error)?.output?.statusCode if (reason === DisconnectReason.loggedOut) { if (fs.existsSync(SessionDir)) { fs.rmSync(SessionDir, { recursive: true, force: true }) } startBot() } else { setTimeout(startBot, 3000) } } else if (connection === "open") { isConnected = true } }) } function clearMemoryStore() { if (store) { if (store.messages) { const chats = Object.keys(store.messages) chats.forEach(id => { if (store.messages[id] && store.messages[id].length > 30) { store.messages[id] = store.messages[id].slice(-30) } }) } } setTimeout(clearMemoryStore, 60 * 1000) } startBot() clearMemoryStore() app.set('json spaces', 2) app.get('/akses', (req, res) => { if (req.cookies.authToken === AccessSecret) return res.redirect('/') res.sendFile(path.join(__dirname, 'page/akses.html')) }) app.post('/login', (req, res) => { const { secret } = req.body if (secret === AccessSecret) { res.cookie('authToken', AccessSecret, { httpOnly: true, maxAge: 24 * 60 * 60 * 1000 }) res.json({ status: true, message: 'Login berhasil' }) } else { res.status(401).json({ status: false, message: 'Secret salah' }) } }) app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'page/dashboard.html')) }) app.get('/broadcast', AccessRequired, (req, res) => { res.sendFile(path.join(__dirname, 'page/broadcast.html')) }) app.get('/tools', AccessRequired, (req, res) => { res.sendFile(path.join(__dirname, 'page/tools.html')) }) app.get('/settings', AccessRequired, (req, res) => { res.sendFile(path.join(__dirname, 'page/settings.html')) }) app.post('/pair', AccessRequired, async (req, res) => { if (isConnected) return res.status(400).json({ status: false, message: 'Bot sudah terhubung!' }) const { number } = req.body if (!number) return res.status(400).json({ status: false, message: 'Nomor diperlukan' }) try { if (!sock.authState.creds.registered) { const code = await sock.requestPairingCode(number.replace(/[^0-9]/g, "")) res.json({ status: true, code: code }) } else { res.status(400).json({ status: false, message: 'Sesi sudah terdaftar' }) } } catch (error) { res.status(500).json({ status: false, message: 'Gagal request pairing', error: error.message }) } }) app.post('/reset-session', AccessRequired, async (req, res) => { try { if (fs.existsSync(SessionDir)) { fs.rmSync(SessionDir, { recursive: true, force: true }) } res.json({ status: true, message: 'Sesi dihapus' }) process.exit(1) } catch (error) { res.status(500).json({ status: false, error: error.message }) } }) app.post('/sendpesan', AccessRequired, async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) const { number, teks } = req.body if (!number || !teks) return res.status(400).json({ status: false, message: 'Parameter salah' }) const cleaned = number.replace(/\D/g, '') const id = cleaned.includes('-') ? `${cleaned}@g.us` : `${cleaned}@s.whatsapp.net` try { await sock.sendPresenceUpdate('composing', id) await delay(Math.floor(Math.random() * 500) + 500) const waMessage = await generateWAMessageFromContent(id, { extendedTextMessage: { text: teks } }, { userJid: sock.user.id }) await sock.relayMessage(id, waMessage.message, { messageId: waMessage.key.id }) await sock.sendPresenceUpdate('paused', id) db.addSuccess() return res.json({ status: true, message: 'Pesan terkirim', data: { key: waMessage.key } }) } catch (err) { db.addFailed() return res.status(500).json({ status: false, error: err.message }) } }) app.post('/settings/name', AccessRequired, async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) try { await sock.updateProfileName(req.body.name) db.addSuccess() res.json({ status: true, message: 'Nama diubah' }) } catch (err) { db.addFailed() res.status(500).json({ status: false, error: err.message }) } }) app.post('/settings/bio', AccessRequired, async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) try { await sock.updateProfileStatus(req.body.bio) db.addSuccess() res.json({ status: true, message: 'Bio diubah' }) } catch (err) { db.addFailed() res.status(500).json({ status: false, error: err.message }) } }) app.post('/settings/photo', AccessRequired, async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) try { const userJid = sock.user.id ? jidDecode(sock.user.id).user + '@s.whatsapp.net' : null await sock.updateProfilePicture(userJid, { url: req.body.url }) db.addSuccess() res.json({ status: true, message: 'Foto diubah' }) } catch (err) { db.addFailed() res.status(500).json({ status: false, error: err.message }) } }) app.get('/stats', (req, res) => { const stats = db.load() const usedMemory = process.memoryUsage().rss / 1024 / 1024 const totalMemory = os.totalmem() / 1024 / 1024 res.json({ status: true, system: { uptime: process.uptime(), ram_usage: usedMemory.toFixed(2), total_mem: totalMemory.toFixed(2), platform: os.platform(), hostname: os.hostname(), arch: os.arch() }, wa: { connected: isConnected, user: sock?.user || null }, api: stats }) }) app.get('/cekidch', async (req, res) => { const url = req.query.url if (!sock) { return res.status(503).json({ status: false, message: "Bot sedang memuat" }) } if (!url) { return res.status(400).json({ status: false, message: "Parameter 'url' diperlukan" }) } if (!url.includes("whatsapp.com/channel/")) { return res.status(400).json({ status: false, message: "URL tidak valid" }) } try { const inviteCode = url.split('/channel/')[1].split('?')[0] const metadata = await sock.newsletterMetadata("invite", inviteCode) const subCount = metadata.subscribers?.toNumber ? metadata.subscribers.toNumber() : (metadata.subscribers || 0) let photo = metadata.preview || null try { const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } }) const match = data.match(/property="og:image" content="(.*?)"/) if (match && match[1]) photo = match[1].replace(/&/g, '&') } catch (e) {} const result = { status: true, data: { id: metadata.id, name: metadata.name, Pengikut: subCount, KreatorTime: metadata.creation_time, Deskripsi: metadata.description, State: metadata.state, Verified: metadata.verification === "VERIFIED" ? "Terverifikasi" : "Tidak", Photo: photo, Invite: metadata.invite, Reaction: metadata.reaction_codes } } db.addSuccess() res.json(result) } catch (error) { db.addFailed() res.status(500).json({ status: false, message: "Gagal mengambil metadata", error: error.message }) } }) app.get('/stalkwa', async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) const number = req.query.number if (!number) return res.status(400).json({ status: false, message: 'Parameter number diperlukan' }) const cleaned = number.replace(/\D/g, '') const id = `${cleaned}@s.whatsapp.net` try { const [onwa] = await sock.onWhatsApp(id) if (!onwa) return res.status(404).json({ status: false, message: 'Nomor tidak terdaftar di WA' }) let status = { status: 'Tidak ada bio/private' } try { status = await sock.fetchStatus(id) } catch (e) {} let ppUrl = null try { ppUrl = await sock.profilePictureUrl(id, 'image') } catch (e) {} let business = null try { const bizProfile = await sock.getBusinessProfile(id) if (bizProfile) { business = { is_business: true, description: bizProfile.description, category: bizProfile.category, email: bizProfile.email, website: bizProfile.website, address: bizProfile.address } } } catch (e) { business = { is_business: false } } db.addSuccess() res.json({ status: true, data: { jid: id, exists: true, bio: status.status || '-', setAt: status.setAt || null, photo: ppUrl || 'https://i.ibb.co/Tq7d7CY/default-profile.png', business_info: business } }) } catch (err) { db.addFailed() res.status(500).json({ status: false, error: err.message }) } }) app.post('/spampairing', AccessRequired, async (req, res) => { const { number, amount } = req.body if (!number) return res.status(400).json({ status: false, message: 'Parameter number diperlukan' }) const target = number.replace(/[^0-9]/g, '').trim() const loopAmount = amount ? parseInt(amount) : 50 const tempSessionId = `TempSpam_${Date.now()}` try { const { state, saveCreds } = await useMultiFileAuthState(tempSessionId) const { version } = await fetchLatestBaileysVersion() const spamSock = makeWASocket({ auth: state, version, logger: pino({ level: 'silent' }), printQRInTerminal: false }) spamSock.ev.on('creds.update', saveCreds) await delay(1500) for (let i = 0; i < loopAmount; i++) { await delay(Math.floor(Math.random() * 1500) + 1000) try { await spamSock.requestPairingCode(target) } catch (error) { } } await delay(2000) if (fs.existsSync(tempSessionId)) { fs.rmSync(tempSessionId, { recursive: true, force: true }) } db.addSuccess() res.json({ status: true, message: `Sukses spam ${loopAmount} pairing code ke ${target}` }) } catch (error) { if (fs.existsSync(tempSessionId)) { fs.rmSync(tempSessionId, { recursive: true, force: true }) } db.addFailed() res.status(500).json({ status: false, message: 'Gagal eksekusi', error: error.message }) } }) app.get('/playch', AccessRequired, async (req, res) => { if (!sock || !isConnected) return res.status(503).json({ status: false, message: 'Bot belum terhubung' }) const text = req.query.query if (!text) return res.status(400).json({ status: false, message: 'Parameter query diperlukan' }) const playChId = '120363403303841726@newsletter' const newsletterInfo = { newsletterJid: playChId, serverMessageId: 20, newsletterName: 'Play Music' } try { const api = `https://api.elrayyxml.web.id/api/downloader/ytplay?q=${encodeURIComponent(text)}` const response = await axios.get(api) const data = response.data if (!data?.result) return res.status(404).json({ status: false, message: 'Tidak ditemukan hasil melalui API.' }) const info = data.result const title = info.title const thumbnail = info.thumbnail const youtubeUrl = info.url const downloadUrl = info.download_url const audioReq = await axios.get(downloadUrl, { responseType: "arraybuffer", headers: { "User-Agent": "Mozilla/5.0" } }) const tempInput = path.join(os.tmpdir(), `${Date.now()}_${Math.random().toString(36).substring(7)}.mp3`) const tempOutput = path.join(os.tmpdir(), `${Date.now()}_${Math.random().toString(36).substring(7)}.opus`) fs.writeFileSync(tempInput, Buffer.from(audioReq.data)) await new Promise((resolve, reject) => { exec(`ffmpeg -i "${tempInput}" -c:a libopus -b:a 128k -vbr on -compression_level 10 "${tempOutput}"`, (error) => { if (error) reject(error) else resolve() }) }) const opusBuffer = fs.readFileSync(tempOutput) let thumbnailBuffer = null if (thumbnail) { try { const thumbReq = await axios.get(thumbnail, { responseType: "arraybuffer" }) thumbnailBuffer = Buffer.from(thumbReq.data) if (thumbnailBuffer.length < 5000) thumbnailBuffer = null } catch {} } await sock.sendMessage(playChId, { audio: opusBuffer, mimetype: "audio/ogg; codecs=opus", ptt: true, contextInfo: { forwardingScore: 999, isForwarded: true, forwardedNewsletterMessageInfo: newsletterInfo, externalAdReply: { title: title, body: info.channel || "YouTube", thumbnail: thumbnailBuffer, mediaType: 2, renderLargerThumbnail: true, sourceUrl: youtubeUrl } } }) if (fs.existsSync(tempInput)) fs.unlinkSync(tempInput) if (fs.existsSync(tempOutput)) fs.unlinkSync(tempOutput) db.addSuccess() res.json({ status: true, message: `Berhasil mengirim lagu "${title}" ke channel sebagai VN.` }) } catch (e) { console.error("PLAYCH ERROR:", e.message) db.addFailed() res.status(500).json({ status: false, message: `Terjadi kesalahan: ${e.message}` }) } }) app.use((req, res) => { res.status(404).sendFile(path.join(__dirname, 'page/Redirect/404.html')) }) let file = require.resolve(__filename) fs.watchFile(file, () => { fs.unwatchFile(file) console.log(chalk.white("• Update"), chalk.white(`${__filename}\n`)) delete require.cache[file] require(file) }) module.exports = { app, Port }