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

@jikey/fcazero

v1.8.13

Published

Facebook Messenger bot, and is one of the most advanced next-generation Facebook Chat API (FCA)

Readme

🚀 FcaZero - Facebook Messenger API for Node.js

npm version TypeScript Node.js License: MIT

FcaZero là thư viện Facebook Messenger API hiện đại và mạnh mẽ nhất, được viết lại hoàn toàn bằng TypeScript theo kiến trúc OOP (Object-Oriented Programming). Dự án này là phiên bản nâng cấp từ ws3-fca, cung cấp API đầy đủ, type-safe và dễ mở rộng cho việc tương tác với Facebook Messenger.

✨ Tính năng nổi bật

🔐 Xác thực & Bảo mật

  • Đa phương thức đăng nhập: AppState/Cookie, Email/Password
  • Tự động reconnect với retry logic thông minh
  • Session management an toàn với cookie handling
  • Proxy support cho các khu vực bị hạn chế
  • Rate limiting tự động để tránh bị block

💬 Tin nhắn & Tương tác Realtime

  • MQTT WebSocket - Real-time messaging với hiệu suất cao
  • Gửi tin nhắn: Text, file, sticker, emoji, mentions
  • Reply & forward tin nhắn
  • Chỉnh sửa & thu hồi tin nhắn đã gửi
  • Typing indicator - hiển thị trạng thái đang gõ
  • Message reactions - thả cảm xúc tin nhắn
  • Read receipts - đánh dấu đã đọc, đã nhận, đã xem
  • Auto mark delivered/read - tự động đánh dấu trạng thái

👥 Quản lý nhóm & người dùng

  • Thread management: tạo nhóm, thêm/xóa thành viên
  • Admin controls: thay đổi quyền admin, approval mode
  • Customization: đổi tên, màu sắc, emoji, avatar nhóm
  • User info: lấy thông tin chi tiết người dùng
  • Friends list: quản lý danh sách bạn bè
  • Search functionality: tìm kiếm thread và người dùng
  • Thread history: lấy lịch sử tin nhắn với phân trang

🎯 Event Handling

  • Message events: tin nhắn mới, reply, edit, unsend
  • Typing events: trạng thái đang gõ
  • Presence events: online/offline status
  • Thread events: join/leave, rename, color change
  • Friend requests: nhận/hủy lời mời kết bạn
  • Group polls: tạo và quản lý polls trong nhóm

🛠️ Kiến trúc kỹ thuật

  • TypeScript 100% với full type safety
  • Modular OOP architecture dễ mở rộng
  • Event-driven với Emittery
  • Professional logging với Pino
  • Cross-platform: Windows, macOS, Linux
  • Memory efficient với connection pooling

📦 Cài đặt

Yêu cầu hệ thống

  • Node.js: v20.0.0+ (LTS recommended)
  • NPM: v9.0.0+
  • Memory: 512MB+ RAM
  • Storage: 100MB+ free space
npm install @jikey/fcazero

Cài đặt từ source (Development)

git clone https://github.com/JIKEY002/jikey-FcaZero.git
cd FcaZero
npm install
npm run build
npm run dev

🚀 Hướng dẫn sử dụng

1. Chuẩn bị Credentials

Phương pháp 1: AppState (Khuyến nghị)

Sử dụng Extension trình duyệt:

  1. Cài đặt extension C3C FbState hoặc CookieEditor
  2. Đăng nhập Facebook
  3. Xuất cookie và lưu thành file appstate.json:
[
  {
    "key": "c_user",
    "value": "100012345678900"
  },
  {
    "key": "datr",
    "value": "your-datr-value"
  },
  {
    "key": "sb",
    "value": "your-sb-value"
  },
  {
    "key": "fr",
    "value": "your-fr-value"
  }
]

Phương pháp 2: Email/Password

const credentials = {
  email: '[email protected]',
  password: 'your-password'
};

2. Khởi tạo Client cơ bản

import { FacebookClient } from '@jikey/fcazero';

const credentials = { appState: require('./appstate.json') };
const options = {
  selfListen: false,
  listenEvents: true,
  autoReconnect: true,
  online: true,
  autoMarkRead: true,
  autoMarkDelivery: false,
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
};

const client = new FacebookClient(credentials, options, (err, api) => {
  if (err) {
    console.error('❌ Đăng nhập thất bại:', err);
    return;
  }
  
  console.log('✅ Đăng nhập thành công!');
  
  // Khởi tạo listener
  startListening(api);
});

// Bắt đầu đăng nhập
client.login();

3. Lắng nghe tin nhắn với listenMqtt.call

Cách sử dụng chính xác api.listenMqtt.call():

function startListening(api) {
  // Sử dụng .call() method đúng cách
  api.listenMqtt.call(null, (err, event) => {
    if (err) {
      console.error('❌ MQTT Error:', err);
      return;
    }
    
    console.log('📨 Event received:', event);
    
    // Xử lý các loại event khác nhau
    handleEvent(api, event);
  });
}

function handleEvent(api, event) {
  switch(event.type) {
    case 'message':
      handleMessage(api, event);
      break;
      
    case 'message_reply':
      handleMessageReply(api, event);
      break;
      
    case 'message_reaction':
      handleReaction(api, event);
      break;
      
    case 'message_unsend':
      handleUnsend(api, event);
      break;
      
    case 'event':
      handleThreadEvent(api, event);
      break;
      
    case 'typ':
      handleTyping(api, event);
      break;
      
    case 'presence':
      handlePresence(api, event);
      break;
      
    case 'friend_request_received':
      handleFriendRequest(api, event);
      break;
      
    default:
      console.log('Unknown event type:', event.type);
  }
}

function handleMessage(api, event) {
  const { threadID, messageID, body, senderID, attachments, mentions } = event;
  
  console.log(`💬 ${senderID}: ${body} (${threadID})`);
  
  // Auto-reply bot example
  if (body && body.toLowerCase().startsWith('/help')) {
    api.sendMessage.call({
      body: `🤖 Available commands:
/help - Show this help
/ping - Check bot status
/info - Get thread info
/weather [city] - Get weather info`,
      threadID
    }, (err, info) => {
      if (err) console.error('Send error:', err);
      else console.log('✅ Help sent:', info.messageID);
    });
  }
  
  // Handle mentions
  if (mentions && Object.keys(mentions).length > 0) {
    console.log('👥 Mentions:', mentions);
  }
  
  // Handle attachments
  if (attachments && attachments.length > 0) {
    attachments.forEach(att => {
      console.log(`📎 Attachment: ${att.type} - ${att.url || att.name}`);
    });
  }
}

function handleMessageReply(api, event) {
  const { threadID, messageReply, body, senderID } = event;
  
  console.log(`↩️ Reply from ${senderID}: ${body}`);
  console.log(`📨 Original message: ${messageReply?.body}`);
}

function handleReaction(api, event) {
  const { threadID, messageID, reaction, senderID, userID } = event;
  
  console.log(`😀 ${senderID} reacted ${reaction} to message ${messageID}`);
}

function handleUnsend(api, event) {
  const { threadID, messageID, senderID } = event;
  
  console.log(`🗑️ ${senderID} unsent message ${messageID} in ${threadID}`);
}

function handleTyping(api, event) {
  const { threadID, from, isTyping } = event;
  
  if (isTyping) {
    console.log(`⌨️ ${from} is typing in ${threadID}...`);
  } else {
    console.log(`⌨️ ${from} stopped typing in ${threadID}`);
  }
}

function handlePresence(api, event) {
  const { userID, timestamp, statuses } = event;
  
  console.log(`👤 ${userID} presence:`, statuses);
}

4. Advanced Message Sending

// Gửi tin nhắn với attachment
api.sendMessage.call({
  body: 'Xin chào! Đây là file của tôi:',
  attachment: [
    fs.createReadStream('./image.jpg'),
    fs.createReadStream('./document.pdf')
  ],
  threadID: '1234567890'
}, (err, info) => {
  if (err) console.error('Send error:', err);
  else console.log('✅ Message sent:', info);
});

// Gửi sticker
api.sendMessage.call({
  sticker: '369239263222822',
  threadID: '1234567890'
}, (err, info) => {
  console.log('Sticker sent:', info);
});

// Reply tin nhắn
api.sendMessage.call({
  body: 'Đây là reply!',
  threadID: '1234567890',
  messageID: 'mid.1234567890' // ID của tin nhắn gốc
}, (err, info) => {
  console.log('Reply sent:', info);
});

// Gửi tin nhắn với mentions
api.sendMessage.call({
  body: 'Hello @user1 and @user2!',
  threadID: '1234567890',
  mentions: [
    {
      tag: '@user1',
      id: '100012345678901'
    },
    {
      tag: '@user2', 
      id: '100012345678902'
    }
  ]
}, (err, info) => {
  console.log('Mention message sent:', info);
});

// Gửi location
api.sendMessage.call({
  body: 'My current location:',
  location: {
    latitude: 21.0285,
    longitude: 105.8542,
    current: true
  },
  threadID: '1234567890'
}, (err, info) => {
  console.log('Location sent:', info);
});

5. Thread Management

// Lấy thông tin thread
api.getThreadInfo.call('1234567890', (err, info) => {
  if (!err) {
    console.log('👥 Thread info:', {
      name: info.threadName,
      participants: info.participantIDs.length,
      messageCount: info.messageCount,
      isGroup: info.isGroup,
      emoji: info.emoji,
      color: info.color
    });
  }
});

// Thêm người vào nhóm
api.addUserToGroup.call({
  userIDs: ['100012345678900', '100012345678901'],
  threadID: '1234567890',
  isGroup: true
}, (err) => {
  if (!err) console.log('✅ Users added to group');
  else console.error('❌ Add user error:', err);
});

// Xóa người khỏi nhóm
api.removeUserFromGroup.call({
  userID: '100012345678900',
  threadID: '1234567890'
}, (err) => {
  if (!err) console.log('✅ User removed from group');
  else console.error('❌ Remove user error:', err);
});

// Đổi tên nhóm
api.setTitle.call({
  title: 'New Group Name 🚀',
  threadID: '1234567890'
}, (err) => {
  if (!err) console.log('✅ Group name changed');
});

// Đổi emoji nhóm
api.changeThreadEmoji.call({
  emoji: '🎉',
  threadID: '1234567890',
  isGroup: true
}, (err) => {
  if (!err) console.log('✅ Thread emoji changed');
});

// Đổi màu thread
api.changeThreadColor.call({
  themeID: '196241301102133', // Blue theme
  threadID: '1234567890',
  isGroup: true
}, (err) => {
  if (!err) console.log('✅ Thread color changed');
});

// Thay đổi quyền admin
api.changeAdminStatus.call({
  userID: '100012345678900',
  threadID: '1234567890',
  adminStatus: true
}, (err) => {
  if (!err) console.log('✅ Admin status changed');
});

6. User & Friends Management

// Lấy thông tin user
api.getUserInfo.call('100012345678900', (err, ret) => {
  if (!err) {
    const user = ret['100012345678900'];
    console.log(`👤 User info:`, {
      name: user.name,
      firstName: user.firstName,
      vanity: user.vanity,
      profileUrl: user.profileUrl,
      gender: user.gender,
      isFriend: user.isFriend,
      isBirthday: user.isBirthday
    });
  }
});

// Lấy UserID từ tên
api.getUserID.call('John Doe', (err, data) => {
  if (!err) {
    data.forEach(user => {
      console.log(`🔍 Found: ${user.name} (${user.userID})`);
    });
  }
});

// Lấy danh sách bạn bè
api.getFriendsList.call((err, data) => {
  if (!err) {
    console.log(`👫 You have ${data.length} friends`);
    data.slice(0, 5).forEach(friend => {
      console.log(`- ${friend.fullName} (${friend.userID})`);
    });
  }
});

// Tìm kiếm thread
api.searchForThread.call('group name', (err, results) => {
  if (!err) {
    results.forEach(thread => {
      console.log(`🔍 Found thread: ${thread.name} (${thread.threadID})`);
    });
  }
});

7. Message History & Threading

// Lấy lịch sử tin nhắn
api.getThreadHistory.call({
  threadID: '1234567890',
  amount: 50,
  timestamp: null
}, (err, history) => {
  if (!err) {
    console.log(`📜 Retrieved ${history.length} messages`);
    history.forEach(msg => {
      console.log(`${msg.senderName}: ${msg.body} (${new Date(msg.timestamp)})`);
    });
  }
});

// Lấy danh sách thread
api.getThreadList.call({
  limit: 20,
  timestamp: null,
  tags: ['INBOX']
}, (err, list) => {
  if (!err) {
    console.log(`📋 Thread list (${list.length} threads):`);
    list.forEach(thread => {
      console.log(`- ${thread.name}: ${thread.snippet} (${thread.unreadCount} unread)`);
    });
  }
});

// Lấy một tin nhắn cụ thể
api.getMessage.call('mid.1234567890', (err, message) => {
  if (!err) {
    console.log('📨 Message details:', message);
  }
});

8. Message Actions

// Đánh dấu đã đọc
api.markAsRead.call({
  threadID: '1234567890'
}, (err) => {
  if (!err) console.log('✅ Marked as read');
});

// Đánh dấu đã nhận
api.markAsDelivered.call({
  messageID: 'mid.1234567890',
  threadID: '1234567890'
}, (err) => {
  if (!err) console.log('✅ Marked as delivered');
});

// Gửi typing indicator
const typingIndicator = api.sendTypingIndicator.call({
  threadID: '1234567890',
  isGroup: true
}, (err, stopTyping) => {
  if (!err) {
    console.log('⌨️ Started typing...');
    
    // Stop typing after 3 seconds
    setTimeout(() => {
      stopTyping.end(() => {
        console.log('⌨️ Stopped typing');
      });
    }, 3000);
  }
});

// React to message
api.setMessageReaction.call({
  messageID: 'mid.1234567890',
  threadID: '1234567890',
  reaction: '😍' // Use emoji or empty string to remove
}, (err) => {
  if (!err) console.log('✅ Reaction added');
});

// Edit message
api.editMessage.call({
  messageID: 'mid.1234567890',
  newBody: 'Edited message content'
}, (err) => {
  if (!err) console.log('✅ Message edited');
});

// Unsend message
api.unsendMessage.call({
  messageID: 'mid.1234567890'
}, (err) => {
  if (!err) console.log('✅ Message unsent');
});

// Forward message
api.forwardMessage.call({
  messageID: 'mid.1234567890',
  threadID: '0987654321'
}, (err, info) => {
  if (!err) console.log('✅ Message forwarded:', info);
});

9. File Upload & Attachments

// Upload attachment trước khi gửi
api.uploadAttachment.call({
  attachments: [
    fs.createReadStream('./photo.jpg'),
    fs.createReadStream('./document.pdf'),
    fs.createReadStream('./video.mp4')
  ]
}, (err, attachmentData) => {
  if (!err) {
    console.log('📎 Attachments uploaded:', attachmentData);
    
    // Sử dụng attachment đã upload
    api.sendMessage.call({
      body: 'Here are the uploaded files:',
      attachment: attachmentData,
      threadID: '1234567890'
    });
  }
});

// Resolve photo URL
api.resolvePhotoUrl.call('photo_fbid_here', (err, url) => {
  if (!err) {
    console.log('🖼️ Photo URL:', url);
  }
});

⚙️ Cấu hình nâng cao

Global Options

const advancedOptions = {
  // Basic Settings
  selfListen: false,              // Lắng nghe tin nhắn của chính mình
  listenEvents: true,             // Lắng nghe events (join/leave/rename...)
  listenTyping: true,             // Lắng nghe typing indicator
  autoReconnect: true,            // Tự động kết nối lại
  online: true,                   // Hiển thị online
  emitReady: false,               // Emit ready event
  
  // Auto Actions
  autoMarkDelivery: false,        // Tự động đánh dấu đã nhận
  autoMarkRead: true,             // Tự động đánh dấu đã đọc
  updatePresence: false,          // Cập nhật trạng thái presence
  forceLogin: false,              // Buộc đăng nhập lại
  
  // Network Settings
  proxy: 'http://proxy:8080',     // Proxy server
  userAgent: 'custom-ua',         // User agent tùy chỉnh
  randomUserAgent: false,         // Random user agent
  bypassRegion: 'US',            // Bypass region restriction
  
  // Page Settings (for page bots)
  pageID: '123456789',           // Page ID nếu chạy bot page
  
  // Event Filtering
  selfListenEvent: ['typ'],       // Events to listen for self
};

Proxy Configuration

// HTTP Proxy
const client = new FacebookClient(credentials, {
  proxy: 'http://username:password@proxy-server:8080'
}, callback);

// SOCKS5 Proxy
const client = new FacebookClient(credentials, {
  proxy: 'socks5://127.0.0.1:1080'
}, callback);

// Proxy with authentication
const client = new FacebookClient(credentials, {
  proxy: 'http://user:[email protected]:3128',
  bypassRegion: 'US'
}, callback);

Runtime Configuration Changes

// Thay đổi options trong runtime
api.setOption.call({
  online: false,
  autoReconnect: false,
  proxy: 'socks5://127.0.0.1:1080',
  userAgent: 'New User Agent String'
}, (err, result) => {
  if (!err) {
    console.log('✅ Configuration updated:', result.updated);
    console.log('📋 Global options:', result.globalOptions);
  }
});

Connection Management

// Stop listening
api.stopListeningAsync.call();

// Soft reconnect (keep session)
api.softReconnectAsync.call();

// Hard restart (new session)
api.hardRestartAsync.call();

// Logout
api.logout.call((err) => {
  if (!err) console.log('👋 Logged out successfully');
});

// Get current AppState
api.getAppState.call((err, appState) => {
  if (!err) {
    console.log('🔑 Current AppState:', appState);
    // Save to file for next session
    fs.writeFileSync('./new_appstate.json', JSON.stringify(appState, null, 2));
  }
});

📚 Complete API Reference

Authentication Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | login() | credentials, options | Promise<void> | Đăng nhập vào Facebook | | logout.call() | - | void | Đăng xuất khỏi Facebook | | getAppState.call() | - | AppState | Lấy session state hiện tại |

Messaging Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | sendMessage.call() | MessageInput | MessageInfo | Gửi tin nhắn | | editMessage.call() | messageID, newBody | void | Chỉnh sửa tin nhắn | | unsendMessage.call() | messageID | void | Thu hồi tin nhắn | | forwardMessage.call() | messageID, threadID | MessageInfo | Chuyển tiếp tin nhắn | | setMessageReaction.call() | messageID, reaction | void | Thả cảm xúc tin nhắn | | deleteMessage.call() | messageID | void | Xóa tin nhắn |

Thread Management Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | getThreadInfo.call() | threadID | ThreadInfo | Lấy thông tin thread | | getThreadList.call() | options | ThreadInfo[] | Lấy danh sách thread | | getThreadHistory.call() | threadID, amount | Message[] | Lấy lịch sử tin nhắn | | searchForThread.call() | name | ThreadInfo[] | Tìm kiếm thread | | setTitle.call() | title, threadID | void | Đổi tên thread | | changeThreadColor.call() | themeID, threadID | void | Đổi màu thread | | changeThreadEmoji.call() | emoji, threadID | void | Đổi emoji thread | | changeGroupImage.call() | image, threadID | void | Đổi ảnh nhóm | | muteThread.call() | threadID, muteSeconds | void | Tắt thông báo thread | | changeArchivedStatus.call() | threadID, archive | void | Archive/unarchive thread |

User Management Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | getUserInfo.call() | userID | UserInfo | Lấy thông tin user | | getUserID.call() | name | UserSearchResult[] | Tìm UserID từ tên | | getFriendsList.call() | - | Friend[] | Lấy danh sách bạn bè | | addUserToGroup.call() | userIDs, threadID | void | Thêm user vào nhóm | | removeUserFromGroup.call() | userID, threadID | void | Xóa user khỏi nhóm | | changeAdminStatus.call() | userID, threadID, status | void | Thay đổi quyền admin | | changeNickname.call() | userID, threadID, nickname | void | Đổi nickname |

Message Status Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | markAsRead.call() | threadID | void | Đánh dấu đã đọc | | markAsDelivered.call() | messageID, threadID | void | Đánh dấu đã nhận | | markAsSeen.call() | threadID | void | Đánh dấu đã xem | | markAsReadAll.call() | - | void | Đánh dấu tất cả đã đọc |

Interaction Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | sendTypingIndicator.call() | threadID, isGroup | TypingControl | Gửi typing indicator | | createPoll.call() | pollData, threadID | Poll | Tạo poll trong nhóm | | createNewGroup.call() | userIDs, groupName | ThreadInfo | Tạo nhóm mới |

File & Media Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | uploadAttachment.call() | attachments | AttachmentInfo[] | Upload file attachment | | resolvePhotoUrl.call() | photoID | string | Lấy URL ảnh từ ID | | getThreadPictures.call() | threadID, offset, limit | Photo[] | Lấy ảnh trong thread |

Utility Methods

| Method | Input | Output | Description | |--------|--------|--------|-------------| | getCurrentUserID.call() | - | string | Lấy ID user hiện tại | | setOption.call() | options | OptionResult | Thay đổi cấu hình runtime | | httpGet.call() | url, options | any | HTTP GET request | | httpPost.call() | url, form, options | any | HTTP POST request | | httpPostFormData.call() | url, form, options | any | HTTP POST với FormData | | refreshConfigRequest.call() | - | void | Refresh config tokens |

Event Listening

| Method | Input | Output | Description | |--------|--------|--------|-------------| | listenMqtt.call() | null, callback | MessageEmitter | Lắng nghe events realtime | | stopListeningAsync.call() | - | void | Dừng lắng nghe | | softReconnectAsync.call() | - | void | Reconnect mềm | | hardRestartAsync.call() | - | void | Restart cứng |


🔧 Xử lý sự cố

Windows Encoding Issues

Trên Windows, có thể gặp vấn đề hiển thị tiếng Việt trong console:

[04-09-2025 08:49:28] INFO: Đặng Hoài Thương -> (100092973548760)

Giải pháp:

# Thiết lập UTF-8 cho session hiện tại
chcp 65001

# Hoặc thêm vào package.json
{
  "scripts": {
    "start": "chcp 65001 >nul && node index.js"
  }
}

Windows Terminal (khuyến nghị):

{
  "profiles": {
    "defaults": {
      "codePage": 65001
    }
  }
}

Common Errors & Solutions

1. Login Failed

❌ Error: Wrong password / email

Solutions:

  • Kiểm tra lại credentials
  • Sử dụng AppState thay vì email/password
  • Đăng nhập bằng trình duyệt để verify account
  • Kiểm tra 2FA settings

2. MQTT Connection Issues

❌ MQTT Error: Connection failed

Solutions:

// Kiểm tra network và retry
const options = {
  autoReconnect: true,
  proxy: 'http://your-proxy:8080', // if behind firewall
  bypassRegion: 'US', // bypass geo-restrictions
  userAgent: 'Mozilla/5.0...' // update user agent
};

3. Rate Limiting

❌ Error: Rate limit exceeded

Solutions:

// Implement delay between requests
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function sendMultipleMessages(api, messages) {
  for (const msg of messages) {
    await api.sendMessage.call(msg);
    await delay(1000); // Wait 1 second between messages
  }
}

4. Memory Leaks

// Proper cleanup
process.on('SIGINT', () => {
  console.log('Shutting down gracefully...');
  api.stopListeningAsync.call();
  process.exit(0);
});

// Clear intervals and timeouts
const interval = setInterval(() => {
  // Some repeated task
}, 5000);

process.on('exit', () => {
  clearInterval(interval);
});

5. Session Expired

// Auto-refresh session
api.refreshConfigRequest.call(null, (err) => {
  if (err) {
    console.log('Session might be expired, re-login required');
    // Implement re-login logic
  }
});

🏗️ Kiến trúc & Mở rộng

Project Structure

src/
├── api/                    # API implementation modules
│   ├── core/              # Core functionality
│   │   ├── parseDelta.ts  # Event parsing logic
│   │   ├── messageEmitter.ts # Event emitter
│   │   └── mqttPublish.ts # MQTT publishing
│   ├── sendMessage.ts     # Message sending
│   ├── listenMqtt.ts     # MQTT listener
│   ├── getThreadInfo.ts  # Thread information
│   └── ...                # Other API methods
├── core/                  # Core system classes
│   ├── facebookClient.ts  # Main client class
│   ├── loginHelper.ts     # Login management
│   ├── buildAPI.ts       # API builder
│   └── setOptions.ts     # Options management
├── types/                 # TypeScript type definitions
│   ├── global.ts         # Global types
│   ├── api/              # API-specific types
│   ├── core/             # Core types
│   └── utils/            # Utility types
└── utils/                 # Utility functions
    ├── axios.ts          # HTTP client
    ├── logger.ts         # Logging system
    ├── constants.ts      # Constants
    ├── formatters.ts     # Data formatters
    └── ...               # Other utilities

Creating Custom API Modules

// src/api/customFeature.ts
import type { IApiModule, Callback } from '../types/IApiModule.js';
import type { Ctx } from '../types/global.js';
import type { TypedApi } from '../types/TypedApi.js';
import type { MakeDefaultsReturn } from '../types/utils/makeDefaults.js';

interface CustomFeatureInput {
  targetID: string;
  action: string;
  data?: any;
}

interface CustomFeatureOutput {
  success: boolean;
  result: any;
}

export default class CustomFeature 
  implements IApiModule<CustomFeatureInput, CustomFeatureOutput> {
  
  constructor(
    private defaultFuncs: MakeDefaultsReturn,
    private api: TypedApi,
    private ctx: Ctx
  ) {}

  call(
    input: CustomFeatureInput, 
    callback?: Callback<CustomFeatureOutput>
  ): Promise<CustomFeatureOutput> {
    return new Promise((resolve, reject) => {
      try {
        // Validate input
        if (!input.targetID) {
          throw new Error('targetID is required');
        }

        // Implement your custom logic
        const form = {
          target_id: input.targetID,
          action: input.action,
          data: JSON.stringify(input.data || {}),
          fb_dtsg: this.ctx.fb_dtsg,
          jazoest: this.ctx.jazoest
        };

        // Make API request
        this.defaultFuncs.post('https://www.facebook.com/api/your-endpoint/', this.ctx.jar, form)
          .then(response => {
            const result = {
              success: true,
              result: response
            };

            if (callback) callback(null, result);
            resolve(result);
          })
          .catch(error => {
            if (callback) callback(error);
            reject(error);
          });

      } catch (error) {
        if (callback) callback(error as Error);
        reject(error);
      }
    });
  }
}

Event System Extensions

// Custom event handler
class AdvancedEventHandler {
  private api: TypedApi;
  private messageQueue: any[] = [];
  private rateLimiter: Map<string, number> = new Map();

  constructor(api: TypedApi) {
    this.api = api;
    this.setupEventListeners();
  }

  private setupEventListeners() {
    this.api.listenMqtt.call(null, (err, event) => {
      if (err) return;

      // Rate limiting per thread
      if (this.isRateLimited(event.threadID)) {
        this.messageQueue.push(event);
        return;
      }

      this.handleEvent(event);
    });

    // Process queued messages
    setInterval(() => this.processQueue(), 1000);
  }

  private isRateLimited(threadID: string): boolean {
    const now = Date.now();
    const lastMessage = this.rateLimiter.get(threadID) || 0;
    
    if (now - lastMessage < 1000) { // 1 second cooldown
      return true;
    }

    this.rateLimiter.set(threadID, now);
    return false;
  }

  private handleEvent(event: any) {
    switch(event.type) {
      case 'message':
        this.handleMessage(event);
        break;
      case 'message_reaction':
        this.handleReaction(event);
        break;
      // Add more event types
    }
  }

  private handleMessage(event: any) {
    // Advanced message processing
    if (event.body?.includes('!admin')) {
      this.handleAdminCommand(event);
    } else if (event.attachments?.length > 0) {
      this.handleAttachments(event);
    }
  }

  private processQueue() {
    if (this.messageQueue.length === 0) return;

    const event = this.messageQueue.shift();
    if (!this.isRateLimited(event.threadID)) {
      this.handleEvent(event);
    } else {
      this.messageQueue.unshift(event); // Put back in queue
    }
  }
}

Plugin System

// Plugin interface
interface FcaPlugin {
  name: string;
  version: string;
  init(api: TypedApi, ctx: Ctx): void;
  destroy?(): void;
}

// Example plugin
class AutoResponderPlugin implements FcaPlugin {
  name = 'auto-responder';
  version = '1.0.0';
  
  private api: TypedApi;
  private responses: Map<string, string> = new Map();

  init(api: TypedApi, ctx: Ctx) {
    this.api = api;
    
    // Load responses from config
    this.responses.set('hello', 'Hi there! 👋');
    this.responses.set('time', () => `Current time: ${new Date().toLocaleString()}`);
    
    // Listen for messages
    api.listenMqtt.call(null, (err, event) => {
      if (err || event.type !== 'message') return;
      
      const response = this.getResponse(event.body);
      if (response) {
        api.sendMessage.call({
          body: typeof response === 'function' ? response() : response,
          threadID: event.threadID
        });
      }
    });
  }

  private getResponse(message: string): string | (() => string) | null {
    const normalizedMessage = message.toLowerCase().trim();
    return this.responses.get(normalizedMessage) || null;
  }

  destroy() {
    this.responses.clear();
  }
}

// Plugin manager
class PluginManager {
  private plugins: FcaPlugin[] = [];

  register(plugin: FcaPlugin, api: TypedApi, ctx: Ctx) {
    plugin.init(api, ctx);
    this.plugins.push(plugin);
    console.log(`Plugin "${plugin.name}" v${plugin.version} loaded`);
  }

  unregisterAll() {
    this.plugins.forEach(plugin => {
      if (plugin.destroy) plugin.destroy();
    });
    this.plugins = [];
  }
}

📋 Changelog

Version 1.8.0 (Current) - September 2025

🆕 Major Features:

  • Complete TypeScript Migration - 100% type-safe codebase
  • OOP Architecture - Modular, maintainable, and extensible design
  • MQTT WebSocket Integration - Real-time messaging with auto-reconnect
  • Advanced Event System - Comprehensive event handling with Emittery
  • Plugin Architecture - Extensible plugin system for custom features
  • Session Management - Robust cookie and session handling
  • Rate Limiting - Built-in protection against API limits
  • Proxy Support - HTTP/SOCKS5 proxy with authentication
  • File Upload System - Advanced attachment handling
  • Thread Management - Complete group/thread control features

🔧 API Improvements:

  • Performance Optimizations - 40% faster message processing
  • 🛡️ Enhanced Security - Improved authentication and session security
  • 🎯 Better Error Handling - Detailed error messages and recovery
  • 📱 Cross-platform Support - Windows, macOS, Linux compatibility
  • 🔄 Auto-recovery - Smart reconnection and session refresh
  • 📝 Professional Logging - Structured logging with Pino
  • 🌐 Region Bypass - Geographic restriction bypass

🐛 Critical Bug Fixes:

  • 🔧 Fixed Windows Encoding - Proper UTF-8 support for Vietnamese
  • 🔧 Resolved Memory Leaks - Better resource management
  • 🔧 MQTT Stability - Improved connection reliability
  • 🔧 Message Formatting - Better emoji and special character handling
  • 🔧 Thread Color/Emoji - Fixed thread customization issues
  • 🔧 User Search - Improved search accuracy and performance

📚 New APIs:

  • listenMqtt.call() - Enhanced event listening with proper typing
  • setOption.call() - Runtime configuration management
  • createPoll.call() - Group poll creation and management
  • changeAdminStatus.call() - Admin rights management
  • getThreadPictures.call() - Thread media retrieval
  • refreshConfigRequest.call() - Session token refresh

Version 1.7.x - July 2025

🔄 Major Refactoring:

  • 📦 Module System Redesign - Better separation of concerns
  • 🏗️ Core Architecture - Foundation for TypeScript migration
  • 📝 TypeScript Integration - Gradual type system implementation
  • 🔌 API Standardization - Consistent method signatures

Version 1.6.x - May 2025

🆕 Core Features:

  • 💬 Basic Messaging APIs - Send, receive, edit messages
  • 👥 Group Management - Create, manage groups and members
  • 📁 File Attachments - Upload and send file attachments
  • 🔍 Search Functions - Thread and user search capabilities

Version 1.5.x - March 2025

🛠️ Infrastructure:

  • 🌐 HTTP Client - Axios-based request handling
  • 🍪 Cookie Management - Session persistence
  • 📊 Basic Logging - Simple logging system

Version 1.0.x - 1.4.x - 2024

🎯 Foundation:

  • 🏁 Initial Release - Based on ws3-fca foundation
  • 🔧 Basic Facebook API - Core messaging functionality
  • 💻 JavaScript Implementation - Original JS codebase
  • 📱 Basic Bot Support - Simple chatbot capabilities

🤝 Contributing

Contribution Guidelines

  1. Fork the repository
  2. Create feature branch: git checkout -b feature/amazing-feature
  3. Follow coding standards: TypeScript, ESLint, Prettier
  4. Add tests: Unit tests for new features
  5. Update documentation: README and JSDoc comments
  6. Commit changes: git commit -m 'feat: Add amazing feature'
  7. Push to branch: git push origin feature/amazing-feature
  8. Create Pull Request: Detailed description of changes

Development Environment

# Development setup
git clone https://github.com/JIKEY002/jikey-FcaZero.git
cd FcaZero
npm install

# Development commands
npm run dev          # Start development server
npm run build        # Build TypeScript
npm run test         # Run test suite
npm run lint         # Check code style
npm run lint:fix     # Fix code style issues
npm run format       # Format code with Prettier
npm run clean        # Clean build directory

# Release commands
npm run release:patch  # Patch version bump
npm run release:minor  # Minor version bump
npm run release:major  # Major version bump

Code Standards

// Follow these patterns for new API modules

/**
 * Brief description of what this API does
 * @example
 * ```typescript
 * api.yourMethod.call(input, (err, result) => {
 *   if (!err) console.log(result);
 * });
 * ```
 */
export default class YourMethod implements IApiModule<Input, Output> {
  constructor(
    private defaultFuncs: MakeDefaultsReturn,
    private api: TypedApi,
    private ctx: Ctx
  ) {}

  call(input: Input, callback?: Callback<Output>): Promise<Output> {
    return new Promise((resolve, reject) => {
      try {
        // Validate input
        // Implement logic
        // Handle response
        // Call callback and resolve
      } catch (error) {
        if (callback) callback(error as Error);
        reject(error);
      }
    });
  }
}

Testing

// Example test structure
describe('YourMethod', () => {
  it('should handle valid input', async () => {
    const result = await api.yourMethod.call(validInput);
    expect(result).toBeDefined();
  });

  it('should throw error for invalid input', async () => {
    await expect(api.yourMethod.call(invalidInput)).rejects.toThrow();
  });
});

🙌 Credits & Acknowledgments

Core Team

  • @jikey - Lead Developer, TypeScript migration, OOP architecture design
  • @NethWs3Dev (Kenneth Aceberos) - Original ws3-fca author and foundation
  • @ChoruOfficial - MQTT implementation, module refactoring, performance optimization
  • @CommunityExocore - Foundation design contributions and early testing

Contributors

  • Community contributors - Bug reports, feature requests, and improvements
  • Beta testers - Early adoption and feedback
  • Documentation team - README improvements and examples

Third-party Libraries

  • Emittery - Event emitter for real-time messaging
  • Pino - High-performance logging
  • MQTT.js - MQTT client implementation
  • Axios - HTTP client library
  • Tough-Cookie - Cookie management
  • TypeScript - Type safety and development experience

Special Thanks

  • Facebook - Platform APIs (unofficial usage)
  • Node.js Community - Runtime environment and ecosystem
  • Open Source Community - Inspiration and collaboration

⚖️ License

MIT License - Free to use, modify, and distribute.

Copyright (c) 2025 JIKEY

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Usage Terms

  • Commercial use allowed
  • Modification allowed
  • Distribution allowed
  • Private use allowed
  • No warranty provided
  • No liability accepted

📞 Support & Community

Getting Help

Community Resources

Reporting Issues

When reporting bugs, please include:

  • Environment: Node.js version, OS, package version
  • Code sample: Minimal reproduction case
  • Error logs: Complete error messages and stack traces
  • Expected behavior: What should happen
  • Actual behavior: What actually happens

Security Issues

For security-related issues, please email: [email protected]

Do not open public issues for security vulnerabilities.


🌟 Star History

Star History Chart

⭐ Star this repository if you find it helpful!

Made with ❤️ by JIKEY and the FcaZero community