@eryxenx/fca 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +325 -0
- package/DOCS.md +2712 -0
- package/README.md +455 -0
- package/func/checkUpdate.js +7 -0
- package/func/logAdapter.js +33 -0
- package/func/logger.js +48 -0
- package/index.d.ts +751 -0
- package/index.js +22 -0
- package/module/config.js +40 -0
- package/module/login.js +133 -0
- package/module/loginHelper.js +1365 -0
- package/module/options.js +44 -0
- package/package.json +98 -0
- package/src/api/action/addExternalModule.js +25 -0
- package/src/api/action/changeAvatar.js +137 -0
- package/src/api/action/changeBio.js +75 -0
- package/src/api/action/enableAutoSaveAppState.js +73 -0
- package/src/api/action/getCurrentUserID.js +7 -0
- package/src/api/action/handleFriendRequest.js +57 -0
- package/src/api/action/logout.js +76 -0
- package/src/api/action/refreshFb_dtsg.js +48 -0
- package/src/api/action/setPostReaction.js +106 -0
- package/src/api/action/unfriend.js +54 -0
- package/src/api/http/httpGet.js +46 -0
- package/src/api/http/httpPost.js +52 -0
- package/src/api/http/postFormData.js +47 -0
- package/src/api/messaging/addUserToGroup.js +68 -0
- package/src/api/messaging/changeAdminStatus.js +126 -0
- package/src/api/messaging/changeArchivedStatus.js +55 -0
- package/src/api/messaging/changeBlockedStatus.js +48 -0
- package/src/api/messaging/changeGroupImage.js +91 -0
- package/src/api/messaging/changeNickname.js +70 -0
- package/src/api/messaging/changeThreadColor.js +79 -0
- package/src/api/messaging/changeThreadEmoji.js +111 -0
- package/src/api/messaging/createNewGroup.js +88 -0
- package/src/api/messaging/createPoll.js +46 -0
- package/src/api/messaging/createThemeAI.js +98 -0
- package/src/api/messaging/deleteMessage.js +136 -0
- package/src/api/messaging/deleteThread.js +56 -0
- package/src/api/messaging/editMessage.js +105 -0
- package/src/api/messaging/forwardAttachment.js +57 -0
- package/src/api/messaging/forwardMessage.js +134 -0
- package/src/api/messaging/getEmojiUrl.js +29 -0
- package/src/api/messaging/getFriendsList.js +82 -0
- package/src/api/messaging/getMessage.js +829 -0
- package/src/api/messaging/getThemePictures.js +62 -0
- package/src/api/messaging/handleMessageRequest.js +65 -0
- package/src/api/messaging/markAsDelivered.js +57 -0
- package/src/api/messaging/markAsRead.js +88 -0
- package/src/api/messaging/markAsReadAll.js +56 -0
- package/src/api/messaging/markAsSeen.js +68 -0
- package/src/api/messaging/muteThread.js +50 -0
- package/src/api/messaging/pinMessage.js +115 -0
- package/src/api/messaging/removeUserFromGroup.js +62 -0
- package/src/api/messaging/resolvePhotoUrl.js +43 -0
- package/src/api/messaging/scheduler.js +264 -0
- package/src/api/messaging/searchForThread.js +53 -0
- package/src/api/messaging/sendBroadcast.js +93 -0
- package/src/api/messaging/sendMessage.js +269 -0
- package/src/api/messaging/sendTypingIndicator.js +90 -0
- package/src/api/messaging/sessionGuard.js +130 -0
- package/src/api/messaging/setMessageReaction.js +109 -0
- package/src/api/messaging/setTitle.js +124 -0
- package/src/api/messaging/shareContact.js +98 -0
- package/src/api/messaging/threadColors.js +128 -0
- package/src/api/messaging/unsendMessage.js +105 -0
- package/src/api/messaging/uploadAttachment.js +492 -0
- package/src/api/socket/OldMessage.js +186 -0
- package/src/api/socket/core/connectMqtt.js +269 -0
- package/src/api/socket/core/emitAuth.js +103 -0
- package/src/api/socket/core/getSeqID.js +321 -0
- package/src/api/socket/core/getTaskResponseData.js +25 -0
- package/src/api/socket/core/parseDelta.js +387 -0
- package/src/api/socket/detail/buildStream.js +215 -0
- package/src/api/socket/detail/constants.js +28 -0
- package/src/api/socket/e2ee/crypto.js +173 -0
- package/src/api/socket/e2ee/index.js +925 -0
- package/src/api/socket/e2ee/localMediaServer.js +59 -0
- package/src/api/socket/e2ee/mediaDecode.js +155 -0
- package/src/api/socket/e2ee/native/NOTICE.md +29 -0
- package/src/api/socket/e2ee/native/build/messagix.dll +0 -0
- package/src/api/socket/e2ee/native/build/messagix.so +0 -0
- package/src/api/socket/e2ee/native/lib/index.mjs +1426 -0
- package/src/api/socket/e2ee/native/nativeMediaBridge.js +233 -0
- package/src/api/socket/e2ee/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/e2ee/proto/proto-writer.ts +76 -0
- package/src/api/socket/e2ee/protocol.js +196 -0
- package/src/api/socket/e2ee/ratchet.js +219 -0
- package/src/api/socket/e2ee/store.js +182 -0
- package/src/api/socket/e2ee/vendor/fme/dist/index.cjs +6477 -0
- package/src/api/socket/e2ee/vendor/fme/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/vendor/fme/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/vendor/fme/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/vendor/fme/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/vendor/fme/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/vendor/fme/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/vendor/fme/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/listenE2EE.js +75 -0
- package/src/api/socket/listenMqtt.js +436 -0
- package/src/api/socket/middleware/index.js +216 -0
- package/src/api/socket/sendMessage.js +314 -0
- package/src/api/socket/sendMessageMqtt.js +69 -0
- package/src/api/threads/getThreadHistory.js +664 -0
- package/src/api/threads/getThreadInfo.js +329 -0
- package/src/api/threads/getThreadList.js +293 -0
- package/src/api/threads/getThreadPictures.js +78 -0
- package/src/api/users/getUserID.js +65 -0
- package/src/api/users/getUserInfo.js +402 -0
- package/src/api/users/getUserInfoV2.js +134 -0
- package/src/app/MessengerBot.js +209 -0
- package/src/app/MessengerContext.js +32 -0
- package/src/app/createFcaClient.js +136 -0
- package/src/app/threadInfoRealtimeSync.js +284 -0
- package/src/core/sendReqMqtt.js +96 -0
- package/src/database/helpers.js +53 -0
- package/src/database/models/index.js +88 -0
- package/src/database/models/thread.js +50 -0
- package/src/database/models/user.js +46 -0
- package/src/database/threadData.js +94 -0
- package/src/database/userData.js +98 -0
- package/src/remote/remoteClient.js +123 -0
- package/src/utils/broadcast.js +51 -0
- package/src/utils/client.js +10 -0
- package/src/utils/constants.js +23 -0
- package/src/utils/cookies.js +68 -0
- package/src/utils/format/attachment.js +357 -0
- package/src/utils/format/cookie.js +9 -0
- package/src/utils/format/date.js +50 -0
- package/src/utils/format/decode.js +44 -0
- package/src/utils/format/delta.js +194 -0
- package/src/utils/format/ids.js +64 -0
- package/src/utils/format/index.js +64 -0
- package/src/utils/format/message.js +88 -0
- package/src/utils/format/presence.js +132 -0
- package/src/utils/format/readTyp.js +44 -0
- package/src/utils/format/thread.js +42 -0
- package/src/utils/format/utils.js +141 -0
- package/src/utils/headers.js +128 -0
- package/src/utils/loginParser/autoLogin.js +125 -0
- package/src/utils/loginParser/helpers.js +43 -0
- package/src/utils/loginParser/index.js +10 -0
- package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
- package/src/utils/loginParser/textUtils.js +28 -0
- package/src/utils/nexca-logger.js +144 -0
- package/src/utils/nexca-utils.js +686 -0
- package/src/utils/request/client.js +26 -0
- package/src/utils/request/config.js +23 -0
- package/src/utils/request/defaults.js +46 -0
- package/src/utils/request/helpers.js +46 -0
- package/src/utils/request/index.js +17 -0
- package/src/utils/request/methods.js +163 -0
- package/src/utils/request/proxy.js +21 -0
- package/src/utils/request/retry.js +77 -0
- package/src/utils/request/sanitize.js +49 -0
- package/src/utils/versionCheck.js +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 💬 @eryxenx/fca
|
|
4
|
+
|
|
5
|
+
**Unofficial Facebook Messenger Bot API for Node.js**
|
|
6
|
+
NEXCA MQTT · Signal Protocol E2EE (mautrix-go powered) · sessionGuard · 90+ API Methods · Zero TypeScript
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/@eryxenx/fca)
|
|
9
|
+
[](./LICENSE-MIT)
|
|
10
|
+
[](https://nodejs.org)
|
|
11
|
+
|
|
12
|
+
[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [E2EE](#-e2ee--encrypted-conversations) • [sessionGuard](#-sessionguard) • [sendBroadcast](#-sendbroadcast) • [API Reference](#-api-reference)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ⚡ Why @eryxenx/fca?
|
|
19
|
+
|
|
20
|
+
- ✅ **NEXCA MQTT** — stable connection core, autoReconnect, jitter
|
|
21
|
+
- ✅ **Signal Protocol E2EE** — Facebook real encrypted conversations support. Reliable media/reaction/unsend delivery is powered by a bundled **mautrix-go / mautrix-meta** native engine (© Tulir Asokan, MPL-2.0) — NEXCA's own hand-written E2EE encoder produced protocol-valid messages that Facebook accepted but Messenger clients failed to render for attachments/reactions/unsend, so this fork routes those through the native engine instead. Text messaging still uses the original engine (already reliable).
|
|
22
|
+
- ✅ **sessionGuard** — appstate corruption and silent logout protection, auto-backup
|
|
23
|
+
- ✅ **sendBroadcast** — rate-limited multi-thread broadcast
|
|
24
|
+
- ✅ **Fixed MQTT subscribe race condition** — no more "Connection refused: No subscription existed"
|
|
25
|
+
- ✅ **`isActiveClient()` guard** — stale MQTT client events no longer processed
|
|
26
|
+
- ✅ **`connectTimeout` extended** — no premature logout on slow networks
|
|
27
|
+
- ✅ **autoReconnect** — auto-reconnect on connection drop
|
|
28
|
+
- ✅ **GoatBot compatible** — all API signatures unchanged (`threadID` optional etc.)
|
|
29
|
+
- ✅ **90+ API methods** — sendMessage, editMessage, setMessageReaction, getThreadInfo and more
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## ✨ Features
|
|
34
|
+
|
|
35
|
+
- ✅ Full Messenger API — messages, reactions, attachments, stickers, polls, pins
|
|
36
|
+
- ✅ NEXCA MQTT — stable connection, autoReconnect, jitter, isActiveClient guard
|
|
37
|
+
- ✅ E2EE — Signal Protocol encrypted threads, auto-connects after login (listenE2EE, e2ee.*)
|
|
38
|
+
- ✅ E2EE media engine — native mautrix-go engine for reliable image/video/audio/document sending, reactions, and unsend in encrypted threads (falls back to the built-in JS engine automatically)
|
|
39
|
+
- ✅ E2EE incoming media — attachments on encrypted messages (including replies) are auto-decrypted and served locally, so existing commands work unmodified
|
|
40
|
+
- ✅ sessionGuard — appstate auto-save, corruption guard, .bak backup
|
|
41
|
+
- ✅ sendBroadcast — parallel/sequential multi-thread sending with rate limit
|
|
42
|
+
- ✅ MessengerBot — Discord.js/Telegraf style (.command, .hears, .launch)
|
|
43
|
+
- ✅ createFcaClient — namespaced facade (client.messages, client.threads etc.)
|
|
44
|
+
- ✅ GoatBot / Mirai compatible — drop-in replacement
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 📦 Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install @eryxenx/fca
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Node.js >= 18 required.
|
|
55
|
+
|
|
56
|
+
The E2EE media engine ships a precompiled native binary (`src/api/socket/e2ee/native/build/`) for **Linux (.so)** and **Windows (.dll)**, loaded via the `koffi` FFI. `npm install` pulls in `koffi` and `yumi-json-bigint` automatically — no extra setup needed. If your deployment platform isn't Linux/Windows x64 (e.g. macOS, ARM), the native engine will fail to load and E2EE media/reactions/unsend automatically fall back to the built-in JS engine (text messaging is unaffected either way).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🚀 Quick Start
|
|
61
|
+
|
|
62
|
+
### Classic (GoatBot compatible)
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
const login = require("@eryxenx/fca");
|
|
66
|
+
|
|
67
|
+
login({ appState: require("./account.json") }, { listenEvents: true }, (err, api) => {
|
|
68
|
+
if (err) throw err;
|
|
69
|
+
|
|
70
|
+
api.sessionGuard("./account.json", {
|
|
71
|
+
interval: 3 * 60 * 1000,
|
|
72
|
+
debounce: 30 * 1000
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
api.listenMqtt((err, event) => {
|
|
76
|
+
if (err) throw err;
|
|
77
|
+
if (event.type === "message") api.sendMessage(event.body, event.threadID);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### With E2EE (regular + encrypted threads)
|
|
83
|
+
|
|
84
|
+
`connectE2EE()` runs **automatically** right after login — you never call it yourself. Calling it again in your own code opens a second, racing connection on the same bridge. Just check `api.e2ee.isConnected()` if you need to know the status.
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
login({ appState: require("./account.json") }, { listenEvents: true }, (err, api) => {
|
|
88
|
+
if (err) throw err;
|
|
89
|
+
|
|
90
|
+
api.sessionGuard("./account.json");
|
|
91
|
+
// E2EE is already connecting in the background here — do not call connectE2EE().
|
|
92
|
+
|
|
93
|
+
api.listenE2EE((err, event) => {
|
|
94
|
+
if (err) throw err;
|
|
95
|
+
if (event.type === "message") {
|
|
96
|
+
if (event.isE2EE) {
|
|
97
|
+
api.e2ee.sendMessage(event.threadID, "Got your encrypted message!");
|
|
98
|
+
} else {
|
|
99
|
+
api.sendMessage("Got it!", event.threadID);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### GoatBot login.js
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const login = require("@eryxenx/fca");
|
|
110
|
+
|
|
111
|
+
login({ appState }, options, (err, api) => {
|
|
112
|
+
if (err) return;
|
|
113
|
+
|
|
114
|
+
api.sessionGuard(path.join(process.cwd(), "account.txt"), {
|
|
115
|
+
interval: 3 * 60 * 1000,
|
|
116
|
+
debounce: 30 * 1000
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Nothing else needed for E2EE — it auto-connects inside @eryxenx/fca
|
|
120
|
+
// right after login. Do NOT call api.connectE2EE() here.
|
|
121
|
+
|
|
122
|
+
api.listenMqtt(callback);
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**This is the only setup needed — nothing else in `login.js` (or anywhere in GoatBot) has to change for E2EE.** Once login succeeds, `api.e2ee` is already connecting in the background, and every existing GoatBot call — `api.sendMessage(...)` (with or without `attachment`), `api.setMessageReaction(...)`, `api.unsendMessage(...)` — automatically detects when a thread is an encrypted DM and routes through the E2EE engine internally. Commands don't need to know or care whether a thread is encrypted; they call the same functions either way.
|
|
127
|
+
|
|
128
|
+
#### Adding this to your own login.js
|
|
129
|
+
|
|
130
|
+
Your bot's actual `login.js` will look different from the snippet above (dashboard setup, database sync, custom logging, etc.) — you're not replacing the file, just adding/checking two things inside your existing login callback:
|
|
131
|
+
|
|
132
|
+
| Step | What to do |
|
|
133
|
+
|---|---|
|
|
134
|
+
| 1. sessionGuard | Add `api.sessionGuard(accountPath, { interval, debounce })` once, right after login succeeds, if it's not already there. |
|
|
135
|
+
| 2. E2EE | Nothing to add — it's automatic. If your `login.js` already has an explicit `await api.connectE2EE()` call, **delete it** — it's redundant with the automatic connection and the two can race. |
|
|
136
|
+
|
|
137
|
+
To check whether E2EE is already wired into your bot, search for `e2ee.isConnected` in your `login.js` — if it's there, setup is already done.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 🔐 E2EE — Encrypted Conversations
|
|
142
|
+
|
|
143
|
+
Uses Facebook's real Signal Protocol infrastructure — same as the official Messenger app.
|
|
144
|
+
|
|
145
|
+
### Setup
|
|
146
|
+
|
|
147
|
+
E2EE connects automatically after login (creating `.nexca/e2ee_device.json` on first run) — there's nothing to call manually. Just check status once connected:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
console.log(api.e2ee.isConnected()); // true (may need a moment right after login)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Listen (regular + E2EE combined)
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
api.listenE2EE((err, event) => {
|
|
157
|
+
if (event.type === "message") {
|
|
158
|
+
if (event.isE2EE) {
|
|
159
|
+
api.e2ee.sendMessage(event.threadID, "Encrypted reply!");
|
|
160
|
+
} else {
|
|
161
|
+
api.sendMessage("Normal reply!", event.threadID);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### E2EE Methods
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
await api.e2ee.sendMessage(threadID, "Hello!");
|
|
171
|
+
await api.e2ee.sendMessage(threadID, { body: "Photo!", attachment: fs.createReadStream("photo.jpg") });
|
|
172
|
+
await api.e2ee.sendReaction(threadID, messageID, "❤️");
|
|
173
|
+
await api.e2ee.sendTyping(threadID, true);
|
|
174
|
+
await api.e2ee.unsendMessage(messageID, threadID);
|
|
175
|
+
await api.e2ee.editMessage(threadID, messageID, "Updated!");
|
|
176
|
+
api.e2ee.isConnected();
|
|
177
|
+
await api.e2ee.disconnect();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Native media engine
|
|
181
|
+
|
|
182
|
+
Sending image/video/audio/document attachments, reactions, and unsend in E2EE threads is routed through a precompiled **mautrix-go / mautrix-meta** binary (`src/api/socket/e2ee/native/`) via the `koffi` FFI, instead of this project's own hand-written Signal Protocol encoder. This project's own encoder builds protocol-valid messages that Facebook's servers accept, but real Messenger clients silently fail to render — the native engine avoids that entirely.
|
|
183
|
+
|
|
184
|
+
- Text messages always use @eryxenx/fca's own E2EE engine (already reliable, no native dependency).
|
|
185
|
+
- If the native engine fails to load or a send fails (unsupported platform, binary mismatch, etc.), @eryxenx/fca automatically falls back to its own engine and logs `[native-media] ... falling back to legacy vendor engine`.
|
|
186
|
+
- See `src/api/socket/e2ee/native/NOTICE.md` for the required MPL-2.0 attribution to the upstream mautrix-go/mautrix-meta project (© Tulir Asokan and contributors) — do not remove it if you redistribute this fork.
|
|
187
|
+
|
|
188
|
+
### Receiving E2EE media (attachments & replies)
|
|
189
|
+
|
|
190
|
+
Incoming encrypted image/video/audio/document messages are automatically decrypted and served over a short-lived local URL (`http://127.0.0.1:<port>/<token>`, 15 min TTL), and populated into `event.attachments` / `event.messageReply.attachments` — same shape as normal (non-E2EE) attachments. Reply-based commands (e.g. "reply to an image with `/imgur`") work the same in E2EE threads as in normal ones, no command code changes needed.
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
if (event.messageReply && event.messageReply.attachments.length) {
|
|
194
|
+
const url = event.messageReply.attachments[0].url; // works for E2EE too
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
If media resolution fails, check the console for `[media-resolve]` / `[media-decode]` errors — this usually means the CDN download host (`FB_E2EE_MEDIA_DOWNLOAD_HOST` env var, default `rupload.facebook.com`) needs adjusting for your account/region.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 🛡️ sessionGuard
|
|
203
|
+
|
|
204
|
+
Protects your appstate from corruption and silent logouts.
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
api.sessionGuard("./account.json");
|
|
208
|
+
|
|
209
|
+
// Custom timing
|
|
210
|
+
api.sessionGuard("./account.json", {
|
|
211
|
+
interval: 3 * 60 * 1000,
|
|
212
|
+
debounce: 30 * 1000
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
What it does:
|
|
217
|
+
- Auto-saves appstate every N minutes
|
|
218
|
+
- Saves after every successful sendMessage (debounced)
|
|
219
|
+
- Corruption guard — never overwrites a larger appstate with a smaller one
|
|
220
|
+
- Auto-backup — writes `.bak` before every overwrite
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
api.saveSession(); // force save now
|
|
224
|
+
api.restoreSessionBackup(); // restore from .bak
|
|
225
|
+
api.stopSessionGuard(); // stop the timer
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 📡 sendBroadcast
|
|
231
|
+
|
|
232
|
+
Rate-limited multi-thread broadcast.
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
const result = await api.sendBroadcast(
|
|
236
|
+
"Hello everyone!",
|
|
237
|
+
["THREAD_1", "THREAD_2", "THREAD_3"],
|
|
238
|
+
{
|
|
239
|
+
delay: 2000,
|
|
240
|
+
parallel: 2,
|
|
241
|
+
onEach: (err, info, id) => {
|
|
242
|
+
console.log(err ? "Failed: " + id : "Sent: " + id);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
console.log(result.sent.length + "/" + result.total + " delivered");
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 🤖 MessengerBot
|
|
252
|
+
|
|
253
|
+
Discord.js/Telegraf style high-level bot class.
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
const { createMessengerBot } = require("@eryxenx/fca");
|
|
257
|
+
|
|
258
|
+
const bot = await createMessengerBot(
|
|
259
|
+
{ appState: require("./account.json") },
|
|
260
|
+
{ commandPrefix: "/", stopOnSignals: true }
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
bot.command("ping", async ctx => await ctx.replyAsync("pong 🏓"));
|
|
264
|
+
bot.hears(/hello/i, async ctx => await ctx.replyAsync("Hi! 👋"));
|
|
265
|
+
bot.on("messageCreate", event => console.log(event.body));
|
|
266
|
+
|
|
267
|
+
await bot.launch({ stopOnSignals: true });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 🎯 createFcaClient
|
|
273
|
+
|
|
274
|
+
Namespaced facade grouping all API methods by domain.
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
const { createFcaClient } = require("@eryxenx/fca");
|
|
278
|
+
const client = createFcaClient(api);
|
|
279
|
+
|
|
280
|
+
await client.messages.send("Hello!", threadID);
|
|
281
|
+
await client.messages.react("❤️", messageID, threadID);
|
|
282
|
+
await client.threads.getInfo(threadID);
|
|
283
|
+
await client.users.getInfo(userID);
|
|
284
|
+
await client.account.refreshDtsg();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 📖 API Reference
|
|
290
|
+
|
|
291
|
+
### Sending Messages
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
api.sendMessage("Hello!", threadID);
|
|
295
|
+
api.sendMessage({ body: "Photo!", attachment: fs.createReadStream("photo.jpg") }, threadID);
|
|
296
|
+
api.sendMessage({ body: "Hey @John", mentions: [{ id: "uid", tag: "@John", fromIndex: 4 }] }, threadID);
|
|
297
|
+
api.sendMessage({ sticker: "369239263222822" }, threadID);
|
|
298
|
+
api.sendMessage({ location: { latitude: 23.8, longitude: 90.4, current: true } }, threadID);
|
|
299
|
+
api.sendBroadcast("msg", ["tid1", "tid2"], { delay: 2000 });
|
|
300
|
+
api.sendGif("https://media.giphy.com/xyz.gif", threadID);
|
|
301
|
+
api.sendLocation(23.8, 90.4, threadID);
|
|
302
|
+
api.sendImage("./photo.jpg", threadID, "caption");
|
|
303
|
+
api.sendVideo("./video.mp4", threadID);
|
|
304
|
+
api.sendAudio("./voice.ogg", threadID);
|
|
305
|
+
api.sendFile("./doc.pdf", threadID);
|
|
306
|
+
api.shareLink("https://github.com", threadID, "Check this!");
|
|
307
|
+
api.shareContact("Meet my friend!", userID, threadID);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Message Actions
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
api.editMessage("Updated text", messageID);
|
|
314
|
+
api.unsendMessage(messageID);
|
|
315
|
+
api.deleteMessage([messageID]);
|
|
316
|
+
api.setMessageReaction("😍", messageID, threadID); // threadID optional
|
|
317
|
+
api.setMessageReaction("", messageID); // remove reaction
|
|
318
|
+
api.getMessage(threadID, messageID);
|
|
319
|
+
api.forwardAttachment(attachmentID, [userID]);
|
|
320
|
+
api.uploadAttachment([fs.createReadStream("photo.jpg")]);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Read Receipts & Typing
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
api.markAsRead(threadID);
|
|
327
|
+
api.markAsReadAll();
|
|
328
|
+
api.markAsDelivered(threadID, messageID);
|
|
329
|
+
api.markAsSeen();
|
|
330
|
+
api.sendTypingIndicator(threadID, true);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Thread Management
|
|
334
|
+
|
|
335
|
+
```javascript
|
|
336
|
+
api.getThreadInfo(threadID);
|
|
337
|
+
api.getThreadList(10, null, ["INBOX"]);
|
|
338
|
+
api.getThreadHistory(threadID, 20);
|
|
339
|
+
api.createGroup("Hey!", ["uid1", "uid2"]);
|
|
340
|
+
api.deleteThread(threadID);
|
|
341
|
+
api.muteThread(threadID, 3600);
|
|
342
|
+
api.changeArchivedStatus(threadID, true);
|
|
343
|
+
api.handleMessageRequest(threadID, true);
|
|
344
|
+
api.searchForThread("query");
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Thread Customization
|
|
348
|
+
|
|
349
|
+
```javascript
|
|
350
|
+
api.setTitle("New Name", threadID);
|
|
351
|
+
api.changeThreadColor("#0084FF", threadID);
|
|
352
|
+
api.changeThreadEmoji("🔥", threadID);
|
|
353
|
+
api.changeNickname("The Boss", threadID, userID);
|
|
354
|
+
api.changeGroupImage(fs.createReadStream("group.jpg"), threadID);
|
|
355
|
+
api.changeAdminStatus(threadID, userID, true);
|
|
356
|
+
api.addUserToGroup(userID, threadID);
|
|
357
|
+
api.removeUserFromGroup(userID, threadID);
|
|
358
|
+
api.createPoll("Question?", threadID, { "Yes": false, "No": false });
|
|
359
|
+
api.pinMessage(messageID, threadID);
|
|
360
|
+
api.unpinMessage(messageID, threadID);
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### User Info
|
|
364
|
+
|
|
365
|
+
```javascript
|
|
366
|
+
api.getUserInfo(userID);
|
|
367
|
+
api.getUserID("John Doe", callback);
|
|
368
|
+
api.getUID("https://facebook.com/zuck");
|
|
369
|
+
api.getFriendsList();
|
|
370
|
+
api.getAvatarUser(userID);
|
|
371
|
+
api.getProfileInfo(userID);
|
|
372
|
+
api.getPublicData(userID);
|
|
373
|
+
api.sendFriendRequest(userID);
|
|
374
|
+
api.handleFriendRequest(userID, true);
|
|
375
|
+
api.changeBlockedStatus(userID, true);
|
|
376
|
+
api.followUser(userID);
|
|
377
|
+
api.unfollowUser(userID);
|
|
378
|
+
api.unfriend(userID);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Social
|
|
382
|
+
|
|
383
|
+
```javascript
|
|
384
|
+
api.reactToPost(postID, "love");
|
|
385
|
+
api.reactToComment(commentID, "haha");
|
|
386
|
+
api.postComment(postID, "Great post!");
|
|
387
|
+
api.sharePost(postID, "Check this!");
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Account & Config
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
api.getCurrentUserID();
|
|
394
|
+
api.getAppState();
|
|
395
|
+
api.setOptions({ listenTyping: true });
|
|
396
|
+
api.logout();
|
|
397
|
+
api.refreshFb_dtsg();
|
|
398
|
+
api.addExternalModule("myFunc", (defaultFuncs, api, ctx) => {
|
|
399
|
+
return function(text, threadID) {
|
|
400
|
+
return api.sendMessage("[BOT] " + text, threadID);
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### HTTP Utilities
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
api.httpGet(url, params, callback);
|
|
409
|
+
api.httpPost(url, form, callback);
|
|
410
|
+
api.httpPostFormData(url, form, callback);
|
|
411
|
+
api.uploadImageToImgbb(imageUrl);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## 📋 Login Options
|
|
417
|
+
|
|
418
|
+
| Option | Type | Default | Description |
|
|
419
|
+
|--------|------|---------|-------------|
|
|
420
|
+
| `selfListen` | boolean | false | Receive your own sent messages |
|
|
421
|
+
| `listenEvents` | boolean | true | Receive thread/group events |
|
|
422
|
+
| `listenTyping` | boolean | false | Receive typing indicator events |
|
|
423
|
+
| `updatePresence` | boolean | false | Receive online/offline presence events |
|
|
424
|
+
| `autoMarkDelivery` | boolean | false | Auto-mark incoming messages as delivered |
|
|
425
|
+
| `autoMarkRead` | boolean | false | Auto-mark threads as read |
|
|
426
|
+
| `autoReconnect` | boolean | true | Auto-reconnect MQTT on disconnect |
|
|
427
|
+
| `online` | boolean | false | Appear as online to others |
|
|
428
|
+
| `emitReady` | boolean | false | Emit ready event when MQTT connected |
|
|
429
|
+
| `proxy` | string | — | HTTP proxy URL |
|
|
430
|
+
| `userAgent` | string | Safari UA | Override HTTP User-Agent |
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## 📄 License
|
|
435
|
+
|
|
436
|
+
MIT License
|
|
437
|
+
|
|
438
|
+
**@eryxenx/fca** by [EryXenX (Mohammad Akash)](https://github.com/EryXenX)
|
|
439
|
+
|
|
440
|
+
> The E2EE native media engine (`src/api/socket/e2ee/native/`) bundles a
|
|
441
|
+
> precompiled binary built from **mautrix-go** / **mautrix-meta**, © Tulir
|
|
442
|
+
> Asokan and contributors, licensed under **MPL-2.0** (not MIT). See
|
|
443
|
+
> `src/api/socket/e2ee/native/NOTICE.md` for full attribution — this notice
|
|
444
|
+
> must be preserved in any redistribution of this fork.
|
|
445
|
+
NEXCA MQTT core by [Deku](https://github.com/dekuzxc) — MIT License
|
|
446
|
+
|
|
447
|
+
> Unauthorized copying or redistribution without credit is prohibited.
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
<div align="center">
|
|
452
|
+
|
|
453
|
+
Made with ❤️ by EryXenX
|
|
454
|
+
|
|
455
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const logger = require("./logger");
|
|
2
|
+
|
|
3
|
+
function formatArgs(args) {
|
|
4
|
+
const [prefix, msg] = args;
|
|
5
|
+
|
|
6
|
+
// Single argument: log as-is
|
|
7
|
+
if (msg === undefined) {
|
|
8
|
+
if (prefix instanceof Error) {
|
|
9
|
+
return prefix.stack || prefix.message || String(prefix);
|
|
10
|
+
}
|
|
11
|
+
return String(prefix);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Two arguments: mimic npmlog("tag", message)
|
|
15
|
+
const tag = prefix == null ? "" : String(prefix);
|
|
16
|
+
if (msg instanceof Error) {
|
|
17
|
+
const base = msg.message || String(msg);
|
|
18
|
+
return tag ? `${tag}: ${base}` : base;
|
|
19
|
+
}
|
|
20
|
+
const text = msg == null ? "" : String(msg);
|
|
21
|
+
return tag ? `${tag}: ${text}` : text;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const log = {
|
|
25
|
+
info: (...args) => logger(formatArgs(args), "info"),
|
|
26
|
+
warn: (...args) => logger(formatArgs(args), "warn"),
|
|
27
|
+
error: (...args) => logger(formatArgs(args), "error"),
|
|
28
|
+
verbose: (...args) => logger(formatArgs(args), "info"),
|
|
29
|
+
silly: (...args) => logger(formatArgs(args), "info")
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
module.exports = log;
|
|
33
|
+
|
package/func/logger.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const chalk = require("chalk");
|
|
2
|
+
const gradient = require("gradient-string");
|
|
3
|
+
|
|
4
|
+
const themes = [
|
|
5
|
+
"blue", "dream2", "dream", "fiery", "rainbow", "pastel", "cristal", "red", "aqua", "pink", "retro", "sunlight", "teen", "summer", "flower", "ghost", "hacker"
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
function buildGradient(name) {
|
|
9
|
+
const t = String(name || "").toLowerCase();
|
|
10
|
+
if (t === "blue") return gradient([{ color: "#1affa3", pos: 0.2 }, { color: "cyan", pos: 0.4 }, { color: "pink", pos: 0.6 }, { color: "cyan", pos: 0.8 }, { color: "#1affa3", pos: 1 }]);
|
|
11
|
+
if (t === "dream2") return gradient("blue", "pink");
|
|
12
|
+
if (t === "dream") return gradient([{ color: "blue", pos: 0.2 }, { color: "pink", pos: 0.3 }, { color: "gold", pos: 0.6 }, { color: "pink", pos: 0.8 }, { color: "blue", pos: 1 }]);
|
|
13
|
+
if (t === "fiery") return gradient("#fc2803", "#fc6f03", "#fcba03");
|
|
14
|
+
if (t === "rainbow") return gradient.rainbow;
|
|
15
|
+
if (t === "pastel") return gradient.pastel;
|
|
16
|
+
if (t === "cristal") return gradient.cristal;
|
|
17
|
+
if (t === "red") return gradient("red", "orange");
|
|
18
|
+
if (t === "aqua") return gradient("#0030ff", "#4e6cf2");
|
|
19
|
+
if (t === "pink") return gradient("#d94fff", "purple");
|
|
20
|
+
if (t === "retro") return gradient.retro;
|
|
21
|
+
if (t === "sunlight") return gradient("orange", "#ffff00", "#ffe600");
|
|
22
|
+
if (t === "teen") return gradient.teen;
|
|
23
|
+
if (t === "summer") return gradient.summer;
|
|
24
|
+
if (t === "flower") return gradient("blue", "purple", "yellow", "#81ff6e");
|
|
25
|
+
if (t === "ghost") return gradient.mind;
|
|
26
|
+
if (t === "hacker") return gradient("#47a127", "#0eed19", "#27f231");
|
|
27
|
+
return gradient("#243aff", "#4687f0", "#5800d4");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const themeName = themes[Math.floor(Math.random() * themes.length)];
|
|
31
|
+
const co = buildGradient(themeName);
|
|
32
|
+
|
|
33
|
+
module.exports = (text, type) => {
|
|
34
|
+
const s = String(type || "info").toLowerCase();
|
|
35
|
+
if (s === "warn") {
|
|
36
|
+
process.stderr.write(co(`\r[ FCA-WARN ] > ${text}`) + "\n");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (s === "error") {
|
|
40
|
+
process.stderr.write(chalk.bold.hex("#ff0000")(`\r[ FCA-ERROR ]`) + ` > ${text}\n`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (s === "info") {
|
|
44
|
+
process.stderr.write(chalk.bold(co(`\r[ FCA-UNO ] > ${text}`)) + "\n");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
process.stderr.write(chalk.bold(co(`\r[ ${s.toUpperCase()} ] > ${text}`)) + "\n");
|
|
48
|
+
};
|