nexus-fca 3.1.2 → 3.1.4
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/lib/safety/FacebookSafety.js +9 -14
- package/package.json +1 -1
- package/src/listenMqtt.js +51 -17
- package/src/refreshFb_dtsg.js +115 -25
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
|
|
9
|
+
// StealthMode module has been removed to avoid global human-like pauses
|
|
10
|
+
// that can harm perceived stability. FacebookSafety now relies on
|
|
11
|
+
// lightweight adaptive delays and backoff only.
|
|
12
|
+
// const StealthMode = require('./StealthMode');
|
|
10
13
|
|
|
11
14
|
class FacebookSafety {
|
|
12
15
|
constructor(options = {}) {
|
|
@@ -24,15 +27,9 @@ class FacebookSafety {
|
|
|
24
27
|
...options
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
enableRandomPauses: true,
|
|
31
|
-
pauseProbability: this.options.ultraLowBanMode ? 0.0001 : 0.00005, // Reduced to 0.01% (Extremely rare)
|
|
32
|
-
minPauseMinutes: 0.1, // Reduced to 6 seconds
|
|
33
|
-
maxPauseMinutes: 0.5, // Reduced to 30 seconds
|
|
34
|
-
dailyRequestLimit: 500000 // Increased to 500k for high traffic
|
|
35
|
-
});
|
|
30
|
+
// StealthMode has been fully removed; keep a placeholder field
|
|
31
|
+
// for backward compatibility but do not perform any throttling.
|
|
32
|
+
this.stealthMode = null;
|
|
36
33
|
|
|
37
34
|
// ULTRA-SAFE user agents - Most common real browsers (Nov 2025)
|
|
38
35
|
// These are the MOST COMMON UAs to blend in with real users
|
|
@@ -821,10 +818,8 @@ class FacebookSafety {
|
|
|
821
818
|
}
|
|
822
819
|
|
|
823
820
|
async applyAdaptiveSendDelay(){
|
|
824
|
-
//
|
|
825
|
-
|
|
826
|
-
await this.stealthMode.waitIfNeeded();
|
|
827
|
-
}
|
|
821
|
+
// StealthMode throttling has been removed; rely on lightweight
|
|
822
|
+
// adaptive delays and backoff logic elsewhere in FacebookSafety.
|
|
828
823
|
|
|
829
824
|
// Then apply adaptive delay based on risk
|
|
830
825
|
const d = this.computeAdaptiveSendDelay();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-fca",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"description": "Nexus-FCA 3.1 – THE BEST, SAFEST, MOST STABLE Facebook Messenger API! Email/password + appState login, proxy support (HTTP/HTTPS/SOCKS5), random user agent, proactive cookie refresh, MQTT stability, session protection, and TypeScript support.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
package/src/listenMqtt.js
CHANGED
|
@@ -402,14 +402,18 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
402
402
|
// Ensure reconnection also triggers on unexpected close without prior error
|
|
403
403
|
mqttClient.on('close', function () {
|
|
404
404
|
ctx.health.onDisconnect();
|
|
405
|
-
if(ctx.health && typeof ctx.health.incFailure === 'function'){ ctx.health.incFailure(); }
|
|
406
|
-
|
|
405
|
+
if (ctx.health && typeof ctx.health.incFailure === 'function') { ctx.health.incFailure(); }
|
|
406
|
+
|
|
407
407
|
const duration = Date.now() - attemptStartTs;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
408
|
+
const seconds = Math.floor(duration / 1000);
|
|
409
|
+
|
|
410
|
+
// Treat long-lived connections as normal lifecycle, keep logs calm
|
|
411
|
+
if (duration >= 30 * 60 * 1000) { // >= 30 minutes
|
|
412
|
+
log.info('listenMqtt', `MQTT connection closed after ${seconds}s (normal lifecycle). Reconnecting...`);
|
|
413
|
+
} else if (duration >= 5 * 60 * 1000) { // 5-30 minutes
|
|
414
|
+
log.info('listenMqtt', `MQTT connection closed after ${seconds}s (remote close). Reconnecting with backoff...`);
|
|
411
415
|
} else {
|
|
412
|
-
|
|
416
|
+
log.warn('listenMqtt', `MQTT bridge socket closed quickly after ${duration}ms (attempt=${ctx._mqttDiag.attempts}).`);
|
|
413
417
|
}
|
|
414
418
|
|
|
415
419
|
if (!ctx.loggedIn) return; // avoid loops if logged out
|
|
@@ -417,15 +421,20 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
417
421
|
scheduleAdaptiveReconnect(defaultFuncs, api, ctx, globalCallback);
|
|
418
422
|
}
|
|
419
423
|
});
|
|
424
|
+
|
|
420
425
|
mqttClient.on('disconnect', function(){
|
|
421
426
|
ctx.health.onDisconnect();
|
|
422
|
-
if(ctx.health && typeof ctx.health.incFailure === 'function'){ ctx.health.incFailure(); }
|
|
423
|
-
|
|
427
|
+
if (ctx.health && typeof ctx.health.incFailure === 'function') { ctx.health.incFailure(); }
|
|
428
|
+
|
|
424
429
|
const duration = Date.now() - attemptStartTs;
|
|
425
|
-
|
|
426
|
-
|
|
430
|
+
const seconds = Math.floor(duration / 1000);
|
|
431
|
+
|
|
432
|
+
if (duration >= 30 * 60 * 1000) {
|
|
433
|
+
log.info('listenMqtt', `MQTT disconnected after ${seconds}s (normal lifecycle). Reconnecting...`);
|
|
434
|
+
} else if (duration >= 5 * 60 * 1000) {
|
|
435
|
+
log.info('listenMqtt', `MQTT disconnected after ${seconds}s (remote close). Reconnecting with backoff...`);
|
|
427
436
|
} else {
|
|
428
|
-
|
|
437
|
+
log.warn('listenMqtt', `MQTT bridge disconnect event after ${duration}ms (attempt=${ctx._mqttDiag.attempts}).`);
|
|
429
438
|
}
|
|
430
439
|
|
|
431
440
|
if (!ctx.loggedIn) return;
|
|
@@ -433,20 +442,45 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
433
442
|
scheduleAdaptiveReconnect(defaultFuncs, api, ctx, globalCallback);
|
|
434
443
|
}
|
|
435
444
|
});
|
|
445
|
+
|
|
436
446
|
mqttClient.on("connect", function () {
|
|
437
447
|
resetBackoff(backoff);
|
|
438
448
|
backoff.consecutiveFails = 0; // Reset consecutive failures on successful connect
|
|
449
|
+
// Reset or wrap MQTT attempt counter so long-lived sessions don't look scary
|
|
450
|
+
ctx._mqttDiag = ctx._mqttDiag || {};
|
|
451
|
+
if (typeof ctx._mqttDiag.attempts !== 'number' || ctx._mqttDiag.attempts > 100000) {
|
|
452
|
+
ctx._mqttDiag.attempts = 1;
|
|
453
|
+
} else {
|
|
454
|
+
ctx._mqttDiag.attempts++;
|
|
455
|
+
}
|
|
439
456
|
ctx.health.onConnect();
|
|
440
457
|
|
|
441
|
-
// WS3-style randomized proactive reconnect (
|
|
458
|
+
// WS3-style randomized proactive reconnect (tunable window).
|
|
459
|
+
// If globalOptions override is provided, respect that; otherwise
|
|
460
|
+
// default to a slightly longer window to avoid overlapping too much
|
|
461
|
+
// with remote/NAT closes.
|
|
442
462
|
if (ctx._reconnectTimer) clearTimeout(ctx._reconnectTimer);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
463
|
+
let reconnectTime = null;
|
|
464
|
+
const opts = ctx.globalOptions || {};
|
|
465
|
+
const proactiveEnabled = opts.mqttProactiveReconnectEnabled;
|
|
466
|
+
|
|
467
|
+
if (proactiveEnabled !== false) {
|
|
468
|
+
const minM = Number.isFinite(opts.mqttProactiveReconnectMinMinutes) ? opts.mqttProactiveReconnectMinMinutes : 120; // 2h
|
|
469
|
+
const maxM = Number.isFinite(opts.mqttProactiveReconnectMaxMinutes) ? opts.mqttProactiveReconnectMaxMinutes : 240; // 4h
|
|
470
|
+
const min = Math.min(minM, maxM);
|
|
471
|
+
const max = Math.max(minM, maxM);
|
|
472
|
+
const intervalMinutes = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
473
|
+
reconnectTime = intervalMinutes * 60 * 1000;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (reconnectTime) {
|
|
477
|
+
if (verboseMqtt) log.info('listenMqtt', `Scheduled proactive reconnect in ${Math.floor(reconnectTime / 60000)} minutes.`);
|
|
478
|
+
ctx._reconnectTimer = setTimeout(() => {
|
|
479
|
+
log.info('listenMqtt', `Executing proactive proactive reconnect (timer window).`);
|
|
447
480
|
if (ctx.mqttClient) ctx.mqttClient.end(true);
|
|
448
481
|
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
449
|
-
|
|
482
|
+
}, reconnectTime);
|
|
483
|
+
}
|
|
450
484
|
|
|
451
485
|
if (verboseMqtt) {
|
|
452
486
|
log.info('listenMqtt', `Nexus MQTT bridge established in ${(Date.now()-attemptStartTs)}ms (attempt=${ctx._mqttDiag.attempts}).`);
|
package/src/refreshFb_dtsg.js
CHANGED
|
@@ -27,33 +27,44 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
27
27
|
callback = (err, data) => (err ? rejectFunc(err) : resolveFunc(data));
|
|
28
28
|
}
|
|
29
29
|
if (Object.keys(obj).length === 0) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
const sources = [
|
|
31
|
+
{ url: "https://www.facebook.com/", label: "facebook.com" },
|
|
32
|
+
{ url: "https://business.facebook.com/", label: "business.facebook.com" },
|
|
33
|
+
{ url: "https://mbasic.facebook.com/", label: "mbasic.facebook.com" }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const tryRefresh = async () => {
|
|
37
|
+
let lastErr = null;
|
|
38
|
+
for (const source of sources) {
|
|
39
|
+
try {
|
|
40
|
+
const resData = await utils.get(source.url, ctx.jar, null, ctx.globalOptions, { noRef: true });
|
|
41
|
+
const tokens = extractTokensFromHtml(resData.body);
|
|
42
|
+
if (tokens.fb_dtsg) {
|
|
43
|
+
ctx.fb_dtsg = tokens.fb_dtsg;
|
|
44
|
+
if (tokens.jazoest) ctx.jazoest = tokens.jazoest;
|
|
45
|
+
return {
|
|
46
|
+
data: { fb_dtsg: ctx.fb_dtsg, jazoest: ctx.jazoest },
|
|
47
|
+
message: `Refreshed fb_dtsg via ${source.label}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
lastErr = new utils.CustomError(`Could not find fb_dtsg in HTML from ${source.label}`);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
lastErr = err;
|
|
53
|
+
log.verbose("refreshFb_dtsg", `Fetch from ${source.url} failed: ${err.message}`);
|
|
45
54
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
}
|
|
56
|
+
throw lastErr || new utils.CustomError("Could not find fb_dtsg in any fallback HTML sources.");
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
(async () => {
|
|
60
|
+
try {
|
|
61
|
+
const result = await tryRefresh();
|
|
62
|
+
callback(null, result);
|
|
63
|
+
} catch (err) {
|
|
54
64
|
log.error("refreshFb_dtsg", err);
|
|
55
65
|
callback(err);
|
|
56
|
-
}
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
57
68
|
} else {
|
|
58
69
|
Object.assign(ctx, obj);
|
|
59
70
|
callback(null, {
|
|
@@ -63,4 +74,83 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
63
74
|
}
|
|
64
75
|
return returnPromise;
|
|
65
76
|
};
|
|
66
|
-
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function extractTokensFromHtml(html) {
|
|
80
|
+
const tokens = { fb_dtsg: null, jazoest: null };
|
|
81
|
+
|
|
82
|
+
const dtsgRegexes = [
|
|
83
|
+
/DTSGInitialData.*?token":"(.*?)"/,
|
|
84
|
+
/"DTSGInitData",\[\],{"token":"(.*?)"/,
|
|
85
|
+
/\["DTSGInitData",\[\],{"token":"(.*?)"/,
|
|
86
|
+
/name="fb_dtsg" value="(.*?)"/,
|
|
87
|
+
/name="dtsg_ag" value="(.*?)"/
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
for (const regex of dtsgRegexes) {
|
|
91
|
+
const match = html.match(regex);
|
|
92
|
+
if (match && match[1]) {
|
|
93
|
+
tokens.fb_dtsg = match[1];
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!tokens.fb_dtsg) {
|
|
99
|
+
try {
|
|
100
|
+
const scriptRegex = /<script type="application\/json"[^>]*>(.*?)<\/script>/g;
|
|
101
|
+
let match;
|
|
102
|
+
const netData = [];
|
|
103
|
+
while ((match = scriptRegex.exec(html)) !== null) {
|
|
104
|
+
try {
|
|
105
|
+
netData.push(JSON.parse(match[1]));
|
|
106
|
+
} catch (_) {}
|
|
107
|
+
}
|
|
108
|
+
const findConfig = (key) => {
|
|
109
|
+
for (const scriptData of netData) {
|
|
110
|
+
if (scriptData.require) {
|
|
111
|
+
for (const req of scriptData.require) {
|
|
112
|
+
if (Array.isArray(req) && req[0] === key && req[2]) {
|
|
113
|
+
return req[2];
|
|
114
|
+
}
|
|
115
|
+
if (
|
|
116
|
+
Array.isArray(req) &&
|
|
117
|
+
req[3] &&
|
|
118
|
+
req[3][0] &&
|
|
119
|
+
req[3][0].__bbox &&
|
|
120
|
+
req[3][0].__bbox.define
|
|
121
|
+
) {
|
|
122
|
+
for (const def of req[3][0].__bbox.define) {
|
|
123
|
+
if (Array.isArray(def) && def[0].endsWith(key) && def[2]) {
|
|
124
|
+
return def[2];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
};
|
|
133
|
+
const dtsgData = findConfig("DTSGInitialData");
|
|
134
|
+
if (dtsgData && dtsgData.token) {
|
|
135
|
+
tokens.fb_dtsg = dtsgData.token;
|
|
136
|
+
}
|
|
137
|
+
} catch (_) {}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!tokens.jazoest) {
|
|
141
|
+
const jazoRegexes = [
|
|
142
|
+
/name="jazoest" value="(\d+)"/,
|
|
143
|
+
/"jazoest","(\d+)"/,
|
|
144
|
+
/jazoest=(\d+)/
|
|
145
|
+
];
|
|
146
|
+
for (const regex of jazoRegexes) {
|
|
147
|
+
const match = html.match(regex);
|
|
148
|
+
if (match && match[1]) {
|
|
149
|
+
tokens.jazoest = match[1];
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return tokens;
|
|
156
|
+
}
|