Baileys is designed with extensibility in mind. Instead of forking the project and rewriting internals, you can write your own extensions by registering custom WebSocket event handlers.
Writing Custom Extensions
You can extend Baileys to handle custom WhatsApp Web protocol messages without modifying the library’s core code.
Enable Debug Logging
First, enable debug-level logging to see all messages WhatsApp sends:
import makeWASocket from '@whiskeysockets/baileys'
import P from 'pino'
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
This will print all unhandled messages from WhatsApp to the console, allowing you to identify messages you want to handle.
For even more detailed output, use level: 'trace' to see the raw XML representation of all binary nodes.
WhatsApp communicates using a binary node structure. When you enable debug logging, you’ll see messages like:
{
"level": 10,
"fromMe": false,
"frame": {
"tag": "ib",
"attrs": {
"from": "@s.whatsapp.net"
},
"content": [
{
"tag": "edge_routing",
"attrs": {},
"content": [
{
"tag": "routing_info",
"attrs": {},
"content": {
"type": "Buffer",
"data": [8,2,8,5]
}
}
]
}
]
},
"msg": "communication"
}
BinaryNode Structure
Each frame/message has three main components:
- tag - Identifies what the frame is about (e.g.,
message, ib, iq)
- attrs - String key-value pairs with metadata (usually contains message ID, type, etc.)
- content - The actual data (can be nested BinaryNodes, string, or Uint8Array)
type BinaryNode = {
tag: string
attrs: { [key: string]: string }
content?: BinaryNode[] | string | Uint8Array
}
Registering Custom Handlers
Once you’ve identified a message type you want to handle, you can register a callback using the WebSocket event system.
Basic Handler Registration
The pattern for registering handlers is documented in the WebSocket Events page:
// Handle any message with tag 'edge_routing'
sock.ws.on('CB:edge_routing', (node: BinaryNode) => {
console.log('Edge routing message:', node)
})
Example: Battery Percentage Tracker
Here’s a complete example of tracking your phone’s battery percentage:
import makeWASocket, { BinaryNode } from '@whiskeysockets/baileys'
import P from 'pino'
const sock = makeWASocket({
logger: P({ level: 'debug' })
})
// Register handler for battery updates
sock.ws.on('CB:ib,,battery', (node: BinaryNode) => {
const batteryNode = node.content?.find(
(child: BinaryNode) => child.tag === 'battery'
)
if (batteryNode?.attrs) {
const level = batteryNode.attrs.level
const charging = batteryNode.attrs.charging === 'true'
console.log(`Battery: ${level}% ${charging ? '(charging)' : ''}`)
}
})
Example: Custom Presence Handler
Handle custom presence events:
sock.ws.on('CB:presence', (node: BinaryNode) => {
const from = node.attrs.from
const type = node.attrs.type // 'available', 'unavailable', 'composing'
console.log(`${from} is now ${type}`)
})
Advanced Pattern Matching
You can register handlers with specific attribute matching. See the WebSocket Events documentation for the full pattern syntax:
// Match specific tag and attribute value
sock.ws.on('CB:iq,type:result', (node: BinaryNode) => {
console.log('IQ result received:', node)
})
// Match tag, attribute, and first content child
sock.ws.on('CB:iq,id:my-custom-id,query', (node: BinaryNode) => {
console.log('Custom query response:', node)
})
Best Practices
Avoid modifying core Baileys internals. Use the event system to extend functionality instead.
Type Safety
Always import and use the BinaryNode type:
import { BinaryNode } from '@whiskeysockets/baileys'
sock.ws.on('CB:message', (node: BinaryNode) => {
// TypeScript will provide autocomplete for node.tag, node.attrs, etc.
})
Error Handling
Wrap your custom handlers in try-catch blocks:
sock.ws.on('CB:custom-event', (node: BinaryNode) => {
try {
// Your custom logic
processCustomMessage(node)
} catch (error) {
console.error('Error handling custom event:', error)
}
})
Avoid Blocking Operations
Use async handlers for I/O operations:
sock.ws.on('CB:message', async (node: BinaryNode) => {
// Perform async operations without blocking
await saveMessageToDatabase(node)
})
Learning the Protocol
If you want to learn more about the WhatsApp Web protocol, study:
- Libsignal Protocol - For understanding encryption
- Noise Protocol - For understanding the handshake mechanism
The source code in src/Socket/socket.ts contains the onMessageReceived function, which shows how Baileys processes and routes WebSocket events internally.
Next Steps