Reverse Engineered WhatsApp Web API in pure Node.js. Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using a WebSocket. Not running Selenium or Chromimum saves you like half a gig of ram :/
Baileys has also been written from the ground up to be very extensible and simple to use.
If you require more functionality than provided, it'll super easy for you to write an extension (More on this at the end).
-
Install
Create and cd to your NPM project directory and then in terminal, write: npm install baileys
Then import in your code using:
const WhatsAppWeb = require('Baileys')
-
Connecting
const client = new WhatsAppWeb()
client.connect()
.then (([user, chats, contacts, unread]) => {
console.log ("oh hello " + user.name + " (" + user.id + ")")
console.log ("you have " + unread.length + " unread messages")
console.log ("you have " + chats.length + " chats")
})
.catch (err => console.log("unexpected error: " + err) )
If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!
-
Handling Events
Implement the following callbacks in your code:
- Called when you have a pending unread message or recieve a new message
client.setOnUnreadMessage (m => {
const [notificationType, messageType] = client.getNotificationType(m)
console.log("got notification of type: " + notificationType)
console.log("message type: " + messageType)
})
- Called when you recieve an update on someone's presence, they went offline or online
client.setOnPresenceUpdate (json => console.log(json.id + " presence is " + json.type))
- Called when your message gets delivered or read
client.setOnMessageStatusChange (json => {
let sent = json.to
if (json.participant)
sent += " ("+json.participant+")"
console.log(sent + " acknowledged message(s) " + json.ids + " as " + json.type + " at " + json.timestamp)
})
- Called when the connection gets disconnected (either the server loses internet or the phone gets unpaired)
client.setOnUnexpectedDisconnect (err => console.log ("disconnected unexpectedly: " + err) )
-
Sending Messages
It's super simple
- Send text messages using
client.sendTextMessage(id, "oh hello there!")
- Send text messages & quote another message using
client.sendTextMessage(id, "oh hello there", quotedMessage)
quotedMessage
is a message object - Send a media (image, video, sticker, pdf) message using
const buffer = fs.readFileSync("example/ma_gif.mp4")
const info = {gif: true, caption: "hello!"}
client.sendMediaMessage(id, buffer, WhatsAppWeb.MessageType.video, info)
- The thumbnail can be generated automatically for images & stickers. Though, to automatically generate thumbnails for videos, you need to have
ffmpeg
installed on your system mediaBuffer
is just a Buffer containing the contents of the media you want to sendmediaType
represents the type of message you are sending. This can be one of the following:
[
WhatsAppWeb.MessageType.image,
WhatsAppWeb.MessageType.video,
WhatsAppWeb.MessageType.audio,
WhatsAppWeb.MessageType.sticker
]
info
is a JSON object, providing some information about the media. It can have the following optional values:
info = {
caption: "hello there!",
thumbnail: null,
mimetype: "application/pdf",
gif: true
}
- Tested formats: png, jpeg, webp (sticker), mp4, ogg
id
is the WhatsApp id of the person or group you're sending the message to.
It must be in the format [country code][phone number]@s.whatsapp.net
, for example +19999999999@s.whatsapp.net
for people. For groups, it must be in the format 123456789-123345@g.us
.
-
Sending Read Receipts
client.sendReadReceipt(id, messageID)
The id is in the same format as mentioned earlier. The message ID is the unique identifier of the message that you are marking as read. On a message object, it can be accessed using messageID = message.key.id
.
-
Update Presence
client.updatePresence(id, WhatsAppWeb.Presence.available)
This lets the person/group with id
know whether you're online, offline, typing etc. where presence
can be one of the following:
[
WhatsAppWeb.Presence.available,
WhatsAppWeb.Presence.unavailable,
WhatsAppWeb.Presence.composing,
WhatsAppWeb.Presence.recording
]
-
Decoding Media
If you want to save & process some images, videos, documents or stickers you received
client.setOnUnreadMessage (m => {
const messageType = client.getMessageType(m.message)
if (messageType !== WhatsAppWeb.MessageType.text && messageType !== WhatsAppWeb.MessageType.extendedText) {
client.decodeMediaMessage(m.message, "filename")
.then (meta => console.log(m.key.remoteJid + " sent media, saved at: " + meta.filename))
.catch (err => console.log("error in decoding message: " + err))
}
}
-
Restoring Sessions
Once you connect successfully, you can get your authentication credentials using
const authJSON = client.base64EncodedAuthInfo()
Then you can use this JSON to log back in without needing to scan a QR code using
const authJSON = JSON.parse( fs.readFileSync("auth_info.json") )
client.connect (authJSON)
.then (([user, chats, contacts, unread]) => console.log ("yay connected"))
-
Querying
- To check if a given ID is on WhatsApp
client.isOnWhatsApp ("xyz@c.us")
.then (([exists, id]) => console.log(id + (exists ? " exists " : " does not exist") + "on WhatsApp"))
- To query chat history on a group or with someone
client.loadConversation ("xyz-abc@g.us", 25)
.then (messages => console.log("got back " + messages.length + " messages"))
You can also load the entire conversation history if you want
client.loadEntireConversation ("xyz@c.us", (message) => console.log("Loaded message with ID: " + message.key.id))
.then (() => console.log("queried all messages"))
- To get the status of some person/group
client.getStatus ("xyz@c.us")
.then (json => console.log("status: " + json.status))
- To get the display picture of some person/group
client.getStatus ("xyz@g.us")
.then (json => console.log("download profile picture from: " + json.eurl))
- To get someone's presence (if they're typing, online)
client.requestPresenceUpdate ("xyz@c.us")
client.setOnPresenceUpdate (json => console.log(json.id + " presence is " + json.type))
Of course, replace xyz
with an actual ID. Also, append @c.us
for individuals & @g.us
for groups.
-
Groups
- To create a group
client.groupCreate ("My Fab Group", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"])
.then (([json, _]) => {
console.log ("created group with id: " + json.gid)
client.sendTextMessage(json.gid, "hello everyone")
})
- To add people to a group
client.groupAdd ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"])
.then (([json, _]) => console.log("added successfully: " + (json.status===200)))
- To make someone admin on a group
client.groupMakeAdmin ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"])
.then (([json, _]) => console.log("made admin successfully: " + (json.status===200)))
- To leave a group
client.groupLeave ("abcd-xyz@g.us")
.then (([json, _]) => console.log("left group successfully: " + (json.status===200)))
- To get the invite code for a group
client.groupInviteCode ("abcd-xyz@g.us")
.then (code => console.log(code))
-
Writing Custom Functionality
Baileys is also written, keeping in mind, that you may require other functionality. Hence, instead of having to fork the project & re-write the internals, you can simply write extensions in your own code.
First, enable the logging of unhandled messages from WhatsApp by setting
client.logUnhandledMessages = true
This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:
-
Functionality to track of the battery percentage of your phone.
You enable logging and you'll see a message about your battery pop up in the console:
[Baileys] [Unhandled] s22, ["action",null,[["battery",{"live":"false","value":"52"},null]]]
You now know what a battery update looks like. It'll have the following characteristics.
- Given
const bMessage = ["action",null,[["battery",{"live":"false","value":"52"},null]]]
bMessage[0]
is always "action"
bMessage[1]
is always null
bMessage[2][0][0]
is always "battery"
Hence, you can register a callback for an event using the following:
client.registerCallback (["action", null, "battery"], json => {
const batteryLevelStr = json[2][0][1].value
const batterylevel = parseInt (batteryLevelStr)
console.log ("battery level: " + batterylevel + "%")
})
This callback will be fired any time a message is received matching the following criteria:
message [0] === "action" && message [1] === null && message[2][0][0] === "battery"
-
Functionality to keep track of the pushname changes on your phone.
You enable logging and you'll see an unhandled message about your pushanme pop up like this:
[Baileys] [Unhandled] s24, ["Conn",{"pushname":"adiwajshing"}]
You now know what a pushname update looks like. It'll have the following characteristics.
- Given
const pMessage = ["Conn",{"pushname":"adiwajshing"}]
pMessage[0]
is always "Conn"
pMessage[1]
always has the key "pushname"
pMessage[2]
is always undefined
Following this, one can implement the following callback:
client.registerCallback (["Conn", "pushname"], json => {
const pushname = json[1].pushname
client.userMetaData.name = pushname
console.log ("Name updated: " + pushname)
})
This callback will be fired any time a message is received matching the following criteria:
message [0] === "Conn" && message [1].pushname
A little more testing will reveal that almost all WhatsApp messages are in the format illustrated above.
Note: except for the first parameter (in the above cases, "action"
or "Conn"
), all the other parameters are optional.
-
Example
Do check out & run example.js to see example usage of these functions.
To run the example script, download or clone the repo and then type the following in terminal:
cd path/to/Baileys/example
node example.js
I am in no way affiliated with WhatsApp. This was written for educational purposes. Use at your own discretion.