npm package: @shinryoku/baileys
Description: WhatsApp Web API Library
Author: johannes
Latest version: 0.1.7

<div align="center">

<img src="https://capsule-render.vercel.app/api?type=waving&height=220&color=0:ff6b6b,40:f06595,100:845ef7&text=@shinryoku/baileys&fontAlignY=40&fontSize=44&fontColor=ffffff&desc=Stable%20WhatsApp%20Web%20API%20Fork%20for%20Production%20Bots&descAlignY=60&descSize=16" alt="Header Banner" />

<p>
  <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=700&size=18&duration=2500&pause=900&color=F8F9FA&center=true&vCenter=true&width=820&lines=Modern+Baileys+Fork;Optimized+Connection+Flow;Better+Stability+for+Long-Running+Bots;Ready+for+Production+Deployment" alt="Typing Animation" />
</p>

<p>
  <a href="https://saweria.co/shinryoku" target="_blank">
    <img src="https://img.shields.io/badge/Donate-Saweria-EA4335?style=for-the-badge&logo=buymeacoffee&logoColor=white" alt="Donate Saweria">
  </a>
  <a href="https://github.com/johannes2803" target="_blank">
    <img src="https://img.shields.io/badge/GitHub-johannes2803-181717?style=for-the-badge&logo=github&logoColor=white" alt="GitHub">
  </a>
</p>

<p>
  <img src="https://img.shields.io/npm/v/@shinryoku/baileys?style=flat-square" alt="NPM Version">
  <img src="https://img.shields.io/npm/dt/@shinryoku/baileys?style=flat-square" alt="Downloads">
  <img src="https://img.shields.io/github/license/johannes2803/marinkitagawa-baileys?style=flat-square" alt="License">
  <img src="https://img.shields.io/node/v/@shinryoku/baileys?style=flat-square" alt="Node Version">
</p>

<b>
  <a href="#install">Install</a> |
  <a href="#quick-start">Quick Start</a> |
  <a href="#dokumentasi-lengkap">Docs</a> |
  <a href="#dukungan">Donate</a>
</b>

</div>

---

## Keunggulan Inti

Dikembangkan untuk melengkapi Baileys dengan fokus pada stabilitas dan pengalaman penggunaan jangka panjang.

- Core stability patch pada beberapa alur pesan dan metadata.
- Reconnect flow lebih aman dan efisien.
- Kompatibel dengan pola penggunaan Baileys yang umum.
- Banner startup bisa dikustomisasi.

---

## Install

### Versi stabil

```bash
npm i @shinryoku/baileys
```

### Versi terbaru

```bash
npm i @shinryoku/baileys@latest
# atau
yarn add @shinryoku/baileys@latest
```

---

## Import

```js
const { default: makeWASocket } = require('@shinryoku/baileys')
```

```js
import makeWASocket from '@shinryoku/baileys'
```

---

## Quick Start

```js
const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('@shinryoku/baileys')
const { Boom } = require('@hapi/boom')

async function connectToWhatsApp() {
  const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')

  const sock = makeWASocket({
    auth: state,
    printQRInTerminal: true,
    browser: ['Marin Kitagawa Bot', 'Chrome', '1.0.0']
  })

  sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect } = update

    if (connection === 'close') {
      const shouldReconnect =
        (lastDisconnect?.error instanceof Boom)?.output?.statusCode !== DisconnectReason.loggedOut

      if (shouldReconnect) connectToWhatsApp()
    } else if (connection === 'open') {
      console.log('Connected to WhatsApp')
    }
  })

  sock.ev.on('messages.upsert', async ({ messages }) => {
    for (const m of messages) {
      if (!m.message) continue
      await sock.sendMessage(m.key.remoteJid, { text: 'Halo! Bot aktif.' })
    }
  })

  sock.ev.on('creds.update', saveCreds)
}

connectToWhatsApp()
```

---

## Dokumentasi Lengkap

### Metode Koneksi

#### 1. Koneksi via QR Code

```js
const { default: makeWASocket, Browsers } = require('@shinryoku/baileys')

const sock = makeWASocket({
  browser: Browsers.ubuntu('Marin Bot'),
  printQRInTerminal: true,
})
```

#### 2. Koneksi via Pairing Code

```js
const { default: makeWASocket } = require('@shinryoku/baileys')

const sock = makeWASocket({ printQRInTerminal: false })

if (!sock.authState.creds.registered) {
  const number = '628xxxxxxxxxx'
  const code = await sock.requestPairingCode(number)
  console.log('Kode Pairing:', code)
}
```

Catatan:
- `requestPairingCode(number)` akan membuat pairing code otomatis.
- Parameter kedua `pairKey` bersifat opsional, misal `requestPairingCode(number, 'ABCDE12345')`.
- Disarankan tidak memakai key statis permanen untuk menghindari reject auth.

#### 3. Full History Sync

```js
const { default: makeWASocket, Browsers } = require('@shinryoku/baileys')

const sock = makeWASocket({
  browser: Browsers.macOS('Desktop'),
  syncFullHistory: true,
})
```

---

### Konfigurasi Penting

#### Caching metadata grup

```js
const { default: makeWASocket } = require('@shinryoku/baileys')
const NodeCache = require('node-cache')

const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })

const sock = makeWASocket({
  cachedGroupMetadata: async (jid) => groupCache.get(jid),
})

sock.ev.on('groups.update', async ([event]) => {
  const metadata = await sock.groupMetadata(event.id)
  groupCache.set(event.id, metadata)
})
```

#### Data store (memori/file)

```js
const { default: makeWASocket, makeInMemoryStore } = require('@shinryoku/baileys')

const store = makeInMemoryStore({})
store.readFromFile('./marin_store.json')
setInterval(() => {
  store.writeToFile('./marin_store.json')
}, 10_000)

const sock = makeWASocket({})
store.bind(sock.ev)
```

---

### Panduan Pengiriman Pesan

#### Pesan teks, reply, mention

```js
await sock.sendMessage(jid, { text: 'halo dunia' })
await sock.sendMessage(jid, { text: 'halo dunia' }, { quoted: message })
await sock.sendMessage(jid, {
  text: '@62857xxxxxxxxx hai',
  mentions: ['62857xxxxxxxxx@s.whatsapp.net'],
})
```

#### Forward

```js
const msg = getMessageFromStore()
await sock.sendMessage(jid, { forward: msg })
```

#### Lokasi

```js
await sock.sendMessage(jid, {
  location: {
    degreesLatitude: 24.121231,
    degreesLongitude: 55.1121221,
  },
})
```

#### Kontak vCard

```js
const vcard =
  'BEGIN:VCARD\n' +
  'VERSION:3.0\n' +
  'FN:Marin Kitagawa\n' +
  'ORG:Waifu Club;\n' +
  'TEL;type=CELL;type=VOICE;waid=62857xxxxxxxxx:+62 857-xxxx-xxxx\n' +
  'END:VCARD'

await sock.sendMessage(jid, {
  contacts: {
    displayName: 'Marin',
    contacts: [{ vcard }],
  },
})
```

#### Reaksi dan pin

```js
await sock.sendMessage(jid, {
  react: { text: 'â¤ï¸', key: message.key },
})

await sock.sendMessage(jid, {
  pin: {
    type: 1,
    time: 86400,
    key: message.key,
  },
})
```

#### Polling

```js
await sock.sendMessage(jid, {
  poll: {
    name: 'Siapa Waifu Terbaik?',
    values: ['Marin Kitagawa', 'Rem', 'Mikasa'],
    selectableCount: 1,
  },
})
```

---

### Panduan Media

#### Kirim gambar

```js
await sock.sendMessage(jid, {
  image: { url: './Media/gambar.png' },
  caption: 'Ini gambar keren',
})
```

#### Kirim video/GIF

```js
await sock.sendMessage(jid, {
  video: { url: './Media/video.mp4' },
  caption: 'Nonton yuk',
  gifPlayback: false,
})

await sock.sendMessage(jid, {
  video: fs.readFileSync('Media/gif.mp4'),
  caption: 'GIF kocak',
  gifPlayback: true,
})
```

#### Kirim audio

```js
await sock.sendMessage(jid, {
  audio: { url: './Media/audio.mp3' },
  mimetype: 'audio/mp4',
})
```

#### View once

```js
await sock.sendMessage(jid, {
  image: { url: './Media/rahasia.jpg' },
  viewOnce: true,
  caption: 'Jangan disebar ya!',
})
```

#### Link preview

```js
await sock.sendMessage(jid, {
  text: 'Kunjungi https://npmjs.com/package/@shinryoku/baileys',
})
```

---

### Manipulasi Pesan

#### Hapus pesan

```js
const msg = await sock.sendMessage(jid, { text: 'pesan yang akan dihapus' })
await sock.sendMessage(jid, { delete: msg.key })
```

#### Edit pesan

```js
await sock.sendMessage(jid, {
  text: 'teks yang sudah diperbarui v2.0',
  edit: msg.key,
})
```

---

### Manipulasi Media

#### Download media

```js
const { downloadMediaMessage, getContentType } = require('@shinryoku/baileys')
const fs = require('fs')

sock.ev.on('messages.upsert', async ({ messages: [m] }) => {
  if (!m.message) return

  const messageType = getContentType(m.message)
  if (messageType === 'imageMessage') {
    const stream = await downloadMediaMessage(
      m,
      'stream',
      {},
      { reuploadRequest: sock.updateMediaMessage },
    )

    const writeStream = fs.createWriteStream('./downloaded_image.jpeg')
    stream.pipe(writeStream)
  }
})
```

#### Upload ulang media

```js
await sock.updateMediaMessage(msg)
```

---

### Manajemen Panggilan

#### Tolak panggilan otomatis

```js
sock.ev.on('call', async ({ from, id }) => {
  await sock.rejectCall(id, from)
  console.log(`Panggilan dari ${from} ditolak.`)
})
```

---

### Status dan Kehadiran

#### Tandai pesan dibaca

```js
await sock.readMessages([key])
```

#### Update presence

```js
await sock.sendPresenceUpdate('composing', jid)
await sock.sendPresenceUpdate('available', jid)
```

---

### Ubah Obrolan (chatModify)

> Hati-hati, salah konfigurasi bisa menyebabkan logout di beberapa perangkat.

#### Arsipkan/buka arsip

```js
await sock.chatModify({ archive: true, lastMessages: [lastMsg] }, jid)
await sock.chatModify({ archive: false, lastMessages: [lastMsg] }, jid)
```

#### Bisukan/buka bisukan

```js
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
await sock.chatModify({ mute: null }, jid)
```

#### Tandai dibaca/tidak dibaca

```js
await sock.chatModify({ markRead: false, lastMessages: [lastMsg] }, jid)
```

#### Hapus chat untuk diri sendiri

```js
await sock.chatModify(
  {
    delete: true,
    lastMessages: [{ key: lastMsg.key, messageTimestamp: lastMsg.messageTimestamp }],
  },
  jid,
)
```

#### Pin/unpin chat

```js
await sock.chatModify({ pin: true }, jid)
await sock.chatModify({ pin: false }, jid)
```

#### Star pesan

```js
await sock.chatModify(
  {
    star: {
      messages: [{ id: 'messageID', fromMe: true }],
      star: true,
    },
  },
  jid,
)
```

#### Ephemeral message

```js
await sock.sendMessage(jid, { disappearingMessagesInChat: 86400 })
await sock.sendMessage(jid, { text: 'Rahasia' }, { ephemeralExpiration: 86400 })
await sock.sendMessage(jid, { disappearingMessagesInChat: false })
```

---

### Query Pengguna dan Profil

#### Cek nomor terdaftar

```js
const [result] = await sock.onWhatsApp('62857xxxxxxxxx@s.whatsapp.net')
if (result.exists) console.log('Nomor terdaftar!')
```

#### Status dan foto profil

```js
const status = await sock.fetchStatus(jid)
const ppUrl = await sock.profilePictureUrl(jid)
const ppHD = await sock.profilePictureUrl(jid, 'image')
const biz = await sock.getBusinessProfile(jid)
```

#### Update profil sendiri

```js
await sock.updateProfileName('Nama Baru Bot')
await sock.updateProfileStatus('Sedang menggunakan @shinryoku/baileys')
await sock.updateProfilePicture('628xxx@s.whatsapp.net', { url: './new.jpg' })
await sock.removeProfilePicture('628xxx@s.whatsapp.net')
```

---

### Manajemen Grup

Pastikan bot adalah admin grup.

#### Buat grup

```js
const group = await sock.groupCreate('Grup Marin', ['6281@s.whatsapp.net', '6282@s.whatsapp.net'])
console.log('Grup ID:', group.gid)
```

#### Update member

```js
await sock.groupParticipantsUpdate(
  jid,
  ['628xxx@s.whatsapp.net'],
  'add',
)
```

#### Update info grup

```js
await sock.groupUpdateSubject(jid, 'Nama Baru')
await sock.groupUpdateDescription(jid, 'Deskripsi baru')
await sock.groupSettingUpdate(jid, 'announcement')
await sock.groupSettingUpdate(jid, 'not_announcement')
```

#### Kode undangan

```js
const code = await sock.groupInviteCode(jid)
await sock.groupRevokeInvite(jid)
await sock.groupAcceptInvite('CODEINVITE')
await sock.groupAcceptInviteV4(jid, groupInviteMessage)
```

#### Metadata dan join request

```js
const meta = await sock.groupMetadata(jid)
const allGroups = await sock.groupFetchAllParticipating()
const requests = await sock.groupRequestParticipantsList(jid)
await sock.groupRequestParticipantsUpdate(jid, ['628xxx@s.whatsapp.net'], 'approve')
```

#### Fitur grup lain

```js
await sock.groupToggleEphemeral(jid, 86400)
await sock.groupMemberAddMode(jid, 'admin_add')
await sock.groupMemberAddMode(jid, 'all_member_add')
```

---

### Privasi dan Block

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

const settings = await sock.fetchPrivacySettings(true)
await sock.updateLastSeenPrivacy('all')
await sock.updateOnlinePrivacy('all')
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('contacts')
await sock.updateReadReceiptsPrivacy('none')
await sock.updateGroupsAddPrivacy('contacts')
```

---

### Broadcast dan Status

```js
await sock.sendMessage('status@broadcast', {
  image: { url: './story.jpg' },
  caption: 'Halo status!',
  statusJidList: ['628xxx@s.whatsapp.net'],
  broadcast: true,
})
```

---

## Tips dan Praktik Terbaik

1. Gunakan database untuk auth/session di server production.
2. Hati-hati dengan `chatModify`.
3. Batasi rate pengiriman pesan.
4. Pairing code umumnya lebih praktis untuk deployment.

---

## Changelog

### 0.1.5 - 2026-02-15

- Perbaikan untuk kasus bot lama idle lalu kembali dipakai: pesan private yang replay lewat jalur berbeda (`append`/`notify`) kini terdeteksi sebagai duplikat.
- Deduplikasi fingerprint private message diperkuat dengan deteksi replay lintas jalur dan replay timestamp berdekatan.
- Mengurangi kasus response dobel setelah delay saat bot kembali aktif.

### 0.1.4 - 2026-02-14

- Perbaikan lanjutan duplikasi response di private chat saat event `notify` duplikat datang dengan `message ID` berbeda.
- Menambahkan deduplikasi berbasis fingerprint payload untuk pesan private masuk (`notify`) termasuk text, button/list/template/interactive response, serta caption media.
- Mencegah command diproses dua kali akibat duplikasi delivery PN/LID yang sebelumnya masih bisa memicu kirim button/media dobel.

### 0.1.3 - 2026-02-14

- Perbaikan respon bot ganda di chat pribadi yang terjadi karena event `messages.upsert` duplikat.
- Menambahkan deduplikasi `messages.upsert` berbasis identity key pesan dengan cache TTL singkat.
- Menormalkan JID private chat (`lid` dan `s.whatsapp.net`) pada dedupe key agar satu pesan tidak diproses dua kali.

---

## Dukungan

Project ini gratis dan open-source. Dukungan kamu berarti untuk pengembangan fitur berikutnya.

- Saweria: https://saweria.co/shinryoku
- GitHub: https://github.com/johannes2803

---

## Disclaimer

Project ini tidak berafiliasi dengan WhatsApp Inc. atau Meta Platforms, Inc.
Gunakan dengan bijak, jangan untuk spam atau aktivitas ilegal.

---

## Lisensi

Distributed under GPL-3.0. Lihat [LICENSE](LICENSE).