Spaces:
Runtime error
Runtime error
| 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 } | |