npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

kezabaileys

v4.0.0

Published

A WebSockets

Readme

kezbail


Catatan Penting

Ini README sementara. Panduan lengkap akan dirilis ketika siap.

Disclaimer

Proyek ini tidak berafiliasi, diasosiasikan, disahkan, didukung, atau terhubung secara resmi dengan WhatsApp maupun anak perusahaannya.
"WhatsApp" serta nama, merek, dan logo terkait adalah merek dagang milik pemiliknya masing‑masing.

Pengelola kezbail tidak menganjurkan penggunaan yang melanggar Ketentuan Layanan WhatsApp. Gunakan secara bertanggung jawab.
Segala bentuk spam, stalkerware, atau pengiriman pesan massal/otomatis tidak disarankan.
Gunakan atas risiko Anda sendiri.


Ringkasan

  • Terhubung langsung ke WhatsApp Web menggunakan WebSocket (tanpa Selenium/Chromium).
  • Mendukung multi-device & Web.
  • Hemat RAM karena tidak memerlukan browser headless.
  • Dapat digunakan untuk membuat bot, integrasi internal, atau tooling otomasi.

Peringatan Perubahan internal WhatsApp dapat menyebabkan API tidak stabil sewaktu‑waktu. Lakukan monitoring & update rutin.


Instalasi

Gunakan versi stabil (contoh via yarn):

yarn add kezbail
# atau
npm i kezbail

Impor pada kode Anda:

import makeWASocket from 'kezbail'

Catatan
Jika Anda mengembangkan dari source lokal, sesuaikan langkah instalasi sesuai workflow Anda (monorepo, pnpm workspaces, dll.).


Indeks


Menghubungkan Akun

WhatsApp multi-device memungkinkan kezbail diautentikasi sebagai klien kedua dengan memindai QR Code atau menggunakan Pairing Code dari ponsel Anda.

Tip
Lihat Intellisense/TypeScript types untuk semua opsi konfigurasi socket yang didukung.

QR Code

import makeWASocket, { Browsers } from 'kezbail'

const sock = makeWASocket({
  browser: Browsers.ubuntu('My App'),
  printQRInTerminal: true
})

Setelah terhubung, QR akan tercetak di terminal. Pindai dengan WhatsApp di ponsel Anda.

Pairing Code

Info
Pairing Code adalah cara login WhatsApp Web tanpa memindai QR. Hanya untuk satu perangkat pada satu waktu.
Nomor telepon harus hanya digit (tanpa +, (), atau -) dan sertakan kode negara.

import makeWASocket from 'kezbail'

const sock = makeWASocket({ printQRInTerminal: false })

if (!sock.authState.creds.registered) {
  const number = '6281234567890'
  const code = await sock.requestPairingCode(number)
  console.log(code)
}

Menerima Riwayat Penuh

import makeWASocket, { Browsers } from 'kezbail'

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

Catatan Penting tentang Konfigurasi Socket

Cache Metadata Grup (Disarankan)

import NodeCache from 'node-cache'
import makeWASocket from 'kezbail'

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

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

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

sock.ev.on('group-participants.update', async (ev) => {
  const meta = await sock.groupMetadata(ev.id)
  groupCache.set(ev.id, meta)
})

Perbaiki Retry & Dekripsi Poll

const sock = makeWASocket({
  getMessage: async (key) => await getMessageFromStore(key)
})

Notifikasi di Aplikasi WhatsApp

const sock = makeWASocket({
  markOnlineOnConnect: false
})

Menyimpan & Memulihkan Sesi

import makeWASocket, { useMultiFileAuthState } from 'kezbail'

const { state, saveCreds } = await useMultiFileAuthState('auth_info_kezbail')
const sock = makeWASocket({ auth: state })

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

Penting
Simpan perubahan authState.keys setiap ada pembaruan agar pengiriman pesan tetap berjalan.


Menangani Event

const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
  console.log('got messages', messages)
})

Contoh Awal

import makeWASocket, { DisconnectReason, useMultiFileAuthState } from 'kezbail'
import { Boom } from '@hapi/boom'

async function connect () {
  const { state, saveCreds } = await useMultiFileAuthState('auth_info_kezbail')
  const sock = makeWASocket({ auth: state, printQRInTerminal: true })

  sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect } = update
    if (connection === 'close') {
      const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
      if (shouldReconnect) connect()
    } else if (connection === 'open') {
      console.log('opened connection')
    }
  })

  sock.ev.on('messages.upsert', async (event) => {
    for (const m of event.messages) {
      await sock.sendMessage(m.key.remoteJid!, { text: 'Hello World' })
    }
  })

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

connect()

Dekripsi Poll

sock.ev.on('messages.update', async (event) => {
  for (const { key, update } of event) {
    if (update.pollUpdates) {
      const pollCreation = await getMessage(key)
      if (pollCreation) {
        console.log('poll aggregation:', getAggregateVotesInPollMessage({
          message: pollCreation,
          pollUpdates: update.pollUpdates
        }))
      }
    }
  }
})

Ringkasan Event Saat Pertama Connect

  1. connection.update meminta restart socket di awal.
  2. Riwayat akan masuk melalui event messaging.history-set.

Membuat Data Store

Contoh in-memory store sederhana:

import makeWASocket, { makeInMemoryStore } from 'kezbail'

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

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

sock.ev.on('chats.upsert', () => {
  console.log('chats', store.chats.all())
})

sock.ev.on('contacts.upsert', () => {
  console.log('contacts', Object.values(store.contacts))
})

Format WhatsApp ID


Fungsi Utilitas

Beberapa utilitas umum:

  • getContentType(message)
  • getDevice(message)
  • makeCacheableSignalKeyStore(store)
  • downloadContentFromMessage(message, type)

Mengirim Pesan

API dasar:

const jid: string
const content: AnyMessageContent
const options: MiscMessageGenerationOptions

await sock.sendMessage(jid, content, options)

Non-Media

// teks
await sock.sendMessage(jid, { text: 'hello world' })

// quote
await sock.sendMessage(jid, { text: 'reply' }, { quoted: message })

// mention
await sock.sendMessage(jid, {
  text: '@6281234567890',
  mentions: ['[email protected]']
})

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

// lokasi
await sock.sendMessage(jid, { location: { degreesLatitude: -6.2, degreesLongitude: 106.8 } })

// kontak
const vcard = `BEGIN:VCARD
VERSION:3.0
FN:John Doe
ORG:My Org
TEL;type=CELL;type=VOICE;waid=6281234567890:+62 812-3456-7890
END:VCARD`
await sock.sendMessage(jid, { contacts: { displayName: 'John', contacts: [{ vcard }] } })

// reaksi
await sock.sendMessage(jid, { react: { text: '💖', key: message.key } })

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

// poll
await sock.sendMessage(jid, { poll: { name: 'Pilih satu', values: ['A', 'B'], selectableCount: 1 } })

Preview Tautan

await sock.sendMessage(jid, {
  text: 'Cek ini: https://example.com/article'
})

Media (GIF/Video/Audio/Gambar/ViewOnce)

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

// video
await sock.sendMessage(jid, {
  video: { url: './Media/video.mp4' },
  caption: 'hai',
  ptv: false
})

// audio
await sock.sendMessage(jid, {
  audio: { url: './Media/voice.ogg' },
  mimetype: 'audio/ogg; codecs=opus'
})

// gambar
await sock.sendMessage(jid, {
  image: { url: './Media/photo.png' },
  caption: 'hai'
})

// view once
await sock.sendMessage(jid, {
  image: { url: './Media/photo.png' },
  viewOnce: true,
  caption: 'sekali lihat'
})

Modifikasi Pesan

// hapus untuk semua
const sent = await sock.sendMessage(jid, { text: 'hapus aku' })
await sock.sendMessage(jid, { delete: sent.key })

// edit
await sock.sendMessage(jid, { text: 'teks baru', edit: sent.key })

Manipulasi Media

  • Thumbnail untuk gambar/stiker dapat dibuat otomatis jika Anda menambahkan dependensi sharp atau jimp.
  • Thumbnail video memerlukan ffmpeg terpasang.
  • Re-upload media lama:
await sock.updateMediaMessage(msg)

Menolak Panggilan

await sock.rejectCall(callId, callFrom)

Status Chat (Read/Preset)

Membaca Pesan

const key: WAMessageKey
await sock.readMessages([key])

Update Presence

await sock.sendPresenceUpdate('available', jid)
// atau offline untuk menerima push notifikasi
await sock.sendPresenceUpdate('unavailable')

Modifikasi Chat

  • Arsipkan
const last = await getLastMessageInChat(jid)
await sock.chatModify({ archive: true, lastMessages: [last] }, jid)
  • Mute/Unmute
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
await sock.chatModify({ mute: null }, jid)
  • Tandai baca/tidak dibaca
const last = await getLastMessageInChat(jid)
await sock.chatModify({ markRead: false, lastMessages: [last] }, jid)
  • Hapus untuk diri sendiri
await sock.chatModify({
  clear: { messages: [{ id: 'ID', fromMe: true, timestamp: '1654823909' }] }
}, jid)
  • Hapus chat
const last = await getLastMessageInChat(jid)
await sock.chatModify({ delete: true, lastMessages: [{ key: last.key, messageTimestamp: last.messageTimestamp }] }, jid)
  • Pin/Unpin chat
await sock.chatModify({ pin: true }, jid)
  • Bintang pesan
await sock.chatModify({
  star: { messages: [{ id: 'messageID', fromMe: true }], star: true }
}, jid)
  • Disappearing messages (detik): 0, 86400 (24h), 604800 (7d), 7776000 (90d)
await sock.sendMessage(jid, { disappearingMessagesInChat: 604800 })
await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: 604800 })
await sock.sendMessage(jid, { disappearingMessagesInChat: false })

Query Pengguna

// cek nomor
const [res] = await sock.onWhatsApp(jid)
if (res?.exists) console.log(`${jid} terdaftar sebagai ${res.jid}`)

// histori chat (perlu pesan tertua)
const oldest = await getOldestMessageInChat(jid)
await sock.fetchMessageHistory(50, oldest.key, oldest.messageTimestamp)

// status
const status = await sock.fetchStatus(jid)
console.log('status:', status)

// foto profil (low/high-res)
const pp = await sock.profilePictureUrl(jid)         // low
const ppHigh = await sock.profilePictureUrl(jid, 'image') // high

Ubah Profil

await sock.updateProfileStatus('Hello World!')
await sock.updateProfileName('Nama Saya')
await sock.updateProfilePicture(jid, { url: './new-pp.jpeg' })
await sock.removeProfilePicture(jid)

Grup

Untuk memodifikasi properti grup Anda harus menjadi admin.

// buat grup
const group = await sock.groupCreate('Grup Keren', ['[email protected]'])
await sock.sendMessage(group.id, { text: 'halo semua' })

// tambah/hapus/promote/demote
await sock.groupParticipantsUpdate(jid, ['[email protected]'], 'add')

// ganti subjek & deskripsi
await sock.groupUpdateSubject(jid, 'Subjek Baru')
await sock.groupUpdateDescription(jid, 'Deskripsi Baru')

// pengaturan
await sock.groupSettingUpdate(jid, 'announcement')     // hanya admin yang boleh kirim
await sock.groupSettingUpdate(jid, 'not_announcement') // semua boleh kirim
await sock.groupSettingUpdate(jid, 'locked')           // hanya admin boleh ubah info
await sock.groupSettingUpdate(jid, 'unlocked')         // semua boleh ubah info

// keluar
await sock.groupLeave(jid)

// undangan
const code = await sock.groupInviteCode(jid)
const info = await sock.groupGetInviteInfo(code)
const joined = await sock.groupAcceptInvite(code)
await sock.groupRevokeInvite(jid)

// metadata & tools lain
const meta = await sock.groupMetadata(jid)
const all = await sock.groupFetchAllParticipating()
await sock.groupToggleEphemeral(jid, 86400)
await sock.groupMemberAddMode(jid, 'all_member_add') // atau 'admin_add'

// permintaan join
const reqs = await sock.groupRequestParticipantsList(jid)
await sock.groupRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')

Privasi

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

const privacy = await sock.fetchPrivacySettings(true)
const blocked = await sock.fetchBlocklist()

await sock.updateLastSeenPrivacy('all')      // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateOnlinePrivacy('match_last_seen')
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('all')
await sock.updateReadReceiptsPrivacy('all')  // 'none'
await sock.updateGroupsAddPrivacy('contacts')
await sock.updateDefaultDisappearingMode(86400)

Broadcast & Stories

await sock.sendMessage(
  jid,
  { image: { url: './Media/photo.png' }, caption: 'status baru' },
  { backgroundColor: '#000000', font: 0, statusJidList: [], broadcast: true }
)

ID broadcast berbentuk 12345678@broadcast.


Menulis Fungsionalitas Kustom

Debug Log

const sock = makeWASocket({
  logger: P({ level: 'debug' })
})

Callback untuk WebSocket Event

// untuk node tertentu
sock.ws.on('CB:edge_routing', (node: BinaryNode) => {})
sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => {})
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => {})

Tip: Pelajari libsignal & noise protocol untuk memahami cara kerja protokol.


Lisensi

Copyright (c) 2025 kezbail contributors

Dilisensikan di bawah MIT License:

Izin diberikan secara gratis kepada siapa pun untuk mendapatkan salinan perangkat lunak ini dan dokumentasinya (“Perangkat Lunak”), untuk memperlakukan Perangkat Lunak tanpa batas, termasuk tanpa batasan hak untuk menggunakan, menyalin, memodifikasi, menggabungkan, memublikasikan, mendistribusikan, mensublisensikan, dan/atau menjual salinan Perangkat Lunak, dan mengizinkan orang yang diberikan Perangkat Lunak melakukannya, dengan ketentuan berikut:

Pernyataan hak cipta di atas dan pernyataan izin ini harus disertakan dalam semua salinan atau bagian substansial dari Perangkat Lunak.

PERANGKAT LUNAK INI DIBERIKAN “SEBAGAIMANA ADANYA”, TANPA JAMINAN APA PUN, TERSURAT MAUPUN TERSIRAT, TERMASUK NAMUN TIDAK TERBATAS PADA JAMINAN KELAYAKAN DIPERDAGANGKAN, KECOCOKAN UNTUK TUJUAN TERTENTU, DAN BEBAS PELANGGARAN. DALAM KEADAAN APA PUN, PENULIS ATAU PEMEGANG HAK CIPTA TIDAK BERTANGGUNG JAWAB ATAS KLAIM, KERUSAKAN, ATAU KEWAJIBAN LAIN, BAIK DALAM KONTRAK, PERBUATAN MELAWAN HUKUM, ATAU LAINNYA, YANG TIMBUL DARI, DARI, ATAU SEHUBUNGAN DENGAN PERANGKAT LUNAK ATAU PENGGUNAAN ATAU TRANSAKSI LAIN DALAM PERANGKAT LUNAK.