<h1 align="center">⚡ Baileys Modification</h1>

<p align="center">
  <strong>JagProject</strong><br>
  Dokumentasi WhatsApp Web API untuk Node.js
</p>

<p align="center">
  <sub>🕒 Pembaruan terakhir: <strong>14 Agustus 2026, 17:04:35 WITA</strong></sub>
</p>

> 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](#instalasi)
- [🚀 Mulai cepat](#mulai-cepat)
- [🔐 QR dan pairing code](#qr-dan-pairing-code)
- [🔄 Koneksi dan reconnect](#koneksi-dan-reconnect)
- [🌐 Versi WhatsApp Web otomatis](#versi-whatsapp-web-otomatis)
- [⚙️ Konfigurasi socket](#konfigurasi-socket)
- [📡 Event](#event)
- [💬 Mengirim pesan](#mengirim-pesan)
- [🧩 Pesan interaktif](#pesan-interaktif)
- [📢 Status WhatsApp](#status-whatsapp)
- [👥 Grup](#grup)
- [📰 Newsletter](#newsletter)
- [🗂️ Chat dan store](#chat-dan-store)
- [🤖 AI Rich Response](#ai-rich-response)
- [🛍️ Katalog bisnis](#katalog-bisnis)
- [🧰 Pemecahan masalah](#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-terminal` jika QR ingin ditampilkan di terminal.
- `sharp` direkomendasikan untuk pengolahan gambar dan sticker.

<a id="instalasi"></a>

## 📦 Instalasi

```bash
npm install jagproject
```

Paket tambahan yang direkomendasikan:

```bash
npm install pino qrcode-terminal sharp
```

<a id="mulai-cepat"></a>

## 🚀 Mulai cepat

Contoh berikut membuat session, menampilkan QR, menyimpan kredensial, menangani reconnect, dan membalas pesan masuk.

```javascript
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)
```

<a id="qr-dan-pairing-code"></a>

## 🔐 QR dan pairing code

### 📷 Login dengan QR

Aktifkan `printQRInTerminal`:

```javascript
const sock = await makeWASocketLatest({
  auth: state,
  logger,
  printQRInTerminal: true
})
```

Jika QR tidak tampil, pasang modul terminal QR:

```bash
npm install qrcode-terminal
```

### 🔢 Login dengan pairing code

Gunakan nomor internasional tanpa tanda `+`, spasi, atau tanda hubung.

```javascript
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:

```javascript
const kode = await sock.requestPairingCode(
  '6281234567890',
  'JAGOAN28'
)
```

> Jangan meminta pairing code berulang-ulang dalam waktu singkat.

<a id="koneksi-dan-reconnect"></a>

## 🔄 Koneksi dan reconnect

Simpan setiap perubahan kredensial:

```javascript
sock.ev.on('creds.update', saveCreds)
```

Tangani status koneksi:

```javascript
sock.ev.on('connection.update', update => {
  const {
    connection,
    lastDisconnect,
    qr,
    isNewLogin
  } = update

  console.log({ connection, qr, isNewLogin, lastDisconnect })
})
```

Contoh reconnect yang aman:

```javascript
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.

<a id="versi-whatsapp-web-otomatis"></a>

## 🌐 Versi WhatsApp Web otomatis

### ✅ Socket dengan versi terbaru

```javascript
const { makeWASocketLatest } = require('jagproject')

const sock = await makeWASocketLatest({
  auth: state,
  logger
})
```

`makeWASocketLatest()` akan:

1. Memeriksa versi WhatsApp Web stable terbaru.
2. Menggunakan versi terbaru saat membuat socket.
3. Menolak downgrade apabila sumber jaringan mengirim versi yang lebih lama.
4. Mencoba sumber cadangan apabila sumber utama gagal.
5. Menggunakan versi bawaan paket apabila seluruh permintaan jaringan gagal.
6. Tetap memakai `version` manual apabila pengguna mengisinya.

### 🔎 Memeriksa versi secara manual

```javascript
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

```javascript
const sock = makeWASocket({
  auth: state,
  logger,
  version: [2, 3000, 1044610476]
})
```

### 📦 Versi bawaan

```javascript
const { default: makeWASocket } = require('jagproject')

const sock = makeWASocket({
  auth: state,
  logger
})
```

`makeWASocket()` bersifat sinkron. Gunakan `makeWASocketLatest()` untuk pemeriksaan versi saat bot mulai.

<a id="konfigurasi-socket"></a>

## ⚙️ Konfigurasi socket

```javascript
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. |

<a id="event"></a>

## 📡 Event

Semua event utama tersedia melalui `sock.ev`.

```javascript
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

```javascript
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 })
  }
})
```

<a id="mengirim-pesan"></a>

## 💬 Mengirim pesan

### ✏️ Teks

```javascript
await sock.sendMessage('6281234567890@s.whatsapp.net', {
  text: 'Halo dari JagProject 👋'
})
```

### 💭 Balas atau quote

```javascript
await sock.sendMessage(jid, {
  text: 'Balasan pesan.'
}, {
  quoted: msg
})
```

### 👤 Mention

```javascript
await sock.sendMessage(jid, {
  text: 'Halo @6281234567890',
  mentions: ['6281234567890@s.whatsapp.net']
})
```

### 🖼️ Gambar

```javascript
await sock.sendMessage(jid, {
  image: { url: './gambar.jpg' },
  caption: 'Gambar baru 📷'
})
```

Buffer juga dapat digunakan:

```javascript
await sock.sendMessage(jid, {
  image: bufferGambar,
  caption: 'Gambar dari buffer.'
})
```

### 🎬 Video

```javascript
await sock.sendMessage(jid, {
  video: { url: './video.mp4' },
  caption: 'Video baru 🎬'
})
```

### 🎞️ GIF

```javascript
await sock.sendMessage(jid, {
  video: { url: './animasi.mp4' },
  gifPlayback: true,
  caption: 'Animasi.'
})
```

### 🎧 Audio dan voice note

```javascript
await sock.sendMessage(jid, {
  audio: { url: './audio.mp3' },
  mimetype: 'audio/mpeg'
})
```

```javascript
await sock.sendMessage(jid, {
  audio: { url: './voice-note.ogg' },
  mimetype: 'audio/ogg; codecs=opus',
  ptt: true
})
```

### 📄 Dokumen

```javascript
await sock.sendMessage(jid, {
  document: { url: './laporan.pdf' },
  fileName: 'laporan.pdf',
  mimetype: 'application/pdf',
  caption: 'Dokumen laporan.'
})
```

### 🏷️ Sticker

```javascript
await sock.sendMessage(jid, {
  sticker: { url: './sticker.webp' }
})
```

### 📍 Lokasi

```javascript
await sock.sendMessage(jid, {
  location: {
    degreesLatitude: -5.1477,
    degreesLongitude: 119.4327,
    name: 'Makassar'
  }
})
```

### ☎️ Kontak

```javascript
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

```javascript
await sock.sendMessage(jid, {
  react: {
    text: '🔥',
    key: msg.key
  }
})
```

Hapus reaction dengan teks kosong:

```javascript
await sock.sendMessage(jid, {
  react: {
    text: '',
    key: msg.key
  }
})
```

### 📊 Polling

```javascript
await sock.sendMessage(jid, {
  poll: {
    name: 'Pilih menu:',
    values: ['Nasi goreng', 'Mie ayam', 'Soto'],
    selectableCount: 1
  }
})
```

### 👁️ Pesan sekali lihat

```javascript
await sock.sendMessage(jid, {
  image: { url: './rahasia.jpg' },
  caption: 'Sekali lihat',
  viewOnce: true
})
```

### 📝 Edit pesan

```javascript
const terkirim = await sock.sendMessage(jid, {
  text: 'Teks awal.'
})

await sock.sendMessage(jid, {
  text: 'Teks yang sudah diperbarui.',
  edit: terkirim.key
})
```

### 🗑️ Hapus pesan

```javascript
await sock.sendMessage(jid, {
  delete: msg.key
})
```

### ↪️ Teruskan pesan

```javascript
await sock.sendMessage(jidTujuan, {
  forward: msg
})
```

<a id="pesan-interaktif"></a>

## 🧩 Pesan interaktif

Format tambahan yang dapat dikirim melalui `sendMessage()`:

- `requestPaymentMessage`
- `productMessage`
- `interactiveMessage`
- `albumMessage`
- `eventMessage`
- `pollResultMessage`
- `groupStatusMessage`
- `flowMessage`

Contoh native flow:

```javascript
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.

<a id="status-whatsapp"></a>

## 📢 Status WhatsApp

### 📝 Status teks

```javascript
await sock.sendMessage('status@broadcast', {
  text: 'Status dari JagProject 🚀'
})
```

### 🖼️ Status media

```javascript
await sock.sendMessage('status@broadcast', {
  image: { url: './status.jpg' },
  caption: 'Status gambar.'
})
```

### 📣 Status mention

```javascript
await sock.sendStatusMention({
  text: 'Status dengan mention.'
}, [
  '120363000000000000@g.us'
])
```

## ⌨️ Membaca pesan dan presence

```javascript
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:

```javascript
await sock.presenceSubscribe(jid)
```

## 📥 Download media

```javascript
const { downloadMediaMessage } = require('jagproject')

const buffer = await downloadMediaMessage(
  msg,
  'buffer',
  {},
  {
    logger,
    reuploadRequest: sock.updateMediaMessage
  }
)
```

Simpan buffer ke file:

```javascript
const fs = require('fs')
fs.writeFileSync('./hasil-media.bin', buffer)
```

## 👤 Profil dan pengguna

```javascript
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:

```javascript
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:

```javascript
await sock.updateBlockStatus(jid, 'block')
await sock.updateBlockStatus(jid, 'unblock')

const blocklist = await sock.fetchBlocklist()
```

## 🔒 Privasi

```javascript
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.

<a id="grup"></a>

## 👥 Grup

### ➕ Membuat grup

```javascript
const grup = await sock.groupCreate('Grup JagProject', [
  '6281234567890@s.whatsapp.net',
  '6289876543210@s.whatsapp.net'
])

console.log(grup)
```

### ℹ️ Metadata grup

```javascript
const metadata = await sock.groupMetadata(
  '120363000000000000@g.us'
)

console.log(metadata.subject)
console.log(metadata.participants)
```

### 👤 Mengelola peserta

```javascript
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

```javascript
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

```javascript
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

```javascript
const daftar = await sock.groupRequestParticipantsList(groupJid)
console.log(daftar)

await sock.groupRequestParticipantsUpdate(
  groupJid,
  ['6281234567890@s.whatsapp.net'],
  'approve'
)
```

Gunakan `'reject'` untuk menolak permintaan.

### ⏳ Pesan sementara dan keluar grup

```javascript
await sock.groupToggleEphemeral(groupJid, 86400)
await sock.groupLeave(groupJid)
```

<a id="newsletter"></a>

## 📰 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

```javascript
const info = await sock.newsletterMetadata(
  'jid',
  '120363315304652958@newsletter'
)

console.log(info)
```

### ➕ Follow dan unfollow

```javascript
await sock.newsletterFollow(newsletterJid)
await sock.newsletterUnfollow(newsletterJid)
```

### 🔕 Mute dan unmute

```javascript
await sock.newsletterMute(newsletterJid)
await sock.newsletterUnmute(newsletterJid)
```

### 📨 Mengambil pesan newsletter

```javascript
const messages = await sock.newsletterFetchMessages(
  newsletterJid,
  20,
  0,
  0
)

console.log(messages)
```

### 🔥 Reaction newsletter

```javascript
await sock.newsletterReactMessage(
  newsletterJid,
  serverId,
  '🔥'
)
```

<a id="chat-dan-store"></a>

## 🗂️ Chat dan store

### 📌 Mengelola chat

```javascript
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:

```javascript
await sock.star(jid, [{
  id: msg.key.id,
  fromMe: msg.key.fromMe
}], true)
```

Ganti parameter terakhir menjadi `false` untuk menghapus star.

<a id="ai-rich-response"></a>

## 🤖 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.

```js
// 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`:

```js
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

```js
// 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

```js
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:

```js
ai.addText(
  'Baca [dokumentasi JagProject](https://example.com/docs) untuk informasi lengkap.'
)
```

Opsi ekstraksi inline entity dapat diatur bila diperlukan:

```js
ai.addText('Teks respons', {
  hyperlink: true,
  citation: true,
  latex: true
})
```

### `addCode()` — blok kode

Format:

```js
ai.addCode(language, code)
```

Contoh JavaScript:

```js
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:

```js
ai.addCode(
  'python',
  `name = "JagProject"
print(name)`
)
```

### `addTable()` — tabel

`addTable()` menerima array dua dimensi berisi string. Baris pertama dipakai sebagai header.

```js
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:

```js
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:

```js
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:

```js
[profileOrFaviconUrl, sourceUrl, displayName]
```

### `addReels()` — kartu reels/video pendek

```js
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:

```js
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:

```js
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:

```js
ai.addImage([
  'https://example.com/image-1.jpg',
  'https://example.com/image-2.jpg',
  'https://example.com/image-3.jpg'
])
```

### `addVideo()` — video

Satu video:

```js
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 `|`:

```js
ai.addVideo('https://example.com/video.mp4|15')
```

Contoh beberapa video:

```js
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:

```js
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:

```js
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

```js
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:

```js
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

```js
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

```js
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:

```js
ai.addSuggestion('Lanjutkan')
```

`addSuggestion()` adalah nama yang direkomendasikan. Alias lama `addSuggest()` tetap tersedia untuk kompatibilitas.

### `setTitle()` dan `setFooter()`

```js
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`.

```js
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

```js
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:

```js
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

```js
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:

```js
await ai.send(m.chat, {
  forwarded: false,
  includesUnifiedResponse: true,
  includesSubmessages: true
})
```

### Format ringkas melalui `sendMessage()`

Selain builder, format ringkas berikut tetap dapat digunakan:

```js
await sock.sendMessage(m.chat, {
  richResponse: [
    { text: 'Contoh jawaban AI' },
    {
      code: 'console.log("JagProject")',
      language: 'javascript'
    }
  ]
})
```

Untuk satu blok kode:

```js
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, dan `communityFetchAllParticipating()` sekarang tersedia.
- `newsletterQuery()` / `newsletterWMexQuery()` serta `newsletterFetchAllParticipating()` kini diekspos.
- `useSingleFileAuthState()` dan `useMongoFileAuthState()` tersedia untuk kompatibilitas/migrasi; multi-file tetap direkomendasikan untuk penggunaan baru.

## 💾 In-memory store

```javascript
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.

<a id="katalog-bisnis"></a>

## 🛍️ Katalog bisnis

### 🛒 Katalog dan koleksi

```javascript
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

```javascript
const order = await sock.getOrderDetails(
  orderId,
  tokenBase64
)

console.log(order)
```

### 🏷️ Produk

```javascript
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

```javascript
const pino = require('pino')

const logger = pino({ level: 'debug' })
const sock = await makeWASocketLatest({
  auth: state,
  logger
})
```

Banner JagProject tidak ditampilkan secara default.

Windows CMD:

```bat
set BAILEYS_SHOW_BANNER=1
node index.js
```

Linux atau macOS:

```bash
BAILEYS_SHOW_BANNER=1 node index.js
```

<a id="pemecahan-masalah"></a>

## 🧰 Pemecahan masalah

### ❌ QR tidak tampil

Pastikan modul QR terpasang dan opsi terminal aktif:

```bash
npm install qrcode-terminal
```

```javascript
printQRInTerminal: 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.update` disimpan.
- Pastikan hanya satu proses menggunakan folder session yang sama.

### 🔁 Pesan gagal retry

Implementasikan `getMessage` agar pesan lama dapat diambil kembali:

```javascript
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:

```javascript
version: [2, 3000, 1044610476]
```

### 🖼️ Sticker atau gambar gagal diproses

Pasang `sharp`:

```bash
npm install sharp
```

Pastikan 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`:

```gitignore
session/
.env
*.tgz
node_modules/
```

## 📄 Lisensi

JagProject didistribusikan sesuai berkas [`LICENSE`](./LICENSE). Setiap dependensi tetap mengikuti lisensinya masing-masing.
