BotWeb / app.js
fikxzmodzz's picture
Update app.js
f3f44ed verified
Raw
History Blame Contribute Delete
18.4 kB
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 }