nexus-fca 2.1.2 → 2.1.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/CHANGELOG.md +4 -0
- package/index.js +101 -4
- package/lib/safety/FacebookSafety.js +71 -57
- package/package.json +1 -1
- package/src/listenMqtt.js +17 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
### Added
|
|
5
5
|
- Soft-stale probing at 2 minutes idle (ping + conditional forced reconnect if no events within 5-8s)
|
|
6
6
|
- Wrapper around `listenMqtt` to automatically feed events into safety heartbeat (`recordEvent`) for precise idle detection
|
|
7
|
+
- Ghost connection detection (10m silent but socket connected triggers forced reconnect after probe)
|
|
8
|
+
- Periodic connection recycle every ~6h ±30m to prevent long-lived silent degradation
|
|
9
|
+
- Force reconnect API: `globalSafety.forceReconnect(tag)`
|
|
7
10
|
|
|
8
11
|
### Improved
|
|
9
12
|
- Faster recovery from silent idle states (previously required >5 min or external trigger)
|
|
10
13
|
- Reduced chance of appearing online but unresponsive after short inactivity
|
|
14
|
+
- Added keepalive foreground_state publishes each heartbeat
|
|
11
15
|
|
|
12
16
|
---
|
|
13
17
|
|
package/index.js
CHANGED
|
@@ -10,6 +10,33 @@ const path = require('path');
|
|
|
10
10
|
const models = require("./lib/database/models");
|
|
11
11
|
const logger = require("./lib/logger");
|
|
12
12
|
const { safeMode, ultraSafeMode, smartSafetyLimiter, isUserAllowed } = require('./utils'); // Enhanced safety system
|
|
13
|
+
// Minimal aesthetic banner system
|
|
14
|
+
let _fancyBannerPrinted = false;
|
|
15
|
+
const gradient = (() => { try { return require('gradient-string'); } catch(_) { return null; } })();
|
|
16
|
+
const pkgMeta = (() => { try { return require('./package.json'); } catch(_) { return { version: 'dev' }; } })();
|
|
17
|
+
function printFancyStartupBanner() {
|
|
18
|
+
if (_fancyBannerPrinted) return; _fancyBannerPrinted = true;
|
|
19
|
+
const lines = [
|
|
20
|
+
'╔══════════════════════════════════════════════════════╗',
|
|
21
|
+
'║ Nexus-FCA Secure Login ║',
|
|
22
|
+
'║ Advanced Stable Messenger Automation API ║',
|
|
23
|
+
'╚══════════════════════════════════════════════════════╝'
|
|
24
|
+
];
|
|
25
|
+
if (gradient) console.log(gradient.cristal.multiline(lines.join('\n'))); else console.log(lines.join('\n'));
|
|
26
|
+
}
|
|
27
|
+
function printIdentityBanner(uid, name) {
|
|
28
|
+
const cleanName = name || 'Unknown';
|
|
29
|
+
const pad = (txt, len) => (txt.length < len ? txt + ' '.repeat(len - txt.length) : txt.substring(0, len));
|
|
30
|
+
const bodyLen = 54;
|
|
31
|
+
const line = (content) => `║ ${pad(content, bodyLen)} ║`;
|
|
32
|
+
const box = [
|
|
33
|
+
'╔════════════════ LOGGED IN IDENTITY ════════════════╗',
|
|
34
|
+
line(`UID : ${uid}`),
|
|
35
|
+
line(`Name: ${cleanName}`),
|
|
36
|
+
'╚════════════════════════════════════════════════════╝'
|
|
37
|
+
];
|
|
38
|
+
if (gradient) console.log(gradient.atlas.multiline(box.join('\n'))); else console.log(box.join('\n'));
|
|
39
|
+
}
|
|
13
40
|
|
|
14
41
|
// Enhanced imports - All new modules
|
|
15
42
|
const { NexusClient } = require('./lib/compatibility/NexusClient');
|
|
@@ -208,7 +235,9 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
208
235
|
firstListen: true,
|
|
209
236
|
fb_dtsg,
|
|
210
237
|
wsReqNumber: 0,
|
|
211
|
-
wsTaskNumber: 0
|
|
238
|
+
wsTaskNumber: 0,
|
|
239
|
+
// Provide safety module reference to lower layers (listenMqtt)
|
|
240
|
+
globalSafety
|
|
212
241
|
};
|
|
213
242
|
const api = {
|
|
214
243
|
setOptions: setOptions.bind(null, globalOptions),
|
|
@@ -269,6 +298,59 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
269
298
|
console.error("An error occurred while refreshing fb_dtsg", err);
|
|
270
299
|
});
|
|
271
300
|
}, 1000 * 60 * 60 * 24);
|
|
301
|
+
// === Group Queue (No Cooldown, Sequential per group) ===
|
|
302
|
+
(function initGroupQueue(){
|
|
303
|
+
const groupQueues = new Map(); // threadID -> { q: [], sending: false }
|
|
304
|
+
const isGroupThread = (tid) => typeof tid === 'string' && tid.length >= 15; // simple heuristic
|
|
305
|
+
const DIRECT_FN = api.sendMessage; // original
|
|
306
|
+
|
|
307
|
+
api.enableGroupQueue = function(enable=true){
|
|
308
|
+
globalOptions.groupQueueEnabled = !!enable;
|
|
309
|
+
};
|
|
310
|
+
api.setGroupQueueCapacity = function(n){ globalOptions.groupQueueMax = n; };
|
|
311
|
+
api.enableGroupQueue(true);
|
|
312
|
+
api.setGroupQueueCapacity(100); // allow up to 100 pending per group
|
|
313
|
+
|
|
314
|
+
api._sendMessageDirect = DIRECT_FN;
|
|
315
|
+
api.sendMessage = function(message, threadID, cb){
|
|
316
|
+
if(!globalOptions.groupQueueEnabled || !isGroupThread(threadID)) {
|
|
317
|
+
return api._sendMessageDirect(message, threadID, cb);
|
|
318
|
+
}
|
|
319
|
+
let entry = groupQueues.get(threadID);
|
|
320
|
+
if(!entry){ entry = { q: [], sending: false }; groupQueues.set(threadID, entry); }
|
|
321
|
+
if(entry.q.length >= (globalOptions.groupQueueMax||100)) {
|
|
322
|
+
// drop oldest (keep newest) to avoid unbounded growth
|
|
323
|
+
entry.q.shift();
|
|
324
|
+
}
|
|
325
|
+
entry.q.push({ message, threadID, cb });
|
|
326
|
+
processQueue(threadID, entry);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
function processQueue(threadID, entry){
|
|
330
|
+
if(entry.sending) return;
|
|
331
|
+
if(!entry.q.length) return;
|
|
332
|
+
entry.sending = true;
|
|
333
|
+
const { message, threadID: tid, cb } = entry.q.shift();
|
|
334
|
+
api._sendMessageDirect(message, tid, function(err, res){
|
|
335
|
+
try { if(!err) globalSafety.recordEvent(); } catch(_) {}
|
|
336
|
+
if(typeof cb === 'function') cb(err, res);
|
|
337
|
+
entry.sending = false;
|
|
338
|
+
// Immediately process next (no cooldown) to keep strict sequence
|
|
339
|
+
setImmediate(()=>processQueue(threadID, entry));
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
api._flushGroupQueue = function(threadID){
|
|
344
|
+
const entry = groupQueues.get(threadID);
|
|
345
|
+
if(!entry) return;
|
|
346
|
+
while(entry.q.length) {
|
|
347
|
+
const item = entry.q.shift();
|
|
348
|
+
api._sendMessageDirect(item.message, item.threadID, item.cb);
|
|
349
|
+
}
|
|
350
|
+
entry.sending = false;
|
|
351
|
+
};
|
|
352
|
+
})();
|
|
353
|
+
// === End Group Queue ===
|
|
272
354
|
return {
|
|
273
355
|
ctx,
|
|
274
356
|
defaultFuncs,
|
|
@@ -364,12 +446,25 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
364
446
|
if (!safetyStatus.safe) {
|
|
365
447
|
logger(`⚠️ Login safety warning: ${safetyStatus.reason}`, 'warn');
|
|
366
448
|
}
|
|
367
|
-
|
|
368
449
|
logger('✅ Session authenticated successfully', 'info');
|
|
369
|
-
|
|
370
450
|
// Initialize safety monitoring
|
|
371
451
|
globalSafety.startMonitoring(ctx, api);
|
|
372
|
-
|
|
452
|
+
// Post-login identity banner
|
|
453
|
+
try {
|
|
454
|
+
const uid = api.getCurrentUserID && api.getCurrentUserID();
|
|
455
|
+
if (api.getUserInfo && uid) {
|
|
456
|
+
api.getUserInfo(uid, (err, info) => {
|
|
457
|
+
if (!err && info) {
|
|
458
|
+
const userObj = info[uid] || info; // depending on structure
|
|
459
|
+
printIdentityBanner(uid, userObj.name || userObj.firstName || userObj.fullName);
|
|
460
|
+
} else {
|
|
461
|
+
printIdentityBanner(uid || 'N/A');
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
printIdentityBanner(uid || 'N/A');
|
|
466
|
+
}
|
|
467
|
+
} catch(_) { /* ignore */ }
|
|
373
468
|
callback(null, api);
|
|
374
469
|
})
|
|
375
470
|
.catch(e => {
|
|
@@ -893,6 +988,7 @@ class IntegratedNexusLoginSystem {
|
|
|
893
988
|
|
|
894
989
|
// Integrated Nexus Login wrapper for easy usage
|
|
895
990
|
async function integratedNexusLogin(credentials = null, options = {}) {
|
|
991
|
+
printFancyStartupBanner();
|
|
896
992
|
const loginSystem = new IntegratedNexusLoginSystem(options);
|
|
897
993
|
|
|
898
994
|
// Professional logging system
|
|
@@ -1008,6 +1104,7 @@ async function integratedNexusLogin(credentials = null, options = {}) {
|
|
|
1008
1104
|
* - Appstate only: Uses existing session directly
|
|
1009
1105
|
*/
|
|
1010
1106
|
async function login(loginData, options = {}, callback) {
|
|
1107
|
+
printFancyStartupBanner();
|
|
1011
1108
|
// Support multiple callback signatures
|
|
1012
1109
|
if (typeof options === 'function') {
|
|
1013
1110
|
callback = options;
|
|
@@ -68,6 +68,10 @@ class FacebookSafety {
|
|
|
68
68
|
this._inFlightRefreshId = 0;
|
|
69
69
|
// New: probing guard to avoid overlapping soft-stale probes
|
|
70
70
|
this._probing = false;
|
|
71
|
+
// Ghost detection guard
|
|
72
|
+
this._ghostChecking = false;
|
|
73
|
+
// Periodic recycle timer
|
|
74
|
+
this._periodicRecycleTimer = null;
|
|
71
75
|
|
|
72
76
|
this.initSafety();
|
|
73
77
|
}
|
|
@@ -80,6 +84,7 @@ class FacebookSafety {
|
|
|
80
84
|
|
|
81
85
|
// Setup session monitoring
|
|
82
86
|
this.setupSessionMonitoring();
|
|
87
|
+
this._schedulePeriodicRecycle();
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
/**
|
|
@@ -245,18 +250,19 @@ class FacebookSafety {
|
|
|
245
250
|
clearTimeout(this._safeRefreshTimer);
|
|
246
251
|
this._safeRefreshTimer = null;
|
|
247
252
|
}
|
|
248
|
-
//
|
|
253
|
+
// Stealth+Resilient profile refresh policy:
|
|
254
|
+
// risk low: 50-60m, medium: 40-50m, high: 25-35m (random inside band)
|
|
249
255
|
const schedule = () => {
|
|
250
256
|
if (this._destroyed) return;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const interval =
|
|
257
|
+
let minM, maxM;
|
|
258
|
+
if (this.sessionMetrics.riskLevel === 'high') { minM = 25; maxM = 35; }
|
|
259
|
+
else if (this.sessionMetrics.riskLevel === 'medium') { minM = 40; maxM = 50; }
|
|
260
|
+
else { minM = 50; maxM = 60; }
|
|
261
|
+
const interval = (minM * 60 * 1000) + Math.random() * ((maxM - minM) * 60 * 1000);
|
|
256
262
|
this._safeRefreshTimer = setTimeout(async () => {
|
|
257
263
|
await this.refreshSafeSession();
|
|
258
264
|
schedule();
|
|
259
|
-
},
|
|
265
|
+
}, interval);
|
|
260
266
|
};
|
|
261
267
|
schedule();
|
|
262
268
|
}
|
|
@@ -311,40 +317,27 @@ class FacebookSafety {
|
|
|
311
317
|
const now = Date.now();
|
|
312
318
|
const disconnected = !this.ctx || !this.ctx.mqttClient || !this.ctx.mqttClient.connected;
|
|
313
319
|
const idle = now - this._lastEventTs;
|
|
314
|
-
const softStale = idle > 2 * 60 * 1000; //
|
|
315
|
-
const hardStale = idle >
|
|
316
|
-
const stale = hardStale;
|
|
317
|
-
|
|
318
|
-
// If totally disconnected or hard stale -> reconnect immediately
|
|
320
|
+
const softStale = idle > (2.5 * 60 * 1000); // Stealth profile: 2m30s
|
|
321
|
+
const hardStale = idle > 8 * 60 * 1000; // escalate earlier than watchdog hard (8m)
|
|
322
|
+
const stale = hardStale;
|
|
319
323
|
if (disconnected || stale) {
|
|
320
324
|
await this._reconnectMqttWithBackoff(disconnected ? 'disconnected' : 'hard-stale');
|
|
321
325
|
return;
|
|
322
326
|
}
|
|
323
|
-
|
|
324
|
-
// Soft-stale probing: connection claims to be open but no events for 2-5 minutes.
|
|
325
|
-
// We issue a ping and if still no events after probe window, we force a reconnect.
|
|
326
327
|
if (softStale && !this._probing) {
|
|
327
328
|
this._probing = true;
|
|
328
329
|
const prevTs = this._lastEventTs;
|
|
329
|
-
try {
|
|
330
|
-
if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
|
|
331
|
-
if (typeof this.ctx.mqttClient.ping === 'function') {
|
|
332
|
-
try { this.ctx.mqttClient.ping(); } catch(_) {}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
} catch(_) {}
|
|
330
|
+
try { if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected && typeof this.ctx.mqttClient.ping === 'function') this.ctx.mqttClient.ping(); } catch(_) {}
|
|
336
331
|
setTimeout(() => {
|
|
337
332
|
if (this._destroyed) return;
|
|
338
|
-
// If no new events arrived since probe start, treat as latent-dead connection
|
|
339
333
|
if (this._lastEventTs <= prevTs) {
|
|
340
|
-
// Reset backoff to allow immediate reconnect (latency sensitive)
|
|
341
334
|
this._backoff.attempt = 0;
|
|
342
335
|
this._reconnectMqttWithBackoff('soft-stale');
|
|
343
336
|
}
|
|
344
337
|
this._probing = false;
|
|
345
|
-
},
|
|
338
|
+
}, 6000 + Math.random() * 2000); // 6-8s probe window (Stealth+Resilient)
|
|
346
339
|
}
|
|
347
|
-
} catch
|
|
340
|
+
} catch(_) {}
|
|
348
341
|
}
|
|
349
342
|
|
|
350
343
|
// Progressive backoff + jitter reconnect
|
|
@@ -353,38 +346,47 @@ class FacebookSafety {
|
|
|
353
346
|
this._reconnecting = true;
|
|
354
347
|
try {
|
|
355
348
|
const now = Date.now();
|
|
356
|
-
if (now < this._backoff.next) {
|
|
357
|
-
return; // respect backoff window
|
|
358
|
-
}
|
|
349
|
+
if (now < this._backoff.next) { return; }
|
|
359
350
|
const attempt = ++this._backoff.attempt;
|
|
360
|
-
|
|
361
|
-
const
|
|
351
|
+
// Stealth backoff: 1.2s * 1.8^n capped ~20s, add jitter 0-500ms
|
|
352
|
+
const baseDelay = Math.min(20000, 1200 * Math.pow(1.8, Math.min(attempt, 6)));
|
|
353
|
+
const jitter = Math.random() * 500;
|
|
362
354
|
const delay = baseDelay + jitter;
|
|
363
355
|
this._backoff.next = now + delay;
|
|
364
356
|
await new Promise(r => setTimeout(r, delay));
|
|
365
|
-
|
|
366
|
-
if (this._activeListenerStop && typeof this._activeListenerStop === 'function') {
|
|
367
|
-
try { this._activeListenerStop(); } catch (_) {}
|
|
368
|
-
}
|
|
357
|
+
if (this._activeListenerStop && typeof this._activeListenerStop === 'function') { try { this._activeListenerStop(); } catch(_) {} }
|
|
369
358
|
if (this.api && typeof this.api.listenMqtt === 'function' && !this._destroyed) {
|
|
370
|
-
const stop = this.api.listenMqtt((err, event) => {
|
|
371
|
-
if (!err && event) this.recordEvent();
|
|
372
|
-
});
|
|
359
|
+
const stop = this.api.listenMqtt((err, event) => { if (!err && event) this.recordEvent(); });
|
|
373
360
|
this._activeListenerStop = stop;
|
|
374
|
-
|
|
375
|
-
else this.safetyEmit('mqttReconnect', { success: true, reason });
|
|
361
|
+
this.safetyEmit('mqttReconnect', { success: true, reason, attempt, delay });
|
|
376
362
|
}
|
|
377
|
-
// Reset backoff on success detection soon after
|
|
378
363
|
setTimeout(() => {
|
|
379
|
-
if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
|
|
380
|
-
this._backoff.attempt = 0;
|
|
381
|
-
}
|
|
364
|
+
if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) { this._backoff.attempt = 0; }
|
|
382
365
|
}, 5000);
|
|
383
|
-
} catch
|
|
384
|
-
this.safetyEmit('mqttReconnect', { success: false, error: e.message });
|
|
385
|
-
} finally {
|
|
386
|
-
|
|
387
|
-
|
|
366
|
+
} catch(e) {
|
|
367
|
+
this.safetyEmit('mqttReconnect', { success: false, error: e.message, reason });
|
|
368
|
+
} finally { this._reconnecting = false; }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Public force reconnect (bypass backoff)
|
|
372
|
+
forceReconnect(tag = 'manual') {
|
|
373
|
+
if (this._destroyed) return;
|
|
374
|
+
this._backoff.attempt = 0;
|
|
375
|
+
return this._reconnectMqttWithBackoff('force-' + tag);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Schedule periodic recycle (connection rejuvenation) every 6h ±30m jitter
|
|
379
|
+
_schedulePeriodicRecycle() {
|
|
380
|
+
if (this._periodicRecycleTimer) clearTimeout(this._periodicRecycleTimer);
|
|
381
|
+
if (this._destroyed) return;
|
|
382
|
+
const base = 6 * 60 * 60 * 1000; // 6h
|
|
383
|
+
const jitter = (Math.random() * 60 - 30) * 60 * 1000; // ±30m
|
|
384
|
+
const delay = base + jitter;
|
|
385
|
+
this._periodicRecycleTimer = setTimeout(() => {
|
|
386
|
+
if (this._destroyed) return;
|
|
387
|
+
this.forceReconnect('periodic');
|
|
388
|
+
this._schedulePeriodicRecycle();
|
|
389
|
+
}, delay);
|
|
388
390
|
}
|
|
389
391
|
|
|
390
392
|
// Heartbeat ping & watchdog
|
|
@@ -392,26 +394,38 @@ class FacebookSafety {
|
|
|
392
394
|
if (this._heartbeatTimer) clearInterval(this._heartbeatTimer);
|
|
393
395
|
if (this._watchdogTimer) clearInterval(this._watchdogTimer);
|
|
394
396
|
if (this._destroyed) return;
|
|
397
|
+
// Stealth profile heartbeat: 80–100s random
|
|
395
398
|
this._heartbeatTimer = setInterval(() => {
|
|
396
399
|
if (this._destroyed) return;
|
|
397
400
|
try {
|
|
398
401
|
if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
|
|
399
402
|
if (this.ctx.mqttClient.ping) this.ctx.mqttClient.ping();
|
|
403
|
+
try { this.ctx.mqttClient.publish('/foreground_state', JSON.stringify({ foreground: true })); } catch(_) {}
|
|
400
404
|
this.safetyEmit('heartbeat', { ts: Date.now() });
|
|
401
405
|
}
|
|
402
|
-
} catch
|
|
403
|
-
},
|
|
406
|
+
} catch(_) {}
|
|
407
|
+
}, (80 + Math.random()*20) * 1000);
|
|
404
408
|
this._watchdogTimer = setInterval(() => {
|
|
405
409
|
if (this._destroyed) return;
|
|
406
410
|
const idle = Date.now() - this._lastEventTs;
|
|
407
|
-
|
|
408
|
-
|
|
411
|
+
// Soft escalate already handled inside _ensureMqttAlive at 2m30s
|
|
412
|
+
// Ghost detection earlier: 9m
|
|
413
|
+
if (idle > 9 * 60 * 1000 && !this._ghostChecking && this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
|
|
414
|
+
this._ghostChecking = true;
|
|
415
|
+
const before = this._lastEventTs;
|
|
416
|
+
try { if (this.ctx.mqttClient.ping) this.ctx.mqttClient.ping(); } catch(_) {}
|
|
417
|
+
setTimeout(() => {
|
|
418
|
+
if (this._destroyed) return;
|
|
419
|
+
if (this._lastEventTs <= before) { this.forceReconnect('ghost'); }
|
|
420
|
+
setTimeout(() => { this._ghostChecking = false; }, 5 * 60 * 1000);
|
|
421
|
+
}, 6000 + Math.random()*2000);
|
|
409
422
|
}
|
|
410
|
-
|
|
411
|
-
|
|
423
|
+
// Hard watchdog escalate: 12m
|
|
424
|
+
if (idle > 12 * 60 * 1000) {
|
|
425
|
+
this._backoff.attempt = 0;
|
|
412
426
|
this._ensureMqttAlive();
|
|
413
427
|
}
|
|
414
|
-
},
|
|
428
|
+
}, 35 * 1000); // slight change to avoid pattern
|
|
415
429
|
}
|
|
416
430
|
|
|
417
431
|
/**
|
|
@@ -536,7 +550,7 @@ class FacebookSafety {
|
|
|
536
550
|
// Cleanup / destroy resources (to prevent dangling timers)
|
|
537
551
|
destroy() {
|
|
538
552
|
this._destroyed = true;
|
|
539
|
-
const timers = [this._safeRefreshInterval, this._safeRefreshTimer, this._heartbeatTimer, this._watchdogTimer];
|
|
553
|
+
const timers = [this._safeRefreshInterval, this._safeRefreshTimer, this._heartbeatTimer, this._watchdogTimer, this._periodicRecycleTimer];
|
|
540
554
|
timers.forEach(t => t && clearTimeout(t));
|
|
541
555
|
if (this._activeListenerStop) {
|
|
542
556
|
try { this._activeListenerStop(); } catch (_) {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-fca",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "A modern, safe, and advanced Facebook Chat API for Node.js with fully integrated Nexus Login System. NPM-ready with ID/password/2FA support, ultra-low ban rate protection, and zero external dependencies.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
package/src/listenMqtt.js
CHANGED
|
@@ -260,6 +260,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
260
260
|
}
|
|
261
261
|
});
|
|
262
262
|
mqttClient.on("connect", function () {
|
|
263
|
+
if (ctx.globalSafety) { try { ctx.globalSafety.recordEvent(); } catch(_) {} }
|
|
263
264
|
if (process.env.OnStatus === undefined) {
|
|
264
265
|
logger("Nexus-FCA premium features works only with Nexus-Bot framework(Kidding)", "info");
|
|
265
266
|
process.env.OnStatus = true;
|
|
@@ -311,6 +312,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
311
312
|
};
|
|
312
313
|
});
|
|
313
314
|
mqttClient.on("message", function (topic, message, _packet) {
|
|
315
|
+
if (ctx.globalSafety) { try { ctx.globalSafety.recordEvent(); } catch(_) {} }
|
|
314
316
|
try {
|
|
315
317
|
let jsonMessage = Buffer.isBuffer(message)
|
|
316
318
|
? Buffer.from(message).toString()
|
|
@@ -404,8 +406,21 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
404
406
|
return;
|
|
405
407
|
}
|
|
406
408
|
});
|
|
407
|
-
mqttClient.on("close", function () {});
|
|
408
|
-
mqttClient.on("disconnect", () => {});
|
|
409
|
+
mqttClient.on("close", function () { if (ctx.globalSafety) { try { ctx.globalSafety._ensureMqttAlive(); } catch(_) {} } });
|
|
410
|
+
mqttClient.on("disconnect", () => { if (ctx.globalSafety) { try { ctx.globalSafety._ensureMqttAlive(); } catch(_) {} } });
|
|
411
|
+
// Lightweight periodic synthetic event to prevent idle expiry if FB sends nothing
|
|
412
|
+
if (!ctx._syntheticKeepAliveInterval) {
|
|
413
|
+
ctx._syntheticKeepAliveInterval = setInterval(() => {
|
|
414
|
+
if (!ctx.mqttClient || !ctx.mqttClient.connected) return;
|
|
415
|
+
if (ctx.globalSafety) {
|
|
416
|
+
const idle = Date.now() - ctx.globalSafety._lastEventTs;
|
|
417
|
+
// Inject synthetic event every 70s if no real traffic -> keeps timers fresh
|
|
418
|
+
if (idle > 65 * 1000) {
|
|
419
|
+
ctx.globalSafety.recordEvent();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}, 30000);
|
|
423
|
+
}
|
|
409
424
|
}
|
|
410
425
|
function getTaskResponseData(taskType, payload) {
|
|
411
426
|
try {
|