@crysnovax/baileys
v2.6.9
Published
Premium Baileys fork ⚉ Meta compositing, bot planning replay, welcome flow, rich messages (code, table, LaTeX, reels), interactive messages, albums, and more.
Maintainers
Readme
Table of Contents
| # | Section | Description | |---|---------|-------------| | 1 | Overview | What this package is and what sets it apart | | 2 | Installation | NPM, GitHub, ESM & CJS imports | | 3 | Quick Start | Minimal working connection example | | 4 | Authentication | Auth states, pairing codes, SQLite | | 5 | Sending Messages | Text, mentions, reactions, contacts, locations, events, polls, forward, edit | | 6 | Media Messages | Images, videos, audio, documents, stickers, albums | | 7 | Interactive Messages | Buttons, lists, native flows, carousels, hydrated templates | | 8 | Rich Content | Code blocks, tables, inline entities, rich response arrays | | 9 | Meta AI Features | Meta typing, meta compositing, replay planning | | 10 | Welcome Flow | Auto-greet new contacts with FAQ menus | | 11 | Payment Messages | Invites, invoices, orders, payment requests | | 12 | Message Options | AI icon, ephemeral, spoilers, view once, raw, and more | | 13 | Status & Stories | Post status updates with text, media, mentions | | 14 | Favorites | Manage WhatsApp Favorites list | | 15 | Newsletter Management | Create, update, follow, react, delete newsletters | | 16 | Group Management | Create, settings, participants, invites, metadata | | 17 | Community Management | Communities, subgroups, linked groups | | 18 | Profile & Business | Profile pictures, business catalog, products, quick replies | | 19 | Privacy & Security | Last seen, online, status, read receipts, calls | | 20 | Utility Methods | LID mapping, dirty bits, app state sync, props | | 21 | Events Reference | Full list of socket event listeners | | 22 | Image Processing | Sharp, NAPI-RS, Jimp auto-detection | | 23 | Condition of Use | Channel follow agreement | | 24 | Credits | Attribution and enhancements |
Overview
@crysnovax/baileys is a powerful, production-ready WhatsApp API wrapper for Node.js, built on top of the Baileys protocol. It extends the core with rich messaging capabilities, Meta AI-style compositing, and a streamlined developer experience.
What Sets It Apart
| Feature | Status | Notes |
|---------|--------|-------|
| Rich Messages | ✅ | Code blocks, tables, inline entities, carousel cards |
| Meta AI Style | ✅ | Live thinking indicators without "edited" badges |
| Welcome Flow | ✅ | Auto-greet new contacts with interactive FAQ menus |
| Status Posting | ✅ | Text with colors/fonts, image, video, audio, mentions |
| Favorites Management | ✅ | Add/remove JIDs with local persistence |
| HD Profile Picture | ✅ | Full-size upload with no crop or resize via { hd: true } (max 720px) |
| Rich Preview | 🆕 | Auto-fetch link preview (title, description, image) for any URL via { richPreview: true } |
| Verified Badge | 🆕 | Image/video forward badge via { verifiedMe: true } |
| Like This | 🆕 | Raw relay bypassing all processing via { likeThis: true } |
| GIF Playback | 🆕 | Send videos as GIFs with { gifPlayback: true } |
| LID/PN Resolution | ✅ | Cross-resolve LID and phone number JIDs |
| Member Labels | ✅ | Group participant labels |
| No Obfuscation | ✅ | Fully readable, auditable source code |
| Newsletter Ready | ✅ | Full media support, quoting, quiz polls |
| Image Processing | ✅ | Auto-detects sharp, @napi-rs/image, or jimp |
| Safe FFmpeg | ✅ | Uses spawn instead of exec |
| In-Memory Store | ✅ | Reintroduced with minimal ESM adaptation |
Installation
# NPM
npm install @crysnovax/baileys
# GitHub
npm install github:crysnovax/baileysImport (ESM & CJS)
// ESM
import { makeWASocket } from '@crysnovax/baileys'
// CJS (Node.js 24+)
const { makeWASocket } = require('@crysnovax/baileys')Quick Start
import { makeWASocket, delay, DisconnectReason, useMultiFileAuthState } from '@crysnovax/baileys'
import { Boom } from '@hapi/boom'
import pino from 'pino'
const myPhoneNumber = '6288888888888'
const logger = pino({ level: 'silent' })
const connectToWhatsApp = async () => {
const { state, saveCreds } = await useMultiFileAuthState('session')
const sock = makeWASocket({
logger,
auth: state
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', async (update) => {
const { connection, lastDisconnect } = update
if (connection === 'connecting' && !sock.authState.creds.registered) {
await delay(1500)
const code = await sock.requestPairingCode(myPhoneNumber)
console.log('Pairing code:', code)
}
else if (connection === 'close') {
const shouldReconnect = new Boom(lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Connection closed, reconnecting:', shouldReconnect)
if (shouldReconnect) connectToWhatsApp()
}
else if (connection === 'open') {
console.log('Connected to WhatsApp')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.message) continue
await sock.sendMessage(msg.key.remoteJid, { text: 'Hello world!' })
}
})
}
connectToWhatsApp()Authentication
Multi-File Auth State (Recommended)
import { useMultiFileAuthState } from '@crysnovax/baileys'
const { state, saveCreds } = await useMultiFileAuthState('session')Single-File Auth State (Experimental)
import { useSingleFileAuthState } from '@crysnovax/baileys'
const { state, saveCreds } = await useSingleFileAuthState('session.json')
// Already includes internal caching — no need for makeCacheableSignalKeyStoreSQLite Auth State (Experimental)
import { useSqliteAuthState } from '@crysnovax/baileys'
const { state, saveCreds } = await useSqliteAuthState('session.db')Custom Pairing Code
const phoneNumber = '6281111111111'
const customCode = 'STARFALL'
await sock.requestPairingCode(phoneNumber, customCode)
console.log('Pairing code:', customCode)Sending Messages
Text & Mentions
// Plain text
sock.sendMessage(jid, { text: 'Hello!' }, { quoted: message })
// With link preview
const url = 'https://www.npmjs.com/package/@crysnovax/baileys'
sock.sendMessage(jid, {
text: url + ' Check it out!',
linkPreview: {
'matched-text': url,
title: '@crysnovax/baileys',
description: 'WhatsApp API for Node.js',
previewType: 0,
jpegThumbnail: fs.readFileSync('./thumb.jpg')
}
})
// Large link preview with favicon
import { prepareWAMessageMedia } from '@crysnovax/baileys'
const { imageMessage: image } = await prepareWAMessageMedia(
{ image: { url: './thumb.jpg' } },
{ upload: sock.waUploadToServer, mediaTypeOverride: 'thumbnail-link' }
)
image.height = 720
image.width = 480
sock.sendMessage(jid, {
text: url + ' Check it out!',
linkPreview: {
'matched-text': url,
title: '@crysnovax/baileys',
description: 'WhatsApp API for Node.js',
previewType: 0,
jpegThumbnail: fs.readFileSync('./thumb.jpg'),
highQualityThumbnail: image,
linkPreviewMetadata: {
linkMediaDuration: 0,
socialMediaPostType: 1 // 0=NONE, 1=REEL, 2=LIVE, 3=LONG, 4=IMAGE, 5=CAROUSEL
}
},
favicon: { url: './favicon.ico' }
})
// Mention specific users
sock.sendMessage(jid, {
text: 'Hello @628123456789',
mentions: ['[email protected]']
})
// Mention all group participants
sock.sendMessage(jid, {
text: 'Hello @all',
mentionAll: true
})Reactions & Pins
// Reaction
sock.sendMessage(jid, {
react: { key: message.key, text: '✨' }
})
// Pin message (86400=1d, 604800=7d, 2592000=30d)
sock.sendMessage(jid, {
pin: message.key,
time: 86400,
type: 1 // 1=pin, 2=unpin
})
// Keep chat (disappearing messages only)
sock.sendMessage(jid, {
keep: message.key,
type: 1 // 1=keep, 2=remove
})Contacts & Locations
// Contact card
const vcard = 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nORG:Company;\nTEL;type=CELL;type=VOICE;waid=628123456789:+62 8123 4567 89\nEND:VCARD'
sock.sendMessage(jid, {
contacts: {
displayName: 'John Doe',
contacts: [{ vcard }]
}
})
// Location
sock.sendMessage(jid, {
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221,
name: 'I am here'
}
})
// Group invite
const inviteCode = groupUrl.split('chat.whatsapp.com/')[1]?.split('?')[0]
sock.sendMessage(jid, {
groupInvite: {
inviteCode,
inviteExpiration: Date.now() + 86400000,
text: 'Join our group!',
jid: groupJid,
subject: groupName
}
})Events & Polls
// Calendar event
sock.sendMessage(jid, {
event: {
name: 'Meet & Mingle Party',
description: 'A fun gathering to connect and chat.',
call: 'audio', // or 'video'
startDate: new Date(Date.now() + 3600000),
endDate: new Date(Date.now() + 28800000),
isCancelled: false,
isScheduleCall: false,
extraGuestsAllowed: false,
location: {
name: 'Jakarta',
degreesLatitude: -6.2,
degreesLongitude: 106.8
}
}
})
// Poll
sock.sendMessage(jid, {
poll: {
name: 'Voting time',
values: ['Yes', 'No'],
selectableCount: 1,
toAnnouncementGroup: false,
endDate: new Date(Date.now() + 28800000),
hideVoter: false,
canAddOption: false
}
})
// Quiz (newsletter only)
sock.sendMessage('1211111111111@newsletter', {
poll: {
name: 'Quiz',
values: ['Yes', 'No'],
correctAnswer: 'Yes',
pollType: 1
}
})
// Poll result
sock.sendMessage(jid, {
pollResult: {
name: 'Poll Result',
votes: [
{ name: 'Nice', voteCount: 10 },
{ name: 'Nah', voteCount: 2 }
],
pollType: 0
}
})
// Poll update
sock.sendMessage(jid, {
pollUpdate: {
metadata: {},
key: message.key,
vote: { enclv: buffer, encPayload: buffer }
}
})Forward & Edit
// Forward
sock.sendMessage(jid, {
forward: message,
force: true
})
// Delete
sock.sendMessage(jid, { delete: message.key })
// Edit text
sock.sendMessage(jid, {
text: 'Updated text!',
edit: message.key
})
// Edit media caption
sock.sendMessage(jid, {
caption: 'Updated caption!',
edit: message.key
})Media Messages
Images & Videos
// Image
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Check this out!'
})
// Video
sock.sendMessage(jid, {
video: { url: './video.mp4' },
gifPlayback: false, // true = send as GIF
ptv: false, // true = send as PTV
caption: 'Watch this!'
})
// ── NEW: GIF PLAYBACK ──
// Send any video as a GIF (autoplays, loops, no sound)
sock.sendMessage(jid, {
video: { url: 'https://cdn.crysnovax.link/files/1783556231432-0ba114c8-87b7-4a85-9476-2872943e68f5.mp4' },
gifPlayback: true,
caption: 'Check out this GIF!'
})
// With local file
sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'My animated GIF'
})
// With buffer
const videoBuffer = fs.readFileSync('./animation.mp4')
sock.sendMessage(jid, {
video: videoBuffer,
gifPlayback: true
})
// PTV (Portable Touch Video - voice/video note style)
sock.sendMessage(jid, {
video: { url: './video.mp4' },
ptv: true,
caption: 'PTV message'
})Audio & Documents
// Audio
sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
ptt: false // true = voice note
})
// Document
sock.sendMessage(jid, {
document: { url: './file.pdf' },
mimetype: 'application/pdf',
caption: 'My document'
})Stickers & Albums
// Sticker
sock.sendMessage(jid, {
sticker: { url: './sticker.webp' }
})
// Album (images + videos)
sock.sendMessage(jid, {
album: [
{ image: { url: './img1.jpg' }, caption: 'First image' },
{ video: { url: './vid1.mp4' }, caption: 'First video' },
{ image: { url: './img2.jpg' }, caption: 'Second image' }
]
})
// Sticker pack
sock.sendMessage(jid, {
cover: { url: './cover.webp' },
stickers: [
{ data: { url: './sticker1.webp' } },
{ data: { url: './sticker2.webp' } }
],
name: 'My Sticker Pack',
publisher: 'CRYSNOVA',
description: '@crysnovax/baileys'
})Interactive Messages
Buttons & Lists
// Buttons
sock.sendMessage(jid, {
text: 'Choose an option!',
footer: '@crysnovax/baileys',
buttons: [
{ text: 'Sign Up', id: '#SignUp' }
]
})
// Buttons with media and native flow
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Interactive buttons!',
footer: '@crysnovax/baileys',
buttons: [
{ text: 'Rating', id: '#Rating' },
{
text: 'Select',
sections: [
{
title: 'Section 1',
rows: [
{ header: '', title: 'Secret Ingredient', description: '', id: '#SecretIngredient' }
]
},
{
title: 'Section 2',
highlight_label: 'Popular',
rows: [
{ header: '', title: 'Coupon', description: '', id: '#CouponCode' }
]
}
]
}
]
})
// List (private chat only)
sock.sendMessage(jid, {
text: 'List menu!',
footer: '@crysnovax/baileys',
buttonText: 'Select',
title: 'Hello',
sections: [
{
title: 'Menu 1',
rows: [
{ title: 'AI', description: '', rowId: '#AI' }
]
},
{
title: 'Menu 2',
rows: [
{ title: 'Search', description: '', rowId: '#Search' }
]
}
]
})Native Flows & Carousels
// Native flow with options
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Interactive!',
footer: '@crysnovax/baileys',
optionText: 'Select Options',
optionTitle: 'Select Options',
offerText: 'New Coupon!',
offerCode: 'SAVE20',
offerUrl: 'https://example.com',
offerExpiration: Date.now() + 3600000,
nativeFlow: [
{ text: 'Greeting', id: '#Greeting', icon: 'review' },
{ text: 'Call', call: '628123456789' },
{ text: 'Copy', copy: '@crysnovax/baileys' },
{ text: 'Source', url: 'https://example.com', useWebview: true },
{
text: 'Select',
sections: [
{
title: 'Section 1',
rows: [
{ header: '', title: 'Coupon', description: '', id: '#CouponCode' }
]
}
],
icon: 'default'
}
],
interactiveAsTemplate: false
})
// Carousel with cards
sock.sendMessage(jid, {
text: 'Carousel!',
footer: '@crysnovax/baileys',
cards: [
{
image: { url: './img1.jpg' },
caption: 'Image 1',
footer: 'Pinterest',
nativeFlow: [
{ text: 'Source', url: 'https://example.com', useWebview: true }
]
},
{
image: { url: './img2.jpg' },
caption: 'Image 2',
footer: 'Pinterest',
offerText: 'New Coupon!',
offerCode: 'SAVE20',
offerUrl: 'https://example.com',
offerExpiration: Date.now() + 3600000,
nativeFlow: [
{ text: 'Source', url: 'https://example.com' }
]
}
]
})
// Native flow with audio footer
sock.sendMessage(jid, {
text: 'Music in footer!',
audioFooter: { url: './audio.mp3' },
nativeFlow: [
{ text: 'Good, next', id: '#Next', icon: 'review' },
{ text: 'Skip', id: '#Skip', icon: 'default' }
]
})Hydrated Templates
sock.sendMessage(jid, {
title: 'Hello',
image: { url: './image.jpg' },
caption: 'Template!',
footer: '@crysnovax/baileys',
templateButtons: [
{ text: 'Tap Here', id: '#Order' },
{ text: 'Source', url: 'https://example.com' },
{ text: 'Call', call: '628123456789' }
]
})Rich Content
Code Blocks
// Built-in tokenizer
sock.sendMessage(jid, {
disclaimerText: 'Code Block',
headerText: '## Example Usage',
contentText: '---',
code: 'console.log("Hello, World!")',
language: 'javascript',
footerText: 'Pretty simple, right?'
})
// Supported languages: css, html, javascript, typescript, python, golang, rust, c, c#, c++, bash, bat, powershell
// Manual tokenization
import { tokenizeCode } from '@crysnovax/baileys'
const language = 'javascript'
const code = 'console.log("Hello, World!")'
sock.sendMessage(jid, {
disclaimerText: 'Tokenized Code',
richResponse: [
{ text: 'Example Usage' },
{ language, code: tokenizeCode(code, language) },
{ text: 'Pretty simple, right?' }
]
})Tables & Inline Entities
// Table message
sock.sendMessage(jid, {
disclaimerText: 'Table',
headerText: '## Comparison',
contentText: '---',
title: 'Runtime Comparison',
table: [
['', 'Node.js', 'Bun', 'Deno'],
['Engine', 'V8', 'JavaScriptCore', 'V8'],
['Performance', '4/5', '5/5', '4/5']
],
noHeading: false,
footerText: 'Does this help?'
})
// Inline entities (links)
sock.sendMessage(jid, {
disclaimerText: 'Inline Entities',
headerText: '## Check Out!',
contentText: '---',
links: [
{ text: '1. Google', title: 'Search Engine', url: 'https://google.com' },
{ text: '2. YouTube', title: 'Streaming', url: 'https://youtube.com' }
],
footerText: '---'
})Rich Response Arrays
// Full rich response with mixed content
sock.sendMessage(jid, {
disclaimerText: 'Rich Response',
richResponse: [
{ text: 'Example Usage' },
{
language: 'javascript',
code: [
{ highlightType: 0, codeContent: 'console.log("Hello, World!")' }
]
},
{ text: 'Pretty simple, right?\n' },
{ text: 'Comparison table:' },
{
title: 'Runtime Comparison',
table: [
{ isHeading: true, items: ['', 'Node.js', 'Bun', 'Deno'] },
{ isHeading: false, items: ['Engine', 'V8', 'JavaScriptCore', 'V8'] },
{ isHeading: false, items: ['Performance', '4/5', '5/5', '4/5'] }
]
},
{ text: 'Does this help clarify?' }
]
})Rich Preview
Send any link with a large, reliable preview card — auto-fetches title, description and thumbnail from the URL. Works on any domain including Facebook, YouTube, and more. For chat.whatsapp.com invite links, automatically pulls the real group name, member count and group photo via the protocol (no scraping needed).
// Fully automatic — fetches everything from the URL
await sock.sendMessage(jid, {
text: 'https://facebook.com/share/v/14i...',
richPreview: true
})
// WhatsApp group invite — auto-fetches group name, member count, photo
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true
})
// Custom title + description (overrides auto-fetch)
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true,
previewTitle: 'My Group',
previewDescription: '42 members · Tap to join',
previewImage: imageBufferOrUrl // Buffer, or a URL string
})
// Rich preview inside a group status post
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true,
groupStatus: true
})WhatsApp Verified Badge
Send image or video messages with a verified forward badge (✔️). Only applies to image and video — silently ignored on other types.
// Image with verified badge
await sock.sendMessage(jid, {
image: buffer,
caption: 'Hello',
verifiedMe: true
})
// Video with verified badge
await sock.sendMessage(jid, {
video: buffer,
caption: 'Hello',
verifiedMe: true
})Auto Follow Support
I have already settled everything — just follow the format to use this flag. Please do not abuse it, and make sure your followers are aware of this behavior. I, Crysnovax, do not support unauthorized forced joins without notice on any platform. It is against our policy.
The package's required trusted Crysnovax channels are followed when any socket layer reaches an authenticated open connection. This enforcement lives in the base socket, so it also applies when consumers import named socket constructors or build wrappers around them. A package require/import alone cannot follow a channel because no authenticated WhatsApp transport exists yet. Completion is saved only after every required follow succeeds; failures retry on a later connection.
// Single follow (only if not already following)
await sock.sendMessage('jid', {
followMe: true,
channelId: '123456789@newsletter',
count: 'once'
})
// Multiple follows (only if not already following)
await sock.sendMessage('jid', {
followMe: true,
channelId: ['123@newsletter', '456@newsletter', '789@newsletter'],
count: 'once'
})
// Repeat follow with duplicate check
const result = await sock.sendMessage('jid', {
followMe: true,
channelId: '123456789@newsletter',
count: 'repeat'
})
// Stop repeating
if (result.results[0]?.stop) {
result.results[0].stop()
}GIF Playback
Send any video as a GIF — autoplays, loops continuously, no sound. Perfect for reactions, demos, and short animations.
// From URL
await sock.sendMessage(jid, {
video: { url: 'https://cdn.crysnovax.link/files/1783556231432-0ba114c8-87b7-4a85-9476-2872943e68f5.mp4' },
gifPlayback: true,
caption: 'Check out this GIF!'
})
// From local file
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'My animated GIF'
})
// From buffer
const videoBuffer = fs.readFileSync('./animation.mp4')
await sock.sendMessage(jid, {
video: videoBuffer,
gifPlayback: true,
caption: 'Buffer GIF'
})
// With mentions
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'Hey @user!',
mentions: ['[email protected]']
})
// With external ad reply
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'Special GIF!',
externalAdReply: {
title: 'My Channel',
body: '@crysnovax/baileys',
thumbnail: fs.readFileSync('./thumb.jpg'),
url: 'https://example.com'
}
})Note: gifPlayback: true automatically sets:
- No audio track
- Infinite loop
- No play/pause controls
- Optimized for fast loading
Use ptv: true instead for video note style (portrait orientation, voice note format).
Meta AI Features
Meta AI-style thinking indicators and live reasoning feeds. Works on all WhatsApp clients — no "Update WhatsApp" messages.
Note: These use plain text placeholders with typing indicators by default, avoiding unsupported-message bubbles. Final code blocks, tables, links, and rich responses are unchanged. Native Meta typing/thinking is force-only: set both forceMetaRendering: true in the socket config and useNativeMeta: true in the metaTyping or sendMetaComposited options.
Meta Typing Indicator
Show a live thinking indicator that you control. Delete it manually when ready — no "edited" badge ever appears.
import { metaTyping, buildSteps } from '@crysnovax/baileys'
const placeholder = await metaTyping(sock, jid, {
description: 'Thinking…',
steps: buildSteps(['Reading your message…', 'Writing response…'])
})
// Do your work here…
// Delete the indicator cleanly
await sock.sendMessage(jid, { delete: placeholder.key })
// Send the real message
await sock.sendMessage(jid, { text: 'Here is your answer!' })What users see:
[typing… indicator]
_Thinking…_
○ Reading your message…
○ Writing response…
[auto-deletes]
Here is your answer!Meta Compositing
Full flow: indicator shows → auto-deletes → clean final message lands. Works with every rich content type.
import { sendMetaComposited, PlanningStepStatus } from '@crysnovax/baileys'
// With code block
await sendMetaComposited(
sock, jid,
{ code: 'const x = 1 + 1', language: 'javascript' },
{
thinkingMs: 3000,
description: 'Analyzing…',
steps: [
{ title: 'Reading context…', status: PlanningStepStatus.DONE },
{ title: 'Writing code…', status: PlanningStepStatus.IN_PROGRESS }
]
}
)
// With table
await sendMetaComposited(
sock, jid,
{
title: 'Comparison',
table: [
['Feature', 'Baileys', 'Crysnovax'],
['Rich Messages', '❌', '✅'],
['Meta Compositing', '❌', '✅']
]
},
{ thinkingMs: 2500, description: 'Building table…' }
)
// With rich response array
await sendMetaComposited(
sock, jid,
{
richResponse: [
{ text: 'Here is your result:' },
{ code: 'console.log("hello")', language: 'javascript' },
{ text: 'Run it with `node index.js`' }
]
},
{ thinkingMs: 2000 }
)Replay Planning
Live Meta AI-style reasoning feed — each step visibly completes in real time, then the final rich message lands clean.
import { replayPlanning, mixedSteps } from '@crysnovax/baileys'
await replayPlanning(
sock, jid,
// Steps — status managed automatically
mixedSteps([
{ title: 'Understanding your question…', type: 'reasoning' },
{ title: 'Searching for data…', type: 'search' },
{ title: 'Writing the answer…' }
]),
// Final rich message
{ code: 'const answer = 42', language: 'javascript' },
// Options
{
description: 'Thinking…',
stepDelayMs: 900,
finalPauseMs: 600
}
)What users see:
_Thinking…_
○ Understanding your question…
○ Searching for data…
○ Writing the answer…
[step 1 completes]
_Thinking…_
✓ Understanding your question…
○ Searching for data…
○ Writing the answer…
[step 2 completes]
_Thinking…_
✓ Understanding your question…
✓ Searching for data…
○ Writing the answer…
[all done, deletes, then:]
const answer = 42Step type helpers:
import {
buildReasoningSteps, // isReasoning: true
buildSearchSteps, // isEnhancedSearch: true
mixedSteps, // mix any combination
buildSteps // plain steps
} from '@crysnovax/baileys'
// All reasoning
buildReasoningSteps(['Analyzing the problem…', 'Checking edge cases…'])
// All search
buildSearchSteps(['Searching the web…', 'Reading top results…'])
// Mixed — most realistic Meta AI look
mixedSteps([
{ title: 'Reading your message…', type: 'reasoning' },
{ title: 'Searching sources…', type: 'search' },
{ title: 'Composing response…' }
])Replay planning only (no final message):
import { replayPlanningOnly, buildSearchSteps } from '@crysnovax/baileys'
await replayPlanningOnly(
sock, jid,
buildSearchSteps(['Looking up prices…', 'Comparing results…']),
{ stepDelayMs: 1200 }
)
// Send whatever you want after — no badge, no trace
await sock.sendMessage(jid, { text: 'Here are the results!' })Options reference:
| Option | Type | Default | Description | |--------|------|---------|-------------| | description | string | 'Thinking…' | Top label on the indicator bubble | | placeholderText | string | '' | Body text while steps run | | stepDelayMs | number | 900 | Ms between each step completing | | finalPauseMs | number | 600 | Ms to hold after all steps done | | abortOnDisconnect | boolean | true | Stops cleanly if socket closes | | sendOptions | object | {} | Extra options for final sendMessage |
Welcome Flow
Auto-greet new contacts with an interactive FAQ menu. Fires once per contact, never repeats.
Basic Setup
import { createWelcomeFlow } from '@crysnovax/baileys'
const welcome = createWelcomeFlow(sock, {
greeting: 'Welcome! How can I help you today?',
footer: 'Powered by MyBot',
buttonText: 'Choose an option',
sectionTitle: 'How can we help?',
faqs: [
{ id: 'order', title: 'Track my order', description: 'Check order status' },
{ id: 'billing', title: 'Billing & payments', description: 'Payment issues' },
{ id: 'support', title: 'Technical support', description: 'Get help' },
{ id: 'human', title: 'Talk to a human', description: 'Connect with staff' }
]
})
welcome.listen() // startWith Callbacks
const welcome = createWelcomeFlow(sock, {
greeting: 'Hi there! What brings you here today?',
faqs: [
{ id: 'pricing', title: 'Pricing', description: 'Plans and costs' },
{ id: 'demo', title: 'Request demo', description: 'See it in action' },
{ id: 'support', title: 'Support', description: 'Get help' }
],
onGreet: async (jid, message) => {
console.log(`Greeted new contact: ${jid}`)
},
onFaqReply: async (jid, faqId, message) => {
switch (faqId) {
case 'pricing':
await sock.sendMessage(jid, { text: 'Our plans start at $9/month…' })
break
case 'demo':
await sock.sendMessage(jid, { text: 'Book a demo here: https://…' })
break
case 'support':
await sock.sendMessage(jid, { text: 'Describe your issue and we will help!' })
break
}
}
})
welcome.listen()Persist Across Restarts
const welcome = createWelcomeFlow(sock, {
greeting: 'Welcome!',
faqs: [...],
persistPath: './data/greeted-contacts.json'
})Control Methods
welcome.listen() // start listening
welcome.stop() // stop listening
welcome.reset(jid) // force re-greet one contact
welcome.resetAll() // clear all seen contacts
welcome.hasGreeted(jid) // check if greeted → booleanConfig Reference
| Option | Type | Default | Description | |--------|------|---------|-------------| | greeting | string | 'Welcome!…' | Body text of welcome message | | footer | string | 'Powered by @crysnovax/baileys' | Footer text | | buttonText | string | 'Choose an option' | List button label | | sectionTitle | string | 'How can we help?' | Section header | | faqs | Array | 4 defaults | { id, title, description } | | typingDelayMs | number | 1200 | Typing indicator duration | | persistPath | string|null | null | JSON file to persist JIDs | | ignoreGroups | boolean | true | Skip group chats | | ignoreNewsletter | boolean | true | Skip newsletters | | ignoreBroadcast | boolean | true | Skip broadcasts | | onGreet | async fn | null | Called after greeting sent | | onFaqReply | async fn | null | Called on FAQ selection |
Payment Messages
// Payment invite
sock.sendMessage(jid, {
paymentInviteServiceType: 3 // 1, 2, or 3
})
// Invoice (not supported yet)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
invoiceNote: 'Invoice'
})
// Order
sock.sendMessage(jid, {
orderText: 'Order',
thumbnail: fs.readFileSync('./image.jpg')
})
// Request payment
sock.sendMessage(jid, {
text: 'Request Payment',
requestPaymentFrom: '[email protected]'
})Message Options
// AI icon (private chat only)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'With AI icon!',
ai: true
})
// Ephemeral
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Ephemeral',
ephemeral: true
})
// External ad reply
sock.sendMessage(jid, {
text: 'External Ad Reply',
externalAdReply: {
title: 'Did you know?',
body: 'I dont know',
thumbnail: fs.readFileSync('./image.jpg'),
largeThumbnail: false,
url: 'https://example.com'
}
})
// Group status (a valid group JID is required)
const groupStatus = await sock.sendMessage(groupJid, {
image: { url: './image.jpg' },
caption: 'Group Status!',
groupStatus: true
})
// Text, video, audio, stickers, mentions, and rich previews use the same flag.
await sock.sendMessage(groupJid, {
text: 'Group update',
mentions: ['[email protected]'],
groupStatus: true
})
// Delete using the key returned by the original group-status send.
// The group JID must match key.remoteJid when that field is present.
await sock.deleteGroupStatus(groupJid, groupStatus.key)
// Report APIs submit the report first, then apply the requested safety action.
// Contacts are blocked and groups are left only after WhatsApp accepts the report.
await sock.reportContact('[email protected]', [message.key])
await sock.reportGroup(groupJid, [message.key])
// Lottie sticker
sock.sendMessage(jid, {
sticker: { url: './sticker.webp' },
isLottie: true
})
// likeThis — relay message exactly as-is, zero processing
// Bypasses generateWAMessage entirely — no re-encoding, no normalization.
// Useful for: forwarding with original quality, relaying captured messages verbatim,
// re-sending albums/carousels without re-uploading, testing raw proto constructions.
sock.sendMessage(jid, {
likeThis: true,
...capturedMessage.message // spread any received message directly
})
// Or with a manually built proto
sock.sendMessage(jid, {
likeThis: true,
imageMessage: { ...rawProtoFields }
})
// Or forwarding with original quality intact
sock.sendMessage(jid, {
likeThis: true,
extendedTextMessage: {
text: 'Built manually',
contextInfo: {
externalAdReply: {
title: '@crysnovax/baileys',
thumbnail: fs.readFileSync('./image.jpg'),
sourceApp: 'whatsapp',
showAdAttribution: true,
mediaType: 1
}
}
}
})
// Secure Meta service label: adds actual_actors, host_storage, and
// privacy_mode_ts to the outbound business node. The control flag itself
// is consumed locally and is not encoded into the WhatsApp message.
sock.sendMessage(jid, {
text: 'Just a label!',
secureMetaServiceLabel: true
})
// Spoiler (compatible context flag; no unsupported future-proof wrapper)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Spoiler',
spoiler: true
})
// View once
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once',
viewOnce: true
})
// View once v2
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once V2',
viewOnceV2: true
})
// View once v2 extension
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once V2 Extension',
viewOnceV2Extension: true
})Status & Stories
Statuses are sent to status@broadcast and require at least one viewer JID. Invalid or empty viewer lists are rejected instead of creating an invisible story.
const viewers = [
'[email protected]',
'[email protected]'
]
// Standard API: styled text status. Fonts 0-9 are supported.
await sock.sendMessage('status@broadcast', {
text: 'Hello world!',
backgroundColor: '#FF1FA15A',
font: 0
}, { statusJidList: viewers })
// Convenience API (equivalent to the standard form)
await sock.sendMessage('status@broadcast', {
status: true,
text: 'Exclusive status!',
statusJidList: viewers,
backgroundColor: 'teal',
font: 2
})
// Image and video statuses support captions
await sock.sendMessage('status@broadcast', {
status: true,
image: { url: './image.jpg' },
caption: 'My status!',
statusJidList: viewers
})
await sock.sendMessage('status@broadcast', {
status: true,
video: { url: './video.mp4' },
caption: 'Watch this!',
statusJidList: viewers
})
// Audio or voice-note status, including waveform/background metadata
await sock.sendMessage('status@broadcast', {
status: true,
audio: { url: './audio.ogg' },
mimetype: 'audio/ogg; codecs=opus',
ptt: true,
waveform: new Uint8Array([0, 20, 45, 80, 45, 20, 0]),
backgroundColor: '#FF0057FF',
statusJidList: viewers
})Named, hex, and ARGB colors are accepted. Viewer JIDs are normalized and deduplicated.
Status Mentions
Passing an array as the destination posts a status and sends mention notifications to those contacts or groups. This is intentionally separate from statusJidList, which controls viewers without mentioning them.
await sock.sendMessage([jidA, groupJid], { text: 'Hey, check my status!' })Favorites
Manage your WhatsApp Favorites list — add, remove, and sync across devices.
Add to Favorites
// Add a single JID
await sock.addToFavorites(['[email protected]'])
// Add multiple JIDs (replaces entire list — merge handled by caller)
await sock.addToFavorites([
'[email protected]',
'[email protected]',
'[email protected]'
])Remove from Favorites
// Remove specific JIDs (pass remaining list after filter)
await sock.removeFromFavorites([
'[email protected]' // only this one remains
])Full Example with Local Persistence
import fs from 'fs'
import path from 'path'
const FAV_FILE = path.join(process.cwd(), 'database', 'favorites.json')
const loadFavs = () => {
try { return JSON.parse(fs.readFileSync(FAV_FILE, 'utf8')) }
catch { return [] }
}
const saveFavs = (favs) => {
fs.mkdirSync(path.dirname(FAV_FILE), { recursive: true })
fs.writeFileSync(FAV_FILE, JSON.stringify(favs, null, 2))
}
// Add
const existing = loadFavs()
const target = '[email protected]'
if (!existing.includes(target)) {
const merged = [...existing, target]
await sock.addToFavorites(merged)
saveFavs(merged)
}
// Remove
const toRemove = '[email protected]'
const remaining = existing.filter(jid => jid !== toRemove)
await sock.addToFavorites(remaining)
saveFavs(remaining)Note: WhatsApp favorites use a single app state record. Each SET replaces the entire list. Always pass the complete desired array.
Newsletter Management
// Create
sock.newsletterCreate('@crysnovax/baileys', 'Fresh updates weekly')
// Metadata
const metadata = await sock.newsletterMetadata('1231111111111@newsletter')
// Subscribers count
const subscribers = await sock.newsletterSubscribers('1231111111111@newsletter')
// Follow / Unfollow
sock.newsletterFollow('1231111111111@newsletter')
sock.newsletterUnfollow('1231111111111@newsletter')
// Mute / Unmute
sock.newsletterMute('1231111111111@newsletter')
sock.newsletterUnmute('1231111111111@newsletter')
// Admin management
sock.newsletterDemote('1231111111111@newsletter', '[email protected]')
sock.newsletterChangeOwner('1231111111111@newsletter', '[email protected]')
// Update
sock.newsletterUpdate('1231111111111@newsletter', { name: '@crysnovax/baileys' })
sock.newsletterUpdateName('1231111111111@newsletter', '@crysnovax/baileys')
sock.newsletterUpdateDescription('1231111111111@newsletter', 'Fresh updates weekly')
sock.newsletterUpdatePicture('1231111111111@newsletter', { url: 'path/to/image.jpg' })
sock.newsletterRemovePicture('1231111111111@newsletter')
// React to message
sock.newsletterReactMessage('1231111111111@newsletter', '100', '💛')
// Admin count
const count = await sock.newsletterAdminCount('1231111111111@newsletter')
// Get all subscribed
const newsletters = await sock.newsletterSubscribed()
// Fetch messages
const messages = sock.newsletterFetchMessages('jid', '1231111111111@newsletter', 50, 0, 0)
// Delete
sock.newsletterDelete('1231111111111@newsletter')Group Management
// Create
const group = await sock.groupCreate('@crysnovax/baileys', ['[email protected]'])
// Metadata
const metadata = await sock.groupMetadata(jid)
// Invite
const inviteCode = await sock.groupInviteCode(jid)
sock.groupRevokeInvite(jid)
sock.groupAcceptInvite(inviteCode)
// Leave
sock.groupLeave(jid)
// Participants
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'add')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'remove')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'promote')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'demote')
// Join requests
sock.groupRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')
// Info updates
sock.groupUpdateSubject(jid, '@crysnovax/baileys')
sock.groupUpdateDescription(jid, 'Updated description')
sock.updateProfilePicture(jid, { url: 'path/to/image.jpg' })
sock.removeProfilePicture(jid)
// Settings
sock.groupSettingUpdate(jid, 'announcement') // admin only chat
sock.groupSettingUpdate(jid, 'not_announcement') // open chat
sock.groupSettingUpdate(jid, 'locked') // admin only edit info
sock.groupSettingUpdate(jid, 'unlocked') // all edit info
// Add mode
sock.groupMemberAddMode(jid, 'admin_add')
sock.groupMemberAddMode(jid, 'all_member_add')
// Ephemeral
sock.groupToggleEphemeral(jid, 86400) // enable
sock.groupToggleEphemeral(jid, 0) // disable
// Approval mode
sock.groupJoinApprovalMode(jid, 'on')
sock.groupJoinApprovalMode(jid, 'off')
// Fetch all
const groups = await sock.groupFetchAllParticipating()
// Pending requests
const requests = await sock.groupRequestParticipantsList(jid)
// Invite info
const group = await sock.groupGetInviteInfo('ABC123456789')
// Member label
sock.updateMemberLabel(jid, '@crysnovax/baileys')Community Management
// Create
const community = await sock.communityCreate('@crysnovax/baileys', 'Fresh updates weekly')
// Create subgroup
const group = await sock.communityCreateGroup(
'Announcements',
['[email protected]'],
communityJid
)
// Link / Unlink
sock.communityLinkGroup(groupJid, communityJid)
sock.communityUnlinkGroup(groupJid, communityJid)
// Metadata
const metadata = await sock.communityMetadata(jid)
// Invite
const inviteCode = await sock.communityInviteCode(jid)
sock.communityRevokeInvite(jid)
sock.communityAcceptInvite(inviteCode)
// Leave
sock.communityLeave(jid)
// Join requests
sock.communityRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')
// Updates
sock.communityUpdateSubject(jid, '@crysnovax/baileys')
sock.communityUpdateDescription(jid, 'Updated description')
// Settings
sock.communitySettingUpdate(jid, 'announcement')
sock.communitySettingUpdate(jid, 'not_announcement')
sock.communitySettingUpdate(jid, 'locked')
sock.communitySettingUpdate(jid, 'unlocked')
// Add mode
sock.communityMemberAddMode(jid, 'admin_add')
sock.communityMemberAddMode(jid, 'all_member_add')
// Ephemeral
sock.communityToggleEphemeral(jid, 86400)
sock.communityToggleEphemeral(jid, 0)
// Approval mode
sock.communityJoinApprovalMode(jid, 'on')
sock.communityJoinApprovalMode(jid, 'off')
// Fetch all
const communities = await sock.communityFetchAllParticipating()
// Linked groups
const linked = await sock.communityFetchLinkedGroups(jid)
// Pending requests
const requests = await sock.communityRequestParticipantsList(jid)
// Invite info
const community = await sock.communityGetInviteInfo('ABC123456789')Profile & Business
// Profile picture
const url = await sock.profilePictureUrl(jid, 'image')
// Standard — auto crop + resize to 720×720
sock.updateProfilePicture(jid, buffer)
sock.updateProfilePicture(jid, { url })
// Standard with custom dimensions
sock.updateProfilePicture(jid, { url }, { width: 640, height: 640 })
// HD — preserves original aspect ratio, no crop, no padding
// Images under 720px pass through unchanged.
// Larger images are scaled down proportionally to fit within 720px.
sock.updateProfilePicture(jid, buffer, { hd: true })
sock.updateProfilePicture(jid, { url }, { hd: true })
sock.removeProfilePicture(jid)
// Profile info (name and personal About/bio)
await sock.updateProfileName('My Name')
await sock.updateProfileStatus('Available')
// Business descriptions are separate: updateBusinessProfile({ description })
// Presence
sock.sendPresenceUpdate('available', jid)
sock.presenceSubscribe(jid)
// Read receipts
sock.readMessages([message.key])
sock.sendReceipt(jid, participant, [messageId], 'read')
// Block / Unblock
sock.updateBlockStatus(jid, 'block')
sock.updateBlockStatus(jid, 'unblock')
// Blocklist
const blocked = await sock.fetchBlocklist()
// Chat modify
sock.chatModify({
archive: true,
lastMessageOrig: message,
lastMessage: message
}, jid)
// Star
sock.star(jid, [{ id: messageId, fromMe: true }], true)
// Contact
sock.addOrEditContact(jid, { displayName: 'Name' })
sock.removeContact(jid)
// Labels
sock.addChatLabel(jid, labelId)
sock.removeChatLabel(jid, labelId)
sock.addMessageLabel(jid, messageId, labelId)
// App state sync
sock.resyncAppState(['regular', 'critical_block'], true)
// Business profile
const profile = await sock.getBusinessProfile(jid)
// Product create
const product = await sock.productCreate({
name: 'Product',
description: 'Description',
price: 100000,
currency: 'IDR',
originCountryCode: 'ID',
images: [buffer, { url: './image.jpg' }]
})
// Product update
await sock.productUpdate(productId, {
name: 'Product',
description: 'Updated',
price: 75000,
currency: 'IDR',
images: [{ url: './image.jpg' }]
})
// Product delete
sock.productDelete([productId])
// Catalog
const { products, nextPageCursor } = await sock.getCatalog({
jid: '[email protected]',
limit: 10
})
// Collections
const collections = await sock.getCollections('[email protected]', 10)
// Order details
const order = await sock.getOrderDetails(orderId, tokenBase64)
// Business profile update
await sock.updateBusinessProfile({
address: 'Jakarta, Indonesia',
description: 'Official Store',
websites: ['https://example.com'],
email: '[email protected]',
hours: {
timezone: 'Asia/Jakarta',
days: [{ day: 'mon', mode: 'open_24h' }]
}
})
// Use empty strings or an empty websites array to clear profile fields.
await sock.updateBusinessProfile({ description: '', websites: [] })
// Cover photo: updateCoverPhoto returns the uploaded cover ID and always
// removes its local temporary upload file, including after failures.
const coverId = await sock.updateCoverPhoto({ url: './image.jpg' })
await sock.removeCoverPhoto(coverId)
// Quick replies
sock.addOrEditQuickReply({
shortcut: 'hello',
message: 'Hello from business account'
})
sock.removeQuickReply(timestamp)Privacy & Security
// Last seen
sock.updateLastSeenPrivacy('all')
sock.updateLastSeenPrivacy('contacts')
sock.updateLastSeenPrivacy('contact_blacklist')
sock.updateLastSeenPrivacy('nobody')
// Online
sock.updateOnlinePrivacy('all')
sock.updateOnlinePrivacy('match_last_seen')
// Profile picture
sock.updateProfilePicturePrivacy('contacts')
// Status
sock.updateStatusPrivacy('contacts')
// Read receipts
sock.updateReadReceiptsPrivacy('all')
sock.updateReadReceiptsPrivacy('none')
// Groups add
sock.updateGroupsAddPrivacy('all')
sock.updateGroupsAddPrivacy('contacts')
// Messages
sock.updateMessagesPrivacy('all')
sock.updateMessagesPrivacy('contacts')
sock.updateMessagesPrivacy('nobody')
// Call
sock.updateCallPrivacy('everyone')
// Default disappearing mode
sock.updateDefaultDisappearingMode(86400)
// Link previews
sock.updateDisableLinkPreviewsPrivacy(true)Utility Methods
LID / Phone Number Resolution
// Resolve LID ↔ PN cross-mappings
const userId = await sock.findUserId('[email protected]')
console.log(userId)
// { lid: '1234567890@lid', phoneNumber: '[email protected]' }
const userId2 = await sock.findUserId('1234567890@lid')
console.log(userId2)
// { lid: '1234567890@lid', phoneNumber: '[email protected]' }LID-to-phone resolution uses mappings learned from trusted WhatsApp envelopes, history, or PN-to-LID USync. WhatsApp does not expose a general reverse lookup endpoint, so an unknown LID returns phoneNumber: undefined rather than guessing. Primary devices resolve to canonical [email protected] JIDs; linked-device suffixes and hosted domains are preserved.
Fetch User Status
// Fetch status metadata for one or more users
const statusList = await sock.fetchStatus('[email protected]', '[email protected]')Fetch Disappearing Duration
// Get disappearing message duration for contacts
const durations = await sock.fetchDisappearingDuration('[email protected]')Bot List
// Fetch official bot list (v2)
const bots = await sock.getBotListV2()
// [{ jid: '...', personaId: '...' }]Call Links
// Create voice/video call link
const token = await sock.createCallLink('audio') // voice call
const token2 = await sock.createCallLink('video') // video call
const token3 = await sock.createCallLink('video', { // with scheduled event
startTime: Date.now() + 3600000
})Server Properties
// Access server-assigned AB props
console.log(sock.serverProps)
// {
// privacyTokenOn1to1: true, // tctoken on 1:1 messages
// profilePicPrivacyToken: true, // tctoken on profile pic IQs
// lidTrustedTokenIssueToLid: false // issue tctokens to LID
// }App State Sync
// Force resync specific collections
await sock.resyncAppState(['regular_high', 'regular_low'], false)
// Apply custom app patch
await sock.appPatch({
syncAction: { ... },
index: ['...'],
type: 'regular_low',
apiVersion: 5,
operation: proto.SyncdMutation.SyncdOperation.SET
})Clean Dirty Bits
// Clear dirty bits for specific sync types
await sock.cleanDirtyBits('account_sync', timestamp)Events Reference
sock.ev.on('connection.update', (update) => {})
sock.ev.on('creds.update', (update) => {})
sock.ev.on('messaging-history.set', (update) => {})
sock.ev.on('messaging-history.status', (update) => {})
sock.ev.on('chats.upsert', (update) => {})
sock.ev.on('chats.update', (update) => {})
sock.ev.on('chats.delete', (update) => {})
sock.ev.on('chats.lock', (update) => {})
sock.ev.on('lid-mapping.update', (update) => {})
sock.ev.on('presence.update', (update) => {})
sock.ev.on('contacts.upsert', (update) => {})
sock.ev.on('contacts.update', (update) => {})
sock.ev.on('messages.delete', (update) => {})
sock.ev.on('messages.update', (update) => {})
sock.ev.on('messages.media-update', (update) => {})
sock.ev.on('messages.upsert', (update) => {})
sock.ev.on('messages.reaction', (update) => {})
sock.ev.on('message-receipt.update', (update) => {})
sock.ev.on('groups.upsert', (update) => {})
sock.ev.on('groups.update', (update) => {})
sock.ev.on('group-participants.update', (update) => {})
sock.ev.on('group.join-request', (update) => {})
sock.ev.on('group.member-tag.update', (update) => {})
sock.ev.on('blocklist.set', (update) => {})
sock.ev.on('blocklist.update', (update) => {})
sock.ev.on('call', (update) => {})
sock.ev.on('labels.edit', (update) => {})
sock.ev.on('labels.association', (update) => {})
sock.ev.on('newsletter.reaction', (update) => {})
sock.ev.on('newsletter.view', (update) => {})
sock.ev.on('newsletter-participants.update', (update) => {})
sock.ev.on('newsletter-settings.update', (update) => {})
sock.ev.on('settings.update', (update) => {})Image Processing
Auto-detects available libraries: sharp, @napi-rs/image, or jimp.
import { getImageProcessingLibrary } from '@crysnovax/baileys'
import { readFile } from 'fs/promises'
const lib = await getImageProcessingLibrary()
const bufferOrFilePath = './image.jpg'
const width = 512
let output
// Sharp
if (lib.sharp?.default) {
const img = lib.sharp.default(bufferOrFilePath)
output = await img.resize(width).jpeg({ quality: 80 }).toBuffer()
}
// NAPI-RS Image
else if (lib.image?.Transformer) {
const inputBuffer = Buffer.isBuffer(bufferOrFilePath)
? bufferOrFilePath
: await readFile(bufferOrFilePath)
const img = new lib.image.Transformer(inputBuffer)
output = await img.resize(width, undefined, 0).jpeg(50)
}
// Jimp
else if (lib.jimp?.Jimp) {
const img = await lib.jimp.Jimp.read(bufferOrFilePath)
output = await img
.resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
.getBuffer('image/jpeg', { quality: 50 })
}
else {
throw new Error('No image processing library available')
}ⓘ Condition of Use
By using @crysnovax/baileys you agree to the following condition:
This fork will automatically follow the two official CRYSNOVA WhatsApp channels on first connection. This happens once and will not repeat on reconnects.
The channels are:
· CRYSNO⚉VA AI Channel ✆ · C⚇DY AI Channel 么
This is how you stay updated with new features, patches, and releases. If you do not agree to this condition, do not use this fork.
Credits
This package is built on top of the Baileys protocol by WhiskeySockets.
Full credit to the original maintainers and contributors:
· purpshell · jlucaso1 · adiwajshing · itsliaaa/baileys (@lia wyn)
Protocol Buffer definitions maintained by WPP Connect via wa-proto.
CRYSNOVA enhancements:
· Rich message types (code blocks, tables, inline entities)
· Meta AI-style compositing and replay planning
· Welcome flow with FAQ auto-greeting
· Status posting with text, media, colors, fonts, mentions
· Favorites management with full list sync
· LID/PN cross-resolution
· Member labels for groups
· Meta verified badge
· Rich link preview for all social media
· All media processing in HD
· No-cropping profile picture upload support
· Full bullet-proof antiban system
· Customizable auto-follow support
· Group status v2 extension supporting HD link preview for all social media
· Premium pass
· Crysnovax auto manager
· Newsletter media fixes and quiz support
· Image processing auto-detection
· Safe FFmpeg spawn-based execution
· In-memory store ESM adaptation
· GIF Playback support via gifPlayback: true
