nexus-fca 3.2.2 → 3.2.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/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.2 🚀
5
+ # Nexus-FCA v3.2.3 🚀
6
6
 
7
- > **High-Performance, Stable, Safe Facebook Messenger API**
8
- > *Now with Stable Reconnects & Quoted Replies*
7
+ > **Advanced, Secure & Stable Facebook Messenger API**
8
+ > *Engineered for Long-Term Stability & Zero Detection*
9
9
 
10
- ## 🔥 New in v3.2.2
11
- - **💬 Fixed Reply Quoting**: Messages now correctly quote the original message using updated `reply_metadata`.
12
- - **🏎️ Async Event Engine**: Non-blocking message processing prevents event loop starvation even under 1000+ msgs/min load.
13
- - **🛡️ Smart Keepalive**: Adaptive 60s heartbeats with 45s pings ensure connection stays alive during CPU spikes.
14
- - **🔄 Stable Reconnects**: Automatically detects and resets 'stuck' connections (Stable Mode), ensuring 24/7 reliability.
15
- - **💾 Memory Optimized**: 50% lighter core memory footprint compared to legacy FCA versions.
16
- - **✨ Stability**: 99.99% uptime guaranteed in high-traffic groups.
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
@@ -187,6 +187,7 @@ const login = require('nexus-fca');
187
187
  ## 📚 Documentation Map
188
188
  | Resource | Location |
189
189
  |----------|----------|
190
+ | **Usage Guide (Examples)** | `USAGE-GUIDE.md` |
190
191
  | Full API Reference | `DOCS.md` |
191
192
  | Feature Guides | `docs/*.md` |
192
193
  | Configuration Reference | `docs/configuration-reference.md` |
package/index.js CHANGED
@@ -25,7 +25,7 @@ function printFancyStartupBanner() {
25
25
  ██║╚██╗██║██╔══╝ ██╔██╗ ██║ ██║╚════██║
26
26
  ██║ ╚████║███████╗██╔╝ ██╗╚██████╔╝███████║
27
27
  ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
28
- [ F A C E B O O K C H A T A P I ]
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/125.0.0.0 Safari/537.36";
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": {
@@ -183,6 +183,9 @@ class ApiFactory {
183
183
  globalOptions[key] = options[key];
184
184
  });
185
185
  },
186
+ autoTyping: (enable = true) => {
187
+ globalOptions.autoTyping = !!enable;
188
+ },
186
189
  getAppState: function () {
187
190
  const appState = utils.getAppState(ctx.jar);
188
191
  return appState.filter((item, index, self) =>
@@ -217,6 +220,11 @@ class ApiFactory {
217
220
  }
218
221
  };
219
222
 
223
+ // Default options
224
+ if (typeof globalOptions.autoTyping === 'undefined') {
225
+ globalOptions.autoTyping = true; // Enabled by default for safety
226
+ }
227
+
220
228
  // Default edit settings
221
229
  if (!globalOptions.editSettings) {
222
230
  globalOptions.editSettings = {
@@ -293,9 +301,18 @@ class ApiFactory {
293
301
  globalOptions.groupQueueIdleMs = 30 * 60 * 1000;
294
302
 
295
303
  api._sendMessageDirect = DIRECT_FN;
296
- api.sendMessage = function (message, threadID, cb) {
304
+ api.sendMessage = function (message, threadID, cb, replyToMessage) {
305
+ // New: Auto-Typing support for improved human-like behavior
306
+ if (globalOptions.autoTyping) {
307
+ try {
308
+ api.sendTypingIndicator(threadID, (err) => {
309
+ // Ignore typing errors to avoid blocking the message
310
+ });
311
+ } catch (_) { /* ignore */ }
312
+ }
313
+
297
314
  if (!globalOptions.groupQueueEnabled || !isGroupThread(threadID)) {
298
- return api._sendMessageDirect(message, threadID, cb);
315
+ return api._sendMessageDirect(message, threadID, cb, replyToMessage);
299
316
  }
300
317
  let entry = groupQueues.get(threadID);
301
318
  if (!entry) { entry = { q: [], sending: false, lastActive: Date.now() }; groupQueues.set(threadID, entry); }
@@ -304,7 +321,7 @@ class ApiFactory {
304
321
  entry.q.shift();
305
322
  if (ctx.health) ctx.health.recordGroupQueuePrune(0, 0, 1);
306
323
  }
307
- entry.q.push({ message, threadID, cb });
324
+ entry.q.push({ message, threadID, cb, replyToMessage });
308
325
  processQueue(threadID, entry);
309
326
  };
310
327
 
@@ -312,13 +329,13 @@ class ApiFactory {
312
329
  if (entry.sending) return;
313
330
  if (!entry.q.length) return;
314
331
  entry.sending = true;
315
- const { message, threadID: tid, cb } = entry.q.shift();
316
- api._sendMessageDirect(message, tid, function (err, res) {
332
+ const item = entry.q.shift();
333
+ api._sendMessageDirect(item.message, item.threadID, function (err, res) {
317
334
  try { if (!err && this.globalSafety) this.globalSafety.recordEvent(); } catch (_) { }
318
- if (typeof cb === 'function') cb(err, res);
335
+ if (typeof item.cb === 'function') item.cb(err, res);
319
336
  entry.sending = false;
320
337
  setImmediate(() => processQueue(threadID, entry));
321
- }.bind(this));
338
+ }.bind(this), item.replyToMessage);
322
339
  }
323
340
 
324
341
  api._flushGroupQueue = function (threadID) {
@@ -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 (most common)
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;
@@ -56,13 +49,13 @@ class FacebookSafety {
56
49
  this.regions = ['ASH', 'ATL', 'DFW', 'ORD', 'PHX', 'SJC', 'IAD'];
57
50
  this.currentRegion = this.regions[Math.floor(Math.random() * this.regions.length)];
58
51
 
59
- // ULTRA-SAFE human delay patterns - Relaxed for usability
52
+ // ULTRA-SAFE human delay patterns - More conservative
60
53
  this.humanDelayPatterns = {
61
- typing: { min: 500, max: 1500 }, // Normal typing (0.5-1.5s)
62
- reading: { min: 1000, max: 3000 }, // Normal reading (1-3s)
63
- thinking: { min: 1000, max: 4000 }, // Normal thinking (1-4s)
64
- browsing: { min: 500, max: 2000 }, // Normal browsing (0.5-2s)
65
- messageDelay: { min: 1000, max: 3000 } // 1-3s between messages (much faster)
54
+ typing: { min: 800, max: 2500 }, // Normal typing (0.8-2.5s)
55
+ reading: { min: 1500, max: 5000 }, // Normal reading (1.5-5s)
56
+ thinking: { min: 1500, max: 6000 }, // Normal thinking (1.5-6s)
57
+ browsing: { min: 1000, max: 3000 }, // Normal browsing (1-3s)
58
+ messageDelay: { min: 1500, max: 4000 } // 1.5-4s between messages (Conservative)
66
59
  };
67
60
 
68
61
  this.sessionMetrics = {
@@ -807,16 +800,17 @@ class FacebookSafety {
807
800
  const risk = this.sessionMetrics.riskLevel;
808
801
  const since = Date.now() - this._lastHeavyMaintenanceTs;
809
802
  const inWindow = since < this._adaptivePacingWindowMs;
810
- let min = 0, max = 0;
811
- if (inWindow) {
812
- if (risk === 'high') { min = 600; max = 1500; }
813
- else if (risk === 'medium') { min = 200; max = 800; }
814
- else { min = 0; max = 300; }
815
- } else {
816
- // outside pacing window only high risk adds mild delay
817
- if (risk === 'high') { min = 150; max = 600; }
803
+
804
+ let min = 1000, max = 2500; // Default baseline human pace
805
+
806
+ if (risk === 'high') {
807
+ min = 3500; max = 6500;
808
+ } else if (risk === 'medium') {
809
+ min = 2000; max = 4500;
810
+ } else if (inWindow) {
811
+ min = 1500; max = 3000;
818
812
  }
819
- if (max <= 0) return 0;
813
+
820
814
  return Math.floor(min + Math.random() * (max - min));
821
815
  }
822
816
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nexus-fca",
3
- "version": "3.2.2",
4
- "description": "Nexus-FCA 3.2.1High-Performance, Stable, Safe Facebook Messenger API.",
3
+ "version": "3.2.4",
4
+ "description": "Nexus-FCA 3.2.4Advanced, 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) || 45000;
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/134.0.0.0 Safari/537.36",
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: 60, // Increased from 30s to 60s (standard) to tolerate event loop lag
568
+ keepalive: 10, // Reduced to 10s (matches ws3-fca) to prevent NAT timeouts and silent drops
569
569
  reschedulePings: true,
570
- reconnectPeriod: 5000, // Increased: 1s -> 5s to prevent rapid loops
571
- connectTimeout: 30000, // Increased: 5s -> 30s to allow slow connections under load
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
- if (typeof options === 'function') {
75
+ jar.getCookies = function (uri, options, callback) {
76
+ if (typeof options === 'function') {
77
77
  callback = options;
78
78
  options = {};
79
79
  }
@@ -87,23 +87,44 @@ function getJar() {
87
87
  }
88
88
 
89
89
  function getHeaders(url, options, ctx, customHeader) {
90
+ const ua = (options?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36");
91
+ const isWindows = ua.includes("Windows NT");
92
+ const isAndroid = ua.includes("Android");
93
+ const isChrome = ua.includes("Chrome") && !ua.includes("Edg");
94
+
90
95
  var headers = {
91
96
  Referer: "https://www.facebook.com/",
92
97
  Host: url.replace("https://", "").split("/")[0],
93
98
  Origin: "https://www.facebook.com",
94
- "user-agent": (options?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"),
99
+ "user-agent": ua,
95
100
  Connection: "keep-alive",
96
- "sec-fetch-site": 'same-origin',
97
- "sec-fetch-mode": 'cors',
98
- "sec-fetch-dest": "empty",
99
101
  "accept": "*/*",
100
102
  "accept-language": "en-US,en;q=0.9",
101
- "sec-ch-ua": '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
102
- "sec-ch-ua-mobile": "?0",
103
- "sec-ch-ua-platform": '"Windows"',
104
103
  "dnt": "1",
105
104
  "upgrade-insecure-requests": "1"
106
105
  };
106
+
107
+ // Human-like Fetch headers
108
+ if (url.includes("/api/graphql/") || url.includes("/messaging/")) {
109
+ headers["sec-fetch-site"] = 'same-origin';
110
+ headers["sec-fetch-mode"] = 'cors';
111
+ headers["sec-fetch-dest"] = "empty";
112
+ } else {
113
+ headers["sec-fetch-site"] = 'none';
114
+ headers["sec-fetch-mode"] = 'navigate';
115
+ headers["sec-fetch-dest"] = "document";
116
+ }
117
+
118
+ // Dynamic Client Hints - CRITICAL: Must match User-Agent
119
+ if (isChrome && isWindows) {
120
+ headers["sec-ch-ua"] = '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"';
121
+ headers["sec-ch-ua-mobile"] = "?0";
122
+ headers["sec-ch-ua-platform"] = '"Windows"';
123
+ } else if (isAndroid) {
124
+ headers["sec-ch-ua-mobile"] = "?1";
125
+ // Remove Windows-specific headers for Android UAs
126
+ }
127
+
107
128
  if (customHeader) Object.assign(headers, customHeader);
108
129
  if (ctx && ctx.region) headers["X-MSGR-Region"] = ctx.region;
109
130
 
@@ -182,10 +203,10 @@ async function post(url, jar, form, options, ctx, customHeader) {
182
203
  if (form) {
183
204
  // 1. Clean null/undefined
184
205
  form = cleanObject(form);
185
-
206
+
186
207
  // 2. Check Content-Type
187
208
  let contentType = headers['Content-Type'] || 'application/x-www-form-urlencoded';
188
-
209
+
189
210
  if (contentType.includes('json')) {
190
211
  op.body = JSON.stringify(form);
191
212
  } else {
@@ -367,23 +388,41 @@ function generatePresence(userID) {
367
388
  }
368
389
 
369
390
  function getGUID() {
370
- /** @type {number} */
391
+ // 1. Check Environment Variable (Best for Render/Heroku/Replit)
392
+ if (process.env.NEXUS_DEVICE_ID) {
393
+ return process.env.NEXUS_DEVICE_ID;
394
+ }
371
395
 
372
- var sectionLength = Date.now();
373
- /** @type {string} */
396
+ var devicePath = require("path").join(process.cwd(), "nexus_device.json");
397
+ var fs = require("fs");
374
398
 
375
- var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
376
- /** @type {number} */
399
+ // 2. Check File System (Local Persistence)
400
+ try {
401
+ if (fs.existsSync(devicePath)) {
402
+ var data = JSON.parse(fs.readFileSync(devicePath, "utf8"));
403
+ if (data && data.guid) return data.guid;
404
+ }
405
+ } catch (e) { }
377
406
 
407
+ // 3. Generate New Device ID
408
+ var sectionLength = Date.now();
409
+ var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
378
410
  var r = Math.floor((sectionLength + Math.random() * 16) % 16);
379
- /** @type {number} */
380
-
381
411
  sectionLength = Math.floor(sectionLength / 16);
382
- /** @type {string} */
383
-
384
412
  var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
385
413
  return _guid;
386
414
  });
415
+
416
+ // 4. Save & Log
417
+ try {
418
+ fs.writeFileSync(devicePath, JSON.stringify({ guid: id, generatedAt: Date.now() }, null, 2));
419
+ } catch (e) { }
420
+
421
+ // Warn user to verify persistence
422
+ var log = require("npmlog");
423
+ log.warn("NEXUS-FCA", "New Device ID generated: " + id);
424
+ log.warn("NEXUS-FCA", ">>> IF ON RENDER/HEROKU: Add 'NEXUS_DEVICE_ID' = '" + id + "' to your Environment Variables to prevent bans! <<<");
425
+
387
426
  return id;
388
427
  }
389
428
 
@@ -1038,7 +1077,7 @@ function generateTimestampRelative() {
1038
1077
  function makeDefaults(html, userID, ctx) {
1039
1078
  var reqCounter = 1;
1040
1079
  let fb_dtsg = null;
1041
-
1080
+
1042
1081
  // Robust fb_dtsg extraction
1043
1082
  const dtsgRegexes = [
1044
1083
  /"DTSGInitData",\[\],{"token":"(.*?)"/,
@@ -1048,7 +1087,7 @@ function makeDefaults(html, userID, ctx) {
1048
1087
  /name="fb_dtsg" value="(.*?)"/,
1049
1088
  /name="dtsg_ag" value="(.*?)"/
1050
1089
  ];
1051
-
1090
+
1052
1091
  for (const regex of dtsgRegexes) {
1053
1092
  const match = html.match(regex);
1054
1093
  if (match && match[1]) {
@@ -1056,7 +1095,7 @@ function makeDefaults(html, userID, ctx) {
1056
1095
  break;
1057
1096
  }
1058
1097
  }
1059
-
1098
+
1060
1099
  // Fallback to ctx if not found in HTML (or if HTML is partial)
1061
1100
  if (!fb_dtsg && ctx.fb_dtsg) {
1062
1101
  fb_dtsg = ctx.fb_dtsg;
@@ -1066,7 +1105,7 @@ function makeDefaults(html, userID, ctx) {
1066
1105
  if (fb_dtsg) {
1067
1106
  for (var i = 0; i < fb_dtsg.length; i++) ttstamp += fb_dtsg.charCodeAt(i);
1068
1107
  }
1069
-
1108
+
1070
1109
  var revision = getFrom(html, 'revision":', ",");
1071
1110
  function mergeWithDefaults(obj) {
1072
1111
  var newObj = {
@@ -1110,7 +1149,7 @@ function parseAndCheckLogin(ctx, defaultFuncs, retryCount = 0, sourceCall) {
1110
1149
  return function (data) {
1111
1150
  return tryPromise(function () {
1112
1151
  log.verbose("parseAndCheckLogin", data.body);
1113
-
1152
+
1114
1153
  // GOT compatibility: map request properties
1115
1154
  const request = data.request;
1116
1155
  const requestUrl = request.options ? request.options.url : request.uri;
@@ -1135,10 +1174,10 @@ function parseAndCheckLogin(ctx, defaultFuncs, retryCount = 0, sourceCall) {
1135
1174
  "parseAndCheckLogin",
1136
1175
  `Got status code ${data.statusCode} - Retrying in ${retryTime}ms...`
1137
1176
  );
1138
-
1177
+
1139
1178
  const url = requestUrl.toString();
1140
1179
  const contentType = requestHeaders?.["content-type"]?.split(";")[0];
1141
-
1180
+
1142
1181
  return delay(retryTime)
1143
1182
  .then(() =>
1144
1183
  contentType === "multipart/form-data"
@@ -1279,14 +1318,14 @@ function saveCookies(jar) {
1279
1318
  if (c.indexOf(".facebook.com") > -1 || c.indexOf("facebook.com") > -1) {
1280
1319
  try {
1281
1320
  jar.setCookie(c, "https://www.facebook.com");
1282
- } catch (e) {}
1283
-
1321
+ } catch (e) { }
1322
+
1284
1323
  try {
1285
1324
  var c_messenger = c.replace(/domain=\.?facebook\.com/i, "domain=.messenger.com");
1286
1325
  if (c_messenger.indexOf(".messenger.com") > -1) {
1287
1326
  jar.setCookie(c_messenger, "https://www.messenger.com");
1288
1327
  }
1289
- } catch (e) {}
1328
+ } catch (e) { }
1290
1329
  }
1291
1330
  });
1292
1331
  return res;
@@ -1522,115 +1561,115 @@ function checkLiveCookie(ctx, defaultFuncs) {
1522
1561
  * Intelligent request management to minimize Facebook account risks
1523
1562
  */
1524
1563
  const smartSafetyLimiter = {
1525
- userSessions: {},
1526
-
1527
- // Human-like delay patterns to avoid detection
1528
- humanDelays: {
1529
- typing: { min: 800, max: 2000 },
1530
- reading: { min: 1000, max: 3000 },
1531
- thinking: { min: 2000, max: 5000 },
1532
- browsing: { min: 500, max: 1500 }
1533
- },
1534
-
1535
- // Risk level assessment
1536
- assessRisk(userID, action) {
1537
- if (!this.userSessions[userID]) {
1538
- this.userSessions[userID] = {
1539
- requestCount: 0,
1540
- errorCount: 0,
1541
- lastActivity: Date.now(),
1542
- riskLevel: 'low'
1543
- };
1544
- }
1545
-
1546
- const session = this.userSessions[userID];
1547
- const timeSinceLastActivity = Date.now() - session.lastActivity;
1548
- const errorRate = session.errorCount / Math.max(1, session.requestCount);
1549
-
1550
- // Update risk level based on activity patterns - ULTRA SAFE TUNING
1551
- if (errorRate > 0.2 || timeSinceLastActivity < 800) { // Stricter error threshold (0.2 vs 0.3)
1552
- session.riskLevel = 'high';
1553
- } else if (errorRate > 0.05 || timeSinceLastActivity < 2000) { // Stricter medium threshold
1554
- session.riskLevel = 'medium';
1555
- } else {
1556
- session.riskLevel = 'low';
1557
- }
1558
-
1559
- return session.riskLevel;
1560
- },
1561
-
1562
- // Get safe delay based on risk level and action type
1563
- getSafeDelay(userID, action = 'browsing') {
1564
- const riskLevel = this.assessRisk(userID, action);
1565
- const baseDelay = this.humanDelays[action] || this.humanDelays.browsing;
1566
-
1567
- // Risk multipliers for safety
1568
- const riskMultipliers = {
1569
- 'low': 1,
1570
- 'medium': 1.5,
1571
- 'high': 2.5
1572
- };
1573
-
1574
- const multiplier = riskMultipliers[riskLevel] || 1;
1575
- const min = baseDelay.min * multiplier;
1576
- const max = baseDelay.max * multiplier;
1577
-
1578
- // Generate human-like random delay
1579
- const baseDelayTime = Math.random() * (max - min) + min;
1580
- const humanVariation = baseDelayTime * 0.1 * (Math.random() - 0.5);
1581
-
1582
- return Math.max(200, Math.floor(baseDelayTime + humanVariation));
1583
- },
1584
-
1585
- // Record activity for safety metrics
1586
- recordActivity(userID, isError = false) {
1587
- if (!this.userSessions[userID]) {
1588
- this.userSessions[userID] = {
1589
- requestCount: 0,
1590
- errorCount: 0,
1591
- lastActivity: Date.now(),
1592
- riskLevel: 'low'
1593
- };
1594
- }
1595
-
1596
- const session = this.userSessions[userID];
1597
- session.requestCount++;
1598
- session.lastActivity = Date.now();
1599
-
1600
- if (isError) {
1601
- session.errorCount++;
1602
- }
1603
-
1604
- // Auto-reset metrics every hour to prevent false risk escalation
1605
- if (session.requestCount > 100) {
1606
- session.requestCount = Math.floor(session.requestCount / 2);
1607
- session.errorCount = Math.floor(session.errorCount / 2);
1608
- }
1609
- },
1610
-
1611
- // Check if action is safe to proceed
1612
- isSafeToExecute(userID, action) {
1613
- const riskLevel = this.assessRisk(userID, action);
1614
-
1615
- // Always allow low risk actions
1616
- if (riskLevel === 'low') return true;
1617
-
1618
- // For higher risk, check recent activity
1619
- const session = this.userSessions[userID];
1620
- const timeSinceLastActivity = Date.now() - session.lastActivity;
1621
-
1622
- // Medium risk: ensure some delay between actions
1623
- if (riskLevel === 'medium' && timeSinceLastActivity < 3000) {
1624
- return false;
1625
- }
1626
-
1627
- // High risk: require longer delays
1628
- if (riskLevel === 'high' && timeSinceLastActivity < 10000) {
1629
- return false;
1564
+ userSessions: {},
1565
+
1566
+ // Human-like delay patterns to avoid detection
1567
+ humanDelays: {
1568
+ typing: { min: 800, max: 2000 },
1569
+ reading: { min: 1000, max: 3000 },
1570
+ thinking: { min: 2000, max: 5000 },
1571
+ browsing: { min: 500, max: 1500 }
1572
+ },
1573
+
1574
+ // Risk level assessment
1575
+ assessRisk(userID, action) {
1576
+ if (!this.userSessions[userID]) {
1577
+ this.userSessions[userID] = {
1578
+ requestCount: 0,
1579
+ errorCount: 0,
1580
+ lastActivity: Date.now(),
1581
+ riskLevel: 'low'
1582
+ };
1583
+ }
1584
+
1585
+ const session = this.userSessions[userID];
1586
+ const timeSinceLastActivity = Date.now() - session.lastActivity;
1587
+ const errorRate = session.errorCount / Math.max(1, session.requestCount);
1588
+
1589
+ // Update risk level based on activity patterns - ULTRA SAFE TUNING
1590
+ if (errorRate > 0.2 || timeSinceLastActivity < 800) { // Stricter error threshold (0.2 vs 0.3)
1591
+ session.riskLevel = 'high';
1592
+ } else if (errorRate > 0.05 || timeSinceLastActivity < 2000) { // Stricter medium threshold
1593
+ session.riskLevel = 'medium';
1594
+ } else {
1595
+ session.riskLevel = 'low';
1596
+ }
1597
+
1598
+ return session.riskLevel;
1599
+ },
1600
+
1601
+ // Get safe delay based on risk level and action type
1602
+ getSafeDelay(userID, action = 'browsing') {
1603
+ const riskLevel = this.assessRisk(userID, action);
1604
+ const baseDelay = this.humanDelays[action] || this.humanDelays.browsing;
1605
+
1606
+ // Risk multipliers for safety
1607
+ const riskMultipliers = {
1608
+ 'low': 1,
1609
+ 'medium': 1.5,
1610
+ 'high': 2.5
1611
+ };
1612
+
1613
+ const multiplier = riskMultipliers[riskLevel] || 1;
1614
+ const min = baseDelay.min * multiplier;
1615
+ const max = baseDelay.max * multiplier;
1616
+
1617
+ // Generate human-like random delay
1618
+ const baseDelayTime = Math.random() * (max - min) + min;
1619
+ const humanVariation = baseDelayTime * 0.1 * (Math.random() - 0.5);
1620
+
1621
+ return Math.max(200, Math.floor(baseDelayTime + humanVariation));
1622
+ },
1623
+
1624
+ // Record activity for safety metrics
1625
+ recordActivity(userID, isError = false) {
1626
+ if (!this.userSessions[userID]) {
1627
+ this.userSessions[userID] = {
1628
+ requestCount: 0,
1629
+ errorCount: 0,
1630
+ lastActivity: Date.now(),
1631
+ riskLevel: 'low'
1632
+ };
1633
+ }
1634
+
1635
+ const session = this.userSessions[userID];
1636
+ session.requestCount++;
1637
+ session.lastActivity = Date.now();
1638
+
1639
+ if (isError) {
1640
+ session.errorCount++;
1641
+ }
1642
+
1643
+ // Auto-reset metrics every hour to prevent false risk escalation
1644
+ if (session.requestCount > 100) {
1645
+ session.requestCount = Math.floor(session.requestCount / 2);
1646
+ session.errorCount = Math.floor(session.errorCount / 2);
1647
+ }
1648
+ },
1649
+
1650
+ // Check if action is safe to proceed
1651
+ isSafeToExecute(userID, action) {
1652
+ const riskLevel = this.assessRisk(userID, action);
1653
+
1654
+ // Always allow low risk actions
1655
+ if (riskLevel === 'low') return true;
1656
+
1657
+ // For higher risk, check recent activity
1658
+ const session = this.userSessions[userID];
1659
+ const timeSinceLastActivity = Date.now() - session.lastActivity;
1660
+
1661
+ // Medium risk: ensure some delay between actions
1662
+ if (riskLevel === 'medium' && timeSinceLastActivity < 3000) {
1663
+ return false;
1664
+ }
1665
+
1666
+ // High risk: require longer delays
1667
+ if (riskLevel === 'high' && timeSinceLastActivity < 10000) {
1668
+ return false;
1669
+ }
1670
+
1671
+ return true;
1630
1672
  }
1631
-
1632
- return true;
1633
- }
1634
1673
  };
1635
1674
 
1636
1675
  /**
@@ -1647,20 +1686,20 @@ const allowList = process.env.NEXUS_FCA_ALLOW_LIST ? process.env.NEXUS_FCA_ALLOW
1647
1686
  const blockList = process.env.NEXUS_FCA_BLOCK_LIST ? process.env.NEXUS_FCA_BLOCK_LIST.split(',') : null;
1648
1687
 
1649
1688
  function isUserAllowed(userID) {
1650
- if (blockList && blockList.includes(userID)) return false;
1651
- if (allowList && !allowList.includes(userID)) return false;
1652
- return true;
1689
+ if (blockList && blockList.includes(userID)) return false;
1690
+ if (allowList && !allowList.includes(userID)) return false;
1691
+ return true;
1653
1692
  }
1654
1693
 
1655
1694
  // Legacy rate limiter - kept for backward compatibility but optimized for safety
1656
1695
  const rateLimiter = {
1657
- limits: {},
1658
- windowMs: 60 * 1000,
1659
- max: 20,
1660
- check(userID, action) {
1661
- // Use smart safety limiter instead of blocking
1662
- return smartSafetyLimiter.isSafeToExecute(userID, action);
1663
- }
1696
+ limits: {},
1697
+ windowMs: 60 * 1000,
1698
+ max: 20,
1699
+ check(userID, action) {
1700
+ // Use smart safety limiter instead of blocking
1701
+ return smartSafetyLimiter.isSafeToExecute(userID, action);
1702
+ }
1664
1703
  };
1665
1704
 
1666
1705
  // Add robust session validation utility used by listenMqtt