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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@realvare/based

v2.7.2

Published

whatsapp api by sam

Readme

Wave

Retro


Table of Contents


🚀 Performance Optimization Guide

This section explains the built-in performance optimizations and JID/LID validation improvements.

🎯 Performance Optimizations

The library now includes comprehensive performance optimizations with these default settings:

// Performance settings
{
  enableCache: true,              // Enable caching for faster data retrieval
  batchSize: 50,                  // Process messages in batches of 50
  maxRetries: 5,                  // Maximum reconnection attempts
  retryDelay: 5000,               // Initial retry delay (5 seconds)
  retryBackoffMultiplier: 1.5,    // Exponential backoff multiplier
  maxRetryDelay: 60000,           // Maximum retry delay (60 seconds)
  syncFullHistory: false,         // Disable full history sync to prevent slowdowns
  enableLidLogging: true,        // Enable LID logging for debugging
  logLevel: 'debug'               // Detailed logging for troubleshooting
}

// Cache settings
{
  lidCache: {
    ttl: 300000,                  // 5 minutes TTL
    maxSize: 10000,               // Maximum 10,000 entries
    cleanupInterval: 120000      // Cleanup every 2 minutes
  },
  jidCache: {
    ttl: 300000,                  // 5 minutes TTL
    maxSize: 10000,               // Maximum 10,000 entries
    cleanupInterval: 120000      // Cleanup every 2 minutes
  },
  lidToJidCache: {
    ttl: 300000,                  // 5 minutes TTL
    maxSize: 5000,                // Maximum 5,000 entries
    cleanupInterval: 180000      // Cleanup every 3 minutes
  },
  groupMetadataCache: {
    ttl: 600000,                  // 10 minutes TTL
    maxSize: 2000,                // Maximum 2,000 entries
    cleanupInterval: 300000      // Cleanup every 5 minutes
  }
}

🔧 JID/LID Validation and Normalization

The library includes comprehensive JID/LID validation utilities:

const { validateJid, getSenderLid, toJid, normalizeJid, isValidJid } = require('@realvare/based');

// Validate JID with detailed error information
const result = validateJid('[email protected]');
console.log(result.isValid, result.error);

// Extract sender information from messages
const sender = getSenderLid(message);
console.log(sender.jid, sender.lid, sender.isValid);

// Convert LID to JID format
const jid = toJid('1234567890@lid'); // Returns '[email protected]'

// Normalize JID format
const normalized = normalizeJid('1234567890@lid'); // Returns '[email protected]'

// Simple validation check
const isValid = isValidJid('[email protected]');

📈 Key Benefits

Performance Improvements:

  • Reduced Latency: Caching reduces repeated API calls by 80-90%
  • Better Throughput: Batch processing handles message bursts efficiently
  • Improved Stability: Exponential backoff prevents rapid reconnection attempts
  • Lower Ban Risk: Disabled full history sync and reduced online presence marking

Error Reduction:

  • JID/LID Validation: Prevents undefined errors from invalid IDs
  • Automatic Conversion: Handles WhatsApp's LID format changes seamlessly
  • Detailed Logging: Helps identify and debug ID-related issues
  • Graceful Fallback: Maintains functionality even with invalid IDs

🎛️ Usage Example

const { makeWASocket, useMultiFileAuthState, setPerformanceConfig, PerformanceConfig } = require('@realvare/based');

// Set up authentication
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');

// Configure performance settings
const performanceConfig = new PerformanceConfig();

// Customize settings (optional)
performanceConfig.updatePerformanceConfig({
  batchSize: 50,
  maxRetries: 5,
  retryDelay: 5000,
  retryBackoffMultiplier: 1.5,
  syncFullHistory: false
});

// Apply configuration
setPerformanceConfig(performanceConfig);

// Create socket
const sock = makeWASocket({
  auth: state,
  printQRInTerminal: true
});

💡 Best Practices

  1. Always validate JIDs before processing messages
  2. Enable caching for production environments
  3. Disable syncFullHistory to prevent performance issues
  4. Use exponential backoff for reconnection attempts
  5. Monitor cache metrics to optimize TTL and size settings
  6. Implement proper error handling for invalid JIDs/LIDs

✨ Main Features

This library, based on Baileys with specific improvements, offers an intuitive API to interact with WhatsApp Web. Here is a summary of the key features:

🚀 Quick Guide

  • This section includes basic examples for authentication and connection management.

Basic Example - Starting Bot

import makeWASocket, { DisconnectReason, useMultiFileAuthState, getPerformanceConfig, setPerformanceConfig } from '@realvare/based';

// Configure performance and cache
setPerformanceConfig({
    performance: {
        enableCache: true,
        enableMetrics: true
    },
    debug: {
        enableLidLogging: true,
        logLevel: 'info'
    }
});

async function startBot() {
    // 🔐 Multi-file authentication setup for persistent sessions
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');

    // 🌐 Socket creation with basic configuration
    const sock = makeWASocket({
        auth: state,
        printQRInTerminal: true,
        logger: console,
        browser: ['VareBot', 'Chrome', '4.0.0'],
    });

    // Improved reconnection system
    let reconnectAttempts = 0;
    const config = getPerformanceConfig();

    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update;

        if (connection === 'close') {
            const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;

            if (shouldReconnect) {
                reconnectAttempts++;
                const delay = Math.min(
                    config.performance.retryDelay * Math.pow(
                        config.performance.retryBackoffMultiplier,
                        reconnectAttempts - 1
                    ),
                    config.performance.maxRetryDelay
                );

                console.log(`🔄 Reconnection attempt ${reconnectAttempts}/${config.performance.maxRetries} in ${delay}ms`);

                if (reconnectAttempts <= config.performance.maxRetries) {
                    setTimeout(startBot, delay);
                } else {
                    console.log('❌ Maximum number of reconnection attempts reached');
                }
            }
        } else if (connection === 'open') {
            console.log('🟢 Connected successfully!');
            reconnectAttempts = 0;
        }
    });

    sock.ev.on('creds.update', saveCreds);
}startBot().catch(console.error);

Anti-Ban Example - Recommended Configuration to Avoid Bans

import makeWASocket, { DisconnectReason, useMultiFileAuthState, getPerformanceConfig, setPerformanceConfig, getSenderLid, validateJid } from '@realvare/based';

// Anti-ban configuration to reduce ban risks from improper acks
setPerformanceConfig({
    performance: {
        enableCache: true,          // Enable TTL cache to reduce API calls
        enableMetrics: true,        // Monitor performance to avoid overload
        batchSize: 50,              // Process messages in smaller batches to simulate human speed
        maxRetries: 5,              // Limit retry attempts to avoid aggressive behavior
        retryDelay: 5000,           // Base delay in ms for reconnections
        retryBackoffMultiplier: 1.5,// Exponential backoff to space retries
        maxRetryDelay: 60000,       // Maximum delay to avoid rapid reconnections
        maxMsgRetryCount: 3         // Limit message resend attempts
    }
});

async function startBot() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');

    const sock = makeWASocket({
        auth: state,
        printQRInTerminal: true,
        logger: console,
        browser: ['YourBotName', 'Chrome', '4.0.0'], // Customize browser fingerprint
        markOnlineOnConnect: false, // Crucial: Prevents always appearing online, reduces ban risk
        syncFullHistory: false      // Avoid syncing unnecessary data that could signal activity
    });

    let reconnectAttempts = 0;
    const config = getPerformanceConfig();

    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update;

        if (connection === 'close') {
            const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;

            if (shouldReconnect) {
                reconnectAttempts++;
                const delay = Math.min(
                    config.performance.retryDelay * Math.pow(
                        config.performance.retryBackoffMultiplier,
                        reconnectAttempts - 1
                    ),
                    config.performance.maxRetryDelay
                );

                console.log(`Reconnecting attempt ${reconnectAttempts}/${config.performance.maxRetries} in ${delay}ms`);

                if (reconnectAttempts <= config.performance.maxRetries) {
                    setTimeout(startBot, delay);
                } else {
                    console.log('Max reconnection attempts reached');
                }
            }
        } else if (connection === 'open') {
            console.log('Connected successfully!');
            reconnectAttempts = 0;
        }
    });

    // Monitor acks through message updates to ensure proper handling
    sock.ev.on('messages.update', (updates) => {
        for (const update of updates) {
            if (update.update.status) {
                console.log(`Message ${update.key.id} status: ${update.update.status}`); // Track acks (1=sent, 2=delivered, 3=read)
                // Add custom logic if necessary, but avoid overriding defaults to prevent detection
            }
        }
    });

    // Advanced LID/JID utilities for ack stability
    sock.ev.on('messages.upsert', ({ messages }) => {
        for (const msg of messages) {
            const info = getSenderLid(msg);
            const validation = validateJid(info.jid);
            if (validation.isValid) {
                // Process and ack safely
                console.log(`Valid JID: ${info.jid}, LID: ${info.lid}`);
            } else {
                console.warn(`Invalid JID detected: ${info.jid}`);
            }
        }
    });

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

startBot().catch(console.error);

📊 Advanced Cache Management

The library now includes an advanced cache system with automatic memory management and configurable TTL:

import { CacheManager } from 'based/lib/Utils/cache-manager';

// Example of using the cache
const cache = CacheManager;

// Save a value in the cache
cache.set('lidCache', 'key', 'value', 300); // TTL of 300 seconds

// Retrieve a value
const value = cache.get('lidCache', 'key');

// Get cache statistics
const stats = cache.getStats('lidCache');
console.log('Cache statistics:', stats);

Advanced Cache Configuration

The cache can be configured with various options to optimize performance:

setPerformanceConfig({
    cache: {
        lidCache: {
            ttl: 5 * 60 * 1000, // Entry lifetime
            maxSize: 10000,      // Maximum number of entries
            cleanupInterval: 2 * 60 * 1000 // Cleanup interval
        }
    },
    performance: {
        memoryThreshold: 0.85 // Threshold for automatic cleanup
    }
});

🔍 Troubleshooting

Connection Issues

  • The library now implements a retry system with exponential backoff
  • Automatic monitoring of connection status
  • Configurable reconnection attempts

Memory Management

  • Automatic monitoring of memory usage
  • Automatic cache cleanup when necessary
  • Configurable TTL for each cache type

Advanced Logging

setPerformanceConfig({
    debug: {
        enableLidLogging: true,
        enablePerformanceLogging: true,
        logLevel: 'debug'
    }
});

Basic Message Management with LID/JID

import makeWASocket, { getSenderLid, toJid, getCacheStats, validateJid, Logger } from '@realvare/based';

// ... (sock creation code here)

conn.ev.on('messages.upsert', ({ messages }) => {
  for (const msg of messages) {
    // 🔍 Extract sender LID with validation
    const info = getSenderLid(msg);
    
    // ✅ Validate JID before using it
    const validation = validateJid(info.jid);
    if (!validation.isValid) {
      Logger.error('Invalid JID:', validation.error);
      continue;
    }
    
    const jid = toJid(info.lid); // Normalize to JID
    
    Logger.info('💬 Message from:', jid, 'Valid:', info.isValid);
    console.log('📝 Content:', msg.message?.conversation);
    
    // Automatically reply only if valid
    if (info.isValid) {
      conn.sendMessage(jid, { text: 'Message received!' });
    }
  }
  
  // 📊 Monitor cache performance
  const stats = getCacheStats();
  Logger.performance('Cache stats:', stats);
});

📚 API Documentation

This section expands on the main methods, with detailed examples and parameters. All methods are typed in TypeScript for a safe development experience.

🏗️ Fundamental Methods

| Method | Description | Import | |--------|-------------|--------| | makeWASocket(config) | Core: Creates WhatsApp socket | ✅ Included | | useMultiFileAuthState(folder) | Auth: Persistent credentials management | ✅ Included | | getSenderLid(msg) | LID: Extracts LID from message | ✅ Included | | toJid(lid) | JID: Converts LID → JID | ✅ Included | | validateJid(jid) | Validation: Verifies JID | ✅ Included | | getCacheStats() | Performance: Cache statistics | ✅ Included | | clearCache() | Cleanup: Clears cache | ✅ Included | | setPerformanceConfig(config) | Config: Customizes performance | ✅ Included |

🔥 Note: All the above methods are ready to use and shown in the Quick Guide. Go directly to the advanced sections for specific features!


🎯 Main Events

| Event | Description | Callback Signature | |---------------------|--------------------------------------|-------------------------------------| | connection.update | Connection status updates | (update: Partial<ConnectionState>) => void | | creds.update | Credentials update | () => void | | messages.upsert | New messages or updates | ({ messages: WAMessage[], type: MessageUpsertType }) => void | | messages.update | Changes to existing messages | (update: WAMessageUpdate[]) => void | | group-participants.update | Group participant changes | (update: GroupParticipantEvent) => void |

Event Registration Example:

conn.ev.on('group-participants.update', (update) => {
  console.log('Participant updated:', update);
});

🎪 Messages and Interactive Features

Basic Messages

Text with Formatting

// Text with formatting and mentions
await conn.sendMessage(jid, { 
    text: `*Bold* _italic_ ~strikethrough~ \`monospace\`\n@mention`, 
    mentions: ['[email protected]'] 
});

Basic Media

// Image
await conn.sendMessage(jid, { 
    image: { url: './media/varebot.jpg' }, // Also supports Buffer
    caption: 'zwag'
});

// Video
await conn.sendMessage(jid, { 
    video: { url: './media/oppastoppa.mp4' },
    caption: 'brrrr',
    gifPlayback: false // true to play as GIF
});

// Audio
await conn.sendMessage(jid, { 
    audio: { url: './media/audio.mp3' },
    mimetype: 'audio/mp4',
    ptt: true // true for voice message, false for normal audio
});

// Document
await conn.sendMessage(jid, { 
    document: { url: './media/doc.pdf' },
    mimetype: 'application/pdf',
    fileName: 'document.pdf'
});

Advanced Media

// Album (Multiple images)
await conn.sendMessage(jid, {
    album: imageBuffers.map(buffer => ({ image: buffer })),
    caption: 'ts gettin real'
});

// Sticker
await conn.sendMessage(jid, { 
    sticker: { url: './stickers/sticker.webp' }
});

// Location message
await conn.sendMessage(jid, { 
    location: { 
        degreesLatitude: 45.4642, 
        degreesLongitude: 9.1900,
        name: "Milano",
        address: "Piazza del Duomo, Milano"
    } 
});

Interactive Messages

These messages include interactive elements like buttons, lists, and templates.

Messages with Simple Buttons

Send quick reply buttons.

await conn.sendMessage(jid, {
  text: 'Choose an option:',
  footer: 'Footer',
  buttons: [
    { buttonId: 'cmd1', buttonText: { displayText: 'text1' }, type: 1 },
    { buttonId: 'cmd2', buttonText: { displayText: 'text2' }, type: 1 },
  ],
  headerType: 1,
});
Messages with Buttons and Image

Combine image with buttons.

await conn.sendMessage(jid, {
  image: { url: 'https://i.ibb.co/hJW7WwxV/varebot.jpg' },
  caption: 'Message with buttons and image',
  footer: 'vare ✧ bot',
  buttons: [
    { buttonId: 'cmd', buttonText: { displayText: 'text1' }, type: 1 },
  ],
});
List Messages

Send a list of options (only in private).

await conn.sendMessage(jid, {
  text: 'This is a list!',
  footer: 'purplepurplepurple!',
  title: 'List Title',
  buttonText: 'View List',
  sections: [
    { 
      title: 'Section 1', 
      rows: [ 
        { title: 'Option 1', rowId: 'opt1',description: 'Descriptionx' }, 
        { title: 'Option 2', rowId: 'opt2', description: 'Descriptiony' } 
      ] 
    },
  ],
});

Other Messages

// Message with quote
await conn.sendMessage(jid, { 
    text: 'bang bang',
    quoted: quotedMessage // The message to quote/reply/cite
});

// Contact Messages
await conn.sendMessage(jid, { 
  contacts: { 
    displayName: 'Sam aka vare', 
    contacts: [{ vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:Sam aka vare\nTEL;waid=393476686131:+39 347 6686131\nEND:VCARD' }] 
  } 
});

// Poll Messages (Survey)
await conn.sendMessage(jid, { 
  poll: { 
    name: 'Favorite Anime?', 
    values: ['Aot', 'Bleach', 'Death note'], 
    selectableCount: 1 // how many choices possible
  } 
});

// Ephemeral Messages (Self-Destructing)
await conn.sendMessage(jid, { 
  text: "This message will self-destruct {hopefully like israel}"
}, {
  ephemeralExpiration: 7 * 24 * 60 * 60
});

// Messages with View Once Response
await conn.sendMessage(
    jid,
    {
        video: { url: './media/hado90.mp4' },
        viewOnce: true,
        caption: 'tuff'
    }
)

// Status Messages
await conn.sendMessage('status@broadcast', {
    text: 'god forgive em'
}, {
    statusJidList: contacts
});

// Send a status with media (visible only to selected contacts)
await conn.sendMessage('status@broadcast', {
    image: { url: './media/menu/menu.jpg' },
    caption: 'all i need in this life of sin is-',
    }, {
    statusJidList: contacts
});

// Newsletter Messages
await conn.sendMessage('120363418582531215@newsletter', {
    text: 'ay yo',
    });

// Payment Messages
await conn.sendMessage(jid, {
    requestPayment: {
        currency: 'EUR',
        amount1000: 5000,
        requestFrom: '[email protected]',
        note: 'js gimme my money' // https://paypal.me/samakavare
    }
});

// Call Messages
await conn.sendMessage(jid, {
    call: {
        callKey: {
            fromMe: true,
            id: Date.now().toString(),
            remoteJid: jid
        },
        type: 'ACCEPT'  // 'MISSED', 'OFFER', 'ACCEPT', 'REJECT'..
    }
});

// Live Location Messages
await conn.sendMessage(jid, {
    liveLocation: {
        degreesLatitude: 45.4642,
        degreesLongitude: 9.1900,
        accuracyInMeters: 50,
        speedInMps: 5,
        degreesClockwiseFromMagneticNorth: 359,
        caption: "kmt im mean kiss my teeth",
        sequenceNumber: 21,
        timeOffset: 2000,
    }
});

// Order/Product Messages
await conn.sendMessage(jid, {
    order: {
        id: 'bysamakavare',
        thumbnail: buffer, // image buffer
        itemCount: 67,
        surface: 'CATALOG',
        title: 'heh',
        text: 'gotta make it happen',
        seller: '[email protected]',
        amount: 67000,
        currency: 'EUR'
    }
});

// Product
await conn.sendMessage(jid, {
    product: {
        productImage: { url: './media/menu/menu.jpg' },
        productId: 'prod123',
        title: 'title',
        description: 'Desc',
        currency: 'EUR',
        priceAmount1000: 67000,
        retailerId: 'bysamakavare',
        url: 'https://varebot.netlify.app',
    },
    businessOwnerJid: m.sender
});

 // Messages with Custom Header
await conn.sendMessage(jid, {
    text: 'dilemma',
            contextInfo: {
                externalAdReply: {
                    title: `title`,
                    body: `desc`,
                    thumbnailUrl: 'https://i.ibb.co/hJW7WwxV/sam.jpg', // image
                    sourceUrl: '', // not recommended to set
                    mediaType: 1,
                    renderLargerThumbnail: false
                }
            }
});
>  note: do not use showAdAttribution or keep it false, because with it active the message is not displayed

Response Management

To manage responses to interactive messages, use the messages.upsert listener and check the response type:

conn.ev.on('messages.upsert', async ({ messages }) => {
    const msg = messages[0];
    
    // Button response
    if (msg.message?.buttonsResponseMessage) {
        const selectedId = msg.message.buttonsResponseMessage.selectedButtonId;
        console.log(`Selected button: ${selectedId}`);
    }
    
    // List response
    if (msg.message?.listResponseMessage) {
        const selectedId = msg.message.listResponseMessage.singleSelectReply.selectedRowId;
        console.log(`Selected option: ${selectedId}`);
    }
    
    // Poll response
    if (msg.message?.pollResponseMessage) {
        const selectedOptions = msg.message.pollResponseMessage.selectedOptions;
        console.log('Selected options:', selectedOptions);
    }
});

🎭 Group Features

Basic Group Management

// Group creation - the jid is for adding participants
const group = await conn.groupCreate('Angels 🩸🕊️', ['[email protected]']);

// Get group info
const metadata = await conn.groupMetadata(jid);

// Get invite code
const code = await conn.groupInviteCode(jid);

// Revoke invite link
await conn.groupRevokeInvite(jid);

// Leave group
await conn.groupLeave(jid);

Participant Management

// Add participants
await conn.groupParticipantsUpdate(
    jid, 
    ['[email protected]'],
    'add'
);

// Remove participants
await conn.groupParticipantsUpdate(
    jid,
    ['[email protected]'],
    'remove'
);

// Promote to admin
await conn.groupParticipantsUpdate(
    jid,
    ['[email protected]'],
    'promote'
);

// Demote from admin
await conn.groupParticipantsUpdate(
    jid,
    ['[email protected]'],
    'demote'
);

Group Settings

// Change group name
await conn.groupUpdateSubject(jid, 'New Name');

// Change description
await conn.groupUpdateDescription(jid, 'New description');

// Change group photo
await conn.updateProfilePicture(jid, { url: './img/group.jpg' });

// Remove group photo
await conn.removeProfilePicture(jid);

// Set group as admin only
await conn.groupSettingUpdate(jid, 'announcement');

// Set group as open to all
await conn.groupSettingUpdate(jid, 'not_announcement');

// Set who can edit info - admin only
await conn.groupSettingUpdate(jid, 'locked');

// Set who can edit info - all
await conn.groupSettingUpdate(jid, 'unlocked');

// Set who can add members - admin only
await conn.groupMemberAddMode(jid, 'admin_add');

// Set who can add members - all
await conn.groupMemberAddMode(jid, 'all_member_add');

// Enable/disable temporary messages (24 hours)
await conn.groupToggleEphemeral(jid, 86400); // seconds

// Disable temporary messages
await conn.groupToggleEphemeral(jid, 0);

// Enable/disable membership approval mode
await conn.groupJoinApprovalMode(jid, 'on'); // requires approval
await conn.groupJoinApprovalMode(jid, 'off'); // open

// Get all groups
const groups = await conn.groupFetchAllParticipating();

// Get pending invites
const invites = await conn.groupGetInviteInfo(code);

// Accept group invite
await conn.groupAcceptInvite(code);

// Get group info from link
const groupInfo = await conn.groupGetInviteInfo('https://chat.whatsapp.com/ABC123');

// Listen to settings changes
conn.ev.on('group-settings.update', async ({ id, announce, restrict }) => {
    if (announce !== undefined) {
        await conn.sendMessage(id, { text: `The group has been set to ${announce ? 'admin only' : 'all'}` });
    }
    if (restrict !== undefined) {
        await conn.sendMessage(id, { text: `Group info can be edited by ${restrict ? 'admin only' : 'all'}` });
    }
});

Advanced Group Messages

// Message with multiple mentions
await conn.sendMessage(jid, {
    text: '@user1 @user2 @user3',
    mentions: [user1, user2, user3],
    contextInfo: {
        mentionedJid: [user1, user2, user3]
    }
});

// Message with Google search
await conn.sendMessage(jid, {
    text: 'ZWAG',
    contextInfo: {
        forwardingScore: 999,
        isForwarded: true
    }
});

🔧 Fix LID/JID in Your Own Main and Handler

The LID/JID support is a strength of this library, solving common problems like sender identification in groups and private chats. Here's how to integrate it into your main code and handler.

Best Practices for LID/JID

  • decodeJid(jid): Main function to normalize JID/LID. Handles:

    • Formats with :\d+@ (uses jidNormalizedUser).
    • Decodes user/server into ${user}@${server}.
    • Converts @lid to @s.whatsapp.net.

    Implementation Example (from main.js):

    decodeJid: (jid) => {
        if (!jid) return jid;
        let decoded = jid;
        if (/:\d+@/gi.test(jid)) {
            decoded = jidNormalizedUser(jid);
        }
        if (typeof decoded === 'object' && decoded.user && decoded.server) {
            decoded = `${decoded.user}@${decoded.server}`;
        }
        if (decoded.endsWith('@lid')) {
            decoded = decoded.replace('@lid', '@s.whatsapp.net');
        }
        return decoded;
    },

    Use conn.decodeJid(jid) everywhere to normalize IDs (e.g., sender, participant, quoted).

  • Monkey-Patch for groupParticipantsUpdate: Fixes group participant updates by finding the real JID from metadata.

    Example (from handler.js):

    if (!this.originalGroupParticipantsUpdate) {
        this.originalGroupParticipantsUpdate = this.groupParticipantsUpdate;
        this.groupParticipantsUpdate = async function(chatId, users, action) {
            try {
                let metadata = global.groupCache.get(chatId);
                if (!metadata) {
                    metadata = await fetchGroupMetadataWithRetry(this, chatId);
                    if (metadata) global.groupCache.set(chatId, metadata);
                }
                if (!metadata) {
                    console.error('[ERROR] No group metadata available for safe update');
                    return this.originalGroupParticipantsUpdate.call(this, chatId, users, action);
                }
    
                const correctedUsers = users.map(userJid => {
                    const decoded = this.decodeJid(userJid);
                    const phone = decoded.split('@')[0].split(':')[0];
                    const participant = metadata.participants.find(p => {
                        const pId = this.decodeJid(p.id || p.jid || '');
                        const pPhone = pId.split('@')[0].split(':')[0];
                        return pPhone === phone;
                    });
                    return participant ? participant.id : userJid; // Fallback to original if not found
                });
    
                return this.originalGroupParticipantsUpdate.call(this, chatId, correctedUsers, action);
            } catch (e) {
                console.error('[ERROR] Error in safeGroupParticipantsUpdate:', e);
                throw e;
            }
        };
    }
  • Cache for Group Metadata: Use NodeCache to store metadata, normalizing with decodeJid.

    Example (from handler.js):

    global.groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false });
    
    // In participantsUpdate or groups.update:
    metadata.participants.forEach(u => {
        const normId = this.decodeJid(u.id);
        const jid = u.jid || normId;
    });
    global.groupCache.set(update.id, metadata);
  • Normalization in Print/Log: Remove :xx from phone numbers for clean display.

    Example (from print.js):

    function formatPhoneNumber(jid, name) {
        if (!jid) return 'Unknown';
        let userPart = jid.split('@')[0];
        let cleanNumber = userPart.split(':')[0]; // Remove the :xx part to get the real number
        try {
            const number = PhoneNumber('+' + cleanNumber).getNumber('international');
            return number + (name ? ` ~${name}` : '');
        } catch {
            return (cleanNumber || '') + (name ? ` ~${name}` : '');
        }
    }
  • Common Problems and Fixes:

    • Missing LID in Groups: Use decodeJid and metadata cache to find real JIDs.
    • JID Conversion: Always apply decodeJid before sendMessage or query.
    • Multi-Device: Set syncFullHistory: true in config for LID sync.
    • Errors in Mention/Quoted: Normalize with decodeJid in handler for quoted.sender and mentionedJid.

Integrated Example in Main

// In your main file
import makeWASocket, { getSenderLid, toJid } from '@realvare/based';
// ... (authentication and sock creation)

conn.ev.on('messages.upsert', async ({ messages }) => {
  const msg = messages[0];
  if (!msg.message) return;

  const info = getSenderLid(msg);
  const senderJid = toJid(info.lid); // Fix LID -> JID

  // Example: Reply only if valid JID
  if (senderJid.endsWith('@s.whatsapp.net')) {
    await conn.sendMessage(senderJid, { text: `Hello from ${senderJid}!` }, { quoted: msg });
  }
});

Custom Handler Example

// handler.js
export async function handleMessage(sock, msg) {
  const info = getSenderLid(msg);
  const jid = toJid(info.lid);
  
  // Log and response
  console.log(`Message from ${jid}`);
  await conn.sendMessage(jid, { text: 'Handler activated!' });
}

// In main: conn.ev.on('messages.upsert', ({ messages }) => handleMessage(sock, messages[0]));

Tip: Test in groups to verify LID, as Baileys upstream has improved native support in 2025, but the custom fixes here ensure backward compatibility.


🚀 Smart LID/JID Cache

The library now includes an advanced cache system to optimize LID/JID conversions:

import { getCacheStats, clearCache, setPerformanceConfig } from '@realvare/based';

// Configure custom cache
setPerformanceConfig({
    cache: {
        lidCache: {
            ttl: 10 * 60 * 1000, // 10 minutes
            maxSize: 15000
        }
    }
});

// Monitor performance
const stats = getCacheStats();
console.log('LID Cache:', stats.lidCache.size, '/', stats.lidCache.maxSize);

// Clear cache if necessary
clearCache();

🛡️ Advanced JID Validation

import { validateJid, Logger } from '@realvare/based';

const jid = '[email protected]';
const validation = validateJid(jid);

if (validation.isValid) {
    Logger.info('Valid JID:', jid);
} else {
    Logger.error('Invalid JID:', validation.error);
}

📊 Conditional Logging

import { Logger, setPerformanceConfig } from '@realvare/based';

// Configure logging
setPerformanceConfig({
    debug: {
        enableLidLogging: true,
        enablePerformanceLogging: true,
        logLevel: 'debug' // 'error', 'warn', 'info', 'debug'
    }
});

// Use conditional logger
Logger.debug('Debug info');
Logger.performance('Performance metrics');
Logger.error('Error occurred');

🔧 Performance Configuration

import { setPerformanceConfig, getPerformanceConfig } from '@realvare/based';

// Complete configuration
setPerformanceConfig({
    performance: {
        enableCache: true,
        enableMetrics: true,
        batchSize: 100,
        maxRetries: 3
    },
    cache: {
        lidCache: { ttl: 5 * 60 * 1000, maxSize: 10000 },
        jidCache: { ttl: 5 * 60 * 1000, maxSize: 10000 }
    },
    debug: {
        enableLidLogging: false,
        logLevel: 'warn'
    }
});

// Get current configuration
const config = getPerformanceConfig();
console.log('Cache enabled:', config.performance.enableCache);

🧩 Events: LID and JID always available (new)

The emitted events include auxiliary fields on messages to easily access both the sender/participant's JID and LID.

conn.ev.on('messages.upsert', ({ messages, type }) => {
  for (const msg of messages) {
    // Additional fields on msg.key
    // - remoteJidNormalized: Normalized JID (e.g., @s.whatsapp.net)
    // - remoteLid: LID equivalent of remoteJid
    // - participantLid: LID equivalent of participant (if present)
    const jid = msg.key.remoteJidNormalized || msg.key.remoteJid;
    const remoteLid = msg.key.remoteLid;
    const participantLid = msg.key.participantLid;

    if (jid?.endsWith('@s.whatsapp.net')) {
      conn.sendMessage(jid, { text: `Hello! LID: ${remoteLid || 'n/a'}` });
    }
  }
});

These fields eliminate ambiguity in groups and multi-device contexts where some events report LID, others JID.


⚙️ Advanced Configuration

Customize the socket for performance and behavior.

🔧 Complete Options for makeWASocket

const sock = makeWASocket({
  // 🔐 Authentication
  auth: state,
  
  // 🖥️ UI and Debug
  printQRInTerminal: true,
  logger: console,
  browser: ['VareBot', 'Chrome', '4.0.0'],
  
  // ⏱️ Timeout and Connection
  defaultQueryTimeoutMs: 60000,
  keepAliveIntervalMs: 30000,
  connectTimeoutMs: 60000,
  retryRequestDelayMs: 250,
  maxMsgRetryCount: 5,
  
  // 🎛️ Behavior
  markOnlineOnConnect: true,
  syncFullHistory: false, // Enable for full sync (consumes data)
  fireInitQueries: true,
  generateHighQualityLinkPreview: true,
});

🛡️ Security and Encryption

| Feature | Description | |---------------------------|------------------------------------------| | 🔐 End-to-End Encryption | Signal protocol for secure messages | | 🔑 Key Management | Automatic key generation/rotation | | 🔍 Authentication | Security via QR code or pairing code | | 🛡️ Data Protection | Secure local credentials storage |


🌐 Support and Community

Join the community for help and contributions.

📞 Contacts and Resources

| Channel | Link/Info | |------------------|------------------------------------------| | Email | [email protected] | | GitHub Issues| Report Bug | | PayPal | Donate | | WhatsApp Channel| Channel |


🙏 Acknowledgments

Thanks to the projects that inspire Based:

| Project | Contribution | |---------------------------|------------------------------------------| | Baileys | Original WhatsApp Web API | | Yupra | LID/JID Fix | | Signal Protocol | End-to-end encryption |


⚠️ Disclaimer & License

📋 Legal Note

⚠️ Important: Not affiliated with WhatsApp Inc. or Meta. Educational/development use only.

🛡️ Responsible Use: Avoid spam, WhatsApp ToS violations. Risk of account ban.

📜 MIT License

MIT License © 2025 realvare

See LICENSE for details.