{"_id":"@mivesensei/baileys","_rev":"2-c93da7bae426d75b2d9b99e6778d8c31","name":"@mivesensei/baileys","dist-tags":{"latest":"0.4.3"},"versions":{"0.4.2":{"name":"@mivesensei/baileys","version":"0.4.2","keywords":["automation","baileys","bot","interactive-messages","js-whatsapp","multi-device","whatsapp","whatsapp-api","whatsapp-bot","whatsapp-web"],"author":{"name":"Mivesensei"},"license":"MIT","_id":"@mivesensei/baileys@0.4.2","maintainers":[{"name":"mivesensei","email":"miirusensei3@gmail.com"}],"homepage":"https://github.com/mirucama/mivesensei-baileys","bugs":{"url":"https://github.com/mirucama/mivesensei-baileys/issues"},"dist":{"shasum":"5c19ee1202edfef6087f5faf9390e1050127ee88","tarball":"https://registry.npmjs.org/@mivesensei/baileys/-/baileys-0.4.2.tgz","fileCount":444,"integrity":"sha512-5sIbwV/Jj9US0DjA0CYn0xlGezrLmAa0LXaWNeLDfjj6ssOSXAXHieljP9EI3+rIb9ZudWgdHH2VxVSq4pptjg==","signatures":[{"sig":"MEUCIQC5nVkTRAe+CaG9YeCpKKv8OCxrHt0eGCq7ySpHiXtq+gIgXAmiSQ94dIUkKKlZkeR17cb24CqUaGOBJLThDdHEuhY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21762813},"main":"lib/index.js","type":"module","types":"lib/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"4c9d37e1801dfb8c41b7e167cbc1262d854a9d61","scripts":{"preinstall":"node ./engine-requirements.js"},"_npmUser":{"name":"mivesensei","email":"miirusensei3@gmail.com"},"repository":{"url":"git+https://github.com/mirucama/mivesensei-baileys.git","type":"git"},"_npmVersion":"10.8.2","description":"Fork Baileys milik Mivesensei — pesan interaktif, album, newsletter, auto-follow saluran, dan panggilan suara (VoIP).","directories":{},"resolutions":{"tar":"^7.5.11","tmp":"^0.2.5","socks":"^2.8.8","flatted":"^3.4.2","basic-ftp":"^5.2.4","handlebars":"^4.7.9","ip-address":"^10.1.1","underscore":"^1.13.8","markdown-it":"^14.1.1","ajv@npm:^6.12.4":"^6.14.0","yaml@npm:^2.6.1":"^2.8.4","glob@npm:^10.2.2":"^10.5.0","glob@npm:^10.5.0":"^10.5.0","glob@npm:^10.3.10":"^10.5.0","js-yaml@npm:^4.1.0":"^4.1.1","js-yaml@npm:^3.13.1":"^3.14.2","minimatch@npm:^3.0.4":"^3.1.5","minimatch@npm:^3.1.2":"^3.1.5","minimatch@npm:^5.0.1":"^5.1.8","minimatch@npm:^9.0.4":"^9.0.9","minimatch@npm:^9.0.5":"^9.0.9","picomatch@npm:^2.0.4":"^2.3.2","picomatch@npm:^2.3.1":"^2.3.2","picomatch@npm:^4.0.0":"^4.0.4","picomatch@npm:^4.0.2":"^4.0.4","brace-expansion@npm:^1.1.7":"^1.1.13","brace-expansion@npm:^2.0.1":"^2.0.3"},"_nodeVersion":"20.20.2","dependencies":{"ws":"^8.19.0","pino":"^9.6.0","fflate":"^0.8.2","p-queue":"^9.1.0","libsignal":"^6.0.0","lru-cache":"^11.2.6","@hapi/boom":"^9.1.3","protobufjs":"^7.5.6","async-mutex":"^0.5.0","music-metadata":"^11.12.3","whatsapp-rust-bridge":"0.5.5","@adiwajshing/keyed-db":"^0.2.4","@cacheable/node-cache":"^1.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"jimp":"^1.6.1","sharp":"*","audio-decode":"^2.2.3","@napi-rs/image":"~1.12.0","better-sqlite3":"^11.0.0","link-preview-js":"^3.0.0"},"peerDependenciesMeta":{"jimp":{"optional":true},"audio-decode":{"optional":true},"@napi-rs/image":{"optional":true},"better-sqlite3":{"optional":true},"link-preview-js":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/baileys_0.4.2_1788997852440_0.5911227735856688","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@mivesensei/baileys","version":"0.4.3","description":"Fork Baileys milik Mivesensei — pesan interaktif, album, newsletter, auto-follow saluran, dan panggilan suara (VoIP).","main":"lib/index.js","types":"lib/index.d.ts","type":"module","scripts":{"preinstall":"node ./engine-requirements.js"},"engines":{"node":">=20.0.0"},"repository":{"type":"git","url":"git+https://github.com/mirucama/mivesensei-baileys.git"},"keywords":["automation","baileys","bot","interactive-messages","js-whatsapp","multi-device","whatsapp","whatsapp-api","whatsapp-bot","whatsapp-web"],"homepage":"https://github.com/mirucama/mivesensei-baileys","author":{"name":"Mivesensei"},"license":"MIT","dependencies":{"@adiwajshing/keyed-db":"^0.2.4","@cacheable/node-cache":"^1.4.0","@hapi/boom":"^9.1.3","async-mutex":"^0.5.0","fflate":"^0.8.2","libsignal":"^6.0.0","lru-cache":"^11.2.6","music-metadata":"^11.12.3","p-queue":"^9.1.0","pino":"^9.6.0","protobufjs":"^7.5.6","whatsapp-rust-bridge":"0.5.5","ws":"^8.19.0"},"peerDependencies":{"@napi-rs/image":"*","audio-decode":"*","better-sqlite3":">=11.0.0","jimp":">=1.6.0","link-preview-js":"*","sharp":"*"},"peerDependenciesMeta":{"@napi-rs/image":{"optional":true},"audio-decode":{"optional":true},"better-sqlite3":{"optional":true},"jimp":{"optional":true},"link-preview-js":{"optional":true}},"resolutions":{"ajv@npm:^6.12.4":"^6.14.0","basic-ftp":"^5.2.4","brace-expansion@npm:^1.1.7":"^1.1.13","brace-expansion@npm:^2.0.1":"^2.0.3","flatted":"^3.4.2","glob@npm:^10.2.2":"^10.5.0","glob@npm:^10.3.10":"^10.5.0","glob@npm:^10.5.0":"^10.5.0","handlebars":"^4.7.9","ip-address":"^10.1.1","js-yaml@npm:^3.13.1":"^3.14.2","js-yaml@npm:^4.1.0":"^4.1.1","markdown-it":"^14.1.1","minimatch@npm:^3.0.4":"^3.1.5","minimatch@npm:^3.1.2":"^3.1.5","minimatch@npm:^5.0.1":"^5.1.8","minimatch@npm:^9.0.4":"^9.0.9","minimatch@npm:^9.0.5":"^9.0.9","picomatch@npm:^2.0.4":"^2.3.2","picomatch@npm:^2.3.1":"^2.3.2","picomatch@npm:^4.0.0":"^4.0.4","picomatch@npm:^4.0.2":"^4.0.4","socks":"^2.8.8","tar":"^7.5.11","tmp":"^0.2.5","underscore":"^1.13.8","yaml@npm:^2.6.1":"^2.8.4"},"bugs":{"url":"https://github.com/mirucama/mivesensei-baileys/issues"},"publishConfig":{"access":"public"},"_id":"@mivesensei/baileys@0.4.3","gitHead":"e27a6f43a6d145795467dc040c7e1e07794ab057","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-igDsFDwpyMG74h2ppyepDOo+VrnpMg2ksmA5h4ZCVlvUr6YHNxGKes8DEgZGH/CkxDuCblJYFfCXBqqOHrUQMg==","shasum":"4f420a67d75fe3ff6f2ca990a4d68ec043c57773","tarball":"https://registry.npmjs.org/@mivesensei/baileys/-/baileys-0.4.3.tgz","fileCount":444,"unpackedSize":21763227,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIG4ivZjJ8IZSH6nWaLcYlM3tIZ7FeV8zKOdgQJjMNYWdAiEA791O38Vx1xaSjDNxIzyvZ2giOcx19aKGZomaxI5TvFE="}]},"_npmUser":{"name":"mivesensei","email":"miirusensei3@gmail.com"},"directories":{},"maintainers":[{"name":"mivesensei","email":"miirusensei3@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/baileys_0.4.3_1789016030363_0.27940345219873586"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-09T23:50:52.243Z","modified":"2026-09-10T04:53:50.743Z","0.4.2":"2026-09-09T23:50:52.639Z","0.4.3":"2026-09-10T04:53:50.579Z"},"bugs":{"url":"https://github.com/mirucama/mivesensei-baileys/issues"},"author":{"name":"Mivesensei"},"license":"MIT","homepage":"https://github.com/mirucama/mivesensei-baileys","keywords":["automation","baileys","bot","interactive-messages","js-whatsapp","multi-device","whatsapp","whatsapp-api","whatsapp-bot","whatsapp-web"],"repository":{"type":"git","url":"git+https://github.com/mirucama/mivesensei-baileys.git"},"description":"Fork Baileys milik Mivesensei — pesan interaktif, album, newsletter, auto-follow saluran, dan panggilan suara (VoIP).","maintainers":[{"name":"mivesensei","email":"miirusensei3@gmail.com"}],"readme":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/mivesensei/mivesensei-baileys/main/banner.jpg\" alt=\"Mivesensei Baileys\" width=\"100%\">\n</p>\n\n<h1 align=\"center\">Mivesensei Baileys</h1>\n\n<div align=\"center\">\n\nPustaka WhatsApp berbasis WebSockets untuk kebutuhan bot dan aplikasi **Mivesensei**.\n\nBerasal dari **Baileys** (`@whiskeysockets/baileys`) dan disesuaikan dengan kebutuhan Mivesensei.\n\n</div>\n\n---\n\n## Daftar Isi\n\n- [Nama Paket](#nama-paket)\n- [Prasyarat](#prasyarat)\n- [Instalasi](#instalasi)\n- [Cara Menghubungkan Akun](#cara-menghubungkan-akun)\n  - [Lewat QR Code](#lewat-qr-code)\n  - [Lewat Pairing Code](#lewat-pairing-code)\n- [Menyimpan & Memulihkan Sesi](#menyimpan--memulihkan-sesi)\n- [Contoh Bot Lengkap](#contoh-bot-lengkap)\n- [Mengirim Pesan](#mengirim-pesan)\n- [Menerima & Mengunduh Media](#menerima--mengunduh-media)\n- [Mengelola Grup](#mengelola-grup)\n- [Event yang Tersedia](#event-yang-tersedia)\n- [Fungsi Utilitas](#fungsi-utilitas)\n- [Format ID WhatsApp (JID)](#format-id-whatsapp-jid)\n- [Panggilan Suara (VoIP)](#panggilan-suara-voip)\n- [Kontak & Channel Mivesensei](#kontak--channel-mivesensei)\n- [Kepemilikan & Identitas Proyek](#kepemilikan--identitas-proyek)\n- [Lisensi](#lisensi)\n- [Kredit & Terima Kasih](#kredit--terima-kasih)\n\n## Nama Paket\n\n`@mivesensei/baileys`\n\n## Prasyarat\n\n| Kebutuhan | Keterangan |\n|---|---|\n| **Node.js 20 atau lebih baru** | **Wajib.** Saat `npm install`, ada script `preinstall` (`engine-requirements.js`) yang akan **menghentikan instalasi** jika versi Node Anda di bawah 20. |\n| **ESM** | Paket ini `\"type\": \"module\"`. Kalau project Anda CommonJS, pakai `import()` dinamis (lihat di bawah). |\n| **`pino`** | Untuk logger. Sudah ikut terpasang sebagai dependency. |\n\n## Instalasi\n\nDari registry npm:\n\n```bash\nnpm install @mivesensei/baileys\n```\n\nLangsung dari repository GitHub ini:\n\n```bash\nnpm install github:mivesensei/mivesensei-baileys\n```\n\nLalu impor di kode Anda:\n\n```js\nimport makeWASocket from \"@mivesensei/baileys\";\n```\n\nDi project CommonJS (`require`):\n\n```js\nasync function main() {\n  const { default: makeWASocket } = await import(\"@mivesensei/baileys\");\n  // ... lanjutkan di sini\n}\nmain();\n```\n\n## Cara Menghubungkan Akun\n\nWhatsApp menyediakan API multi-device yang membuat paket ini bisa autentikasi sebagai\nperangkat WhatsApp kedua — pindai **QR code** atau pakai **pairing code** dari HP.\n\n### Lewat QR Code\n\n> [!IMPORTANT]\n> Kode QR dikirim lewat event `connection.update` sebagai properti `qr` — ambil dari situ,\n> jangan mengandalkan opsi `printQRInTerminal` (sudah deprecated, tidak mencetak QR).\n\n```js\nimport makeWASocket, { Browsers } from \"@mivesensei/baileys\";\n\nconst sock = makeWASocket({\n    browser: Browsers.ubuntu(\"Mivesensei App\")\n});\n\nsock.ev.on(\"connection.update\", ({ qr }) => {\n    if (qr) console.log(\"Scan kode QR ini dengan WhatsApp Anda:\\n\", qr);\n});\n```\n\nPindai lewat **WhatsApp → Perangkat Tertaut → Tautkan Perangkat**, dan Anda langsung masuk.\n\nMau QR yang bisa dibaca mata (bukan string teks) di terminal? Tambahkan dependency kecil:\n\n```bash\nnpm install qrcode-terminal\n```\n```js\nimport qrcode from \"qrcode-terminal\";\n\nsock.ev.on(\"connection.update\", ({ qr }) => {\n    if (qr) qrcode.generate(qr, { small: true });\n});\n```\n\n### Lewat Pairing Code\n\nPairing code dipakai kalau tidak bisa memindai QR (misalnya di server/VPS).\nNomor **tanpa** `+`, `()`, atau `-` — awali dengan kode negara. Untuk Indonesia pakai `62`.\n\n```js\nimport makeWASocket from \"@mivesensei/baileys\";\n\nconst sock = makeWASocket({});\n\nif (!sock.authState.creds.registered) {\n    const nomor = \"6281234567890\";\n    const kode = await sock.requestPairingCode(nomor);\n    console.log(\"Masukkan kode ini di WhatsApp:\", kode);\n}\n```\n\nAnda juga bisa menentukan kode sendiri, dengan syarat **panjangnya harus pas 8 karakter**:\n\n```js\nconst kode = await sock.requestPairingCode(\"6281234567890\", \"MIVESENSEI01\");\n```\n\nKalau panjangnya bukan 8, akan error: `Custom pairing code must be exactly 8 chars`.\n\n## Menyimpan & Memulihkan Sesi\n\nSupaya tidak perlu memindai QR berulang kali, simpan kredensial ke folder:\n\n```js\nimport makeWASocket, { useMultiFileAuthState } from \"@mivesensei/baileys\";\n\nconst { state, saveCreds } = await useMultiFileAuthState(\"auth_info_mivesensei\");\n\nconst sock = makeWASocket({\n    auth: state         // perhatikan: namanya `auth`, bukan `authState`\n});\n\n// dipanggil setiap kali kredensial diperbarui\nsock.ev.on(\"creds.update\", saveCreds);\n```\n\n> [!WARNING]\n> Folder `auth_info_mivesensei` berisi **kunci sesi** akun WhatsApp Anda. Siapa pun yang memegang\n> folder itu bisa mengambil alih sesi. **Jangan pernah** commit ke Git — tambahkan ke `.gitignore`:\n>\n> ```gitignore\n> auth_info_mivesensei/\n> ```\n\nAgar sesi makin awet dan tidak sering diminta pairing ulang, bungkus key store dengan cache:\n\n```js\nimport makeWASocket, { useMultiFileAuthState, makeCacheableSignalKeyStore } from \"@mivesensei/baileys\";\nimport pino from \"pino\";\n\nconst logger = pino({ level: \"silent\" });\nconst { state, saveCreds } = await useMultiFileAuthState(\"auth_info_mivesensei\");\n\nconst sock = makeWASocket({\n    logger,\n    auth: {\n        creds: state.creds,\n        keys: makeCacheableSignalKeyStore(state.keys, logger)\n    }\n});\n\nsock.ev.on(\"creds.update\", saveCreds);\n```\n\n## Contoh Bot Lengkap\n\nIni pola dasar yang paling umum dipakai: tangkap pesan masuk, balas, tangani koneksi putus.\n\n```js\nimport makeWASocket, { DisconnectReason, useMultiFileAuthState } from \"@mivesensei/baileys\";\nimport { Boom } from \"@hapi/boom\";\n\nasync function jalankanBot() {\n    const { state, saveCreds } = await useMultiFileAuthState(\"auth_info_mivesensei\");\n\n    const sock = makeWASocket({\n        auth: state\n    });\n\n    sock.ev.on(\"connection.update\", (update) => {\n        const { connection, lastDisconnect, qr } = update;\n\n        if (qr) console.log(\"Scan kode QR ini:\", qr);\n\n        if (connection === \"close\") {\n            const logout = lastDisconnect?.error\n                ? new Boom(lastDisconnect.error)?.output?.statusCode === DisconnectReason.loggedOut\n                : false;\n\n            console.log(\"Koneksi tertutup:\", lastDisconnect?.error);\n            // sambungkan lagi, kecuali Anda memang logout secara sadar\n            if (!logout) jalankanBot();\n        } else if (connection === \"open\") {\n            console.log(\"Bot aktif!\");\n        }\n    });\n\n    sock.ev.on(\"messages.upsert\", async ({ messages }) => {\n        // selalu pakai loop: messages adalah array\n        for (const m of messages) {\n            if (m.key.fromMe) continue;                 // lewati pesan dari bot sendiri\n            if (m.message?.protocolMessage) continue;   // lewati edit/hapus pesan\n\n            const teks = m.message?.conversation\n                || m.message?.extendedTextMessage?.text\n                || \"\";\n\n            console.log(`Pesan dari ${m.key.remoteJid}:`, teks);\n            await sock.sendMessage(m.key.remoteJid, { text: \"Halo dari Mivesensei Baileys!\" });\n        }\n    });\n\n    sock.ev.on(\"creds.update\", saveCreds);\n}\n\njalankanBot();\n```\n\n## Mengirim Pesan\n\nSemua jenis pesan dikirim lewat satu fungsi: `sock.sendMessage(jid, konten, opsi)`.\n\n**Teks**\n\n```js\nawait sock.sendMessage(nomor, { text: \"halo apa kabar\" });\n```\n\n**Balas dengan quote**\n\n```js\nawait sock.sendMessage(nomor, { text: \"oke siap\" }, { quoted: pesanMasuk });\n```\n\n**Sebut (mention) pengguna**\n\n```js\nawait sock.sendMessage(nomor, {\n    text: \"halo @6281234567890 👋\",\n    mentions: [\"6281234567890@s.whatsapp.net\"]\n});\n```\n\n**Gambar**\n\n```js\n// dari file\nawait sock.sendMessage(nomor, { image: { url: \"./foto.jpg\" }, caption: \"caption di sini\" });\n\n// dari Buffer hasil unduhan\nawait sock.sendMessage(nomor, { image: buffer, caption: \"langsung dari memori\" });\n```\n\n**Video, audio, dokumen, sticker, GIF**\n\n```js\nawait sock.sendMessage(nomor, { video: { url: \"./klip.mp4\" }, caption: \"video\", gifPlayback: false });\nawait sock.sendMessage(nomor, { audio: { url: \"./suara.mp3\" }, ptt: true });   // ptt: true = voice note\nawait sock.sendMessage(nomor, { document: { url: \"./kata-kata.pdf\" }, fileName: \"kata-kata.pdf\" });\nawait sock.sendMessage(nomor, { sticker: \"./stiker.webp\" });\nawait sock.sendMessage(nomor, { video: { url: \"./loop.mp4\" }, gifPlayback: true });\n```\n\n**View Once**\n\n```js\nawait sock.sendMessage(nomor, { image: { url: \"./rahasia.jpg\" }, viewOnce: true });\n```\n\n**Lokasi & Kontak**\n\n```js\nawait sock.sendMessage(nomor, {\n    location: { degreesLatitude: -3.5952, degreesLongitude: 97.6571 }  // Medan\n});\n\nconst vcard =\n    \"BEGIN:VCARD\\n\" +\n    \"VERSION:3.0\\n\" +\n    \"FN:Mivesensei\\n\" +\n    \"TEL;type=CELL;type=VOICE;waid=6281234567890:+62 812-3456-7890\\n\" +\n    \"END:VCARD\";\n\nawait sock.sendMessage(nomor, {\n    contacts: { displayName: \"Mivesensei\", contacts: [{ vcard }] }\n});\n```\n\n**Reaksi, Poll, dan Maju (forward)**\n\n```js\nawait sock.sendMessage(nomor, { react: { text: \"🔥\", key: pesanMasuk.key } });\nawait sock.sendMessage(nomor, { react: { text: \"\", key: pesanMasuk.key } });  // string kosong = hapus reaksi\n\nawait sock.sendMessage(nomor, {\n    poll: { name: \"Makan siang?\", values: [\"Nasi padang\", \"Soto\", \"Gado-gado\"], selectableCount: 1 }\n});\n\nawait sock.sendMessage(nomor, { forward: pesanMasuk });\n```\n\n> [!TIP]\n> Supaya tautan ikut menampilkan *preview*, pasang dependency opsionalnya:\n>\n> ```bash\n> npm install link-preview-js\n> ```\n>\n> Opsi `generateHighQualityLinkPreview` sudah aktif secara default, jadi\n> tidak perlu di-set ulang — cukup kirim teks yang berisi URL:\n>\n> ```js\n> await sock.sendMessage(nomor, { text: \"lihat: https://example.com\" });\n> ```\n\n### Bentuk pesan khas Mivesensei\n\nTiga fitur tambahan di paket ini memakai bentuk payload sendiri — sudah diuji terhadap kode\nyang ter-publish, jadi ikuti persis seperti ini:\n\n```js\n// album: array di tingkat atas, minimal 2 gambar/video (bukan { album: { messages: [...] } })\nawait sock.sendMessage(nomor, {\n    caption: \"liburan\",\n    album: [{ image: { url: \"./a.jpg\" } }, { video: { url: \"./b.mp4\" } }]\n});\n\n// quiz: lewat `poll` dengan `pollType: 1` + `correctAnswer` (bukan key `quiz`)\nawait sock.sendMessage(nomor, {\n    poll: { name: \"Kuis\", values: [\"Ai\", \"Ruby\"], selectableCount: 1, pollType: 1, correctAnswer: \"Ai\" }\n});\n\n// list: `sections` di tingkat atas (bukan { list: { sections: [...] } })\nawait sock.sendMessage(nomor, {\n    text: \"Pilih menu\", buttonText: \"Menu\", title: \"Mivesensei Bot\", footer: \"ketuk di bawah\",\n    sections: [{ title: \"Utama\", rows: [{ rowId: \"1\", title: \"Cek saldo\" }] }]\n});\n```\n\nBentuk yang salah ditolak dengan pesan jelas, bukan crash: `Invalid album type. Expected an array.`,\n`Minimum provide 2 media to upload album message`, `No \"correctAnswer\" provided for quiz`.\n\n## Menerima & Mengunduh Media\n\n```js\nimport { downloadMediaMessage } from \"@mivesensei/baileys\";\nimport fs from \"node:fs\";\n\nsock.ev.on(\"messages.upsert\", async ({ messages }) => {\n    for (const m of messages) {\n        const media = m.message?.imageMessage\n            || m.message?.videoMessage\n            || m.message?.audioMessage\n            || m.message?.documentMessage;\n\n        if (!media) continue;\n\n        const buffer = await downloadMediaMessage(m, \"buffer\", {}, {\n            logger: sock.logger,\n            reuploadRequest: sock.updateMediaMessage\n        });\n\n        fs.writeFileSync(`unduhan_${m.message.imageMessage ? \"foto.jpg\" : \"media\"}`, buffer);\n    }\n});\n```\n\nButuh alurnya terpisah (unduh manual per konten)? Tersedia juga `downloadContentFromMessage`.\n\n## Mengelola Grup\n\n```js\n// buat grup (array nomor)\nconst { id: jidGrup } = await sock.groupCreate(\"Ruang Mivesensei\", [\n    \"6281234567890@s.whatsapp.net\",\n    \"6281234567891@s.whatsapp.net\"\n]);\n\n// info grup: nama, deskripsi, peserta\nconst meta = await sock.groupMetadata(jidGrup);\nconsole.log(meta.subject, \"| peserta:\", meta.participants.length);\n\n// tambah / keluarkan / naikkan-turunkan admin\nawait sock.groupParticipantsUpdate(jidGrup, [\"6281234567892@s.whatsapp.net\"], \"add\");\nawait sock.groupParticipantsUpdate(jidGrup, [\"6281234567892@s.whatsapp.net\"], \"remove\");\nawait sock.groupParticipantsUpdate(jidGrup, [\"6281234567892@s.whatsapp.net\"], \"promote\");\nawait sock.groupParticipantsUpdate(jidGrup, [\"6281234567892@s.whatsapp.net\"], \"demote\");\n\n// ubah nama & deskripsi\nawait sock.groupUpdateSubject(jidGrup, \"Nama Baru\");\nawait sock.groupUpdateDescription(jidGrup, \"Deskripsi grup\");\n\n// kunci grup (hanya admin yang bisa kirim)\nawait sock.groupSettingUpdate(jidGrup, \"announcement\");\nawait sock.groupSettingUpdate(jidGrup, \"not_announcement\");  // buka lagi\n\n// pesan sementara (auto-delete)\nawait sock.groupToggleEphemeral(jidGrup, 7 * 24 * 3600);  // detik\n```\n\n## Event yang Tersedia\n\nPaket ini memakai gaya `EventEmitter`, jadi tinggal `sock.ev.on(...)`:\n\n| Event | Kapan terjadi |\n|---|---|\n| `connection.update` | Status koneksi berubah (`open`, `close`, QR menunggu) |\n| `messages.upsert` | Ada pesan baru |\n| `messages.update` | Pesan berubah (dibaca, diedit, dihapus, vote poll) |\n| `messaging-history.set` | Riwayat chat terkirim saat pertama kali sinkron |\n| `chats.upsert` / `chats.update` / `chats.delete` | Data chat berubah |\n| `contacts.upsert` / `contacts.update` | Data kontak berubah |\n| `groups.upsert` / `groups.update` | Informasi grup berubah |\n| `group-participants.update` | Ada yang masuk/keluar/di-promosi |\n| `creds.update` | Kredensial sesi perlu disimpan ulang |\n| `presence.update` | Ada yang sedang mengetik / online |\n\nContoh menangani peserta grup baru:\n\n```js\nsock.ev.on(\"group-participants.update\", async (event) => {\n    if (event.action === \"add\") {\n        await sock.sendMessage(event.id, { text: `Selamat datang @${event.participants[0].split(\"@\")[0]}!`, mentions: event.participants });\n    }\n});\n```\n\n## Fungsi Utilitas\n\nSemua ini bisa diimpor langsung dari `\"@mivesensei/baileys\"`:\n\n| Fungsi | Kegunaan |\n|---|---|\n| `getContentType(pesan)` | Tahu jenis isi pesan (`imageMessage`, `conversation`, dst.) |\n| `getDevice(pesan)` | Tahu perangkat pengirim (`android`, `desktop`, `smba`) |\n| `normalizeMessageContent(m)` | Buka bungkus pesan (`viewOnce`, `ephemeral`, album, dll.) jadi isi aslinya |\n| `hasValidAlbumMedia` / `getAggregateResponsesInEventMessage` | Tambahan khas fork ini |\n| `jidDecode(\"62..@s.whatsapp.net\")` | Pecah JID jadi `{ user, server, agent, device }` |\n| `jidNormalizedUser(jid)` | Normalisasi JID (buang agent/device) |\n| `isJidGroup` / `isJidBroadcast` / `isJidStatusBroadcast` | Cek jenis JID |\n| `downloadMediaMessage` / `downloadContentFromMessage` | Unduh media |\n| `prepareWAMessageMedia` | Siapkan media sebelum dikirim (untuk kontrol penuh) |\n| `generateWAMessageFromContent` | Susun objek pesan mentah |\n| `updateMediaMessage` | Kirim ulang media yang habis masa berlakunya |\n| `getAggregateVotesInPollMessage` | Baca hasil vote poll |\n| `useMultiFileAuthState` / `useSingleFileAuthState` / `useSqliteAuthState` | Simpan sesi (folder / satu file / SQLite) |\n| `makeCacheableSignalKeyStore` | Cache kunci sesi (lebih cepat & lebih awet) |\n| `initAuthCreds` | Bikin kredensial kosong dari nol |\n| `makeInMemoryStore` | Penyimpanan chat/kontak sementara di memori |\n| `useSqliteAuthState({ dbPath })` | Simpan sesi di SQLite (butuh `better-sqlite3`) |\n| `Browsers` | Pengaturan identitas perangkat (`macOS`, `windows`, `ubuntu`, `android`, `baileys`, `appropriate`) |\n| `delay(ms)` | Jeda (Promise) |\n| `fetchLatestMivesenseiBaileysVersion()` / `fetchLatestWaWebVersion()` | Ambil nomor versi WA untuk opsi `version` |\n| `proto` | Akses objek protokol WhatsApp (`proto.Message`, `proto.WebMessageInfo`) |\n\nPenyimpanan di memori (praktis untuk bot kecil, **boros RAM** untuk pemakaian serius):\n\n```js\nimport makeWASocket, { makeInMemoryStore } from \"@mivesensei/baileys\";\n\nconst store = makeInMemoryStore({});\nstore.readFromFile(\"./mivesensei_store.json\");\nsetInterval(() => store.writeToFile(\"./mivesensei_store.json\"), 10_000);\n\nconst sock = makeWASocket({});\nstore.bind(sock.ev);\n\nsock.ev.on(\"chats.upsert\", () => console.log(store.chats.all()));\n```\n\n## Format ID WhatsApp (JID)\n\nID tujuan (disebut **JID**) wajib format `[kode negara][nomor]@s.whatsapp.net`:\n\n| Untuk | Format | Contoh |\n|---|---|---|\n| Pengguna | `nomor@s.whatsapp.net` | `6281234567890@s.whatsapp.net` |\n| Grup | `id-internal@g.us` | `120363012345678901@g.us` |\n| Broadcast list | `[waktu-dibuat]@broadcast` | — |\n| Status/story | `status@broadcast` | — |\n\nNomor **selalu** pakai kode negara tanpa `+` (Indonesia → `62`).\n\n## Panggilan Suara (VoIP)\n\nLibrary ini bisa **menelepon nomor WhatsApp** dan memutar audio ke dalam\npanggilan itu — mirip bot musik di Discord. Cocok untuk fitur *fake call*\natau pemutar musik lewat telepon WA.\n\n### Prasyarat tambahan\n\n**`ffmpeg` wajib terpasang** di server/PC Anda — dipakai untuk mengubah file\naudio menjadi PCM yang bisa dikirim ke panggilan.\n\n```bash\n# Ubuntu / Debian / VPS\nsudo apt install ffmpeg\n\n# Termux\npkg install ffmpeg\n```\n\nCek berhasil: `ffmpeg -version`.\n\n### Cara pakai\n\n`VoipClient` dipakai **menempel pada socket yang sudah tersambung**, jadi\nakun harus sudah login lebih dulu.\n\n```js\nimport makeWASocket, { useMultiFileAuthState, VoipClient } from \"@mivesensei/baileys\";\n\nconst { state, saveCreds } = await useMultiFileAuthState(\"./auth\");\nconst sock = makeWASocket({ auth: state });\nsock.ev.on(\"creds.update\", saveCreds);\n\nsock.ev.on(\"connection.update\", async ({ connection }) => {\n  if (connection !== \"open\") return;\n\n  const voip = new VoipClient();\n  await voip.connectWithSocket(sock);\n\n  const call = await voip.call(\"6281234567890\", {\n    durationMs: 30_000,        // telepon otomatis ditutup setelah 30 detik\n    audioSource: \"./lagu.mp3\", // audio yang diputar ke lawan bicara\n  });\n\n  call.on(\"ringing\",   () => console.log(\"HP tujuan berdering...\"));\n  call.on(\"connected\", () => console.log(\"Panggilan diangkat, musik diputar\"));\n  call.on(\"ended\",     (alasan) => console.log(\"Panggilan selesai:\", alasan));\n\n  await call.waitForEnd();\n  voip.disconnect();\n});\n```\n\n### Pilihan `audioSource`\n\n| Nilai | Artinya |\n|---|---|\n| `\"silence\"` | **Default.** Tidak ada suara — murni *fake call*. |\n| `\"./lagu.mp3\"` | Putar file audio dari disk (format apa pun yang didukung ffmpeg). |\n| `\"https://...\"` | Putar audio langsung dari URL streaming. |\n| `\"lavfi:sine=frequency=440\"` | Nada buatan lewat filter ffmpeg. |\n\n### Method dan event\n\n| Anggota | Kegunaan |\n|---|---|\n| `new VoipClient()` | Membuat klien VoIP. |\n| `voip.connectWithSocket(sock)` | Menempel ke socket yang sudah login. **Wajib dipanggil dulu.** |\n| `voip.call(nomor, opsi)` | Memulai panggilan, mengembalikan objek `ActiveCall`. |\n| `voip.disconnect()` | Menutup semua dan melepas memori. |\n| `call.end()` | Menutup panggilan lebih awal. |\n| `call.mute(true)` | Membisukan mikrofon. |\n| `call.waitForEnd()` | `await` sampai panggilan berakhir. |\n| `call.state` | Status saat ini (lihat `CallState`). |\n| Event `ringing` | HP tujuan mulai berdering. |\n| Event `connected` | Panggilan diangkat. |\n| Event `audio` | Menerima PCM suara dari lawan bicara. |\n| Event `ended` | Panggilan berakhir, membawa alasannya. |\n\n`CallState` berisi: `Idle` `Calling` `PreacceptReceived` `ReceivedCall`\n`AcceptSent` `AcceptReceived` `Active` `ActiveElsewhere` `Ending`.\n\n### Catatan penting\n\n- Nomor tujuan ditulis **tanpa** `@s.whatsapp.net` — cukup `6281234567890`.\n- Panggilan berjalan dari akun yang sedang login, jadi tercatat di riwayat\n  telepon WhatsApp akun tersebut.\n- Modul ini memuat mesin WASM ±9,8 MB saat pertama dipakai. Wajar kalau\n  panggilan pertama sedikit lebih lambat.\n- Jangan dipakai untuk spam telepon — akun berisiko diblokir WhatsApp.\n\n## Kontak & Channel Reviza\n\nSemua tautan di bawah ini diambil dari situs resmi Reviza: <https://revizayowa.biz.id/>\n\n| Kanal | Tautan |\n|---|---|\n| WhatsApp (Admin) | https://wa.me/628978595858 |\n| Telegram | https://t.me/zayuyoo |\n| Instagram | https://instagram.com/Always_revizaa |\n| TikTok | https://www.tiktok.com/@reviza4ever |\n| YouTube | https://www.youtube.com/@reviza4ever |\n| Discord | https://discord.gg/7Nn352cN9b |\n| Grup WhatsApp | https://chat.whatsapp.com/BW0zgEgEPUOI5Arb80RWG0 |\n| Dukungan (Saweria) | https://saweria.co/REVIZAHOSHI |\n\n### Saluran (Channel) WhatsApp Reviza\n\n| Nama saluran | Kode undangan | JID untuk dipakai via library |\n|---|---|---|\n| Preset AM / Saluran preset | https://whatsapp.com/channel/0029Vb7xLulDJ6GrrJGtU51t | `120363426118421279@newsletter` |\n| Info Channel | https://whatsapp.com/channel/0029VbBc5ak3AzNL2pXnJJ1u | `120363423129630445@newsletter` |\n\nJID di atas berguna kalau Anda mengirim atau menjadwalkan konten ke saluran lewat `sock.sendMessage(jid, ...)`.\n\n### Auto-follow saluran\n\nSaat koneksi berhasil terbuka (`connection === 'open'`), socket otomatis mengikuti dua saluran di atas\nlewat `newsletterFollow()`. Perilaku ini **hanya sekali per proses** — reconnect tidak mengirim ulang\npermintaan join. Hasilnya dicatat ke log pada level `info`, jadi bisa Anda lihat langsung:\n\n```\n{\"level\":30,...,\"jid\":\"...@newsletter\",\"msg\":\"sukses follow saluran Reviza\"}\n{\"level\":30,...,\"jid\":\"...@newsletter\",\"alasan\":409,\"msg\":\"follow saluran Reviza dilewati\"}\n```\n\n`alasan: 409` artinya sudah pernah ikut, `429`/`503` artinya sedang dibatasi sementara. Keduanya\ntidak menghentikan bot.\n\n```js\n// matikan kalau tidak dipakai\nconst sock = makeWASocket({ auth: state, autoFollowSaluran: false });\n\n// atau ganti daftarnya\nconst sock2 = makeWASocket({ auth: state, autoFollowSaluran: [\"1234...@newsletter\"] });\n```\n\n## Kepemilikan & Identitas Proyek\n\nPaket ini adalah proyek pribadi **Mivesensei**, dikemas untuk kebutuhan bot dan aplikasinya sendiri.\n\n| Aspek | Keterangan |\n|---|---|\n| Nama paket | `@mivesensei/baileys` |\n| Versi | `0.3.18-final` |\n| Pemegang hak cipta | **Mivesensei** — © 2026 (lihat `LICENSE`) |\n| Penulis di `package.json` | `author: \"Mivesensei\"` |\n| Lisensi | MIT |\n| Rumah proyek | https://github.com/mirucama/mivesensei-baileys |\n| Kontak & kanal | lihat [Kontak & Channel Mivesensei](#kontak--channel-mivesensei) |\n\n**Jejak perubahan Mivesensei di dalam source.** Fork ini bukan cuma ganti nama: ada **80 penanda**\n`Mivesensei@Changes` / `Mivesensei@Note` tersebar di **12 file** `lib/`, tercatat sejak 30-01-26.\nKebanyakan menambah jenis & opsi pesan yang tidak ada di Baileys upstream:\n\n- `album`, `quiz` (khusus saluran), `poll result snapshot`, `poll update`, `reaction`/`receipt` update\n- `request payment`, `invoice`, `order`, `groupStatus`, `spoiler`, `ephemeral`, `viewOnceV2`,\n  `lottieSticker`, `futureProofMessage`, `interactiveAsTemplate`, `native flow`\n- `single_select` shortcut, validasi `hasValidAlbumMedia` / `hasValidCarouselHeader`,\n  `getAggregateResponsesInEventMessage`, `normalizeMessageContent`\n- lapisan rich message (`lib/Utils/rich-message-utils.js`, bertanda `[WIP]`), `useSingleFileAuthState`\n  dengan LRUCache + mutex, perbaikan newsletter (`/m1/`, thumbnail server, `additionalNodes`),\n  `newsletterSubscribed`, pengurangan pemakaian RSS, `lib/Store/make-in-memory-store.js` (`[WIP]`)\n\n**Perlu Anda ketahui sebelum pakai:** ada nilai `DONATE_URL = \"https://github.com/mirucama/mivesensei-baileys\"` di\n`lib/Defaults/index.js` yang dipakai sebagai **nilai cadangan** pada tiga tempat:\n\n| Kondisi | Yang terjadi |\n|---|---|\n| `externalAdReply` dikirim tanpa `url` | `url` diisi `DONATE_URL` |\n| `offerText` diisi tapi `offerUrl` kosong | tombol offer menunjuk `DONATE_URL` (teks offer jatuh ke `LIBRARY_NAME`, yaitu `@mivesensei/baileys`) |\n| `richResponseMessage` punya link tanpa `url` | memakai `DONATE_URL`, label sumbernya `\"Donate\"` / `\"Saweria\"` |\n\nSemuanya hanya kena kalau field-nya memang kosong — kirim `url` sendiri dan tidak ada yang berubah.\n\n**Yang tetap milik upstream:** basis kode Baileys, protokol `WAProto/`, implementasi Signal, dan\nseluruh dependency di `package.json`. Tidak ada klaim kepemilikan atas bagian ini — karena itu\n`LICENSE` MIT dibiarkan utuh beserta pemberitahuan hak ciptanya.\n\n## Lisensi\n\nKetentuan lisensi dan pemberitahuan hak cipta yang diwajibkan oleh komponen sumber tetap tersedia\ndi file `LICENSE`. File tersebut tidak dihapus atau diubah.\n\n## Catatan\n\nJangan menghapus file `LICENSE` ketika menyalin atau menggunakan paket ini apabila ketentuan\nlisensinya mewajibkan pemberitahuan tersebut tetap disertakan.\n\n## Repository\n\nSumber resmi proyek: https://github.com/mirucama/mivesensei-baileys\n\nContoh penggunaan di halaman ini diadaptasi dari dokumentasi\n[Baileys (`WhiskeySockets/Baileys`)](https://github.com/WhiskeySockets/Baileys) dan diterjemahkan\nserta disesuaikan ke `@mivesensei/baileys`.\n\n## Kredit & Terima Kasih\n\nPustaka ini tidak berdiri sendiri — ia berdiri di atas kerja banyak pihak. Terima kasih\nsebesar-besarnya untuk semua fork dan pengembang Baileys yang karyanya mengalir ke\n`@mivesensei/baileys`:\n\n| Proyek / Pengembang | Peran |\n|---|---|\n| [**Baileys** — Adhiraj Singh (`adiwajshing`)](https://github.com/adiwajshing/Baileys) | Pustaka asal. Basis kode, protokol `WAProto/`, dan implementasi Signal berasal dari sini |\n| [**Baileys** — WhiskeySockets](https://github.com/WhiskeySockets/Baileys) | Kelanjutan resmi Baileys yang menjadi dasar fork ini |\n| [**Baileys** — itsliaa](https://github.com/itsliaa) | Salah satu pengembang di ekosistem fork Baileys — terima kasih atas karya dan kontribusinya |\n| [**ourin-baileys** — zanpiww (`@Zann`)](https://www.npmjs.com/package/ourin-baileys) | Pengembang Ourin MD. Modul **VoIP** (panggilan suara / *fake call* + putar musik) di pustaka ini berasal dari `ourin-baileys` dan dipakai **atas izin langsung dari beliau**, Lisensi MIT — terima kasih banyak, zanpiww |\n| [**reviza-baileys** — Reviza / `revizahoshii-no`](https://github.com/revizahoshii-no/reviza-baileys) | Pustaka ini **di-fork dari sini**. Seluruh penyesuaian, penambahan jenis & opsi pesan, dan pemeliharaan sebelumnya dikerjakan di `reviza-baileys` — terima kasih banyak 🙏 |\n| [**mivesensei-baileys** — Mivesensei](https://github.com/mirucama/mivesensei-baileys) | Proyek ini sendiri: penyesuaian, penambahan jenis & opsi pesan, dan pemeliharaan |\n\nTidak ada klaim kepemilikan atas bagian mana pun yang berasal dari proyek-proyek di atas.\nPemberitahuan hak cipta dan ketentuan lisensinya tetap tersedia di file `LICENSE`.\n","readmeFilename":"README.md"}