jagproject
v28.8.2
Published
JagProject Baileys Modification — WhatsApp Web API untuk Node.js dengan dukungan multi-device, pesan, grup, newsletter, dan bisnis.
Maintainers
Readme
JagProject bukan produk resmi WhatsApp dan tidak berafiliasi dengan WhatsApp LLC. Gunakan hanya pada akun yang Anda miliki atau kelola, patuhi ketentuan layanan, dan hindari spam serta penyalahgunaan.
🧭 Navigasi
- 📦 Instalasi
- 🚀 Mulai cepat
- 🔐 QR dan pairing code
- 🔄 Koneksi dan reconnect
- 🌐 Versi WhatsApp Web otomatis
- ⚙️ Konfigurasi socket
- 📡 Event
- 💬 Mengirim pesan
- 🧩 Pesan interaktif
- 📢 Status WhatsApp
- 👥 Grup
- 📰 Newsletter
- 🗂️ Chat dan store
- 🤖 AI Rich Response
- 🛍️ Katalog bisnis
- 🧰 Pemecahan masalah
✨ Fitur utama
- Koneksi multi-device melalui QR atau pairing code.
- Pengambilan versi WhatsApp Web stable terbaru dengan fallback aman.
- Pengiriman teks, gambar, video, audio, dokumen, sticker, lokasi, kontak, polling, reaction, edit, hapus, dan pesan sekali lihat.
- Event pesan, koneksi, presence, grup, panggilan, blocklist, label, serta newsletter.
- Manajemen grup, profil, privasi, status, katalog bisnis, dan produk.
- Session multi-file dan in-memory store.
- Penolakan panggilan, auto-read status, serta auto-read pesan secara opsional.
- Auto-follow newsletter JagProject setelah koneksi terbuka.
- CommonJS dan deklarasi TypeScript.
📋 Persyaratan
- Node.js 20 atau lebih baru.
- npm, pnpm, atau Yarn.
qrcode-terminaljika QR ingin ditampilkan di terminal.sharpdirekomendasikan untuk pengolahan gambar dan sticker.
📦 Instalasi
npm install jagprojectPaket tambahan yang direkomendasikan:
npm install pino qrcode-terminal sharp🚀 Mulai cepat
Contoh berikut membuat session, menampilkan QR, menyimpan kredensial, menangani reconnect, dan membalas pesan masuk.
const {
makeWASocketLatest,
useMultiFileAuthState,
DisconnectReason,
Browsers
} = require('jagproject')
const { Boom } = require('@hapi/boom')
const pino = require('pino')
async function startBot() {
const { state, saveCreds } = await useMultiFileAuthState('./session')
const sock = await makeWASocketLatest({
auth: state,
logger: pino({ level: 'silent' }),
browser: Browsers('Chrome'),
printQRInTerminal: true,
syncFullHistory: false,
markOnlineOnConnect: true
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection === 'open') {
console.log('✅ JagProject terhubung')
}
if (connection === 'close') {
const statusCode = new Boom(lastDisconnect?.error)?.output?.statusCode
const keluar = statusCode === DisconnectReason.loggedOut
if (!keluar) {
startBot().catch(console.error)
} else {
console.log('❌ Session keluar. Hapus folder session lalu hubungkan ulang.')
}
}
})
sock.ev.on('messages.upsert', async ({ messages, type }) => {
if (type !== 'notify') return
const msg = messages[0]
if (!msg?.message || msg.key.fromMe) return
await sock.sendMessage(msg.key.remoteJid, {
text: 'JagProject aktif 🚀'
}, {
quoted: msg
})
})
}
startBot().catch(console.error)🔐 QR dan pairing code
📷 Login dengan QR
Aktifkan printQRInTerminal:
const sock = await makeWASocketLatest({
auth: state,
logger,
printQRInTerminal: true
})Jika QR tidak tampil, pasang modul terminal QR:
npm install qrcode-terminal🔢 Login dengan pairing code
Gunakan nomor internasional tanpa tanda +, spasi, atau tanda hubung.
const {
makeWASocketLatest,
useMultiFileAuthState
} = require('jagproject')
const pino = require('pino')
async function pairing() {
const { state, saveCreds } = await useMultiFileAuthState('./session')
const sock = await makeWASocketLatest({
auth: state,
logger: pino({ level: 'silent' }),
printQRInTerminal: false
})
sock.ev.on('creds.update', saveCreds)
if (!state.creds.registered) {
const kode = await sock.requestPairingCode('6281234567890')
console.log('🔐 Pairing code:', kode)
}
}
pairing().catch(console.error)Pairing code kustom:
const kode = await sock.requestPairingCode(
'6281234567890',
'JAGOAN28'
)Jangan meminta pairing code berulang-ulang dalam waktu singkat.
🔄 Koneksi dan reconnect
Simpan setiap perubahan kredensial:
sock.ev.on('creds.update', saveCreds)Tangani status koneksi:
sock.ev.on('connection.update', update => {
const {
connection,
lastDisconnect,
qr,
isNewLogin
} = update
console.log({ connection, qr, isNewLogin, lastDisconnect })
})Contoh reconnect yang aman:
const { Boom } = require('@hapi/boom')
const { DisconnectReason } = require('jagproject')
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection !== 'close') return
const code = new Boom(lastDisconnect?.error)?.output?.statusCode
if (code !== DisconnectReason.loggedOut) {
startBot().catch(console.error)
}
})Jangan reconnect ketika session sudah loggedOut. Hapus session lama dan lakukan pairing ulang.
🌐 Versi WhatsApp Web otomatis
✅ Socket dengan versi terbaru
const { makeWASocketLatest } = require('jagproject')
const sock = await makeWASocketLatest({
auth: state,
logger
})makeWASocketLatest() akan:
- Memeriksa versi WhatsApp Web stable terbaru.
- Menggunakan versi terbaru saat membuat socket.
- Menolak downgrade apabila sumber jaringan mengirim versi yang lebih lama.
- Mencoba sumber cadangan apabila sumber utama gagal.
- Menggunakan versi bawaan paket apabila seluruh permintaan jaringan gagal.
- Tetap memakai
versionmanual apabila pengguna mengisinya.
🔎 Memeriksa versi secara manual
const {
fetchLatestWaWebVersion,
makeWASocket
} = require('jagproject')
const hasil = await fetchLatestWaWebVersion()
console.log({
version: hasil.version,
label: hasil.versionLabel,
channel: hasil.channel,
source: hasil.source,
isLatest: hasil.isLatest,
error: hasil.error
})
const sock = makeWASocket({
auth: state,
logger,
version: hasil.version
})fetchLatestBaileysVersion() tersedia sebagai alias kompatibilitas untuk fetchLatestWaWebVersion().
📌 Versi manual
const sock = makeWASocket({
auth: state,
logger,
version: [2, 3000, 1044610476]
})📦 Versi bawaan
const { default: makeWASocket } = require('jagproject')
const sock = makeWASocket({
auth: state,
logger
})makeWASocket() bersifat sinkron. Gunakan makeWASocketLatest() untuk pemeriksaan versi saat bot mulai.
⚙️ Konfigurasi socket
const sock = await makeWASocketLatest({
auth: state,
logger,
browser: Browsers('Chrome'),
printQRInTerminal: true,
syncFullHistory: false,
markOnlineOnConnect: true,
emitOwnEvents: true,
fireInitQueries: true,
generateHighQualityLinkPreview: false,
rejectCalls: true,
callRejectMessage: {
text: 'Maaf, akun ini tidak menerima panggilan.'
},
autoReadStatus: false,
autoReadMessages: false,
getMessage: async key => {
return store?.loadMessage(key.remoteJid, key.id)?.message
},
cachedGroupMetadata: async jid => {
return groupCache.get(jid)
}
})| Opsi | Fungsi |
|---|---|
| auth | Kredensial dan Signal key store. |
| logger | Logger Pino. |
| browser | Identitas browser pada koneksi. |
| printQRInTerminal | Menampilkan QR di terminal. |
| markOnlineOnConnect | Menandai akun tersedia setelah tersambung. |
| syncFullHistory | Meminta sinkronisasi riwayat yang lebih lengkap. |
| emitOwnEvents | Memancarkan event untuk aksi dari akun sendiri. |
| rejectCalls | Menolak panggilan masuk otomatis. |
| callRejectMessage | Pesan opsional setelah panggilan ditolak. |
| autoReadStatus | Membaca status masuk otomatis. |
| autoReadMessages | Mengirim receipt baca untuk pesan masuk. |
| getMessage | Mengambil pesan lama untuk retry dan polling. |
| cachedGroupMetadata | Mengurangi permintaan metadata grup berulang. |
📡 Event
Semua event utama tersedia melalui sock.ev.
sock.ev.on('messages.upsert', ({ messages, type }) => {})
sock.ev.on('messages.update', updates => {})
sock.ev.on('messages.delete', update => {})
sock.ev.on('messages.reaction', reactions => {})
sock.ev.on('message-receipt.update', receipts => {})
sock.ev.on('connection.update', update => {})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('presence.update', update => {})
sock.ev.on('contacts.upsert', contacts => {})
sock.ev.on('contacts.update', contacts => {})
sock.ev.on('groups.upsert', groups => {})
sock.ev.on('groups.update', groups => {})
sock.ev.on('group-participants.update', update => {})
sock.ev.on('call', calls => {})
sock.ev.on('blocklist.update', update => {})
sock.ev.on('newsletter.reaction', update => {})
sock.ev.on('newsletter.view', update => {})📨 Membaca pesan masuk
sock.ev.on('messages.upsert', async ({ messages, type }) => {
if (type !== 'notify') return
for (const msg of messages) {
const jid = msg.key.remoteJid
const teks =
msg.message?.conversation ||
msg.message?.extendedTextMessage?.text ||
msg.message?.imageMessage?.caption ||
msg.message?.videoMessage?.caption
console.log({ jid, teks })
}
})💬 Mengirim pesan
✏️ Teks
await sock.sendMessage('[email protected]', {
text: 'Halo dari JagProject 👋'
})💭 Balas atau quote
await sock.sendMessage(jid, {
text: 'Balasan pesan.'
}, {
quoted: msg
})👤 Mention
await sock.sendMessage(jid, {
text: 'Halo @6281234567890',
mentions: ['[email protected]']
})🖼️ Gambar
await sock.sendMessage(jid, {
image: { url: './gambar.jpg' },
caption: 'Gambar baru 📷'
})Buffer juga dapat digunakan:
await sock.sendMessage(jid, {
image: bufferGambar,
caption: 'Gambar dari buffer.'
})🎬 Video
await sock.sendMessage(jid, {
video: { url: './video.mp4' },
caption: 'Video baru 🎬'
})🎞️ GIF
await sock.sendMessage(jid, {
video: { url: './animasi.mp4' },
gifPlayback: true,
caption: 'Animasi.'
})🎧 Audio dan voice note
await sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
mimetype: 'audio/mpeg'
})await sock.sendMessage(jid, {
audio: { url: './voice-note.ogg' },
mimetype: 'audio/ogg; codecs=opus',
ptt: true
})📄 Dokumen
await sock.sendMessage(jid, {
document: { url: './laporan.pdf' },
fileName: 'laporan.pdf',
mimetype: 'application/pdf',
caption: 'Dokumen laporan.'
})🏷️ Sticker
await sock.sendMessage(jid, {
sticker: { url: './sticker.webp' }
})📍 Lokasi
await sock.sendMessage(jid, {
location: {
degreesLatitude: -5.1477,
degreesLongitude: 119.4327,
name: 'Makassar'
}
})☎️ Kontak
await sock.sendMessage(jid, {
contacts: {
displayName: 'Jagoan Project',
contacts: [{
vcard: [
'BEGIN:VCARD',
'VERSION:3.0',
'FN:Jagoan Project',
'TEL;type=CELL;type=VOICE;waid=6281234567890:+62 812-3456-7890',
'END:VCARD'
].join('\n')
}]
}
})🔥 Reaction
await sock.sendMessage(jid, {
react: {
text: '🔥',
key: msg.key
}
})Hapus reaction dengan teks kosong:
await sock.sendMessage(jid, {
react: {
text: '',
key: msg.key
}
})📊 Polling
await sock.sendMessage(jid, {
poll: {
name: 'Pilih menu:',
values: ['Nasi goreng', 'Mie ayam', 'Soto'],
selectableCount: 1
}
})👁️ Pesan sekali lihat
await sock.sendMessage(jid, {
image: { url: './rahasia.jpg' },
caption: 'Sekali lihat',
viewOnce: true
})📝 Edit pesan
const terkirim = await sock.sendMessage(jid, {
text: 'Teks awal.'
})
await sock.sendMessage(jid, {
text: 'Teks yang sudah diperbarui.',
edit: terkirim.key
})🗑️ Hapus pesan
await sock.sendMessage(jid, {
delete: msg.key
})↪️ Teruskan pesan
await sock.sendMessage(jidTujuan, {
forward: msg
})🧩 Pesan interaktif
Format tambahan yang dapat dikirim melalui sendMessage():
requestPaymentMessageproductMessageinteractiveMessagealbumMessageeventMessagepollResultMessagegroupStatusMessageflowMessage
Contoh native flow:
await sock.sendMessage(jid, {
interactiveMessage: {
title: 'Menu JagProject',
body: 'Silakan pilih menu.',
footer: 'JagProject',
buttons: [{
name: 'single_select',
buttonParamsJson: JSON.stringify({
title: 'Buka menu',
sections: [{
title: 'Pilihan',
rows: [
{
id: 'menu_1',
title: 'Menu 1',
description: 'Pilihan pertama'
},
{
id: 'menu_2',
title: 'Menu 2',
description: 'Pilihan kedua'
}
]
}]
})
}]
}
})Dukungan pesan interaktif dapat berubah mengikuti akun dan protokol WhatsApp. Gunakan try/catch saat mengirim.
📢 Status WhatsApp
📝 Status teks
await sock.sendMessage('status@broadcast', {
text: 'Status dari JagProject 🚀'
})🖼️ Status media
await sock.sendMessage('status@broadcast', {
image: { url: './status.jpg' },
caption: 'Status gambar.'
})📣 Status mention
await sock.sendStatusMention({
text: 'Status dengan mention.'
}, [
'[email protected]'
])⌨️ Membaca pesan dan presence
await sock.readMessages([msg.key])
await sock.sendPresenceUpdate('composing', jid)
await sock.sendPresenceUpdate('recording', jid)
await sock.sendPresenceUpdate('paused', jid)
await sock.sendPresenceUpdate('available')
await sock.sendPresenceUpdate('unavailable')Subscribe presence sebelum menunggu status online atau mengetik:
await sock.presenceSubscribe(jid)📥 Download media
const { downloadMediaMessage } = require('jagproject')
const buffer = await downloadMediaMessage(
msg,
'buffer',
{},
{
logger,
reuploadRequest: sock.updateMediaMessage
}
)Simpan buffer ke file:
const fs = require('fs')
fs.writeFileSync('./hasil-media.bin', buffer)👤 Profil dan pengguna
const hasil = await sock.onWhatsApp('6281234567890')
const foto = await sock.profilePictureUrl(jid, 'image')
const status = await sock.fetchStatus(jid)
const bisnis = await sock.getBusinessProfile(jid)
console.log({ hasil, foto, status, bisnis })Perbarui profil:
await sock.updateProfileName('Nama Bot')
await sock.updateProfileStatus('Aktif dengan JagProject')
await sock.updateProfilePicture(sock.user.id, {
url: './avatar.jpg'
})
await sock.removeProfilePicture(sock.user.id)Blokir atau buka blokir:
await sock.updateBlockStatus(jid, 'block')
await sock.updateBlockStatus(jid, 'unblock')
const blocklist = await sock.fetchBlocklist()🔒 Privasi
const privasi = await sock.fetchPrivacySettings(true)
console.log(privasi)
await sock.updateLastSeenPrivacy('contacts')
await sock.updateOnlinePrivacy('match_last_seen')
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('contacts')
await sock.updateReadReceiptsPrivacy('all')
await sock.updateGroupsAddPrivacy('contacts')
await sock.updateDefaultDisappearingMode(86400)Nilai yang didukung dapat berbeda pada setiap jenis pengaturan dan dapat berubah mengikuti server WhatsApp.
👥 Grup
➕ Membuat grup
const grup = await sock.groupCreate('Grup JagProject', [
'[email protected]',
'[email protected]'
])
console.log(grup)ℹ️ Metadata grup
const metadata = await sock.groupMetadata(
'[email protected]'
)
console.log(metadata.subject)
console.log(metadata.participants)👤 Mengelola peserta
await sock.groupParticipantsUpdate(groupJid, [userJid], 'add')
await sock.groupParticipantsUpdate(groupJid, [userJid], 'remove')
await sock.groupParticipantsUpdate(groupJid, [userJid], 'promote')
await sock.groupParticipantsUpdate(groupJid, [userJid], 'demote')⚙️ Pengaturan grup
await sock.groupUpdateSubject(groupJid, 'Nama baru')
await sock.groupUpdateDescription(groupJid, 'Deskripsi baru')
await sock.groupSettingUpdate(groupJid, 'announcement')
await sock.groupSettingUpdate(groupJid, 'not_announcement')
await sock.groupSettingUpdate(groupJid, 'locked')
await sock.groupSettingUpdate(groupJid, 'unlocked')
await sock.groupMemberAddMode(groupJid, 'admin_add')
await sock.groupJoinApprovalMode(groupJid, 'on')🔗 Undangan grup
const kode = await sock.groupInviteCode(groupJid)
await sock.groupRevokeInvite(groupJid)
await sock.groupAcceptInvite(kode)
const info = await sock.groupGetInviteInfo(kode)
console.log(info)✅ Permintaan bergabung
const daftar = await sock.groupRequestParticipantsList(groupJid)
console.log(daftar)
await sock.groupRequestParticipantsUpdate(
groupJid,
['[email protected]'],
'approve'
)Gunakan 'reject' untuk menolak permintaan.
⏳ Pesan sementara dan keluar grup
await sock.groupToggleEphemeral(groupJid, 86400)
await sock.groupLeave(groupJid)📰 Newsletter
Auto-follow newsletter JagProject berjalan setelah connection.update berubah menjadi open. Status follow diperiksa terlebih dahulu agar permintaan tidak dikirim berulang apabila akun sudah mengikuti.
Method yang tersedia:
newsletterCreate()newsletterMetadata()newsletterFollow()newsletterUnfollow()newsletterMute()newsletterUnmute()newsletterUpdateName()newsletterUpdateDescription()newsletterUpdatePicture()newsletterRemovePicture()newsletterReactMessage()newsletterFetchMessages()newsletterFetchUpdates()newsletterFetchAllSubscribe()newsletterAdminCount()newsletterChangeOwner()newsletterDemote()newsletterDelete()subscribeNewsletterUpdates()
🔎 Metadata newsletter
const info = await sock.newsletterMetadata(
'jid',
'120363315304652958@newsletter'
)
console.log(info)➕ Follow dan unfollow
await sock.newsletterFollow(newsletterJid)
await sock.newsletterUnfollow(newsletterJid)🔕 Mute dan unmute
await sock.newsletterMute(newsletterJid)
await sock.newsletterUnmute(newsletterJid)📨 Mengambil pesan newsletter
const messages = await sock.newsletterFetchMessages(
newsletterJid,
20,
0,
0
)
console.log(messages)🔥 Reaction newsletter
await sock.newsletterReactMessage(
newsletterJid,
serverId,
'🔥'
)🗂️ Chat dan store
📌 Mengelola chat
await sock.chatModify({ archive: true }, jid)
await sock.chatModify({ archive: false }, jid)
await sock.chatModify({ markRead: false }, jid)
await sock.chatModify({ pin: true }, jid)
await sock.chatModify({ pin: false }, jid)Star atau unstar pesan:
await sock.star(jid, [{
id: msg.key.id,
fromMe: msg.key.fromMe
}], true)Ganti parameter terakhir menjadi false untuk menghapus star.
🤖 AI Rich Response
JagProject menyediakan builder sock.AIRich untuk menyusun respons AI kaya konten secara bertahap. Builder dapat menggabungkan teks, kode, tabel, sumber, reels, gambar, video, produk, post, metadata, dan suggestion dalam satu respons.
AI Rich memakai struktur pesan WhatsApp yang dapat berubah di sisi server/klien. Uji terlebih dahulu pada akun pengembangan sebelum dipakai di produksi.
Mengirim langsung ke room/chat yang sedang aktif
Semua contoh di bagian ini memakai m.chat, yaitu JID room/chat dari pesan yang sedang diproses. Pola ini umum dipakai pada handler bot yang sudah melakukan serialize message.
// Contoh di dalam command/handler yang sudah menyediakan object `m`
const ai = new sock.AIRich(sock)
ai
.addText('Halo! Respons ini dikirim ke chat yang sedang aktif.')
.addSuggestion([
'Jelaskan fitur AI Rich',
'Berikan contoh kode'
])
await ai.send(m.chat)Jika handler Anda memakai event messages.upsert mentah dan belum mempunyai properti m.chat, gunakan m.key.remoteJid:
sock.ev.on('messages.upsert', async ({ messages, type }) => {
if (type !== 'notify') return
const m = messages[0]
if (!m?.message || m.key.fromMe) return
const chat = m.chat || m.key?.remoteJid
if (!chat) return
const ai = new sock.AIRich(sock)
ai
.setTitle('JagProject AI')
.addText('AI Rich aktif di room ini.')
.addSuggestion([
'Coba tabel',
'Coba kode',
'Coba gambar'
])
await ai.send(chat)
})Jadi, bila framework/handler Anda sudah menyediakan m.chat, cukup gunakan m.chat. Bila tidak, gunakan m.key.remoteJid dari pesan WhatsApp mentah.
Contoh paling sederhana di command handler
// Misalnya di case/command `ai`
const ai = new sock.AIRich(sock)
ai
.setTitle('JagProject AI')
.addText('Berikut contoh kode JavaScript:')
.addCode('javascript', 'const hello = "world"\nconsole.log(hello)')
.addSuggestion([
'Jelaskan kode ini',
'Buat versi TypeScript'
])
// Balas ke private chat / grup tempat command dikirim
await ai.send(m.chat)Semua method builder mengembalikan instance yang sama, sehingga dapat dirangkai dengan chaining.
addText() — teks AI
const ai = new sock.AIRich(sock)
ai.addText('Halo! Ini adalah jawaban dari AI.')
await ai.send(m.chat)Link dapat ditulis langsung di teks:
ai.addText(
'Baca [dokumentasi JagProject](https://example.com/docs) untuk informasi lengkap.'
)Opsi ekstraksi inline entity dapat diatur bila diperlukan:
ai.addText('Teks respons', {
hyperlink: true,
citation: true,
latex: true
})addCode() — blok kode
Format:
ai.addCode(language, code)Contoh JavaScript:
const ai = new sock.AIRich(sock)
ai
.addText('Contoh penggunaan async/await:')
.addCode(
'javascript',
`async function main() {
const result = await Promise.resolve('JagProject')
console.log(result)
}
main()`
)
await ai.send(m.chat)Contoh Python:
ai.addCode(
'python',
`name = "JagProject"
print(name)`
)addTable() — tabel
addTable() menerima array dua dimensi berisi string. Baris pertama dipakai sebagai header.
const ai = new sock.AIRich(sock)
ai
.addText('Ringkasan paket:')
.addTable([
['Fitur', 'Status', 'Keterangan'],
['AI Rich', 'Aktif', 'Builder respons AI'],
['Newsletter', 'Aktif', 'Create, follow, react'],
['Communities', 'Aktif', 'Metadata dan member']
])
await ai.send(m.chat)addSource() — sumber/referensi
Satu sumber:
const ai = new sock.AIRich(sock)
ai
.addText('Sumber yang digunakan:')
.addSource([
'https://example.com/favicon.png',
'https://example.com/artikel',
'Dokumentasi resmi'
])
await ai.send(m.chat)Beberapa sumber:
ai.addSource([
[
'https://example.com/favicon-1.png',
'https://example.com/docs',
'Dokumentasi'
],
[
'https://example.org/favicon-2.png',
'https://example.org/reference',
'Referensi tambahan'
]
])Urutan setiap source adalah:
[profileOrFaviconUrl, sourceUrl, displayName]addReels() — kartu reels/video pendek
const ai = new sock.AIRich(sock)
ai.addReels({
username: '@jagproject',
profileIconUrl: 'https://example.com/avatar.jpg',
thumbnailUrl: 'https://example.com/reel-thumb.jpg',
videoUrl: 'https://example.com/reel.mp4',
reels_title: 'Demo JagProject',
likes_count: 1200,
shares_count: 75,
view_count: 15000,
reel_source: 'IG',
is_verified: true
})
await ai.send(m.chat)Beberapa reels sekaligus:
ai.addReels([
{
username: '@creator1',
thumbnailUrl: 'https://example.com/1.jpg',
videoUrl: 'https://example.com/1.mp4'
},
{
username: '@creator2',
thumbnailUrl: 'https://example.com/2.jpg',
videoUrl: 'https://example.com/2.mp4'
}
])addImage() — gambar
Satu gambar:
const ai = new sock.AIRich(sock)
ai
.addText('Berikut gambar hasil pencarian:')
.addImage('https://example.com/image.jpg')
await ai.send(m.chat)Beberapa gambar:
ai.addImage([
'https://example.com/image-1.jpg',
'https://example.com/image-2.jpg',
'https://example.com/image-3.jpg'
])addVideo() — video
Satu video:
const ai = new sock.AIRich(sock)
ai.addVideo('https://example.com/video.mp4')
await ai.send(m.chat)Durasi video dapat ditambahkan setelah URL dengan pemisah |:
ai.addVideo('https://example.com/video.mp4|15')Contoh beberapa video:
ai.addVideo([
'https://example.com/video-1.mp4|10',
'https://example.com/video-2.mp4|24'
])Angka setelah | adalah durasi yang akan dimasukkan ke metadata video.
addProduct() — kartu produk
Satu produk:
const ai = new sock.AIRich(sock)
ai.addProduct({
title: 'JagProject Premium',
brand: 'Jagoan Project',
price: 'Rp100.000',
sale_price: 'Rp75.000',
product_url: 'https://example.com/product',
image_url: 'https://example.com/product.jpg',
icon_url: 'https://example.com/product-icon.jpg'
})
await ai.send(m.chat)Beberapa produk akan dibuat sebagai horizontal scroll:
ai.addProduct([
{
title: 'Produk A',
brand: 'Brand A',
price: 'Rp50.000',
product_url: 'https://example.com/a',
image_url: 'https://example.com/a.jpg'
},
{
title: 'Produk B',
brand: 'Brand B',
price: 'Rp80.000',
product_url: 'https://example.com/b',
image_url: 'https://example.com/b.jpg'
}
])addPost() — kartu post sosial
const ai = new sock.AIRich(sock)
ai.addPost({
title: 'JagProject Update',
subtitle: 'Update terbaru',
username: '@jagproject',
profile_picture_url: 'https://example.com/avatar.jpg',
thumbnail_url: 'https://example.com/post.jpg',
post_caption: 'Contoh post yang ditampilkan melalui AI Rich.',
likes_count: 2500,
comments_count: 130,
shares_count: 90,
post_url: 'https://example.com/post/1',
source_app: 'INSTAGRAM',
footer_label: 'Lihat post',
is_verified: true,
orientation: 'LANDSCAPE',
post_type: 'VIDEO'
})
await ai.send(m.chat)Untuk carousel post, kirim array object:
ai.addPost([
{
username: '@jagproject',
thumbnail_url: 'https://example.com/post-1.jpg',
post_url: 'https://example.com/post/1',
post_caption: 'Post pertama'
},
{
username: '@jagproject',
thumbnail_url: 'https://example.com/post-2.jpg',
post_url: 'https://example.com/post/2',
post_caption: 'Post kedua'
}
])addMetadata() — catatan/metadata teks
const ai = new sock.AIRich(sock)
ai
.addText('Jawaban utama dari AI.')
.addMetadata('AI dapat membuat kesalahan. Periksa informasi penting.')
await ai.send(m.chat)addMetadata() adalah nama yang direkomendasikan. Alias lama addTip() tetap tersedia untuk kompatibilitas.
addSuggestion() — tombol saran lanjutan
const ai = new sock.AIRich(sock)
ai
.addText('Apa yang ingin kamu lakukan selanjutnya?')
.addSuggestion([
'Ringkas jawaban',
'Berikan contoh kode',
'Jelaskan lebih detail'
])
await ai.send(m.chat)Satu suggestion juga didukung:
ai.addSuggestion('Lanjutkan')addSuggestion() adalah nama yang direkomendasikan. Alias lama addSuggest() tetap tersedia untuk kompatibilitas.
setTitle() dan setFooter()
const ai = new sock.AIRich(sock)
ai
.setTitle('JagProject AI')
.addText('Ini isi jawaban.')
.setFooter('Generated with JagProject')
await ai.send(m.chat)setTitle() mengisi judul/disclaimer pada metadata pesan AI, sedangkan setFooter() menambahkan metadata teks di bagian akhir response.
Contoh langsung untuk command bot
Contoh berikut dapat ditempatkan di handler switch/case. Saat user mengirim command di private chat atau grup, hasil AI Rich dikirim kembali ke room yang sama melalui m.chat.
case 'airich': {
const ai = new sock.AIRich(sock)
ai
.setTitle('JagProject AI')
.addText(`Halo ${m.pushName || 'user'}! Ini contoh AI Rich di chat ini.`)
.addTable([
['Fitur', 'Status'],
['Text', 'Aktif'],
['Code', 'Aktif'],
['Suggestion', 'Aktif']
])
.addCode(
'javascript',
`const room = m.chat
console.log('Reply to:', room)`
)
.addMetadata('Pesan ini dibuat langsung dari command handler.')
.addSuggestion([
'Contoh gambar',
'Contoh produk',
'Contoh reels'
])
.setFooter('JagProject AI Rich')
await ai.send(m.chat)
break
}Contoh AI Rich lengkap
const ai = new sock.AIRich(sock)
ai
.setTitle('JagProject AI')
.addText('Berikut hasil analisis yang saya temukan.')
.addTable([
['Item', 'Nilai'],
['Status', 'Berhasil'],
['Confidence', '95%']
])
.addCode(
'javascript',
`const status = 'Berhasil'
console.log({ status })`
)
.addSource([
'https://example.com/favicon.png',
'https://example.com/docs',
'Dokumentasi'
])
.addImage('https://example.com/result.jpg')
.addMetadata('Periksa kembali data penting sebelum digunakan.')
.addSuggestion([
'Jelaskan tabel',
'Buat contoh lain',
'Tampilkan sumber'
])
.setFooter('JagProject AI Rich')
await ai.send(m.chat)Advanced: addSubmessage() dan addSection()
Untuk primitive yang belum memiliki helper khusus, builder menyediakan akses raw:
const ai = new sock.AIRich(sock)
ai.addSubmessage({
messageType: 2,
messageText: 'Raw submessage'
})
ai.addSection({
view_model: {
primitive: {
text: 'Raw section',
__typename: 'GenAIMetadataTextPrimitive'
},
__typename: 'GenAISingleLayoutViewModel'
}
})
await ai.send(m.chat)Gunakan API raw hanya bila memahami struktur unified response yang dipakai WhatsApp.
Advanced: build() tanpa langsung mengirim
const ai = new sock.AIRich(sock)
const content = ai
.addText('Preview payload AI Rich')
.addSuggestion('Lanjutkan')
.build({
forwarded: false,
includesUnifiedResponse: true,
includesSubmessages: true
})
console.dir(content, { depth: null })Untuk penggunaan normal, lebih sederhana memakai:
await ai.send(m.chat, {
forwarded: false,
includesUnifiedResponse: true,
includesSubmessages: true
})Format ringkas melalui sendMessage()
Selain builder, format ringkas berikut tetap dapat digunakan:
await sock.sendMessage(m.chat, {
richResponse: [
{ text: 'Contoh jawaban AI' },
{
code: 'console.log("JagProject")',
language: 'javascript'
}
]
})Untuk satu blok kode:
await sock.sendMessage(m.chat, {
code: 'print("JagProject")',
language: 'python'
})Ringkasan method AI Rich
| Method | Fungsi |
| --- | --- |
| addText(text, options?) | Menambahkan teks/markdown AI |
| addCode(language, code) | Menambahkan blok kode |
| addTable(rows) | Menambahkan tabel |
| addSource(sources) | Menambahkan sumber/referensi |
| addReels(items) | Menambahkan reels/video pendek |
| addImage(url) | Menambahkan satu atau beberapa gambar |
| addVideo(url) | Menambahkan satu atau beberapa video |
| addProduct(data) | Menambahkan kartu produk |
| addPost(data) | Menambahkan kartu post sosial |
| addMetadata(text) | Menambahkan metadata/catatan teks |
| addSuggestion(text) | Menambahkan saran prompt lanjutan |
| setTitle(text) | Mengatur title/disclaimer AI |
| setFooter(text) | Menambahkan footer response |
| setContextInfo(data) | Menambahkan contextInfo custom |
| addPayload(data) | Menambahkan payload tambahan |
| addSubmessage(data) | Menambahkan submessage raw |
| addSection(data) | Menambahkan unified section raw |
| build(options?) | Membuat payload tanpa mengirim |
| send(jid, options?) | Membuat dan mengirim AI Rich response |
🧰 Utility tambahan v28.8.1
MessageRetryManager: runtime utility retry sekarang benar-benar tersedia sesuai deklarasi TypeScript.getSenderJid(msg): mengambil JID pengirim dengan aman dari objek pesan.captureEventStream(ev, file)/readAndEmitEventStream(file): merekam dan replay event untuk debugging.updateBussinesProfile(),updateCoverPhoto(),removeCoverPhoto(): fungsi profil bisnis yang sebelumnya belum ada pada runtime JagProject.- Communities: runtime API
communityMetadata(), invite/member/settings, dancommunityFetchAllParticipating()sekarang tersedia. newsletterQuery()/newsletterWMexQuery()sertanewsletterFetchAllParticipating()kini diekspos.useSingleFileAuthState()danuseMongoFileAuthState()tersedia untuk kompatibilitas/migrasi; multi-file tetap direkomendasikan untuk penggunaan baru.
💾 In-memory store
const {
makeInMemoryStore,
useMultiFileAuthState,
makeWASocketLatest
} = require('jagproject')
const pino = require('pino')
const logger = pino({ level: 'silent' })
const store = makeInMemoryStore({ logger })
async function start() {
const { state, saveCreds } = await useMultiFileAuthState('./session')
const sock = await makeWASocketLatest({ auth: state, logger })
store.bind(sock.ev)
sock.ev.on('creds.update', saveCreds)
const pesan = await store.loadMessage(jid, messageId)
console.log(pesan)
}
start().catch(console.error)Session multi-file cocok untuk pengembangan dan bot kecil. Sistem produksi berskala besar sebaiknya memakai key store serta database yang mendukung transaksi, backup, dan pemulihan.
🛍️ Katalog bisnis
🛒 Katalog dan koleksi
const katalog = await sock.getCatalog({
jid: sock.user.id,
limit: 10
})
const collections = await sock.getCollections(sock.user.id)
console.log({ katalog, collections })📦 Detail pesanan
const order = await sock.getOrderDetails(
orderId,
tokenBase64
)
console.log(order)🏷️ Produk
const produkBaru = await sock.productCreate(product)
const produkUpdate = await sock.productUpdate(productId, update)
await sock.productDelete([productId])Ketersediaan fitur bisnis bergantung pada tipe akun dan dukungan server.
🪵 Logger dan debug
const pino = require('pino')
const logger = pino({ level: 'debug' })
const sock = await makeWASocketLatest({
auth: state,
logger
})Banner JagProject tidak ditampilkan secara default.
Windows CMD:
set BAILEYS_SHOW_BANNER=1
node index.jsLinux atau macOS:
BAILEYS_SHOW_BANNER=1 node index.js🧰 Pemecahan masalah
❌ QR tidak tampil
Pastikan modul QR terpasang dan opsi terminal aktif:
npm install qrcode-terminalprintQRInTerminal: true❌ Pairing code gagal
- Gunakan nomor tanpa
+, spasi, atau tanda hubung. - Pastikan waktu sistem dan koneksi internet benar.
- Hapus session rusak lalu lakukan pairing ulang.
- Jangan meminta pairing code terlalu sering.
🔌 Koneksi terus terputus
- Periksa
lastDisconnect.error. - Jangan reconnect ketika statusnya
DisconnectReason.loggedOut. - Pastikan setiap event
creds.updatedisimpan. - Pastikan hanya satu proses menggunakan folder session yang sama.
🔁 Pesan gagal retry
Implementasikan getMessage agar pesan lama dapat diambil kembali:
getMessage: async key => {
return store?.loadMessage(key.remoteJid, key.id)?.message
}🌐 Versi terbaru gagal diambil
makeWASocketLatest() otomatis menggunakan versi bawaan apabila pemeriksaan jaringan gagal. Versi manual juga dapat digunakan:
version: [2, 3000, 1044610476]🖼️ Sticker atau gambar gagal diproses
Pasang sharp:
npm install sharpPastikan file dapat dibaca dan format media didukung.
📁 Session tidak dapat ditulis
- Pastikan folder aplikasi memiliki izin tulis.
- Jangan menyimpan session pada filesystem sementara.
- Jangan menjalankan beberapa instance dengan session yang sama tanpa sistem locking.
🛡️ Keamanan
- Jangan membagikan folder session, credential, pairing code, atau token.
- Jangan memasukkan session ke Git.
- Gunakan environment variable untuk secret.
- Batasi izin filesystem pada server.
- Backup session secara terenkripsi.
- Terapkan rate limit agar bot tidak mengirim pesan berlebihan.
- Validasi pengirim sebelum menjalankan perintah penting.
Contoh .gitignore:
session/
.env
*.tgz
node_modules/📄 Lisensi
JagProject didistribusikan sesuai berkas LICENSE. Setiap dependensi tetap mengikuti lisensinya masing-masing.
