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 🙏

© 2026 – Pkg Stats / Ryan Hefner

chat-adapter-zaileys

v0.2.3

Published

WhatsApp adapter for Chat SDK powered by zaileys — rich media, native buttons/cards, real message history, polls, and scheduling

Readme

Quick start  •  Why this adapter  •  Install  •  What you can build  •  Configuration  •  Zaileys Docs

[!NOTE] This adapter wraps the full Zaileys client — everything in the Zaileys documentation (groups, communities, newsletters, privacy, broadcast, plugins) is reachable via adapter.client.


Quick start

import { Chat } from 'chat'
import { createMemoryState } from '@chat-adapter/state-memory'
import { createZaileysAdapter } from 'chat-adapter-zaileys'

const whatsapp = createZaileysAdapter({
  session: { sessionId: 'main' }, // QR prints to the terminal on first run
})

const bot = new Chat({
  userName: 'mybot',
  adapters: { whatsapp },
  state: createMemoryState(),
})

bot.onNewMention(async (thread, message) => {
  await thread.subscribe()
  await thread.post(`Hello, ${message.author.fullName}!`)
})

await bot.initialize()
await whatsapp.connect() // register handlers first, then connect

Scan the printed QR via WhatsApp → Linked Devices, done. Prefer a pairing code?

const whatsapp = createZaileysAdapter({
  session: { sessionId: 'main', authType: 'pairing', phoneNumber: '6281234567890' },
})

Why this adapter

| Capability | chat-adapter-zaileys | raw-Baileys adapters | | --- | --- | --- | | Message history (thread.fetchMessages) | ✅ real, backed by the Zaileys message store (memory/SQLite/Postgres/Redis/Convex) | ❌ empty arrays | | Cards & buttons (chat.onAction) | ✅ rendered as native WhatsApp buttons, clicks round-trip to onAction | ❌ fallback text only | | Poll votes | ✅ decrypted natively — works across restarts, zero bookkeeping | ⚠️ manual messageSecret persistence | | Scheduled messages (thread.schedule) | ✅ native, persisted through the Zaileys scheduler | ❌ | | Auth & reconnect | ✅ QR terminal / pairing code built in, auto-reconnect with backoff | ⚠️ wire onQR/reconnect yourself | | Rich sends | ✅ image/video/audio/document/sticker (incl. animated Lottie), voice notes, locations, polls, albums | ⚠️ partial | | Media in queue/debounce strategies | ✅ rehydrateAttachment re-downloads by message key | ❌ | | Raw escape hatch | message.raw.context = full Zaileys MessageContext (media helpers, reply/react, citation) | plain WAMessage |

Install

npm i chat-adapter-zaileys zaileys chat @chat-adapter/state-memory   # or: pnpm add  •  yarn add  •  bun add

Requires Node.js v20+. Peer dependencies are just chat and zaileys — no direct Baileys dependency.

For message history that survives restarts, give Zaileys a durable store (optional peer deps, install only what you use):

npm i better-sqlite3   # sqlite  •  redis (redis)  •  pg (postgres)  •  convex (convex)

What you can build

Bring your own Zaileys client

Full control — stores, plugins, citation, commands — then hand it to the adapter:

import { Client, SqliteMessageStore } from 'zaileys'
import { createZaileysAdapter } from 'chat-adapter-zaileys'

const client = new Client({
  sessionId: 'main',
  store: new SqliteMessageStore({ database: './wa.db' }), // durable fetchMessages history
})
const whatsapp = createZaileysAdapter({ client })

Cards → native WhatsApp buttons

bot.onNewMention(async (thread) => {
  await thread.post(
    <Card title="Deploy?">
      <Actions>
        <Button id="deploy" value="prod">Ship it</Button>
        <Button id="cancel">Cancel</Button>
      </Actions>
    </Card>
  )
})

bot.onAction('deploy', async (event) => {
  await event.thread?.post(`Deploying ${event.value}…`)
})

The zaileys payload, one call away

Every live message carries the full zaileys MessageContext — flags, lazy media, quoted decode, citation:

import { zaileysContext } from 'chat-adapter-zaileys'

bot.onSubscribedMessage(async (thread, message) => {
  const ctx = zaileysContext(message)
  if (!ctx) return

  ctx.isGroup / ctx.isForwarded / ctx.isViewOnce / ctx.isEphemeral // 20+ flags
  ctx.senderDevice                        // 'android' | 'ios' | 'web' | …
  const media = ctx.media                 // lazy — nothing downloads until you ask
  const quoted = await ctx.replied()      // full decoded quoted message
  await ctx.react('🔥')                   // zaileys shortcuts still work
})

WhatsApp-native extensions

Narrow any Thread/Channel with requireZaileysAdapter and go beyond the Chat SDK surface:

import { requireZaileysAdapter } from 'chat-adapter-zaileys'

bot.onSubscribedMessage(async (thread, message) => {
  const wa = requireZaileysAdapter(thread)

  await wa.markRead(thread.id)                     // blue ticks
  await wa.reply(message, 'Got it!')               // native quoted reply
  await wa.sendLocation({ threadId: thread.id, latitude: -6.2, longitude: 106.8 })
  await wa.sendSticker(thread.id, stickerBuffer)   // auto webp conversion, Lottie included
  await wa.sendVoiceNote(thread.id, oggBuffer)     // push-to-talk bubble
  await wa.sendContact(thread.id, vcardString)
  await wa.startRecording(thread.id)               // "recording audio…" indicator
  await wa.forwardMessage(thread.id, message.id, otherThreadId)
  await wa.pinMessage(thread.id, message.id)
  await wa.setPresence('available')
  await wa.setDisappearing(thread.id, 86_400)      // disappearing messages (0 disables)

  const participants = await wa.fetchGroupParticipants(thread.id)
  const admins = participants.filter((p) => p.isAdmin)
})

Polls — zero bookkeeping

const poll = await wa.sendPoll({ threadId: thread.id, question: 'Lunch?', options: ['A', 'B'] })

wa.onPollVote(poll.id, (vote) => {
  console.log(vote.voter.userName, 'picked', vote.selectedOptions)
})

Votes are decrypted natively by Zaileys — no messageSecret persistence, and it works across restarts for any poll this account sent.

native() — the everything hatch

One method unlocks the entire Zaileys message builder, pre-targeted at a thread:

await wa.native(thread.id).image(buffer, { viewOnce: true })
await wa.native(thread.id).album([{ type: 'image', src: img1 }, { type: 'image', src: img2 }])
await wa.native(thread.id).list({ title: 'Menu', buttonText: 'Open', sections })
await wa.native(thread.id).text('hey').mentionAll()

Scheduling

const scheduled = await thread.schedule('Reminder!', { postAt: new Date(Date.now() + 3600_000) })
await scheduled.cancel() // if you change your mind

Scheduled jobs persist in the Zaileys store and survive restarts (with a durable store adapter).

Configuration

| Option | Default | Description | | --- | --- | --- | | client / session | — | Existing Zaileys Client, or ClientOptions to create one | | adapterName | "zaileys" | Thread-ID prefix; set unique per account for multi-account | | userName | "zaileys-bot" | Bot display name | | forwardPollVotes | true | Also deliver poll votes to processMessage as text | | autoMarkRead | false | Mark chats read on inbound messages | | richMessages | false | Render { markdown }/{ ast } posts as Meta-AI-style rich bubbles (zaileys AIRich) | | slashCommands | false | Route prefixed messages (/cmd args) to chat.onSlashCommand | | logger | Chat SDK logger | Logger override |

Caveats

  • WhatsApp via Zaileys/Baileys is an unofficial API — protocol changes can break things, and accounts risk suspension under WhatsApp's ToS. Use responsibly.
  • fetchMessages history depth equals what your Zaileys store has seen. Use a durable store (SQLite/Postgres/Redis/Convex) for history that survives restarts.
  • No modals, no ephemeral messages — WhatsApp has no equivalent.

Documentation

Issues & feedback

Hit a problem or have a feature request? Open an issue.

License

Distributed under the MIT License. See LICENSE for details.