const { makeWASocket, DisconnectReason, useMultiFileAuthState, Browsers } = require("@whiskeysockets/baileys"); const pino = require("pino"); const { atlantic, alerts } = require("./settings.js"); const AdmZip = require('adm-zip'); const inquirer = require("inquirer"); const fs = require("fs"); const cashbackEventPath = './Database/CashbackEvent.json'; const toMs = require("ms"); const QRCode = require("qrcode"); const scheduler = require('./scheduler.js'); const moment = require('moment-timezone'); require('moment/locale/id'); process.on("uncaughtException", (err) => {console.error("❌ Uncaught Exception:", err);}); process.on("unhandledRejection", (reason) => {console.error("❌ Unhandled Rejection:", reason);}); let qrLoopStatus = {}; const supportModePath = './Database/supportMode.json'; global.supportMode = {}; try { if (fs.existsSync(supportModePath)) { global.supportMode = JSON.parse(fs.readFileSync(supportModePath, 'utf-8')); } else { fs.writeFileSync(supportModePath, JSON.stringify({}, null, 2)); } } catch (e) { console.error("❌ Gagal memuat supportMode.json, menginisialisasi ulang:", e); global.supportMode = {}; fs.writeFileSync(supportModePath, JSON.stringify({}, null, 2)); } function saveSupportMode() { try { fs.writeFileSync(supportModePath, JSON.stringify(global.supportMode, null, 2)); } catch (e) { console.error("❌ Gagal menyimpan supportMode ke supportMode.json:", e); } } const klaimPath = './Database/Klaim.json'; if (!fs.existsSync(klaimPath)) { fs.writeFileSync(klaimPath, JSON.stringify([], null, 2)); } global.lastStockRequestTime = {}; const STOCK_REQUEST_COOLDOWN = 2000; function genTicket() { return Date.now().toString().slice(-7); } let useCode = { isTrue: true }; const transaksiHarianPath = "./Database/TransaksiHarian.json"; const dailyResetFile = "./Database/.last_reset_day.txt"; function getTodayDateWIB() { return moment().tz("Asia/Jakarta").format("YYYY-MM-DD"); } function resetIfNewDay() { const today = getTodayDateWIB(); let lastReset = ""; if (fs.existsSync(dailyResetFile)) { lastReset = fs.readFileSync(dailyResetFile, "utf-8"); } if (lastReset !== today) { fs.writeFileSync(transaksiHarianPath, "[]"); fs.writeFileSync(dailyResetFile, today); console.log(`Transaksi harian direset: ${today}`); } } let axios = require("axios"); async function createQris(path, qrString) { await QRCode.toFile(path, qrString, { margin: 2, scale: 10 }); return path; } function getGroupAdmins(participants) { let admins = []; for (let i of participants) { if (i.admin !== null) { admins.push(i.id); } } return admins; } function addSaldoSafe(userId, amount) { let users = JSON.parse(fs.readFileSync('./Database/User.json')); const idx = users.findIndex(u => u.id === userId); if (idx !== -1) { users[idx].saldo += amount; fs.writeFileSync('./Database/User.json', JSON.stringify(users, null, 3)); console.log(`[addSaldoSafe] Saldo user ${userId} berhasil ditambahkan ${amount}. File diupdate.`); } else { console.log(`[addSaldoSafe] User ${userId} tidak ditemukan di database.`); } } function minSaldoSafe(userId, amount) { console.log(`[minSaldoSafe] Memulai pengurangan saldo untuk user: ${userId}, jumlah: ${amount}`); let users = JSON.parse(fs.readFileSync('./Database/User.json')); const idx = users.findIndex(u => u.id === userId); if (idx !== -1) { console.log(`[minSaldoSafe] Saldo sebelum: ${users[idx].saldo}`); users[idx].saldo -= amount; console.log(`[minSaldoSafe] Saldo setelah: ${users[idx].saldo}`); fs.writeFileSync('./Database/User.json', JSON.stringify(users, null, 3)); console.log(`[minSaldoSafe] Saldo user ${userId} berhasil dikurangi ${amount}. File diupdate.`); } else { console.log(`[minSaldoSafe] User ${userId} tidak ditemukan di database.`); } } const cekSaldo = (userId, _dir) => { let position = null; Object.keys(_dir).forEach((x) => { if (_dir[x].id === userId) { position = x; } }); if (position !== null) { return _dir[position].saldo; } else { return 0; } }; function formatrupiah(nominal) { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', currencyDisplay: 'narrowSymbol', maximumFractionDigits: 0 }).format(nominal).replace(/\s+/g, ''); } const sleep = async (ms) => { return new Promise(resolve => setTimeout(resolve, ms)) }; const getCashbackEvent = () => { if (!fs.existsSync(cashbackEventPath)) return null; const data = JSON.parse(fs.readFileSync(cashbackEventPath)); return data; }; const isCashbackEligible = (user, jumlahtrx, kelipatan) => { return jumlahtrx > 0 && jumlahtrx % kelipatan === 0; }; const applyCashbackIfEligible = (userId, userData, tehtarik) => { const event = getCashbackEvent(); if (!event) return; const today = moment().format('YYYY-MM-DD'); if (today < event.mulai || today > event.selesai) return; console.log(`➡️ Mengecek kelipatan: ${userData.jumlahtransaksi} % ${event.kelipatan} = ${userData.jumlahtransaksi % event.kelipatan}`); if (isCashbackEligible(userData, userData.jumlahtransaksi, event.kelipatan)) { console.log(`🎯 User ${userId} eligible cashback! Menambah saldo...`); addSaldoSafe(userId, event.nominal); tehtarik.sendMessage(userId, { text: `🎉 Selamat! Kamu telah melakukan ${event.kelipatan} transaksi.\n💸 Cashback ${formatrupiah(event.nominal)} telah masuk ke saldo kamu.` }).then(() => { console.log('✅ Notifikasi cashback terkirim!'); }).catch(e => { console.log('❌ Gagal kirim notifikasi cashback:', e); }); } else { console.log(`⛔️ Belum memenuhi syarat cashback.`); } }; function scheduleBackup(sock) { const fs = require("fs"); const moment = require("moment-timezone"); const AdmZip = require("adm-zip"); const file = "./Database/Backup.json"; if (!fs.existsSync(file)) return; let { delay, next } = JSON.parse(fs.readFileSync(file)); const now = Date.now(); let remaining = Math.max(1000, next - now); if (remaining <= 1000) { next = now + delay * 1000; remaining = delay * 1000; fs.writeFileSync(file, JSON.stringify({ delay, next }, null, 2)); } console.log(`📅 Next backup in ${Math.floor(remaining / 1000)} detik`); setTimeout(() => { const path = `Backup-${moment().format("YYYYMMDD-HHmmss")}.zip`; const zip = new AdmZip(); zip.addLocalFolder("Database", "Database"); if (fs.existsSync(global.session)) { zip.addLocalFolder(global.session, global.session); } zip.writeZip(path); sock.sendMessage(global.nomerowner, { document: fs.readFileSync(path), fileName: path, mimetype: 'application/zip', caption: `📦 Backup otomatis - ${moment().tz("Asia/Jakarta").locale("id").format("dddd, D MMMM YYYY, HH:mm:ss")} WIB` }); fs.unlinkSync(path); if (global.customBackupInterval) { clearInterval(global.customBackupInterval); } const ulang = setInterval(() => { const path = `Backup-${moment().format("YYYYMMDD-HHmmss")}.zip`; const zip = new AdmZip(); zip.addLocalFolder("Database", "Database"); if (fs.existsSync(global.session)) { zip.addLocalFolder(global.session, global.session); } zip.writeZip(path); sock.sendMessage(global.nomerowner, { document: fs.readFileSync(path), fileName: path, mimetype: 'application/zip', caption: `📦 Backup otomatis - ${moment().tz("Asia/Jakarta").locale("id").format("dddd, D MMMM YYYY, HH:mm:ss")} WIB` }); fs.unlinkSync(path); const newNext = Date.now() + delay * 1000; fs.writeFileSync(file, JSON.stringify({ delay, next: newNext }, null, 2)); }, delay * 1000); global.customBackupInterval = ulang; const newNext = Date.now() + delay * 1000; fs.writeFileSync(file, JSON.stringify({ delay, next: newNext }, null, 2)); }, remaining); } async function startBot() { const { state, saveCreds } = await useMultiFileAuthState(global.session); const { downloadMediaMessage } = require("@whiskeysockets/baileys"); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const tehtarik = await makeWASocket({ logger: pino({ level: "fatal" }), auth: state, printQRInTerminal: !useCode.isTrue, defaultQueryTimeoutMs: undefined, keepAliveIntervalMs: 30000, browser: Browsers.macOS("Edge"), shouldSyncHistoryMessage: () => false, markOnlineOnConnect: true, syncFullHistory: false, generateHighQualityLinkPreview: true }); scheduler(tehtarik, saveSupportMode, scheduleBackup); if (useCode.isTrue && !tehtarik.authState.creds.me) { useCode = await inquirer.prompt([ { type: 'confirm', name: 'isTrue', message: 'Gunakan Pairing Code?', default: true } ]); if (useCode.isTrue) { const waNumber = await inquirer.prompt([ { type: "number", name: "res", message: "Masukkan Nomor Whatsapp Anda:" } ]); console.log(waNumber); const code = await tehtarik.requestPairingCode(waNumber.res); console.log(code); } else { useCode.isTrue = false; startBot(); } } tehtarik.ev.on("connection.update", ({ connection, lastDisconnect }) => { if (connection === "close") { const shouldReconnect = lastDisconnect.error?.output.statusCode !== DisconnectReason.loggedOut; if (shouldReconnect) { console.log("Reconnect"); startBot(); } else { console.log("❌ Bot logout. Exit supaya Pterodactyl restart."); process.exit(); } } if (connection === "open") { console.log("✅ Terhubung ke WhatsApp!"); } }); const voucherHistoryPath = './Database/VoucherHistory.json'; if (!fs.existsSync(voucherHistoryPath)) { fs.writeFileSync(voucherHistoryPath, "{}"); } function canRedeemVoucher(userId) { const history = JSON.parse(fs.readFileSync(voucherHistoryPath)); const today = moment().tz("Asia/Jakarta").format("YYYY-MM-DD"); if (!history[userId]) return true; if (!history[userId][today]) return true; return history[userId][today] < 1; } function recordRedeemVoucher(userId) { const history = JSON.parse(fs.readFileSync(voucherHistoryPath)); const today = moment().tz("Asia/Jakarta").format("YYYY-MM-DD"); if (!history[userId]) history[userId] = {}; if (!history[userId][today]) history[userId][today] = 0; history[userId][today] += 1; fs.writeFileSync(voucherHistoryPath, JSON.stringify(history, null, 2)); } tehtarik.ev.on("creds.update", saveCreds); tehtarik.ev.on("messages.upsert", async ({ messages }) => { const msg = messages[0]; const jid = msg.key.remoteJid; if (!msg.key.fromMe) { const chatType = Object.keys(msg.message)[0]; const chatText = msg.message.conversation || msg.message.extendedTextMessage?.text || (msg.message.imageMessage?.caption || msg.message.videoMessage?.caption) || 'Media Message'; const senderInfo = msg.pushName || 'Unknown'; const greenBackground = "\x1b[42m"; const BlueBackground = "\x1b[44m"; const blackText = "\x1b[30m"; const whiteText = "\x1b[37m"; const resetColor = "\x1b[0m"; console.log(`${greenBackground} ${blackText}Pesan dari ${senderInfo} ${BlueBackground} ${whiteText}${chatText} ${resetColor} `); } if (!jid.endsWith('@broadcast')) { await tehtarik.readMessages([msg.key]); } if (!msg.message) return; function replyText(text) { tehtarik.sendMessage(from, {text: text}, {quoted: msg}); } function replyImage(capt, url) { tehtarik.sendMessage(from, {image: {url: url}, caption: capt}, {quoted: msg}); } const type = Object.keys(msg.message)[0]; const chats = msg.message.interactiveResponseMessage ? JSON.parse(msg.message.interactiveResponseMessage.nativeFlowResponseMessage.paramsJson).id : (type === 'conversation' && msg.message.conversation) ? msg.message.conversation : (type === 'imageMessage') && msg.message.imageMessage.caption ? msg.message.imageMessage.caption : (type === 'videoMessage') && msg.message.videoMessage.caption ? msg.message.videoMessage.caption : (type === 'extendedTextMessage') && msg.message.extendedTextMessage.text ? msg.message.extendedTextMessage.text : (type === 'buttonsResponseMessage')&& msg.message.buttonsResponseMessage.selectedButtonId ? msg.message.buttonsResponseMessage.selectedButtonId : (type === 'templateButtonReplyMessage') && msg.message.templateButtonReplyMessage.selectedId ? msg.message.templateButtonReplyMessage.selectedId : (type === 'messageContextInfo') ? (msg.message.buttonsResponseMessage?.selectedButtonId || msg.message.listResponseMessage?.singleSelectReply.selectedRowId) : (type == 'listResponseMessage') && msg.message.listResponseMessage.singleSelectReply.selectedRowId ? msg.message.listResponseMessage.singleSelectReply.selectedRowId : ""; if (chats == undefined) { chats = ''; } const prefix = /^[°•π÷×¶∆£¢€¥®™✓_=|~!?#$%^&.+-,\/\\©^]/.test(chats) ? chats.match(/^[°•π÷×¶∆£¢€¥®™✓_=|~!?#$%^&.+-,\/\\©^]/gi) : '.'; const from = msg.key.remoteJid; const content = JSON.stringify(msg.message); const pushName = msg.pushName; const isGroup = from.endsWith('@g.us'); const sender = msg.key.fromMe ? (tehtarik.user.id.split(':')[0] + '@s.whatsapp.net' || tehtarik.user.id) : (msg.key.participant || from); function isRegistered(id) { let regist = false; for (const user of global.User) { if (user.id === id) { regist = true; break; } } if (!regist) { let Datauser = { id: id, saldo: 0, jumlahtransaksi: 0, pengeluaran: 0, createdAt: Date.now() }; global.User.push(Datauser); fs.writeFileSync("./Database/User.json", JSON.stringify(global.User, null, 3)); console.log(`✅ User baru terdaftar otomatis: ${id}`); regist = true; } return regist; } const senderNumber = sender.split('@')[0]; const fromMe = msg.key.fromMe; if (fromMe) return; const isImage = (type == 'imageMessage'); const isQuotedMsg = (type == 'extendedTextMessage'); const isOwner = [global.nomerowner, global.nomerbot].includes(sender) ? true : false; const maintenancePath = './Database/Maintenance.json'; if (!fs.existsSync(maintenancePath)) { fs.writeFileSync(maintenancePath, JSON.stringify({ aktif: false }, null, 2)); } const isQuotedImage = isQuotedMsg ? content.includes('imageMessage') : false; const command = chats.replace(prefix, "").trim().split(/ +/).shift().toLowerCase(); const maintenance = JSON.parse(fs.readFileSync(maintenancePath)); const blokirSaatMaintenance = [ 'menu', 'help', 'stok', 'saldo', 'profil', 'deposit', 'buynow', 'buy', 'order', 'manual', 'qris', 'klaim', 'garansi', 'admin', 'ingatkan', 'cekcashback', 'redeem', 'riwayat', 'transaksi', 'stock', 'produk', 'list' ]; if (maintenance.aktif && !isOwner && blokirSaatMaintenance.includes(command)) { return await replyText("🚧 Maaf, bot sedang dalam mode *pemeliharaan*. Beberapa fitur pelanggan sementara tidak tersedia."); } const args = chats.trim().split(/ +/).slice(1); const text = args.join(" "); const groupMetadata = isGroup ? await tehtarik.groupMetadata(from) : ''; const groupName = isGroup ? groupMetadata.subject : ''; const groupId = isGroup ? groupMetadata.id : ''; const participants = isGroup ? await groupMetadata.participants : ''; const groupMembers = isGroup ? groupMetadata.participants : ''; const groupAdmins = isGroup ? getGroupAdmins(groupMembers) : ''; const isBotGroupAdmins = groupAdmins.includes(global.nomerbot) || false; const isGroupAdmins = groupAdmins.includes(sender); const mentionByTag = type == "extendedTextMessage" && msg.message.extendedTextMessage.contextInfo != null ? msg.message.extendedTextMessage.contextInfo.mentionedJid : []; const mentionByReply = type == "extendedTextMessage" && msg.message.extendedTextMessage.contextInfo != null ? msg.message.extendedTextMessage.contextInfo.participant || "" : ""; const mention = typeof(mentionByTag) == 'string' ? [mentionByTag] : mentionByTag; if (mention != undefined) { mention.push(mentionByReply); } const mentionUser = mention != undefined ? mention.filter(n => n) : []; function digit(nominal = 0) { return Math.ceil(nominal * 0.007); } const kategoriCache = {}; function loadKategoriCache() { kategoriCache.list = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); kategoriCache.data = {}; kategoriCache.list.forEach((file, idx) => { const fullPath = "./Database/Kategori/" + file; kategoriCache.data[idx] = JSON.parse(fs.readFileSync(fullPath)); }); } loadKategoriCache(); function getKategori(chat, kodeproduk) { if (!kategoriCache.list || !kategoriCache.data) loadKategoriCache(); for (const [idx, file] of kategoriCache.list.entries()) { const data = kategoriCache.data[idx]; for (const key in data) { if (data[key].kodeproduk.toLowerCase() === kodeproduk.toLowerCase()) { return file; } } } return "Kategori tidak ditemukan!"; } function isExistProduk(kategoriIndex, kodeproduk) { if (!kategoriCache.list || !kategoriCache.data) loadKategoriCache(); const data = kategoriCache.data[kategoriIndex]; if (!data) return false; for (const key in data) { if (data[key].kodeproduk.toLowerCase() === kodeproduk.toLowerCase()) { return true; } } return false; } if (isOwner && command === 'reply') { const [ticket, ...pesanParts] = args; const replyMsg = pesanParts.join(" ").trim(); if (!ticket || !replyMsg) { return await replyText("Format salah!\nContoh: reply 12345 halo kenapa?"); } const entry = Object.entries(global.supportMode).find( ([userId, data]) => data.ticket === ticket && data.active ); if (!entry) { return await replyText(`❌ Ticket *${ticket}* tidak ditemukan atau sudah ditutup.`); } const [userJid] = entry; const isImageMsg = !!msg.message.imageMessage; const isVideoMsg = !!msg.message.videoMessage; let buffer = null; let messageObj = { text: `💬 *Admin [${ticket}]:* ${replyMsg}` }; if (isImageMsg || isVideoMsg) { buffer = await downloadMediaMessage(msg, 'buffer', {}, { reuploadRequest: tehtarik }); if (isImageMsg) { messageObj = { image: buffer, caption: `💬 *Admin [${ticket}]:* ${replyMsg}` }; } else if (isVideoMsg) { messageObj = { video: buffer, caption: `💬 *Admin [${ticket}]:* ${replyMsg}` }; } } await tehtarik.sendMessage(userJid, messageObj); return await replyText(`✅ Balasan terkirim ke ${userJid.split('@')[0]}.`); } if ( !msg.key.fromMe && global.supportMode[sender]?.active && command !== 'klaim' ) { global.supportMode[sender].lastActivity = Date.now(); const ticket = global.supportMode[sender].ticket; const tagUser = sender.split('@')[0]; const prefix = `🎫 [${ticket}] LiveChat dari *${pushName}* (${tagUser}):`; const isImageMsg = !!msg.message.imageMessage; const isVideoMsg = !!msg.message.videoMessage; if (isImageMsg || isVideoMsg) { const buffer = await downloadMediaMessage( msg, 'buffer', {}, { reuploadRequest: tehtarik } ); const userCaption = (isImageMsg ? msg.message.imageMessage.caption : msg.message.videoMessage.caption) || ''; const mediaMsg = isImageMsg ? { image: buffer, caption: `${prefix}\n${userCaption}` } : { video: buffer, caption: `${prefix}\n${userCaption}` }; await tehtarik.sendMessage(global.nomerowner, mediaMsg); } else { const teks = msg.message.conversation || msg.message.extendedTextMessage?.text || ''; await tehtarik.sendMessage(global.nomerowner, { text: `${prefix}\n${teks}` }); } return await replyText(`✅ Pesanmu (${ticket}) sudah dikirim ke admin. Harap tunggu maks 1x24 Jam.`); } let customBackupInterval = null; let petik = '`'; const chokidar = require("chokidar"); function clearRequireCache(modulePath) { delete require.cache[require.resolve(modulePath)]; } chokidar.watch('./settings.js').on('change', () => { console.log('[UPDATE] settings.js updated! Reloading...'); clearRequireCache('./settings.js'); global.settings = require('./settings.js'); }); chokidar.watch('./server.js').on('change', () => { console.log('[UPDATE] server.js updated! Reloading...'); console.log("⚠️ Perubahan di server.js terdeteksi. Silakan restart bot secara manual untuk menerapkan perubahan."); }); function findNearestKategori(input, kategoriList) { input = input.toLowerCase(); let bestMatch = null; let lowestDistance = Infinity; for (const name of kategoriList) { const nameLower = name.toLowerCase(); const distance = levenshtein(input, nameLower); const threshold = Math.floor(nameLower.length * 0.5); if (distance <= threshold && distance < lowestDistance) { lowestDistance = distance; bestMatch = name; } } return bestMatch; } function levenshtein(a, b) { const matrix = Array.from({ length: b.length + 1 }, (_, i) => Array.from({ length: a.length + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)) ); for (let i = 1; i <= b.length; i++) { for (let j = 1; j <= a.length; j++) { const cost = a[j - 1] === b[i - 1] ? 0 : 1; matrix[i][j] = Math.min( matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost ); } } return matrix[b.length][a.length]; } switch (command) { case 'cekip': { if (!isOwner) return await replyText("❌ Khusus owner!"); try { const response = await axios.get('https://api.ipify.org?format=json'); const ipAddress = response.data.ip; await replyText(`🌐 Alamat IP Publik Bot: *${ipAddress}*`); } catch (error) { console.error("❌ Gagal mendapatkan IP publik:", error.message); await replyText("❌ Gagal mendapatkan alamat IP publik bot. Silakan coba lagi nanti."); } break; } case 'setbackup': { if (!isOwner) return await replyText("❌ Khusus owner!"); const input = text.toLowerCase().trim(); const match = input.match(/^(\d+)(h|d)$/); if (!match) { return await replyText("❌ Format salah!\nContoh: `setbackup 2h` atau `setbackup 1d`"); } const [_, angka, satuan] = match; const intervalMs = satuan === "h" ? parseInt(angka) * 60 * 60 * 1000 : parseInt(angka) * 24 * 60 * 60 * 1000; const next = Date.now() + intervalMs; const data = { delay: intervalMs / 1000, next }; fs.writeFileSync("./Database/Backup.json", JSON.stringify(data, null, 2)); if (global.customBackupInterval) { clearInterval(global.customBackupInterval); global.customBackupInterval = null; } scheduleBackup(tehtarik); await replyText(`✅ Backup otomatis akan dilakukan setiap *${angka}${satuan === 'h' ? ' jam' : ' hari'}*`); break; } case 'garansi': { if (!isRegistered(sender)) return await replyText(alerts.daftar); if (global.supportMode[sender]?.active) { return await replyText('❌ Anda sedang dalam sesi LiveChat. Silakan kirim keluhan Anda di sana atau ketik `klaim off` untuk mengakhiri sesi.'); } const uniqueId = args[0]; const alasan = args.slice(1).join(" ").trim(); if (!uniqueId || !alasan) { return await replyText('Format salah. Contoh:\ngaransi MZS123456 akun mamamia@1.me backfree'); } const Transaksi = JSON.parse(fs.readFileSync("./Database/Transaksi.json")); const trxData = Transaksi.find(trx => trx.unique === uniqueId && trx.buyer === sender); if (!trxData) { return await replyText(`❌ Transaksi dengan Order ID *${uniqueId}* tidak ditemukan di riwayat Anda.`); } let klaimData = JSON.parse(fs.readFileSync(klaimPath)); if (klaimData.some(k => k.unique === uniqueId)) { return await replyText(`⚠️ Klaim untuk Order ID *${uniqueId}* sudah ada. Mohon tunggu balasan dari admin.`); } const messageTimestamp = moment.unix(msg.messageTimestamp).tz("Asia/Jakarta").format('YYYY-MM-DD HH:mm:ss'); const waktuBeli = moment(trxData.tanggal).format('YYYY-MM-DD'); let isImageMsg = isImage || isQuotedImage; let imageBuffer = null; if (isImageMsg) { imageBuffer = await downloadMediaMessage(msg, 'buffer', {}, { reuploadRequest: tehtarik }); } const tagUser = sender.split('@')[0]; const ownerJid = global.nomerowner; const tanggalBeli = moment(trxData.tanggal).tz("Asia/Jakarta").locale("id").format("YYYY-MM-DD"); let pesanOwner = `🛑 *Klaim Garansi/Refund*\n`; pesanOwner += `\n*Kode:* ${uniqueId}`; pesanOwner += `\n*Waktu Klaim:* ${messageTimestamp}`; pesanOwner += `\n*User:* ${pushName} (${tagUser})`; pesanOwner += `\n*Produk:* ${trxData.produk}`; pesanOwner += `\n*Tanggal beli:* ${tanggalBeli}`; pesanOwner += `\n*Harga:* ${formatrupiah(trxData.harga)}`; pesanOwner += `\n\n*Alasan:* ${alasan}`; if (imageBuffer) { await tehtarik.sendMessage(ownerJid, { image: imageBuffer, caption: pesanOwner }); } else { await tehtarik.sendMessage(ownerJid, { text: pesanOwner }); } klaimData.push({ unique: uniqueId, buyer: sender, produk: trxData.produk, harga: trxData.harga, tanggalBeli: trxData.tanggal, tanggalKlaim: messageTimestamp, alasan: alasan, status: 'pending' }); fs.writeFileSync(klaimPath, JSON.stringify(klaimData, null, 2)); await replyText(`✅ Klaim garansi Anda untuk Order ID *${uniqueId}* sudah kami teruskan ke Admin. Mohon tunggu balasan dari kami.`); break; } case 'acc': { if (!isOwner) return await replyText("❌ Khusus owner!"); const uniqueId = args[0]; if (!uniqueId) return await replyText("Format salah! Contoh: `acc MZS123456`"); let klaimData = JSON.parse(fs.readFileSync(klaimPath)); const klaimIndex = klaimData.findIndex(k => k.unique === uniqueId); if (klaimIndex === -1) { return await replyText(`❌ Klaim dengan Order ID *${uniqueId}* tidak ditemukan di database klaim.`); } const klaim = klaimData[klaimIndex]; const buyerJid = klaim.buyer; await tehtarik.sendMessage(buyerJid, { text: `✅ Klaim dengan kode *${uniqueId}* untuk produk *${klaim.produk}* telah disetujui oleh admin. Silakan tunggu data pengganti dikirim maks 24 jam.` }); await replyText(`✅ Klaim untuk Order ID *${uniqueId}* telah disetujui dan notifikasi sudah dikirim ke pembeli. Klaim ini *belum dihapus* dari database. Kirim data pengganti menggunakan perintah \`sendata\` untuk menyelesaikannya.`); break; } case 'tolak': { if (!isOwner) return await replyText("❌ Khusus owner!"); const uniqueId = args[0]; const alasan = args.slice(1).join(" ").trim(); if (!uniqueId) { return await replyText("Format salah! Contoh: `tolak MZS123456` atau `tolak MZS123456 Barang sudah dipakai`"); } let klaimData = JSON.parse(fs.readFileSync(klaimPath)); const klaimIndex = klaimData.findIndex(k => k.unique === uniqueId); if (klaimIndex === -1) { return await replyText(`❌ Klaim dengan Order ID *${uniqueId}* tidak ditemukan di database klaim.`); } const klaim = klaimData[klaimIndex]; const buyerJid = klaim.buyer; const waktuKlaim = klaim.tanggalKlaim; const tanggalBeli = moment(klaim.tanggalBeli).tz("Asia/Jakarta").locale("id").format("YYYY-MM-DD"); let pesanBuyer = `🔴 *KLAIM GARANSI/REFUND DITOLAK*\n\n`; pesanBuyer += `🆔 *Order ID:* ${klaim.unique}\n`; pesanBuyer += `⏰ *Waktu Klaim:* ${waktuKlaim}\n`; pesanBuyer += `👤 *User:* ${buyerJid.split('@')[0]}\n`; pesanBuyer += `📦 *Produk:* ${klaim.produk}\n`; pesanBuyer += `🗓️ *Tanggal beli:* ${tanggalBeli}\n`; pesanBuyer += `💬 *Alasan:* ${alasan || '-'}`; await tehtarik.sendMessage(buyerJid, { text: pesanBuyer }); klaimData.splice(klaimIndex, 1); fs.writeFileSync(klaimPath, JSON.stringify(klaimData, null, 2)); await replyText(`✅ Klaim untuk Order ID *${uniqueId}* telah ditolak dan notifikasi sudah dikirim ke pembeli. Data klaim telah dihapus.`); break; } case 'sendata': { if (!isOwner) return await replyText("❌ Khusus owner!"); const [uniqueId, kodeproduk, jumlahStr, ...deskripsiParts] = text.split(" "); const deskripsi = deskripsiParts.join(" "); if (!uniqueId || !kodeproduk || !jumlahStr) { return await replyText("Format salah! Contoh: `sendata MZS123456 canvaown 1` atau `sendata MZS123456 canvaown 1 Akun pengganti`"); } const jumlah = parseInt(jumlahStr); if (isNaN(jumlah) || jumlah < 1) { return await replyText("❌ Jumlah harus berupa angka dan minimal 1."); } const Transaksi = JSON.parse(fs.readFileSync("./Database/Transaksi.json")); const trxData = Transaksi.find(trx => trx.unique === uniqueId); if (!trxData) { return await replyText(`❌ Transaksi dengan Order ID *${uniqueId}* tidak ditemukan.`); } let klaimData = JSON.parse(fs.readFileSync(klaimPath)); const klaimIndex = klaimData.findIndex(k => k.unique === uniqueId); if (klaimIndex === -1) { return await replyText(`⚠️ Order ID *${uniqueId}* tidak ditemukan atau sudah ditangani.`); } const klaim = klaimData[klaimIndex]; const waktuKlaim = klaim.tanggalKlaim; let kategoriFiles = fs.readdirSync("./Database/Kategori/").filter(f => f.endsWith(".json")); let produkDitemukan = null; let kategoriPath = null; for (let file of kategoriFiles) { const path = `./Database/Kategori/${file}`; const data = JSON.parse(fs.readFileSync(path)); const idx = data.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { produkDitemukan = data[idx]; kategoriPath = path; break; } } if (!produkDitemukan) { return await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan di semua kategori.`); } if (produkDitemukan.tipeproduk.toLowerCase() !== 'data') { return await replyText(`❌ Produk *${produkDitemukan.namaproduk}* bukan tipe produk 'Data'. Fitur ini hanya untuk produk bertipe 'Data'.`); } if (produkDitemukan.dataproduk.length < jumlah) { return await replyText(`❌ Stok produk *${produkDitemukan.namaproduk}* tidak cukup. Stok tersedia: ${produkDitemukan.dataproduk.length}`); } const dataProdukDikirim = produkDitemukan.dataproduk.splice(0, jumlah); fs.writeFileSync(kategoriPath, JSON.stringify(JSON.parse(fs.readFileSync(kategoriPath)), null, 2)); let dataProdukFormatted = ""; dataProdukDikirim.forEach(data => { let [mail, pass, key, profil, pin] = data.split(":"); dataProdukFormatted += `- Email : ${mail || '-'}\n- Pass : ${pass || '-'}\n- Key : ${key || '-'}\n- Profil : ${profil || '-'}\n- PIN : ${pin || '-'}\n\n`; }); const tanggalBeli = moment(trxData.tanggal).tz("Asia/Jakarta").locale("id").format("YYYY-MM-DD"); const buyerJid = trxData.buyer; let notifikasi = `🟢 *KLAIM GARANSI/REFUND DITERIMA*\n\n`; notifikasi += `🆔 *Order ID:* ${uniqueId}\n`; notifikasi += `🗓️ *Waktu Klaim:* ${waktuKlaim}\n`; notifikasi += `👤 *User:* ${trxData.buyer.split('@')[0]}\n`; notifikasi += `📦 *Produk:* ${produkDitemukan.namaproduk}\n`; notifikasi += `🗓️ *Tanggal beli:* ${tanggalBeli}\n\n`; if (deskripsi) { notifikasi += `📝 *Catatan Admin:* ${deskripsi}\n\n`; } notifikasi += `*「 BERIKUT DETAIL AKUN ANDA 」*\n\n`; notifikasi += dataProdukFormatted; notifikasi += `*NOTE:*\n${produkDitemukan.catatanproduk}\n\n`; await tehtarik.sendMessage(buyerJid, { text: notifikasi }); klaimData.splice(klaimIndex, 1); fs.writeFileSync(klaimPath, JSON.stringify(klaimData, null, 2)); await replyText(`✅ Berhasil mengirimkan ${jumlah} data produk *${produkDitemukan.namaproduk}* ke ${buyerJid.split('@')[0]} untuk Order ID *${uniqueId}*. Klaim telah dihapus dari database.`); break; } case 'maintenance': { if (!isOwner) return await replyText("❌ Khusus owner!"); const arg = text.trim().toLowerCase(); if (arg !== 'on' && arg !== 'off') { return await replyText("Gunakan:\n#maintenance on\n#maintenance off"); } const status = arg === 'on'; fs.writeFileSync("./Database/Maintenance.json", JSON.stringify({ aktif: status }, null, 2)); return await replyText(`✅ Mode maintenance ${status ? 'diaktifkan' : 'dinonaktifkan'}.`); } case 'setevent': case 'seteventnote': { if (!isOwner) return replyText("❌ Khusus owner!"); if (!text) return replyText("Contoh: #seteventnote ✨ PROMO IDUL FITRI ✨\n(Berlaku 1-6 April 2025)"); const data = { aktif: true, pesan: text.trim() }; fs.writeFileSync("./Database/EventNote.json", JSON.stringify(data, null, 2)); return replyText("✅ Pesan event berhasil diatur!"); } case 'delevent': case 'reseteventnote': { if (!isOwner) return replyText("❌ Khusus owner!"); fs.writeFileSync("./Database/EventNote.json", JSON.stringify({ aktif: false, pesan: "" }, null, 2)); return replyText("✅ Pesan event dinonaktifkan."); } case 'setcashback': { if (!isOwner) return await replyText("❌ Khusus owner!"); const [mulai, selesai, kelipatan, nominal] = text.split("|"); if (!mulai || !selesai || !kelipatan || !nominal) return await replyText("Format salah! Contoh:#setcashback 2025-05-01|2025-05-31|5|1000"); const data = { mulai, selesai, kelipatan: parseInt(kelipatan), nominal: parseInt(nominal) }; fs.writeFileSync(cashbackEventPath, JSON.stringify(data, null, 2)); await replyText(`✅ Event cashback berhasil disetel. 🗓 Periode: ${mulai} s.d. ${selesai} 🔁 Setiap ${kelipatan} transaksi 💰 Cashback: ${formatrupiah(nominal)}`); break; } case 'hapuscashback': { if (!isOwner) return await replyText("❌ Khusus owner!"); fs.writeFileSync("./Database/CashbackEvent.json", JSON.stringify({ aktif: false }, null, 2)); await replyText("✅ Event cashback dinonaktifkan."); break; } case 'cekcashback': { if (!isRegistered(sender)) return await replyText(alerts.daftar); if (!fs.existsSync(cashbackEventPath)) return await replyText("📭 Tidak ada event cashback yang sedang berlangsung."); let event = {}; try { event = JSON.parse(fs.readFileSync(cashbackEventPath)); } catch (e) { console.error("❌ Gagal baca cashback event:", e); event = { aktif: false }; } if (!event || event.aktif === false || !event.mulai || !event.selesai || !event.kelipatan || !event.nominal) { return await replyText("📭 Tidak ada event cashback yang sedang berlangsung."); } const today = moment().format('YYYY-MM-DD'); const mulai = event.mulai; const selesai = event.selesai; const kelipatan = event.kelipatan; const nominal = formatrupiah(event.nominal); if (today < mulai || today > selesai) { return await replyText(`📅 Event cashback *tidak aktif* saat ini.\nPeriode: ${mulai} s.d. ${selesai}`); } global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let pos = global.User.findIndex(u => u.id === sender); if (pos === -1) return await replyText(alerts.daftar); const trxSekarang = global.User[pos].jumlahtransaksi; const sisa = kelipatan - (trxSekarang % kelipatan); const next = sisa === 0 ? kelipatan : sisa; let txt = `🎁 *INFO EVENT CASHBACK*\n\n`; txt += `📅 Periode: ${mulai} s.d. ${selesai}\n`; txt += `🔁 Cashback setiap ${kelipatan} transaksi\n`; txt += `💰 Nominal: ${nominal}\n\n`; txt += `🧾 Transaksi kamu saat ini: *${trxSekarang}*\n`; txt += `⏳ Transaksi lagi *${next}x* untuk dapat cashback!`; await replyText(txt); break; } case 'klaim': case 'live': { const arg = text.trim().toLowerCase(); if (arg === 'on') { if (global.supportMode[sender]?.active) { return await replyText( 'Maks 1 tiket per sesi live chat!\nSilahkan ketik *klaim off* untuk menghentikan sesi sebelumnya.' ); } const ticket = genTicket(); global.supportMode[sender] = { active: true, ticket, lastActivity: Date.now() }; saveSupportMode(); const ownerNumber = global.nomerowner.includes('@s.whatsapp.net') ? global.nomerowner : `${global.nomerowner}@s.whatsapp.net`; const nomorUser = senderNumber; const pesan = `🎫 [${ticket}] *${pushName}* (${nomorUser}) terhubung ke livechat`; await tehtarik.sendMessage(ownerNumber, { text: pesan }); return await replyText( `🟢 LiveChat diaktifkan.\n🎫 Ticket kamu: *${ticket}*\nKetik pesan apa saja, nanti akan diteruskan. Batalkan ketik ${petik}klaim off${petik}` ); } if (arg === 'off') { if (!global.supportMode[sender]?.active) { return await replyText( 'Anda tidak terhubung dengan live chat saat ini' ); } const ticket = global.supportMode[sender].ticket; global.supportMode[sender] = { active: false }; saveSupportMode(); return await replyText( `🔴 LiveChat (${ticket}) dinonaktifkan.` ); } return await replyText('Gunakan:\n#klaim on\n#klaim off'); } case 'reply': { break; } case 'stop': { if (!isOwner) return await replyText('❌ Khusus owner!'); const ticket = args[0]; if (!ticket) { return await replyText('❌ Masukkan ticket yang ingin dihentikan.\nContoh: stop 1234567'); } const entry = Object.entries(global.supportMode) .find(([userId, data]) => data.ticket === ticket && data.active); if (!entry) { return await replyText(`❌ Ticket *${ticket}* tidak ditemukan atau sudah ditutup.`); } const [userJid] = entry; global.supportMode[userJid].active = false; saveSupportMode(); await replyText(`✅ LiveChat (${ticket}) telah dihentikan.`); await tehtarik.sendMessage(userJid, { text: `🔴 LiveChat (${ticket}) telah diakhiri oleh Admin.` }); break; } case 'backup': { if (!isOwner) return await replyText('❌ Khusus owner!'); const zip = new AdmZip(); try { const folders = ['Database', global.session]; folders.forEach(folder => { if (fs.existsSync(`./${folder}`)) { zip.addLocalFolder(`./${folder}`, folder); } }); const backupFileName = `Backup-${moment().format("YYYYMMDD-HHmmss")}.zip`; const backupPath = `./${backupFileName}`; zip.writeZip(backupPath); await tehtarik.sendMessage(from, { document: fs.readFileSync(backupPath), fileName: backupFileName, mimetype: 'application/zip', caption: `📦 Backup berhasil dibuat`}); console.log(`✅ File backup berhasil dibuat dan dikirim: ${backupPath}`); fs.unlinkSync(backupPath); console.log(`🗑️ Berhasil menghapus file backup: ${backupPath}`); } catch (err) { console.error('❌ Gagal backup:', err); await replyText('❌ Terjadi kesalahan saat backup.'); } break; } case 'resetdiskon': { if (!isOwner) return await replyText("❌ Khusus owner!"); let Kategori = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let totalProduk = 0; for (let file of Kategori) { let path = "./Database/Kategori/" + file; let Produk = JSON.parse(fs.readFileSync(path)); Produk.forEach((item, idx) => { Produk[idx].diskonproduk = 0; totalProduk++; }); fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); } await replyText(`🔄 Semua diskon berhasil direset. Total ${totalProduk} produk kini tanpa diskon.`); break; } case 'diskonall': { if (!isOwner) return await replyText("❌ Khusus owner!"); let diskon = text.trim(); if (!diskon) return await replyText("Contoh penggunaan: *#diskonall 25*"); if (isNaN(diskon)) return await replyText("Diskon harus berupa angka."); if (Number(diskon) < 0 || Number(diskon) > 100) return await replyText("Diskon hanya boleh antara 0% sampai 100%."); let Kategori = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let totalProduk = 0; for (let file of Kategori) { let path = "./Database/Kategori/" + file; let Produk = JSON.parse(fs.readFileSync(path)); Produk.forEach((item, idx) => { Produk[idx].diskonproduk = Number(diskon); totalProduk++; }); fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); } await replyText(`✅ Diskon *${diskon}%* berhasil diterapkan ke semua produk (${totalProduk} item).`); break; } case 'setdiskon': case 'editdiskon': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let diskon = text.split("|")[1]?.trim(); if (!kodeproduk || !diskon) return await replyText(`Contoh penggunaan: #setdiskon netflix1b|20`); if (isNaN(diskon)) return await replyText(`Diskon harus berupa angka`); if (Number(diskon) < 0 || Number(diskon) > 100) return await replyText(`Diskon hanya boleh antara 0 sampai 100%`); let Kategori = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of Kategori) { let path = "./Database/Kategori/" + file; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { Produk[idx].diskonproduk = Number(diskon); fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Diskon produk *${kodeproduk}* diubah jadi *${diskon}%*`); found = true; break; } } if (!found) await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); break; } case 'menu': case 'help': { let txt = `*📋 MENU UTAMA - ${global.namabot}*\n\n`; txt += `👤 *Akun & Saldo*\n`; txt += `├─ profil / saldo - Lihat info akun dan saldo\n`; txt += `├─ leaderboard - 10 user dengan pengeluaran terbanyak\n`; txt += `├─ riwayat - 5 transaksi terakhir kamu\n`; txt += `└─ ingatkan [kode] - Notif restok produk\n\n`; txt += `🛍️ *Produk & Pembelian*\n`; txt += `├─ stok / list / produk - Lihat semua produk\n`; txt += `└─ buynow [kode] [jumlah] - Beli produk\n\n`; txt += `💳 *Saldo & Promo*\n`; txt += `├─ deposit [jumlah] - Isi saldo via QRIS\n`; txt += `├─ redeem [kode] - Tukar voucher saldo\n`; txt += `└─ cekcashback - Info event cashback aktif\n\n`; txt += `💬 *Layanan Bantuan*\n`; txt += `├─ klaim on - Mulai sesi Live Chat\n`; txt += `└─ klaim off - Akhiri Live Chat\n\n`; txt += `📌 *Contoh Penggunaan*\n`; txt += `├─ buynow zoom 1\n`; txt += `├─ deposit 5000\n`; txt += `└─ ingatkan zoom\n\n`; txt += `📣 *Info Tambahan*\n`; txt += `├─ Update stok jam 08.00 WIB\n`; txt += `├─ Minimal deposit Rp100\n`; txt += `└─ Live Chat max 1x/sesi\n\n`; txt += `\n_${global.footer}_`; await replyText(txt); break; } case 'list': case 'stok': case 'stock': { if (!isRegistered(sender)) return await replyText(alerts.daftar); let hidden = []; if (fs.existsSync("./Database/HiddenKategori.json")) { hidden = JSON.parse(fs.readFileSync("./Database/HiddenKategori.json")); } const kategoriFiles = fs .readdirSync("./Database/Kategori/") .filter(f => f.endsWith(".json")) .filter(f => !hidden.includes(f.replace(".json", ""))); if (!text) { // Jika pengguna hanya ketik #stok tanpa argumen const eventNotePath = "./Database/EventNote.json"; let eventNote = { aktif: false, pesan: "" }; if (fs.existsSync(eventNotePath)) { eventNote = JSON.parse(fs.readFileSync(eventNotePath, "utf-8")); } let list = ` *╭──〔 LIST MARSZEN STORE 〕─•* *┊* Beli = No refund, kecuali kendala dari admin *┊* Update stok setiap Jam 08.00 WIB *┊* Ketik *nomor* atau *nama* yang tersedia di list *┊* Contoh: *1* atau *zoom* *╰─────────────────────•*\n\n`; if (eventNote.aktif && eventNote.pesan) { list += `*INFO:*\n${eventNote.pesan}\n\n`; } list += `*╭─────────────•*\n`; const timestampPath = "./Database/KategoriTimestamps.json"; let timestamps = {}; if (fs.existsSync(timestampPath)) { timestamps = JSON.parse(fs.readFileSync(timestampPath)); } const now = Date.now(); let updated = false; Object.keys(timestamps).forEach(key => { if (now - timestamps[key] >= 24 * 60 * 60 * 1000) { delete timestamps[key]; updated = true; } }); if (updated) { fs.writeFileSync(timestampPath, JSON.stringify(timestamps, null, 2)); } kategoriFiles.forEach((file, i) => { const nama = file.replace(".json", "").toUpperCase(); const createdAt = timestamps[nama]; const isBaru = createdAt && (Date.now() - createdAt < 24 * 60 * 60 * 1000); const label = `${isBaru ? "`ɴᴇᴡ`" : ""}`; list += `*┊ [ ${i + 1} ]* ${nama} ${label}\n`; }); list += `*╰─────────────•*\n\n`; list += ` ᴜɴᴛᴜᴋ ɪɴꜰᴏʀᴍᴀꜱɪ, ᴜᴘᴅᴀᴛᴇ ꜱᴛᴏᴋ, ᴘᴇʀᴛᴀɴʏᴀᴀɴ, ᴅʟʟ. ʜᴜʙᴜɴɢɪ ᴀᴅᴍɪɴ ᴀᴛᴀᴜ ɢᴀʙᴜɴɢ ᴋᴇ ɢʀᴜᴘ. ᴀᴅᴍɪɴ : wa.me/6287741620315 ɢʀᴜᴘ : https://chat.whatsapp.com/JB4e2GAIaBxAhzgbTXKXFE \n_${global.footer}_`; return await replyText(list); } // Pengecekan Cooldown if (global.lastStockRequestTime[sender] && (Date.now() - global.lastStockRequestTime[sender] < STOCK_REQUEST_COOLDOWN)) { const timeLeft = Math.ceil((STOCK_REQUEST_COOLDOWN - (Date.now() - global.lastStockRequestTime[sender])) / 1000); return await replyText(`⏳ Harap tunggu *${timeLeft} detik* lagi sebelum melihat detail kategori produk.`); } let idx = -1; let isNumber = /^\d+$/.test(text); if (isNumber) { idx = parseInt(text) - 1; if (idx < 0 || idx >= kategoriFiles.length) { // Jangan update timestamp jika input tidak valid return await replyText("❌ Nomor kategori tidak valid. Ketik ulang *stok* untuk melihat daftar kategori."); } } else { // Cari berdasarkan nama kategori persis (case-insensitive) idx = kategoriFiles.findIndex(file => file.replace(".json", "").toLowerCase() === text.toLowerCase()); if (idx === -1) { const kategoriNamesOnly = kategoriFiles.map(f => f.replace(".json", "")); const matchedName = findNearestKategori(text, kategoriNamesOnly); // Pastikan fungsi findNearestKategori ada if (matchedName) { idx = kategoriNamesOnly.findIndex(name => name === matchedName); } if (idx === -1) { return await replyText(`❌ Kategori *${text}* tidak ditemukan. Ketik *stok* untuk melihat daftar kategori.`); } } } const file = kategoriFiles[idx]; const kategoriName = file.replace(".json", ""); const produkList = JSON.parse(fs.readFileSync(`./Database/Kategori/${file}`)).filter(p => !p.hidden); if (produkList.length === 0) { global.lastStockRequestTime[sender] = Date.now(); // Update timestamp return await replyText(`Kategori *${kategoriName}* belum memiliki produk.`); } let pesan =` *╭────〔 BOT ${global.namabot.toUpperCase()} 〕─* *┊* • Ketik buynow code jumlah_beli *┊* • Contoh : buynow zoom 1 *┊* • Pastikan code & jumlah di ketik dengan benar *┊* • Update stok setiap jam 08.00 WIB *╰───────•* ⚠️ Stok habis? Kamu bisa ketik perintah yang ada di *Waitlist* untuk mendapatkan notif restok. `; produkList.forEach(item => { const lastUpdateText = (() => { if (!item.lastUpdateStock) return "-"; const last = moment.utc(item.lastUpdateStock).tz("Asia/Jakarta"); const now = moment().tz("Asia/Jakarta"); const diffMs = now.diff(last); const diffMinutes = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / (24 * 3600000)); if (diffMinutes < 1) { return "Baru saja"; } else if (diffHours < 1) { return `${diffMinutes} menit yang lalu`; } else if (diffHours < 24) { return `${diffHours} jam yang lalu`; } else if (diffDays === 1) { return `1 hari yang lalu`; } else { return `${diffDays} hari yang lalu`; } })(); const stok = item.tipeproduk.toLowerCase() === "file" ? item.stokproduk : item.dataproduk.length; const terjual = item.stokTerjual || 0; const hargaAwal = item.hargaproduk; const hargaDiskon = Math.floor(hargaAwal * (100 - item.diskonproduk) / 100); const hargaText = item.diskonproduk > 0 ? `~${formatrupiah(hargaAwal)}~ ➜ ${formatrupiah(hargaDiskon)}` : formatrupiah(hargaAwal); const diskonLine = item.diskonproduk > 0 ? `\n*┊ 💥 Diskon:* ${item.diskonproduk}%` : ""; pesan += ` *╭──〔 ${item.namaproduk} 〕─* *┊ 🔄 Restok:* ${lastUpdateText} *┊ 🆔 Kode:* ${item.kodeproduk} *┊ 📝 Ketik:* ${petik}buynow ${item.kodeproduk} 1${petik} *┊ 💵 Harga:* ${hargaText}${diskonLine} *┊ 📦 Stok:* ${stok} *┊ 📉 Terjual:* ${terjual} *┊ 📥 Waitlist:* ${petik}ingatkan ${item.kodeproduk}${petik} *┊ 🗒 Deskripsi:* ${item.deskripsiproduk} *╰────────*\n`; }); pesan += `\n\n_${global.footer}_`; global.lastStockRequestTime[sender] = Date.now(); return await replyText(pesan); } case 'deposit': { if (!isRegistered(sender)) return await replyText(alerts.daftar); const wib = moment().tz("Asia/Jakarta").format('dddd, D MMMM YYYY HH:mm:ss z'); let jumlah = args[0]; if (qrLoopStatus[sender] && qrLoopStatus[sender].aktif) { return await replyText("❌ Kamu masih memiliki QRIS aktif! Ketik *batal* dulu untuk membatalkan."); } if (!jumlah) return await replyText(`Contoh penggunaan: *#deposit 5000*`); if (isNaN(jumlah)) return await replyText("Input harus berupa angka."); if (Number(jumlah) < 500) return await replyText("Minimal deposit adalah Rp500."); if (Number(jumlah) > 1000000) return await replyText("Maksimal deposit adalah Rp 1.000.000"); let Unique = "MZS" + require("crypto").randomBytes(5).toString("hex").toUpperCase(); let fee = digit(Number(jumlah)); let totalAmount = Number(jumlah) + Number(fee); let time = Date.now() + toMs("5m"); let filePathQR = `./Database/QR/${Unique}.jpg`; try { // Atlantic Deposit Create API Call let dd = await axios.post(`https://atlantich2h.com/deposit/create`, { api_key: atlantic.apikey, reff_id: Unique, // Use Unique as reff_id for deposit nominal: totalAmount, type: "ewallet", metode: "qris" // pengen cair instant ganti "qris" jadi "qrisfast" }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); if (dd.data && dd.data.data && dd.data.data.qr_string) { qrLoopStatus[sender] = { aktif: true, msgKey: null, filePath: filePathQR, atlanticDepositId: dd.data.data.id }; let pag = await createQris(filePathQR, dd.data.data.qr_string); let txt = `*KONFIRMASI DEPOSIT* *- Order ID:* ${Unique} *- Jumlah:* ${formatrupiah(Number(jumlah))} *- Fee:* ${formatrupiah(Number(fee))} *- Total Bayar:* ${formatrupiah(Number(totalAmount))} *- Expired:* 5 Menit Silakan scan QRIS di atas untuk melanjutkan pembayaran. Pembayaran akan dicek otomatis setiap 5 detik. Ketik ${petik}batal${petik} untuk membatalkan pesanan.`; let msggg = await tehtarik.sendMessage(from, { image: fs.readFileSync(pag), caption: txt }); qrLoopStatus[sender].msgKey = msggg.key; let statusP = false; while (!statusP && qrLoopStatus[sender] && qrLoopStatus[sender].aktif) { await sleep(5000); if (Date.now() >= time) { statusP = true; await tehtarik.sendMessage(from, { delete: msggg.key }); await replyText("Request depositmu telah expired."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); if (qrLoopStatus[sender] && qrLoopStatus[sender].atlanticDepositId) { const currentDepositId = qrLoopStatus[sender].atlanticDepositId; try { response = await axios.post(`https://atlantich2h.com/deposit/cancel`, { api_key: atlantic.apikey, id: currentDepositId }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); } catch {} } delete qrLoopStatus[sender]; break; } try { let response = await axios.post(`https://atlantich2h.com/deposit/status`, { api_key: atlantic.apikey, id: dd.data.data.id }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); console.log("[ ATLANTIC LOG ] : " + JSON.stringify(response.data.data, null, 3)); let status = response.data.data.status; if (status === "processing") { // ubah processing ke success jika pake qrisfast statusP = true; // Hapus /* dan */ supaya metode qris bisa cair instant tanpa pke qrisfast /*await axios.post(`https://atlantich2h.com/deposit/instant`, { api_key: atlantic.apikey, id: dd.data.data.id, action: true }, { headers: { 'Content-Type': "application/x-www-form-urlencoded" } });*/ await tehtarik.sendMessage(from, { delete: qrLoopStatus[sender].msgKey }); addSaldoSafe(sender, Number(jumlah)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let txtr = `*DEPOSIT BERHASIL✅* *- Order ID:* ${Unique} *- Jumlah:* ${formatrupiah(Number(jumlah))} *- Fee:* ${formatrupiah(Number(fee))} *- Total Bayar:* ${formatrupiah(Number(totalAmount))} *- Waktu:* ${wib} Terimakasih sudah melakukan deposit di _${global.namabot}_`; let txtl = `*LOG DEPOSIT*\n\n`; txtl += `👤 *Penerima:* ${sender.split("@")[0]}\n`; txtl += `📝 *Order ID:* ${Unique}\n`; txtl += `💸 *Jumlah:* ${formatrupiah(Number(jumlah))}\n\n`; txtl += `_${global.footer}_`; try {await replyText(txtr);} catch (err) {console.error("❌ Gagal kirim notifikasi ke user:", err.message);} await sleep(500); try {await tehtarik.sendMessage(global.nomerowner, { text: txtl });} catch (err) {console.error("❌ Gagal kirim log ke owner:", err.message);} if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); delete qrLoopStatus[sender]; } } catch (err) { console.log("Error saat cek mutasi QRIS (Deposit Command):", err.message); } } } else { await replyText("❌ Gagal membuat QRIS. Mohon coba lagi nanti atau hubungi admin."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); delete qrLoopStatus[sender]; } } catch (err) { console.log("Error saat request deposit ke Atlantic:", err.message); await replyText("❌ Terjadi kesalahan saat memproses deposit. Mohon coba lagi nanti."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); delete qrLoopStatus[sender]; } break; } case 'saldo': case 'profil': { if (!isRegistered(sender)) return await replyText(alerts.daftar); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const userData = global.User.find(u => u.id === sender); if (!userData) return await replyText(alerts.daftar); const txt = `*📋 PROFIL AKUN*\n\n` + `👤 *Nama:* ${pushName}\n` + `📱 *Nomor:* ${sender.split("@")[0]}\n` + `💰 *Saldo:* ${formatrupiah(userData.saldo)}\n` + `📊 *Jumlah Transaksi:* ${userData.jumlahtransaksi}\n` + `Jika saldo anda kurang, ketik *deposit*\n\n\n` + `_${global.footer}_`; await replyText(txt); break; } case 'leaderboard': { if (!isRegistered(sender)) return await replyText(alerts.daftar); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let txt = `*LEADERBOARD TRANSAKSI*\n`; txt += `*TOP 10 USER DENGAN PENGELUARAN TERBANYAK*\n\n`; let sortedUsers = global.User.sort((a, b) => b.pengeluaran - a.pengeluaran); sortedUsers.slice(0, 10).forEach((usr, index) => { const nomor = usr.id.split("@")[0]; const nomorSensor = nomor.slice(0, 2) + '*'.repeat(nomor.length - 4) + nomor.slice(-2); txt += `*${index + 1}.* @${nomorSensor}\n`; txt += ` *Pengeluaran:* ${formatrupiah(usr.pengeluaran)}\n\n\n`; }); txt += `_${global.footer}_`; await replyText(txt); break; } case 'addk': case 'addkategori': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kategori = text.trim(); if (!kategori) return await replyText("Contoh penggunaan: *#addkategori Akun*"); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let existingKategori = KategoriFiles.find(file => file.replace(".json", "").toLowerCase() === kategori.toLowerCase()); if (existingKategori) return replyText(`Kategori produk *${kategori}* sudah digunakan!`); fs.writeFileSync(`./Database/Kategori/${kategori}.json`, "[]"); const timestampPath = "./Database/KategoriTimestamps.json"; let timestamps = {}; if (fs.existsSync(timestampPath)) { timestamps = JSON.parse(fs.readFileSync(timestampPath)); } timestamps[kategori.toUpperCase()] = Date.now(); fs.writeFileSync(timestampPath, JSON.stringify(timestamps, null, 2)); await replyText("✅ Berhasil menambah kategori *" + kategori + "* dengan label *BARU* (aktif 24 jam)."); loadKategoriCache(); break; } case 'delk': case 'delkategori': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kategori = text.trim().toLowerCase(); if (!kategori) return await replyText("Contoh penggunaan: *#delkategori Akun*"); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let fileKategori = KategoriFiles.find(kat => kat.replace(".json", "").toLowerCase() === kategori); if (!fileKategori) { return await replyText(`⚠️ Kategori produk *${kategori}* tidak ditemukan!`); } const pathKategori = "./Database/Kategori/" + fileKategori; if (fs.existsSync(pathKategori)) { fs.unlinkSync(pathKategori); const timestampPath = "./Database/KategoriTimestamps.json"; if (fs.existsSync(timestampPath)) { let timestamps = JSON.parse(fs.readFileSync(timestampPath)); delete timestamps[fileKategori.replace(".json", "").toUpperCase()]; fs.writeFileSync(timestampPath, JSON.stringify(timestamps, null, 2)); } await replyText(`✅ Berhasil menghapus kategori *${fileKategori.replace(".json", "")}*`); loadKategoriCache(); } else { await replyText(`⚠️ Kategori produk *${kategori}* tidak ditemukan.`); } break; } case 'renamek': case 'renamekategori': { if (!isOwner) return await replyText("❌ Khusus owner!"); let [kategorilama, kategoribaru] = text.split("|").map(s => s.trim()); if (!kategorilama || !kategoribaru) return await replyText("Contoh penggunaan: *#renamekategori Akun|AkunFF*"); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let oldFile = KategoriFiles.find(file => file.replace(".json", "").toLowerCase() === kategorilama.toLowerCase()); let newFileExists = KategoriFiles.find(file => file.replace(".json", "").toLowerCase() === kategoribaru.toLowerCase()); if (!oldFile) return await replyText(`Kategori produk *${kategorilama}* tidak ditemukan!`); if (newFileExists) return await replyText(`Nama kategori *${kategoribaru}* sudah digunakan!`); fs.renameSync(`./Database/Kategori/${oldFile}`, `./Database/Kategori/${kategoribaru}.json`); const timestampPath = "./Database/KategoriTimestamps.json"; if (fs.existsSync(timestampPath)) { let timestamps = JSON.parse(fs.readFileSync(timestampPath)); timestamps[kategoribaru.toUpperCase()] = timestamps[oldFile.replace(".json", "").toUpperCase()]; delete timestamps[oldFile.replace(".json", "").toUpperCase()]; fs.writeFileSync(timestampPath, JSON.stringify(timestamps, null, 2)); } await replyText(`Berhasil mengganti nama kategori *${oldFile.replace(".json", "")}* menjadi *${kategoribaru}*`); loadKategoriCache(); break; } case 'hideproduk': case 'hidep': { if (!isOwner) return await replyText("❌ Khusus owner!"); const [kategoriInput, status] = text.split("|").map(s => s.trim()); if (!kategoriInput || !status) { return await replyText( "Format salah!\nContoh:\n#hideproduk Netflix|on\n#hideproduk Netflix|off" ); } const files = fs.readdirSync("./Database/Kategori/").filter(f => f.endsWith(".json")); const matched = files.find(f => f.replace(".json", "").toLowerCase() === kategoriInput.toLowerCase()); if (!matched) { return await replyText(`⚠️ Kategori *${kategoriInput}* tidak ditemukan!`); } const key = matched.replace(".json", ""); const hiddenPath = "./Database/HiddenKategori.json"; if (!fs.existsSync(hiddenPath)) fs.writeFileSync(hiddenPath, "[]"); let hiddenList = JSON.parse(fs.readFileSync(hiddenPath, "utf-8")); if (status.toLowerCase() === "on") { if (!hiddenList.includes(key)) hiddenList.push(key); await replyText(`✅ Kategori *${key}* sekarang *disembunyikan* dari daftar.`); } else if (status.toLowerCase() === "off") { hiddenList = hiddenList.filter(k => k !== key); await replyText(`✅ Kategori *${key}* sekarang *ditampilkan* kembali.`); } else { return await replyText("Status harus 'on' atau 'off'!"); } fs.writeFileSync(hiddenPath, JSON.stringify(hiddenList, null, 2)); loadKategoriCache(); break; } case 'addp': case 'addproduk': { if (!isOwner) return await replyText("❌ Khusus owner!"); let [kategoriproduk, namaproduk, kodeproduk, hargaproduk, tipeproduk, deskripsiproduk, catatanproduk] = text.split("|").map(s => s.trim()); if (!kategoriproduk || !namaproduk || !kodeproduk || !hargaproduk || !tipeproduk || !deskripsiproduk || !catatanproduk) { return await replyText("Contoh penggunaan: #addproduk Netflix|Netflix 1 Bulan|netflix1b|20000|Data|Netflix Untuk 1 Bulan|Harap digunakan sebelum expired"); } if (isNaN(hargaproduk)) return await replyText("Harga produk harus berupa angka!"); if (tipeproduk.toLowerCase() !== "data" && tipeproduk.toLowerCase() !== "file") return replyText("Tipe produk harus berupa 'Data' atau 'File'!"); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let kategoriFileFound = KategoriFiles.find(file => file.replace(".json", "").toLowerCase() === kategoriproduk.toLowerCase()); if (!kategoriFileFound) return await replyText(`Kategori produk *${kategoriproduk}* tidak ditemukan!`); let ProdukInKategori = JSON.parse(fs.readFileSync(`./Database/Kategori/${kategoriFileFound}`)); if (ProdukInKategori.some(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase())) { return await replyText(`Kode produk *${kodeproduk}* sudah digunakan di kategori *${kategoriproduk}*!`); } let Data = { namaproduk: namaproduk, kodeproduk: kodeproduk.replace(/\s/g, ""), hargaproduk: Number(hargaproduk), tipeproduk: tipeproduk, dataproduk: [], stokproduk: 0, stokTerjual: 0, deskripsiproduk: deskripsiproduk, catatanproduk: catatanproduk, diskonproduk: 0 }; ProdukInKategori.push(Data); fs.writeFileSync(`./Database/Kategori/${kategoriFileFound}`, JSON.stringify(ProdukInKategori, null, 3)); await replyText(`✅ Berhasil menambah produk *${namaproduk}* ke kategori *${kategoriproduk}*`); loadKategoriCache(); break; } case 'delp': case 'delproduk': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.trim(); if (!kodeproduk) return await replyText(`Contoh penggunaan: #delproduk netflix1b`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of KategoriFiles) { let path = "./Database/Kategori/" + file; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { Produk.splice(idx, 1); fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Berhasil menghapus produk *${kodeproduk}*`); found = true; loadKategoriCache(); break; } } if (!found) await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); break; } case 'cekstok': case 'checkstok': { if (!isOwner) return await replyText("❌ Khusus owner!"); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let stokTersedia = []; let stokHabis = []; for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let Produk = JSON.parse(fs.readFileSync(path)); Produk.forEach((item) => { let stok = 0; if (item.tipeproduk.toLowerCase() === "data") { stok = item.dataproduk.length; } else { stok = item.stokproduk; } let info = { nama: item.namaproduk, kode: item.kodeproduk, stok: stok }; if (stok <= 2 && stok > 0) { info.label = '⚠️ Hampir Habis'; } if (stok > 0) { stokTersedia.push(info); } else { stokHabis.push(info); } }); } let pesan = `📦 *Produk dengan Stok Tersedia:*\n\n`; stokTersedia.forEach((p, i) => { pesan += `${i + 1}. ${p.nama} - ${p.kode}\n`; pesan += ` ➤ Stok: ${p.stok} ${p.label ? p.label : ''}\n\n`; }); pesan += `\n❌ *Produk yang Habis:*\n\n`; stokHabis.forEach((p, i) => { pesan += `${i + 1}. ${p.nama} - ${p.kode}\n ➤ Stok: ${p.stok}\n\n`; }); await replyText(pesan); break; } case 'adds': case 'addstok': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim().toLowerCase(); // Langsung toLowerCase let input = text.split("|")[1]?.trim(); if (!kodeproduk || !input) { return await replyText(`Contoh penggunaan:\n• #addstok netflix1b|email:pass\n• #addstok netflix1b|10`); } const wishlistPath = './Database/Wishlist.json'; if (!fs.existsSync(wishlistPath)) { fs.writeFileSync(wishlistPath, '[]'); } let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of KategoriFiles) { let path = "./Database/Kategori/" + file; let ProdukArray = JSON.parse(fs.readFileSync(path)); let ps = ProdukArray.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk); if (ps !== -1) { let produk = ProdukArray[ps]; let jumlahDitambah = 0; if (produk.tipeproduk.toLowerCase() === "data") { let items = input.split(/\r?\n/).map(line => line.trim()).filter(Boolean); produk.dataproduk.push(...items); jumlahDitambah = items.length; } else if (produk.tipeproduk.toLowerCase() === "file") { if (isNaN(input)) { return await replyText(`Stok harus berupa angka untuk tipe produk 'file'.`); } produk.stokproduk += Number(input); jumlahDitambah = Number(input); } else { return await replyText("❌ Tipe produk tidak dikenal!"); } produk.lastUpdateStock = moment().utc().format(); fs.writeFileSync(path, JSON.stringify(ProdukArray, null, 3)); await replyText(`✅ Berhasil menambah ${jumlahDitambah} stok ke *${produk.kodeproduk}*`); console.log(`[addstok] Menunggu 1 detik sebelum mengirim notifikasi wishlist untuk ${produk.kodeproduk}...`); await sleep(1000); if (fs.existsSync(wishlistPath)) { let Wishlist = []; try { Wishlist = JSON.parse(fs.readFileSync(wishlistPath, 'utf-8')); } catch (e) { console.error("Gagal parse Wishlist.json, menganggap kosong:", e); Wishlist = []; } let indexWishlist = Wishlist.findIndex(item => item.kodeproduk.toLowerCase() === kodeproduk); if (indexWishlist !== -1 && Array.isArray(Wishlist[indexWishlist].users) && Wishlist[indexWishlist].users.length > 0) { const usersToNotify = [...Wishlist[indexWishlist].users]; let successfullyNotifiedUsers = []; const delayBetweenMessages = 500; console.log(`[Notif Restok] Memulai pengiriman notifikasi untuk ${produk.kodeproduk} ke ${usersToNotify.length} user dengan delay ${delayBetweenMessages / 1000} detik antar user...`); for (const jid of usersToNotify) { try { await tehtarik.sendMessage(jid, { text: `📢 Produk *${produk.namaproduk}* (${produk.kodeproduk}) sudah tersedia kembali!\nLangsung ketik: *buynow ${produk.kodeproduk} 1* untuk membeli sekarang.` }); console.log(`✅ Notifikasi restok terkirim ke ${jid} untuk produk ${produk.kodeproduk}`); successfullyNotifiedUsers.push(jid); } catch (err) { console.error(`❌ Gagal kirim notifikasi restok ke ${jid} untuk produk ${produk.kodeproduk}:`, err.message); } if (usersToNotify.length > 1 && usersToNotify.indexOf(jid) < usersToNotify.length - 1) { await sleep(delayBetweenMessages); } } if (successfullyNotifiedUsers.length > 0) { Wishlist[indexWishlist].users = Wishlist[indexWishlist].users.filter(userJid => !successfullyNotifiedUsers.includes(userJid)); if (Wishlist[indexWishlist].users.length === 0) { Wishlist.splice(indexWishlist, 1); console.log(`[Notif Restok] Semua user untuk ${produk.kodeproduk} telah dinotifikasi, entri produk dihapus dari wishlist.`); } fs.writeFileSync(wishlistPath, JSON.stringify(Wishlist, null, 2)); console.log(`[Notif Restok] ${successfullyNotifiedUsers.length} user berhasil dinotifikasi. Wishlist diperbarui.`); } } else { console.log(`[Notif Restok] Tidak ada user di wishlist untuk produk ${produk.kodeproduk}.`); } } else { console.log("[Notif Restok] File Wishlist.json tidak ditemukan, tidak ada notifikasi dikirim."); } found = true; loadKategoriCache(); break; } } if (!found) { await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); } break; } case 'setjual': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let jumlahTerjual = text.split("|")[1]?.trim(); if (!kodeproduk || !jumlahTerjual) return await replyText(`Contoh penggunaan: #setjual netflix1b|10`); if (isNaN(jumlahTerjual)) return await replyText(`Jumlah terjual harus berupa angka!`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of KategoriFiles) { let path = "./Database/Kategori/" + file; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { Produk[idx].stokTerjual = Number(jumlahTerjual); fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Berhasil mengatur terjual *${jumlahTerjual}* untuk *${kodeproduk}*`); found = true; break; } } if (!found) await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); break; } case 'dels': case 'delstok': case 'deletestok': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.trim(); if (!kodeproduk) return await replyText(`Contoh penggunaan: #delstok netflix1b`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of KategoriFiles) { let path = "./Database/Kategori/" + file; let data = JSON.parse(fs.readFileSync(path)); let idx = data.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { if (data[idx].tipeproduk.toLowerCase() === "data") { data[idx].dataproduk = []; } else if (data[idx].tipeproduk.toLowerCase() === "file") { data[idx].stokproduk = 0; } else { return await replyText("❌ Tipe produk tidak dikenal!"); } fs.writeFileSync(path, JSON.stringify(data, null, 3)); await replyText(`✅ Berhasil menghapus semua stok untuk produk *${kodeproduk}*`); found = true; break; } } if (!found) { await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); } break; } case 'mins': case 'minstok': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let jumlah = text.split("|")[1]?.trim(); if (!kodeproduk || !jumlah) return await replyText(`Contoh: #minstok netflix1b|3`); if (isNaN(jumlah)) return await replyText(`Jumlah harus berupa angka!`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let found = false; for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { let item = Produk[idx]; if (item.tipeproduk.toLowerCase() === "data") { if (item.dataproduk.length < Number(jumlah)) return await replyText(`Stok tidak mencukupi!`); item.dataproduk.splice(0, Number(jumlah)); } else { if (item.stokproduk < Number(jumlah)) return await replyText(`Stok tidak mencukupi!`); item.stokproduk -= Number(jumlah); } fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Berhasil mengurangi *${jumlah}* stok *${kodeproduk}*`); found = true; break; } } if (!found) await replyText(`❌ Produk *${kodeproduk}* tidak ditemukan.`); break; } case 'setname': case 'editnama': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let namabaru = text.split("|")[1]?.trim(); if (!kodeproduk || !namabaru) return await replyText(`Contoh: #editnama netflix1b|Netflix Premium`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { Produk[idx].namaproduk = namabaru; fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); return await replyText(`✅ Nama produk *${kodeproduk}* diubah jadi *${namabaru}*`); } } await replyText(`❌ Produk *${kodeproduk}* tidak ditemukan.`); break; } case 'setkode': case 'editkode': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let kodebaru = text.split("|")[1]?.trim(); if (!kodeproduk || !kodebaru) return await replyText(`Contoh: #editkode netflix1b|net1b`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { if (Produk.some(p => p.kodeproduk.toLowerCase() === kodebaru.toLowerCase() && p.kodeproduk.toLowerCase() !== kodeproduk.toLowerCase())) { return await replyText(`❌ Kode produk *${kodebaru}* sudah ada di kategori ini!`); } Produk[idx].kodeproduk = kodebaru; fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Kode produk *${kodeproduk}* diubah jadi *${kodebaru}*`); break; } } await replyText(`❌ Produk *${kodeproduk}* tidak ditemukan.`); break; } case 'setharga': case 'editharga': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeprodukInput = text.split("|")[0]?.trim(); let hargabaru = text.split("|")[1]?.trim(); if (!kodeprodukInput || !hargabaru) { return await replyText(`Contoh penggunaan:\n#setharga netflix1b|30000`); } if (isNaN(hargabaru)) { return await replyText(`Harga harus berupa angka!`); } const kodeprodukLower = kodeprodukInput.toLowerCase(); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let productWasFoundAndUpdated = false; for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let ProdukArray = JSON.parse(fs.readFileSync(path)); let idx = ProdukArray.findIndex(p => p.kodeproduk.toLowerCase() === kodeprodukLower); if (idx !== -1) { ProdukArray[idx].hargaproduk = Number(hargabaru); fs.writeFileSync(path, JSON.stringify(ProdukArray, null, 3)); await replyText(`✅ Harga produk *${ProdukArray[idx].kodeproduk}* diubah jadi *${formatrupiah(hargabaru)}*`); productWasFoundAndUpdated = true; break; } } if (!productWasFoundAndUpdated) { await replyText(`❌ Produk dengan kode *${kodeprodukInput}* tidak ditemukan di semua kategori.`); } break; } case 'setdesk': case 'editdeskripsi': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim().toLowerCase(); let deskbaru = text.split("|")[1]?.trim(); if (!kodeproduk || !deskbaru) { return await replyText(`Contoh penggunaan:\n#setdesk netflix1b|Deskripsi baru untuk produk netflix`); } let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let productWasFoundAndUpdated = false; for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let ProdukArray = JSON.parse(fs.readFileSync(path)); let idx = ProdukArray.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk); if (idx !== -1) { ProdukArray[idx].deskripsiproduk = deskbaru; fs.writeFileSync(path, JSON.stringify(ProdukArray, null, 3)); await replyText(`✅ Deskripsi produk *${ProdukArray[idx].kodeproduk}* berhasil diubah.`); productWasFoundAndUpdated = true; break; } } if (!productWasFoundAndUpdated) { await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan di semua kategori.`); } break; } case 'setsnk': case 'editcatatan': { if (!isOwner) return await replyText("❌ Khusus owner!"); let kodeproduk = text.split("|")[0]?.trim(); let catatanbaru = text.split("|")[1]?.trim(); if (!kodeproduk || !catatanbaru) return await replyText(`Contoh: #editcatatan netflix1b|Gunakan sebelum expired`); let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); for (let file of KategoriFiles) { let path = `./Database/Kategori/${file}`; let Produk = JSON.parse(fs.readFileSync(path)); let idx = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (idx !== -1) { Produk[idx].catatanproduk = catatanbaru; fs.writeFileSync(path, JSON.stringify(Produk, null, 3)); await replyText(`✅ Catatan produk *${kodeproduk}* berhasil diubah`); break; } } await replyText(`❌ Produk *${kodeproduk}* tidak ditemukan.`); break; } case 'listuser': case 'daftaruser': { if (!isOwner) return await replyText("❌ Khusus owner!"); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let sortedUsers = Object.values(global.User).sort((a, b) => b.jumlahtransaksi - a.jumlahtransaksi); let txt = `*DAFTAR USER*\n`; txt += `*Jumlah User:* ${sortedUsers.length}\n\n`; sortedUsers.forEach((usr, index) => { const nomor = usr.id.split("@")[0]; txt += `*${index + 1}.* ${nomor}\n`; txt += ` *Saldo:* ${formatrupiah(usr.saldo)}\n`; txt += ` *Jumlah Transaksi:* ${usr.jumlahtransaksi}\n`; txt += ` *Pengeluaran:* ${formatrupiah(usr.pengeluaran)}\n\n\n`; }); txt += `_${global.footer}_`; await replyText(txt); break; } case 'addsaldo': { if (!isOwner) return await replyText("❌ Khusus owner!"); let parts = text.split(/\s+/); if (parts.length < 2) return await replyText(`Cara Penggunaan: #addsaldo 628123456789 20000`); let user = parts[0].trim(); let jumlah = parts[1].trim(); if (!/^\d+$/.test(user)) return await replyText(`Format nomor tidak valid!`); if (isNaN(jumlah)) return await replyText(`Jumlah harus berupa angka!`); user = user.includes('@s.whatsapp.net') ? user : `${user}@s.whatsapp.net`; if (!isRegistered(user)) return await replyText(`User tidak ditemukan!`); try { addSaldoSafe(user, Number(jumlah)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const saldoSekarang = cekSaldo(user, global.User); const formattedNominal = formatrupiah(Number(jumlah)); const formattedSaldo = formatrupiah(saldoSekarang); const customerMessage = `💳 *NOTIFIKASI SALDO* 💳 ⬆️ Saldo ditambahkan: *${formattedNominal}* 💰 Saldo sekarang: *${formattedSaldo}* Terima kasih telah menjadi pelanggan setia ${global.namabot}!`; await tehtarik.sendMessage(user, { text: customerMessage }); const adminMessage = `✅ Berhasil menambahkan *${formattedNominal}* ke ${user.split('@')[0]} 📤 Notifikasi terkirim ke customer 💰 Saldo customer sekarang: ${formattedSaldo}`; await replyText(adminMessage); } catch (e) { console.error('Gagal tambah saldo:', e); await replyText(`❌ Error: ${e.message}`); } break; } case 'minsaldo': { if (!isOwner) return await replyText("❌ Khusus owner!"); let user = text.split(" ")[0]; let jumlah = text.split(" ")[1]; if (!user || !jumlah) return await replyText(`Cara Penggunaan: #minsaldo 62822827383 20000`); if (isNaN(jumlah)) return await replyText(`Jumlah harus berupa angka!`); user = user.includes('@s.whatsapp.net') ? user : `${user}@s.whatsapp.net`; if (!isRegistered(user)) return await replyText(`User tidak ditemukan!`); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const saldoSebelum = cekSaldo(user, global.User); if (saldoSebelum < Number(jumlah)) { return await replyText(`❌ Saldo user *${user.split('@')[0]}* (${formatrupiah(saldoSebelum)}) tidak cukup untuk dikurangi sebesar *${formatrupiah(Number(jumlah))}*`); } minSaldoSafe(user, Number(jumlah)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const saldoSekarang = cekSaldo(user, global.User); await replyText(`✅ Berhasil mengurangi saldo user *${user.split('@')[0]}* sebesar *${formatrupiah(Number(jumlah))}*. Saldo sekarang: ${formatrupiah(saldoSekarang)}.`); break; } case 'deluser': { if (!isOwner) return await replyText("❌ Khusus owner!"); let user = text.trim(); if (!user) return await replyText(`Cara penggunaan: #deluser 628283372828`); user = user.includes('@s.whatsapp.net') ? user : `${user}@s.whatsapp.net`; global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let initialUserCount = global.User.length; global.User = global.User.filter(u => u.id !== user); if (global.User.length < initialUserCount) { fs.writeFileSync("./Database/User.json", JSON.stringify(global.User, null, 3)); await replyText(`✅ Berhasil menghapus user *${user.split('@')[0]}*`); } else { await replyText(`❌ User *${user.split('@')[0]}* tidak ditemukan!`); } break; } case 'ingatkan': case 'waitlist':{ if (!isRegistered(sender)) return await replyText(alerts.daftar); let kodeproduk = text.trim(); if (!kodeproduk) return await replyText("Contoh: *ingatkan zoom*"); const wishlistPath = './Database/Wishlist.json'; if (!fs.existsSync(wishlistPath)) fs.writeFileSync(wishlistPath, '[]'); let Wishlist = JSON.parse(fs.readFileSync(wishlistPath, 'utf-8')); let kategoriFiles = fs.readdirSync("./Database/Kategori/").filter(f => f.endsWith(".json")); let produkDitemukan = null; for (let file of kategoriFiles) { const data = JSON.parse(fs.readFileSync(`./Database/Kategori/${file}`)); const item = data.find(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (item) { produkDitemukan = item; break; } } if (!produkDitemukan) { return await replyText(`❌ Produk dengan kode *${kodeproduk}* tidak ditemukan.`); } let index = Wishlist.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (index !== -1 && Wishlist[index].users.includes(sender)) { return await replyText(`⚠️ Kamu sudah terdaftar di waitlist untuk produk *${produkDitemukan.namaproduk}*`); } if (index === -1) { Wishlist.push({ kodeproduk: kodeproduk.toLowerCase(), users: [sender] }); } else { Wishlist[index].users.push(sender); } fs.writeFileSync(wishlistPath, JSON.stringify(Wishlist, null, 2)); return await replyText(`🔔 Kamu akan mendapat notifikasi saat *${produkDitemukan.namaproduk}* tersedia kembali!`); } case 'beli': case 'buy': case 'order': case 'buynow': {if (!isRegistered(sender)) return await replyText(alerts.daftar); let kodeproduk = text.split(" ")[0]?.toLowerCase(); let jumlah = text.split(" ")[1]; if (!kodeproduk) return await replyText("❌ Kode produk belum dimasukkan."); if (!jumlah) return await replyText("Jumlahnya berapa?"); if (isNaN(jumlah) || Number(jumlah) < 1) { return await replyText("❌ Jumlah beli harus berupa angka dan minimal 1."); } const transaksiPath = `./Database/Trx/${sender.split("@")[0]}.json`; if (fs.existsSync(transaksiPath)) { return await replyText(`❌ Anda masih memiliki transaksi pembelian yang aktif. Silakan selesaikan atau batalkan transaksi tersebut terlebih dahulu dengan mengetik ${petik}batal${petik}.`); } let kategoriprodukFileName = getKategori(sender.split("@")[0], kodeproduk); if (kategoriprodukFileName === "Kategori tidak ditemukan!") { return await replyText(`❌ Kategori produk untuk kode *${kodeproduk}* tidak ditemukan!`); } let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let kategoriIndex = KategoriFiles.findIndex(file => file === kategoriprodukFileName); if (kategoriIndex === -1) { return await replyText(`❌ Kategori produk tidak ditemukan!`); } if (!isExistProduk(kategoriIndex, kodeproduk)) { return await replyText(`❌ Kode produk *${kodeproduk}* tidak ditemukan!`); } let Produk = JSON.parse(fs.readFileSync("./Database/Kategori/" + kategoriprodukFileName)); let ps = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (ps !== -1) { let stok = Produk[ps].tipeproduk.toLowerCase() === "file" ? Produk[ps].stokproduk : Produk[ps].dataproduk.length; if (stok === 0) { return await replyText( `❌ Maaf, produk *${Produk[ps].namaproduk}* saat ini *habis*.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[ps].kodeproduk}${petik}` ); } if (Number(jumlah) > stok) { return await replyText( `❌ Jumlah beli melebihi stok tersedia!\n` + `Stok saat ini: *${stok}*.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[ps].kodeproduk}${petik}` ); } if (Number(jumlah) >= 5) { return await replyText(`Ada harga spesial nih untuk jumlah order lebih dari 5, untuk produk *${Produk[ps].namaproduk}*.\nKalau kamu mau, bisa ketik:\n${petik}bulknow ${kodeproduk} ${jumlah}${petik}`); } let hargaSatuan = Produk[ps].hargaproduk; let diskon = Produk[ps].diskonproduk || 0; let hargaDiskon = hargaSatuan - (hargaSatuan * diskon / 100); let harga = Number(jumlah) * hargaDiskon; let Unique = "MZS" + require("crypto").randomBytes(3).toString("hex").toUpperCase(); let DataBeli = { buyer: sender, unique: Unique, kategoriproduk: kategoriprodukFileName.replace(".json", ""), kodeproduk: kodeproduk, harga: harga, jumlah: Number(jumlah), isBulk: false }; fs.writeFileSync(transaksiPath, JSON.stringify(DataBeli, null, 3)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const currentUser = global.User.find(u => u.id === sender); const saldoUser = currentUser ? currentUser.saldo : 0; let txt = `*KONFIRMASI PESANAN* > *Order ID:* ${petik}${Unique}${petik} > *Kode:* ${Produk[ps].kodeproduk} > *Produk:* ${Produk[ps].namaproduk} > *Harga asli:* ${formatrupiah(hargaSatuan)} > *Diskon:* ${diskon}% > *Harga setelah diskon:* ${formatrupiah(hargaDiskon)} > *Jumlah beli:* ${jumlah} > *Total Harga:* ${formatrupiah(harga)} Silahkan pilih metode pembayaran untuk melanjutkan - ${petik}manual${petik} (Saldo kamu: *${formatrupiah(saldoUser)}*) - ${petik}qris${petik} (Pembayaran langsung) - ${petik}batal${petik} (Batalkan pesanan)`; await replyText(txt); } break; } case 'bulknow': case 'bulk': { if (!isRegistered(sender)) return await replyText(alerts.daftar); let kodeproduk = text.split(" ")[0]?.toLowerCase(); let jumlah = parseInt(text.split(" ")[1]); if (!kodeproduk) return await replyText("❌ Kode produk belum dimasukkan."); if (isNaN(jumlah) || jumlah < 5 || jumlah % 1 !== 0) { return await replyText("❌ Pembelian bulk minimal *5* dan berlaku kelipatan."); } const transaksiPath = `./Database/Trx/${sender.split("@")[0]}.json`; if (fs.existsSync(transaksiPath)) { return await replyText(`❌ Anda masih memiliki transaksi pembelian yang aktif. Silakan selesaikan atau batalkan transaksi tersebut terlebih dahulu dengan mengetik ${petik}batal${petik}.`); } let kategoriprodukFileName = getKategori(sender.split("@")[0], kodeproduk); if (kategoriprodukFileName === "Kategori tidak ditemukan!") { return await replyText(`❌ Kategori produk untuk kode *${kodeproduk}* tidak ditemukan!`); } let KategoriFiles = fs.readdirSync("./Database/Kategori/").filter(file => file.endsWith(".json")); let kategoriIndex = KategoriFiles.findIndex(file => file === kategoriprodukFileName); if (kategoriIndex === -1) { return await replyText(`❌ Kategori produk tidak ditemukan!`); } if (!isExistProduk(kategoriIndex, kodeproduk)) { return await replyText(`❌ Kode produk *${kodeproduk}* tidak ditemukan!`); } let Produk = JSON.parse(fs.readFileSync("./Database/Kategori/" + kategoriprodukFileName)); let ps = Produk.findIndex(p => p.kodeproduk.toLowerCase() === kodeproduk.toLowerCase()); if (ps !== -1) { let stok = Produk[ps].tipeproduk.toLowerCase() === "file" ? Produk[ps].stokproduk : Produk[ps].dataproduk.length; if (stok === 0) { return await replyText( `❌ Maaf, produk *${Produk[ps].namaproduk}* saat ini *habis*.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[ps].kodeproduk}${petik}` ); } if (jumlah > stok) { return await replyText( `❌ Jumlah beli melebihi stok tersedia!\n` + `Stok saat ini: *${stok}*.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[ps].kodeproduk}${petik}` ); } let hargaSatuan = Produk[ps].hargaproduk; let diskonBulk = 0; if (hargaSatuan < 5000) { if (jumlah >= 5 && jumlah <= 9) { diskonBulk = 10; } else if (jumlah >= 10) { diskonBulk = 15; } } else { if (jumlah >= 5 && jumlah <= 9) { diskonBulk = 6; } else if (jumlah >= 10) { diskonBulk = 10; } } let diskonProduk = Produk[ps].diskonproduk || 0; let hargaSetelahDiskonProduk = hargaSatuan * (100 - diskonProduk) / 100; let hargaFinalPerUnit = hargaSetelahDiskonProduk * (100 - diskonBulk) / 100; let totalHarga = jumlah * hargaFinalPerUnit; let Unique = "MZS" + require("crypto").randomBytes(3).toString("hex").toUpperCase(); let DataBeli = { buyer: sender, unique: Unique, kategoriproduk: kategoriprodukFileName.replace(".json", ""), kodeproduk: kodeproduk, harga: totalHarga, jumlah: jumlah, isBulk: true, bulkDiscountApplied: diskonBulk }; fs.writeFileSync(transaksiPath, JSON.stringify(DataBeli, null, 3)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const currentUser = global.User.find(u => u.id === sender); const saldoUser = currentUser ? currentUser.saldo : 0; let txt = `*KONFIRMASI PESANAN BULK* > *Order ID:* ${petik}${Unique}${petik} > *Kode:* ${Produk[ps].kodeproduk} > *Produk:* ${Produk[ps].namaproduk} > *Harga asli per unit:* ${formatrupiah(hargaSatuan)} > *Diskon produk:* ${diskonProduk}% > *Diskon Bulk:* ${diskonBulk}% > *Harga final per unit:* ${formatrupiah(hargaFinalPerUnit)} > *Jumlah beli:* ${jumlah} > *Total Harga:* ${formatrupiah(totalHarga)} Silahkan pilih metode pembayaran untuk melanjutkan - ${petik}manual${petik} (Saldo kamu: *${formatrupiah(saldoUser)}*) - ${petik}qris${petik} (Pembayaran langsung) - ${petik}batal${petik} (Batalkan pesanan)`; await replyText(txt); } break; } case 'manual': { resetIfNewDay(); if (!isRegistered(sender)) return await replyText(alerts.daftar); const transaksiPath = `./Database/Trx/${sender.split("@")[0]}.json`; if (!fs.existsSync(transaksiPath)) return await replyText(`Kamu belum memilih produk apapun!`); const wib = moment().tz("Asia/Jakarta").format('dddd, D MMMM YYYY HH:mm:ss z'); let Data = JSON.parse(fs.readFileSync(transaksiPath)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); const saldoUser = cekSaldo(sender, global.User); let Produk = JSON.parse(fs.readFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json")); let po = Produk.findIndex(p => p.kodeproduk.toLowerCase() === Data.kodeproduk.toLowerCase()); if (po === -1) { fs.unlinkSync(transaksiPath); return await replyText(`❌ Produk *${Data.kodeproduk}* tidak ditemukan lagi. Transaksi dibatalkan.`); } let currentStok = Produk[po].tipeproduk.toLowerCase() === "file" ? Produk[po].stokproduk : Produk[po].dataproduk.length; if (currentStok < Data.jumlah) { fs.unlinkSync(transaksiPath); return await replyText( `❌ Maaf, stok produk *${Produk[po].namaproduk}* (${Data.kodeproduk}) saat ini *habis* atau *tidak mencukupi* (${currentStok} tersedia, Anda meminta ${Data.jumlah}). Saldo Anda tidak dipotong. Transaksi dibatalkan.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[po].kodeproduk}${petik}` ); } if (saldoUser < Data.harga) { fs.unlinkSync(transaksiPath); return await replyText(`❌ Saldo kamu tidak cukup untuk metode *manual*.\nTotal harga: ${formatrupiah(Data.harga)}\nSaldo kamu: ${formatrupiah(saldoUser)}\n\nSilakan pilih metode *qris* atau lakukan *deposit* terlebih dahulu.`); } minSaldoSafe(sender, Number(Data.harga)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let DataProduk = ""; await replyText("Pesananmu sedang diproses."); await sleep(1000); if (Produk[po].tipeproduk.toLowerCase() === "data") { for (let i = 0; i < Number(Data.jumlah); i++) { let akun = Produk[po].dataproduk[0]; let [mail, pass, key, profil, pin] = akun.split(":"); DataProduk += `- Email : ${mail || '-'}\n- Pass : ${pass || '-'}\n- Key : ${key || '-'}\n- Profil : ${profil || '-'}\n- PIN : ${pin || '-'}\n\n`; Produk[po].dataproduk.splice(0, 1); } if (Produk[po].dataproduk.length === 0) { let notif = `⚠️ *STOK HABIS*\n\nProduk: *${Produk[po].namaproduk}*\nKode: *${Produk[po].kodeproduk}*\nKategori: *${Data.kategoriproduk}*`; await tehtarik.sendMessage(global.nomerowner, { text: notif }); } fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); fs.unlinkSync(transaksiPath); Produk[po].stokTerjual = (Produk[po].stokTerjual || 0) + Number(Data.jumlah); fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); await tehtarik.sendMessage(sender, { text: `*「 TRANSAKSI BERHASIL 」* *DETAIL PESANAN :* > *Customer:* ${pushName} > *Order ID:* ${petik}${Data.unique}${petik} > *Produk:* ${Produk[po].namaproduk} > *Kode:* ${Produk[po].kodeproduk} > *Jumlah Beli:* ${Data.jumlah} > *Harga:* ${formatrupiah(Data.harga)} > *Waktu:* ${wib} *NOTE:* ${Produk[po].catatanproduk} *「 BERIKUT DETAIL AKUN ANDA 」* ${DataProduk} Klaim garansi ketik ${petik}garansi ${Data.unique} (mail & alasan)${petik}\nHarap simpan baik-baik pesan ini dan jangan sampai terhapus, terimakasih. \n_${global.footer}_`}); } else if (Produk[po].tipeproduk.toLowerCase() === "file") { Produk[po].stokproduk -= 1; fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); fs.unlinkSync(transaksiPath); Produk[po].stokTerjual = (Produk[po].stokTerjual || 0) + Number(Data.jumlah); fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); await replyText(`*DETAIL PESANAN* *- Order ID:* ${Data.unique} *- Produk:* ${Produk[po].namaproduk} *- Kode:* ${Produk[po].kodeproduk} *- Jumlah Beli:* ${Data.jumlah} *- Harga:* ${formatrupiah(Data.harga)} ||${Produk[po].dataproduk[0]}|| Note: ${Produk[po].catatanproduk} _${global.footer}_`);} await tehtarik.sendMessage(sender, { sticker: fs.readFileSync('./terimakasih_stiker.webp') }); let log = `*LOG TRANSAKSI*\n\n${DataProduk}📝 *Order ID:* ${Data.unique}\n👤 *Buyer:* ${sender.split("@")[0]}\n🛍 *Produk:* ${Produk[po].namaproduk}\n📊 *Jumlah Beli:* ${Data.jumlah}\n💸 *Harga:* ${formatrupiah(Data.harga)}\n💳 *Pembayaran:* Manual\n📆 *Waktu:* ${wib}`; await tehtarik.sendMessage(global.nomerowner, { text: log }); const tanggal = getTodayDateWIB(); let Transaksi = JSON.parse(fs.readFileSync("./Database/Transaksi.json")); const DataTrx = { buyer: sender, unique: Data.unique, tanggal: tanggal, produk: Produk[po].namaproduk, harga: Data.harga, metode: 'manual' }; Transaksi.push(DataTrx); fs.writeFileSync("./Database/Transaksi.json", JSON.stringify(Transaksi, null, 3)); resetIfNewDay(); let trxHarian = fs.existsSync(transaksiHarianPath) ? JSON.parse(fs.readFileSync(transaksiHarianPath)) : []; trxHarian.push(DataTrx); fs.writeFileSync(transaksiHarianPath, JSON.stringify(trxHarian, null, 3)); let pos = global.User.findIndex(u => u.id === sender); if (pos === -1) return await replyText("❌ User tidak ditemukan."); global.User[pos].jumlahtransaksi += 1; global.User[pos].pengeluaran += Number(Data.harga); fs.writeFileSync("./Database/User.json", JSON.stringify(global.User, null, 3)); let UserBaruUntukCashback = JSON.parse(fs.readFileSync("./Database/User.json")); let posisiCashback = UserBaruUntukCashback.findIndex(u => u.id === sender); if (posisiCashback !== -1) { applyCashbackIfEligible(sender, UserBaruUntukCashback[posisiCashback], tehtarik); } await tehtarik.sendMessage(sender, { text: `💰 *Sisa saldo kamu sekarang:* ${formatrupiah(cekSaldo(sender, global.User))}` }); break; } case 'qris': { resetIfNewDay(); if (qrLoopStatus[sender] && qrLoopStatus[sender].aktif) { return await replyText("❌ Kamu masih memiliki QRIS aktif! Ketik *batal* dulu untuk membatalkan."); } if (!isRegistered(sender)) return await replyText(alerts.daftar); const transaksiPath = `./Database/Trx/${sender.split("@")[0]}.json`; if (!fs.existsSync(transaksiPath)) return await replyText(`Kamu belum memilih produk apapun!`); const wib = moment().tz("Asia/Jakarta").format('dddd, D MMMM YYYY HH:mm:ss z'); let Data = JSON.parse(fs.readFileSync(transaksiPath)); let Produk = JSON.parse(fs.readFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json")); let po = Produk.findIndex(p => p.kodeproduk.toLowerCase() === Data.kodeproduk.toLowerCase()); if (po === -1) { fs.unlinkSync(transaksiPath); return await replyText(`❌ Produk *${Data.kodeproduk}* tidak ditemukan lagi. Transaksi dibatalkan.`); } let currentStok = Produk[po].tipeproduk.toLowerCase() === "file" ? Produk[po].stokproduk : Produk[po].dataproduk.length; if (currentStok < Data.jumlah) { fs.unlinkSync(transaksiPath); return await replyText( `❌ Maaf, stok produk *${Produk[po].namaproduk}* (${Data.kodeproduk}) saat ini *habis* atau *tidak mencukupi* (${currentStok} tersedia, Anda meminta ${Data.jumlah}). Transaksi dibatalkan.\n` + `Ingin diberi tahu saat restok?\nKetik: ${petik}ingatkan ${Produk[po].kodeproduk}${petik}` ); } let DataProduk = ""; let Unique = "MZS" + require("crypto").randomBytes(5).toString("hex").toUpperCase(); let fee = digit(Number(Data.harga)); let totalAmount = Number(Data.harga) + Number(fee); let time = Date.now() + toMs("5m"); let filePathQR = `./Database/QR/${Unique}.jpg`; try { let dd = await axios.post(`https://atlantich2h.com/deposit/create`, { api_key: atlantic.apikey, reff_id: Unique, nominal: totalAmount, type: "ewallet", metode: "qris" // pengen cair instant ganti "qris" jadi "qrisfast" }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); if (dd.data && dd.data.data && dd.data.data.qr_string) { let dgp = await createQris(filePathQR, dd.data.data.qr_string); let msggg = await tehtarik.sendMessage(from, { image: fs.readFileSync(dgp), caption: `*DETAIL PEMBAYARAN* *- Kode Transaksi:* ${Unique} *- Produk:* ${Produk[po].namaproduk} *- Kode:* ${Produk[po].kodeproduk} *- Jumlah beli:* ${Data.jumlah} *- Fee QRIS:* ${fee} *- Harga:* ${formatrupiah(totalAmount)} *- Expired:* 5 Menit Silakan scan QRIS di atas untuk melanjutkan pembayaran. Pembayaran akan dicek otomatis setiap 5 detik. Ketik ${petik}batal${petik} untuk membatalkan pesanan.` }); qrLoopStatus[sender] = { aktif: true, msgKey: msggg.key, filePath: filePathQR, atlanticDepositId: dd.data.data.id }; let statusP = false; while (!statusP && qrLoopStatus[sender] && qrLoopStatus[sender].aktif) { await sleep(5000); if (Date.now() >= time) { statusP = true; await tehtarik.sendMessage(from, { delete: msggg.key }); await replyText("QRIS Pesananmu telah expired."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); if (qrLoopStatus[sender] && qrLoopStatus[sender].atlanticDepositId) { const currentDepositId = qrLoopStatus[sender].atlanticDepositId; try { response = await axios.post(`https://atlantich2h.com/deposit/cancel`, { api_key: atlantic.apikey, id: currentDepositId }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); } catch {} } delete qrLoopStatus[sender]; if (fs.existsSync(transaksiPath)) { fs.unlinkSync(transaksiPath); } break; } try { let response = await axios.post(`https://atlantich2h.com/deposit/status`, { api_key: atlantic.apikey, id: dd.data.data.id }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); console.log("[ ATLANTIC LOG ] : " + JSON.stringify(response.data.data, null, 3)); let status = response.data.data.status; if (status === "processing") { // ubah processing ke success jika pake qrisfast statusP = true; // Hapus /* dan */ supaya metode qris bisa cair instant tanpa pke qrisfast /*await axios.post(`https://atlantich2h.com/deposit/instant`, { api_key: atlantic.apikey, id: dd.data.data.id, action: true }, { headers: { 'Content-Type': "application/x-www-form-urlencoded" } });*/ await tehtarik.sendMessage(from, { delete: msggg.key }); Produk = JSON.parse(fs.readFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json")); // Reload product data po = Produk.findIndex(p => p.kodeproduk.toLowerCase() === Data.kodeproduk.toLowerCase()); if (po === -1 || (Produk[po].tipeproduk.toLowerCase() === "file" ? Produk[po].stokproduk : Produk[po].dataproduk.length) < Data.jumlah) { await replyText( `❌ Pembayaran berhasil, namun stok produk *${Produk[po]?.namaproduk || Data.kodeproduk}* saat ini *habis* atau *tidak mencukupi*. Dana Anda akan direfund dalam waktu 1x24 jam. Kami mohon maaf atas ketidaknyamanannya.` ); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); if (fs.existsSync(transaksiPath)) fs.unlinkSync(transaksiPath); delete qrLoopStatus[sender]; return; }fs.unlinkSync(transaksiPath); Produk[po].stokTerjual = (Produk[po].stokTerjual || 0) + Number(Data.jumlah); fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); if (Produk[po].tipeproduk.toLowerCase() === "data") { for (let i = 0; i < Number(Data.jumlah); i++) { let akun = Produk[po].dataproduk[0]; let [mail, pass, key, profil, pin] = akun.split(":"); DataProduk += `- Email : ${mail || '-'}\n- Pass : ${pass || '-'}\n- Key : ${key || '-'}\n- Profil : ${profil || '-'}\n- PIN : ${pin || '-'}\n\n`; Produk[po].dataproduk.splice(0, 1); } if (Produk[po].dataproduk.length === 0) { let notif = `⚠️ *STOK HABIS*\n\nProduk: *${Produk[po].namaproduk}*\nKode: *${Produk[po].kodeproduk}*\nKategori: *${Data.kategoriproduk}*`; await tehtarik.sendMessage(global.nomerowner, { text: notif }); } fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); let tujuan = isGroup ? sender : from; await tehtarik.sendMessage(tujuan, { text: `*「 TRANSAKSI BERHASIL 」* *DETAIL PESANAN :* > *Customer:* ${pushName} > *Order ID:* ${petik}${Data.unique}${petik} > *Produk:* ${Produk[po].namaproduk} > *Kode:* ${Produk[po].kodeproduk} > *Jumlah beli:* ${Data.jumlah} > *Harga (included fee):* ${formatrupiah(totalAmount)} > *Waktu:* ${wib} *NOTE :* ${Produk[po].catatanproduk} *「 BERIKUT DETAIL AKUN ANDA 」* ${DataProduk} Klaim garansi ketik ${petik}garansi ${Data.unique} (mail & alasan)${petik}\nHarap simpan baik-baik pesan ini dan jangan sampai terhapus, terimakasih. \n_${global.footer}_` }); } else if (Produk[po].tipeproduk.toLowerCase() === "file") { Produk[po].stokproduk -= 1; fs.writeFileSync("./Database/Kategori/" + Data.kategoriproduk + ".json", JSON.stringify(Produk, null, 3)); await replyText(`*DETAIL PESANAN* *- Order ID:* ${Data.unique} *- Produk:* ${Produk[po].namaproduk} *- Kode:* ${Produk[po].kodeproduk} *- Jumlah beli:* ${Data.jumlah} *- Harga:* ${formatrupiah(Data.harga)} ||${Produk[po].dataproduk[0]}|| Note : ${Produk[po].catatanproduk} \n_${global.footer}_`); } await tehtarik.sendMessage(sender, { sticker: fs.readFileSync('./terimakasih_stiker.webp') }); let log = `*LOG TRANSAKSI*\n\n`; log += `${DataProduk}`; log += `📝 *Order ID:* ${Data.unique}\n`; log += `👤 *Buyer:* ${sender.split("@")[0]}\n`; log += `🛍 *Produk:* ${Produk[po].namaproduk}\n`; log += `📊 *Jumlah Beli:* ${Data.jumlah}\n`; log += `💸 *Harga:* ${formatrupiah(Data.harga)}\n`; log += `💳 *Pembayaran:* QRIS\n`; log += `📆 *Waktu:* ${wib}`; await sleep(2000); await tehtarik.sendMessage(global.nomerowner, { text: log }); const tanggal = getTodayDateWIB(); let Transaksi = JSON.parse(fs.readFileSync("./Database/Transaksi.json")); let DataTrx = { buyer: sender, unique: Data.unique, tanggal: tanggal, produk: Produk[po].namaproduk, harga: Data.harga, metode: 'qris' }; resetIfNewDay(); let trxHarian = []; if (fs.existsSync(transaksiHarianPath)) { trxHarian = JSON.parse(fs.readFileSync(transaksiHarianPath)); } trxHarian.push(DataTrx); fs.writeFileSync(transaksiHarianPath, JSON.stringify(trxHarian, null, 3)); Transaksi.push(DataTrx); fs.writeFileSync("./Database/Transaksi.json", JSON.stringify(Transaksi, null, 3)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); let pp = global.User.findIndex(u => u.id === sender); if (pp !== -1) { global.User[pp].jumlahtransaksi += 1; global.User[pp].pengeluaran += Number(Data.harga); fs.writeFileSync("./Database/User.json", JSON.stringify(global.User, null, 3)); let UserBaruUntukCashback = JSON.parse(fs.readFileSync("./Database/User.json")); let posisiCashback = UserBaruUntukCashback.findIndex(u => u.id === sender); if (posisiCashback !== -1) { applyCashbackIfEligible(sender, UserBaruUntukCashback[posisiCashback], tehtarik); } } if (fs.existsSync(filePathQR)) { fs.unlinkSync(filePathQR); } else { console.log(`File QR tidak ditemukan saat ingin dihapus.`); } delete qrLoopStatus[sender]; } } catch (err) { console.log("Error saat cek mutasi QRIS (loop):", err.message); } } } else { await replyText("❌ Gagal membuat QRIS. Mohon coba lagi nanti atau hubungi admin."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); delete qrLoopStatus[sender]; if (fs.existsSync(transaksiPath)) fs.unlinkSync(transaksiPath); } } catch (err) { console.log("Error saat request QRIS ke Atlantic:", err.message); await replyText("❌ Terjadi kesalahan saat memproses QRIS. Mohon coba lagi nanti."); if (fs.existsSync(filePathQR)) fs.unlinkSync(filePathQR); delete qrLoopStatus[sender]; if (fs.existsSync(transaksiPath)) fs.unlinkSync(transaksiPath); } break; } case 'kodebank': { try { const payload = new URLSearchParams({ api_key: atlantic.apikey }); const res = await axios.post("https://atlantich2h.com/transfer/bank_list", payload.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const list = res.data?.data; if (!Array.isArray(list) || list.length === 0) { return await replyText("❌ Gagal ambil list bank."); } const batchSize = 50; const total = list.length; const totalPage = Math.ceil(total / batchSize); for (let i = 0; i < total; i += batchSize) { const batch = list.slice(i, i + batchSize); const page = Math.floor(i / batchSize) + 1; let teks = `🏦 *Daftar Kode Bank Atlantic* (Bagian ${page} dari ${totalPage}):\n\n - \n`; for (const item of batch) { teks += `• ${item.bank_code} - ${item.bank_name}\n`; } teks += `\nTotal: ${total} bank\nGunakan: cekrekening |`; await replyText(teks); } } catch (err) { console.error("Gagal ambil list bank:", err.message); return await replyText("❌ Gagal mengambil kode bank."); } break; } case 'cekrekening': { if (!isOwner) return await replyText("❌ Khusus owner!"); const [bank_code, account_number] = text.split("|").map(e => e.trim()); if (!bank_code || !account_number) { return await replyText("❌ Format salah!\nContoh:\ncekrekening 002|1234567890"); } try { const payload = new URLSearchParams(); payload.append("api_key", atlantic.apikey); payload.append("bank_code", bank_code); payload.append("account_number", account_number); const res = await axios.post("https://atlantich2h.com/transfer/cek_rekening", payload, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const data = res.data; if (data?.status && data?.data?.nama_pemilik) { return await replyText(` ✅ *Rekening ditemukan!* 🏦 Bank: ${data.data.kode_bank} 💳 Rek: ${data.data.nomor_akun} 👤 Nama: ${data.data.nama_pemilik} 🔒 Status: ${data.data.status} `); } else { return await replyText(`❌ Gagal cek rekening: ${data?.msg || 'Tidak ditemukan'}`); } } catch (err) { console.error("Cek rekening error:", err.message); return await replyText("❌ Terjadi kesalahan saat cek rekening."); } break; } case 'transfer': { if (!isOwner) return await replyText("❌ Khusus owner!"); const [kode_bank, no_rek, nama_penerima, nominalStr, note] = text.split("|").map(e => e.trim()); if (!kode_bank || !no_rek || !nama_penerima || !nominalStr) { return await replyText("❌ Format salah!\nContoh:\ntransfer kode_bank|no_rek|nama_penerima|jumlah_transfer|catatan"); } const nominal = parseInt(nominalStr); if (isNaN(nominal) || nominal < 100) { return await replyText("❌ Nominal harus berupa angka dan minimal 100"); } const ref_id = `TRF-${Date.now()}`; try { const payload = new URLSearchParams({ api_key: atlantic.apikey, ref_id, kode_bank, nomor_akun: no_rek, nama_pemilik: nama_penerima, nominal: nominal.toString(), note: note || 'Transfer via bot' }); const res = await axios.post("https://atlantich2h.com/transfer/create", payload.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const response = res.data; if (!response.status || !response.data?.id) { return await replyText(`❌ Gagal transfer: ${response?.msg || 'Terjadi kesalahan'}`); } const id = response.data.id; const detail = ` ✅ *Transfer berhasil dibuat!* 🆔 ID: ${id} 📄 Ref ID: ${ref_id} 🏦 Bank: ${kode_bank} 💳 Rek: ${no_rek} 👤 Nama: ${nama_penerima} 💰 Nominal: ${formatrupiah(nominal)} 📌 Note: ${note || '-'} 🔄 Status: pending `; await replyText(detail); let attempt = 0; const maxAttempts = 12; // 2 menit const interval = setInterval(async () => { try { attempt++; const statusPayload = new URLSearchParams({ api_key: atlantic.apikey, id: id }); const statusRes = await axios.post("https://atlantich2h.com/transfer/status", statusPayload.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const statusData = statusRes.data?.data; const status = statusData?.status?.toLowerCase(); if (status === "success" || status === "failed") { clearInterval(interval); const statusNotif = ` ${status === "success" ? "✅ *Transfer berhasil!*" : "❌ *Transfer gagal!*"} 🆔 ID: ${id} 📄 Ref ID: ${ref_id} 🏦 Bank: ${kode_bank} 💳 Rek: ${no_rek} 👤 Nama: ${nama_penerima} 💰 Nominal: ${formatrupiah(nominal)} 📌 Note: ${note || '-'} 🔄 Status: ${status.charAt(0).toUpperCase() + status.slice(1)} ${status === "success" ? "✅" : "❌"} `; await tehtarik.sendMessage(from, { text: statusNotif }, { quoted: msg }); } if (attempt >= maxAttempts) { clearInterval(interval); await tehtarik.sendMessage(from, { text: `⏱️ Status transfer *masih pending* setelah 2 menit. Cek manual:\ntransferstatus ${id}` }, { quoted: msg }); } } catch (err) { clearInterval(interval); console.error("Gagal polling status:", err.message); await tehtarik.sendMessage(from, { text: "❌ Gagal mengecek status transfer. Silakan cek manual:\ntransferstatus " }, { quoted: msg }); } }, 10_000); // tiap 10 detik } catch (err) { console.error("Transfer error:", err.message); return await replyText("❌ Terjadi kesalahan saat menghubungi server transfer."); } break; } case 'transferstatus': { if (!isOwner) return await replyText("❌ Khusus owner!"); if (!text) return await replyText("❌ Masukkan ID transfer.\nContoh: transferstatus ID123"); try { const payload = new URLSearchParams({ api_key: atlantic.apikey, id: text.trim() }); const res = await axios.post("https://atlantich2h.com/transfer/status", payload.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const data = res.data?.data; if (!res.data?.status || !data?.id) { return await replyText(`❌ Tidak ditemukan atau ID salah.`); } const out = ` 🔍 *Status Transfer* 🆔 ID: ${data.id} 📄 Ref ID: ${data.reff_id} 🏦 Bank: ${data.bank_code} 💳 Tujuan: ${data.nomor_tujuan} 👤 Nama: ${data.nama} 💰 Nominal: ${formatrupiah(data.nominal)} 💸 Fee: ${formatrupiah(data.fee)} 📦 Total: ${formatrupiah(data.total)} 📌 Note: ${data.detail?.note || "-"} 📧 Email: ${data.detail?.email || "-"} 📱 HP: ${data.detail?.phone || "-"} ⏰ Dibuat: ${data.created_at} 🔄 Status: ${data.status.toUpperCase()} `.trim(); await replyText(out); } catch (err) { console.error("Gagal cek status transfer:", err.message); return await replyText("❌ Gagal mengambil status transfer."); } break; } case 'info': { if (!isOwner) return await replyText("❌ Khusus owner!"); try { const payload = new URLSearchParams({ api_key: atlantic.apikey }); const res = await axios.post("https://atlantich2h.com/get_profile", payload.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const data = res.data?.data; if (!res.data?.status || !data?.username) { return await replyText("❌ Gagal mengambil info akun Atlantic."); } const teks = ` 👤 *INFO AKUN ATLANTIC* 🧑 Username: ${data.username} 🏷️ Nama: ${data.name} 📧 Email: ${data.email} 📱 Nomor HP: ${data.phone} 💰 Saldo: ${formatrupiah(data.balance)} 🛡️ Status: ${data.status.toUpperCase()} `.trim(); await replyText(teks); } catch (err) { console.error("❌ Gagal ambil profil Atlantic:", err.message); await replyText("❌ Terjadi kesalahan saat mengambil info akun."); } break; } case 'batal': case 'cancel': { if (!isRegistered(sender)) return await replyText(alerts.daftar); const transaksiPath = `./Database/Trx/${sender.split("@")[0]}.json`; if (qrLoopStatus[sender] && qrLoopStatus[sender].aktif) { try { const currentDepositId = qrLoopStatus[sender].atlanticDepositId; if (currentDepositId) { await axios.post(`https://atlantich2h.com/deposit/cancel`, { api_key: atlantic.apikey, id: currentDepositId }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }) } if (qrLoopStatus[sender].msgKey) { await tehtarik.sendMessage(from, { delete: qrLoopStatus[sender].msgKey }); } if (fs.existsSync(qrLoopStatus[sender].filePath)) { fs.unlinkSync(qrLoopStatus[sender].filePath); } } catch (e) { console.log("Gagal hapus QR saat batal:", e); } delete qrLoopStatus[sender]; if (fs.existsSync(transaksiPath)) { fs.unlinkSync(transaksiPath); } return await replyText("✅ Transaksi aktif dan QRIS berhasil dibatalkan."); } if (fs.existsSync(transaksiPath)) { fs.unlinkSync(transaksiPath); return await replyText("✅ Transaksi aktif Anda telah dibatalkan."); } return await replyText("❌ Anda tidak memiliki transaksi aktif yang dapat dibatalkan."); } case 'riwayat': case 'transaksi': { if (!isRegistered(sender)) return await replyText(alerts.daftar); let Transaksi = JSON.parse(fs.readFileSync("./Database/Transaksi.json")); let riwayatUser = Transaksi.filter(trx => trx.buyer === sender); if (riwayatUser.length === 0) { return await replyText("❌ Kamu belum pernah melakukan transaksi."); } let lastTrx = riwayatUser.slice(-5).reverse(); let txt = `📦 *Riwayat Transaksi Terakhir Kamu:*\n\n`; lastTrx.forEach((trx, index) => { txt += `${index + 1}. ✅ ${trx.produk}\n`; txt += ` 💸 ${formatrupiah(trx.harga)}\n`; txt += ` 🆔 Order ID: ${trx.unique}\n`; txt += ` 📅 ${trx.tanggal}\n\n`; }); txt += `Keluhan/garansi ketik ${petik}klaim on${petik}\n\n_${global.footer}_`; await replyText(txt); break; } case 'rekapbulan': case 'rekap': { if (!isOwner) return await replyText("❌ Khusus owner!"); const parts = text.trim().split(/\s+/); let targetMonthName, targetYear; if (parts.length === 2) { targetMonthName = parts[0]; targetYear = parseInt(parts[1]); } else if (parts.length === 0) { const now = moment().tz("Asia/Jakarta"); targetMonthName = now.locale('id').format("MMMM"); targetYear = now.year(); } else { return await replyText("❌ Format salah! Contoh: *rekapbulan Mei 2025*"); } let monthNamesId; try { const localeData = moment.localeData('id'); if (localeData && localeData._months) { monthNamesId = localeData._months; } else { console.error("❌ Gagal memuat nama bulan dari moment locale 'id'. Mungkin locale 'id' belum siap."); return await replyText("❌ Terjadi masalah dalam memuat data bulan. Coba lagi atau hubungi developer."); } } catch (e) { console.error("❌ Error saat mencoba mendapatkan nama bulan dari moment locale 'id' di rekapbulan:", e.message); return await replyText("❌ Terjadi masalah internal. Coba lagi atau hubungi developer."); } const monthIndex = monthNamesId.findIndex(m => m.toLowerCase() === targetMonthName.toLowerCase()); if (monthIndex === -1 || isNaN(targetYear)) { return await replyText("❌ Bulan atau tahun tidak valid. Pastikan penulisan bulan benar (misal: Januari, Februari) dan tahun adalah angka."); } const transaksiPath = "./Database/Transaksi.json"; if (!fs.existsSync(transaksiPath)) { return await replyText("📭 File Transaksi.json tidak ditemukan."); } const TransaksiGlobal = JSON.parse(fs.readFileSync(transaksiPath, 'utf-8')); const bulanIni = TransaksiGlobal.filter(trx => { if (!trx.tanggal) return false; let m = moment(trx.tanggal, 'YYYY-MM-DD'); if (!m.isValid()) { m = moment(trx.tanggal, 'dddd, D MMMM YYYY', 'id'); } return m.isValid() && m.month() === monthIndex && m.year() === targetYear; }); if (bulanIni.length === 0) { return await replyText(`📭 Belum ada transaksi pada bulan *${moment().month(monthIndex).locale('id').format("MMMM")} ${targetYear}*.`); } const total = bulanIni.length; const totalHarga = bulanIni.reduce((sum, t) => sum + Number(t.harga || 0), 0); const manualList = bulanIni.filter(t => t.metode === 'manual'); const qrisList = bulanIni.filter(t => t.metode === 'qris'); const countManual = manualList.length; const countQris = qrisList.length; const totalManual = manualList.reduce((sum, t) => sum + Number(t.harga || 0), 0); const totalQris = qrisList.reduce((sum, t) => sum + Number(t.harga || 0), 0); let produkHitung = {}, userTotal = {}; bulanIni.forEach(trx => { produkHitung[trx.produk] = (produkHitung[trx.produk] || 0) + 1; const no = trx.buyer.split("@")[0]; userTotal[no] = (userTotal[no] || 0) + Number(trx.harga || 0); }); const topProduk = Object.entries(produkHitung).sort((a, b) => b[1] - a[1]).slice(0, 5); const topUser = Object.entries(userTotal).sort((a, b) => b[1] - a[1]).slice(0, 5); let txt = `📆 *LAPORAN BULANAN - ${moment().month(monthIndex).locale('id').format("MMMM").toUpperCase()} ${targetYear}*\n\n`; txt += `📦 Total Transaksi: *${total}x*\n`; txt += `💵 Manual : *${countManual}x* : ${formatrupiah(totalManual)}\n`; txt += `📱 QRIS : *${countQris}x* : ${formatrupiah(totalQris)}\n`; txt += `💰 Total Omzet: ${formatrupiah(totalHarga)}\n\n`; txt += `🔥 *Produk Terlaris:*\n`; if (topProduk.length > 0) { topProduk.forEach(([nama, jml], i) => { txt += `${i + 1}. ${nama} (${jml}x)\n`; }); } else { txt += `Tidak ada produk terlaris.\n`; } txt += `\n👑 *Top 5 User Aktif:*\n`; if (topUser.length > 0) { topUser.forEach(([no, sum], i) => { txt += `${i + 1}. @${no} - ${formatrupiah(sum)}\n`; }); } else { txt += `Tidak ada top user aktif.\n`; } await replyText(txt); break; } case 'trxhari': { if (!isOwner) return await replyText("❌ Khusus owner!"); const today = getTodayDateWIB(); const transaksiPath = './Database/Transaksi.json'; if (!fs.existsSync(transaksiPath)) { return await replyText("❌ Belum ada data transaksi."); } const Transaksi = JSON.parse(fs.readFileSync(transaksiPath)); const hariIni = Transaksi.filter(t => t.tanggal === today); if (hariIni.length === 0) { return await replyText(`📭 Belum ada transaksi pada ${today}`); } let total = hariIni.length; let manual = hariIni.filter(t => t.metode === 'manual'); let qris = hariIni.filter(t => t.metode === 'qris'); let totalOmzet = hariIni.reduce((a, b) => a + Number(b.harga || 0), 0); let totalManual = manual.reduce((a, b) => a + Number(b.harga || 0), 0); let totalQris = qris.reduce((a, b) => a + Number(b.harga || 0), 0); let laporan = `📊 *Laporan Transaksi Harian (${today}):*\n\n`; laporan += `📦 Total Transaksi: *${total}x*\n`; laporan += `💵 Manual: *${manual.length}x* : ${formatrupiah(totalManual)}\n`; laporan += `📱 QRIS: *${qris.length}x* : ${formatrupiah(totalQris)}\n`; laporan += `💰 Total Omzet: ${formatrupiah(totalOmzet)}\n\n`; laporan += `🗒 *Rincian Transaksi:*\n\n`; hariIni.forEach((t, i) => { const buyer = t.buyer.replace("@s.whatsapp.net", ""); const tanggalIndo = moment(t.tanggal).tz("Asia/Jakarta").locale("id").format("dddd, D MMMM YYYY"); laporan += `${i + 1}. ${t.produk} 💰 ${formatrupiah(t.harga)} 👤 ${buyer} 🆔 ${t.unique} 📅 ${tanggalIndo}\n\n`; }); await replyText(laporan); break; } case 'trxbulan': case 'laporanbulan': { if (!isOwner) return await replyText("❌ Khusus owner!"); const transaksiPath = "./Database/Transaksi.json"; if (!fs.existsSync(transaksiPath)) { return await replyText("📭 File Transaksi.json tidak ditemukan."); } const Transaksi = JSON.parse(fs.readFileSync(transaksiPath)); const now = moment().tz("Asia/Jakarta"); const currentMonth = now.locale('id').format("MMMM YYYY"); const bulanIni = Transaksi.filter(trx => { if (!trx.tanggal) return false; let m = moment(trx.tanggal, 'YYYY-MM-DD'); if (!m.isValid()) { m = moment(trx.tanggal, 'dddd, D MMMM YYYY', 'id'); } return m.isValid() && m.month() === now.month() && m.year() === now.year(); }); const total = bulanIni.length; const totalHarga = bulanIni.reduce((sum, t) => sum + Number(t.harga || 0), 0); const manualList = bulanIni.filter(t => t.metode === 'manual'); const qrisList = bulanIni.filter(t => t.metode === 'qris'); const countManual = manualList.length; const countQris = qrisList.length; const totalManual = manualList.reduce((sum, t) => sum + Number(t.harga || 0), 0); const totalQris = qrisList.reduce((sum, t) => sum + Number(t.harga || 0), 0); let produkHitung = {}, userTotal = {}; bulanIni.forEach(trx => { produkHitung[trx.produk] = (produkHitung[trx.produk] || 0) + 1; const no = trx.buyer.split("@")[0]; userTotal[no] = (userTotal[no] || 0) + Number(trx.harga || 0); }); const topProduk = Object.entries(produkHitung).sort((a, b) => b[1] - a[1]).slice(0, 5); const topUser = Object.entries(userTotal).sort((a, b) => b[1] - a[1]).slice(0, 5); let txt = `📆 *LAPORAN BULANAN - ${currentMonth.toUpperCase()}*\n\n`; txt += `📦 Total Transaksi: *${total}x*\n`; txt += `💵 Manual : *${countManual}x* : ${formatrupiah(totalManual)}\n`; txt += `📱 QRIS : *${countQris}x* : ${formatrupiah(totalQris)}\n`; txt += `💰 Total Omzet: ${formatrupiah(totalHarga)}\n\n`; txt += `🔥 *Produk Terlaris:*\n`; topProduk.forEach(([nama, jml], i) => { txt += `${i + 1}. ${nama} (${jml}x)\n`; }); txt += `\n👑 *Top 5 User Aktif:*\n`; topUser.forEach(([no, sum], i) => { txt += `${i + 1}. @${no} - ${formatrupiah(sum)}\n`; }); await replyText(txt); break; } case 'addv': case 'buatvoucher': { if (!isOwner) return await replyText("❌ Khusus owner!"); let [jumlah, nominal, masa] = text.split("|").map(s => s.trim()); if (!jumlah || !nominal || !masa) return await replyText("Format salah!\nContoh:\n#buatvoucher 5|10000|1d\n#buatvoucher 5|random|5m"); const ms = require("ms"); const duration = ms(masa); if (!duration) return await replyText("❌ Format waktu tidak valid!\nContoh: *1d*, *5m*, *2h*, etc."); const vouchers = fs.existsSync("./Database/Voucher.json") ? JSON.parse(fs.readFileSync("./Database/Voucher.json")) : []; let isRandom = nominal.toLowerCase() === "random"; let list = ""; function getRandomVoucherValue() { const chance = Math.random() * 100; if (chance < 94) { return Math.floor(Math.random() * (1500 - 100 + 1)) + 100; } else if (chance < 97) { return Math.floor(Math.random() * (3000 - 1600 + 1)) + 1600; } else if (chance < 99) { return Math.floor(Math.random() * (5000 - 3001 + 1)) + 3001; } else { return Math.floor(Math.random() * (10000 - 5001 + 1)) + 5001; } } for (let i = 0; i < Number(jumlah); i++) { let kode = "MZS" + require("crypto").randomBytes(5).toString("hex").toUpperCase(); let value = isRandom ? getRandomVoucherValue() : Number(nominal); vouchers.push({ kode, value, dipakai: false, expiredAt: Date.now() + duration }); list += `${kode}\n`; } fs.writeFileSync("./Database/Voucher.json", JSON.stringify(vouchers, null, 2)); let masaBerlaku = ms(duration, { long: true }) .replace(/seconds?/, 'Detik') .replace(/minutes?/, 'Menit') .replace(/hours?/, 'Jam') .replace(/days?/, 'Hari'); let keteranganNominal = isRandom ? 'Random' : formatrupiah(Number(nominal)); await replyText(`*VOUCHER BERHASIL DIBUAT*\n*Berlaku:* ${masaBerlaku}\n*Amount:* ${keteranganNominal}\n\n${list}\nCara redeem:\n${petik}redeem MZS123456${petik}\nRedeem di https://wa.me/62895385736633`); break; } case 'redeem': { if (!isRegistered(sender)) return await replyText(alerts.daftar); const kode = text.trim().toUpperCase(); if (!kode) return await replyText("Masukkan kode voucher.\nContoh: `redeem MZS27885856`"); if (!canRedeemVoucher(sender)) { return await replyText("❌ Batas maksimal redeem voucher hanya 1 kali per hari!"); } const Vouchers = JSON.parse(fs.readFileSync("./Database/Voucher.json")); const voucherIndex = Vouchers.findIndex(v => v.kode === kode); if (voucherIndex === -1) return await replyText("❌ Voucher tidak ditemukan."); const voucher = Vouchers[voucherIndex]; const now = Date.now(); if (voucher.expiredAt && now > voucher.expiredAt) { return await replyText("❌ Voucher sudah *expired* dan tidak bisa digunakan."); } if (voucher.dipakai) return await replyText("❌ Voucher ini sudah digunakan."); addSaldoSafe(sender, Number(voucher.value)); recordRedeemVoucher(sender); Vouchers[voucherIndex].dipakai = true; Vouchers[voucherIndex].pengguna = sender; Vouchers[voucherIndex].waktu = new Date().toISOString(); fs.writeFileSync("./Database/Voucher.json", JSON.stringify(Vouchers, null, 2)); global.User = JSON.parse(fs.readFileSync("./Database/User.json")); await replyText(`🎉 Voucher berhasil diredeem!\n💰 Kamu mendapatkan saldo: *${formatrupiah(Number(voucher.value))}*\nKetik ${petik}saldo${petik} untuk melihat saldo saat ini`); break; } case 'delvoucher': case 'delv': case 'hapusvoucher': { if (!isOwner) return await replyText("❌ Khusus owner!"); const path = "./Database/Voucher.json"; if (!fs.existsSync(path)) return await replyText("📭 Tidak ada data voucher!"); fs.writeFileSync(path, "[]"); await replyText("✅ Semua kode voucher berhasil dihapus."); break; } case 'ownermenu': case 'ownmenu': { if (!isOwner) return await replyText("❌ Khusus owner!"); let txt = `*👑 OWNER MENU - ${global.namabot}*\n\n 📁 *Manajemen Kategori & Produk* ├─ addkategori - Tambah kategori baru ├─ delkategori - Hapus kategori ├─ renamekategori - Ganti nama kategori ├─ addproduk - Tambah produk baru ├─ delproduk - Hapus produk ├─ addstok - Tambah stok produk ├─ minstok - Kurangi stok produk ├─ deletestok - Hapus semua stok produk ├─ cekstok - Lihat semua stok & produk habis ├─ setjual - Set jumlah terjual produk ├─ editnama - Ubah nama produk ├─ editkode - Ubah kode produk ├─ editharga - Ubah harga produk ├─ editdiskon - Atur diskon produk ├─ diskonall - Diskon semua produk ├─ resetdiskon - Reset semua diskon produk ├─ editdeskripsi - Ubah deskripsi produk ├─ editcatatan - Ubah catatan produk └─ hideproduk - Sembunyikan/tampilkan kategori\n\n 👥 *Manajemen User & Saldo* ├─ listuser - Daftar semua user ├─ addsaldo - Tambah saldo user ├─ minsaldo - Kurangi saldo user └─ deluser - Hapus user\n\n 🛡️ *Manajemen Garansi & Klaim* ├─ acc [unique] - Setujui klaim garansi ├─ tolak [unique] [alasan] - Tolak klaim garansi ├─ klaim on/off - Aktifkan/tutup sesi klaim └─ reply - Balas LiveChat user └─ sendata [unique] [kode] [jumlah] - Kirim data garansi\n\n 🎟️ *Voucher & Promo* ├─ buatvoucher - Buat kode voucher ├─ delvoucher - Hapus semua voucher ├─ seteventnote - Tampilkan info event di menu utama └─ reseteventnote - Hapus pesan event\n\n 🎁 *Event Cashback* ├─ setcashback - Atur event cashback └─ hapuscashback - Nonaktifkan cashback\n\n 📊 *Laporan Transaksi* ├─ trxhari - Laporan transaksi hari ini ├─ trxbulan - Laporan bulan berjalan └─ rekapbulan [Mei 2025] - Rekap bulan tertentu\n\n 💸 *Fitur PG Atlantic* ├─ info - Profil akun atlantic ├─ transfer kode|norek|nama|jumlah|catatan - Kirim dana ├─ transferstatus [id] - Cek status transfer ├─ cekrekening kode|norek - Cek nama penerima └─ kodebank - Tampilkan daftar bank\n\n 🛠️ *Admin Tools & Backup* ├─ backup - Backup manual semua data ├─ setbackup [1h/2d] - Atur backup otomatis ├─ maintenance on/off - Mode pemeliharaan └─ stop - Hentikan sesi LiveChat\n\n _${global.footer}_`; await replyText(txt); break; } } let allCommands = [ 'menu', 'help', 'list', 'stok', 'stock', 'deposit', 'saldo', 'profil', 'leaderboard', 'addkategori', 'delkategori', 'renamekategori', 'addproduk', 'delproduk', 'cekstok', 'addstok', 'minstok', 'deletestok', 'setjual', 'editnama', 'editkode', 'editharga', 'editdeskripsi', 'editcatatan', 'setdiskon', 'diskonall', 'resetdiskon', 'addsaldo', 'minsaldo', 'deluser', 'buynow', 'buy', 'order', 'manual', 'qris', 'batal', 'transaksi', 'riwayat', 'laporanbulan', 'trxhari', 'ownermenu', 'listuser', 'klaim', 'live', 'reply', 'stop', 'backup', 'maintenance', 'setevent', 'seteventnote', 'delevent', 'reseteventnote', 'setcashback', 'hapuscashback', 'cekcashback', 'addv', 'buatvoucher', 'redeem', 'delvoucher', 'delv', 'hapusvoucher', 'ingatkan' ]; const tipeYangDiizinkan = ['conversation', 'extendedTextMessage', 'buttonsResponseMessage', 'templateButtonReplyMessage', 'listResponseMessage', 'interactiveResponseMessage']; if (!tipeYangDiizinkan.includes(type)) return; let isCommandValid = allCommands.includes(command); let hiddenList = []; const hiddenPath = "./Database/HiddenKategori.json"; if (fs.existsSync(hiddenPath)) { hiddenList = JSON.parse(fs.readFileSync(hiddenPath, "utf-8")); } if (!isCommandValid && !command.startsWith(prefix) && command !== '') { if (maintenance.aktif && !isOwner) { return await replyText("🚧 Maaf, bot sedang dalam mode *pemeliharaan*. Beberapa fitur sementara tidak tersedia."); } if (global.lastStockRequestTime[sender] && (Date.now() - global.lastStockRequestTime[sender] < STOCK_REQUEST_COOLDOWN)) { const timeLeft = Math.ceil((STOCK_REQUEST_COOLDOWN - (Date.now() - global.lastStockRequestTime[sender])) / 1000); return await replyText(`⏳ Harap tunggu *${timeLeft} detik* lagi sebelum melihat detail kategori produk.`); } const input = chats.trim().split(/\s+/)[0]; const kategoriFiles = fs .readdirSync("./Database/Kategori/") .filter(f => f.endsWith(".json")); const visibleKategori = kategoriFiles.filter( f => !hiddenList.includes(f.replace(".json", "")) ); let idx = -1; if (!isNaN(input)) { idx = parseInt(input) - 1; if (idx < 0 || idx >= visibleKategori.length) { return; } } else { const kategoriNames = visibleKategori.map(f => f.replace(".json", "")); const exactIdx = kategoriNames.findIndex(k => k.toLowerCase() === input.toLowerCase()); if (exactIdx !== -1) { idx = exactIdx; } else { const matchedName = findNearestKategori(input, kategoriNames); if (!matchedName) { return; } idx = kategoriNames.findIndex(name => name === matchedName); if (idx === -1) { return; } } } if (idx >= 0 && idx < visibleKategori.length) { const file = visibleKategori[idx]; const produkList = JSON.parse( fs.readFileSync(`./Database/Kategori/${file}`) ).filter(p => !p.hidden); if (produkList.length === 0) { global.lastStockRequestTime[sender] = Date.now(); return await replyText(`Kategori *${file.replace(".json", "")}* belum memiliki produk.`); } let pesan = ` *╭────〔 BOT ${global.namabot.toUpperCase()} 〕─* *┊* • Ketik buynow code jumlah_beli *┊* • Contoh : buynow zoom 1 *┊* • Pastikan code & jumlah di ketik dengan benar *┊* • Update stok setiap jam 08.00 WIB *╰───────•* ⚠️ Stok habis? Kamu bisa ketik perintah yang ada di *Waitlist* untuk mendapatkan notif restok. `; produkList.forEach(item => { const lastUpdateText = (() => { if (!item.lastUpdateStock) return "-"; const last = moment.utc(item.lastUpdateStock).tz("Asia/Jakarta"); const now = moment().tz("Asia/Jakarta"); const diffMs = now.diff(last); const diffMinutes = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / (24 * 3600000)); if (diffMinutes < 1) { return "Baru saja"; } else if (diffHours < 1) { return `${diffMinutes} menit yang lalu`; } else if (diffHours < 24) { return `${diffHours} jam yang lalu`; } else { return `${diffDays} hari yang lalu`; } })(); const stok = item.tipeproduk.toLowerCase() === "file" ? item.stokproduk : item.dataproduk.length; const terjual = item.stokTerjual || 0; const hargaAwal = item.hargaproduk; const hargaDiskon = Math.floor(hargaAwal * (100 - item.diskonproduk) / 100); const hargaText = item.diskonproduk > 0 ? `~${formatrupiah(hargaAwal)}~ ➜ ${formatrupiah(hargaDiskon)}` : formatrupiah(hargaAwal); const diskonLine = item.diskonproduk > 0 ? `\n*┊ 💥 Diskon:* ${item.diskonproduk}%` : ""; pesan += ` *╭──〔 ${item.namaproduk} 〕─* *┊ 🔄 Restok:* ${lastUpdateText} *┊ 🆔 Kode:* ${item.kodeproduk} *┊ 📝 Ketik:* ${petik}buynow ${item.kodeproduk} 1${petik} *┊ 💵 Harga:* ${hargaText}${diskonLine} *┊ 📦 Stok:* ${stok} *┊ 📉 Terjual:* ${terjual} *┊ 📥 Waitlist:* ${petik}ingatkan ${item.kodeproduk}${petik} *┊ 🗒 Deskripsi:* ${item.deskripsiproduk} *╰────────*\n`; }); pesan += `\n\n_${global.footer}_`; global.lastStockRequestTime[sender] = Date.now(); return await replyText(pesan); } return; } }); } startBot();