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

sirobail

v2.3.0

Published

A WebSockets library for interacting with WhatsApp Web

Readme

SiroBail RC13

npm version npm downloads License Node.js ESM

Open-source WhatsApp automation library · No browser required. Built on WebSocket for speed, stability, and full multi-device support.

Installation · Documentation · Features · Telegram


What is sirobail?

sirobail is a powerful, open-source library for developers who need reliable WhatsApp automation without the overhead of a browser. Powered by WebSocket technology, it supports message management, group administration, interactive messages, and action buttons — all in a lightweight and modular package.

Actively maintained with continuous improvements to pairing stability, session management, and WhatsApp multi-device compatibility.

Perfect for:

  • Business bots & chat automation
  • Customer service systems
  • Broadcast & notification tools
  • E-commerce integrations

Features

| Feature | Description | |---|---| | Custom Pairing | Stable pairing with your own codes — no disconnection issues | | Interactive Messages | Buttons, menus, native flows, and more | | Rich Response | AI-style messages with code blocks, tables, LaTeX, and maps | | Business Messaging | Payment requests, product cards, and order messages | | Session Management | Automatic, efficient, and long-term stable | | Multi-Device Support | Fully compatible with WhatsApp's latest multi-device API | | Lightweight & Modular | Easy to integrate into any Node.js project | | Rich Documentation | Comprehensive guides and example code included | | Secure Auth | Improved authentication flow with fixed prior vulnerabilities |


Installation

npm install sirobail

Requires Node.js >= 20.0.0


Quick Start

import makeWASocket, { useMultiFileAuthState, DisconnectReason } from 'sirobail'

async function start() {
  const { state, saveCreds } = await useMultiFileAuthState('./auth_info')
  const sock = makeWASocket({ auth: state })

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

  sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
    if (connection === 'close') {
      const shouldReconnect =
        lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut
      if (shouldReconnect) setTimeout(start, 3000)
    } else if (connection === 'open') {
      console.log('✅ Connected')
    }
  })
}

start()

Pairing Code

const code = await sock.requestPairingCode('628xxxxxxxxx')
console.log(code) // e.g. A1B2C3D4

Must be exactly 8 characters.

const code = await sock.requestPairingCode('628xxxxxxxxx', 'MYBOT123')

SendMessage

await sock.sendMessage(jid, { text: 'Hello!' })
await sock.sendMessage(jid, {
  text: '@628xxx hello!',
  mentions: ['[email protected]']
})
await sock.sendMessage(jid, { text: 'This is a reply!' }, { quoted: m })
await sock.sendMessage(jid, {
  text: 'Edited message',
  edit: msg.key
})
await sock.sendMessage(jid, { delete: msg.key })
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Caption'
})
await sock.sendMessage(jid, {
  video: { url: 'https://example.com/video.mp4' },
  caption: 'Caption'
})
await sock.sendMessage(jid, {
  video: { url: 'https://example.com/video.mp4' },
  ptv: true
})
// Regular audio
await sock.sendMessage(jid, {
  audio: { url: 'https://example.com/audio.mp3' },
  mimetype: 'audio/mp4'
})

// Voice note (PTT)
await sock.sendMessage(jid, {
  audio: { url: 'https://example.com/audio.ogg' },
  mimetype: 'audio/ogg; codecs=opus',
  ptt: true
})
await sock.sendMessage(jid, {
  document: { url: 'https://example.com/file.pdf' },
  mimetype: 'application/pdf',
  fileName: 'document.pdf'
})
await sock.sendMessage(jid, {
  sticker: { url: 'https://example.com/sticker.webp' }
})
await sock.sendMessage(jid, {
  albumMessage: [
    { image: { url: 'https://example.com/1.jpg' }, caption: 'First' },
    { image: { url: 'https://example.com/2.jpg' }, caption: 'Second' }
  ]
}, { quoted: m })
await sock.sendMessage(jid, {
  react: { text: '👍', key: msg.key }
})

// Remove reaction
await sock.sendMessage(jid, {
  react: { text: '', key: msg.key }
})
await sock.sendMessage(jid, {
  poll: {
    name: 'Which do you prefer?',
    values: ['Option A', 'Option B', 'Option C'],
    selectableCount: 1
  }
})
await sock.sendMessage(jid, {
  pollResultMessage: {
    name: 'Poll Title',
    pollVotes: [
      { optionName: 'Option A', optionVoteCount: '42' },
      { optionName: 'Option B', optionVoteCount: '7' }
    ]
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  eventMessage: {
    isCanceled: false,
    name: 'Event Name',
    description: 'Description here',
    location: { degreesLatitude: -6.2, degreesLongitude: 106.8, name: 'Jakarta' },
    joinLink: 'https://call.whatsapp.com/video/example',
    startTime: '1763019000',
    endTime: '1763026200',
    extraGuestsAllowed: true
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  location: {
    degreesLatitude: -6.2088,
    degreesLongitude: 106.8456,
    name: 'Monas, Jakarta'
  }
})
const vcard =
  'BEGIN:VCARD\nVERSION:3.0\nFN:Contact Name\nTEL;type=CELL;type=VOICE;waid=628123456789:+62 812-3456-789\nEND:VCARD'

await sock.sendMessage(jid, {
  contacts: {
    displayName: 'Contact Name',
    contacts: [{ vcard }]
  }
})
await sock.sendMessage(jid, {
  pin: msg.key,
  type: 1 // 1 = pin, 2 = unpin
})
await sock.sendMessage(jid, {
  forward: msg,
  force: true
})
await sock.sendMessage(jid, {
  groupStatusMessage: { text: 'Hello World' }
})
await sock.sendMessage(groupJid, {
  groupStatus: { text: 'Announcement for the group!' }
})

Max 30 characters. Group only (@g.us).

await sock.sendMessage(groupJid, {
  groupLabel: { labelText: '⭐ VIP Member' }
}, { quoted: m })

Button & Interactive Messages

await sock.sendMessage(jid, {
  text: 'Choose one:',
  footer: 'sirobail',
  buttons: [
    { buttonId: 'btn1', buttonText: { displayText: '🔴 Option 1' }, type: 1 },
    { buttonId: 'btn2', buttonText: { displayText: '🟡 Option 2' }, type: 1 }
  ],
  headerType: 1
}, { quoted: m })
await sock.sendMessage(jid, {
  text: 'Pick a menu:',
  footer: 'sirobail',
  title: 'Bot Menu',
  buttonText: 'Open Menu',
  sections: [
    {
      title: 'Main Features',
      rows: [
        { title: '📊 Status', description: 'Check bot status', rowId: 'status' },
        { title: '🏓 Ping', description: 'Check response time', rowId: 'ping' }
      ]
    }
  ]
}, { quoted: m })
await sock.sendMessage(jid, {
  interactiveMessage: {
    header: 'Header',
    title: 'Title',
    footer: 'Footer',
    buttons: [
      {
        name: 'cta_copy',
        buttonParamsJson: JSON.stringify({
          display_text: 'Copy Code',
          id: '123456789',
          copy_code: 'SIROBAIL'
        })
      }
    ]
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  interactiveMessage: {
    header: 'Menu',
    title: 'Bot',
    footer: 'sirobail',
    image: { url: 'https://example.com/image.jpg' },
    nativeFlowMessage: {
      messageParamsJson: JSON.stringify({
        bottom_sheet: { in_thread_buttons_limit: 2, list_title: 'Menu', button_title: 'Open' }
      }),
      buttons: [
        {
          name: 'single_select',
          buttonParamsJson: JSON.stringify({
            title: 'Select',
            sections: [
              {
                title: 'Options',
                rows: [
                  { title: 'Ping', description: 'Check status', id: 'ping' },
                  { title: 'Info', description: 'Bot info', id: 'info' }
                ]
              }
            ]
          })
        },
        {
          name: 'cta_copy',
          buttonParamsJson: JSON.stringify({
            display_text: 'Copy', id: '123', copy_code: 'SIROBAIL'
          })
        }
      ]
    }
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  interactiveMessage: {
    header: 'Our Website',
    body: 'Visit our website for more info',
    footer: 'sirobail',
    nativeFlowMessage: {
      buttons: [
        {
          name: 'cta_url',
          buttonParamsJson: JSON.stringify({
            display_text: '🌐 Open Website',
            url: 'https://github.com/xyrosiro',
            merchant_url: 'https://github.com/xyrosiro'
          })
        }
      ]
    }
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  interactiveMessage: {
    header: 'Contact Us',
    body: 'Need help? Call our support',
    footer: 'sirobail',
    nativeFlowMessage: {
      buttons: [
        {
          name: 'cta_call',
          buttonParamsJson: JSON.stringify({
            display_text: '📞 Call Support',
            phone_number: '+628123456789'
          })
        }
      ]
    }
  }
}, { quoted: m })
sock.ev.on('messages.upsert', async ({ messages }) => {
  const m = messages[0]
  if (!m.message) return

  // Legacy button response
  if (m.message.buttonsResponseMessage) {
    const id = m.message.buttonsResponseMessage.selectedButtonId
    console.log('Button pressed:', id)
  }

  // List response
  if (m.message.listResponseMessage) {
    const id = m.message.listResponseMessage.singleSelectReply.selectedRowId
    console.log('List selected:', id)
  }

  // Interactive / native flow response
  if (m.message.interactiveResponseMessage) {
    const nativeFlow = m.message.interactiveResponseMessage.nativeFlowResponseMessage
    if (nativeFlow) {
      const params = JSON.parse(nativeFlow.paramsJson || '{}')
      console.log('Native flow selected:', params.id || params.select_id)
    }
  }
})

Rich Response

AI-style structured messages — text, code blocks, tables, LaTeX, maps, and images.

await sock.sendMessage(jid, {
  richResponse: {
    text: '**Hello!** This is a *rich message* from sirobail'
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Example JavaScript code:',
    code: `const greet = (name) => {
  console.log('Hello, ' + name)
}
greet('sirobail')`,
    language: 'javascript'
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Price list:',
    table: {
      rows: [
        ['Item', 'Price', 'Stock'],
        ['Item A', 'Rp 50.000', '10'],
        ['Item B', 'Rp 75.000', '5']
      ]
    }
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Math formula:',
    latex: ['E = mc^2', 'a^2 + b^2 = c^2']
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Our store location:',
    map: {
      latitude: -6.2088,
      longitude: 106.8456,
      zoom: 15,
      title: 'Sirobail Store',
      annotations: [
        { latitude: -6.2088, longitude: 106.8456, title: 'Main Store' }
      ]
    }
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Check this image:',
    imageUrl: 'https://example.com/image.jpg'
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    text: 'Product gallery:',
    imageUrls: [
      'https://example.com/img1.jpg',
      'https://example.com/img2.jpg',
      'https://example.com/img3.jpg'
    ]
  }
})
await sock.sendMessage(jid, {
  richResponse: {
    botJid: '867051314767696@bot',
    text: '## Bot Report\nCurrent status:',
    table: {
      rows: [
        ['Metric', 'Value'],
        ['Uptime', '99.9%'],
        ['Response', '42ms']
      ]
    },
    code: 'console.log("sirobail active")',
    language: 'javascript'
  }
})

Business Messaging

await sock.sendMessage(jid, {
  requestPaymentMessage: {
    amount: 50000,        // in units of 1000 (50000 = Rp 50)
    currency: 'IDR',
    expiry: Date.now() + 86400000, // expires in 24h
    from: '[email protected]',
    note: 'Payment for order #001'
  }
})
await sock.sendMessage(jid, {
  productMessage: {
    title: 'Product Name',
    description: 'Product description',
    thumbnail: { url: 'https://example.com/img.jpg' },
    productId: 'PROD001',
    retailerId: 'SKU-001',
    url: 'https://example.com/product',
    priceAmount1000: 50000,
    currencyCode: 'IDR',
    body: 'Body text',
    footer: 'Footer text'
  }
}, { quoted: m })
await sock.sendMessage(jid, {
  orderMessage: {
    orderTitle: 'Order #001',
    message: 'Thank you for your order',
    itemCount: 3,
    totalAmount1000: 150000,
    totalCurrencyCode: 'IDR'
  }
}, { quoted: m })
// Get catalog
const catalog = await sock.getCatalog({ jid: '[email protected]' })

// Create product
await sock.productCreate({
  name: 'New Product',
  retailerId: 'SKU-002',
  price: 75000,
  currency: 'IDR',
  isHidden: false
})

Group Management

const group = await sock.groupCreate(
  'Group Name',
  ['[email protected]', '[email protected]']
)
console.log('Group created:', group.id)
const metadata = await sock.groupMetadata('[email protected]')
console.log(metadata.subject)
console.log(metadata.participants)
await sock.groupParticipantsUpdate('[email protected]', ['[email protected]'], 'add')
await sock.groupParticipantsUpdate('[email protected]', ['[email protected]'], 'remove')
await sock.groupParticipantsUpdate('[email protected]', ['[email protected]'], 'promote')
await sock.groupParticipantsUpdate('[email protected]', ['[email protected]'], 'demote')
await sock.groupUpdateSubject('[email protected]', 'New Group Name')
await sock.groupUpdateDescription('[email protected]', 'New description')
await sock.groupSettingUpdate('[email protected]', 'announcement')     // only admins can chat
await sock.groupSettingUpdate('[email protected]', 'not_announcement') // everyone can chat
await sock.groupSettingUpdate('[email protected]', 'locked')           // only admins edit info
await sock.groupSettingUpdate('[email protected]', 'unlocked')         // everyone can edit info
const link = await sock.groupInviteCode('[email protected]')
console.log('https://chat.whatsapp.com/' + link)

await sock.groupRevokeInvite('[email protected]')
const groupId = await sock.groupAcceptInvite('INVITECODE')
await sock.groupLeave('[email protected]')

Newsletter / Channel

const id = await sock.newsletterId('https://whatsapp.com/channel/...')
const channel = await sock.newsletterCreate('Channel Name', 'Channel description')
console.log(channel.id)
await sock.newsletterFollow('120363xxx@newsletter')
await sock.newsletterUnfollow('120363xxx@newsletter')
await sock.newsletterMute('120363xxx@newsletter')
await sock.newsletterUnmute('120363xxx@newsletter')
await sock.newsletterUpdateName('120363xxx@newsletter', 'New Channel Name')
await sock.newsletterUpdateDescription('120363xxx@newsletter', 'New description')
await sock.newsletterUpdatePicture('120363xxx@newsletter', fs.readFileSync('./photo.jpg'))
await sock.newsletterRemovePicture('120363xxx@newsletter')
// 'ALL' | 'BASIC' | 'NONE'
await sock.newsletterReactionMode('120363xxx@newsletter', 'ALL')
const adminCount = await sock.newsletterAdminCount('120363xxx@newsletter')
await sock.newsletterChangeOwner('120363xxx@newsletter', '[email protected]')
await sock.newsletterDemote('120363xxx@newsletter', '[email protected]')
await sock.newsletterDelete('120363xxx@newsletter')
const result = await sock.checkWhatsApp('[email protected]')

Events

// Incoming messages
sock.ev.on('messages.upsert', ({ messages, type }) => { })

// Message status updates (sent, read, etc.)
sock.ev.on('messages.update', updates => { })

// Connection updates
sock.ev.on('connection.update', ({ connection, lastDisconnect, qr }) => { })

// Credentials update (must be saved)
sock.ev.on('creds.update', saveCreds)

// Group members changed
sock.ev.on('group-participants.update', ({ id, participants, action }) => { })

// Group metadata changed
sock.ev.on('groups.update', updates => { })

// Presence (typing, online, etc.)
sock.ev.on('presence.update', ({ id, presences }) => { })

Changelog

v2.3.0 (latest)

  • Changed: migrated the package to ESM ("type": "module") for compatibility with whatsapp-rust-bridge; require('sirobail') no longer works — use import (see Quick Start)
  • Changed: whatsapp-rust-bridge does not support CommonJS — this was the root cause forcing the CJS → ESM migration above
  • Changed: all 106 files in lib/ verified ESM-clean — no remaining require(), exports.*, or module.exports from the previous CommonJS base
  • Added makeInMemoryStore and makeCacheManagerStore for chat/contact/message/presence/label state, with JSON serialize/deserialize support
  • Added message-composer builder (tokenizeCode, generateTableContent, generateLatexContent, and related helpers) for lower-level control over rich sub-messages

v2.2.8

  • Added richResponse — AI-style messages with code blocks, tables, LaTeX, and maps
  • Added requestPaymentMessage, productMessage, orderMessage for business use cases
  • Added groupStatus (group story) and groupLabel (member labels)
  • Added 5 newsletter admin functions: newsletterUpdateName, newsletterUpdateDescription, newsletterUpdatePicture, newsletterRemovePicture, newsletterReactionMode
  • Improved Album message reliability

License

MIT © xyrosiro