@vreden/meta
Version:
Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.
1,349 lines (1,153 loc) • 43.1 kB
Markdown
# <div align='center'>Baileys - WhatsApp Web API</div>
<div align='center'>

</div>
## Example
Here is an example you can use: [example.ts](Example/example.ts) or here is a tutorial for running the Baileys WhatsApp API code
1. ``` cd path/to/Baileys ```
2. ``` npm install```
3. ``` node example.js```
## Install
Use the stable version:
```bash
npm install @vreden/meta
```
Use the edge version (no guarantee of stability, but latest fixes + features)
```bash
yarn add @vreden/meta@latest
```
Then import your code using:
```javascript
const { default: makeWASocket } = require("@vreden/meta")
```
# Index
- [Connecting Account](#connecting-account)
- [Connect with QR-CODE](#starting-socket-with-qr-code)
- [Connect with Pairing Code](#starting-socket-with-pairing-code)
- [Receive Full History](#receive-full-history)
- [Important Notes About Socket Config](#important-notes-about-socket-config)
- [Caching Group Metadata (Recommended)](#caching-group-metadata-recommended)
- [Improve Retry System & Decrypt Poll Votes](#improve-retry-system--decrypt-poll-votes)
- [Receive Notifications in Whatsapp App](#receive-notifications-in-whatsapp-app)
- [Save Auth Info](#saving--restoring-sessions)
- [Handling Events](#handling-events)
- [Example to Start](#example-to-start)
- [Decrypt Poll Votes](#decrypt-poll-votes)
- [Summary of Events on First Connection](#summary-of-events-on-first-connection)
- [Implementing a Data Store](#implementing-a-data-store)
- [Whatsapp IDs Explain](#whatsapp-ids-explain)
- [Utility Functions](#utility-functions)
- [Sending Messages](#sending-messages)
- [Non-Media Messages](#non-media-messages)
- [Buttons Message](#buttons-message)
- [Buttons Flow](#buttons-flow)
- [Interactive Message](#interactive-message)
- [Text Message](#text-message)
- [Quote Message](#quote-message-works-with-all-types)
- [Mention User](#mention-user-works-with-most-types)
- [Forward Messages](#forward-messages)
- [Location Message](#location-message)
- [Contact Message](#contact-message)
- [Reaction Message](#reaction-message)
- [Pin Message](#pin-message)
- [Poll Message](#poll-message)
- [Sending with Link Preview](#sending-messages-with-link-previews)
- [Media Messages](#media-messages)
- [Gif Message](#gif-message)
- [Video Message](#video-message)
- [Audio Message](#audio-message)
- [Image Message](#image-message)
- [ViewOnce Message](#view-once-message)
- [Modify Messages](#modify-messages)
- [Delete Messages (for everyone)](#deleting-messages-for-everyone)
- [Edit Messages](#editing-messages)
- [Manipulating Media Messages](#manipulating-media-messages)
- [Thumbnail in Media Messages](#thumbnail-in-media-messages)
- [Downloading Media Messages](#downloading-media-messages)
- [Re-upload Media Message to Whatsapp](#re-upload-media-message-to-whatsapp)
- [Reject Call](#reject-call)
- [Send States in Chat](#send-states-in-chat)
- [Reading Messages](#reading-messages)
- [Update Presence](#update-presence)
- [Modifying Chats](#modifying-chats)
- [Archive a Chat](#archive-a-chat)
- [Mute/Unmute a Chat](#muteunmute-a-chat)
- [Mark a Chat Read/Unread](#mark-a-chat-readunread)
- [Delete a Message for Me](#delete-a-message-for-me)
- [Delete a Chat](#delete-a-chat)
- [Star/Unstar a Message](#starunstar-a-message)
- [Disappearing Messages](#disappearing-messages)
- [User Querys](#user-querys)
- [Check If ID Exists in Whatsapp](#check-if-id-exists-in-whatsapp)
- [Query Chat History (groups too)](#query-chat-history-groups-too)
- [Fetch Status](#fetch-status)
- [Fetch Profile Picture (groups too)](#fetch-profile-picture-groups-too)
- [Fetch Bussines Profile (such as description or category)](#fetch-bussines-profile-such-as-description-or-category)
- [Fetch Someone's Presence (if they're typing or online)](#fetch-someones-presence-if-theyre-typing-or-online)
- [Change Profile](#change-profile)
- [Change Profile Status](#change-profile-status)
- [Change Profile Name](#change-profile-name)
- [Change Display Picture (groups too)](#change-display-picture-groups-too)
- [Remove display picture (groups too)](#remove-display-picture-groups-too)
- [Groups](#groups)
- [Create a Group](#create-a-group)
- [Add/Remove or Demote/Promote](#addremove-or-demotepromote)
- [Change Subject (name)](#change-subject-name)
- [Change Description](#change-description)
- [Change Settings](#change-settings)
- [Leave a Group](#leave-a-group)
- [Get Invite Code](#get-invite-code)
- [Revoke Invite Code](#revoke-invite-code)
- [Join Using Invitation Code](#join-using-invitation-code)
- [Get Group Info by Invite Code](#get-group-info-by-invite-code)
- [Query Metadata (participants, name, description...)](#query-metadata-participants-name-description)
- [Join using groupInviteMessage](#join-using-groupinvitemessage)
- [Get Request Join List](#get-request-join-list)
- [Approve/Reject Request Join](#approvereject-request-join)
- [Get All Participating Groups Metadata](#get-all-participating-groups-metadata)
- [Toggle Ephemeral](#toggle-ephemeral)
- [Change Add Mode](#change-add-mode)
- [Privacy](#privacy)
- [Block/Unblock User](#blockunblock-user)
- [Get Privacy Settings](#get-privacy-settings)
- [Get BlockList](#get-blocklist)
- [Update LastSeen Privacy](#update-lastseen-privacy)
- [Update Online Privacy](#update-online-privacy)
- [Update Profile Picture Privacy](#update-profile-picture-privacy)
- [Update Status Privacy](#update-status-privacy)
- [Update Read Receipts Privacy](#update-read-receipts-privacy)
- [Update Groups Add Privacy](#update-groups-add-privacy)
- [Update Default Disappearing Mode](#update-default-disappearing-mode)
- [Broadcast Lists & Stories](#broadcast-lists--stories)
- [Send Broadcast & Stories](#send-broadcast--stories)
- [Query a Broadcast List's Recipients & Name](#query-a-broadcast-lists-recipients--name)
- [Writing Custom Functionality](#writing-custom-functionality)
- [Enabling Debug Level in Baileys Logs](#enabling-debug-level-in-baileys-logs)
- [How Whatsapp Communicate With Us](#how-whatsapp-communicate-with-us)
- [Register a Callback for Websocket Events](#register-a-callback-for-websocket-events)
## Connecting Account
WhatsApp provides a multi-device API that allows Baileys to be authenticated as a second WhatsApp client by scanning a **QR code** or **Pairing Code** with WhatsApp on your phone.
### Starting socket with **QR-CODE**
> [!TIP]
> You can customize browser name if you connect with **QR-CODE**, with `Browser` constant, we have some browsers config, **see [here](https://baileys.whiskeysockets.io/types/BrowsersMap.html)**
```javascript
const { default: makeWASocket } = require("@vreden/meta")
const sock = makeWASocket({
// can provide additional config here
browser: Browsers.ubuntu('My App'),
printQRInTerminal: true
})
```
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!
### Starting socket with **Pairing Code**
> [!IMPORTANT]
> Pairing Code isn't Mobile API, it's a method to connect Whatsapp Web without QR-CODE, you can connect only with one device, see [here](https://faq.whatsapp.com/1324084875126592/?cms_platform=web)
The phone number can't have `+` or `()` or `-`, only numbers, you must provide country code
```javascript
const { default: makeWASocket } = require("@vreden/meta")
const sock = makeWASocket({
// can provide additional config here
printQRInTerminal: false //need to be false
})
if (!sock.authState.creds.registered) {
const number = 'XXXXXXXXXXX'
const code = await sock.requestPairingCode(number)
console.log(code)
}
```
### Receive Full History
1. Set `syncFullHistory` as `true`
2. Baileys, by default, use chrome browser config
- If you'd like to emulate a desktop connection (and receive more message history), this browser setting to your Socket config:
```javascript
const sock = makeWASocket({
...otherOpts,
// can use Windows, Ubuntu here too
browser: Browsers.macOS('Desktop'),
syncFullHistory: true
})
```
## Important Notes About Socket Config
### Caching Group Metadata (Recommended)
- If you use baileys for groups, we recommend you to set `cachedGroupMetadata` in socket config, you need to implement a cache like this:
```javascript
const groupCache = new NodeCache({stdTTL: 5 * 60, useClones: false})
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => groupCache.get(jid)
})
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
sock.ev.on('group-participants.update', async (event) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
```
### Improve Retry System & Decrypt Poll Votes
- If you want to improve sending message, retrying when error occurs and decrypt poll votes, you need to have a store and set `getMessage` config in socket like this:
```javascript
const sock = makeWASocket({
getMessage: async (key) => await getMessageFromStore(key)
})
```
### Receive Notifications in Whatsapp App
- If you want to receive notifications in whatsapp app, set `markOnlineOnConnect` to `false`
```javascript
const sock = makeWASocket({
markOnlineOnConnect: false
})
```
## Saving & Restoring Sessions
You obviously don't want to keep scanning the QR code every time you want to connect.
So, you can load the credentials to log back in:
```javascript
const makeWASocket = require("@vreden/meta").default;
const { useMultiFileAuthState } = require("@vreden/meta");
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
// will use the given state to connect
// so if valid credentials are available -- it'll connect without QR
const sock = makeWASocket({ auth: state })
// this will be called as soon as the credentials are updated
sock.ev.on('creds.update', saveCreds)
```
> [!IMPORTANT]
> `useMultiFileAuthState` is a utility function to help save the auth state in a single folder, this function serves as a good guide to help write auth & key states for SQL/no-SQL databases, which I would recommend in any production grade system.
> [!NOTE]
> When a message is received/sent, due to signal sessions needing updating, the auth keys (`authState.keys`) will update. Whenever that happens, you must save the updated keys (`authState.keys.set()` is called). Not doing so will prevent your messages from reaching the recipient & cause other unexpected consequences. The `useMultiFileAuthState` function automatically takes care of that, but for any other serious implementation -- you will need to be very careful with the key state management.
## Handling Events
- Baileys 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.
> [!IMPORTANT]
> **The events are [these](https://baileys.whiskeysockets.io/types/BaileysEventMap.html)**, it's important you see all events
You can listen to these events like this:
```javascript
const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
console.log('got messages', messages)
})
```
### Example to Start
> [!NOTE]
> This example includes basic auth storage too
```javascript
const makeWASocket = require("@vreden/meta").default;
const { DisconnectReason, useMultiFileAuthState } = require("@vreden/meta");
const Boom = require('@hapi/boom');
async function connectToWhatsApp () {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
const sock = makeWASocket({
// can provide additional config here
auth: state,
printQRInTerminal: true
})
sock.ev.on('connection.update', (update) => {
const { connection, lastDisconnect } = update
if(connection === 'close') {
const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
// reconnect if not logged out
if(shouldReconnect) {
connectToWhatsApp()
}
} else if(connection === 'open') {
console.log('opened connection')
}
})
sock.ev.on('messages.upsert', event => {
for (const m of event.messages) {
console.log(JSON.stringify(m, undefined, 2))
console.log('replying to', m.key.remoteJid)
await sock.sendMessage(m.key.remoteJid!, { text: 'Hello Word' })
}
})
// to storage creds (session info) when it updates
sock.ev.on('creds.update', saveCreds)
}
// run in main file
connectToWhatsApp()
```
> [!IMPORTANT]
> In `messages.upsert` it's recommended to use a loop like `for (const message of event.messages)` to handle all messages in array
### Decrypt Poll Votes
- By default poll votes are encrypted and handled in `messages.update`
- That's a simple example
```javascript
sock.ev.on('messages.update', event => {
for(const { key, update } of event) {
if(update.pollUpdates) {
const pollCreation = await getMessage(key)
if(pollCreation) {
console.log(
'got poll update, aggregation: ',
getAggregateVotesInPollMessage({
message: pollCreation,
pollUpdates: update.pollUpdates,
})
)
}
}
}
})
```
- `getMessage` is a [store](#implementing-a-data-store) implementation (in your end)
### Summary of Events on First Connection
1. When you connect first time, `connection.update` will be fired requesting you to restart sock
2. Then, history messages will be received in `messaging.history-set`
## Implementing a Data Store
- Baileys does not come with a defacto storage for chats, contacts, or messages. However, a simple in-memory implementation has been provided. The store listens for chat updates, new messages, message updates, etc., to always have an up-to-date version of the data.
> [!IMPORTANT]
> I highly recommend building your own data store, as storing someone's entire chat history in memory is a terrible waste of RAM.
It can be used as follows:
```javascript
const makeWASocket = require("@vreden/meta").default;
const { makeInMemoryStore } = require("@vreden/meta");
// the store maintains the data of the WA connection in memory
// can be written out to a file & read from it
const store = makeInMemoryStore({ })
// can be read from a file
store.readFromFile('./baileys_store.json')
// saves the state to a file every 10s
setInterval(() => {
store.writeToFile('./baileys_store.json')
}, 10_000)
const sock = makeWASocket({ })
// will listen from this socket
// the store can listen from a new socket once the current socket outlives its lifetime
store.bind(sock.ev)
sock.ev.on('chats.upsert', () => {
// can use 'store.chats' however you want, even after the socket dies out
// 'chats' => a KeyedDB instance
console.log('got chats', store.chats.all())
})
sock.ev.on('contacts.upsert', () => {
console.log('got contacts', Object.values(store.contacts))
})
```
The store also provides some simple functions such as `loadMessages` that utilize the store to speed up data retrieval.
## Whatsapp IDs Explain
- `id` is the WhatsApp ID, called `jid` too, of the person or group you're sending the message to.
- It must be in the format ```[country code][phone number]@s.whatsapp.net```
- Example for people: ```+19999999999@s.whatsapp.net```.
- 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`.
## Utility Functions
- `getContentType`, returns the content type for any message
- `getDevice`, returns the device from message
- `makeCacheableSignalKeyStore`, make auth store more fast
- `downloadContentFromMessage`, download content from any message
## Sending Messages
- Send all types of messages with a single function
- **[Here](https://baileys.whiskeysockets.io/types/AnyMessageContent.html) you can see all message contents supported, like text message**
- **[Here](https://baileys.whiskeysockets.io/types/MiscMessageGenerationOptions.html) you can see all options supported, like quote message**
```javascript
const jid: string
const content: AnyMessageContent
const options: MiscMessageGenerationOptions
sock.sendMessage(jid, content, options)
```
### Non-Media Messages
#### Buttons Message
```javascript
// send a buttons message!
const buttons = [
{buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1},
{buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1},
{buttonId: 'id3', buttonText: {displayText: 'Button 3'}, type: 1}
]
const buttonMessage = {
text: "Hi it's button message",
footer: 'Hello World',
buttons: buttons,
headerType: 1
}
sock.sendMessage(id, buttonMessage)
```
#### Buttons Flow
```javascript
const flow = {
"name": "single_select",
"paramsJson": `{\"title\":\"Selection\",\"sections\":[{\"title\":\"Here Is title\",\"highlight_label\":\"meta native flow\",\"rows\":[{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"},{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"}]}]}`
}
const buttons = [
{buttonId: 'id1', buttonText: {displayText: 'Button 1'}, type: 1},
{buttonId: 'id2', buttonText: {displayText: 'Button 2'}, type: 1},
{buttonId: 'template', buttonText: {displayText: 'template'}, nativeFlowInfo: flow, type: 2}
]
const buttonMessage = {
text: "Hi it's button flow",
footer: 'Hello World',
buttons: buttons,
headerType: 1
}
sock.sendMessage(id, buttonMessage)
```
#### Interactive Message
```javascript
const button = [{
"name": "single_select",
"buttonParamsJson": `{\"title\":\"Selection\",\"sections\":[{\"title\":\"Here Is title\",\"highlight_label\":\"meta native flow\",\"rows\":[{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"},{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"}]}]}`
},
{
"name": "quick_reply",
"buttonParamsJson": `{\"display_text\":\"quick_reply\",\"id\":\"here is id\"}`
},
{
"name": "cta_url",
"buttonParamsJson": `{\"display_text\":\"url\",\"url\":\"https://www.meta.com\",\"merchant_url\":\"https://www.meta.com\"}`
},
{
"name": "cta_call",
"buttonParamsJson": `{\"display_text\":\"call\",\"id\":\"message\"}`
},
{
"name": "cta_copy",
"buttonParamsJson": `{\"display_text\":\"copy\",\"id\":\"123456789\",\"copy_code\":\"message\"}`
},
{
"name": "cta_reminder",
"buttonParamsJson": `{\"display_text\":\"cta_reminder\",\"id\":\"message\"}`
},
{
"name": "cta_cancel_reminder",
"buttonParamsJson": `{\"display_text\":\"cta_cancel_reminder\",\"id\":\"message\"}`
},
{
"name": "address_message",
"buttonParamsJson": `{\"display_text\":\"address_message\",\"id\":\"message\"}`
},
{
"name": "send_location",
"buttonParamsJson": ""
}]
let msg = generateWAMessageFromContent(jid, {
interactiveMessage: proto.Message.InteractiveMessage.create({
body: {
text: "Hi it's interactive message"
},
footer: {
text: "WhatsApp API"
},
header: {
title: "Footer Text",
hasMediaAttachment: false
},
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
buttons: button,
})
})
}, {
quoted: message
})
sock.relayMessage(msg.key.remoteJid, msg.message, {
messageId: msg.key.id
})
```
#### Text Message
```javascript
await sock.sendMessage(jid, { text: 'hello word' })
```
#### Quote Message (works with all types)
```javascript
await sock.sendMessage(jid, { text: 'hello word' }, { quoted: message })
```
#### Mention User (works with most types)
- @number is to mention in text, it's optional
```javascript
await sock.sendMessage(
jid,
{
text: '@12345678901',
mentions: ['12345678901@s.whatsapp.net']
}
)
```
#### Forward Messages
- You need to have message object, can be retrieved from [store](#implementing-a-data-store) or use a [message](https://baileys.whiskeysockets.io/types/WAMessage.html) object
```javascript
const msg = getMessageFromStore() // implement this on your end
await sock.sendMessage(jid, { forward: msg }) // WA forward the message!
```
#### Location Message
```javascript
await sock.sendMessage(
jid,
{
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221
}
}
)
```
#### Contact Message
```javascript
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'
await sock.sendMessage(
id,
{
contacts: {
displayName: 'Jeff',
contacts: [{ vcard }]
}
}
)
```
#### Reaction Message
- You need to pass the key of message, you can retrieve from [store](#implementing-a-data-store) or use a [key](https://baileys.whiskeysockets.io/types/WAMessageKey.html) object
```javascript
await sock.sendMessage(
jid,
{
react: {
text: '💖', // use an empty string to remove the reaction
key: message.key
}
}
)
```
#### Pin Message
- You need to pass the key of message, you can retrieve from [store](#implementing-a-data-store) or use a [key](https://baileys.whiskeysockets.io/types/WAMessageKey.html) object
- Time can be:
| Time | Seconds |
|-------|----------------|
| 24h | 86.400 |
| 7d | 604.800 |
| 30d | 2.592.000 |
```javascript
await sock.sendMessage(
jid,
{
pin: {
type: 1, // 0 to remove
time: 86400
key: message.key
}
}
)
```
#### Poll Message
```javascript
await sock.sendMessage(
jid,
{
poll: {
name: 'My Poll',
values: ['Option 1', 'Option 2', ...],
selectableCount: 1,
toAnnouncementGroup: false // or true
}
}
)
```
### Sending Messages with Link Previews
1. By default, wa does not have link generation when sent from the web
2. Baileys has a function to generate the content for these link previews
3. To enable this function's usage, add `link-preview-js` as a dependency to your project with `yarn add link-preview-js`
4. Send a link:
```javascript
await sock.sendMessage(
jid,
{
text: 'Hi, this was sent using https://github.com/whiskeysockets/baileys'
}
)
```
### Media Messages
Sending media (video, stickers, images) is easier & more efficient than ever.
> [!NOTE]
> In media messages, you can pass `{ stream: Stream }` or `{ url: Url }` or `Buffer` directly, you can see more [here](https://baileys.whiskeysockets.io/types/WAMediaUpload.html)
- When specifying a media url, Baileys never loads the entire buffer into memory; it even encrypts the media as a readable stream.
> [!TIP]
> It's recommended to use Stream or Url to save memory
#### Gif Message
- Whatsapp doesn't support `.gif` files, that's why we send gifs as common `.mp4` video with `gifPlayback` flag
```javascript
await sock.sendMessage(
jid,
{
video: fs.readFileSync('Media/ma_gif.mp4'),
caption: 'hello word',
gifPlayback: true
}
)
```
#### Video Message
```javascript
await sock.sendMessage(
id,
{
video: {
url: './Media/ma_gif.mp4'
},
caption: 'hello word',
ptv: false // if set to true, will send as a `video note`
}
)
```
#### Audio Message
- To audio message work in all devices you need to convert with some tool like `ffmpeg` with this flags:
```bash
codec: libopus //ogg file
ac: 1 //one channel
avoid_negative_ts
make_zero
```
- Example:
```bash
ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
```
```javascript
await sock.sendMessage(
jid,
{
audio: {
url: './Media/audio.mp3'
},
mimetype: 'audio/mp4'
}
)
```
#### Image Message
```javascript
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
caption: 'hello word'
}
)
```
#### View Once Message
- You can send all messages above as `viewOnce`, you only need to pass `viewOnce: true` in content object
```javascript
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
viewOnce: true, //works with video, audio too
caption: 'hello word'
}
)
```
## Modify Messages
### Deleting Messages (for everyone)
```javascript
const msg = await sock.sendMessage(jid, { text: 'hello word' })
await sock.sendMessage(jid, { delete: msg.key })
```
**Note:** deleting for oneself is supported via `chatModify`, see in [this section](#modifying-chats)
### Editing Messages
- You can pass all editable contents here
```javascript
await sock.sendMessage(jid, {
text: 'updated text goes here',
edit: response.key,
});
```
## Manipulating Media Messages
### Thumbnail in Media Messages
- For media messages, the thumbnail can be generated automatically for images & stickers provided you add `jimp` or `sharp` as a dependency in your project using `yarn add jimp` or `yarn add sharp`.
- Thumbnails for videos can also be generated automatically, though, you need to have `ffmpeg` installed on your system.
### Downloading Media Messages
If you want to save the media you received
```javascript
const { createWriteStream } = require('fs');
const { downloadMediaMessage, getContentType } = require("@vreden/meta");
sock.ev.on('messages.upsert', async ({ [m] }) => {
if (!m.message) return // if there is no text or media message
const messageType = getContentType(m) // get what type of message it is (text, image, video...)
// if the message is an image
if (messageType === 'imageMessage') {
// download the message
const stream = await downloadMediaMessage(
m,
'stream', // can be 'buffer' too
{ },
{
logger,
// pass this so that baileys can request a reupload of media
// that has been deleted
reuploadRequest: sock.updateMediaMessage
}
)
// save to file
const writeStream = createWriteStream('./my-download.jpeg')
stream.pipe(writeStream)
}
}
```
### Re-upload Media Message to Whatsapp
- WhatsApp automatically removes old media from their servers. For the device to access said media -- a re-upload is required by another device that has it. This can be accomplished using:
```javascript
await sock.updateMediaMessage(msg)
```
## Reject Call
- You can obtain `callId` and `callFrom` from `call` event
```javascript
await sock.rejectCall(callId, callFrom)
```
## Send States in Chat
### Reading Messages
- A set of message [keys](https://baileys.whiskeysockets.io/types/WAMessageKey.html) must be explicitly marked read now.
- You cannot mark an entire 'chat' read as it were with Baileys Web.
This means you have to keep track of unread messages.
```javascript
const key: WAMessageKey
// can pass multiple keys to read multiple messages as well
await sock.readMessages([key])
```
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
- ``` presence ``` can be one of [these](https://baileys.whiskeysockets.io/types/WAPresence.html)
- The presence expires after about 10 seconds.
- This lets the person/group with `jid` know whether you're online, offline, typing etc.
```javascript
await sock.sendPresenceUpdate('available', jid)
```
> [!NOTE]
> If a desktop client is active, WA doesn't send push notifications to the device. If you would like to receive said notifications -- mark your Baileys client offline using `sock.sendPresenceUpdate('unavailable')`
## Modifying Chats
WA uses an encrypted form of communication to send chat/app updates. This has been implemented mostly and you can send the following updates:
> [!IMPORTANT]
> If you mess up one of your updates, WA can log you out of all your devices and you'll have to log in again.
### Archive a Chat
```javascript
const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)
```
### Mute/Unmute a Chat
- Supported times:
| Time | Miliseconds |
|-------|-----------------|
| Remove | null |
| 8h | 86.400.000 |
| 7d | 604.800.000 |
```javascript
// mute for 8 hours
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
// unmute
await sock.chatModify({ mute: null }, jid)
```
### Mark a Chat Read/Unread
```javascript
const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
// mark it unread
await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)
```
### Delete a Message for Me
```javascript
await sock.chatModify(
{
clear: {
messages: [
{
id: 'ATWYHDNNWU81732J',
fromMe: true,
timestamp: '1654823909'
}
]
}
},
jid
)
```
### Delete a Chat
```javascript
const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
await sock.chatModify({
delete: true,
lastMessages: [
{
key: lastMsgInChat.key,
messageTimestamp: lastMsgInChat.messageTimestamp
}
]
},
jid
)
```
### Pin/Unpin a Chat
```javascript
await sock.chatModify({
pin: true // or `false` to unpin
},
jid
)
```
### Star/Unstar a Message
```javascript
await sock.chatModify({
star: {
messages: [
{
id: 'messageID',
fromMe: true // or `false`
}
],
star: true // - true: Star Message; false: Unstar Message
}
},
jid
)
```
### Disappearing Messages
- Ephemeral can be:
| Time | Seconds |
|-------|----------------|
| Remove | 0 |
| 24h | 86.400 |
| 7d | 604.800 |
| 90d | 7.776.000 |
- You need to pass in **Seconds**, default is 7 days
```javascript
// turn on disappearing messages
await sock.sendMessage(
jid,
// this is 1 week in seconds -- how long you want messages to appear for
{ disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
)
// will send as a disappearing message
await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })
// turn off disappearing messages
await sock.sendMessage(
jid,
{ disappearingMessagesInChat: false }
)
```
## User Querys
### Check If ID Exists in Whatsapp
```javascript
const [result] = await sock.onWhatsApp(jid)
if (result.exists) console.log (`${jid} exists on WhatsApp, as jid: ${result.jid}`)
```
### Query Chat History (groups too)
- You need to have oldest message in chat
```javascript
const msg = await getOldestMessageInChat(jid)
await sock.fetchMessageHistory(
50, //quantity (max: 50 per query)
msg.key,
msg.messageTimestamp
)
```
- Messages will be received in `messaging.history-set` event
### Fetch Status
```javascript
const status = await sock.fetchStatus(jid)
console.log('status: ' + status)
```
### Fetch Profile Picture (groups too)
- To get the display picture of some person/group
```javascript
// for low res picture
const ppUrl = await sock.profilePictureUrl(jid)
console.log(ppUrl)
// for high res picture
const ppUrl = await sock.profilePictureUrl(jid, 'image')
```
### Fetch Bussines Profile (such as description or category)
```javascript
const profile = await sock.getBusinessProfile(jid)
console.log('business description: ' + profile.description + ', category: ' + profile.category)
```
### Fetch Someone's Presence (if they're typing or online)
```javascript
// the presence update is fetched and called here
sock.ev.on('presence.update', console.log)
// request updates for a chat
await sock.presenceSubscribe(jid)
```
## Change Profile
### Change Profile Status
```javascript
await sock.updateProfileStatus('Hello World!')
```
### Change Profile Name
```javascript
await sock.updateProfileName('My name')
```
### Change Display Picture (groups too)
- To change your display picture or a group's
> [!NOTE]
> Like media messages, you can pass `{ stream: Stream }` or `{ url: Url }` or `Buffer` directly, you can see more [here](https://baileys.whiskeysockets.io/types/WAMediaUpload.html)
```javascript
await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })
```
### Remove display picture (groups too)
```javascript
await sock.removeProfilePicture(jid)
```
## Groups
- To change group properties you need to be admin
### Create a Group
```javascript
// title & participants
const group = await sock.groupCreate('My Fab Group', ['1234@s.whatsapp.net', '4564@s.whatsapp.net'])
console.log('created group with id: ' + group.gid)
await sock.sendMessage(group.id, { text: 'hello there' }) // say hello to everyone on the group
```
### Add/Remove or Demote/Promote
```javascript
// id & people to add to the group (will throw error if it fails)
await sock.groupParticipantsUpdate(
jid,
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'add' // replace this parameter with 'remove' or 'demote' or 'promote'
)
```
### Change Subject (name)
```javascript
await sock.groupUpdateSubject(jid, 'New Subject!')
```
### Change Description
```javascript
await sock.groupUpdateDescription(jid, 'New Description!')
```
### Change Settings
```javascript
// only allow admins to send messages
await sock.groupSettingUpdate(jid, 'announcement')
// allow everyone to send messages
await sock.groupSettingUpdate(jid, 'not_announcement')
// allow everyone to modify the group's settings -- like display picture etc.
await sock.groupSettingUpdate(jid, 'unlocked')
// only allow admins to modify the group's settings
await sock.groupSettingUpdate(jid, 'locked')
```
### Leave a Group
```javascript
// will throw error if it fails
await sock.groupLeave(jid)
```
### Get Invite Code
- To create link with code use `'https://chat.whatsapp.com/' + code`
```javascript
const code = await sock.groupInviteCode(jid)
console.log('group code: ' + code)
```
### Revoke Invite Code
```javascript
const code = await sock.groupRevokeInvite(jid)
console.log('New group code: ' + code)
```
### Join Using Invitation Code
- Code can't have `https://chat.whatsapp.com/`, only code
```javascript
const response = await sock.groupAcceptInvite(code)
console.log('joined to: ' + response)
```
### Get Group Info by Invite Code
```javascript
const response = await sock.groupGetInviteInfo(code)
console.log('group information: ' + response)
```
### Query Metadata (participants, name, description...)
```javascript
const metadata = await sock.groupMetadata(jid)
console.log(metadata.id + ', title: ' + metadata.subject + ', description: ' + metadata.desc)
```
### Join using `groupInviteMessage`
```javascript
const response = await sock.groupAcceptInviteV4(jid, groupInviteMessage)
console.log('joined to: ' + response)
```
### Get Request Join List
```javascript
const response = await sock.groupRequestParticipantsList(jid)
console.log(response)
```
### Approve/Reject Request Join
```javascript
const response = await sock.groupRequestParticipantsUpdate(
jid, // group id
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'approve' // or 'reject'
)
console.log(response)
```
### Get All Participating Groups Metadata
```javascript
const response = await sock.groupFetchAllParticipating()
console.log(response)
```
### Toggle Ephemeral
- Ephemeral can be:
| Time | Seconds |
|-------|----------------|
| Remove | 0 |
| 24h | 86.400 |
| 7d | 604.800 |
| 90d | 7.776.000 |
```javascript
await sock.groupToggleEphemeral(jid, 86400)
```
### Change Add Mode
```javascript
await sock.groupMemberAddMode(
jid,
'all_member_add' // or 'admin_add'
)
```
## Privacy
### Block/Unblock User
```javascript
await sock.updateBlockStatus(jid, 'block') // Block user
await sock.updateBlockStatus(jid, 'unblock') // Unblock user
```
### Get Privacy Settings
```javascript
const privacySettings = await sock.fetchPrivacySettings(true)
console.log('privacy settings: ' + privacySettings)
```
### Get BlockList
```javascript
const response = await sock.fetchBlocklist()
console.log(response)
```
### Update LastSeen Privacy
```javascript
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateLastSeenPrivacy(value)
```
### Update Online Privacy
```javascript
const value = 'all' // 'match_last_seen'
await sock.updateOnlinePrivacy(value)
```
### Update Profile Picture Privacy
```javascript
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateProfilePicturePrivacy(value)
```
### Update Status Privacy
```javascript
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy(value)
```
### Update Read Receipts Privacy
```javascript
const value = 'all' // 'none'
await sock.updateReadReceiptsPrivacy(value)
```
### Update Groups Add Privacy
```javascript
const value = 'all' // 'contacts' | 'contact_blacklist'
await sock.updateGroupsAddPrivacy(value)
```
### Update Default Disappearing Mode
- Like [this](#disappearing-messages), ephemeral can be:
| Time | Seconds |
|-------|----------------|
| Remove | 0 |
| 24h | 86.400 |
| 7d | 604.800 |
| 90d | 7.776.000 |
```javascript
const ephemeral = 86400
await sock.updateDefaultDisappearingMode(ephemeral)
```
## Broadcast Lists & Stories
### Send Broadcast & Stories
- Messages can be sent to broadcasts & stories. You need to add the following message options in sendMessage, like this:
```javascript
await sock.sendMessage(
jid,
{
image: {
url: url
},
caption: caption
},
{
backgroundColor: backgroundColor,
font: font,
statusJidList: statusJidList,
broadcast: true
}
)
```
- Message body can be a `extendedTextMessage` or `imageMessage` or `videoMessage` or `voiceMessage`, see [here](https://baileys.whiskeysockets.io/types/AnyRegularMessageContent.html)
- You can add `backgroundColor` and other options in the message options, see [here](https://baileys.whiskeysockets.io/types/MiscMessageGenerationOptions.html)
- `broadcast: true` enables broadcast mode
- `statusJidList`: a list of people that you can get which you need to provide, which are the people who will get this status message.
- You can send messages to broadcast lists the same way you send messages to groups & individual chats.
- Right now, WA Web does not support creating broadcast lists, but you can still delete them.
- Broadcast IDs are in the format `12345678@broadcast`
### Query a Broadcast List's Recipients & Name
```javascript
const bList = await sock.getBroadcastListInfo('1234@broadcast')
console.log (`list name: ${bList.name}, recps: ${bList.recipients}`)
```
## Writing Custom Functionality
Baileys is written with custom functionality in mind. Instead of forking the project & re-writing the internals, you can simply write your own extensions.
### Enabling Debug Level in Baileys Logs
First, enable the logging of unhandled messages from WhatsApp by setting:
```javascript
const sock = makeWASocket({
logger: P({ level: 'debug' }),
})
```
This will enable you to see all sorts of messages WhatsApp sends in the console.
### How Whatsapp Communicate With Us
> [!TIP]
> If you want to learn whatsapp protocol, we recommend to study about Libsignal Protocol and Noise Protocol
- **Example:** Functionality to track the battery percentage of your phone. You enable logging and you'll see a message about your battery pop up in the console:
```
{
"level": 10,
"fromMe": false,
"frame": {
"tag": "ib",
"attrs": {
"from": "@s.whatsapp.net"
},
"content": [
{
"tag": "edge_routing",
"attrs": {},
"content": [
{
"tag": "routing_info",
"attrs": {},
"content": {
"type": "Buffer",
"data": [8,2,8,5]
}
}
]
}
]
},
"msg":"communication"
}
```
The `'frame'` is what the message received is, it has three components:
- `tag` -- what this frame is about (eg. message will have 'message')
- `attrs` -- a string key-value pair with some metadata (contains ID of the message usually)
- `content` -- the actual data (eg. a message node will have the actual message content in it)
- read more about this format [here](/src/WABinary/readme.md)
### Register a Callback for Websocket Events
> [!TIP]
> Recommended to see `onMessageReceived` function in `socket.ts` file to understand how websockets events are fired
```javascript
// for any message with tag 'edge_routing'
sock.ws.on('CB:edge_routing', (node: BinaryNode) => { })
// for any message with tag 'edge_routing' and id attribute = abcd
sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => { })
// for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => { })
```
> [!NOTE]
> Also, this repo is now licenced under GPL 3 since it uses [libsignal-node](https://git.questbook.io/backend/service-coderunner/-/merge_requests/1)