nexus-fca 3.1.0 β 3.1.2
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 +7 -3
- package/index.js +168 -5
- package/lib/safety/CookieRefresher.js +12 -1
- package/lib/safety/FacebookSafety.js +84 -35
- package/lib/safety/StealthMode.js +131 -0
- package/package.json +5 -3
- package/src/listenMqtt.js +96 -42
- package/src/listenNotification.js +22 -56
- package/src/listenRealtime.js +161 -0
- package/src/listenSpeed.js +197 -0
- package/utils.js +224 -102
- package/src/changeAvatarV2.js +0 -75
- package/src/getThreadHistoryDeprecated.js +0 -55
- package/src/getThreadInfoDeprecated.js +0 -49
- package/src/getThreadListDeprecated.js +0 -54
package/README.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
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.1.
|
|
5
|
+
# Nexus-FCA v3.1.1 β π THE BEST, SAFEST, MOST STABLE FCA
|
|
6
6
|
|
|
7
7
|
Modern, safe, productionβready Messenger (Facebook Chat) API layer with **email/password + appState login**, **proxy support**, **random user agent**, adaptive session & connection resilience, proactive cookie refresh, MQTT stability enhancements, delivery reliability safeguards, memory protection, and rich runtime metrics. Promise + callback compatible, TypeScript typed, minimal friction.
|
|
8
8
|
|
|
9
|
-
## π NEW in 3.1.
|
|
9
|
+
## π NEW in 3.1.1 - Industry Leading Features!
|
|
10
|
+
- β
**Smart MQTT Recovery** - Auto-refreshes Sequence ID on errors to prevent loops
|
|
11
|
+
- β
**Proactive Lifecycle Management** - Randomized reconnects (26-60m) to mimic human behavior
|
|
10
12
|
- β
**Email/Password Login** - Login with Facebook credentials (not just cookies!)
|
|
11
13
|
- β
**Advanced Proxy Support** - HTTP/HTTPS/SOCKS5 proxy for all connections
|
|
12
14
|
- β
**Random User Agent** - 14+ realistic user agents to avoid detection
|
|
@@ -139,7 +141,9 @@ await login({ appState }, { disablePreflight: true });
|
|
|
139
141
|
```
|
|
140
142
|
|
|
141
143
|
---
|
|
142
|
-
## π°οΈ MQTT Enhancements (Since
|
|
144
|
+
## π°οΈ MQTT Enhancements (Since 3.1.x)
|
|
145
|
+
- **Smart Recovery**: Fetches fresh Sequence ID before reconnecting on errors (prevents stale token loops)
|
|
146
|
+
- **Lifecycle Management**: Proactive randomized reconnects (26-60m) to avoid long-session forced disconnects
|
|
143
147
|
- Adaptive reconnect curve (caps 5m)
|
|
144
148
|
- Layered post-refresh probes (1s / 10s / 30s)
|
|
145
149
|
- Synthetic randomized keepalives (55β75s)
|
package/index.js
CHANGED
|
@@ -76,7 +76,7 @@ const globalSafety = new FacebookSafety({
|
|
|
76
76
|
enableLoginValidation: true,
|
|
77
77
|
enableSafeDelays: true, // Human-like delays to reduce detection
|
|
78
78
|
bypassRegionLock: true,
|
|
79
|
-
ultraLowBanMode:
|
|
79
|
+
ultraLowBanMode: true // FORCED ULTRA-SAFE MODE for maximum protection
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
let checkVerified = null;
|
|
@@ -209,9 +209,75 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
209
209
|
if (process.env.NEXUS_REGION) {
|
|
210
210
|
try { region = process.env.NEXUS_REGION.toUpperCase(); } catch(_) {}
|
|
211
211
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
212
|
+
|
|
213
|
+
// Robust fb_dtsg extraction
|
|
214
|
+
const dtsgRegexes = [
|
|
215
|
+
/DTSGInitialData.*?token":"(.*?)"/,
|
|
216
|
+
/"DTSGInitData",\[\],{"token":"(.*?)"/,
|
|
217
|
+
/\["DTSGInitData",\[\],{"token":"(.*?)"/,
|
|
218
|
+
/name="fb_dtsg" value="(.*?)"/,
|
|
219
|
+
/name="dtsg_ag" value="(.*?)"/
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
for (const regex of dtsgRegexes) {
|
|
223
|
+
const match = html.match(regex);
|
|
224
|
+
if (match && match[1]) {
|
|
225
|
+
fb_dtsg = match[1];
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// NEW: JSON Parsing Fallback (inspired by ws3-fca)
|
|
231
|
+
if (!fb_dtsg) {
|
|
232
|
+
try {
|
|
233
|
+
const extractNetData = (html) => {
|
|
234
|
+
const allScriptsData = [];
|
|
235
|
+
const scriptRegex = /<script type="application\/json"[^>]*>(.*?)<\/script>/g;
|
|
236
|
+
let match;
|
|
237
|
+
while ((match = scriptRegex.exec(html)) !== null) {
|
|
238
|
+
try {
|
|
239
|
+
allScriptsData.push(JSON.parse(match[1]));
|
|
240
|
+
} catch (e) { }
|
|
241
|
+
}
|
|
242
|
+
return allScriptsData;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const netData = extractNetData(html);
|
|
246
|
+
|
|
247
|
+
const findConfig = (key) => {
|
|
248
|
+
for (const scriptData of netData) {
|
|
249
|
+
if (scriptData.require) {
|
|
250
|
+
for (const req of scriptData.require) {
|
|
251
|
+
if (Array.isArray(req) && req[0] === key && req[2]) {
|
|
252
|
+
return req[2];
|
|
253
|
+
}
|
|
254
|
+
if (Array.isArray(req) && req[3] && req[3][0] && req[3][0].__bbox && req[3][0].__bbox.define) {
|
|
255
|
+
for (const def of req[3][0].__bbox.define) {
|
|
256
|
+
if (Array.isArray(def) && def[0].endsWith(key) && def[2]) {
|
|
257
|
+
return def[2];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const dtsgData = findConfig("DTSGInitialData");
|
|
268
|
+
if (dtsgData && dtsgData.token) {
|
|
269
|
+
fb_dtsg = dtsgData.token;
|
|
270
|
+
log.verbose("login", "Found fb_dtsg via JSON parsing");
|
|
271
|
+
}
|
|
272
|
+
} catch (e) {
|
|
273
|
+
log.verbose("login", "JSON parsing for fb_dtsg failed: " + e.message);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!fb_dtsg) {
|
|
278
|
+
// log.warn("login", "Could not find fb_dtsg in HTML. Session might be limited.");
|
|
279
|
+
} else {
|
|
280
|
+
log.verbose("login", "Found fb_dtsg: " + fb_dtsg.substring(0, 10) + "...");
|
|
215
281
|
}
|
|
216
282
|
|
|
217
283
|
|
|
@@ -304,6 +370,36 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
304
370
|
}
|
|
305
371
|
};
|
|
306
372
|
const defaultFuncs = utils.makeDefaults(html, i_userID || userID, ctx);
|
|
373
|
+
|
|
374
|
+
// ULTRA-SAFE WRAPPER: Throttle ALL API calls, not just sendMessage
|
|
375
|
+
const originalPost = defaultFuncs.post;
|
|
376
|
+
const originalPostFormData = defaultFuncs.postFormData;
|
|
377
|
+
const originalGet = defaultFuncs.get;
|
|
378
|
+
|
|
379
|
+
defaultFuncs.post = async function(...args) {
|
|
380
|
+
if (globalSafety && typeof globalSafety.applyAdaptiveSendDelay === 'function') {
|
|
381
|
+
await globalSafety.applyAdaptiveSendDelay();
|
|
382
|
+
}
|
|
383
|
+
return originalPost.apply(this, args);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
defaultFuncs.postFormData = async function(...args) {
|
|
387
|
+
if (globalSafety && typeof globalSafety.applyAdaptiveSendDelay === 'function') {
|
|
388
|
+
await globalSafety.applyAdaptiveSendDelay();
|
|
389
|
+
}
|
|
390
|
+
return originalPostFormData.apply(this, args);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// We don't throttle GET as much, but maybe a little?
|
|
394
|
+
// Actually, let's throttle everything for "Most Safe" mode.
|
|
395
|
+
defaultFuncs.get = async function(...args) {
|
|
396
|
+
if (globalSafety && typeof globalSafety.applyAdaptiveSendDelay === 'function') {
|
|
397
|
+
// Use a lighter delay for GETs if needed, but for now use the same safe pipeline
|
|
398
|
+
await globalSafety.applyAdaptiveSendDelay();
|
|
399
|
+
}
|
|
400
|
+
return originalGet.apply(this, args);
|
|
401
|
+
};
|
|
402
|
+
|
|
307
403
|
require("fs")
|
|
308
404
|
.readdirSync(__dirname + "/src/")
|
|
309
405
|
.filter((v) => v.endsWith(".js"))
|
|
@@ -555,9 +651,76 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
555
651
|
return res;
|
|
556
652
|
})
|
|
557
653
|
.then(handleRedirect)
|
|
558
|
-
.then(res => {
|
|
654
|
+
.then(async res => {
|
|
559
655
|
const html = res.body;
|
|
560
656
|
const Obj = buildAPI(globalOptions, html, jar);
|
|
657
|
+
|
|
658
|
+
// Fallback: Try mbasic.facebook.com if fb_dtsg is missing
|
|
659
|
+
if (!Obj.ctx.fb_dtsg) {
|
|
660
|
+
logger("Attempting to fetch fb_dtsg from mbasic.facebook.com...", "info");
|
|
661
|
+
try {
|
|
662
|
+
const mobileOptions = {
|
|
663
|
+
...globalOptions,
|
|
664
|
+
userAgent: "Mozilla/5.0 (Linux; Android 12; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36"
|
|
665
|
+
};
|
|
666
|
+
const mRes = await utils.get("https://mbasic.facebook.com/", jar, null, mobileOptions);
|
|
667
|
+
const mHtml = mRes.body;
|
|
668
|
+
const mMatch = mHtml.match(/name="fb_dtsg" value="(.*?)"/);
|
|
669
|
+
if (mMatch && mMatch[1]) {
|
|
670
|
+
Obj.ctx.fb_dtsg = mMatch[1];
|
|
671
|
+
Obj.ctx.ttstamp = "2" + Obj.ctx.fb_dtsg.split("").map(c => c.charCodeAt(0)).join("");
|
|
672
|
+
logger("Found fb_dtsg from mbasic.facebook.com", "success");
|
|
673
|
+
|
|
674
|
+
// Re-create defaultFuncs with the new token
|
|
675
|
+
Obj.defaultFuncs = utils.makeDefaults(html, Obj.ctx.userID, Obj.ctx);
|
|
676
|
+
} else {
|
|
677
|
+
// Try one more: dtsg_ag
|
|
678
|
+
const agMatch = mHtml.match(/name="dtsg_ag" value="(.*?)"/);
|
|
679
|
+
if (agMatch && agMatch[1]) {
|
|
680
|
+
Obj.ctx.fb_dtsg = agMatch[1];
|
|
681
|
+
Obj.ctx.ttstamp = "2" + Obj.ctx.fb_dtsg.split("").map(c => c.charCodeAt(0)).join("");
|
|
682
|
+
logger("Found fb_dtsg (ag) from mbasic.facebook.com", "success");
|
|
683
|
+
Obj.defaultFuncs = utils.makeDefaults(html, Obj.ctx.userID, Obj.ctx);
|
|
684
|
+
} else {
|
|
685
|
+
logger("Failed to find fb_dtsg in mbasic response", "warn");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Second Fallback: Try business.facebook.com
|
|
690
|
+
if (!Obj.ctx.fb_dtsg) {
|
|
691
|
+
logger("Attempting to fetch fb_dtsg from business.facebook.com...", "info");
|
|
692
|
+
try {
|
|
693
|
+
const bRes = await utils.get("https://business.facebook.com/business_locations", jar, null, globalOptions);
|
|
694
|
+
const bHtml = bRes.body;
|
|
695
|
+
const bMatch = bHtml.match(/name="fb_dtsg" value="(.*?)"/) || bHtml.match(/DTSGInitialData.*?token":"(.*?)"/);
|
|
696
|
+
if (bMatch && bMatch[1]) {
|
|
697
|
+
Obj.ctx.fb_dtsg = bMatch[1];
|
|
698
|
+
Obj.ctx.ttstamp = "2" + Obj.ctx.fb_dtsg.split("").map(c => c.charCodeAt(0)).join("");
|
|
699
|
+
logger("Found fb_dtsg from business.facebook.com", "success");
|
|
700
|
+
Obj.defaultFuncs = utils.makeDefaults(html, Obj.ctx.userID, Obj.ctx);
|
|
701
|
+
} else {
|
|
702
|
+
logger("Failed to find fb_dtsg in business response", "warn");
|
|
703
|
+
// Debug: Print what we got
|
|
704
|
+
if (bHtml.includes("login_form") || bHtml.includes("checkpoint")) {
|
|
705
|
+
logger("Response indicates login/checkpoint page", "error");
|
|
706
|
+
} else {
|
|
707
|
+
logger("Response start: " + bHtml.substring(0, 200), "verbose");
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} catch (e) {
|
|
711
|
+
logger("Failed to fetch fallback fb_dtsg from business: " + e.message, "warn");
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
} catch (e) {
|
|
716
|
+
logger("Failed to fetch fallback fb_dtsg: " + e.message, "warn");
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (!Obj.ctx.fb_dtsg) {
|
|
721
|
+
log.warn("login", "Could not find fb_dtsg in HTML or fallbacks. Session might be limited.");
|
|
722
|
+
}
|
|
723
|
+
|
|
561
724
|
ctx = Obj.ctx;
|
|
562
725
|
api = Obj.api;
|
|
563
726
|
return res;
|
|
@@ -101,7 +101,12 @@ class CookieRefresher {
|
|
|
101
101
|
try {
|
|
102
102
|
// 1. First do a simple fetch to keep session alive
|
|
103
103
|
logger('CookieRefresher', 'Refreshing cookies to extend session...', 'info');
|
|
104
|
+
|
|
105
|
+
// Enhanced URL list to simulate real user activity and keep session alive
|
|
104
106
|
const urls = [
|
|
107
|
+
'https://www.facebook.com/',
|
|
108
|
+
'https://www.facebook.com/notifications',
|
|
109
|
+
'https://www.facebook.com/messages/t/',
|
|
105
110
|
'https://www.facebook.com/me',
|
|
106
111
|
'https://www.facebook.com/ajax/haste-response/?__a=1',
|
|
107
112
|
'https://www.facebook.com/ajax/bootloader-endpoint/?__a=1'
|
|
@@ -110,6 +115,11 @@ class CookieRefresher {
|
|
|
110
115
|
let success = false;
|
|
111
116
|
for (const url of urls) {
|
|
112
117
|
try {
|
|
118
|
+
// Add random delay between requests to simulate human browsing
|
|
119
|
+
if (success) {
|
|
120
|
+
await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 3000));
|
|
121
|
+
}
|
|
122
|
+
|
|
113
123
|
// Try multiple URLs in case some are blocked
|
|
114
124
|
const res = await this.defaultFuncs.get(url, this.jar, {});
|
|
115
125
|
|
|
@@ -117,7 +127,8 @@ class CookieRefresher {
|
|
|
117
127
|
if (res && res.headers && res.headers["set-cookie"]) {
|
|
118
128
|
this.utils.saveCookies(this.jar)(res);
|
|
119
129
|
success = true;
|
|
120
|
-
break
|
|
130
|
+
// Don't break immediately, hit a few more to look like a real session
|
|
131
|
+
if (Math.random() > 0.7) break;
|
|
121
132
|
}
|
|
122
133
|
} catch (err) {
|
|
123
134
|
logger('CookieRefresher', `Failed to refresh with ${url}: ${err.message}`, 'debug');
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const StealthMode = require('./StealthMode');
|
|
9
10
|
|
|
10
11
|
class FacebookSafety {
|
|
11
12
|
constructor(options = {}) {
|
|
@@ -23,15 +24,30 @@ class FacebookSafety {
|
|
|
23
24
|
...options
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
//
|
|
27
|
+
// Initialize Stealth Mode with RELAXED settings (User Feedback: "Too slow")
|
|
28
|
+
this.stealthMode = new StealthMode({
|
|
29
|
+
maxRequestsPerMinute: this.options.ultraLowBanMode ? 1000 : 2000, // Increased massively for high-traffic bots
|
|
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
|
+
});
|
|
36
|
+
|
|
37
|
+
// ULTRA-SAFE user agents - Most common real browsers (Nov 2025)
|
|
38
|
+
// These are the MOST COMMON UAs to blend in with real users
|
|
27
39
|
this.safeUserAgents = [
|
|
28
|
-
|
|
29
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
30
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64
|
|
31
|
-
|
|
32
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
40
|
+
// Chrome Windows (most 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',
|
|
42
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
43
|
+
// Edge Windows (very common)
|
|
44
|
+
'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',
|
|
45
|
+
// Chrome Mac (common)
|
|
46
|
+
'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',
|
|
47
|
+
// Safari Mac (very common for Mac users)
|
|
48
|
+
'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'
|
|
33
49
|
];
|
|
34
|
-
// NEW: fixed user agent anchor (set once per session)
|
|
50
|
+
// NEW: fixed user agent anchor (set once per session) - NEVER CHANGE during session!
|
|
35
51
|
this._fixedUA = null;
|
|
36
52
|
|
|
37
53
|
this.safeDomains = [
|
|
@@ -43,11 +59,13 @@ class FacebookSafety {
|
|
|
43
59
|
this.regions = ['ASH', 'ATL', 'DFW', 'ORD', 'PHX', 'SJC', 'IAD'];
|
|
44
60
|
this.currentRegion = this.regions[Math.floor(Math.random() * this.regions.length)];
|
|
45
61
|
|
|
62
|
+
// ULTRA-SAFE human delay patterns - Relaxed for usability
|
|
46
63
|
this.humanDelayPatterns = {
|
|
47
|
-
typing: { min:
|
|
48
|
-
reading: { min: 1000, max: 3000 },
|
|
49
|
-
thinking: { min:
|
|
50
|
-
browsing: { min: 500, max:
|
|
64
|
+
typing: { min: 500, max: 1500 }, // Normal typing (0.5-1.5s)
|
|
65
|
+
reading: { min: 1000, max: 3000 }, // Normal reading (1-3s)
|
|
66
|
+
thinking: { min: 1000, max: 4000 }, // Normal thinking (1-4s)
|
|
67
|
+
browsing: { min: 500, max: 2000 }, // Normal browsing (0.5-2s)
|
|
68
|
+
messageDelay: { min: 1000, max: 3000 } // 1-3s between messages (much faster)
|
|
51
69
|
};
|
|
52
70
|
|
|
53
71
|
this.sessionMetrics = {
|
|
@@ -150,17 +168,25 @@ class FacebookSafety {
|
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
/**
|
|
153
|
-
* Generate human-like delay patterns
|
|
171
|
+
* Generate human-like delay patterns - ULTRA SAFE VERSION
|
|
154
172
|
*/
|
|
155
173
|
getHumanDelay(action = 'browsing') {
|
|
156
|
-
if (!this.options.enableSafeDelays) return
|
|
174
|
+
if (!this.options.enableSafeDelays) return 5000; // Minimum 5s even if disabled!
|
|
157
175
|
|
|
158
176
|
const pattern = this.humanDelayPatterns[action] || this.humanDelayPatterns.browsing;
|
|
159
177
|
const baseDelay = Math.random() * (pattern.max - pattern.min) + pattern.min;
|
|
160
178
|
|
|
161
|
-
// Add randomness to make it
|
|
162
|
-
const variation = baseDelay * 0.
|
|
163
|
-
|
|
179
|
+
// Add MORE randomness to make it VERY human-like
|
|
180
|
+
const variation = baseDelay * 0.4 * (Math.random() - 0.5); // Β±40% variation
|
|
181
|
+
const jitter = Math.random() * 2000; // Add 0-2s random jitter
|
|
182
|
+
|
|
183
|
+
// Risk-based multiplier - slow down even more if high risk
|
|
184
|
+
const riskMultiplier = this.sessionMetrics.riskLevel === 'high' ? 2.5 :
|
|
185
|
+
this.sessionMetrics.riskLevel === 'medium' ? 1.8 : 1.3;
|
|
186
|
+
|
|
187
|
+
const finalDelay = (baseDelay + variation + jitter) * riskMultiplier;
|
|
188
|
+
|
|
189
|
+
return Math.max(3000, Math.floor(finalDelay)); // Minimum 3 seconds!
|
|
164
190
|
}
|
|
165
191
|
|
|
166
192
|
/**
|
|
@@ -200,9 +226,21 @@ class FacebookSafety {
|
|
|
200
226
|
|
|
201
227
|
// Check cookie age (older than 30 days might be risky)
|
|
202
228
|
const oldCookies = parsed.filter(cookie => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
229
|
+
let expVal = cookie.expires || cookie.expirationDate;
|
|
230
|
+
// Handle Unix timestamp in seconds (common in FB cookies)
|
|
231
|
+
if (typeof expVal === 'number' && expVal < 10000000000) {
|
|
232
|
+
expVal *= 1000;
|
|
233
|
+
}
|
|
234
|
+
const expires = new Date(expVal);
|
|
235
|
+
// If invalid date, assume valid to be safe
|
|
236
|
+
if (isNaN(expires.getTime())) return false;
|
|
237
|
+
|
|
238
|
+
// Check if expired
|
|
239
|
+
const timeUntilExpiry = expires.getTime() - Date.now();
|
|
240
|
+
// If expired more than 30 days ago, it's "old"
|
|
241
|
+
// If timeUntilExpiry is negative, it's expired.
|
|
242
|
+
// We want to flag cookies that are LONG expired.
|
|
243
|
+
return timeUntilExpiry < -(30 * 24 * 60 * 60 * 1000);
|
|
206
244
|
});
|
|
207
245
|
|
|
208
246
|
if (oldCookies.length > parsed.length * 0.5) {
|
|
@@ -267,7 +305,7 @@ class FacebookSafety {
|
|
|
267
305
|
}
|
|
268
306
|
|
|
269
307
|
/**
|
|
270
|
-
* Setup safe token refresh intervals
|
|
308
|
+
* Setup safe token refresh intervals - ULTRA SAFE VERSION
|
|
271
309
|
*/
|
|
272
310
|
setupSafeRefresh() {
|
|
273
311
|
// Replace previous interval/timer to avoid stacking
|
|
@@ -279,20 +317,25 @@ class FacebookSafety {
|
|
|
279
317
|
clearTimeout(this._safeRefreshTimer);
|
|
280
318
|
this._safeRefreshTimer = null;
|
|
281
319
|
}
|
|
282
|
-
//
|
|
283
|
-
// Previous risk-tier windows (25β60m) replaced per instruction.
|
|
320
|
+
// MAXIMUM STEALTH: Very long refresh intervals (4-8 hours!)
|
|
284
321
|
const schedule = () => {
|
|
285
322
|
if (this._destroyed) return;
|
|
286
|
-
// Base window 3hβ5h. If risk escalates HIGH, clamp to 1hβ1.5h for safety.
|
|
287
323
|
let minMs, maxMs;
|
|
288
324
|
if (this.sessionMetrics.riskLevel === 'high') {
|
|
289
|
-
|
|
290
|
-
|
|
325
|
+
// High risk: 2-3 hours (still slow to avoid detection)
|
|
326
|
+
minMs = 2 * 60 * 60 * 1000; // 2h
|
|
327
|
+
maxMs = 3 * 60 * 60 * 1000; // 3h
|
|
328
|
+
} else if (this.sessionMetrics.riskLevel === 'medium') {
|
|
329
|
+
// Medium risk: 1.5-2.5 hours
|
|
330
|
+
minMs = 1.5 * 60 * 60 * 1000; // 1.5h
|
|
331
|
+
maxMs = 2.5 * 60 * 60 * 1000; // 2.5h
|
|
291
332
|
} else {
|
|
292
|
-
|
|
293
|
-
|
|
333
|
+
// Low risk: 50-90 minutes (Keep session alive!)
|
|
334
|
+
minMs = 50 * 60 * 1000; // 50m
|
|
335
|
+
maxMs = 90 * 60 * 1000; // 90m
|
|
294
336
|
}
|
|
295
337
|
const interval = minMs + Math.random() * (maxMs - minMs);
|
|
338
|
+
console.log(`π Next token refresh in ${Math.floor(interval / (60 * 1000))}m (keep-alive mode)`);
|
|
296
339
|
const t = setTimeout(async () => {
|
|
297
340
|
await this.refreshSafeSession();
|
|
298
341
|
schedule();
|
|
@@ -438,22 +481,22 @@ class FacebookSafety {
|
|
|
438
481
|
this._periodicRecycleTimer = t;
|
|
439
482
|
}
|
|
440
483
|
|
|
441
|
-
// Heartbeat ping & watchdog
|
|
484
|
+
// Heartbeat ping & watchdog - ULTRA SAFE VERSION
|
|
442
485
|
_startHeartbeat() {
|
|
443
486
|
if (this._heartbeatTimer) clearInterval(this._heartbeatTimer);
|
|
444
487
|
if (this._watchdogTimer) clearInterval(this._watchdogTimer);
|
|
445
488
|
if (this._destroyed) return;
|
|
446
|
-
//
|
|
489
|
+
// MAXIMUM STEALTH: Much slower heartbeat (2-3 minutes!)
|
|
447
490
|
this._heartbeatTimer = setInterval(() => {
|
|
448
491
|
if (this._destroyed) return;
|
|
449
492
|
try {
|
|
450
493
|
if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
|
|
494
|
+
// Only ping, don't publish foreground state (less detectable)
|
|
451
495
|
if (this.ctx.mqttClient.ping) this.ctx.mqttClient.ping();
|
|
452
|
-
try { this.ctx.mqttClient.publish('/foreground_state', JSON.stringify({ foreground: true })); } catch(_) {}
|
|
453
496
|
this.safetyEmit('heartbeat', { ts: Date.now() });
|
|
454
497
|
}
|
|
455
498
|
} catch(_) {}
|
|
456
|
-
}, (
|
|
499
|
+
}, (120 + Math.random()*60) * 1000); // 2-3 minutes!
|
|
457
500
|
this._watchdogTimer = setInterval(() => {
|
|
458
501
|
if (this._destroyed) return;
|
|
459
502
|
const idle = Date.now() - this._lastEventTs;
|
|
@@ -607,8 +650,8 @@ class FacebookSafety {
|
|
|
607
650
|
*/
|
|
608
651
|
scheduleLightPoke() {
|
|
609
652
|
if (this._lightPokeTimer || this._destroyed) return;
|
|
610
|
-
const base =
|
|
611
|
-
const jitter = (Math.random()*
|
|
653
|
+
const base = 45 * 60 * 1000; // 45m (Reduced from 6h to keep session alive)
|
|
654
|
+
const jitter = (Math.random()*20 - 10) * 60 * 1000; // Β±10m
|
|
612
655
|
const schedule = () => {
|
|
613
656
|
if (this._destroyed) return;
|
|
614
657
|
const t = setTimeout(async () => {
|
|
@@ -627,7 +670,7 @@ class FacebookSafety {
|
|
|
627
670
|
}
|
|
628
671
|
} catch(_) {}
|
|
629
672
|
schedule();
|
|
630
|
-
}, base + (Math.random()*
|
|
673
|
+
}, base + (Math.random()*20 - 10) * 60 * 1000);
|
|
631
674
|
this._registerTimer(t);
|
|
632
675
|
this._lightPokeTimer = t;
|
|
633
676
|
};
|
|
@@ -777,7 +820,13 @@ class FacebookSafety {
|
|
|
777
820
|
return Math.floor(min + Math.random()*(max-min));
|
|
778
821
|
}
|
|
779
822
|
|
|
780
|
-
applyAdaptiveSendDelay(){
|
|
823
|
+
async applyAdaptiveSendDelay(){
|
|
824
|
+
// First, check Stealth Mode limits (rate limiting & pauses)
|
|
825
|
+
if (this.stealthMode) {
|
|
826
|
+
await this.stealthMode.waitIfNeeded();
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Then apply adaptive delay based on risk
|
|
781
830
|
const d = this.computeAdaptiveSendDelay();
|
|
782
831
|
if (!d) return Promise.resolve();
|
|
783
832
|
return new Promise(r=> setTimeout(r, d));
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus-FCA Stealth Mode - Human Behavior Simulation
|
|
3
|
+
* Implements strict rate limiting and human-like pauses to prevent bans.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class StealthMode {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = {
|
|
9
|
+
maxRequestsPerMinute: 1000, // Relaxed from 30 to 1000 based on user feedback
|
|
10
|
+
enableRandomPauses: true,
|
|
11
|
+
pauseProbability: 0.0001, // Reduced from 1% to 0.01%
|
|
12
|
+
minPauseMinutes: 0.1,
|
|
13
|
+
maxPauseMinutes: 0.5,
|
|
14
|
+
dailyRequestLimit: 500000, // Increased from 2000 to 500k
|
|
15
|
+
...options
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
this.requestHistory = [];
|
|
19
|
+
this.dailyCount = 0;
|
|
20
|
+
this.lastReset = Date.now();
|
|
21
|
+
this.inPause = false;
|
|
22
|
+
this.pauseUntil = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if we can proceed with a request
|
|
27
|
+
* Returns { allowed: boolean, waitMs: number, reason: string }
|
|
28
|
+
*/
|
|
29
|
+
canProceed() {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
|
|
32
|
+
// Check if we are in a forced pause
|
|
33
|
+
if (this.inPause) {
|
|
34
|
+
if (now < this.pauseUntil) {
|
|
35
|
+
return {
|
|
36
|
+
allowed: false,
|
|
37
|
+
waitMs: this.pauseUntil - now,
|
|
38
|
+
reason: 'Human pause active'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
this.inPause = false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Reset daily limit if it's a new day
|
|
45
|
+
if (now - this.lastReset > 24 * 60 * 60 * 1000) {
|
|
46
|
+
this.dailyCount = 0;
|
|
47
|
+
this.lastReset = now;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check daily limit
|
|
51
|
+
if (this.dailyCount >= this.options.dailyRequestLimit) {
|
|
52
|
+
return {
|
|
53
|
+
allowed: false,
|
|
54
|
+
waitMs: 60 * 60 * 1000, // Wait an hour before checking again (or stop)
|
|
55
|
+
reason: 'Daily limit reached'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Clean up old history (older than 1 minute)
|
|
60
|
+
this.requestHistory = this.requestHistory.filter(ts => now - ts < 60000);
|
|
61
|
+
|
|
62
|
+
// Check rate limit
|
|
63
|
+
if (this.requestHistory.length >= this.options.maxRequestsPerMinute) {
|
|
64
|
+
// Calculate when the oldest request expires
|
|
65
|
+
const oldest = this.requestHistory[0];
|
|
66
|
+
const waitMs = 60000 - (now - oldest) + 1000; // +1s buffer
|
|
67
|
+
return {
|
|
68
|
+
allowed: false,
|
|
69
|
+
waitMs: waitMs,
|
|
70
|
+
reason: 'Rate limit exceeded'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { allowed: true, waitMs: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Record a request and potentially trigger a random pause
|
|
79
|
+
*/
|
|
80
|
+
recordAction() {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
this.requestHistory.push(now);
|
|
83
|
+
this.dailyCount++;
|
|
84
|
+
|
|
85
|
+
// Random pause trigger
|
|
86
|
+
if (this.options.enableRandomPauses && Math.random() < this.options.pauseProbability) {
|
|
87
|
+
this.triggerRandomPause();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Trigger a random "coffee break" pause
|
|
93
|
+
*/
|
|
94
|
+
triggerRandomPause() {
|
|
95
|
+
const minMs = this.options.minPauseMinutes * 60 * 1000;
|
|
96
|
+
const maxMs = this.options.maxPauseMinutes * 60 * 1000;
|
|
97
|
+
const duration = Math.floor(Math.random() * (maxMs - minMs)) + minMs;
|
|
98
|
+
|
|
99
|
+
this.inPause = true;
|
|
100
|
+
this.pauseUntil = Date.now() + duration;
|
|
101
|
+
|
|
102
|
+
const minutes = Math.floor(duration / 60000);
|
|
103
|
+
const seconds = Math.floor((duration % 60000) / 1000);
|
|
104
|
+
const timeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
105
|
+
console.log(`β Stealth Mode: Taking a human break for ${timeStr}...`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Wait until it's safe to proceed
|
|
110
|
+
*/
|
|
111
|
+
async waitIfNeeded() {
|
|
112
|
+
let logged = false;
|
|
113
|
+
while (true) {
|
|
114
|
+
const status = this.canProceed();
|
|
115
|
+
if (status.allowed) {
|
|
116
|
+
this.recordAction();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Only log once per wait cycle to avoid spamming console from parallel requests
|
|
121
|
+
if (!logged && status.waitMs > 2000) {
|
|
122
|
+
console.log(`π‘οΈ Stealth Mode: Waiting ${Math.ceil(status.waitMs / 1000)}s (${status.reason})`);
|
|
123
|
+
logged = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, status.waitMs));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = StealthMode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-fca",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
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": {
|
|
@@ -15,20 +15,22 @@
|
|
|
15
15
|
"chalk": "^4.1.2",
|
|
16
16
|
"cheerio": "^1.0.0-rc.10",
|
|
17
17
|
"duplexify": "^4.1.3",
|
|
18
|
+
"form-data": "^4.0.5",
|
|
18
19
|
"got": "^11.8.6",
|
|
19
20
|
"gradient-string": "^2.0.2",
|
|
21
|
+
"http-proxy-agent": "^7.0.2",
|
|
20
22
|
"https-proxy-agent": "^7.0.5",
|
|
21
23
|
"lodash": "^4.17.21",
|
|
22
24
|
"mqtt": "^4.3.8",
|
|
23
|
-
"socks-proxy-agent": "^8.0.4",
|
|
24
25
|
"node-cache": "^5.1.2",
|
|
25
26
|
"npmlog": "^7.0.1",
|
|
26
|
-
"request": "^2.88.2",
|
|
27
27
|
"semver": "^7.5.0",
|
|
28
28
|
"sequelize": "^6.37.6",
|
|
29
|
+
"socks-proxy-agent": "^8.0.4",
|
|
29
30
|
"sqlite": "^5.1.1",
|
|
30
31
|
"sqlite3": "^5.1.7",
|
|
31
32
|
"totp-generator": "^1.0.0",
|
|
33
|
+
"tough-cookie": "^6.0.0",
|
|
32
34
|
"uuid": "^9.0.1",
|
|
33
35
|
"ws": "^8.18.1"
|
|
34
36
|
},
|