π Changelog / Updates (v2.0.2)
1. Fix additionalNodes Conflict (v2.0.2)
- Fixed an issue where supplying a custom
<biz> node in additionalNodes alongside interactive messages caused duplicate <biz> nodes in the stanza, which led to the message being silently rejected by WhatsApp.
2. Built-in VoIP (Voice Call) Engine (v9.0.0)
- Full integration of the WebRTC and WASM engines to enable native WhatsApp voice calling, eliminating the need for a separate package.
2. Fix View Once Error (Anti-Crash)
- WhatsApp (Meta) Web API no longer receives media content for View Once messages, which previously caused the socket to crash. We've added robust parsing (
viewOnceMessageV3 & ptvMessage) to gracefully handle these empty stubs without crashing your bot.
2. Decryption Retry Fix (Private Chats)
- Fixed an issue in
messages-recv.js where "Bad MAC" / decryption errors would close the connection. Combined with a proper getMessage hook in your main connection setup, the bot now seamlessly retries failed decryptions.
- Browser parameter spoofing upgraded to MacOS to prevent Error 405 (Method Not Allowed) during pairing.
3. AI Rich Implementation (ORich)
-
Added the ORich class (an extension of AIRich from message_builder.js) to generate stunning AI response layouts easily.
-
Example Usage:
import { ORich } from 'lunaaa-baileys';
const luna = new ORich(sock)
.addText('Hello from ORich AI!')
.addSuggest(['Tell me more', 'Next']);
await luna.send(jid);
Table of Contents
β¨ Features
| VoIP Voice Calls | Native VoIP engine (WASM/WebRTC) via VoipClient, single shared socket | β
|
| Interactive Messages | Native flow buttons, list/select menus, button wrappers, carousel support via Dugong | β
|
| Album Messages | Multi-image/video album relay with count metadata and grouped delivery | β
|
| Rich Response Helpers | sendTable, sendList, sendCodeBlock, sendRichMessage, sendUnifiedResponse | β
|
| Unified Response V2 | sendTableV2, sendCodeBlockV2, sendLinkV2 with Meta-AI-style unified sections | β
|
| Link Messages | Rich inline links with citations, proofs, and forwarded bot context | β
|
| Latex Rendering Hooks | sendLatex, sendLatexImage, sendLatexInlineImage helper APIs | β
|
| Payment Messages | Request payment messages with note/sticker support | β
|
| Product / Catalog | Business product messages, catalog fetch/create/update/delete, cover photo helpers | β
|
| Event / Poll Result | Event message builders and poll result snapshots | β
|
| Newsletter Extras | URL resolve, metadata fetch, create/update, bulk follow, admin utilities, live updates | β
|
| Communities & Groups | Community CRUD, linked groups, invite workflows, join approval, labels | β
|
| Status Mention | sendStatusMention() helper to mention users/groups in status flows | β
|
| LID & Session Handling | LIDβPN mapping, session migration, retry/session recreation helpers | β
|
| Build Output Ready | Published runtime in lib/ and protobuf artifacts in WAProto/ | β
|
| TypeScript Declarations | Included .d.ts files for exported APIs and socket methods | β
|
β
Requirements
- Node.js
>= 20.0.0
- ESM project (
"type": "module" in your package.json)
- A persistent auth store implementation for production use
- Optional helpers depending on your use case:
pino for logging
qrcode-terminal if you want to render QR codes in the terminal yourself
sharp, jimp, audio-decode, link-preview-js for optional media/rich utilities
[!IMPORTANT]
lunaaa-baileys is ESM-only. CommonJS projects will need migration or dynamic import wrappers.
π Whatβs Inside This Repo
This package is not the TypeScript source workspace. It contains the compiled distributable files you actually publish/use.
lib/ | Main compiled runtime, types, socket layers, utilities |
WAProto/ | Generated protobuf runtime and typings |
package.json | Published package metadata, dependencies, build script |
tsconfig.json | TypeScript config used for authoring/build pipeline |
tsconfig.build.json | Build-specific TS emit config targeting lib/ |
proto-extract/ | Protobuf-related extraction/build assets |
If you are documenting or integrating this project, treat lib/index.js and lib/index.d.ts as the primary public surface.
π¦ Installation
From NPM
npm install lunaaa-baileys
Recommended companion packages for examples in this README:
npm install pino qrcode-terminal
Using Alias (Recommended for Luna-MD Bots)
Add to package.json:
{
"dependencies": {
"luna": "npm:lunaaa-baileys@latest"
}
}
Then run:
npm install
[!TIP]
Using the alias luna allows you to import luna from 'luna' without changing existing code.
[!NOTE]
Peer dependencies such as sharp, jimp, audio-decode, and link-preview-js are optional. Install only what your bot features need.
π οΈ Quick Start
1. Project Setup
mkdir my-bot && cd my-bot
npm init -y
Ensure package.json has "type": "module":
{
"name": "my-bot",
"type": "module",
"dependencies": {
"luna": "npm:lunaaa-baileys@latest"
}
}
[!IMPORTANT]
Required: "type": "module" β lunaaa-baileys is ESM-only. Without this, imports will fail.
2. Install Dependencies
npm install lunaaa-baileys pino qrcode-terminal
3. Create index.js
import makeWASocket, {
useMultiFileAuthState,
DisconnectReason,
Browsers,
} from "lunaaa-baileys";
import pino from "pino";
import qrcode from "qrcode-terminal";
const logger = pino({ level: "silent" });
async function startBot() {
const { state, saveCreds } = await useMultiFileAuthState("./session");
const sock = makeWASocket({
auth: state,
logger,
browser: Browsers.ubuntu("Chrome"),
syncFullHistory: false,
});
sock.ev.on("creds.update", saveCreds);
sock.ev.on("connection.update", ({ connection, lastDisconnect, qr }) => {
if (qr) {
qrcode.generate(qr, { small: true });
}
if (connection === "close") {
const shouldReconnect =
lastDisconnect?.error?.output?.statusCode !==
DisconnectReason.loggedOut;
if (shouldReconnect) startBot();
}
if (connection === "open") console.log("Connected!");
});
sock.ev.on("messages.upsert", async ({ messages }) => {
const msg = messages[0];
if (!msg?.message || msg.key.fromMe) return;
const text =
msg.message.conversation || msg.message.extendedTextMessage?.text || "";
if (text === ".ping") {
await sock.sendMessage(msg.key.remoteJid, { text: "Pong!" });
}
});
}
startBot();
4. Run
node index.js
Scan the QR code, wait until the socket reaches connection: "open", then send .ping to test.
5. Pairing Code Example
If you prefer pairing code over QR:
const sock = makeWASocket({
auth: state,
logger,
browser: Browsers.windows("Chrome"),
});
sock.ev.on("connection.update", async ({ connection }) => {
if (connection === "connecting") {
const code = await sock.requestPairingCode("6283874851613");
console.log("Pairing code:", code);
}
});
You can also provide your own custom pairing code:
const code = await sock.requestPairingCode("6283874851613", "LUNABAIL");
console.log("Custom pairing code:", code);
Custom pairing codes must be exactly 8 characters.
[!WARNING]
printQRInTerminal still exists in the type surface for compatibility, but it is deprecated in the current build. Prefer handling the qr value from connection.update yourself.
ποΈ Architecture
Socket Layer Chain
Each socket layer extends the previous via composition, adding functionality from bottom to top:
makeSocket β WebSocket + Noise protocol + pre-key management
makeChatsSocket β App state sync + chat modifications + profile
makeGroupsSocket β Group CRUD + participant management
makeMessagesSendSocket β Message sending + rich messages + relay
makeMessagesRecvSocket β Message receiving + decryption + notifications
makeBusinessSocket β Product catalog + business profile
makeCommunitiesSocket β Community CRUD + sub-groups
makeNewsletterSocket β Newsletter follow/unfollow/metadata
makeWASocket β TOP LEVEL EXPORT
Key Modules
| Socket | lib/Socket/ | Connection, messaging, groups, newsletter, business, communities |
| Signal | lib/Signal/ | Signal protocol: 1:1 encryption, group SenderKey, LID mapping |
| WABinary | lib/WABinary/ | Binary node encoding/decoding, JID utilities |
| Utils | lib/Utils/ | Crypto, auth state, noise handler, rich messages, media |
| WAProto | WAProto/ | Compiled protobuf encode/decode |
| WAUSync | lib/WAUSync/ | User sync protocol (contact, device, LID) |
| WAM | lib/WAM/ | WhatsApp analytics binary encoding |
| Types | lib/Types/ | Full TypeScript type definitions |
Build Repo Layout
This repository does not currently expose a src/ directory in the checked-in package tree.
That means:
- Documentation should reference
lib/ as the public implementation
- API discovery is best done through:
lib/index.js
lib/index.d.ts
lib/Socket/*.d.ts
lib/Utils/*.d.ts
- Consumers should treat this repo as a ready-to-install runtime artifact, not the authoring source tree
Connection Flow
- WebSocket connect β Noise XX handshake (Curve25519 + AES-256-GCM)
- QR code or Pairing Code authentication
CB:success β upload pre-keys β send passive IQ β emit connection: 'open'
- Store own LIDβPN mapping, migrate own session
- Buffer events β process offline notifications β flush β
receivedPendingNotifications: true
π‘ Socket API Reference
Connection & Authentication
const sock = makeWASocket({
auth: state,
logger,
browser: Browsers.ubuntu("Chrome"),
version: [2, 3000, 1035194821],
connectTimeoutMs: 20_000,
defaultQueryTimeoutMs: 60_000,
retryRequestDelayMs: 250,
maxMsgRetryCount: 5,
enableRecentMessageCache: true,
enableAutoSessionRecreation: true,
syncFullHistory: false,
shouldIgnoreJid: (jid) => false,
qrTimeout: 60_000,
generateHighQualityLinkPreview: false,
});
Pairing Code Authentication:
const sock = makeWASocket({
auth: state,
logger,
browser: Browsers.windows("Chrome"),
});
sock.ev.on("connection.update", async ({ connection, qr, isNewLogin }) => {
if (qr) {
}
});
const code = await sock.requestPairingCode("6283874851613");
console.log("Pairing code:", code);
Authentication State:
import { useMultiFileAuthState } from "lunaaa-baileys";
const { state, saveCreds } = await useMultiFileAuthState("./session");
Custom Data Store / Memory Store Pattern:
import {
BufferJSON,
initAuthCreds,
makeCacheableSignalKeyStore,
} from "lunaaa-baileys";
const credsStore = new Map();
const keyStoreData = new Map();
const creds = credsStore.get("creds") || initAuthCreds();
const keys = makeCacheableSignalKeyStore({
get: async (type, ids) => {
const data = {};
for (const id of ids) {
data[id] = keyStoreData.get(`${type}:${id}`);
}
return data;
},
set: async (data) => {
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const key = `${category}:${id}`;
if (value == null) keyStoreData.delete(key);
else keyStoreData.set(key, value);
}
}
},
});
const state = { creds, keys };
const saveCreds = async () => {
credsStore.set(
"creds",
JSON.parse(
JSON.stringify(state.creds, BufferJSON.replacer),
BufferJSON.reviver,
),
);
};
This package does not currently ship a built-in makeInMemoryStore() helper.
Use these patterns instead:
useMultiFileAuthState() for bot/local usage
initAuthCreds() + custom SignalKeyStore for DB/Redis/in-memory persistence
makeCacheableSignalKeyStore() when you want faster repeated key access with caching
[!WARNING]
useMultiFileAuthState() is convenient for bots and local testing, but the package itself warns against using it as your long-term production persistence layer. For production, write your own SQL/NoSQL-backed auth store.
Useful companion helpers:
import {
Browsers,
DisconnectReason,
fetchLatestBaileysVersion,
makeCacheableSignalKeyStore,
} from "lunaaa-baileys";
const { version, isLatest } = await fetchLatestBaileysVersion();
console.log(version, isLatest);
VoIP (Voice Calls)
Modul VoIP kini terintegrasi langsung di lunaaa-baileys menggunakan VoipClient dan memanfaatkan shared socket (tanpa double pairing). Aset WebAssembly sudah built-in.
Inisiasi VoIP Client:
import { VoipClient, CallState } from "lunaaa-baileys";
if (!global.voipClient) {
global.voipClient = new VoipClient();
await global.voipClient.connectWithSocket(sock);
console.log("VoIP engine aktif!");
}
Melakukan Panggilan:
try {
const call = await global.voipClient.call("6281234567890", {
durationMs: 60000,
audioSource: "silence",
});
call.on("ringing", () => console.log("Berdering..."));
call.on("connected", () => console.log("Terhubung!"));
call.on("ended", (reason) => console.log("Panggilan berakhir:", reason));
await call.waitForEnd();
} catch (e) {
console.error("Gagal menelpon:", e);
}
Message Sending
await sock.sendMessage(jid, { text: "Hello!" });
await sock.sendMessage(jid, {
text: "Hello @628xxx!",
mentions: ["628xxx@s.whatsapp.net"],
});
await sock.sendMessage(jid, {
image: { url: "./photo.jpg" },
caption: "A photo",
});
await sock.sendMessage(jid, {
video: { url: "./video.mp4" },
caption: "A video",
gifPlayback: false,
});
await sock.sendMessage(jid, {
audio: { url: "./audio.ogg" },
mimetype: "audio/ogg; codecs=opus",
ptt: true,
});
await sock.sendMessage(jid, {
sticker: { url: "./sticker.webp" },
});
await sock.sendMessage(
jid,
makeStickerPack({
name: "luna Starter Pack",
publisher: "luna",
description: "Starter sticker pack",
stickers: [
{ url: "./stickers/1.webp", emojis: ["π₯"] },
{ url: "./stickers/2.webp", emojis: ["π"] },
],
cover: { url: "./stickers/cover.webp" },
}),
);
await sock.sendMessage(jid, {
document: { url: "./file.pdf" },
fileName: "document.pdf",
mimetype: "application/pdf",
});
await sock.sendMessage(jid, {
react: { key: msg.key, text: "π" },
});
await sock.sendMessage(jid, {
location: {
degreesLatitude: -6.2,
degreesLongitude: 106.8,
name: "Jakarta",
},
});
await sock.sendMessage(jid, {
contacts: {
displayName: "Contacts",
contacts: [
{ vcard: "BEGIN:VCARD\nVERSION:3.0\nFN:John\nTEL:+628xxx\nEND:VCARD" },
],
},
});
await sock.sendMessage(jid, {
poll: {
name: "Vote!",
values: ["Option A", "Option B", "Option C"],
selectableCount: 1,
},
});
await sock.sendMessage(jid, {
forward: msg,
forwardingScore: 1,
isForwarded: true,
});
await sock.sendMessage(jid, { delete: msg.key });
await sock.sendMessage(jid, {
text: "Edited text",
edit: msg.key,
});
Interactive Messages
Native Flow Buttons:
await sock.sendMessage(jid, {
interactiveMessage: {
title: "Welcome!",
footer: "Powered by luna",
buttons: [
{
name: "quick_reply",
buttonParamsJson: JSON.stringify({
display_text: "Menu",
id: "menu",
}),
},
{
name: "cta_url",
buttonParamsJson: JSON.stringify({
display_text: "Website",
url: "https://example.com",
}),
},
{
name: "cta_copy",
buttonParamsJson: JSON.stringify({
display_text: "Copy Code",
copy_code: "LUNA2026",
}),
},
],
header: "Choose an option",
image: { url: "https://example.com/banner.jpg" },
},
});
List Menu (single_select):
await sock.sendMessage(jid, {
interactiveMessage: {
title: "Select Category",
footer: "Powered by luna",
buttons: [
{
name: "single_select",
buttonParamsJson: JSON.stringify({
title: "Menu",
sections: [
{
title: "Games",
rows: [
{ title: "Quiz", id: ".quiz" },
{ title: "Tebak Gambar", id: ".tebakgambar" },
],
},
{
title: "Tools",
rows: [
{ title: "Sticker", id: ".sticker" },
{ title: "TTS", id: ".tts" },
],
},
],
}),
},
],
header: "Bot Menu",
},
});
Album Messages
await sock.sendMessage(jid, {
albumMessage: [
{ image: { url: "./photo1.jpg" }, caption: "First" },
{ image: { url: "./photo2.jpg" }, caption: "Second" },
{ video: { url: "./clip.mp4" }, caption: "Video" },
],
});
[!NOTE]
Albums automatically set expectedImageCount and expectedVideoCount based on array content.
AI Rich Response Messages
Send messages styled like Meta AI β tables, code blocks, and rich text. All rendered via botForwardedMessage > richResponseMessage.
Table (with heading):
await sock.sendTable(
jid,
"Java vs JavaScript",
["Feature", "Java", "JavaScript"],
[
["Type", "Compiled", "Interpreted"],
["Typing", "Static", "Dynamic"],
["Main Use", "Enterprise", "Web, Full-stack"],
],
quoted,
{
headerText: "Comparison:",
footer: "Hope this helps!",
},
);
List (without heading):
await sock.sendList(
jid,
"Bot Info",
[
["Name", "Luna-MD"],
["Version", "2.0.0"],
["Developer", "noxXza.exe"],
],
quoted,
{ footer: "Β© Luna-MD" },
);
Code Block V1 (basic syntax highlighting):
await sock.sendCodeBlock(
jid,
`const greeting = "Hello World"
function sayHello(name) {
return greeting + " " + name
}
sayHello("luna")`,
quoted,
{
language: "javascript",
title: "Example Code",
footer: "Powered by Luna-MD",
},
);
Supported V1 languages: javascript, typescript, python
Table V2 (Unified Response):
The V2 table uses the unified response format with GenATableUXPrimitive typename and base64-encoded data, matching the Meta AI rich response protocol.
Features over V1:
- String-based table input with flexible delimiters (
| or , for columns, ;; for rows)
- Unified response sections with
GenATableUXPrimitive / GenAIMarkdownTextUXPrimitive typenames
- Dual output:
rows (for submessages) + unified_rows (for sections)
- Enhanced
contextInfo with botMessageSharingInfo
await sock.sendTableV2(
jid,
[
"Java vs JavaScript",
"Feature | Java | JavaScript",
"Type | Compiled | Interpreted;;Typing | Static | Dynamic;;Main Use | Enterprise | Web, Full-stack",
],
quoted,
{
headerText: "Comparison:",
text: "Here is a comparison table:",
footer: "Hope this helps!",
},
);
Input format:
table[0] β title string
table[1] β header row (columns separated by | or ,)
table[2+] β data rows (rows separated by ;;, columns by | or ,)
V2 Options:
title | string | β | Title (used if headerText not set) |
headerText | string | β | Text shown before the table |
text | string | β | Markdown text section (GenAIMarkdownTextUXPrimitive) |
footer | string | β | Text shown after the table |
Code Block V2 (Unified Response)
The V2 code block uses the unified response format with sections/view_model/primitive structure and base64-encoded data, matching the Meta AI rich response protocol exactly.
Features over V1:
- 6 language families with keyword detection
/* */ block comment support
# comment support for python/bash/lua
- Hex/binary/octal number literals
- Dual token output: numeric
codeBlock + string-typed unified_codeBlock
- Unified response sections with
GenAICodeUXPrimitive / GenAIMarkdownTextUXPrimitive typenames
- Enhanced
contextInfo with botMessageSharingInfo
await sock.sendCodeBlockV2(
jid,
`package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}`,
quoted,
{
language: "go",
title: "Go Example",
text: "Here is a Go code snippet:",
footer: "Powered by Luna-MD",
},
);
Supported V2 languages:
javascript | js, typescript, ts |
python | py |
go | golang |
lua | β |
bash | sh, shell |
V2 Options:
language | string | 'javascript' | Programming language for syntax highlighting |
title | string | β | Title text shown before the code block |
text | string | β | Markdown text section (GenAIMarkdownTextUXPrimitive) |
footer | string | β | Footer text shown after the code block |
V2 Token Types:
| 0 | DEFAULT | DEFAULT | Normal text, whitespace, operators |
| 1 | KEYWORD | KEYWORD | Language keywords (const, func, if, etc.) |
| 2 | METHOD | METHOD | Function/method calls (identifier followed by () |
| 3 | STRING | STR | String literals ("...", '...', `...`) |
| 4 | NUMBER | NUMBER | Numeric values (int, float, hex, binary, octal) |
| 5 | COMMENT | COMMENT | Comments (//, /* */, #) |
Link Message (Inline Embeds)
Send rich text with inline clickable links using {{IE_N}}display_text{{/IE_N}} placeholders, with optional citation metadata and verification proofs.
await sock.sendLink(
jid,
"Upload results:\nβ
Freeimage\nπ Klik: {{IE_0}}link disini{{/IE_0}}\nβ
Yardsansh\nπ Klik: {{IE_1}}link disini{{/IE_1}}",
["https://example.com/upload1", "https://example.com/upload2"],
quoted,
{
headerText: "π Media Uploader V3",
footer: "β¨ Selesai!",
botJid: "867051314767696@bot",
forwardingScore: 3,
citations: [
{
sourceTitle: "Freeimage",
citationNumber: 1,
faviconCdnUrl: "https://cdn.example.com/favicon.ico",
},
{ sourceTitle: "Yardsansh", citationNumber: 2 },
],
proofs: [
{
version: 1,
useCase: 1,
signature: "base64signature==",
certificateChain: ["base64cert1", "base64cert2"],
},
],
},
);
Link Options:
headerText | string | β | Text shown before the link content |
footer | string | β | Text shown after the link content |
botJid | string | '867051314767696@bot' | Bot JID for forwardedAiBotMessageInfo |
forwardingScore | number | 3 | Forward score for context info |
citations | Citation[] | [] | Citation metadata entries |
proofs | Proof[] | [] | Verification proof entries |
Citation object:
sourceQuery | string | Source query text |
faviconCdnUrl | string | CDN URL for favicon |
citationNumber | number | Citation index (auto-incremented if omitted) |
sourceTitle | string | Title of the source |
Proof object:
version | number | Proof version |
useCase | number | Use case identifier |
signature | string | Base64 signature |
certificateChain | string[] | Base64 certificate chain |
Link V2 (search-result style):
await sock.sendLinkV2(
jid,
"Search results:\n- {{IE_0}}Official docs{{/IE_0}}\n- {{IE_1}}GitHub repo{{/IE_1}}",
[
{
url: "https://www.npmjs.com/package/lunaaa-baileys",
displayName: "Official docs",
sourceDisplayName: "npm",
sourceSubtitle: "package registry",
},
{
url: "https://github.com/noxXza/lunaaa-baileys",
displayName: "GitHub repo",
sourceDisplayName: "github",
sourceSubtitle: "source hosting",
},
],
quoted,
{
headerText: "lunaaa-baileys",
footer: "Reference links",
searchEngine: "MAME",
},
);
Other Message Types
Payment Request:
await sock.sendMessage(jid, {
requestPaymentMessage: {
amount: 50000,
currency: "IDR",
note: "Payment for order #123",
from: "628xxx@s.whatsapp.net",
},
});
Event:
await sock.sendMessage(jid, {
eventMessage: {
name: "Community Meetup",
description: "Join us for the monthly meetup!",
startTime: Date.now() + 86400000,
endTime: Date.now() + 90000000,
location: {
name: "Jakarta",
degreesLatitude: -6.2,
degreesLongitude: 106.8,
},
},
});
Poll Result:
await sock.sendMessage(jid, {
pollResultMessage: {
name: "Favorite Color?",
pollVotes: [
{ optionName: "Red", optionVoteCount: 42 },
{ optionName: "Blue", optionVoteCount: 38 },
{ optionName: "Green", optionVoteCount: 25 },
],
},
});
Status with Mentions:
await sock.sendStatusMention({ text: "Big update coming!" }, [
"628xxx@s.whatsapp.net",
"groupid@g.us",
]);
Product Message:
await sock.sendMessage(jid, {
productMessage: {
title: "Wireless Headphones",
description: "High quality bluetooth headphones",
productId: "WH-001",
retailerId: "luna-shop",
url: "https://example.com/product",
priceAmount1000: 299000,
currencyCode: "IDR",
thumbnail: { url: "https://example.com/product.jpg" },
body: "Check out this product!",
footer: "luna Shop",
buttons: [
{
name: "quick_reply",
buttonParamsJson: JSON.stringify({
display_text: "Buy Now",
id: "buy_wh001",
}),
},
],
},
});
Mixed Rich Message (custom submessages):
await sock.sendRichMessage(
jid,
[
{ messageType: 2, messageText: "Here is some info:" },
{
messageType: 4,
tableMetadata: {
title: "Stats",
rows: [
{ items: ["Metric", "Value"], isHeading: true },
{ items: ["Users", "1000"] },
{ items: ["Uptime", "99.9%"] },
],
},
},
{ messageType: 2, messageText: "And some code:" },
{
messageType: 5,
codeMetadata: {
codeLanguage: "javascript",
codeBlocks: [{ highlightType: 0, codeContent: 'console.log("OK")' }],
},
},
],
quoted,
);
SubMessage Types:
| 0 | UNKNOWN | β |
| 1 | GRID_IMAGE | gridImageMetadata |
| 2 | TEXT | messageText |
| 3 | INLINE_IMAGE | imageMetadata |
| 4 | TABLE | tableMetadata |
| 5 | CODE | codeMetadata |
| 6 | DYNAMIC | dynamicMetadata |
| 7 | MAP | mapMetadata |
| 8 | LATEX | latexMetadata |
| 9 | CONTENT_ITEMS | contentItemsMetadata |
Newsletter Methods
const info = await sock.cekIDSaluran("https://whatsapp.com/channel/xxxxx");
console.log(info.name, info.subscribers);
await sock.newsletterMultipleFollow("id1@newsletter id2@newsletter");
const channels = await sock.newsletterFetchAllSubscribe();
await sock.newsletterAction("id@newsletter", "follow");
const nl = await sock.newsletterCreate("My Channel", "Description");
await sock.newsletterUpdate("id@newsletter", { name: "New Name" });
const { subscribers } = await sock.newsletterSubscribers("id@newsletter");
const meta = await sock.newsletterMetadata("jid", "id@newsletter");
await sock.newsletterFollow("id@newsletter");
await sock.newsletterUnfollow("id@newsletter");
await sock.newsletterMute("id@newsletter");
await sock.newsletterUnmute("id@newsletter");
await sock.newsletterUpdateName("id@newsletter", "New Name");
await sock.newsletterUpdateDescription("id@newsletter", "New Desc");
await sock.newsletterUpdatePicture("id@newsletter", mediaUpload);
await sock.newsletterRemovePicture("id@newsletter");
await sock.newsletterReactMessage("id@newsletter", serverId, "π");
const msgs = await sock.newsletterFetchMessages("id@newsletter", 50, 0, 0);
const count = await sock.newsletterAdminCount("id@newsletter");
await sock.newsletterChangeOwner("id@newsletter", newOwnerJid);
await sock.newsletterDemote("id@newsletter", userJid);
await sock.newsletterDelete("id@newsletter");
Group Methods
const meta = await sock.groupMetadata("id@g.us");
const group = await sock.groupCreate("My Group", ["628xxx@s.whatsapp.net"]);
await sock.groupLeave("id@g.us");
await sock.groupUpdateSubject("id@g.us", "New Subject");
await sock.groupUpdateDescription("id@g.us", "New Description");
const result = await sock.groupParticipantsUpdate(
"id@g.us",
["628xxx@s.whatsapp.net"],
"add",
);
const requests = await sock.groupRequestParticipantsList("id@g.us");
await sock.groupRequestParticipantsUpdate(
"id@g.us",
["628xxx@s.whatsapp.net"],
"approve",
);
const code = await sock.groupInviteCode("id@g.us");
await sock.groupRevokeInvite("id@g.us");
await sock.groupAcceptInvite("ABCDE12345");
const info = await sock.groupGetInviteInfo("ABCDE12345");
await sock.groupSettingUpdate("id@g.us", "announcement");
await sock.groupSettingUpdate("id@g.us", "locked");
await sock.groupMemberAddMode("id@g.us", "admin_add");
await sock.groupJoinApprovalMode("id@g.us", "on");
await sock.groupToggleEphemeral("id@g.us", 86400);
const groups = await sock.groupFetchAllParticipating();
await sock.updateMemberLabel("id@g.us", "VIP");
const meta = await sock.communityMetadata("communityid@g.us");
const community = await sock.communityCreate(
"Our Community",
"Community description",
);
const subgroup = await sock.communityCreateGroup(
"Announcements",
["628xxx@s.whatsapp.net"],
"communityid@g.us",
);
await sock.communityLinkGroup("groupid@g.us", "communityid@g.us");
await sock.communityUnlinkGroup("groupid@g.us", "communityid@g.us");
const linked = await sock.communityFetchLinkedGroups("communityid@g.us");
const code = await sock.communityInviteCode("communityid@g.us");
await sock.communityRevokeInvite("communityid@g.us");
await sock.communityAcceptInvite(code);
const inviteInfo = await sock.communityGetInviteInfo(code);
const requests =
await sock.communityRequestParticipantsList("communityid@g.us");
await sock.communityRequestParticipantsUpdate(
"communityid@g.us",
["628xxx@s.whatsapp.net"],
"approve",
);
await sock.communityParticipantsUpdate(
"communityid@g.us",
["628xxx@s.whatsapp.net"],
"add",
);
await sock.communityUpdateSubject("communityid@g.us", "New Subject");
await sock.communityUpdateDescription("communityid@g.us", "New Description");
await sock.communityToggleEphemeral("communityid@g.us", 86400);
await sock.communitySettingUpdate("communityid@g.us", "announcement");
await sock.communityMemberAddMode("communityid@g.us", "admin_add");
await sock.communityJoinApprovalMode("communityid@g.us", "on");
await sock.communityLeave("communityid@g.us");
const communities = await sock.communityFetchAllParticipating();
Business Methods
const { products, nextPageCursor } = await sock.getCatalog({
jid: "628xxx@s.whatsapp.net",
limit: 10,
});
const { collections } = await sock.getCollections("628xxx@s.whatsapp.net", 10);
const order = await sock.getOrderDetails(orderId, tokenBase64);
const product = await sock.productCreate({
name: "Premium Package",
description: "Official premium plan",
price: 150000,
currency: "IDR",
originCountryCode: "ID",
images: [mediaUpload],
});
await sock.productUpdate(product.id, {
name: "Premium Package Plus",
description: "Official premium plan plus",
price: 175000,
currency: "IDR",
images: [mediaUpload],
});
await sock.productDelete([product.id]);
await sock.updateBusinessProfile({
address: "Jakarta, Indonesia",
description: "Official store",
websites: ["https://example.com"],
email: "hello@example.com",
hours: {
timezone: "Asia/Jakarta",
days: [{ day: "mon", mode: "open_24h" }],
},
});
await sock.updateBussinesProfile({
description: "Legacy alias still works",
});
await sock.updateCoverPhoto(mediaUpload);
await sock.removeCoverPhoto(coverId);
await sock.addOrEditQuickReply({
shortcut: "hello",
message: "Hello from business account",
});
await sock.removeQuickReply(timestamp);
Chat & Profile Methods
const url = await sock.profilePictureUrl(jid, "image");
await sock.updateProfilePicture(jid, mediaUpload);
await sock.removeProfilePicture(jid);
await sock.updateProfileName("My Name");
await sock.updateProfileStatus("Available");
await sock.sendPresenceUpdate("available", jid);
await sock.presenceSubscribe(jid);
await sock.readMessages([msg.key]);
await sock.sendReceipt(jid, participant, [msgId], "read");
await sock.updateBlockStatus(jid, "block");
await sock.updateBlockStatus(jid, "unblock");
const list = await sock.fetchBlocklist();
await sock.chatModify(
{
archive: true,
lastMessageOrig: msg,
lastMessage: msg,
},
jid,
);
await sock.star(jid, [{ id: msgId, fromMe: true }], true);
await sock.addOrEditContact(jid, { displayName: "Name" });
await sock.removeContact(jid);
await sock.addChatLabel(jid, labelId);
await sock.removeChatLabel(jid, labelId);
await sock.addMessageLabel(jid, messageId, labelId);
await sock.resyncAppState(["regular", "critical_block"], true);
const biz = await sock.getBusinessProfile(jid);
Privacy Settings
await sock.updateLastSeenPrivacy("all");
await sock.updateOnlinePrivacy("all");
await sock.updateProfilePicturePrivacy("contacts");
await sock.updateStatusPrivacy("contacts");
await sock.updateReadReceiptsPrivacy("all");
await sock.updateGroupsAddPrivacy("all");
await sock.updateMessagesPrivacy("all");
await sock.updateCallPrivacy("everyone");
await sock.updateDefaultDisappearingMode(86400);
await sock.updateDisableLinkPreviewsPrivacy(true);
π‘ Event Reference
sock.ev.on('connection.update', ({ connection, lastDisconnect, qr, isNewLogin, receivedPendingNotifications, isOnline }) => {})
sock.ev.on('creds.update', (update) => {})
sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => {})
sock.ev.on('chats.upsert', (chats) => {})
sock.ev.on('chats.update', (updates) => {})
sock.ev.on('chats.delete', (jids) => {})
sock.ev.on('contacts.upsert', (contacts) => {})
sock.ev.on('contacts.update', (updates) => {})
sock.ev.on('messages.upsert', ({ messages, type }) => {})
sock.ev.on('messages.update', (updates) => {})
sock.ev.on('messages.delete', (keys) => {})
sock.ev.on('messages.reaction', (reactions) => {})
sock.ev.on('message-receipt.update', (updates) => {})
sock.ev.on('groups.update', (updates) => {})
sock.ev.on('group-participants.update', (update) => {})
sock.ev.on('group.join-request', (update) => {})
sock.ev.on('call', (calls) => {})
sock.ev.on('labels.edit', (label) => {})
sock.ev.on('labels.associations', ({ associated, label, type }) => {})
sock.ev.on('newsletter.update', (updates) => {})
sock.ev.on('newsletter.follow', (jid) => {})
sock.ev.on('newsletter.unfollow', (jid) => {})
sock.ev.on('settings.update', ({ isAutoEmojiEnabled, isReadReceiptsEnabled, ... }) => {})
π§ Utility Exports
import {
useMultiFileAuthState,
makeCacheableSignalKeyStore,
initAuthCreds,
BufferJSON,
fetchLatestBaileysVersion,
tokenizeCode,
tokenizeCodeV2,
CodeHighlightType,
RichSubMessageType,
generateTableContent,
generateTableContentV2,
toTableMetadataV2,
generateListContent,
generateCodeBlockContent,
generateCodeBlockContentV2,
generateLinkContent,
generateLinkContentV2,
generateRichMessageContent,
generateLatexContent,
generateLatexImageContent,
generateLatexInlineImageContent,
generateUnifiedResponseContent,
captureUnifiedResponse,
buildRichContextInfo,
buildBotForwardedMessage,
makeStickerPack,
JS_KEYWORDS,
PYTHON_KEYWORDS,
GO_KEYWORDS,
LUA_KEYWORDS,
BASH_KEYWORDS,
LANGUAGE_KEYWORDS,
Curve,
signedKeyPair,
aesEncryptGCM,
aesDecryptGCM,
jidEncode,
jidDecode,
jidNormalizedUser,
areJidsSameUser,
isJidGroup,
isJidNewsletter,
isLidUser,
isPnUser,
isJidBot,
isJidMetaAI,
isJidBroadcast,
isJidStatusBroadcast,
DisconnectReason,
Browsers,
Dugong,
WASocket,
} from "lunaaa-baileys";
[!NOTE]
In this build, some advanced helper typings may appear first in lib/Socket/messages-send.d.ts before they are reflected everywhere else in aggregated socket declarations. If your editor autocomplete lags behind runtime behavior, inspect lib/Socket/messages-send.js and lib/Utils/rich-messages.d.ts for the most complete helper surface.
ποΈ Building This Package
This repository ships compiled files, but it still includes the build script used to emit/update lib/.
npm install
npm run build
What the build does:
- runs TypeScript with
tsconfig.build.json
- emits JavaScript and declaration files into
lib/
- runs
tsc-esm-fix so generated ESM imports resolve cleanly
Important build notes:
tsconfig.json is authoring-oriented and uses noEmit: true
tsconfig.build.json flips emit back on and excludes test files
- published package files are limited to
lib/**/* and WAProto/**/*
π Differences from Official Baileys
| Interactive messages | No native support | Full support via Dugong |
| Album messages | Not supported | Multi-media albums |
| AI Rich Response | Not available | Table, code block, rich text |
| Table V1 | Not available | Basic table with headers |
| Table V2 | Not available | Unified response + GenATableUXPrimitive |
| Code Block V1 | Not available | Syntax highlighting (JS/TS/Python) |
| Code Block V2 | Not available | Unified response + 6 languages |
| Link Message | Not available | Inline embeds + citations + verification |
| Business nodes | Manual injection | Auto-inject on relay |
| Newsletter extras | Basic only | Auto-follow, bulk follow, URL resolve |
| Button detection | Not available | getButtonType() auto-detect |
| Payment messages | Not supported | Full support |
| Event messages | Basic | Enhanced all fields |
| Status mentions | Not available | sendStatusMention() |
| Product messages | Not supported | Catalog + buttons |
| LID support | Basic | Full LIDβPN mapping + session migration |
| Identity changes | No handling | Debounced session refresh |
| Message retry | Basic | MessageRetryManager with cache |
π€ Contributing
- Fork this repo
- Create a branch (
git checkout -b feature/name)
- Commit changes (
git commit -m 'feat: add feature')
- Push to branch (
git push origin feature/name)
- Open a Pull Request
π License
MIT Β© noxXza
[!CAUTION]
This library is for educational purposes. Ensure compliance with WhatsApp Terms of Service.