@kangwifi72/baileys
v1.4.9
Published
Advanced WhatsApp bot library. v1.4.9: Fix mobile flag for pairing code auth (401 fix).
Maintainers
Readme
@kangwifi72/baileys
Advanced WhatsApp bot library with multi-branch SKDM recovery, AI Rich messages, interactive buttons, media processing, TaskQueue, RateLimiter, and all the latest WhatsApp features built on @whiskeysockets/baileys.
Install
npm install @kangwifi72/baileys @whiskeysockets/baileysSemua dependencies bundel (ffmpeg, jimp, file-type, dll.) sudah termasuk otomatis saat install.
Quick Start
import pino from 'pino';
import { Client, createLogger } from '@kangwifi72/baileys';
const client = new Client({
sessionId: 'my-bot',
qrTerminal: true,
logger: createLogger({ level: 'info' }),
baileys: { logger: pino({ level: 'silent' }) },
});
client.on('qr', ({ qrString }) => {
console.log('Scan QR:', qrString);
});
client.on('connect', ({ me }) => {
console.log('Connected as:', me.id);
});
client.on('text', async (msg) => {
console.log(`[${msg.senderName}]: ${msg.text}`);
if (msg.text === '!ping') {
await msg.reply('Pong! 🏓');
}
});
client.on('disconnect', ({ reason, willReconnect }) => {
console.log(`Disconnected: ${reason}, reconnecting: ${willReconnect}`);
});Features
Multi-Branch SKDM Recovery
Unlike basic reconnect that only tries one path, this library implements a full multi-branch Session Key & Device Management (SKDM) recovery system. When a disconnect happens, it tries multiple recovery strategies in order:
| Disconnect Reason | Recovery Branches (in order) | |---|---| | Connection lost | Immediate retry → Stream reconnect → Backoff → Key refresh → Pre-key fetch → Full re-auth | | Rate limited (429) | Wait cooldown (5min) → Backoff retry | | Restart required (428) | Update app version → Key refresh → Full re-auth QR | | Logged out (401/403) | Key refresh → No recovery (fatal) | | Forbidden (511) | Immediate retry → Key refresh → No recovery | | Multi-device mismatch (409) | Key refresh → Full re-auth QR → No recovery | | Bad session | Key refresh → Pre-key fetch → Full re-auth QR | | Connection replaced (440) | Key refresh → Full re-auth QR |
Each branch has its own retry limit (default 3). Total attempts across all branches are capped (default 20).
const client = new Client({
recovery: {
enabled: true,
maxAttemptsPerBranch: 3,
maxTotalAttempts: 20,
initialDelayMs: 1000,
maxDelayMs: 60000,
rateLimitedDelayMs: 300000,
},
});Interactive Message Builder
Built-in interactive message builder for WhatsApp — supports Native Flow buttons, Carousel, AI Rich responses with code blocks, tables, images, videos, suggestions, and more.
Cara 1: Tools(sock) — Paling Mudah
Cukup 1 fungsi untuk akses semua fitur:
import { Tools } from '@kangwifi72/baileys';
// Setelah socket connect:
const t = Tools(sock);
console.log(t.version);Cara 2: client.tools — Via Client Class
import { Client } from '@kangwifi72/baileys';
const client = new Client({ sessionId: 'my-bot' });
await client.connect();
// Langsung pakai
await client.tools.button(jid).setTitle('Menu').send();Cara 3: Import Class Langsung
import { Button, ButtonV2, Carousel, AIRich, bindSocket, BuilderKit } from '@kangwifi72/baileys';
const btn = new Button(sock);
const airich = new AIRich(sock);Button (Native Flow)
Semua tipe button didukung: reply, URL, copy, call, location, address, reminder, selection/section, dan media header.
// Simple button menu
await t.button('[email protected]')
.setTitle('Menu Bot')
.setBody('Silakan pilih menu di bawah ini:')
.setFooter('Powered by @kangwifi72/baileys')
.addReply('Info', 'id:info')
.addReply('Bantuan', 'id:help')
.addUrl('Website', 'https://example.com')
.addCopy('Salin Kode', 'PROMO2024')
.addCall('Hubungi Kami', '+6281234567890')
.send();
// Button dengan gambar
await t.button('[email protected]')
.setImage('./thumbnail.jpg')
.setTitle('Promo Hari Ini!')
.setBody('Diskon 50% untuk semua produk')
.addReply('Beli Sekarang', 'id:buy')
.addUrl('Lihat Katalog', 'https://example.com/catalog')
.send();
// Button dengan selection/section
await t.button('[email protected]')
.setTitle('Pilih Produk')
.setBody('Kategori produk kami:')
.addSelection('Elektronik')
.makeRow('HP', 'Smartphone', 'iPhone 15 Pro Max', 'id:iphone')
.makeRow('LP', 'Laptop', 'MacBook Pro M3', 'id:macbook')
.addSelection('Aksesoris')
.makeRow('CHG', 'Charger', 'Charger 65W Fast Charging', 'id:charger')
.addReply('Lihat Semua', 'id:all')
.send();
// Button dengan dokumen
await t.button('[email protected]')
.setDocument('./brochure.pdf', { fileName: 'Katalog.pdf' })
.setTitle('Katalog Produk')
.setBody('Download katalog lengkap kami')
.addReply('Pesan', 'id:order')
.send();ButtonV2 (Multi-Row Buttons)
const card = new t.buttonV2('[email protected]');
card
.setTitle('Quick Actions')
.setBody('Pilih aksi:')
.setThumbnail('./image.jpg')
.addButton('Lihat Menu', 'id:menu')
.addButton('Hubungi Admin', 'id:admin');
await card.send();Carousel
const card1 = new t.buttonV2('[email protected]')
.setTitle('Produk 1')
.setBody('Deskripsi lengkap produk 1')
.setThumbnail('./product1.jpg')
.addButton('Pesan', 'id:order1');
const card2 = new t.buttonV2('[email protected]')
.setTitle('Produk 2')
.setBody('Deskripsi lengkap produk 2')
.setThumbnail('./product2.jpg')
.addButton('Pesan', 'id:order2');
await t.carousel('[email protected]')
.setTitle('Katalog Produk')
.setBody('Geser untuk melihat lebih banyak')
.addCard(card1)
.addCard(card2)
.send();AIRich (AI Rich Messages)
Pesan interaktif canggih dengan code blocks, tables, images, videos, suggestions, widgets, dan metadata:
// Sederhana
await t.aiRich('[email protected]')
.setTitle('AI Assistant')
.addText('Halo! Ada yang bisa saya bantu?')
.addSuggest(['Lihat Menu', 'Hubungi Admin', 'FAQ'])
.send();
// Code block
await t.aiRich('[email protected]')
.setTitle('Code Example')
.addText('Berikut contoh kode JavaScript:')
.addCode('javascript', `const greeting = "Hello World";
console.log(greeting);`)
.send();
// Tabel data
await t.aiRich('[email protected]')
.setTitle('Daftar Harga')
.addTable([
['Produk', 'Harga', 'Stok'],
['Nasi Goreng', 'Rp 15.000', 'Tersedia'],
['Mie Ayam', 'Rp 12.000', 'Tersedia'],
['Es Teh', 'Rp 5.000', 'Habis'],
])
.addSuggest('Pesan Sekarang')
.send();
// Dengan gambar
await t.aiRich('[email protected]')
.setTitle('Hasil Pencarian')
.addText('Berikut gambar yang kamu cari:')
.addImage('https://example.com/image.png', { width: 400 })
.addSuggest(['Cari Lagi', 'Gambar Lainnya'])
.send();Toolkit Utilities
// Extract inline entities dari teks
const result = t.toolkit.extractIE('Check https://example.com');
console.log(result.text);
console.log(result.ie);
// Fetch buffer dari URL
const buf = await t.toolkit.fetchBuffer('https://example.com/image.png');Tools() API Reference
| Property/Method | Type | Description |
|---|---|---|
| version | string | Versi builder |
| button(jid) | ButtonLike | Builder untuk Native Flow button |
| buttonV2(jid) | ButtonV2Like | Builder untuk multi-row button |
| carousel(jid) | CarouselLike | Builder untuk carousel message |
| aiRich(jid, opts?) | AIRichLike | Builder untuk AI Rich response |
| toolkit | BuilderKit | Utility functions |
| sendLinkPreview(...) | Promise | Kirim link preview message |
| bindToSocket(sock) | object | Bind builder ke socket object |
AI Rich Messages
Send AI-formatted text with WhatsApp's native rich text support:
await client.send(jid).text(
'*Bold*, _italic_, ~strikethrough~, and `code`',
{ rich: true }
);
await client.send(jid).text(
'```javascript\nconst x = 42;\nconsole.log(x);\n```',
{ rich: true, aiGenerated: true }
);Interactive Buttons (Fluent API)
await client.send(jid).buttons(
{ text: 'Choose an option:', footer: 'Powered by @kangwifi72/baileys' },
[
{ type: 'reply', id: 'opt_1', text: 'Option 1' },
{ type: 'url', text: 'Visit Website', url: 'https://example.com' },
{ type: 'copy', text: 'Copy Code', code: 'PROMO2024' },
]
);List Messages
await client.send(jid).list({
buttonText: 'Menu',
description: 'Select a category',
sections: [
{
title: 'Products',
rows: [
{ id: 'prod_1', title: 'Product A', description: 'Description of A' },
{ id: 'prod_2', title: 'Product B', description: 'Description of B' },
],
},
],
});Polls
await client.send(jid).poll('Best programming language?', [
'JavaScript', 'Python', 'TypeScript', 'Go',
], { multipleChoice: false });Media Messages
await client.send(jid).image('https://example.com/photo.jpg', { caption: 'Check this!' });
await client.send(jid).video('./video.mp4', { gifPlayback: true });
await client.send(jid).videoNote('./round.mp4');
await client.send(jid).audio('./voice.ogg');
await client.send(jid).document('./file.pdf', { fileName: 'Report.pdf', mimetype: 'application/pdf' });
await client.send(jid).sticker('./sticker.webp');
await client.send(jid).album([
{ type: 'image', src: './photo1.jpg', caption: 'First' },
{ type: 'video', src: './clip.mp4' },
]);Location & Contact
await client.send(jid).location(-6.2088, 106.8456, { name: 'Monas', address: 'Jakarta' });
await client.send(jid).contact('John Doe', '6281234567890', 'Company Inc');Events
client.on('connect', ({ me }) => {});
client.on('disconnect', ({ reason, willReconnect }) => {});
client.on('qr', ({ qrString }) => {});
client.on('pairing-code', ({ code }) => {});
client.on('message', (msg) => {});
client.on('text', (msg) => {});
client.on('image', (msg) => {});
client.on('video', (msg) => {});
client.on('reaction', (data) => {});
client.on('edit', (data) => {});
client.on('delete', (data) => {});
client.on('group-update', (data) => {});
client.on('group-join', (data) => {});
client.on('group-leave', (data) => {});
client.on('call-incoming', (data) => {});
client.on('presence', (data) => {});
client.on('newsletter', (data) => {});Message Context
client.on('text', async (msg) => {
console.log(msg.senderId);
console.log(msg.senderName);
console.log(msg.text);
console.log(msg.isGroup);
await msg.reply('Got it!');
await msg.react('👍');
});Command System
client
.command('ping', async (ctx) => { await ctx.reply('Pong! 🏓'); })
.command({ name: 'echo', aliases: ['say'] }, async (ctx) => {
await ctx.reply(ctx.args.join(' '));
});TaskQueue, RateLimiter & PRetry
TaskQueue (p-queue)
import { TaskQueue } from '@kangwifi72/baileys';
const queue = new TaskQueue({ concurrency: 3 });
await queue.add(() => fetchData('url1'));
queue.pause();
queue.resume();
queue.onIdle().then(() => console.log('Done!'));
console.log(queue.size, queue.pending, queue.isPaused);RateLimiter (bottleneck)
import { RateLimiter } from '@kangwifi72/baileys';
const limiter = new RateLimiter({ maxConcurrent: 1, rateLimit: 5, interval: 1000, minTime: 200 });
const result = await limiter.schedule(() => sendMessage(jid, text));
console.log(limiter.running, limiter.empty(), limiter.queued());PRetry (p-retry)
import { PRetry } from '@kangwifi72/baileys';
const result = await PRetry(() => fetchWithRetry(url), {
retries: 3,
onFailedAttempt: (err) => console.log(`Attempt ${err.attemptNumber} failed`),
});Logging
Yang Direkomendasikan (Bersih & Gampang Dibaca)
Penting: pisahkan logger client dengan logger internal Baileys. Internal Baileys nge-log hal-hal noisy seperti HistorySyncNotification, Uint8Array, PreKeyError, SessionError — ini normal tapi bikin console berantakan.
import pino from 'pino';
import { Client, createLogger } from '@kangwifi72/baileys';
const client = new Client({
sessionId: 'my-bot',
qrTerminal: true,
// Logger untuk client — output bersih & gampang dibaca
logger: createLogger({ level: 'info' }),
// Silent logger untuk internal Baileys — supaya console nggak berantakan
baileys: {
logger: pino({ level: 'silent' }),
},
});Sebelum (berantakan):
} error in handling message
[null]: { histNotification: HistorySyncNotification { fileLength: Long { low: 0, high: 0 },
initialHistBootstrapInlinePayload: Uint8Array(28487) [120, 1, 220, 188, ...28000 more items] } }
err: PreKeyError: Invalid PreKey ID
err: SessionError: No session record
err: Error: No session found to decrypt message
err: Error: failed to find key "AAAAACuC" to decode mutationSesudah (bersih):
[@kangwifi72/baileys] Connected as [email protected]
[@kangwifi72/baileys] Reconnect attempt 2/10
[@kangwifi72/baileys] Connected successfullyKenapa
PreKeyError/SessionErrormuncul? Itu normal — terjadi saat pertama kali sync history dari WhatsApp. Aman di-silent.
Level Log
import { createLogger } from '@kangwifi72/baileys';
// Tersedia: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
const logger = createLogger({ level: 'debug' }); // lebih detailCustom Logger (pino-pretty)
Kalau mau lebih fancy, pakai pino + pino-pretty (sudah termasuk di bundle):
import pino from 'pino';
import { Client } from '@kangwifi72/baileys';
const client = new Client({
sessionId: 'my-bot',
qrTerminal: true,
logger: pino({
level: 'info',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:dd/mm HH:MM:ss',
ignore: 'pid,hostname',
},
},
}),
baileys: { logger: pino({ level: 'silent' }) },
});Custom Logger (emoji minimal)
import { Client } from '@kangwifi72/baileys';
const client = new Client({
sessionId: 'my-bot',
baileys: { logger: { level: 'silent' } as any },
logger: {
level: 'info',
info: (...a) => console.log('✅', ...a),
warn: (...a) => console.log('⚠️ ', ...a),
error: (...a) => console.log('❌', ...a),
debug: (...a) => {},
fatal: (...a) => console.log('💀', ...a),
trace: (...a) => {},
child: () => client.logger,
},
});Pairing Code
const client = new Client({
sessionId: 'my-bot',
authType: 'pairing',
phoneNumber: '6281234567890',
});
client.on('pairing-code', ({ code }) => {
console.log('Pairing code:', code);
});Domain Modules
await client.group.create('My Group', ['[email protected]']);
await client.chat.archive(jid);
await client.profile.setName('My Bot');
await client.contact.check('6281234567890');
await client.privacy.set({ groupAdd: 'all', readReceipts: 'all' });
await client.newsletter.create('My Newsletter');ProtoHelper (Opt-in)
ProtoHelper dihapus dari default export sejak v1.3.0. Kalau kamu butuh akses proto-level (follow newsletter, sendViewOnceV2, votePoll, dll), akses via getProto():
// Hanya init saat dipanggil — tidak otomatis di-start
const proto = client.getProto();
await proto.followNewsletter(jid);
await proto.sendViewOnceV2(jid, content);
await proto.votePoll(creatorJid, messageId, [0, 2]);Auth Stores
import { FileAuthStore, MemoryAuthStore } from '@kangwifi72/baileys';
const client = new Client({ auth: new FileAuthStore('./auth/my-session') });Bundled Dependencies
Library ini menyertakan dependencies yang sering digunakan untuk WhatsApp bot development. Tidak perlu install satu-satu — semuanya sudah termasuk saat install @kangwifi72/baileys.
Media Processing
| Package | Versi | Deskripsi |
|---|---|---|
| @ffmpeg-installer/ffmpeg | ^1.1.0 | Binary FFmpeg untuk konversi audio/video (voice note, video processing, dll.) |
| @ffprobe-installer/ffprobe | ^2.1.2 | Binary FFprobe untuk membaca metadata media (durasi, resolusi, codec) |
| audio-decode | ^3.12.0 | Decode audio buffer ke raw PCM — berguna untuk voice note processing & waveform |
| jimp | ^1.6.1 | Image processing tanpa dependency native — resize, crop, watermark, sticker creation |
| node-webpmux | ^3.2.1 | Manipulasi file WebP (baca/tulis EXIF metadata pada sticker) |
| file-type | ^22.0.2 | Deteksi tipe file dari buffer (mimetype detection untuk upload media) |
Utilities
| Package | Versi | Deskripsi |
|---|---|---|
| async-mutex | ^0.5.0 | Mutex untuk operasi async — mencegah race condition pada concurrent message handling |
| lru-cache | ^11.5.2 | Cache LRU berperforma tinggi — caching session, profile, atau response |
| nanospinner | ^1.2.2 | Terminal spinner ringan — indikator loading di CLI bot |
| qrcode-terminal | ^0.12.0 | Render QR code langsung di terminal untuk pairing/QR login |
| valibot | ^1.4.2 | Schema validation ringan — validasi input pesan, config, dan command arguments |
Logging & Core
| Package | Versi | Deskripsi |
|---|---|---|
| pino | ^9.0.0 | Logger berperforma tinggi untuk production |
| pino-pretty | ^11.0.0 | Pretty-print formatter untuk development log |
| baileys | ^6.7.0 | Core WhatsApp Web API (kompatibel dengan @whiskeysockets/baileys) |
| bottleneck | ^2.19.5 | Rate limiter untuk mencegah ban dari WhatsApp |
| eventemitter3 | ^5.0.1 | Event emitter berperforma tinggi |
| p-queue | ^9.3.3 | Queue dengan concurrency control |
| p-retry | ^8.0.0 | Retry otomatis dengan exponential backoff |
Contoh Penggunaan
import { fileTypeFromBuffer } from 'file-type';
import { createSpinner } from 'nanospinner';
import * as qrTerminal from 'qrcode-terminal';
import { Mutex } from 'async-mutex';
import { LRUCache } from 'lru-cache';
import { v } from 'valibot';
// Deteksi tipe file sebelum kirim media
const buffer = await fetchBuffer('https://example.com/file');
const type = await fileTypeFromBuffer(buffer);
console.log(type?.mime); // 'image/png'
// Mutex untuk mencegah race condition
const mutex = new Mutex();
const release = await mutex.acquire();
try {
await processMessage(msg);
} finally {
release();
}
// LRU Cache untuk profile cache
const profileCache = new LRUCache<string, Profile>({ max: 500, ttl: 1000 * 60 * 5 });
// Validasi input dengan Valibot
const MessageSchema = v.object({
text: v.string(),
jid: v.pipe(v.string(), v.minLength(20)),
});
const result = v.safeParse(MessageSchema, { text: 'hello', jid: '[email protected]' });
// QR Terminal untuk CLI login
client.on('qr', ({ qrString }) => {
qrTerminal.generate(qrString, { small: true }, (qrcode) => {
console.log(qrcode);
});
});
// Spinner untuk CLI
const spinner = createSpinner('Connecting...').start();
client.on('connect', () => {
spinner.success({ text: 'Connected!' });
});Key Features
- Interactive message builder — Button, ButtonV2, Carousel, AIRich, Toolkit
Tools(sock)— satu fungsi untuk semua fitur builderclient.tools— akses builder langsung dari Client- Bundled media tools — FFmpeg, Jimp, WebP mux, file-type, audio decode
- Bundled utilities — async-mutex, LRU cache, nanospinner, qrcode-terminal, valibot
- Multi-branch SKDM recovery (10 branch types, exponential backoff with jitter)
getProto()— opt-in proto-level access (lazy init)TaskQueue(p-queue) — antrian tugas dengan concurrency controlRateLimiter(bottleneck) — rate limiting untuk aman dari banPRetry(p-retry) — retry otomatis dengan exponential backoff- All button types (Reply, URL, Copy, Call, Location, Address, Reminder, Selection)
- AI Rich message formatting (code blocks, tables, images, suggestions)
- Command system with middleware
- Domain modules (Group, Chat, Profile, Contact, Privacy, Newsletter, Community, Business)
- Broadcast & scheduling
- LID/PN mapping & username resolution
- v1.4.0-rc.1: LRU zero-timer cache injection (fixes #2090 memory leak), backpressure event buffering (fixes #1997), identity change debounce, MAC error detection, offline batch throttle, bounded message store, getMessage retry support
Requirements
- Node.js >= 18.0.0
@whiskeysockets/baileys>= 6.7.0
Changelog
v1.4.0-rc.1 (Performance Optimization Release)
- Memory fix: LRU Cache injection — mengganti semua
NodeCacheBaileys dengan LRU zero-timer. Memperbaiki memory leak ~0.1MB/pesan (issue #2090, proposal #2530) - Memory fix: Cache cleanup on disconnect — semua LRU cache di-destroy saat
disconnect(), menghentikan timer leak - Performance: Backpressure-controlled event buffering — max 200 pesan per flush cycle, mencegah memory spike di grup aktif (issue #1997)
- Performance: Offline batch throttling — skip processing pesan non-notify saat menerima offline notification batch
- Stability: Identity change handler — debounce 5 detik saat identity key berubah, auto-clear signal store (sesuai Baileys v7: "fix(retry): detect identity key changes and reset sessions")
- Stability: MAC error detection — track repeated MAC decryption failures, auto-clear user device cache setelah 10 error
- Stability:
getMessageinjection — menginjeksigetMessageke Baileys config untuk message retry support - Memory fix: Bounded default store — message store di-cap 5000 pesan dengan LRU eviction
- Note:
lru-cachebundled dependency tetap tersedia untuk user-side caching
v1.4.0
- Performance: Event buffering — messages di-batch via
queueMicrotasksebelum emit (inspired by Baileys v7 EventBuffer) - Performance: Decrypt-error debouncing — dedup
decrypt-errorevent dalam window 5 detik, auto-prune cache - Performance: Reaction deduplication — batch reaction events, dedup by key
- Performance: Optimized Baileys defaults —
keepAliveIntervalMs: 25000,connectTimeoutMs: 20000 - Dependency:
baileysminimum bumped ke^6.7.8
v1.3.0
- Breaking:
ProtoHelperdihapus dari top-level export - Breaking:
client.protodihapus — gunakanclient.getProto()sebagai pengganti (lazy init, hanya aktif saat dipanggil) - Hapus keyword
protodanprotobufdari package metadata
v1.2.1
- Critical fix: state machine crash
Invalid transition: connected → reconnecting— bot sekarang bisa reconnect otomatis setelah WebSocket putus
v1.2.0
- Bug fix: typeMap missing event types —
button_click,list_select,contact,locationsekarang ke-emit sebagai event - Bug fix: text extraction handle proto fields baru (
buttonsResponseMessage,listResponseMessage,templateButtonReplyMessage,nativeFlowResponseMessage,ephemeralMessage) - Bug fix: unwrap
ephemeralMessagewrapper sebelum proses pesan - Bug fix:
senderLidsekarang otomatis terisi dari@lidparticipant - New:
client.sockgetter — akses langsung ke raw Baileys socket - New:
decrypt-errorevent — listen untuk track pesan yang gagal di-decrypt
v1.1.3
- Logging: dokumentasi cara bersihin console dari noise internal Baileys (pino silent)
v1.1.2
- Logging: tambahkan section dokumentasi logging yang gampang dibaca
v1.1.1
- Fix: dist files missing di v1.1.0
v1.1.0
- Menambahkan 12 dependencies baru sebagai bundled packages:
@ffmpeg-installer/ffmpeg,@ffprobe-installer/ffprobe— media processingaudio-decode,jimp,node-webpmux,file-type— media utilitiesasync-mutex,lru-cache,nanospinner,qrcode-terminal,valibot— utilitiesbaileys— core compatibility
- Update dokumentasi dengan daftar lengkap dependencies dan contoh penggunaan
License
MIT
