@eryxenx/fca 1.0.6 → 1.0.7
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/README.md
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# 💬 @eryxenx/fca
|
|
4
4
|
|
|
5
5
|
**Unofficial Facebook Messenger Bot API for Node.js**
|
|
6
|
-
NEXCA MQTT
|
|
6
|
+
NEXCA MQTT · Signal Protocol E2EE (mautrix-go powered) · sessionGuard · 90+ API Methods · Zero TypeScript
|
|
7
7
|
|
|
8
8
|
[](https://www.npmjs.com/package/@eryxenx/fca)
|
|
9
9
|
[](./LICENSE-MIT)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
|
|
12
|
-
[Features](#-features)
|
|
12
|
+
[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [E2EE](#-e2ee--encrypted-conversations) • [sessionGuard](#-sessionguard) • [sendBroadcast](#-sendbroadcast) • [API Reference](#-api-reference)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## ⚡ Why @eryxenx/fca?
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
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). The native engine is opt-in (`FCA_E2EE_NATIVE=1`) — see [Native media engine](#native-media-engine).
|
|
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
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## ✨ Features
|
|
34
34
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
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 (opt-in via `FCA_E2EE_NATIVE=1`) 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
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
48
|
-
##
|
|
48
|
+
## 📦 Installation
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
npm install @eryxenx/fca
|
|
@@ -53,13 +53,13 @@ npm install @eryxenx/fca
|
|
|
53
53
|
|
|
54
54
|
Node.js >= 18 required.
|
|
55
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
|
|
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. Note the native engine only runs when `FCA_E2EE_NATIVE=1` is set (see [Native media engine](#native-media-engine)); on platforms other than Linux/Windows x64 (e.g. macOS, ARM) the binary fails to load and E2EE media/reactions/unsend automatically fall back to the built-in JS engine (text messaging is unaffected either way).
|
|
57
57
|
|
|
58
58
|
The E2EE socket's User-Agent defaults to desktop Windows Chrome 139 to match the rest of the client; override with `FCA_E2EE_UA` if you run with a different UA.
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
-
##
|
|
62
|
+
## 🚀 Quick Start
|
|
63
63
|
|
|
64
64
|
### Classic (GoatBot compatible)
|
|
65
65
|
|
|
@@ -83,14 +83,14 @@ login({ appState: require("./account.json") }, { listenEvents: true }, (err, api
|
|
|
83
83
|
|
|
84
84
|
### With E2EE (regular + encrypted threads)
|
|
85
85
|
|
|
86
|
-
`connectE2EE()` runs **automatically** right after login
|
|
86
|
+
`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.
|
|
87
87
|
|
|
88
88
|
```javascript
|
|
89
89
|
login({ appState: require("./account.json") }, { listenEvents: true }, (err, api) => {
|
|
90
90
|
if (err) throw err;
|
|
91
91
|
|
|
92
92
|
api.sessionGuard("./account.json");
|
|
93
|
-
// E2EE is already connecting in the background here
|
|
93
|
+
// E2EE is already connecting in the background here — do not call connectE2EE().
|
|
94
94
|
|
|
95
95
|
api.listenE2EE((err, event) => {
|
|
96
96
|
if (err) throw err;
|
|
@@ -118,35 +118,35 @@ login({ appState }, options, (err, api) => {
|
|
|
118
118
|
debounce: 30 * 1000
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
// Nothing else needed for E2EE
|
|
121
|
+
// Nothing else needed for E2EE — it auto-connects inside @eryxenx/fca
|
|
122
122
|
// right after login. Do NOT call api.connectE2EE() here.
|
|
123
123
|
|
|
124
124
|
api.listenMqtt(callback);
|
|
125
125
|
});
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
**This is the only setup needed
|
|
128
|
+
**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.
|
|
129
129
|
|
|
130
130
|
#### Adding this to your own login.js
|
|
131
131
|
|
|
132
|
-
Your bot's actual `login.js` will look different from the snippet above (dashboard setup, database sync, custom logging, etc.)
|
|
132
|
+
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:
|
|
133
133
|
|
|
134
134
|
| Step | What to do |
|
|
135
135
|
|---|---|
|
|
136
136
|
| 1. sessionGuard | Add `api.sessionGuard(accountPath, { interval, debounce })` once, right after login succeeds, if it's not already there. |
|
|
137
|
-
| 2. E2EE | Nothing to add
|
|
137
|
+
| 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. |
|
|
138
138
|
|
|
139
|
-
To check whether E2EE is already wired into your bot, search for `e2ee.isConnected` in your `login.js`
|
|
139
|
+
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.
|
|
140
140
|
|
|
141
141
|
---
|
|
142
142
|
|
|
143
|
-
##
|
|
143
|
+
## 🔐 E2EE — Encrypted Conversations
|
|
144
144
|
|
|
145
|
-
Uses Facebook's real Signal Protocol infrastructure
|
|
145
|
+
Uses Facebook's real Signal Protocol infrastructure — same as the official Messenger app.
|
|
146
146
|
|
|
147
147
|
### Setup
|
|
148
148
|
|
|
149
|
-
E2EE connects automatically after login (creating `.nexca/e2ee_device.json` on first run)
|
|
149
|
+
E2EE connects automatically after login (creating `.nexca/e2ee_device.json` on first run) — there's nothing to call manually. Just check status once connected:
|
|
150
150
|
|
|
151
151
|
```javascript
|
|
152
152
|
console.log(api.e2ee.isConnected()); // true (may need a moment right after login)
|
|
@@ -171,7 +171,7 @@ api.listenE2EE((err, event) => {
|
|
|
171
171
|
```javascript
|
|
172
172
|
await api.e2ee.sendMessage(threadID, "Hello!");
|
|
173
173
|
await api.e2ee.sendMessage(threadID, { body: "Photo!", attachment: fs.createReadStream("photo.jpg") });
|
|
174
|
-
await api.e2ee.sendReaction(threadID, messageID, "
|
|
174
|
+
await api.e2ee.sendReaction(threadID, messageID, "❤️");
|
|
175
175
|
await api.e2ee.sendTyping(threadID, true);
|
|
176
176
|
await api.e2ee.unsendMessage(messageID, threadID);
|
|
177
177
|
await api.e2ee.editMessage(threadID, messageID, "Updated!");
|
|
@@ -181,20 +181,20 @@ await api.e2ee.disconnect();
|
|
|
181
181
|
|
|
182
182
|
### Native media engine
|
|
183
183
|
|
|
184
|
-
Sending image/video/audio/document attachments, reactions, and unsend in E2EE threads can be 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
|
|
184
|
+
Sending image/video/audio/document attachments, reactions, and unsend in E2EE threads can be 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.
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
**⚠️ The native engine is DISABLED by default.** Both the vendored JS engine and the native engine register with Facebook as the *same* E2EE device (same device id), and Facebook's server keeps only one live E2EE socket per device — so when both run at once they kick each other off in a loop (a fresh handshake every few seconds, which looks like automated behavior to Facebook). The vendored JS engine is therefore given exclusive use of the socket unless you opt in:
|
|
187
187
|
|
|
188
|
-
- Set `FCA_E2EE_NATIVE=1` in the environment to enable the native engine. With it on you get E2EE group text receive/send, native media sends, native reactions/unsend/edit/markRead
|
|
188
|
+
- Set `FCA_E2EE_NATIVE=1` in the environment to enable the native engine. With it on you get E2EE group text receive/send, native media sends, native reactions/unsend/edit/markRead — but you also accept the device-kick socket churn described above (the JS engine reconnects with exponential backoff, so it stays usable, just noisy).
|
|
189
189
|
- With it off (default): E2EE DM text + DM media work through the vendored engine. E2EE **group** send/receive, and E2EE markRead, are unavailable; reactions/unsend/edit in E2EE DMs fall back to the vendored engine.
|
|
190
190
|
|
|
191
191
|
- Text messages always use @eryxenx/fca's own E2EE engine (already reliable, no native dependency).
|
|
192
192
|
- 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`.
|
|
193
|
-
- See `src/api/socket/e2ee/native/NOTICE.md` for the required MPL-2.0 attribution to the upstream mautrix-go/mautrix-meta project (
|
|
193
|
+
- 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.
|
|
194
194
|
|
|
195
195
|
### Receiving E2EE media (attachments & replies)
|
|
196
196
|
|
|
197
|
-
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`
|
|
197
|
+
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.
|
|
198
198
|
|
|
199
199
|
```javascript
|
|
200
200
|
if (event.messageReply && event.messageReply.attachments.length) {
|
|
@@ -202,11 +202,11 @@ if (event.messageReply && event.messageReply.attachments.length) {
|
|
|
202
202
|
}
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
-
If media resolution fails, check the console for `[media-resolve]` / `[media-decode]` errors
|
|
205
|
+
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.
|
|
206
206
|
|
|
207
207
|
---
|
|
208
208
|
|
|
209
|
-
##
|
|
209
|
+
## 🛡️ sessionGuard
|
|
210
210
|
|
|
211
211
|
Protects your appstate from corruption and silent logouts.
|
|
212
212
|
|
|
@@ -223,8 +223,8 @@ api.sessionGuard("./account.json", {
|
|
|
223
223
|
What it does:
|
|
224
224
|
- Auto-saves appstate every N minutes
|
|
225
225
|
- Saves after every successful sendMessage (debounced)
|
|
226
|
-
- Corruption guard
|
|
227
|
-
- Auto-backup
|
|
226
|
+
- Corruption guard — never overwrites a larger appstate with a smaller one
|
|
227
|
+
- Auto-backup — writes `.bak` before every overwrite
|
|
228
228
|
|
|
229
229
|
```javascript
|
|
230
230
|
api.saveSession(); // force save now
|
|
@@ -234,7 +234,7 @@ api.stopSessionGuard(); // stop the timer
|
|
|
234
234
|
|
|
235
235
|
---
|
|
236
236
|
|
|
237
|
-
##
|
|
237
|
+
## 📡 sendBroadcast
|
|
238
238
|
|
|
239
239
|
Rate-limited multi-thread broadcast.
|
|
240
240
|
|
|
@@ -255,7 +255,7 @@ console.log(result.sent.length + "/" + result.total + " delivered");
|
|
|
255
255
|
|
|
256
256
|
---
|
|
257
257
|
|
|
258
|
-
##
|
|
258
|
+
## 🤖 MessengerBot
|
|
259
259
|
|
|
260
260
|
Discord.js/Telegraf style high-level bot class.
|
|
261
261
|
|
|
@@ -267,8 +267,8 @@ const bot = await createMessengerBot(
|
|
|
267
267
|
{ commandPrefix: "/", stopOnSignals: true }
|
|
268
268
|
);
|
|
269
269
|
|
|
270
|
-
bot.command("ping", async ctx => await ctx.replyAsync("pong
|
|
271
|
-
bot.hears(/hello/i, async ctx => await ctx.replyAsync("Hi!
|
|
270
|
+
bot.command("ping", async ctx => await ctx.replyAsync("pong 🏓"));
|
|
271
|
+
bot.hears(/hello/i, async ctx => await ctx.replyAsync("Hi! 👋"));
|
|
272
272
|
bot.on("messageCreate", event => console.log(event.body));
|
|
273
273
|
|
|
274
274
|
await bot.launch({ stopOnSignals: true });
|
|
@@ -276,7 +276,7 @@ await bot.launch({ stopOnSignals: true });
|
|
|
276
276
|
|
|
277
277
|
---
|
|
278
278
|
|
|
279
|
-
##
|
|
279
|
+
## 🎯 createFcaClient
|
|
280
280
|
|
|
281
281
|
Namespaced facade grouping all API methods by domain.
|
|
282
282
|
|
|
@@ -285,7 +285,7 @@ const { createFcaClient } = require("@eryxenx/fca");
|
|
|
285
285
|
const client = createFcaClient(api);
|
|
286
286
|
|
|
287
287
|
await client.messages.send("Hello!", threadID);
|
|
288
|
-
await client.messages.react("
|
|
288
|
+
await client.messages.react("❤️", messageID, threadID);
|
|
289
289
|
await client.threads.getInfo(threadID);
|
|
290
290
|
await client.users.getInfo(userID);
|
|
291
291
|
await client.account.refreshDtsg();
|
|
@@ -293,7 +293,7 @@ await client.account.refreshDtsg();
|
|
|
293
293
|
|
|
294
294
|
---
|
|
295
295
|
|
|
296
|
-
##
|
|
296
|
+
## 📖 API Reference
|
|
297
297
|
|
|
298
298
|
### Sending Messages
|
|
299
299
|
|
|
@@ -320,7 +320,7 @@ api.shareContact("Meet my friend!", userID, threadID);
|
|
|
320
320
|
api.editMessage("Updated text", messageID);
|
|
321
321
|
api.unsendMessage(messageID);
|
|
322
322
|
api.deleteMessage([messageID]);
|
|
323
|
-
api.setMessageReaction("
|
|
323
|
+
api.setMessageReaction("😍", messageID, threadID); // threadID optional
|
|
324
324
|
api.setMessageReaction("", messageID); // remove reaction
|
|
325
325
|
api.getMessage(threadID, messageID);
|
|
326
326
|
api.forwardAttachment(attachmentID, [userID]);
|
|
@@ -356,7 +356,7 @@ api.searchForThread("query");
|
|
|
356
356
|
```javascript
|
|
357
357
|
api.setTitle("New Name", threadID);
|
|
358
358
|
api.changeThreadColor("#0084FF", threadID);
|
|
359
|
-
api.changeThreadEmoji("
|
|
359
|
+
api.changeThreadEmoji("🔥", threadID);
|
|
360
360
|
api.changeNickname("The Boss", threadID, userID);
|
|
361
361
|
api.changeGroupImage(fs.createReadStream("group.jpg"), threadID);
|
|
362
362
|
api.changeAdminStatus(threadID, userID, true);
|
|
@@ -420,7 +420,7 @@ api.uploadImageToImgbb(imageUrl);
|
|
|
420
420
|
|
|
421
421
|
---
|
|
422
422
|
|
|
423
|
-
##
|
|
423
|
+
## 📋 Login Options
|
|
424
424
|
|
|
425
425
|
| Option | Type | Default | Description |
|
|
426
426
|
|--------|------|---------|-------------|
|
|
@@ -433,23 +433,23 @@ api.uploadImageToImgbb(imageUrl);
|
|
|
433
433
|
| `autoReconnect` | boolean | true | Auto-reconnect MQTT on disconnect |
|
|
434
434
|
| `online` | boolean | false | Appear as online to others |
|
|
435
435
|
| `emitReady` | boolean | false | Emit ready event when MQTT connected |
|
|
436
|
-
| `proxy` | string |
|
|
436
|
+
| `proxy` | string | — | HTTP proxy URL |
|
|
437
437
|
| `userAgent` | string | Safari UA | Override HTTP User-Agent |
|
|
438
438
|
|
|
439
439
|
---
|
|
440
440
|
|
|
441
|
-
##
|
|
441
|
+
## 📄 License
|
|
442
442
|
|
|
443
443
|
MIT License
|
|
444
444
|
|
|
445
445
|
**@eryxenx/fca** by [EryXenX (Mohammad Akash)](https://github.com/EryXenX)
|
|
446
446
|
|
|
447
447
|
> The E2EE native media engine (`src/api/socket/e2ee/native/`) bundles a
|
|
448
|
-
> precompiled binary built from **mautrix-go** / **mautrix-meta**,
|
|
448
|
+
> precompiled binary built from **mautrix-go** / **mautrix-meta**, © Tulir
|
|
449
449
|
> Asokan and contributors, licensed under **MPL-2.0** (not MIT). See
|
|
450
|
-
> `src/api/socket/e2ee/native/NOTICE.md` for full attribution
|
|
450
|
+
> `src/api/socket/e2ee/native/NOTICE.md` for full attribution — this notice
|
|
451
451
|
> must be preserved in any redistribution of this fork.
|
|
452
|
-
NEXCA MQTT core by [Deku](https://github.com/dekuzxc)
|
|
452
|
+
NEXCA MQTT core by [Deku](https://github.com/dekuzxc) — MIT License
|
|
453
453
|
|
|
454
454
|
> Unauthorized copying or redistribution without credit is prohibited.
|
|
455
455
|
|
|
@@ -457,6 +457,6 @@ NEXCA MQTT core by [Deku](https://github.com/dekuzxc) — MIT License
|
|
|
457
457
|
|
|
458
458
|
<div align="center">
|
|
459
459
|
|
|
460
|
-
Made with
|
|
460
|
+
Made with ❤️ by EryXenX
|
|
461
461
|
|
|
462
|
-
</div>
|
|
462
|
+
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eryxenx/fca",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Facebook Chat API by EryXenX | Stable • Auto Re-login • Full E2EE Support — send messages, media, reactions & more in encrypted chats, hassle-free",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const log = require("../../../func/logAdapter");
|
|
4
|
-
const { getFrom } = require("../../utils/constants");
|
|
5
4
|
const { get } = require("../../utils/request")
|
|
6
5
|
const { getType } = require("../../utils/format");
|
|
6
|
+
const { extractDtsg } = require("../../utils/loginParser/extractDtsg");
|
|
7
7
|
module.exports = function (defaultFuncs, api, ctx) {
|
|
8
8
|
return function refreshFb_dtsg(obj, callback) {
|
|
9
9
|
if (typeof obj === "function") {
|
|
@@ -24,8 +24,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
24
24
|
}
|
|
25
25
|
if (Object.keys(obj).length === 0) {
|
|
26
26
|
get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, { noRef: true }).then(({ data }) => {
|
|
27
|
-
const fb_dtsg =
|
|
28
|
-
const jazoest = getFrom(data, "jazoest=", '",');
|
|
27
|
+
const { fb_dtsg, jazoest } = extractDtsg(data);
|
|
29
28
|
if (!fb_dtsg) throw new Error("Could not find fb_dtsg in HTML after requesting Facebook.");
|
|
30
29
|
Object.assign(ctx, { fb_dtsg, jazoest });
|
|
31
30
|
callback(null, {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Multi-pattern fb_dtsg / jazoest extractor.
|
|
4
|
+
// Facebook serves the token in different shapes depending on surface, A/B
|
|
5
|
+
// bucket and login state (DTSGInitialData / DTSGInitData blobs, escaped JSON
|
|
6
|
+
// inside <script>, plain hidden inputs, async_get_token, ...). Matching only
|
|
7
|
+
// one shape is why refreshes intermittently fail with "Could not find
|
|
8
|
+
// fb_dtsg in HTML". Pattern order is first-match-wins.
|
|
9
|
+
|
|
10
|
+
const DTSG_PATTERNS = [
|
|
11
|
+
/\["DTSGInitialData",\[\],\{"token":"([^"]+)"\}\]/,
|
|
12
|
+
/\["DTSGInitData",\[\],\{"token":"([^"]+)"/,
|
|
13
|
+
/"DTSGInitData",\[\],\{"token":"([^"]+)"/,
|
|
14
|
+
/"dtsg":\{"token":"([^"]+)"/,
|
|
15
|
+
/\{\\"token\\":\\"([^\\]+)\\"/,
|
|
16
|
+
/,\{"token":"([^"]+)"\},\d+\]/,
|
|
17
|
+
/"async_get_token":"([^"]+)"/,
|
|
18
|
+
/name="fb_dtsg"\s+value="([^"]+)"/,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const JAZOEST_PATTERNS = [
|
|
22
|
+
/name="jazoest"\s+value="([^"]+)"/,
|
|
23
|
+
/jazoest=(\d+)/,
|
|
24
|
+
/"jazoest":"?(\d+)"?/,
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function firstMatch(html, patterns) {
|
|
28
|
+
for (const pattern of patterns) {
|
|
29
|
+
const m = html.match(pattern);
|
|
30
|
+
if (m && m[1]) return m[1];
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function extractDtsg(html) {
|
|
36
|
+
if (!html) return { fb_dtsg: null, jazoest: null };
|
|
37
|
+
const raw = String(html);
|
|
38
|
+
return {
|
|
39
|
+
fb_dtsg: firstMatch(raw, DTSG_PATTERNS),
|
|
40
|
+
jazoest: firstMatch(raw, JAZOEST_PATTERNS),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { extractDtsg, DTSG_PATTERNS, JAZOEST_PATTERNS };
|