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

@sheikhtamim/wca

v1.1.1

Published

FCA-style WhatsApp Client API built on Baileys — send messages, media, manage groups, and listen to all WhatsApp events with a familiar callback interface.

Readme

npm version npm downloads Node.js GitHub stars License Visitors

WCA is an FCA-style WhatsApp bot framework built on top of Baileys.
If you know how to write a Facebook Messenger bot using stfca, you already know WCA.

📦 NPM🤖 Example Bot🐛 Issues💬 Support Group📸 Instagram


✨ Features

| Category | Feature | |---|---| | 🔌 Connection | QR Code login · Pairing Code (no phone scan) · Auto-reconnect | | 💬 Messaging | Send text · Reply/quote · Mentions · Emoji reactions · Delete for everyone · Edit message · Send poll | | 📎 Media | Image · Video · Audio · PTT/Voice · Document · Sticker · GIF · Location | | 👥 Groups | Create · Leave · Add/Kick · Promote/Demote · Rename · Description · Invite link · Settings · Pin message · Get all groups | | 👤 Profile | Get/Set profile picture · Status text · Display name · Block/Unblock · Get DM info | | 📡 Events | Messages · Reactions · Delete events · Group changes · Typing presence · Calls | | 💬 Chats | Get all chats · Get all contacts · Archive/Unarchive · Mute/Unmute | | 🔄 Updates | Auto-update on startup — always keeps itself current |


📋 Requirements

  • Node.js ≥ 18.x (LTS recommended — download)
  • npm ≥ 8.x
  • A WhatsApp account
# Check your versions
node -v
npm -v

📦 Installation

npm install @sheikhtamim/wca

🚀 Quick Start

QR Code login

const wca = require('@sheikhtamim/wca');

wca({ authFolder: './wca_auth' }, (err, api) => {
    if (err) return console.error(err);

    api.listen((err, msg) => {
        if (err || !msg || msg.type !== 'message') return;
        if (msg.body === '!ping') {
            api.sendMessage('🏓 Pong!', msg.threadID);
        }
    });
});

Pairing Code login (no QR scan needed)

const wca = require('@sheikhtamim/wca');

wca({
    authFolder:     './wca_auth',
    phoneNumber:    '8801xxxxxxxxx',   // international format, no +
    usePairingCode: true,
}, (err, api) => {
    if (err) return console.error(err);
    console.log('Connected as', api.getCurrentUserID());

    api.listen((err, msg) => {
        if (!msg || msg.type !== 'message') return;
        if (msg.body === '!ping') {
            api.sendMessage('🏓 Pong!', msg.threadID);
        }
    });
});

When the pairing code prints in your console:

  1. Open WhatsApp → ⋮ Menu → Linked Devices
  2. Tap Link a deviceLink with phone number instead
  3. Enter the code shown in your terminal

⚙️ Options

wca({
    authFolder:       './wca_auth',   // auth state folder  (default: './wca_auth')
    phoneNumber:      '628xxx',       // for pairing code
    usePairingCode:   true,           // force pairing code flow
    skipUpdateCheck:  false,          // set true to skip auto-update on start

    globalOptions: {
        selfListen:            false,   // process own sent messages
        selfListenEvent:       false,   // emit own messages to listen()
        listenEvents:          true,    // emit group / call / presence events
        updatePresence:        false,   // emit all presence updates
        listenTyping:          false,   // emit only typing (composing/paused) events
        autoMarkDelivery:      false,   // auto-read every received message
        autoReconnect:         true,    // reconnect on unexpected drop
        emitReady:             false,   // emit { type:'ready' } on connection open
        enableTypingIndicator: false,   // show typing before every sendMessage
        typingDuration:        3000,    // typing indicator duration ms
        logLevel:              'error', // 'silent' | 'error' | 'warn' | 'info' | 'debug'
        online:                true,    // appear online when connected
    },
}, callback);

🔄 Auto-Update (built-in)

WCA automatically checks for updates every time it starts and installs the latest version before connecting. No manual setup needed — just run your bot normally.

// WCA auto-updates itself. No extra code required.
wca({ authFolder: './wca_auth' }, (err, api) => { ... });

To disable auto-update (not recommended):

wca({ skipUpdateCheck: true, authFolder: './wca_auth' }, (err, api) => { ... });

You can also call it manually:

const { checkForWCAUpdate } = require('@sheikhtamim/wca/checkUpdate');
await checkForWCAUpdate();

// Options:
await checkForWCAUpdate({
    autoUpdate:   true,    // install the update   (default: true)
    silent:       false,   // suppress console log (default: false)
    exitOnUpdate: true,    // exit(2) so pm2/nodemon restarts (default: true)
});

📨 Message Event Format

{
    type:          'message',
    body:          'Hello!',
    threadID:      '[email protected]',   // DM  OR  [email protected]  (group)
    senderID:      '[email protected]',   // always a phone JID (LIDs resolved)
    author:        '[email protected]',   // alias for senderID
    messageID:     'XXXXXXXXXXXXXXXXXX',
    isGroup:       false,
    isSingleUser:  true,
    fromMe:        false,
    timestamp:     1716900000,
    attachments:   [ { type:'image', mimetype:'image/jpeg', caption:'...', ... } ],
    mentions:      [ '[email protected]' ],
    replyToMessage: {
        messageID: 'YYYYY',
        senderID:  '[email protected]',
        body:      'quoted text',
        attachments: [],
    },
    location:     null,   // { latitude, longitude, name, address } if location msg
    poll:         null,   // { name, options, selectableCount }     if poll msg
    quoteOptions: { quoted: <rawWAMessage> },  // pass directly to sendMessage for reply
    args:         ['!ping'],    // body.split(' ')
    raw:          <WAMessage>,  // raw Baileys object
}

Other event types

// Emoji reaction
{ type: 'message_reaction', emoji: '👍', reactionKey: { id, remoteJid, fromMe }, senderID, threadID, ... }

// Someone deleted a message
{ type: 'message_unsend', logMessageType: 'log:unsend', deletedMessageID, senderID, threadID, ... }

// Group participant change
{ type: 'event', logMessageType: 'log:subscribe'|'log:unsubscribe'|'log:thread-admins',
  participants: ['[email protected]'], author, threadID, logMessageData: { action, ... } }

// Group metadata change
{ type: 'group_update', logMessageType: 'log:thread-name'|'log:thread-image'|'log:thread-description'|...,
  threadID, author, logMessageData: { value } }

// Typing / presence
{ type: 'presence', userID, threadID, presence: 'composing'|'paused'|'available'|'unavailable', isTyping }

// Incoming call
{ type: 'call', from, isVideo, isGroup, status }

📚 API Reference

Messaging

api.sendMessage(msg, threadID, [cb])        // text, attachment, reply, mention
api.sendImage(url, threadID, caption, opts)
api.sendVideo(url, threadID, caption, opts)
api.sendAudio(url, threadID, opts)
api.sendPTT(url, threadID, opts)            // voice note
api.sendDocument(url, threadID, caption, opts)
api.sendSticker(url, threadID, opts)
api.sendGif(url, threadID, caption, opts)
api.sendLocation(threadID, lat, lon, opts)
api.sendTypingIndicator(bool, threadID)
api.markAsRead(threadID, senderID, [ids])
api.reactToMessage(threadID, msgID, emoji)
api.deleteMessage(threadID, msgID, forAll)
api.editMessage(threadID, msgID, newText)   // edit a sent message
api.sendPoll(threadID, question, options, [opts])  // send a poll
api.pinMessage(threadID, msgID)             // pin a message
api.unpinMessage(threadID, msgID)           // unpin a message
api.downloadMedia(rawMsg)                   // → Buffer

Quoted reply

// Using quoteOptions from the incoming event (best):
api.sendMessage('Got it!', msg.threadID, null, { replyToMessage: msg.quoteOptions.quoted });

// Or pass the raw message directly:
api.sendMessage({ body: 'Reply!', replyToMessage: msg.raw }, msg.threadID);

Groups

api.getGroupInfo(threadID)                  // full group info (for DB saving)
api.getAllGroups()                           // all groups the bot is in
api.getGroupAdmins(threadID)
api.getGroupInviteLink(threadID)
api.groupRevokeInvite(threadID)
api.groupAcceptInvite(code)
api.addUserToGroup(threadID, [userIDs])
api.kickUser(threadID, [userIDs])
api.createGroup(name, [participantIDs])
api.leaveGroup(threadID)
api.promoteAdmin(threadID, [userIDs])
api.demoteAdmin(threadID, [userIDs])
api.changeGroupSubject(threadID, name)
api.changeGroupDescription(threadID, desc)
api.groupSettingUpdate(threadID, setting)   // 'announcement'|'not_announcement'|'locked'|'unlocked'

Profile & user

api.getProfilePicture(userID, type)
api.getUserInfo([userIDs])
api.getDMInfo(userID)                       // full DM user info (for DB saving)
api.getContacts()                           // all contacts
api.getChats()                              // all chats / conversations
api.fetchStatus(userID)
api.updateProfilePicture(media)
api.updateProfileStatus(text)
api.updateProfileName(name)
api.blockContact(userID)
api.unblockContact(userID)
api.sendPresenceUpdate(type, threadID)      // 'available'|'unavailable'|'composing'|'paused'
api.getCurrentUserID()                      // own JID
api.sock                                    // raw Baileys socket

Chats

api.archiveChat(threadID, [archive=true])   // archive a chat
api.unarchiveChat(threadID)                 // unarchive a chat
api.muteChat(threadID, [durationMs])        // mute a chat  (default 8 hours)
api.unmuteChat(threadID)                    // unmute a chat

🗄️ Group / DM Info for Database

Get full group info (GC)

const info = await api.getGroupInfo('[email protected]');
// Returns:
// {
//   threadID, name, description, owner, creation,
//   size, adminIDs, participantIDs,
//   participants: [{ userID, isAdmin, isSuperAdmin }],
//   announcement, restrict, ephemeralDuration, raw
// }

Get all groups the bot is in

const groups = await api.getAllGroups();
groups.forEach(g => console.log(g.name, g.size));

Get full DM user info

const info = await api.getDMInfo('[email protected]');
// Returns:
// {
//   userID, phone, name, status, statusTimestamp,
//   profilePicture, isBusiness, businessProfile, isMe, raw
// }

🤖 Full Bot Example

const wca = require('@sheikhtamim/wca');

// WCA auto-updates itself before connecting — no extra code needed.
wca({
    authFolder:     './wca_auth',
    phoneNumber:    '8801xxxxxxxxx',
    usePairingCode: true,
    globalOptions:  {
        listenEvents:  true,
        selfListen:    false,
        autoReconnect: true,
    },
}, (err, api) => {
    if (err) return console.error('WCA error:', err);
    console.log('Bot ready:', api.getCurrentUserID());

    api.listen(async (err, msg) => {
        if (err) return;
        if (!msg || msg.type !== 'message' || msg.fromMe) return;

        const { body, threadID } = msg;
        if (!body.startsWith('!')) return;

        const [cmd, ...args] = body.slice(1).split(' ');

        switch (cmd.toLowerCase()) {
            case 'ping':
                await api.sendMessage('🏓 Pong!', threadID);
                break;

            case 'poll':
                await api.sendPoll(threadID, 'Favourite colour?', ['Red', 'Blue', 'Green']);
                break;

            case 'gcinfo':
                if (msg.isGroup) {
                    const info = await api.getGroupInfo(threadID);
                    await api.sendMessage(
                        `*${info.name}*\nMembers: ${info.size}\nAdmins: ${info.adminIDs.length}`,
                        threadID
                    );
                }
                break;

            case 'dminfo':
                if (!msg.isGroup) {
                    const info = await api.getDMInfo(msg.senderID);
                    await api.sendMessage(
                        `Name: ${info.name || 'Unknown'}\nStatus: ${info.status || 'None'}`,
                        threadID
                    );
                }
                break;

            case 'react':
                await api.reactToMessage(threadID, msg.messageID, '👍');
                break;

            case 'reply':
                await api.sendMessage(
                    { body: 'This is a reply!', replyToMessage: msg.raw },
                    threadID
                );
                break;

            case 'image':
                await api.sendImage('https://picsum.photos/800/600', threadID, 'Test image');
                break;
        }
    });
});

📂 Project Structure

wca/
 ├── index.js              ← Main entry  wca(options, callback)
 ├── utils.js              ← JID helpers, LID resolver, event formatter
 ├── checkUpdate.js        ← Auto-update checker (runs on every startup)
 └── src/                  ← One file per feature
     ├── listenMqtt.js
     ├── sendMessage.js
     ├── sendMedia.js
     ├── sendPoll.js
     ├── editMessage.js
     ├── pinMessage.js
     ├── sendTypingIndicator.js
     ├── sendReadReceipt.js
     ├── sendLocation.js
     ├── sendPresenceUpdate.js
     ├── reactToMessage.js
     ├── deleteMessage.js
     ├── downloadMedia.js
     ├── getGroupInfo.js
     ├── getAllGroups.js
     ├── getGroupAdmins.js
     ├── getGroupInviteLink.js
     ├── groupRevokeInvite.js
     ├── groupAcceptInvite.js
     ├── groupSettingUpdate.js
     ├── addUserToGroup.js
     ├── kickUser.js
     ├── createGroup.js
     ├── leaveGroup.js
     ├── setGroupAdmin.js
     ├── changeGroupSubject.js
     ├── changeGroupDescription.js
     ├── getProfilePicture.js
     ├── getUserInfo.js
     ├── getDMInfo.js
     ├── getContacts.js
     ├── getChats.js
     ├── archiveChat.js
     ├── muteChat.js
     ├── fetchStatus.js
     ├── updateProfilePicture.js
     ├── updateProfileStatus.js
     ├── updateProfileName.js
     ├── blockContact.js
     ├── sendButtons.js
     └── getAppState.js

🧠 LID Resolution (Delta System)

WhatsApp internally uses LID (Linked Identity) JIDs — e.g. 186393124970625@lid — which are opaque internal IDs that differ from phone numbers. WCA automatically resolves these to real @s.whatsapp.net JIDs using Baileys' contact store.


🔗 Links

| | | |---|---| | 📦 NPM package | @sheikhtamim/wca | | 🧩 WCA source | github.com/sheikhtamimlover/wca | | 🤖 Example bot | github.com/sheikhtamimlover/ST_WhatsappBot | | 💬 Support Group | Join WCA Support GC | | 📸 Instagram | instagram.com/sheikh.tamim_lover | | 📞 Contact | wa.me/8801600203673 |


Made with ❤️ by Sheikh Tamim

GitHub Instagram WhatsApp Group