Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
whatstrackerapp
Advanced tools
Readme
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 the guide to reverse engineering 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.
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:
cd path/to/Baileys
npm install
npm run example
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
Create and cd to your NPM project directory and then in terminal, write:
npm install @adiwajshing/baileys
npm install github:adiwajshing/baileys
Then import in your code using:
import { WAClient } from '@adiwajshing/baileys'
const client = new WAClient()
client.connect()
.then (([user, chats, contacts, unread]) => {
console.log ("oh hello " + user.name + " (" + user.id + ")")
console.log ("you have " + unread.length + " unread messages")
console.log ("you have " + chats.length + " chats")
})
.catch (err => console.log("unexpected error: " + err) )
If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!
If you don't want to wait for WhatsApp to send all your chats while connecting, you can use the following function:
const client = new WAClient()
client.connectSlim() // does not wait for chats & contacts
.then (user => {
console.log ("oh hello " + user.name + " (" + user.id + ")")
client.receiveChatsAndContacts () // wait for chats & contacts in the background
.then (([chats, contacts, unread]) => {
console.log ("you have " + unread.length + " unread messages")
console.log ("you have " + chats.length + " chats")
})
})
.catch (err => console.log("unexpected error: " + err))
You obviously don't want to keep scanning the QR code every time you want to connect.
So, do the following the first time you connect:
import * as fs from 'fs'
const client = new WAClient()
client.connectSlim() // connect first
.then (user => {
const creds = client.base64EncodedAuthInfo () // contains all the keys you need to restore a session
fs.writeFileSync('./auth_info.json', JSON.stringify(creds, null, '\t')) // save JSON to file
})
Then, to restore a session:
const client = new WAClient()
client.connectSlim('./auth_info.json') // will load JSON credentials from file
.then (user => {
// yay connected without scanning QR
})
Optionally, you can load the credentials yourself from somewhere & pass in the JSON object as well.
Implement the following callbacks in your code:
import { getNotificationType } from '@adiwajshing/baileys'
// set first param to `true` if you want to receive outgoing messages that may be sent from your phone
client.setOnUnreadMessage (false, (m: WAMessage) => {
// get what type of notification it is -- message, group add notification etc.
const [notificationType, messageType] = getNotificationType(m)
console.log("got notification of type: " + notificationType) // message, groupAdd, groupLeave
console.log("message type: " + messageType) // conversation, imageMessage, videoMessage, contactMessage etc.
})
client.setOnPresenceUpdate ((json: PresenceUpdate) => console.log(json.id + " presence is " + json.type))
client.setOnMessageStatusChange ((json: MessageStatusUpdate) => {
let sent = json.to
if (json.participant) // participant exists when the message is from a group
sent += " ("+json.participant+")" // mention as the one sent to
// log that they acknowledged the message
console.log(sent + " acknowledged message(s) " + json.ids + " as " + json.type + " at " + json.timestamp)
})
client.setOnUnexpectedDisconnect (err => console.log ("disconnected unexpectedly: " + err) )
Send like, all types of messages with a single function:
import { MessageType, MessageOptions, Mimetype } from '@adiwajshing/baileys'
const id = 'abcd@s.whatsapp.net' // the WhatsApp ID
// send a simple text!
client.sendMessage (id, 'oh hello there', MessageType.text)
// send a location!
client.sendMessage(id, {degreeslatitude: 24.121231, degreesLongitude: 55.1121221}, MessageType.location)
// send a contact!
const vcard = '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'
client.sendMessage(id, {displayname: "Jeff", vcard: vcard}, MessageType.contact)
// send a gif
const buffer = fs.readFileSync("Media/ma_gif.mp4") // load some gif
const options: MessageOptions = {mimetype: Mimetype.gif, caption: "hello!"} // some metadata & caption
client.sendMessage(id, buffer, MessageType.video, options)
To note:
id
is the WhatsApp ID of the person or group you're sending the message to.
[country code][phone number]@s.whatsapp.net
, for example +19999999999@s.whatsapp.net
for people. For groups, it must be in the format 123456789-123345@g.us
.@c.us
for individual people IDs, It won't work.ffmpeg
installed on your system. const info: MessageOptions = {
quoted: quotedMessage, // the message you want to quote
timestamp: Date() // optional, if you want to manually set the timestamp of the message
caption: "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'
*/
}
client.sendReadReceipt(id, messageID)
The id is in the same format as mentioned earlier. The message ID is the unique identifier of the message that you are marking as read. On a WAMessage
, it can be accessed using messageID = message.key.id
.
client.updatePresence(id, WhatsAppWeb.Presence.available)
This lets the person/group with id
know whether you're online, offline, typing etc. where presence
can be one of the following:
// call: import { Presence } from '@adiwajshing/baileys'
export enum Presence {
available = 'available', // "online"
unavailable = 'unavailable', // "offline"
composing = 'composing', // "typing..."
recording = 'recording', // "recording..."
paused = 'paused', // I have no clue
}
If you want to save & process some images, videos, documents or stickers you received
import { getNotificationType, MessageType } from '@adiwajshing/baileys'
client.setOnUnreadMessage (false, async m => {
const messageType = getNotificationType(m.message) // get what type of message it is -- text, image, video
// if the message is not a text message
if (messageType !== MessageType.text && messageType !== MessageType.extendedText) {
const savedFilename = await client.decodeMediaMessage(m.message, "filename") // extension applied automatically
console.log(m.key.remoteJid + " sent media, saved at: " + savedFilename)
}
}
const id = 'xyz@s.whatsapp.net'
const exists = await client.isOnWhatsApp (id)
console.log (`${id} ${exists ? " exists " : " does not exist"} on WhatsApp`)
// query the last 25 messages (replace 25 with the number of messages you want to query)
const messages = await client.loadConversation ("xyz-abc@g.us", 25)
console.log("got back " + messages.length + " messages")
You can also load the entire conversation history if you want
await client.loadEntireConversation ("xyz@c.us", message => console.log("Loaded message with ID: " + message.key.id))
console.log("queried all messages") // promise resolves once all messages are retreived
const status = await client.getStatus ("xyz@c.us") // leave empty to get your own status
console.log("status: " + status)
const ppUrl = await client.getProfilePicture ("xyz@g.us") // leave empty to get your own
console.log("download profile picture from: " + ppUrl)
// the presence update is fetched and called here
client.setOnPresenceUpdate (json => console.log(json.id + " presence is " + json.type))
await client.requestPresenceUpdate ("xyz@c.us")
Of course, replace xyz
with an actual ID.
Also, append @s.whatsapp.net
for individuals & @g.us
for groups.
const metadata = await client.groupMetadata ("abcd-xyz@g.us")
console.log(json.id + ", title: " + json.subject + ", description: " + json.desc)
// title & participants
const group = await client.groupCreate ("My Fab Group", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"])
console.log ("created group with id: " + group.gid)
client.sendTextMessage(group.gid, "hello everyone") // say hello to everyone on the group
// id & people to add to the group
const response = await client.groupAdd ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"])
console.log("added successfully: " + (response.status===200))
const response = await client.groupMakeAdmin ("abcd-xyz@g.us", ["abcd@s.whatsapp.net", "efgh@s.whatsapp.net"]) // id & people to make admin
console.log("made admin successfully: " + (response.status===200))
const response = await client.groupLeave ("abcd-xyz@g.us")
console.log("left group successfully: " + (response.status===200))
const code = await client.groupInviteCode ("abcd-xyz@g.us")
console.log("group code: " + code)
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
client.logLevel = MessageLogLevel.unhandled // set to MessageLogLevel.all to see all messages received
This will enable you to see all sorts of messages WhatsApp sends in the console. Some examples:
Functionality to track of the battery percentage of your phone.
You enable logging and you'll see a message about your battery pop up in the console:
[Baileys] [Unhandled] s22, ["action",null,[["battery",{"live":"false","value":"52"},null]]]
You now know what a battery update looks like. It'll have the following characteristics.
const bMessage = ["action",null,[["battery",{"live":"false","value":"52"},null]]]
bMessage[0]
is always "action"
bMessage[1]
is always null
bMessage[2][0][0]
is always "battery"
Hence, you can register a callback for an event using the following:
client.registerCallback (["action", null, "battery"], json => {
const batteryLevelStr = json[2][0][1].value
const batterylevel = parseInt (batteryLevelStr)
console.log ("battery level: " + batterylevel + "%")
})
This callback will be fired any time a message is received matching the following criteria:
message [0] === "action" && message [1] === null && message[2][0][0] === "battery"
Functionality to keep track of the pushname changes on your phone.
You enable logging and you'll see an unhandled message about your pushanme pop up like this:
[Baileys] [Unhandled] s24, ["Conn",{"pushname":"adiwajshing"}]
You now know what a pushname update looks like. It'll have the following characteristics.
const pMessage = ["Conn",{"pushname":"adiwajshing"}]
pMessage[0]
is always "Conn"
pMessage[1]
always has the key "pushname"
pMessage[2]
is always undefined
Following this, one can implement the following callback:
client.registerCallback (["Conn", "pushname"], json => {
const pushname = json[1].pushname
client.userMetaData.name = pushname // update on client too
console.log ("Name updated: " + pushname)
})
This callback will be fired any time a message is received matching the following criteria:
message [0] === "Conn" && message [1].pushname
A little more testing will reveal that almost all WhatsApp messages are in the format illustrated above.
Note: except for the first parameter (in the above cases, "action"
or "Conn"
), all the other parameters are optional.
This library is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.
FAQs
WhatsApp Web API
The npm package whatstrackerapp receives a total of 3 weekly downloads. As such, whatstrackerapp popularity was classified as not popular.
We found that whatstrackerapp demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.