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

fca-anodite

v1.0.0

Published

npm wrapper for the botws FCA private WebSocket connection — drop-in compatible with / fca-unofficial API surface

Readme

botws-fca

An npm wrapper for the botws private WebSocket FCA connection — fully drop-in compatible with the @tas33n/ws3-fca / fca-unofficial API surface.

All method names (sendMessage, getThreadInfo, listen, etc.) are identical to ws3-fca, so any bot already built on that interface needs zero changes to its command or event handlers.


How it works

Instead of speaking to Facebook's servers directly, this package routes every API call through the khotools.com private WebSocket server — the same backend used by the botws bot framework.

Your bot ──► botws-fca ──► WebSocket ──► khotools.com ──► Facebook

Requirements

  • Node.js 18 or newer
  • A Facebook session cookie (c_user + xs at minimum)
  • A Khotools API key — register for free at https://khotools.com

Installation

npm install botws-fca
# or
yarn add botws-fca

Quick start

Callback style (ws3-fca drop-in)

const fca = require('botws-fca');

fca.login(
  {
    cookie: 'c_user=YOUR_UID; xs=YOUR_XS; ...', // or appState array
    apiKey: 'YOUR_KHOTOOLS_API_KEY',
    apiUrl: 'https://api.khotools.com', // default, can omit
  },
  (err, api) => {
    if (err) return console.error(err);

    api.listen((err, event) => {
      if (err) return console.error(err);

      if (event.type === 'message') {
        api.sendMessage('Hello from botws-fca!', event.threadID);
      }
    });
  }
);

Promise / async-await style

const fca = require('botws-fca');

async function main() {
  const api = await fca.login({
    cookie: 'c_user=...; xs=...;',
    apiKey: 'YOUR_KHOTOOLS_API_KEY',
  });

  api.listen(async (err, event) => {
    if (event.type === 'message') {
      await api.sendMessage('pong', event.threadID);
    }
  });
}

main().catch(console.error);

Using appState (fbstate) instead of a raw cookie string

const appState = require('./fbstate.json'); // array of { key, value, domain, ... }

const api = await fca.login({ appState, apiKey: 'YOUR_KEY' });

Configuration reference

| Field | Aliases | Required | Description | |-------|---------|----------|-------------| | cookie | appState, fbstate, state | yes | Facebook session cookie string or fbstate array | | apiKey | api_key, key | yes | Khotools API key | | apiUrl | api_url, url | no | Khotools API base URL (default: https://api.khotools.com) | | wsUrl | ws_url, wsURL | no | Override the WebSocket URL (auto-derived from apiUrl if omitted) | | botName | BOTNAME, name | no | Display name for the session | | fca | | no | FCA version: "fcaPrime" (default) or "fca2" | | reconnectMinutes | | no | Scheduled reconnect interval in minutes (default: 60) | | reconnectMs | reconnectIntervalMs | no | Reconnect interval in milliseconds (overrides reconnectMinutes) | | facebookAccount | account | no | { email, password } for auto-login fallback |


API reference

Every method mirrors ws3-fca exactly. All methods accept an optional callback (err, result) => void as the last argument. When the callback is omitted the method returns a Promise.

Messaging

// Send a text message
api.sendMessage('Hello!', threadID);

// Send with attachment (stream or Buffer)
const fs = require('fs');
api.sendMessage({ body: 'Here is a photo', attachment: fs.createReadStream('./photo.jpg') }, threadID);

// Reply to a message
api.sendMessage('Got it!', threadID, callback, messageID);
// or (third-arg reply shorthand used by some bots)
api.sendMessage('Got it!', threadID, messageID);

// Aliases expected by GoatBot / similar frameworks
api.sendMessageMqtt(msg, threadID);
api.sendMessageMqttv2(msg, threadID);

// Remove a sent message
api.unsendMessage(messageID);

// React to a message
api.setMessageReaction('😍', messageID);
// Remove a reaction
api.setMessageReaction('', messageID);

Thread & user info

const threadInfo = await api.getThreadInfo(threadID);
// threadInfo.threadName, .participantIDs, .adminIDs, .emoji, .color …

const threads = await api.getThreadList(10, null, ['INBOX']);

const users = await api.getUserInfo(['123456789', '987654321']);
// users['123456789'].name, .profileUrl, .thumbSrc …

const message = await api.getMessage(threadID, messageID);

Group management

// Add / remove users
api.addUserToGroup(userID, threadID);
api.addUserToGroup([uid1, uid2], threadID);  // multiple at once
api.removeUserFromGroup(userID, threadID);

// Admin
api.changeAdminStatus(threadID, userID, true);   // promote
api.changeAdminStatus(threadID, userID, false);  // demote

// Group settings
api.setTitle('New group name', threadID);
api.gcname('New group name', threadID);          // alias for setTitle
api.changeGroupImage(fs.createReadStream('./icon.jpg'), threadID);
api.changeThreadColor('#0084ff', threadID);
api.changeThreadEmoji('🎉', threadID);
api.setNickname('Nickname', threadID, participantID);

// Group call
api.callGroup(threadID);
api.callGroup(threadID, callback);

Contact & presence

api.shareContact('Check out this person', userID, threadID);
api.sendTypingIndicator(threadID);
api.markAsRead(threadID);
api.markAsReadAll();
api.markAsDelivered(threadID, messageID);
api.markAsSeen(Date.now());

Misc

const photoUrl = await api.resolvePhotoUrl(photoID);
api.emojiMqtt('❤️', 5, threadID);   // animated emoji burst
const myUID = api.getCurrentUserID();
api.logout();

Listening for events

// Identical to ws3-fca
const stopListening = api.listen((err, event) => {
  if (err) return console.error(err);
  console.log(event.type, event);
});

// listenMqtt is an alias
api.listenMqtt((err, event) => { ... });

Common event types (same as ws3-fca):

| type | Description | |------|-------------| | message | Incoming text / attachment message | | message_reply | Reply to an existing message | | message_reaction | Reaction added/removed | | message_unsend | Message unsent | | event | Group join/leave/name change etc. | | read_receipt | Read receipt from participant | | typing | Typing indicator |


Migrating from ws3-fca

If your bot currently does:

const login = require('@tas33n/ws3-fca');
login({ appState }, (err, api) => { ... });

Change it to:

const { login } = require('botws-fca');
login({ appState, apiKey: 'YOUR_KHOTOOLS_KEY' }, (err, api) => { ... });

That's it — every api.* call in your bot works unchanged.


Goat-Bot / similar framework integration

This package is designed to work as a direct replacement for the FCA dependency used in Goat-Bot, NashBot, and similar frameworks. In your bot's main entry point:

// Replace:
const { login } = require('@tas33n/ws3-fca');
// With:
const { login } = require('botws-fca');

Pass your apiKey and apiUrl in the credentials object (or merge them into your existing config) and everything else stays the same.


Getting a Khotools API key

  1. Go to https://khotools.com
  2. Register or log in
  3. Navigate to the API-Key tab
  4. Create a new key and copy it
  5. Pass it as apiKey in the login() call

License

ISC