@kangwifiinc/baileys
v5.1.17
Published
WhatsApp library — ringan, multi-fitur, tahan lama. Client API di atas Baileys 7: QR/pairing, auto-reconnect, state-machine recovery, MessageBuilder, CommandSystem, Automation, Plugins, Media/Sticker helpers. 1 dependency.
Maintainers
Readme
@kangwifiinc/baileys
Library WhatsApp — ringan, multi-fitur, tahan lama.
Satu Client di atas Baileys 7:
login QR/pairing, auto-reconnect tahan-banting, MessageBuilder fluent,
CommandSystem (arg-parser ala CLI), Wizard percakapan multi-langkah, AI-rich
cards, A2UI widget, media/sticker, LID⇄JID, plugin system, dan util zero-dep.
- 1 dependency inti. Tools berat (
sharp,ffmpeg,qrcode-terminal) bersifat optional peer — dimuat hanya saat dipakai. - 95 unit test + tes integrasi koneksi nyata ke server WhatsApp (QR terbit, pairing code, kirim konten interaktif).
Instalasi
npm i @kangwifiinc/baileys
# opsional — pasang sesuai kebutuhan:
npm i sharp # gambar & sticker statis
npm i ffmpeg-static fluent-ffmpeg # sticker/video animasi
npm i qrcode-terminal # QR ASCII di terminalTanpa semua peer opsional di atas, Client tetap jalan penuh (QR dicetak sebagai
URL; fungsi yang butuh sharp/ffmpeg menolak dengan pesan jelas, bukan crash).
Quick start
import { Client, FileAuthStore } from '@kangwifiinc/baileys';
const client = new Client({
sessionId: 'bot',
auth: new FileAuthStore({ basePath: './auth' }),
ownerIds: ['[email protected]'],
autoRead: true, // tanda-tangan baca otomatis
autoRejectCall: true, // tolak panggilan masuk
});
client.on('connect', ({ me }) => console.log('online:', me.id));
client.on('text', msg => console.log('pesan:', msg.text));QR muncul di terminal → scan lewat WhatsApp → Perangkat Tertaut.
Pairing code (tanpa scan QR)
const client = new Client({ authType: 'pairing', phoneNumber: '6281234567890' });
client.on('pairing-code', ({ code }) => console.log('kode:', code));
// atau manual: await client.requestPairingCode('62812…'[, custom8]);MessageBuilder
await client.send(jid).text('Halo!').reply(message).mentions([user]).send();
await client.send(jid).image(buf, { caption: 'Lihat ini!' }).send();
await client.send(jid).voice(buf).send(); // voice note
await client.send(jid).document(buf, { fileName: 'a.pdf' }).send();
await client.send(jid).poll('Makan apa?', ['Nasi', 'Mie']).send();
await client.send(jid).location(-6.208, 106.845, { name: 'Jakarta' }).send();
await client.send(jid).viewOnce(); // dsb.
// thenable — tanpa .send() pun jalan
await client.send(jid).text('auto-send saat di-await');Builder lain: ButtonBuilder, ListBuilder, CarouselBuilder, PollBuilder,
RichTextBuilder, NativeFlowBuilder, AIRichBuilder, A2UI.
CommandSystem (+ arg-parser ala CLI)
import { CommandSystem } from '@kangwifiinc/baileys';
const cmd = new CommandSystem(client.socket, { prefix: ['!', '/'], ownerIds: ['…'] });
cmd.command('ping', ctx => ctx.reply('pong! 🏓'));
cmd.command('kick', { adminOnly: true, cooldown: 5000, aliases: ['remove'] }, async ctx => {
// ctx.args / ctx.positionals / ctx.flags tersedia + getter bertipe:
const days = ctx.getNum('days', 0); // kick --days=7 → 7
if (ctx.getBool('soft')) { /* … */ }
const target = ctx.mentions[0] ?? ctx.quoted?.sender;
if (target) await ctx.sock.groupParticipantsUpdate(ctx.jid, [target], 'remove');
});
cmd.use(async (ctx, next) => { console.log(ctx.sender, ctx.command); await next(); });
cmd.installHelp().attach(); // "!help" otomatisAtau langsung lewat client (ter-pasang saat connect):
client.command('ping', ctx => ctx.reply('pong'));
client.middleware(logger);Guards: adminOnly, ownerOnly, groupOnly, privateOnly, cooldown,
rateLimit, hidden. Ban/unban per JID.
Parser berdiri sendiri (bisa dipakai di luar command):
import { parseArgs, str, num, bool, arr } from '@kangwifiinc/baileys';
const { flags, positionals, rest } = parseArgs('add --name="John Doe" -vv file -- tail');
str(flags, 'name'); // 'John Doe'
bool(flags, 'v'); // true (cluster -vv)
arr(flags, 'tag'); // untuk --tag a --tag bWizard (percakapan multi-langkah)
client.wizard()
.flow('daftar', {
trigger: '!daftar',
timeoutMs: 5 * 60_000,
steps: [
{ ask: 'Nama?', key: 'nama', validate: v => v.length >= 2 ? { ok: true, value: v } : { ok: false, error: 'terlalu pendek' } },
{ ask: 'Umur?', key: 'umur', validate: v => /^\d+$/.test(v) ? { ok: true, value: +v } : { ok: false, error: 'harus angka' } },
],
onFinish: (data, ctx) => ctx.send(`Selesai ${data.nama}, umur ${data.umur}!`),
})
.attach();Per-chat state, validasi tiap jawaban, kata batal (batal/cancel/stop/keluar),
timeout idle otomatis.
AI-rich & kartu interaktif
import { AIRichBuilder, md } from '@kangwifiinc/baileys';
const card = new AIRichBuilder()
.title('🤖 wazo AI')
.answer(md.heading('Ringkasan') + '\n' + md.bullet('poin ' + md.bold('tebal')) + '\n\n' + md.code('ping()', 'py'))
.footer('via wazo')
.reply('Mantap', 'like')
.url('Buka', 'https://www.npmjs.com/package/@kangwifiinc/baileys');
await client.send(jid).raw(card.build()).send(); // AI-rich asli (richResponseMessage)
await card.sendAsText(client.socket, jid); // fallback: teks berformat WAAIRichBuildermenghasilkan AI-rich WhatsApp sejati (botForwardedMessage.richResponseMessage+messageContextInfo.botMetadata).NativeFlowBuilder/ListBuilder→ kartunative_flow(quick-reply / URL / copy-code / single_select).parseRichText&toWhatsAppMarkdown→ konversi markdown → markup WA.
Rendering kartu interaktif/AI bergantung pada jenis akun WhatsApp. Payload valid & diterima server, tapi WhatsApp terutama menampilkan widget dari akun Business/bot resmi; dari akun personal, banyak klien merender teks fallback. Pakai
.toText()/.sendAsText()untuk hasil yang selalu tampil.
A2UI (widget Bloks)
import { A2UI } from '@kangwifiinc/baileys';
const ui = new A2UI();
const title = ui.text('🧩 Widget', { variant: 'h1' });
const pic = ui.image('https://…/a.jpg');
const btn = ui.button(ui.text('Buka'), { action: { type: 'openUrl', url: 'https://…' } });
ui.root([ ui.column([title, pic, btn]) ]);
await client.sendA2UI(jid, ui, {
bodyText: 'Halo', footer: 'wazo',
buttons: [{ name: 'cta_url', params: { display_text: 'Info', url: 'https://…' } }],
});A2UI/Bloks memakai bloksWidget. Protobuf Baileys rilis saat ini belum punya
field tersebut → sendA2UI melempar error jelas (bukan diam-diam jadi teks),
kecuali { allowFallback: true }. Untuk mengaktifkan penuh, tambal protobuf:
npx @kangwifiinc/baileys # (opsional) atau:
node_modules/.bin/wazo-proto-updateupdateBaileysProto() (di-export) mengunduh WAProto yang memuat pesan Bloks,
memvalidasi (menolak file kecil / yang berisi API berbahaya), lalu menulis ke
node_modules/@whiskeysockets/baileys/WAProto. Opt-in, tak pernah otomatis.
Cek kemampuan protomu kapan pun:
import { baileysCapabilities } from '@kangwifiinc/baileys';
const caps = await baileysCapabilities();
// { nativeFlow, singleSelect, poll, messageSecret, bloksWidget, isAIGenerated, … }LID ⇄ JID
WhatsApp memigrasi dari JID nomor (…@s.whatsapp.net) ke "LID" (…@lid).
import { isLidJid, toLidJid, toPnJid, jidToLid, lidToJid, supportsLidMapping } from '@kangwifiinc/baileys';
isLidJid('62812@lid'); // true
toLidJid('62812-34'); // '6281234@lid'
supportsLidMapping(client.socket); // ada map LID di akun ini?
await jidToLid('[email protected]', client.socket); // '6281234@lid' (butuh map)
await lidToJid('6281234@lid', client.socket); // PN (butuh reverse map)Format murni selalu tersedia; pemetaan penuh butuh map LID yang sudah disinkron kontak akun.
Media & sticker (EXIF asli)
import { MediaHelper, StickerHelper, fetchBuffer } from '@kangwifiinc/baileys';
const img = await fetchBuffer('https://…/a.jpg'); // native fetch, tanpa axios
const sticker = await StickerHelper.fromImage(img.buffer, { pack: 'Pack', author: 'bot', categories: ['😀'] });
await client.send(jid).sticker(sticker).send();
StickerHelper.extractMetadata(sticker); // { pack: 'Pack', author: 'bot', … } — round-trip nyata
await StickerHelper.fromText('Halo', { pack: 'T' }); // teks → WebP
await StickerHelper.fromVideo(buf, { pack: 'A' }); // video → animasiMediaHelper: resizeImage, toWebP, toJPEG/PNG, compress, createThumbnail,
addWatermark, blur, grayscale, rotate, flip, getMetadata, formatSize.
Automation
import { AutoRead, AutoRejectCall, AutoPresence, AutoDelete, Broadcast, Scheduler } from '@kangwifiinc/baileys';
new AutoRead(client.socket, { excludeGroups: true }).start();
new AutoRejectCall(client.socket).start();
new Broadcast(client.socket, 800).sendText(jids, 'Halo!', { onProgress: (d, t) => console.log(`${d}/${t}`) });
new Scheduler(client.socket).schedule(jid, { text: 'meeting' }, 60 * 60_000);Plugin system
import { definePlugin, PluginLoader } from '@kangwifiinc/baileys';
export default definePlugin({ name: 'echo', version: '1.0.0', onMessage(msg, ctx) { /* … */ } });
await new PluginLoader(client.socket).loadFromDir('./plugins');Koneksi tahan-banting
const client = new Client({ sessionId: 'bot', reconnect: { initialDelayMs: 1000, maxDelayMs: 30000 } });
client.on('state-change', ({ prev, next }) => console.log(prev, '→', next));
client.on('reconnecting', ({ branch, action, delayMs }) => console.log(`recovery ${branch}: ${action} (${delayMs}ms)`));- Connection state machine — transisi legal saja, tiap perubahan jadi event.
- Multi-branch recovery —
immediate-retry→stream-reconnect→backoff→key-refresh→signal-prekey-fetch→full-reauth; creds dibersihkan otomatis saatshouldClearAuth(logged-out/forbidden/bad-session). - AuthGuard — laju pairing-code dibatasi agar tak kena-ban WhatsApp.
- Rich event decoders — reaksi, edit, hapus, poll-vote, button/list-select.
Performa & footprint
- Idle (sesi aktif, tanpa trafik): ~60–64 MB RSS — didominasi Baileys + baseline Node; kode wazo sendiri hanya beberapa MB. Stabil, tanpa kebocoran (terverifikasi flat 3 menit).
lightweight: true— store kecil,syncFullHistory:false,markOnlineOnConnect:false.MemoryMessageStore({ cap })— riwayat dibatasi (default 500), FIFO.- Saran VPS kecil:
node --max-old-space-size=64 bot.js.
Utilitas bawaan (zero-dep)
retry, sleep, debounce, throttle, PromiseQueue, RateLimiter,
IdempotencyCache, CircuitBreaker, LruCache, RecoveryPlan,
createReconnectStrategy, mapDisconnectReason/shouldReconnect/shouldClearAuth,
AuthGuard, normalizePhoneNumber/validateE164, helper JID (phoneNumberToJid,
normalizeUserJid, buildVcard, jidsEqual), MIME via magic-byte, dan deteksi
kapabilitas proto.
Pengembangan
npm run build # tsc → dist/ (CJS + deklarasi tipe)
npm test # 95 unit + media + sistem unik (offline)
npm run test:esm # smoke import ESM
npm run test:integration # koneksi nyata WhatsApp: QR + pairing (butuh internet)
npm run test:all # semuanyaBatasan jujur (dari sisi WhatsApp, bukan library)
- Kartu interaktif/AI/A2UI terkirim valid (server terima), tapi render di
klien terutama untuk pengirim Business/bot resmi; akun personal sering hanya
menampilkan teks. Solusi:
.toText()/.sendAsText(). isAIGeneratedbadge &bloksWidgetbutuh WAProto terbaru (di luar rilis@whiskeysockets/baileyssaat ini) — sediakanupdateBaileysProto()/bin.- LID→JID penuh butuh map kontak yang disinkron.
Lisensi
MIT
