Mulai

Panduan lengkap bikin bot WhatsApp dengan @mataram/wa — dari instalasi sampe pairing code.

1. Install

Butuh Node.js minimal versi 20 (udah dicek sama engines di package.json).

npm install @mataram/wa

Atau pake yarn:

yarn add @mataram/wa

2. Import & Setup Session

useMultiFileAuthState adalah fungsi yang nyimpen session ke folder di disk. Setiap kali ada perubahan di creds.update, lu harus panggil saveCreds biar data session (key Signal, token pairing, dll) tersimpan.

Parameter useMultiFileAuthState

ParameterTipeKeterangan
folderstringNama folder buat nyimpen auth. Contoh: 'auth'

Return Value

PropertyTipeKeterangan
stateAuthenticationStateBerisi creds (data login) & keys (Signal keys)
saveCreds() => Promise<void>Callback buat nyimpen creds setiap ada update
import makeWASocket, { useMultiFileAuthState, Browsers, DisconnectReason } from '@mataram/wa'

const { state, saveCreds } = await useMultiFileAuthState('auth')
sock.ev.on('creds.update', saveCreds)

Folder auth/ bakal berisi file-file session. Jangan pernah commit folder ini ke git. Isinya: creds.json, pre-key, session, sender-key, app-state-sync-key, dll.

3. Buat Koneksi

makeWASocket — Semua Config Options

OptionTipeDefaultKeterangan
authAuthenticationState(wajib)State auth dari useMultiFileAuthState
browser[string, string, string]macOS ChromeIdentitas browser. WhatsApp reject kalo nama browser aneh
waWebSocketUrlstring | URLwss://web.whatsapp.com/ws/chatURL WebSocket WA
connectTimeoutMsnumber20000Timeout koneksi (ms)
defaultQueryTimeoutMsnumber | undefined60000Timeout default buat query (ms)
keepAliveIntervalMsnumber30000Ping interval ke server (ms)
loggerILoggerpino childLogger pino-compatible
emitOwnEventsbooleantrueEmit event untuk aksi yang dilakukan socket sendiri
markOnlineOnConnectbooleantrueTandai online pas konek
syncFullHistorybooleantrueSinkronisasi history penuh
fireInitQueriesbooleantrueJalankan query inisialisasi otomatis
countryCodestring'US'Kode negara (USA -> US)
linkPreviewImageThumbnailWidthnumber192Lebar thumbnail link preview
generateHighQualityLinkPreviewbooleanfalseHasilkan link preview kualitas tinggi (upload thumbnail ke WA)
maxMsgRetryCountnumber5Maks percobaan ulang pesan gagal
enableRecentMessageCachebooleantrueCache pesan terbaru buat retry
getMessage(key) => Promise<proto.IMessage | undefined>async () => undefinedAmbil pesan dari database lu buat retry
cachedGroupMetadata(jid) => Promise<GroupMetadata | undefined>async () => undefinedCache metadata grup biar ga repeated request
shouldSyncHistoryMessage(msg) => booleanskip FULLFilter tipe history sync yang diproses
agentAgentundefinedProxy agent untuk koneksi WS (contoh: SocksProxyAgent)
fetchAgentAgentundefinedProxy agent untuk fetch request (upload/download media)

Browser yang Valid

WA cuma nerima browser tertentu. Jangan asal ngasih string random. Pake helper dari Browsers:

FungsiContohHasil
Browsers.macOS(name)Browsers.macOS('Chrome')['Chrome (Mac OS)', 'Chrome', '10.15.7']
Browsers.windows(name)Browsers.windows('Edge')['Edge (Windows)', 'Edge', '10.0']
Browsers.ubuntu(name)Browsers.ubuntu('Firefox')['Firefox (Ubuntu)', 'Firefox', '22.04']
Browsers.mataram(name)Browsers.mataram('App')['App (Mac OS)', 'App', '1.0.0']
Browsers.appropriate(name)Browsers.appropriate('Bot')Deteksi OS dari process.platform
const sock = makeWASocket({
  auth: state,
  browser: Browsers.macOS('Chrome'),
  emitOwnEvents: true,
  markOnlineOnConnect: true,
  syncFullHistory: true,
})

4. Tangani Event Koneksi

connection.update — Semua Property

PropertyTipeKeterangan
connection'open' | 'connecting' | 'close'Status koneksi saat ini
lastDisconnect{ error: Boom | Error, date: Date }Error terakhir yang nutup koneksi. Cek error.output.statusCode
isNewLoginbooleanApakah ini login baru (baru pairing)
qrstring | undefinedQR code string (Base64) — kalau pake QR pairing
receivedPendingNotificationsboolean | undefinedUdah nerima notifikasi offline apa belum
isOnlineboolean | undefinedApakah ditandai online di WA
reachoutTimeLockReachoutTimelockStateInfo pembatasan akun (kalo kena restrict)
sock.ev.on('connection.update', async ({ connection, lastDisconnect, qr }) => {
  if (qr) {
    console.log('Scan QR ini:', qr)
  }
  if (connection === 'open') {
    console.log('Nyambung!', sock.user?.id)
  }
  if (connection === 'close') {
    const code = lastDisconnect?.error?.output?.statusCode
    if (code === DisconnectReason.loggedOut) {
      console.log('Session expired. Hapus folder auth dan pairing ulang.')
    } else {
      setTimeout(start, 3000)
    }
  }
})

Status Koneksi — Flow

  1. connecting — Socket baru connect, handshake Noise Protocol
  2. connecting dengan receivedPendingNotifications: false — Nunggu notifikasi offline
  3. Server kirim CB:ib,,offline — tandanya semua notifikasi offline udah dikirim
  4. receivedPendingNotifications: true — Siap masuk ke sync phase
  5. open — Koneksi siap dipake. Event udah di-flush
  6. close — Koneksi putus. Cek lastDisconnect.error buat reconnect logic

creds.update — Kenapa Wajib di-save?

Creds berisi semua data krusial: key Signal (identity, pre-key, signed pre-key), pairing code, LID mapping, account sync state. Kalo ga disave, tiap restart harus pairing ulang. Makanya selalu pasang listener ini:

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

saveCreds adalah function dari useMultiFileAuthState yang nulis ulang file auth/creds.json. Ini bakal kepanggil otomatis pas pairing, pas ada update key, pas sync selesai, dll.

5. Pairing Code (Login)

requestPairingCode — Parameter

ParameterTipeKeterangan
phoneNumberstringNomor HP tujuan. Format: 6281234567890 (tanpa +, tanpa spasi)
customPairingCode (opsional)stringKode pairing kustom, harus PERSIS 8 karakter. Kalo dikasih, WA bakal nampilin kode ini.

Return Value

Balikannya string — kode pairing yang harus diketik di WhatsApp → Linked Devices → Pair a device.

if (!state.creds.registered) {
  const code = await sock.requestPairingCode('6281234567890')
  console.log('Kode pairing (8 digit):', code)
}

Pairing Code Kustom

Lu bisa tentuin sendiri kode pairingnya (wajib 8 karakter):

const code = await sock.requestPairingCode('6281234567890', 'MATARAM')

Catatan: requestPairingCode bakal ngeset creds.me dengan ID user dan emit creds.update. Setelah pairing berhasil, server bakal restart koneksi (stream error 515) — itu normal.

6. Konsep Penting

JID (Jabber ID / WhatsApp ID)

JID adalah alamat unik tiap entitas di WhatsApp. Formatnya:

TipeFormat JIDContoh
User (Phone Number)nomor@s.whatsapp.net6281234567890@s.whatsapp.net
User (LID)nomor@lid1234567890@lid
Grupid@g.us1234567890-123456@g.us
Newsletterid@newsletter1234567890@newsletter
Status Broadcaststatus@broadcaststatus@broadcast
PSA0@broadcast0@broadcast

Ada juga device suffix: 628xxx@s.whatsapp.net.0 (device 0 = primary). Fungsi helper yang disediain:

import { jidDecode, jidNormalizedUser, areJidsSameUser, isJidUser, isJidGroup } from '@mataram/wa'

jidDecode('6281234567890@s.whatsapp.net')
// { user: '6281234567890', server: 's.whatsapp.net', device: undefined, agent: undefined }

jidNormalizedUser('6281234567890@s.whatsapp.net.0')
// '6281234567890@s.whatsapp.net' — hapus device suffix

areJidsSameUser('6281234567890@s.whatsapp.net', '6281234567890@s.whatsapp.net.3')
// true — user yang sama, device beda

MessageKey — Struktur & Cara Dapetin

MessageKey adalah "alamat" unik sebuah pesan. Setiap pesan yang diterima atau dikirim punya ini:

PropertyTipeKeterangan
idstringID unik pesan (format: BAE5xxxx atau 3EBxxxx)
remoteJidstringJID chat tempat pesan berada
fromMebooleanApakah pesan dari kita sendiri
participantstring | undefinedDi grup: JID pengirim. Di chat pribadi: undefined

Cara dapetin:

sock.ev.on('messages.upsert', ({ messages }) => {
  const msg = messages[0]
  const key = msg.key
  console.log(key.id, key.remoteJid, key.fromMe, key.participant)
})

MessageKey dipake di sendMessage buat reply, reaction, edit, delete, pin, forward. Simpen aja key-nya pas nerima pesan, tinggal dipake kapan aja.

7. Contoh Lengkap

import makeWASocket, { useMultiFileAuthState, Browsers, DisconnectReason } from '@mataram/wa'

const start = async () => {
  const { state, saveCreds } = await useMultiFileAuthState('auth')

  const sock = makeWASocket({
    auth: state,
    browser: Browsers.macOS('Chrome'),
    getMessage: async key => undefined,
  })

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

  sock.ev.on('connection.update', async ({ connection, lastDisconnect, qr }) => {
    if (qr) console.log('QR:', qr)
    if (connection === 'open') console.log('Nyambung sebagai', sock.user?.id)
    if (connection === 'close') {
      const code = lastDisconnect?.error?.output?.statusCode
      const shouldReconnect = code !== DisconnectReason.loggedOut
      if (shouldReconnect) start()
    }
  })

  if (!state.creds.registered) {
    const code = await sock.requestPairingCode('6281234567890')
    console.log('Kode:', code)
  }

  sock.ev.on('messages.upsert', async ({ messages, type }) => {
    if (type !== 'notify') return
    const msg = messages[0]
    if (msg.key.fromMe) return
    console.log('Pesan dari', msg.key.remoteJid, msg.message?.conversation)
  })
}

start()
← Beranda Lanjut: Kirim Pesan →