Skip to content

xhclintohn/Baileys

Repository files navigation

toxic-baileysβ„’ ⭐

npm version License: MIT npm downloads GitHub Node

A professionally enhanced, feature-rich fork of the Baileys WhatsApp Web API. Built for developers who need robust, stable WhatsApp automation with LID identity mapping, AI group support, interoperability, extended message types, and improved connection handling.

Maintainer: 𝐱𝐑_𝐜π₯𝐒𝐧𝐭𝐨𝐧 βœ“


Important

πŸ†” LID Mapping β€” Critical Feature in This Fork

WhatsApp has rolled out Linked Identity (LID) JIDs as part of its cross-platform interoperability initiative. Group messages and status updates now arrive with @lid domain JIDs instead of standard @s.whatsapp.net phone-number JIDs. Without LID resolution, you cannot identify who sent a message in many groups.

This fork ships a complete LIDMappingStore (bidirectional LRU cache + persistent key store) and UsyncLIDProtocol so your bot always knows the real phone number behind every @lid JID. See the πŸ†” LID Mapping System section for full integration details.


πŸ“‹ Table of Contents


πŸ†• What's New

Compared to upstream Baileys, this fork adds:

Feature Description
πŸ†” LID Mapping System Full LIDMappingStore with LRU cache, persistent key store, bidirectional lookups, and UsyncLIDProtocol
πŸ€– AI Groups makeAIGroupsSocket β€” create, manage, and receive events for WhatsApp AI-powered groups
πŸ”— Interoperability API makeInteropSocket β€” fetch third-party integrators, accept Interop TOS, opt in/out
πŸ“‘ USync Protocol Layer Full WAUSync module: LID, Contact, Device, Disappearing Mode, Status, Username, Bot Profile protocols
πŸ”Ž MEX / GraphQL Queries executeWMexQuery for structured WhatsApp server queries via the w:mex IQ namespace
πŸ—οΈ me.lid Credential Bot's own LID identity stored in credentials on pairing via configureSuccessfulPairing
πŸ“Œ lidDbMigrated Login Flag Signals to WhatsApp to push down LID mappings on connect
πŸ“Ί Group Status V2 ToxicHandler exposed on socket for rich group story/status management
πŸ”§ SignalRepositoryWithLIDStore Extended signal repository type that exposes lidMapping directly on the socket
πŸ“£ newsletterId() Helper Utility to extract a clean newsletter/channel ID from any JID format

✨ Features

  • πŸš€ Modern & Fast β€” Latest WA version [2,3000,1035194821], optimised pre-key upload (812 keys)
  • πŸ†” Full LID Identity Resolution β€” Bidirectional LID↔PN mapping with LRU cache, USync lookup, and persistent storage
  • πŸ€– AI Groups Support β€” Create and manage WhatsApp's AI-powered group type
  • πŸ”— Cross-Platform Interop β€” Third-party integrator management (BirdyChat, Haiket, and more)
  • πŸ”§ Enhanced Stability β€” Improved connection handling, rate-limit backoff, 5 s keepAlive grace
  • πŸ“± Multi-Device Support β€” Full WhatsApp multi-device protocol with improved historySyncConfig
  • πŸ” End-to-End Encryption β€” Signal Protocol, inlineInitialPayloadInE2EeMsg: true
  • πŸ“¨ Extended Message Types β€” Interactive, album, event, poll result, group status, payment, product
  • πŸ‘₯ Advanced Group Management β€” Group controls, group status V2, communities support
  • πŸ’Ύ Flexible Auth β€” Multi-file auth state with makeCacheableSignalKeyStore
  • πŸ“‘ Full Newsletter/Channel API β€” Follow, create, metadata, newsletterId() helper
  • πŸ› οΈ Developer Friendly β€” toxicHandler and ToxicHandler exposed on socket, clean API
  • 🌐 WebSocket Improvements β€” perMessageDeflate: false, 100 MB max payload

πŸ“¦ Installation

Via npm

npm install toxic-baileys

Via Yarn

yarn add toxic-baileys

From GitHub (edge)

npm install github:xhclintohn/Baileys

Drop-in replacement for @whiskeysockets/baileys

{
  "dependencies": {
    "@whiskeysockets/baileys": "npm:toxic-baileys@latest"
  }
}

πŸš€ Quick Start

Basic Connection (QR Code)
import makeWASocket, { useMultiFileAuthState, DisconnectReason } from 'toxic-baileys';
import { Boom } from '@hapi/boom';

async function connectToWhatsApp() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
    const sock = makeWASocket({ auth: state, printQRInTerminal: true });

    sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
        if (connection === 'close') {
            const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
            if (shouldReconnect) connectToWhatsApp();
        } else if (connection === 'open') {
            console.log('Connected! Bot LID:', sock.user?.lid);
        }
    });

    sock.ev.on('messages.upsert', async ({ messages }) => {
        for (const m of messages) {
            if (!m.message) continue;
            console.log('Message:', JSON.stringify(m, undefined, 2));
        }
    });

    sock.ev.on('creds.update', saveCreds);
}

connectToWhatsApp().catch(console.error);
Pairing Code (no QR)
import makeWASocket, { useMultiFileAuthState } from 'toxic-baileys';

async function connectWithPairing() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
    const sock = makeWASocket({ auth: state, printQRInTerminal: false });
    sock.ev.on('creds.update', saveCreds);

    if (!sock.authState.creds.registered) {
        const phoneNumber = '254712345678'; // no + or spaces
        const code = await sock.requestPairingCode(phoneNumber);
        console.log('Pairing Code:', code);
    }
}

connectWithPairing().catch(console.error);

πŸ”Œ Connection & Configuration

Full Socket Configuration
import makeWASocket, { Browsers, makeCacheableSignalKeyStore } from 'toxic-baileys';
import NodeCache from '@cacheable/node-cache';

const groupCache = new NodeCache({ stdTTL: 300, useClones: false });

const sock = makeWASocket({
    browser: Browsers.macOS('Chrome'),
    syncFullHistory: true,
    markOnlineOnConnect: false,
    connectTimeoutMs: 60_000,
    defaultQueryTimeoutMs: 60_000,
    keepAliveIntervalMs: 30_000,
    generateHighQualityLinkPreview: true,
    cachedGroupMetadata: async (jid) => groupCache.get(jid),
    getMessage: async (key) => await yourStore.getMessage(key),
});

sock.ev.on('groups.update', async ([event]) => {
    const metadata = await sock.groupMetadata(event.id);
    groupCache.set(event.id, metadata);
});

πŸ’Ύ Authentication State Management

Multi-File Auth (Development)
import makeWASocket, { useMultiFileAuthState } from 'toxic-baileys';

const { state, saveCreds } = await useMultiFileAuthState('./auth_info');
const sock = makeWASocket({ auth: state });
sock.ev.on('creds.update', saveCreds);
Custom Database Auth (Production)
import makeWASocket, { makeCacheableSignalKeyStore } from 'toxic-baileys';

const myAuthState = {
    creds: await db.getAuthCreds(),
    keys: makeCacheableSignalKeyStore(await db.getSignalKeys(), console)
};
const sock = makeWASocket({ auth: myAuthState });
sock.ev.on('creds.update', async (creds) => await db.saveAuthCreds(creds));

πŸ†” LID Mapping System

Important

This is one of the most critical new features in this fork. WhatsApp is migrating groups to use Linked Identity (LID) JIDs β€” identifiers in the @lid domain that replace @s.whatsapp.net phone-number JIDs for privacy and cross-platform reasons. If you receive a message from 1234567890:0@lid and don't have a mapping, you cannot identify the sender.

What is a LID?

A Linked Identity (LID) is an opaque numeric identifier assigned to each WhatsApp account in the @lid domain. WhatsApp uses LIDs in groups to decouple a person's phone number from their group identity. The LID is stable across phone number changes and is used for the Signal encryption protocol in newer group types.

Standard JID:  254712345678@s.whatsapp.net  ← phone number visible
LID JID:       9876543210:0@lid             ← opaque β€” phone number hidden

How the Mapping Store Works

This fork implements LIDMappingStore in src/Signal/lid-mapping.ts:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  LIDMappingStore                     β”‚
β”‚                                                     β”‚
β”‚  LRU Cache (3-day TTL, auto-purge)                  β”‚
β”‚    pn:{pnUser}  β†’  lidUser                          β”‚
β”‚    lid:{lidUser} β†’  pnUser                          β”‚
β”‚         ↕                                           β”‚
β”‚  SignalKeyStoreWithTransaction                      β”‚
β”‚    lid-mapping/{pnUser}          β†’ lidUser          β”‚
β”‚    lid-mapping/{lidUser}_reverse β†’ pnUser           β”‚
β”‚         ↕                                           β”‚
β”‚  USync Lookup (UsyncLIDProtocol)                    β”‚
β”‚    Query WhatsApp servers via w:sync IQ             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Persistent file naming (with useMultiFileAuthState):

Session/
  lid-mapping-{pnUser}.json           ← pnUser β†’ lidUser
  lid-mapping-{lidUser}_reverse.json  ← lidUser β†’ pnUser  (reverse lookup)

me.lid β€” Your Bot's Own LID

When your bot pairs with WhatsApp, its own LID is stored in the session credentials:

// Stored automatically by configureSuccessfulPairing()
// Access it any time after connecting:
const myLid = sock.user?.lid;
console.log('My LID:', myLid); // e.g. "9876543210@lid"

lidDbMigrated Login Flag

The login payload includes lidDbMigrated: false. This signals to WhatsApp that the client has not yet migrated its local LID database, which causes WhatsApp to push down a full set of LID↔PN mappings on the initial connection β€” seeding your local store automatically.

API Reference

// On the socket (via SignalRepositoryWithLIDStore):
sock.signalRepository.lidMapping.getPNForLID(lidJid)         // string | null
sock.signalRepository.lidMapping.getLIDForPN(pnJid)          // string | null
sock.signalRepository.lidMapping.getPNsForLIDs(lidJids[])    // LIDMapping[] | null
sock.signalRepository.lidMapping.getLIDsForPNs(pnJids[])     // LIDMapping[] | null
sock.signalRepository.lidMapping.storeLIDPNMappings(pairs[]) // void

// Type:
type LIDMapping = { lid: string; pn: string }

Live Integration Example

import makeWASocket, { useMultiFileAuthState, makeCacheableSignalKeyStore } from 'toxic-baileys';

const { state, saveCreds } = await useMultiFileAuthState('./Session');
const lidPhoneCache = new Map();

const sock = makeWASocket({
    auth: {
        creds: state.creds,
        keys: makeCacheableSignalKeyStore(state.keys, console)
    }
});

sock.ev.on('creds.update', saveCreds);

// Wire up LID globals for use throughout your bot
globalThis.resolvePhoneFromLid = (lidJid) => {
    // synchronous β€” only works if the mapping is already cached
    return null;
};

globalThis.resolvePhoneFromLidAsync = async (lidJid) => {
    return await sock.signalRepository.lidMapping.getPNForLID(lidJid);
};

// Keep the in-memory cache warm from live LID mapping events
sock.ev.on('lid-mapping.update', (map) => {
    for (const [lid, pn] of Object.entries(map)) {
        const lidNum = lid.split('@')[0].split(':')[0];
        const phone = String(pn).split('@')[0].split(':')[0].replace(/\D/g, '');
        if (lidNum && phone) lidPhoneCache.set(lidNum, phone);
    }
});

// Resolving a sender from a group message
sock.ev.on('messages.upsert', async ({ messages }) => {
    for (const m of messages) {
        const sender = m.key.participant || m.key.remoteJid || '';
        if (sender.endsWith('@lid')) {
            const resolved = await sock.signalRepository.lidMapping.getPNForLID(sender);
            console.log(`LID ${sender} β†’ ${resolved ?? 'unknown'}`);
        }
    }
});

Multi-Layer Resolution (Recommended Pattern)

For maximum reliability, resolve LIDs in this priority order:

async function resolveLid(lidJid, sock, lidPhoneCache) {
    const lidNum = lidJid.split('@')[0].split(':')[0].replace(/\D/g, '');
    if (!lidNum) return null;

    // 1. In-memory LRU cache (fastest)
    const cached = lidPhoneCache?.get(lidNum);
    if (cached) return String(cached).replace(/\D/g, '') + '@s.whatsapp.net';

    // 2. Baileys LIDMappingStore (LRU + persistent key store)
    const fromStore = await sock.signalRepository?.lidMapping?.getPNForLID(lidJid);
    if (fromStore) {
        const num = fromStore.split('@')[0].replace(/\D/g, '');
        if (num) { lidPhoneCache?.set(lidNum, num); return num + '@s.whatsapp.net'; }
    }

    // 3. Session file (lid-mapping-{lidNum}_reverse.json)
    try {
        const revFile = `./Session/lid-mapping-${lidNum}_reverse.json`;
        if (fs.existsSync(revFile)) {
            const jid = JSON.parse(fs.readFileSync(revFile, 'utf-8'));
            const num = String(jid).split('@')[0].replace(/\D/g, '');
            if (num && num !== lidNum) {
                lidPhoneCache?.set(lidNum, num);
                return num + '@s.whatsapp.net';
            }
        }
    } catch {}

    // 4. Group metadata participant scan (network call β€” use sparingly)
    try {
        const meta = await sock.groupMetadata(chatJid);
        for (const p of meta.participants || []) {
            const pLid = (p.lid || p.id || '').split('@')[0].split(':')[0].replace(/\D/g, '');
            if (pLid !== lidNum) continue;
            const pBase = p.id || p.jid || '';
            if (pBase && !pBase.endsWith('@lid')) {
                const num = pBase.split('@')[0].replace(/\D/g, '');
                if (num) { lidPhoneCache?.set(lidNum, num); return num + '@s.whatsapp.net'; }
            }
        }
    } catch {}

    return null; // unresolvable β€” pass lidJid through and let WhatsApp handle it
}

Note

Session file cleanup warning: If your bot cleans up session files periodically, make sure lid-mapping-* files are preserved in your keep-list, or your mapping cache will be lost on restart and need to be re-fetched from WhatsApp's servers.


πŸ€– AI Groups

WhatsApp introduced AI-powered groups β€” special group types that include an AI bot participant. This fork exposes full management of these groups via makeAIGroupsSocket (which is included in the socket chain automatically).

Socket Chain

makeWASocket
  └─ makeInteropSocket
       └─ makeAIGroupsSocket
            └─ makeCommunitiesSocket
                 └─ makeGroupsSocket
                      └─ ...

AI Group Events

// Automatically fires when an AI group is created
sock.ev.on('groups.upsert', ([groupMeta]) => {
    console.log('New group:', groupMeta.id, groupMeta.subject);
});

// Fires on participant changes (add/remove/promote/demote) in AI groups
sock.ev.on('group-participants.update', ({ id, participants, action }) => {
    console.log(`${action} in ${id}:`, participants);
});

AI Group Metadata

// Fetch full metadata for an AI group (uses interactive query)
const meta = await sock.aiGroupMetadata('1234567890@g.us');
console.log('AI group subject:', meta.subject);
console.log('Participants:', meta.participants);

Add Bot to AI Group

// Add an AI bot participant to an AI group
await sock.aiGroupAddBot('1234567890@g.us');

πŸ”— Interoperability API

WhatsApp's interoperability (interop) system allows messages to be exchanged with third-party messaging platforms. This fork exposes the full interop socket layer via makeInteropSocket.

Supported Integrators

ID Name Notes
12 BirdyChat Cross-platform messaging bridge
13 Haiket Cross-platform messaging bridge

Fetch Available Integrators

const integrators = await sock.fetchIntegrators();
/*
[
  {
    id: 12,
    name: 'BirdyChat',
    status: 'active',         // 'active' | 'onboarding' | 'removed'
    identifierType: 'pn',     // 'email' | 'pn' | 'username'
    optedIn: false,
    features: { groupMessaging: true }
  },
  ...
]
*/

Accept Interop Terms of Service

// Must be called before opting in to any integrator
await sock.acceptInteropTOS();

Opt In to Integrators

// Opt in to all default integrators (BirdyChat + Haiket)
await sock.optInIntegrators();

// Opt in to specific integrators by ID
await sock.optInIntegrators([12]);

πŸ”„ USync Protocol

WAUSync is WhatsApp's batch user-info query system. This fork exports the full USync module so you can run structured queries against WhatsApp's w:sync:user IQ namespace.

Available Protocols

Protocol Class Purpose
USyncContactProtocol Resolve contact info
USyncDeviceProtocol Fetch registered devices
USyncDisappearingModeProtocol Get disappearing message settings
USyncStatusProtocol Fetch user status
USyncUsernameProtocol Resolve usernames
UsyncBotProfileProtocol Fetch AI bot profile data
UsyncLIDProtocol Resolve LID ↔ phone-number pairs

Running a USync Query

import { USyncQuery, USyncUser, UsyncLIDProtocol, USyncDeviceProtocol } from 'toxic-baileys';

const query = new USyncQuery();
query.withContext('interactive');
query.withProtocol(new UsyncLIDProtocol());
query.withProtocol(new USyncDeviceProtocol());

const user = new USyncUser();
user.withPhone('254712345678@s.whatsapp.net');
query.withUser(user);

const result = await sock.executeUSyncRequest(query);
console.log(result);

MEX / GraphQL Queries

For structured server queries, this fork exposes executeWMexQuery β€” a GraphQL-style query executor over WhatsApp's w:mex IQ namespace:

import { executeWMexQuery } from 'toxic-baileys';

const result = await executeWMexQuery(
    { userId: '254712345678' },   // variables
    'xwa2_user_profile',          // queryId (WhatsApp's internal query name)
    'xwa2_user_profile',          // dataPath (key in the response data object)
    sock.query.bind(sock),        // query function
    sock.generateMessageTag.bind(sock)
);
console.log('MEX result:', result);

πŸ“€ Sending Messages

Basic Messages

Text, Reply, Mention, Forward, Edit, Delete, React
await sock.sendMessage(jid, { text: 'Hello World!' });
await sock.sendMessage(jid, { text: 'Reply!' }, { quoted: m });
await sock.sendMessage(jid, { text: 'Hi @user!', mentions: ['254712345678@s.whatsapp.net'] });
await sock.sendMessage(jid, { forward: m });

const sent = await sock.sendMessage(jid, { text: 'Original' });
await sock.sendMessage(jid, { text: 'Edited!', edit: sent.key });
await sock.sendMessage(jid, { delete: sent.key });

await sock.sendMessage(jid, { react: { text: 'πŸ”₯', key: m.key } });
await sock.sendMessage(jid, { react: { text: '', key: m.key } }); // remove reaction

Media Messages

Image, Video, Audio, Document, Sticker, View Once
await sock.sendMessage(jid, { image: { url: './image.jpg' }, caption: 'Caption' });
await sock.sendMessage(jid, { image: fs.readFileSync('./img.jpg') });
await sock.sendMessage(jid, { video: { url: './video.mp4' }, caption: 'Video' });
await sock.sendMessage(jid, { video: { url: './animation.mp4' }, gifPlayback: true });
await sock.sendMessage(jid, { audio: { url: './voice.ogg' }, mimetype: 'audio/ogg; codecs=opus', ptt: true });
await sock.sendMessage(jid, { audio: { url: './song.mp3' }, mimetype: 'audio/mp4' });
await sock.sendMessage(jid, { document: { url: './file.pdf' }, fileName: 'MyDoc.pdf', mimetype: 'application/pdf' });
await sock.sendMessage(jid, { sticker: { url: './sticker.webp' } });
await sock.sendMessage(jid, { image: fs.readFileSync('./img.jpg'), viewOnce: true });

Interactive Messages

Interactive Message with Native Flow Buttons
// Copy button
await sock.sendMessage(jid, {
    interactiveMessage: {
        header: 'toxic-baileysβ„’',
        title: 'Hello World',
        footer: 'By 𝐱𝐑_𝐜π₯𝐒𝐧𝐭𝐨𝐧 βœ“',
        buttons: [
            {
                name: 'cta_copy',
                buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', id: '1', copy_code: 'TOXIC123' })
            }
        ]
    }
}, { quoted: m });

// URL button
await sock.sendMessage(jid, {
    interactiveMessage: {
        header: 'Visit Us',
        title: 'toxic-baileys',
        footer: 'GitHub',
        buttons: [
            {
                name: 'cta_url',
                buttonParamsJson: JSON.stringify({ display_text: 'Open GitHub', url: 'https://github.com/xhclintohn/Baileys' })
            }
        ]
    }
}, { quoted: m });

// With image thumbnail
await sock.sendMessage(jid, {
    interactiveMessage: {
        header: 'With Image',
        title: 'toxic-baileysβ„’',
        footer: 'Best Baileys Fork',
        image: { url: 'https://example.com/image.jpg' },
        buttons: [
            { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', id: '1', copy_code: 'HELLO' }) }
        ]
    }
}, { quoted: m });

// Single select list
await sock.sendMessage(jid, {
    interactiveMessage: {
        header: 'Choose',
        title: 'Menu',
        footer: 'Select below',
        nativeFlowMessage: {
            buttons: [
                {
                    name: 'single_select',
                    buttonParamsJson: JSON.stringify({
                        title: 'Options',
                        sections: [
                            {
                                title: 'Commands',
                                rows: [
                                    { title: 'Option 1', description: 'First', id: 'opt_1' },
                                    { title: 'Option 2', description: 'Second', id: 'opt_2' }
                                ]
                            }
                        ]
                    })
                }
            ]
        }
    }
}, { quoted: m });

// With externalAdReply
await sock.sendMessage(jid, {
    interactiveMessage: {
        header: 'Premium',
        title: 'toxic-baileysβ„’',
        footer: 'By 𝐱𝐑_𝐜π₯𝐒𝐧𝐭𝐨𝐧',
        externalAdReply: {
            title: 'toxic-baileys',
            body: 'Best WhatsApp Library',
            mediaType: 1,
            thumbnailUrl: 'https://example.com/thumb.jpg',
            sourceUrl: 'https://github.com/xhclintohn/Baileys',
            showAdAttribution: true,
            renderLargerThumbnail: true
        },
        buttons: [
            { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Visit', url: 'https://github.com/xhclintohn/Baileys' }) }
        ]
    }
}, { quoted: m });

Album Messages

Send multiple images/videos in a single album
await sock.sendMessage(jid, {
    albumMessage: [
        { image: fs.readFileSync('./photo1.jpg'), caption: 'First photo' },
        { image: { url: 'https://example.com/photo2.jpg' }, caption: 'Second photo' },
        { video: fs.readFileSync('./clip.mp4'), caption: 'A video clip' }
    ]
}, { quoted: m });

Event Messages

Send WhatsApp event invitations
await sock.sendMessage(jid, {
    eventMessage: {
        isCanceled: false,
        name: 'toxic-baileys Launch',
        description: 'Join us for the launch!',
        location: { degreesLatitude: -1.2921, degreesLongitude: 36.8219, name: 'Nairobi, Kenya' },
        joinLink: 'https://call.whatsapp.com/video/your-link',
        startTime: String(Math.floor(Date.now() / 1000) + 3600),
        endTime: String(Math.floor(Date.now() / 1000) + 7200),
        extraGuestsAllowed: true
    }
}, { quoted: m });

Poll & Poll Result Messages

Polls and displaying poll results
// Create poll
await sock.sendMessage(jid, {
    poll: {
        name: 'Best WhatsApp library?',
        values: ['toxic-baileys', 'Baileys', 'Other'],
        selectableCount: 1
    }
});

// Display poll results
await sock.sendMessage(jid, {
    pollResultMessage: {
        name: 'Best Library Results',
        pollVotes: [
            { optionName: 'toxic-baileys', optionVoteCount: '42' },
            { optionName: 'Baileys', optionVoteCount: '10' },
            { optionName: 'Other', optionVoteCount: '2' }
        ]
    }
}, { quoted: m });

Group Status Messages

Post status/story to a group (Group Status V2)
await sock.sendMessage(groupJid, { groupStatusMessage: { text: 'Hello group! πŸ‘‹' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { image: fs.readFileSync('./banner.jpg'), caption: 'Update!' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { video: fs.readFileSync('./promo.mp4'), caption: 'Promo' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { audio: fs.readFileSync('./audio.mp4'), mimetype: 'audio/mp4' } });

// Using ToxicHandler directly (exposed on socket)
const storyResult = await sock.toxicHandler.handleGroupStory(content, groupJid, quotedMsg);

Payment Request Messages

Send payment requests
await sock.sendMessage(jid, {
    requestPaymentMessage: {
        currency: 'KES',
        amount: 500000,
        from: m.sender,
        background: { id: 'DEFAULT', placeholderArgb: 0xFFF0F0F0 }
    }
}, { quoted: m });

Product Messages

Send product catalog messages
await sock.sendMessage(jid, {
    productMessage: {
        title: 'Premium Script',
        description: 'Best bot script available',
        thumbnail: { url: 'https://example.com/product.jpg' },
        productId: 'PROD001',
        retailerId: 'xhclinton',
        url: 'https://github.com/xhclintohn/Baileys',
        body: 'Full featured automation',
        footer: 'Special price',
        priceAmount1000: 10000,
        currencyCode: 'USD',
        buttons: [{ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Buy Now', url: 'https://github.com/xhclintohn/Baileys' }) }]
    }
}, { quoted: m });

Button & Template Messages

Classic buttons, list messages, template buttons
// Classic buttons
await sock.sendMessage(jid, {
    text: 'Choose:',
    footer: 'toxic-baileysβ„’',
    buttons: [
        { buttonId: 'btn_1', buttonText: { displayText: 'Option 1' }, type: 1 },
        { buttonId: 'btn_2', buttonText: { displayText: 'Option 2' }, type: 1 }
    ],
    headerType: 1
});

// List message
await sock.sendMessage(jid, {
    text: 'Pick one:',
    footer: 'toxic-baileysβ„’',
    title: 'Menu',
    buttonText: 'Open List',
    sections: [
        {
            title: 'Section 1',
            rows: [
                { title: 'Item 1', rowId: 'row_1', description: 'Description 1' },
                { title: 'Item 2', rowId: 'row_2', description: 'Description 2' }
            ]
        }
    ]
});

πŸ“ Chat & Message Management

Read receipts, archive, pin, delete, star
await sock.readMessages([m.key]);
await sock.sendReadReceipt(jid, participant, [m.key.id]);
await sock.chatModify({ archive: true, lastMessages: [{ key: m.key, messageTimestamp: m.messageTimestamp }] }, jid);
await sock.chatModify({ pin: true }, jid);
await sock.chatModify({ delete: true, lastMessages: [{ key: m.key, messageTimestamp: m.messageTimestamp }] }, jid);
await sock.chatModify({ star: { messages: [{ id: m.key.id, fromMe: m.key.fromMe }], star: true } }, jid);
await sock.sendMessage(jid, { delete: m.key });

πŸ‘₯ Group Management

Create, modify, manage participants
const group = await sock.groupCreate('Group Name', ['254712345678@s.whatsapp.net']);
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'add');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'remove');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'promote');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'demote');
await sock.groupUpdateSubject(jid, 'New Name');
await sock.groupUpdateDescription(jid, 'New description');
await sock.groupSettingUpdate(jid, 'announcement');
await sock.groupSettingUpdate(jid, 'not_announcement');
await sock.groupSettingUpdate(jid, 'locked');
await sock.groupSettingUpdate(jid, 'unlocked');
await sock.groupLeave(jid);
const inviteCode = await sock.groupInviteCode(jid);
const meta = await sock.groupMetadata(jid);
const all = await sock.groupFetchAllParticipating();

πŸ‘€ User & Profile Management

Profile photo, status, presence
await sock.updateProfilePicture(jid, { url: './avatar.jpg' });
await sock.removeProfilePicture(jid);
const pp = await sock.profilePictureUrl(jid, 'image');
await sock.updateProfileStatus('Hey there! I am using toxic-baileysβ„’');
await sock.updateProfileName('My Bot Name');
await sock.fetchStatus(jid);
const exists = await sock.onWhatsApp('254712345678@s.whatsapp.net');
await sock.sendPresenceUpdate('available', jid);
await sock.sendPresenceUpdate('composing', jid);
await sock.sendPresenceUpdate('recording', jid);
await sock.sendPresenceUpdate('paused', jid);

πŸ“‘ Newsletter / Channel Management

Create, follow, manage newsletters and channels
import { newsletterId } from 'toxic-baileys';

// Follow a channel
await sock.newsletterFollow('1234567890@newsletter');

// Unfollow
await sock.newsletterUnfollow('1234567890@newsletter');

// Fetch channel metadata
const meta = await sock.newsletterMetadata('invite', 'your-invite-link');
console.log(meta.id, meta.name, meta.subscribers);

// Create a channel
const channel = await sock.newsletterCreate('Channel Name', { description: 'My channel' });

// Send a message to your channel
await sock.sendMessage('1234567890@newsletter', { text: 'Channel update!' });

// newsletterId() helper β€” extract clean ID from any format
const cleanId = newsletterId('1234567890@newsletter');
const cleanIdFromLink = newsletterId('https://whatsapp.com/channel/yourlink');
console.log('Clean ID:', cleanId);

// Mute/unmute
await sock.newsletterMute('1234567890@newsletter');
await sock.newsletterUnmute('1234567890@newsletter');

πŸ”’ Privacy & Block Management

Privacy settings and block list
await sock.updateProfilePicturePrivacy('contacts');   // 'all' | 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy('contacts');
await sock.updateReadReceiptsPrivacy('all');           // 'all' | 'none'
await sock.updateGroupsAddPrivacy('contacts');
await sock.updateLastSeenPrivacy('contacts');
await sock.updateOnlinePrivacy('all');

const privacy = await sock.fetchPrivacySettings(true);

await sock.updateBlockStatus(jid, 'block');
await sock.updateBlockStatus(jid, 'unblock');
const blocked = await sock.fetchBlocklist();

πŸ—„οΈ Data Store Implementation

In-Memory Store (Development)
import makeWASocket, { makeInMemoryStore } from 'toxic-baileys';

const store = makeInMemoryStore({ logger: console });
store.readFromFile('./baileys_store.json');
setInterval(() => store.writeToFile('./baileys_store.json'), 10_000);

const sock = makeWASocket({});
store.bind(sock.ev);
Using ToxicHandler Directly
// ToxicHandler and toxicHandler are both exposed on the socket
const { toxicHandler } = sock;

const paymentContent  = await toxicHandler.handlePayment(content, quoted);
const interactive     = await toxicHandler.handleInteractive(content, jid, quoted);
const album           = await toxicHandler.handleAlbum(content, jid, quoted);
const event           = await toxicHandler.handleEvent(content, jid, quoted);
const pollResult      = await toxicHandler.handlePollResult(content, jid, quoted);
const groupStory      = await toxicHandler.handleGroupStory(content, jid, quoted);

πŸ› οΈ Utility Functions

Core Utilities
import {
    getContentType, areJidsSameUser, isJidGroup, isJidBroadcast,
    isJidStatusBroadcast, isJidNewsLetter, jidNormalizedUser,
    isLidUser, isPnUser, isHostedPnUser,
    generateMessageID, generateMessageIDV2, generateWAMessage,
    generateWAMessageContent, generateWAMessageFromContent,
    downloadContentFromMessage, getAggregateVotesInPollMessage,
    extractMessageContent, normalizeMessageContent, newsletterId,
    proto
} from 'toxic-baileys';

const type = getContentType(m.message);
console.log(isJidGroup('123@g.us'));           // true
console.log(isJidNewsLetter('123@newsletter')); // true
console.log(isLidUser('12345@lid'));            // true

// Extract clean newsletter ID
const id = newsletterId('https://whatsapp.com/channel/mylink');

// Download media
const stream = await downloadContentFromMessage(m.message.imageMessage, 'image');
const chunks = [];
for await (const chunk of stream) chunks.push(chunk);
const buffer = Buffer.concat(chunks);

// Aggregate poll votes
const votes = getAggregateVotesInPollMessage(
    { message: pollMsg.message, pollUpdates },
    sock.user.id
);

πŸ’‘ Best Practices & Tips

Connection Stability

  1. Always implement reconnect logic on connection === 'close'
  2. Cache group metadata using cachedGroupMetadata
  3. Use markOnlineOnConnect: false to still receive phone notifications
  4. Set syncFullHistory: true for complete history

LID Mapping

  1. Always preserve lid-mapping-* files in session cleanup routines
  2. Listen to the lid-mapping.update event to keep your in-memory cache warm
  3. Fall back to group metadata scan only when all cache/store lookups fail
  4. Use sock.user?.lid to access your own bot's LID identity

Performance

  1. Use @cacheable/node-cache for group metadata caching
  2. Implement a message queue with rate limiting for bulk sends
  3. Use databases instead of in-memory store for production
Auto-reconnect pattern
async function connectWithRetry(maxRetries = 10) {
    let attempt = 0;
    const connect = async () => {
        attempt++;
        try {
            const { state, saveCreds } = await useMultiFileAuthState('./auth');
            const sock = makeWASocket({ auth: state });
            sock.ev.on('creds.update', saveCreds);
            sock.ev.on('connection.update', async ({ connection, lastDisconnect }) => {
                if (connection === 'close') {
                    const code = lastDisconnect?.error?.output?.statusCode;
                    if (code === DisconnectReason.loggedOut) return;
                    const delay = Math.min(5000 * attempt, 60000);
                    if (attempt < maxRetries) setTimeout(connect, delay);
                } else if (connection === 'open') {
                    attempt = 0;
                }
            });
        } catch (err) {
            if (attempt < maxRetries) setTimeout(connect, 5000 * attempt);
        }
    };
    await connect();
}
Message queue with rate limiting
class MessageQueue {
    constructor(sock, delayMs = 1000) {
        this.sock = sock;
        this.queue = [];
        this.processing = false;
        this.delayMs = delayMs;
    }
    async add(jid, content, options = {}) {
        this.queue.push({ jid, content, options });
        if (!this.processing) this._process();
    }
    async _process() {
        this.processing = true;
        while (this.queue.length > 0) {
            const { jid, content, options } = this.queue.shift();
            try {
                await this.sock.sendMessage(jid, content, options);
                await new Promise(r => setTimeout(r, this.delayMs));
            } catch (err) {
                console.error('Send failed:', err.message);
            }
        }
        this.processing = false;
    }
}

const queue = new MessageQueue(sock, 1500);
await queue.add(jid, { text: 'Message 1' });
await queue.add(jid, { text: 'Message 2' });

⚠️ Important Legal Notice

This project is NOT affiliated with, authorized, maintained, sponsored, or endorsed by WhatsApp LLC or any of its affiliates.

  • Only message users who have explicitly consented
  • Do NOT use for spamming, bulk unsolicited messaging, or harassment
  • Respect WhatsApp's Terms of Service and rate limits
  • The maintainer assumes NO liability for misuse or damages

πŸ†˜ Getting Help

  1. GitHub Issues β€” github.com/xhclintohn/Baileys/issues
  2. Response Time β€” Typically within 24–48 hours

WhatsApp Chat GitHub Follow


πŸ“„ License

MIT License. See LICENSE for details.

Credits: Original Baileys by WhiskeySockets Β· toxic-baileys enhancements by 𝐱𝐑_𝐜π₯𝐒𝐧𝐭𝐨𝐧 βœ“


🌟 toxic-baileysβ„’ β€” Crafted with ❀️ by 𝐱𝐑_𝐜π₯𝐒𝐧𝐭𝐨𝐧 βœ“

The most powerful WhatsApp automation toolkit

⭐ Star the repository if this helped you!

About

xD

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors