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.
@@ -6,7 +6,10 @@
6
6
  const crypto = require('crypto');
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
- const StealthMode = require('./StealthMode');
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
- // 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
- });
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
- // First, check Stealth Mode limits (rate limiting & pauses)
825
- if (this.stealthMode) {
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.2",
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
- // Consider connections lasting longer than 5 minutes as "stable"
409
- if (duration > 5 * 60 * 1000) {
410
- log.info('listenMqtt', `MQTT connection closed after ${Math.floor(duration/1000)}s (normal lifecycle). Reconnecting...`);
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
- log.warn('listenMqtt', `MQTT bridge socket closed abruptly after ${duration}ms (attempt=${ctx._mqttDiag.attempts}).`);
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
- if (duration > 5 * 60 * 1000) {
426
- log.info('listenMqtt', `MQTT disconnected after ${Math.floor(duration/1000)}s (normal lifecycle). Reconnecting...`);
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
- log.warn('listenMqtt', `MQTT bridge disconnect event after ${duration}ms (attempt=${ctx._mqttDiag.attempts}).`);
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 (26-60 mins)
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
- const reconnectTime = getRandomReconnectTime();
444
- if (verboseMqtt) log.info('listenMqtt', `Scheduled proactive reconnect in ${Math.floor(reconnectTime / 60000)} minutes.`);
445
- ctx._reconnectTimer = setTimeout(() => {
446
- log.info('listenMqtt', `Executing proactive reconnect...`);
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
- }, reconnectTime);
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}).`);
@@ -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
- utils
31
- .get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, {
32
- noRef: true,
33
- })
34
- .then((resData) => {
35
- const fb_dtsg = utils.getFrom(
36
- resData.body,
37
- '["DTSGInitData",[],{"token":"',
38
- '","'
39
- );
40
- const jazoest = utils.getFrom(resData.body, "jazoest=", '",');
41
- if (!fb_dtsg) {
42
- throw new utils.CustomError(
43
- "Could not find fb_dtsg in HTML after requesting Facebook."
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
- ctx.fb_dtsg = fb_dtsg;
47
- ctx.jazoest = jazoest;
48
- callback(null, {
49
- data: { fb_dtsg, jazoest },
50
- message: "Refreshed fb_dtsg and jazoest",
51
- });
52
- })
53
- .catch((err) => {
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
+ }