npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

toxic-baileys

v1.0.9

Published

WhatsApp API Enhancement — Feature-rich fork of Baileys by xhclintohn

Readme

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 FriendlytoxicHandler and ToxicHandler exposed on socket, clean API
  • 🌐 WebSocket ImprovementsperMessageDeflate: 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

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);
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

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

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

const { state, saveCreds } = await useMultiFileAuthState('./auth_info');
const sock = makeWASocket({ auth: state });
sock.ev.on('creds.update', saveCreds);
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:  [email protected]  ← 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('[email protected]');
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('[email protected]');

🔗 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('[email protected]');
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

await sock.sendMessage(jid, { text: 'Hello World!' });
await sock.sendMessage(jid, { text: 'Reply!' }, { quoted: m });
await sock.sendMessage(jid, { text: 'Hi @user!', mentions: ['[email protected]'] });
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

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

// 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

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

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

// 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

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

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

Product 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
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

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

const group = await sock.groupCreate('Group Name', ['[email protected]']);
await sock.groupParticipantsUpdate(jid, ['[email protected]'], 'add');
await sock.groupParticipantsUpdate(jid, ['[email protected]'], 'remove');
await sock.groupParticipantsUpdate(jid, ['[email protected]'], 'promote');
await sock.groupParticipantsUpdate(jid, ['[email protected]'], '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

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('[email protected]');
await sock.sendPresenceUpdate('available', jid);
await sock.sendPresenceUpdate('composing', jid);
await sock.sendPresenceUpdate('recording', jid);
await sock.sendPresenceUpdate('paused', jid);

📡 Newsletter / Channel Management

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

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

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);
// 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

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('[email protected]'));           // 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
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();
}
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 Issuesgithub.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!