sirobail
v2.3.0
Published
A WebSockets library for interacting with WhatsApp Web
Readme
SiroBail RC13
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 sirobailRequires 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. A1B2C3D4Must 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 infoconst 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 withwhatsapp-rust-bridge;require('sirobail')no longer works — useimport(see Quick Start) - Changed:
whatsapp-rust-bridgedoes not support CommonJS — this was the root cause forcing the CJS → ESM migration above - Changed: all 106 files in
lib/verified ESM-clean — no remainingrequire(),exports.*, ormodule.exportsfrom the previous CommonJS base - Added
makeInMemoryStoreandmakeCacheManagerStorefor chat/contact/message/presence/label state, with JSON serialize/deserialize support - Added
message-composerbuilder (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,orderMessagefor business use cases - Added
groupStatus(group story) andgroupLabel(member labels) - Added 5 newsletter admin functions:
newsletterUpdateName,newsletterUpdateDescription,newsletterUpdatePicture,newsletterRemovePicture,newsletterReactionMode - Improved Album message reliability
License
MIT © xyrosiro
