Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - devops35/Baileys: Lightweight full-featured typescript/javascript WhatsApp Web API · GitHub
Skip to content

Repository files navigation

Baileys - Typescript/Javascript WhatsApp Web API

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 :/

Thank you to @Sigalor for writing his observations on the workings of WhatsApp Web and thanks to @Rhymen for the go implementation.

Baileys is type-safe, 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 here.

If you're interested in building a WhatsApp bot, you may wanna check out WhatsAppInfoBot and an actual bot built with it, Messcat.

Read the docs hereJoin the Discord here

Multi-Device Support

Baileys now supports the latest multi-device beta. However, as it's a completely different implementation from WA Web & will break a lot of things, it's on its own branch which will be merged into master once mutli-device hits production. You may find the MD repo here.

The master branch only supports WA Web at the moment.

Example

Do check out & run example.ts to see example usage of the library. The script covers most common use cases. To run the example script, download or clone the repo and then type the following in terminal:

  1. cd path/to/Baileys
  2. npm install
  3. npm run example

Install

Create and cd to your NPM project directory and then in terminal, write:

  1. stable: npm install @adiwajshing/baileys
  2. stabl-ish w quicker fixes & latest features: npm install github:adiwajshing/baileys

Do note, the library will likely vary if you're using the NPM package, read that here

Then import in your code using:

import{WAConnection}from'@adiwajshing/baileys'

Unit Tests

Baileys also comes with a unit test suite. Simply cd into the Baileys directory & run npm test.

You will require a phone with WhatsApp to test, and a second WhatsApp number to send messages to. Set the phone number you can randomly send messages to in a .env file with TEST_JID=1234@s.whatsapp.net

Connecting

import{WAConnection}from'@adiwajshing/baileys'asyncfunctionconnectToWhatsApp(){constconn=newWAConnection()// called when WA sends chats// this can take up to a few minutes if you have thousands of chats!conn.on('chats-received',async({ hasNewChats })=>{console.log(`you have ${conn.chats.length} chats, new chats available: ${hasNewChats}`)constunread=awaitconn.loadAllUnreadMessages()console.log("you have "+unread.length+" unread messages")})// called when WA sends chats// this can take up to a few minutes if you have thousands of contacts!conn.on('contacts-received',()=>{console.log('you have '+Object.keys(conn.contacts).length+' contacts')})awaitconn.connect()conn.on('chat-update',chatUpdate=>{// `chatUpdate` is a partial object, containing the updated properties of the chat// received a new messageif(chatUpdate.messages&&chatUpdate.count){constmessage=chatUpdate.messages.all()[0]console.log(message)}elseconsole.log(chatUpdate)// see updates (can be archived, pinned etc.)})}// run in main fileconnectToWhatsApp().catch(err=>console.log("unexpected error: "+err))// catch any errors

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!

Do note, the conn.chats object is a KeyedDB. This is done for the following reasons:

  • Most applications require chats to be ordered in descending order of time. (KeyedDB does this in log(N) time)
  • Most applications require pagination of chats (Use chats.paginated())
  • Most applications require O(1) access to chats via the chat ID. (Use chats.get(jid) with KeyedDB)

Configuring the Connection

You can configure the connection via the connectOptions property. You can even specify an HTTPS proxy. For example:

import{WAConnection,ProxyAgent}from'@adiwajshing/baileys'constconn=newWAConnecion()conn.connectOptions.agent=ProxyAgent('http://some-host:1234')awaitconn.connect()console.log("oh hello "+conn.user.name+"! You connected via a proxy")

The entire WAConnectOptions struct is mentioned here with default values:

conn.connectOptions={/** fails the connection if no data is received for X seconds */maxIdleTimeMs?: 60_000,/** maximum attempts to connect */maxRetries?: 10,/** max time for the phone to respond to a connectivity test */phoneResponseTime?: 15_000,/** minimum time between new connections */connectCooldownMs?: 4000,/** agent used for WS connections (could be a proxy agent) */agent?: Agent=undefined,/** agent used for fetch requests -- uploading/downloading media */fetchAgent?: Agent=undefined,/** always uses takeover for connecting */alwaysUseTakeover: true/** log QR to terminal */logQR: true}asWAConnectOptions

Saving & Restoring Sessions

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can save the credentials to log back in via:

import*asfsfrom'fs'constconn=newWAConnection()// this will be called as soon as the credentials are updatedconn.on('open',()=>{// save credentials whenever updatedconsole.log(`credentials updated!`)constauthInfo=conn.base64EncodedAuthInfo()// get all the auth info we need to restore this sessionfs.writeFileSync('./auth_info.json',JSON.stringify(authInfo,null,'\t'))// save this info to a file})awaitconn.connect()// connect

Then, to restore a session:

constconn=newWAConnection()conn.loadAuthInfo('./auth_info.json')// will load JSON credentials from fileawaitconn.connect()// yay connected without scanning QR/* Optionally, you can load the credentials yourself from somewhere  & pass in the JSON object to loadAuthInfo () as well.*/

If you're considering switching from a Chromium/Puppeteer based library, you can use WhatsApp Web's Browser credentials to restore sessions too:

conn.loadAuthInfo('./auth_info_browser.json')awaitconn.connect()// works the same

See the browser credentials type in the docs.

Note: Upon every successive connection, WA can update part of the stored credentials. Whenever that happens, the credentials are uploaded, and you should probably update your saved credentials upon receiving the open event. Not doing so may lead WA to log you out after a few weeks with a 419 error code.

QR Callback

If you want to do some custom processing with the QR code used to authenticate, you can register for the following event:

conn.on('qr',qr=>{// Now, use the 'qr' string to display in QR UI or send somewhere}awaitconn.connect()

Handling Events

Baileys now uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.

/** when the connection has opened successfully */on(event: 'open',listener: (result: WAOpenResult)=>void): this/** when the connection is opening */on(event: 'connecting',listener: ()=>void): this/** when the connection has closed */on(event: 'close',listener: (err: {reason?: DisconnectReason|string,isReconnecting: boolean})=>void): this/** when the socket is closed */on(event: 'ws-close',listener: (err: {reason?: DisconnectReason|string})=>void): this/** when a new QR is generated, ready for scanning */on(event: 'qr',listener: (qr: string)=>void): this/** when the connection to the phone changes */on(event: 'connection-phone-change',listener: (state: {connected: boolean})=>void): this/** when a contact is updated */on(event: 'contact-update',listener: (update: WAContactUpdate)=>void): this/** when a new chat is added */on(event: 'chat-new', listener: (chat: WAChat) => void): this
/** when contacts are sent by WA */on(event: 'contacts-received',listener: (u: {updatedContacts: Partial<WAContact>[]})=>void): this/** when chats are sent by WA, and when all messages are received */on(event: 'chats-received',listener: (update: {hasNewChats?: boolean})=>void): this/** when all initial messages are received from WA */on(event: 'initial-data-received',listener: (update: {chatsWithMissingMessages: {jid: string,count: number}[]})=>void): this/** when multiple chats are updated (new message, updated message, deleted, pinned, etc) */on(event: 'chats-update',listener: (chats: WAChatUpdate[])=>void): this/** when a chat is updated (new message, updated message, read message, deleted, pinned, presence updated etc) */on(event: 'chat-update',listener: (chat: WAChatUpdate)=>void): this/** when participants are added to a group */on(event: 'group-participants-update',listener: (update: {jid: string,participants: string[],actor?: string,action: WAParticipantAction})=>void): this/** when the group is updated */on(event: 'group-update',listener: (update: Partial<WAGroupMetadata>&{jid: string,actor?: string})=>void): this/** when WA sends back a pong */on(event: 'received-pong',listener: ()=>void): this/** when a user is blocked or unblockd */on(event: 'blocklist-update',listener: (update: BlocklistUpdate)=>void): this

Sending Messages

Send all types of messages with a single function:

Non-Media Messages

import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'constid='abcd@s.whatsapp.net'// the WhatsApp ID // send a simple text!constsentMsg=awaitconn.sendMessage(id,'oh hello there',MessageType.text)// send a location!constsentMsg=awaitconn.sendMessage(id,{degreesLatitude: 24.121231,degreesLongitude: 55.1121221},MessageType.location)// send a contact!constvcard='BEGIN:VCARD\n'// metadata of the contact card+'VERSION:3.0\n'+'FN:Jeff Singh\n'// full name+'ORG:Ashoka Uni;\n'// the organization of the contact+'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'// WhatsApp ID + phone number+'END:VCARD'constsentMsg=awaitconn.sendMessage(id,{displayname: "Jeff",vcard: vcard},MessageType.contact)// send a list message!constrows=[{title: 'Row 1',description: "Hello it's description 1",rowId:"rowid1"},{title: 'Row 2',description: "Hello it's description 2",rowId:"rowid2"}]constsections=[{title: "Section 1",rows: rows}]constbutton={buttonText: 'Click Me!',description: "Hello it's list message",sections: sections,listType: 1}constsendMsg=awaitconn.sendMessage(id,button,MessageType.listMessage)// send a buttons message!constbuttons=[{buttonId: 'id1',buttonText: {displayText: 'Button 1'},type: 1},{buttonId: 'id2',buttonText: {displayText: 'Button 2'},type: 1}]constbuttonMessage={contentText: "Hi it's button message",footerText: 'Hello World',buttons: buttons,headerType: 1}constsendMsg=awaitconn.sendMessage(id,buttonMessage,MessageType.buttonsMessage)

Media Messages

Sending media (video, stickers, images) is easier & more efficient than ever.

  • You can specify a buffer, a local url or even a remote url.
  • When specifying a media url, Baileys never loads the entire buffer into memory, it even encrypts the media as a readable stream.
import{MessageType,MessageOptions,Mimetype}from'@adiwajshing/baileys'// Sending gifsawaitconn.sendMessage(id,fs.readFileSync("Media/ma_gif.mp4"),// load a gif and send itMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'Media/ma_gif.mp4'},// send directly from local fileMessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})awaitconn.sendMessage(id,{url: 'https://giphy.com/gifs/11JTxkrmq4bGE0/html5'},// send directly from remote url!MessageType.video,{mimetype: Mimetype.gif,caption: "hello!"})// send an audio fileawaitconn.sendMessage(id,{url: "Media/audio.mp3"},// can send mp3, mp4, & oggMessageType.audio,{mimetype: Mimetype.mp4Audio}// some metadata (can't have caption in audio))

Notes

  • id is the WhatsApp ID of the person or group you're sending the message to.
    • It must be in the format [country code without +][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.
    • For broadcast lists it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.
  • For media messages, the thumbnail can be generated automatically for images & stickers. Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.
  • MessageOptions: some extra info about the message. It can have the following optional values:
    constinfo: MessageOptions={quoted: quotedMessage,// the message you want to quotecontextInfo: {forwardingScore: 2,isForwarded: true},// some random context info (can show a forwarded message with this too)timestamp: Date(),// optional, if you want to manually set the timestamp of the messagecaption: "hello there!",// (for media messages) the caption to send with the media (cannot be sent with stickers though)thumbnail: "23GD#4/==",/* (for location & media messages) has to be a base 64 encoded JPEG if you want to send a custom thumb,  or set to null if you don't want to send a thumbnail. Do not enter this field if you want to automatically generate a thumb */mimetype: Mimetype.pdf,/* (for media messages) specify the type of media (optional for all media types except documents), import {Mimetype} from '@adiwajshing/baileys' */filename: 'somefile.pdf',// (for media messages) file name for the media/* will send audio messages as voice notes, if set to true */ptt: true,// will detect links & generate a link preview automatically (default true)detectLinks: true,/** Should it send as a disappearing messages.  * By default 'chat' -- which follows the setting of the chat */sendEphemeral: 'chat'}

Forwarding Messages

constmessages=awaitconn.loadConversation('1234@s.whatsapp.net',1)constmessage=messages[0]// get the last message from this conversationawaitconn.forwardMessage('455@s.whatsapp.net',message)// WA forward the message!

Reading Messages

constid='1234-123@g.us'constmessageID='AHASHH123123AHGA'// id of the message you want to readawaitconn.chatRead(id)// mark all messages in chat as read (equivalent of opening a chat in WA)awaitconn.chatRead(id,'unread')// mark the chat as unread

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

Update Presence

import{Presence}from'@adiwajshing/baileys'awaitconn.updatePresence(id,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:

exportenumPresence{available='available',// "online"composing='composing',// "typing..."recording='recording',// "recording..."paused='paused'// stopped typing, back to "online"}

The presence expires after about 10 seconds.

Downloading Media Messages

If you want to save the media you received

import{MessageType}from'@adiwajshing/baileys'conn.on('message-new',asyncm=>{if(!m.message)return// if there is no text or media messageconstmessageType=Object.keys(m.message)[0]// get what type of message it is -- text, image, video// if the message is not a text messageif(messageType!==MessageType.text&&messageType!==MessageType.extendedText){constbuffer=awaitconn.downloadMediaMessage(m)// to decrypt & use as a bufferconstsavedFilename=awaitconn.downloadAndSaveMediaMessage(m)// to decrypt & save to fileconsole.log(m.key.remoteJid+" sent media, saved at: "+savedFilename)}}

Deleting Messages

constjid='1234@s.whatsapp.net'// can also be a groupconstresponse=awaitconn.sendMessage(jid,'hello!',MessageType.text)// send a messageawaitconn.deleteMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for everyone!awaitconn.clearMessage(jid,{id: response.messageID,remoteJid: jid,fromMe: true})// will delete the sent message for only you!

Modifying Chats

constjid='1234@s.whatsapp.net'// can also be a groupawaitconn.modifyChat(jid,ChatModification.archive)// archive chatawaitconn.modifyChat(jid,ChatModification.unarchive)// unarchive chatconstresponse=awaitconn.modifyChat(jid,ChatModification.pin)// pin the chatawaitconn.modifyChat(jid,ChatModification.unpin)// unpin itawaitconn.modifyChat(jid,ChatModification.mute,8*60*60*1000)// mute for 8 hourssetTimeout(()=>{conn.modifyChat(jid,ChatModification.unmute)},5000)// unmute after 5 secondsawaitconn.modifyChat(jid,ChatModification.delete)// will delete the chat (can be a group or broadcast list as well)

Note: to unmute or unpin a chat, one must pass the timestamp of the pinning or muting. This is returned by the pin & mute functions. This is also available in the WAChat objects of the respective chats, as a mute or pin property.

Disappearing Messages

constjid='1234@s.whatsapp.net'// can also be a group// turn on disappearing messagesawaitconn.toggleDisappearingMessages(jid,WA_DEFAULT_EPHEMERAL// this is 1 week in seconds -- how long you want messages to appear for)// will automatically send as a disappearing messageawaitconn.sendMessage(jid,'Hello poof!',MessageType.text)// turn off disappearing messagesawaitconn.toggleDisappearingMessages(jid,0)

Misc

  • To load chats in a paginated manner
    const{chats, cursor}=awaitconn.loadChats(25)if(cursor){constmoreChats=awaitconn.loadChats(25,cursor)// load the next 25 chats}
  • To check if a given ID is on WhatsApp Note: this method falls back to using https://wa.me to determine whether a number is on WhatsApp in case the WebSocket connection is not open yet.
    constid='123456'constexists=awaitconn.isOnWhatsApp(id)if(exists)console.log(`${id} exists on WhatsApp, as jid: ${exists.jid}`)
  • To query chat history on a group or with someone
    // query the last 25 messages (replace 25 with the number of messages you want to query)constmessages=awaitconn.loadMessages("xyz-abc@g.us",25)console.log("got back "+messages.length+" messages")
    You can also load the entire conversation history if you want
    awaitconn.loadAllMessages("xyz@c.us",message=>console.log("Loaded message with ID: "+message.key.id))console.log("queried all messages")// promise resolves once all messages are retrieved
  • To get the status of some person
    conststatus=awaitconn.getStatus("xyz@c.us")// leave empty to get your own statusconsole.log("status: "+status)
  • To get the display picture of some person/group
    constppUrl=awaitconn.getProfilePicture("xyz@g.us")// leave empty to get your ownconsole.log("download profile picture from: "+ppUrl)
  • To change your display picture or a group's
    constjid='111234567890-1594482450@g.us'// can be your own tooconstimg=fs.readFileSync('new-profile-picture.jpeg')// can be PNG alsoawaitconn.updateProfilePicture(jid,img)
  • To get someone's presence (if they're typing, online)
    // the presence update is fetched and called hereconn.on('CB:Presence',json=>console.log(json.id+" presence is "+json.type))awaitconn.requestPresenceUpdate("xyz@c.us")// request the update
  • To search through messages
    constresponse=awaitconn.searchMessages('so cool',null,25,1)// search in all chatsconsole.log(`got ${response.messages.length} messages in search`)constresponse2=awaitconn.searchMessages('so cool','1234@c.us',25,1)// search in given chat
  • To block or unblock user
    awaitconn.blockUser("xyz@c.us","add")// Block userawaitconn.blockUser("xyz@c.us","remove")// Unblock user

Of course, replace xyz with an actual ID.

Groups

  • To create a group
    // title & participantsconstgroup=awaitconn.groupCreate("My Fab Group",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])console.log("created group with id: "+group.gid)conn.sendMessage(group.gid,"hello everyone",MessageType.extendedText)// say hello to everyone on the group
  • To add people to a group
    // id & people to add to the group (will throw error if it fails)constresponse=awaitconn.groupAdd("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])
  • To make/demote admins on a group
    // id & people to make admin (will throw error if it fails)awaitconn.groupMakeAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])awaitconn.groupDemoteAdmin("abcd-xyz@g.us",["abcd@s.whatsapp.net","efgh@s.whatsapp.net"])// demote admins
  • To change the group's subject
    awaitconn.groupUpdateSubject("abcd-xyz@g.us","New Subject!")
  • To change the group's description
    awaitconn.groupUpdateDescription("abcd-xyz@g.us","This group has a new description")
  • To change group settings
    import{GroupSettingChange}from'@adiwajshing/baileys'// only allow admins to send messagesawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.messageSend,true)// allow everyone to modify the group's settings -- like display picture etc.awaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,false)// only allow admins to modify the group's settingsawaitconn.groupSettingChange("abcd-xyz@g.us",GroupSettingChange.settingsChange,true)
  • To leave a group
    awaitconn.groupLeave("abcd-xyz@g.us")// (will throw error if it fails)
  • To get the invite code for a group
    constcode=awaitconn.groupInviteCode("abcd-xyz@g.us")console.log("group code: "+code)
  • To query the metadata of a group
    constmetadata=awaitconn.groupMetadata("abcd-xyz@g.us")console.log(json.id+", title: "+json.subject+", description: "+json.desc)// Or if you've left the group -- call thisconstmetadata2=awaitconn.groupMetadataMinimal("abcd-xyz@g.us")
  • To join the group using the invitation code
    constresponse=awaitconn.acceptInvite("xxx")console.log("joined to: "+response.gid)
    Of course, replace xxx with invitation code.
  • To revokes the current invite link of a group
    constresponse=awaitconn.revokeInvite("abcd-xyz@g.us")console.log("new group code: "+response.code)

Broadcast Lists & Stories

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.
  • Unfortunately, WA Web does not support creating broadcast lists right now but you can still delete them.
  • Broadcast IDs are in the format 12345678@broadcast
  • To query a broadcast list's recipients & name:
    constbList=awaitconn.getBroadcastListInfo("1234@broadcast")console.log(`list name: ${bList.name}, recps: ${bList.recipients}`)

Writing Custom Functionality

Baileys is written, keeping in mind, that you may require other custom 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

conn.logger.level='debug'

This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:

  1. 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: 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:

    conn.on(`CB:action,,battery`,json=>{constbatteryLevelStr=json[2][0][1].valueconstbatterylevel=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"

  2. 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: 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:

    conn.on('CB:Conn,pushname',json=>{constpushname=json[1].pushnameconn.user.name=pushname// update on client tooconsole.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.

Note

This library was originally a project for CS-2362 at Ashoka University and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.

About

Lightweight full-featured typescript/javascript WhatsApp Web API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages