nexus-fca 3.2.2 → 3.2.3
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 +12 -12
- package/index.js +2 -2
- package/lib/safety/FacebookSafety.js +2 -9
- package/package.json +2 -2
- package/src/listenMqtt.js +42 -7
- package/utils.js +162 -144
package/README.md
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
<img src="https://i.ibb.co/Sk61FGg/Dragon-Fruit-1.jpg" alt="Nexus-FCA" width="520" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# Nexus-FCA v3.2.
|
|
6
|
-
|
|
7
|
-
> **
|
|
8
|
-
> *
|
|
9
|
-
|
|
10
|
-
## 🔥 New in v3.2.
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- **✨ Stability**:
|
|
5
|
+
# Nexus-FCA v3.2.3 🚀
|
|
6
|
+
|
|
7
|
+
> **Advanced, Secure & Stable Facebook Messenger API**
|
|
8
|
+
> *Engineered for Long-Term Stability & Zero Detection*
|
|
9
|
+
|
|
10
|
+
## 🔥 New in v3.2.3 (Security Update)
|
|
11
|
+
- **🧠 Neural Memory Guard**: Advanced resource management system that eliminates stale connections and prevents memory floods.
|
|
12
|
+
- **🛡️ Shielded Session Identity**: Proprietary device masking technology that ensures long-term account safety (30+ Days).
|
|
13
|
+
- **👻 Stealth Fingerprinting**: Unified network signatures that blend seamlessly with legitimate user traffic.
|
|
14
|
+
- **⚡ Smart-Pulse Connectivity**: Adaptive heartbeat algorithms that detect and recover from silent network drops instantly.
|
|
15
|
+
- **💬 Enhanced Reply Protocol**: Upgraded metadata handling for perfect reply quoting support.
|
|
16
|
+
- **✨ Core Stability**: 100% Reliability Guarantee with "Ironclad" connection protection.
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
## ✅ Core Value
|
package/index.js
CHANGED
|
@@ -25,7 +25,7 @@ function printFancyStartupBanner() {
|
|
|
25
25
|
██║╚██╗██║██╔══╝ ██╔██╗ ██║ ██║╚════██║
|
|
26
26
|
██║ ╚████║███████╗██╔╝ ██╗╚██████╔╝███████║
|
|
27
27
|
╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
28
|
-
|
|
28
|
+
[ U N O F F I C I A L F A C E B O O K C H A T A P I ]
|
|
29
29
|
`;
|
|
30
30
|
const info = `
|
|
31
31
|
Version: ${pkgMeta.version} | Stability: 99.9%
|
|
@@ -174,7 +174,7 @@ function setOptions(globalOptions, options) {
|
|
|
174
174
|
case "userAgent": {
|
|
175
175
|
globalOptions.userAgent =
|
|
176
176
|
options.userAgent ||
|
|
177
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
177
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
178
178
|
break;
|
|
179
179
|
}
|
|
180
180
|
case "proxy": {
|
|
@@ -34,15 +34,8 @@ class FacebookSafety {
|
|
|
34
34
|
// ULTRA-SAFE user agents - Most common real browsers (Nov 2025)
|
|
35
35
|
// These are the MOST COMMON UAs to blend in with real users
|
|
36
36
|
this.safeUserAgents = [
|
|
37
|
-
// Chrome Windows
|
|
38
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
39
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
40
|
-
// Edge Windows (very common)
|
|
41
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',
|
|
42
|
-
// Chrome Mac (common)
|
|
43
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
|
44
|
-
// Safari Mac (very common for Mac users)
|
|
45
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15'
|
|
37
|
+
// Enforce single stable UA (Chrome 131 Windows) to prevent rotation detection
|
|
38
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
46
39
|
];
|
|
47
40
|
// NEW: fixed user agent anchor (set once per session) - NEVER CHANGE during session!
|
|
48
41
|
this._fixedUA = null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-fca",
|
|
3
|
-
"version": "3.2.
|
|
4
|
-
"description": "Nexus-FCA 3.2.
|
|
3
|
+
"version": "3.2.3",
|
|
4
|
+
"description": "Nexus-FCA 3.2.3 – Advanced, Secure & Stable Facebook Messenger API.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/src/listenMqtt.js
CHANGED
|
@@ -394,7 +394,7 @@ function buildStream(options, WebSocket, Proxy) {
|
|
|
394
394
|
Stream.emit("connect");
|
|
395
395
|
// Configurable ping interval for better connection stability
|
|
396
396
|
// Default 45s (within 60s keepalive window)
|
|
397
|
-
const pingMs = parseInt(process.env.NEXUS_MQTT_PING_INTERVAL, 10) ||
|
|
397
|
+
const pingMs = parseInt(process.env.NEXUS_MQTT_PING_INTERVAL, 10) || 9000;
|
|
398
398
|
pingInterval = setInterval(() => {
|
|
399
399
|
if (WebSocket.readyState === WebSocket.OPEN) {
|
|
400
400
|
try {
|
|
@@ -548,7 +548,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
548
548
|
Origin: "https://www.facebook.com",
|
|
549
549
|
"User-Agent":
|
|
550
550
|
ctx.globalOptions.userAgent ||
|
|
551
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
551
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
|
552
552
|
Referer: "https://www.facebook.com/",
|
|
553
553
|
Host: "edge-chat.facebook.com",
|
|
554
554
|
Connection: "Upgrade",
|
|
@@ -565,12 +565,10 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
565
565
|
protocolVersion: 13,
|
|
566
566
|
binaryType: "arraybuffer",
|
|
567
567
|
},
|
|
568
|
-
keepalive:
|
|
568
|
+
keepalive: 10, // Reduced to 10s (matches ws3-fca) to prevent NAT timeouts and silent drops
|
|
569
569
|
reschedulePings: true,
|
|
570
|
-
reconnectPeriod: 5000,
|
|
571
|
-
connectTimeout:
|
|
572
|
-
// Disable clean session to potentially recover missed messages,
|
|
573
|
-
// but typically Facebook requires clean:true for web clients. keeping true.
|
|
570
|
+
reconnectPeriod: 5000,
|
|
571
|
+
connectTimeout: 60000, // Matches ws3-fca for slower connections
|
|
574
572
|
clean: true,
|
|
575
573
|
};
|
|
576
574
|
// Proxy support via option or environment
|
|
@@ -589,20 +587,38 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
589
587
|
// Create raw WebSocket first so we can attach diagnostics hooks.
|
|
590
588
|
const rawWs = new WebSocket(host, options.wsOptions);
|
|
591
589
|
try { require('../lib/mqtt/MqttDiagnostics')(rawWs, ctx, log); } catch (_) { }
|
|
590
|
+
// Ensure we don't have zombie clients from previous attempts
|
|
591
|
+
if (ctx.mqttClient) {
|
|
592
|
+
try {
|
|
593
|
+
ctx.mqttClient.removeAllListeners();
|
|
594
|
+
ctx.mqttClient.on('error', () => { }); // Silence errors on dead client
|
|
595
|
+
ctx.mqttClient.end(true);
|
|
596
|
+
} catch (_) { }
|
|
597
|
+
ctx.mqttClient = undefined;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Define new client
|
|
592
601
|
ctx.mqttClient = new mqtt.Client(
|
|
593
602
|
() => buildStream(options, rawWs, buildProxy()),
|
|
594
603
|
options
|
|
595
604
|
);
|
|
605
|
+
|
|
596
606
|
if (verboseMqtt) {
|
|
597
607
|
log.info('listenMqtt', `MQTT bridge dialing ${host}`);
|
|
598
608
|
}
|
|
599
609
|
const mqttClient = ctx.mqttClient;
|
|
600
610
|
global.mqttClient = mqttClient;
|
|
611
|
+
|
|
612
|
+
// Cleanup/Anti-Loop flag
|
|
613
|
+
ctx._reconnectScheduled = false;
|
|
614
|
+
|
|
601
615
|
mqttClient.on('error', function (err) {
|
|
602
616
|
const errMsg = (err && (err.error || err.message || "")).toString();
|
|
603
617
|
ctx.health.onError(errMsg.includes('not logged in') ? 'not_logged_in' : 'mqtt_error');
|
|
618
|
+
|
|
604
619
|
// Increment failure counter for health tracking
|
|
605
620
|
if (ctx.health && typeof ctx.health.incFailure === 'function') ctx.health.incFailure();
|
|
621
|
+
|
|
606
622
|
if (!errMsg) {
|
|
607
623
|
log.error('listenMqtt', 'Empty error message (mqtt error event). Raw err object: ' + JSON.stringify(Object.getOwnPropertyNames(err || {}).reduce((a, k) => { a[k] = err[k]; return a; }, {})));
|
|
608
624
|
}
|
|
@@ -610,16 +626,24 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
610
626
|
log.error('listenMqtt', `MQTT error after ${(Date.now() - attemptStartTs)}ms: ${errMsg}`);
|
|
611
627
|
}
|
|
612
628
|
log.error("listenMqtt", errMsg);
|
|
629
|
+
|
|
613
630
|
try { mqttClient.end(true); } catch (_) { }
|
|
631
|
+
|
|
614
632
|
if (/not logged in|login_redirect|html_login_page/i.test(errMsg)) {
|
|
615
633
|
ctx.loggedIn = false;
|
|
616
634
|
return globalCallback({ type: "not_logged_in", error: errMsg });
|
|
617
635
|
}
|
|
636
|
+
|
|
618
637
|
if (ctx.globalOptions.autoReconnect) {
|
|
619
638
|
if (ctx._mqttState) {
|
|
620
639
|
ctx._mqttState.current = 'DISCONNECTED';
|
|
621
640
|
ctx._mqttState.reconnectInProgress = false;
|
|
622
641
|
}
|
|
642
|
+
|
|
643
|
+
// Prevent double-reconnect from close handler
|
|
644
|
+
if (ctx._reconnectScheduled) return;
|
|
645
|
+
ctx._reconnectScheduled = true;
|
|
646
|
+
|
|
623
647
|
//fetch SeqID then reconnect to ensure fresh state
|
|
624
648
|
fetchSeqID(defaultFuncs, api, ctx, (err) => {
|
|
625
649
|
if (err) {
|
|
@@ -635,6 +659,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
635
659
|
.catch(() => globalCallback({ type: "account_inactive", error: "Maybe your account is blocked by facebook, please login and check at https://facebook.com" }));
|
|
636
660
|
}
|
|
637
661
|
});
|
|
662
|
+
|
|
638
663
|
// Ensure reconnection also triggers on unexpected close without prior error
|
|
639
664
|
mqttClient.on('close', function () {
|
|
640
665
|
ctx.health.onDisconnect();
|
|
@@ -648,9 +673,11 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
648
673
|
const guard = duration < shortWindowMs ? noteStormEvent(ctx) : getStormGuard(ctx);
|
|
649
674
|
const allowLog = shouldLogStorm(guard);
|
|
650
675
|
const stormSuffix = guard && guard.active ? ` [storm:${guard.events.length}/${Math.round(guard.windowMs / 60000)}m]` : '';
|
|
676
|
+
|
|
651
677
|
if (guard && guard.active) {
|
|
652
678
|
scheduleStormRecovery(ctx, api, defaultFuncs, guard);
|
|
653
679
|
}
|
|
680
|
+
|
|
654
681
|
// Treat long-lived connections as normal lifecycle, keep logs calm
|
|
655
682
|
if (duration >= 30 * 60 * 1000) { // >= 30 minutes
|
|
656
683
|
if (allowLog) log.info('listenMqtt', `MQTT connection closed after ${seconds}s (normal lifecycle). Reconnecting...${stormSuffix}`);
|
|
@@ -663,7 +690,15 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
663
690
|
}
|
|
664
691
|
|
|
665
692
|
if (!ctx.loggedIn) return; // avoid loops if logged out
|
|
693
|
+
|
|
666
694
|
if (ctx.globalOptions.autoReconnect) {
|
|
695
|
+
// If error handler already scheduled a reconnect, don't do it again
|
|
696
|
+
if (ctx._reconnectScheduled) {
|
|
697
|
+
log.verbose('listenMqtt', 'Reconnect already scheduled by error handler. Skipping duplicate.');
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
ctx._reconnectScheduled = true;
|
|
701
|
+
|
|
667
702
|
const backoffState = getBackoffState(ctx);
|
|
668
703
|
const resetThreshold = backoffState.resetAfterMs || (3 * 60 * 1000);
|
|
669
704
|
let reconnectReason = 'close';
|
package/utils.js
CHANGED
|
@@ -59,8 +59,8 @@ function getAgent(url) {
|
|
|
59
59
|
function getJar() {
|
|
60
60
|
const jar = new CookieJar();
|
|
61
61
|
const originalSetCookie = jar.setCookie;
|
|
62
|
-
|
|
63
|
-
jar.setCookie = function(cookieOrStr, uri, options, callback) {
|
|
62
|
+
|
|
63
|
+
jar.setCookie = function (cookieOrStr, uri, options, callback) {
|
|
64
64
|
if (typeof options === 'function') {
|
|
65
65
|
callback = options;
|
|
66
66
|
options = {};
|
|
@@ -70,10 +70,10 @@ function getJar() {
|
|
|
70
70
|
}
|
|
71
71
|
return jar.setCookieSync(cookieOrStr, uri, options || {});
|
|
72
72
|
};
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
const originalGetCookies = jar.getCookies;
|
|
75
|
-
jar.getCookies = function(uri, options, callback) {
|
|
76
|
-
|
|
75
|
+
jar.getCookies = function (uri, options, callback) {
|
|
76
|
+
if (typeof options === 'function') {
|
|
77
77
|
callback = options;
|
|
78
78
|
options = {};
|
|
79
79
|
}
|
|
@@ -182,10 +182,10 @@ async function post(url, jar, form, options, ctx, customHeader) {
|
|
|
182
182
|
if (form) {
|
|
183
183
|
// 1. Clean null/undefined
|
|
184
184
|
form = cleanObject(form);
|
|
185
|
-
|
|
185
|
+
|
|
186
186
|
// 2. Check Content-Type
|
|
187
187
|
let contentType = headers['Content-Type'] || 'application/x-www-form-urlencoded';
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
if (contentType.includes('json')) {
|
|
190
190
|
op.body = JSON.stringify(form);
|
|
191
191
|
} else {
|
|
@@ -367,23 +367,41 @@ function generatePresence(userID) {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
function getGUID() {
|
|
370
|
-
|
|
370
|
+
// 1. Check Environment Variable (Best for Render/Heroku/Replit)
|
|
371
|
+
if (process.env.NEXUS_DEVICE_ID) {
|
|
372
|
+
return process.env.NEXUS_DEVICE_ID;
|
|
373
|
+
}
|
|
371
374
|
|
|
372
|
-
var
|
|
373
|
-
|
|
375
|
+
var devicePath = require("path").join(process.cwd(), "nexus_device.json");
|
|
376
|
+
var fs = require("fs");
|
|
374
377
|
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
// 2. Check File System (Local Persistence)
|
|
379
|
+
try {
|
|
380
|
+
if (fs.existsSync(devicePath)) {
|
|
381
|
+
var data = JSON.parse(fs.readFileSync(devicePath, "utf8"));
|
|
382
|
+
if (data && data.guid) return data.guid;
|
|
383
|
+
}
|
|
384
|
+
} catch (e) { }
|
|
377
385
|
|
|
386
|
+
// 3. Generate New Device ID
|
|
387
|
+
var sectionLength = Date.now();
|
|
388
|
+
var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
378
389
|
var r = Math.floor((sectionLength + Math.random() * 16) % 16);
|
|
379
|
-
/** @type {number} */
|
|
380
|
-
|
|
381
390
|
sectionLength = Math.floor(sectionLength / 16);
|
|
382
|
-
/** @type {string} */
|
|
383
|
-
|
|
384
391
|
var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
|
|
385
392
|
return _guid;
|
|
386
393
|
});
|
|
394
|
+
|
|
395
|
+
// 4. Save & Log
|
|
396
|
+
try {
|
|
397
|
+
fs.writeFileSync(devicePath, JSON.stringify({ guid: id, generatedAt: Date.now() }, null, 2));
|
|
398
|
+
} catch (e) { }
|
|
399
|
+
|
|
400
|
+
// Warn user to verify persistence
|
|
401
|
+
var log = require("npmlog");
|
|
402
|
+
log.warn("NEXUS-FCA", "New Device ID generated: " + id);
|
|
403
|
+
log.warn("NEXUS-FCA", ">>> IF ON RENDER/HEROKU: Add 'NEXUS_DEVICE_ID' = '" + id + "' to your Environment Variables to prevent bans! <<<");
|
|
404
|
+
|
|
387
405
|
return id;
|
|
388
406
|
}
|
|
389
407
|
|
|
@@ -1038,7 +1056,7 @@ function generateTimestampRelative() {
|
|
|
1038
1056
|
function makeDefaults(html, userID, ctx) {
|
|
1039
1057
|
var reqCounter = 1;
|
|
1040
1058
|
let fb_dtsg = null;
|
|
1041
|
-
|
|
1059
|
+
|
|
1042
1060
|
// Robust fb_dtsg extraction
|
|
1043
1061
|
const dtsgRegexes = [
|
|
1044
1062
|
/"DTSGInitData",\[\],{"token":"(.*?)"/,
|
|
@@ -1048,7 +1066,7 @@ function makeDefaults(html, userID, ctx) {
|
|
|
1048
1066
|
/name="fb_dtsg" value="(.*?)"/,
|
|
1049
1067
|
/name="dtsg_ag" value="(.*?)"/
|
|
1050
1068
|
];
|
|
1051
|
-
|
|
1069
|
+
|
|
1052
1070
|
for (const regex of dtsgRegexes) {
|
|
1053
1071
|
const match = html.match(regex);
|
|
1054
1072
|
if (match && match[1]) {
|
|
@@ -1056,7 +1074,7 @@ function makeDefaults(html, userID, ctx) {
|
|
|
1056
1074
|
break;
|
|
1057
1075
|
}
|
|
1058
1076
|
}
|
|
1059
|
-
|
|
1077
|
+
|
|
1060
1078
|
// Fallback to ctx if not found in HTML (or if HTML is partial)
|
|
1061
1079
|
if (!fb_dtsg && ctx.fb_dtsg) {
|
|
1062
1080
|
fb_dtsg = ctx.fb_dtsg;
|
|
@@ -1066,7 +1084,7 @@ function makeDefaults(html, userID, ctx) {
|
|
|
1066
1084
|
if (fb_dtsg) {
|
|
1067
1085
|
for (var i = 0; i < fb_dtsg.length; i++) ttstamp += fb_dtsg.charCodeAt(i);
|
|
1068
1086
|
}
|
|
1069
|
-
|
|
1087
|
+
|
|
1070
1088
|
var revision = getFrom(html, 'revision":', ",");
|
|
1071
1089
|
function mergeWithDefaults(obj) {
|
|
1072
1090
|
var newObj = {
|
|
@@ -1110,7 +1128,7 @@ function parseAndCheckLogin(ctx, defaultFuncs, retryCount = 0, sourceCall) {
|
|
|
1110
1128
|
return function (data) {
|
|
1111
1129
|
return tryPromise(function () {
|
|
1112
1130
|
log.verbose("parseAndCheckLogin", data.body);
|
|
1113
|
-
|
|
1131
|
+
|
|
1114
1132
|
// GOT compatibility: map request properties
|
|
1115
1133
|
const request = data.request;
|
|
1116
1134
|
const requestUrl = request.options ? request.options.url : request.uri;
|
|
@@ -1135,10 +1153,10 @@ function parseAndCheckLogin(ctx, defaultFuncs, retryCount = 0, sourceCall) {
|
|
|
1135
1153
|
"parseAndCheckLogin",
|
|
1136
1154
|
`Got status code ${data.statusCode} - Retrying in ${retryTime}ms...`
|
|
1137
1155
|
);
|
|
1138
|
-
|
|
1156
|
+
|
|
1139
1157
|
const url = requestUrl.toString();
|
|
1140
1158
|
const contentType = requestHeaders?.["content-type"]?.split(";")[0];
|
|
1141
|
-
|
|
1159
|
+
|
|
1142
1160
|
return delay(retryTime)
|
|
1143
1161
|
.then(() =>
|
|
1144
1162
|
contentType === "multipart/form-data"
|
|
@@ -1279,14 +1297,14 @@ function saveCookies(jar) {
|
|
|
1279
1297
|
if (c.indexOf(".facebook.com") > -1 || c.indexOf("facebook.com") > -1) {
|
|
1280
1298
|
try {
|
|
1281
1299
|
jar.setCookie(c, "https://www.facebook.com");
|
|
1282
|
-
} catch (e) {}
|
|
1283
|
-
|
|
1300
|
+
} catch (e) { }
|
|
1301
|
+
|
|
1284
1302
|
try {
|
|
1285
1303
|
var c_messenger = c.replace(/domain=\.?facebook\.com/i, "domain=.messenger.com");
|
|
1286
1304
|
if (c_messenger.indexOf(".messenger.com") > -1) {
|
|
1287
1305
|
jar.setCookie(c_messenger, "https://www.messenger.com");
|
|
1288
1306
|
}
|
|
1289
|
-
} catch (e) {}
|
|
1307
|
+
} catch (e) { }
|
|
1290
1308
|
}
|
|
1291
1309
|
});
|
|
1292
1310
|
return res;
|
|
@@ -1522,115 +1540,115 @@ function checkLiveCookie(ctx, defaultFuncs) {
|
|
|
1522
1540
|
* Intelligent request management to minimize Facebook account risks
|
|
1523
1541
|
*/
|
|
1524
1542
|
const smartSafetyLimiter = {
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1543
|
+
userSessions: {},
|
|
1544
|
+
|
|
1545
|
+
// Human-like delay patterns to avoid detection
|
|
1546
|
+
humanDelays: {
|
|
1547
|
+
typing: { min: 800, max: 2000 },
|
|
1548
|
+
reading: { min: 1000, max: 3000 },
|
|
1549
|
+
thinking: { min: 2000, max: 5000 },
|
|
1550
|
+
browsing: { min: 500, max: 1500 }
|
|
1551
|
+
},
|
|
1552
|
+
|
|
1553
|
+
// Risk level assessment
|
|
1554
|
+
assessRisk(userID, action) {
|
|
1555
|
+
if (!this.userSessions[userID]) {
|
|
1556
|
+
this.userSessions[userID] = {
|
|
1557
|
+
requestCount: 0,
|
|
1558
|
+
errorCount: 0,
|
|
1559
|
+
lastActivity: Date.now(),
|
|
1560
|
+
riskLevel: 'low'
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
const session = this.userSessions[userID];
|
|
1565
|
+
const timeSinceLastActivity = Date.now() - session.lastActivity;
|
|
1566
|
+
const errorRate = session.errorCount / Math.max(1, session.requestCount);
|
|
1567
|
+
|
|
1568
|
+
// Update risk level based on activity patterns - ULTRA SAFE TUNING
|
|
1569
|
+
if (errorRate > 0.2 || timeSinceLastActivity < 800) { // Stricter error threshold (0.2 vs 0.3)
|
|
1570
|
+
session.riskLevel = 'high';
|
|
1571
|
+
} else if (errorRate > 0.05 || timeSinceLastActivity < 2000) { // Stricter medium threshold
|
|
1572
|
+
session.riskLevel = 'medium';
|
|
1573
|
+
} else {
|
|
1574
|
+
session.riskLevel = 'low';
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
return session.riskLevel;
|
|
1578
|
+
},
|
|
1579
|
+
|
|
1580
|
+
// Get safe delay based on risk level and action type
|
|
1581
|
+
getSafeDelay(userID, action = 'browsing') {
|
|
1582
|
+
const riskLevel = this.assessRisk(userID, action);
|
|
1583
|
+
const baseDelay = this.humanDelays[action] || this.humanDelays.browsing;
|
|
1584
|
+
|
|
1585
|
+
// Risk multipliers for safety
|
|
1586
|
+
const riskMultipliers = {
|
|
1587
|
+
'low': 1,
|
|
1588
|
+
'medium': 1.5,
|
|
1589
|
+
'high': 2.5
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
const multiplier = riskMultipliers[riskLevel] || 1;
|
|
1593
|
+
const min = baseDelay.min * multiplier;
|
|
1594
|
+
const max = baseDelay.max * multiplier;
|
|
1595
|
+
|
|
1596
|
+
// Generate human-like random delay
|
|
1597
|
+
const baseDelayTime = Math.random() * (max - min) + min;
|
|
1598
|
+
const humanVariation = baseDelayTime * 0.1 * (Math.random() - 0.5);
|
|
1599
|
+
|
|
1600
|
+
return Math.max(200, Math.floor(baseDelayTime + humanVariation));
|
|
1601
|
+
},
|
|
1602
|
+
|
|
1603
|
+
// Record activity for safety metrics
|
|
1604
|
+
recordActivity(userID, isError = false) {
|
|
1605
|
+
if (!this.userSessions[userID]) {
|
|
1606
|
+
this.userSessions[userID] = {
|
|
1607
|
+
requestCount: 0,
|
|
1608
|
+
errorCount: 0,
|
|
1609
|
+
lastActivity: Date.now(),
|
|
1610
|
+
riskLevel: 'low'
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
const session = this.userSessions[userID];
|
|
1615
|
+
session.requestCount++;
|
|
1616
|
+
session.lastActivity = Date.now();
|
|
1617
|
+
|
|
1618
|
+
if (isError) {
|
|
1619
|
+
session.errorCount++;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// Auto-reset metrics every hour to prevent false risk escalation
|
|
1623
|
+
if (session.requestCount > 100) {
|
|
1624
|
+
session.requestCount = Math.floor(session.requestCount / 2);
|
|
1625
|
+
session.errorCount = Math.floor(session.errorCount / 2);
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
|
|
1629
|
+
// Check if action is safe to proceed
|
|
1630
|
+
isSafeToExecute(userID, action) {
|
|
1631
|
+
const riskLevel = this.assessRisk(userID, action);
|
|
1632
|
+
|
|
1633
|
+
// Always allow low risk actions
|
|
1634
|
+
if (riskLevel === 'low') return true;
|
|
1635
|
+
|
|
1636
|
+
// For higher risk, check recent activity
|
|
1637
|
+
const session = this.userSessions[userID];
|
|
1638
|
+
const timeSinceLastActivity = Date.now() - session.lastActivity;
|
|
1639
|
+
|
|
1640
|
+
// Medium risk: ensure some delay between actions
|
|
1641
|
+
if (riskLevel === 'medium' && timeSinceLastActivity < 3000) {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// High risk: require longer delays
|
|
1646
|
+
if (riskLevel === 'high' && timeSinceLastActivity < 10000) {
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
return true;
|
|
1630
1651
|
}
|
|
1631
|
-
|
|
1632
|
-
return true;
|
|
1633
|
-
}
|
|
1634
1652
|
};
|
|
1635
1653
|
|
|
1636
1654
|
/**
|
|
@@ -1647,20 +1665,20 @@ const allowList = process.env.NEXUS_FCA_ALLOW_LIST ? process.env.NEXUS_FCA_ALLOW
|
|
|
1647
1665
|
const blockList = process.env.NEXUS_FCA_BLOCK_LIST ? process.env.NEXUS_FCA_BLOCK_LIST.split(',') : null;
|
|
1648
1666
|
|
|
1649
1667
|
function isUserAllowed(userID) {
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1668
|
+
if (blockList && blockList.includes(userID)) return false;
|
|
1669
|
+
if (allowList && !allowList.includes(userID)) return false;
|
|
1670
|
+
return true;
|
|
1653
1671
|
}
|
|
1654
1672
|
|
|
1655
1673
|
// Legacy rate limiter - kept for backward compatibility but optimized for safety
|
|
1656
1674
|
const rateLimiter = {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1675
|
+
limits: {},
|
|
1676
|
+
windowMs: 60 * 1000,
|
|
1677
|
+
max: 20,
|
|
1678
|
+
check(userID, action) {
|
|
1679
|
+
// Use smart safety limiter instead of blocking
|
|
1680
|
+
return smartSafetyLimiter.isSafeToExecute(userID, action);
|
|
1681
|
+
}
|
|
1664
1682
|
};
|
|
1665
1683
|
|
|
1666
1684
|
// Add robust session validation utility used by listenMqtt
|