nexus-fca 2.1.8 → 3.0.0

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 CHANGED
@@ -1,202 +1,21 @@
1
1
  # Changelog
2
+ ## [3.0.0] - 2025-09-11 - Advanced Core Release
2
3
 
3
- ## [2.1.8] - 2025-09-02 - Extended Safe Refresh Window
4
- ### Changed
5
- - Safe session refresh interval widened to 3–5h (adaptive: 1–1.5h when risk=high) to reduce token churn and mitigate premature cookie expiry heuristics.
6
-
7
- ### Rationale
8
- - Frequent refreshes can accelerate cookie rotation patterns → earlier invalidation.
9
- - Longer, randomized window maintains stealth while heartbeats, ghost detection, and adaptive reconnect still ensure liveness.
10
-
11
- ### Notes
12
- - Lightweight mid‑session fb_dtsg poke (≈6h ±40m) retained; heavy refresh cadence now sparser.
4
+ ### Overview
5
+ Version 3.0.0 represents a pivotal milestone in the evolution of the Nexus-fCA platform, transitioning from the iterative stabilization efforts of the 2.1.x series to a unified, production-ready foundation. This release is the culmination of extensive engineering, rigorous validation, and a commitment to delivering enterprise-grade reliability. The legacy diagnostics harness has been formally retired, replaced by a standardized internal instrumentation framework that streamlines monitoring, troubleshooting, and ongoing maintenance. These enhancements collectively reinforce the platform’s robustness, scalability, and operational transparency.
13
6
 
14
- ---
15
-
16
- ## [2.1.7] - 2025-09-01 - Session Stability Patch
17
7
  ### Added
18
- - User-Agent continuity (anchored single UA for entire session via safety module; eliminates mid-session UA drift increasing 20–22h expiry risk).
19
- - Exposed `setFixedUserAgent()` in `FacebookSafety` to allow explicit anchoring from credential phase.
20
- - Mid-session lightweight token poke (6h ±40m) to keep session warm without full heavy refresh cycle.
8
+ - **Delivery Receipt Health Metrics:** Introduced a comprehensive suite of metrics for delivery receipts, encompassing attempt counts, success rates, failure rates, timeout tracking, and adaptive disablement flags. These metrics provide granular visibility into delivery performance and facilitate proactive issue resolution.
9
+ - **Advanced Timeout Suppression Logic:** Implemented sophisticated suppression mechanisms for repeated delivery timeouts. This ensures consistent reply performance, mitigates the risk of systemic degradation, and enhances overall service reliability.
10
+ - **Internal Instrumentation Standardization:** All diagnostic and monitoring capabilities have been consolidated under a unified instrumentation framework, simplifying maintenance and enabling more effective root cause analysis.
21
11
 
22
12
  ### Changed
23
- - Removed legacy mobile agent override fallback in `loginHelper` that caused mixed UA fingerprints.
24
- - All safe requests now inherit continuity-aware UA through `applySafeRequestOptions`.
25
-
26
- ### Improved
27
- - Extended resilience against 20–22h cookie invalidation observed with prior dual-phase UA pattern.
28
- - Reduced unnecessary full refresh churn while preserving stealth (`safeRefresh` + light poke coexist).
29
-
30
- ---
31
-
32
- ## [2.1.6] - 2025-08-31 - Memory Guard & Queue Sweeping
33
- ### Added
34
- - Central lightweight memory guard sweeps: group queue pruning (idle >30m, overflow trim) and pendingEdits TTL sweeper (every 4m).
35
- - Health metrics extended: memoryGuardRuns, memoryGuardActions, groupQueueDroppedMessages, groupQueueExpiredQueues, groupQueuePrunedThreads, pendingEditSweeps.
36
- - API: `api.getMemoryMetrics()` returns focused memory-related counters.
37
- - Typings updated (`EditOptions`, new API methods) in `index.d.ts`.
38
-
39
- ### Improved
40
- - Group queue now tracks `lastActive` and enforces idle purge + overflow protection with metrics.
41
- - Pending edits TTL enforcement separated from resend watchdog for deterministic expiry.
42
-
43
- ### Notes
44
- - All guards are low-frequency, low-impact; no change to delivery reliability or safety – only prevention of unbounded growth.
45
-
46
- ---
47
-
48
- ## [2.1.5] - 2025-08-28 - PendingEdits & ACK Metrics
49
- ### Added
50
- - PendingEdits buffer with cap (default 200) + TTL (5m) + resend attempts (2) + ACK timeout (12s).
51
- - Automatic edit resend watchdog with safe limits and metrics (editResends, editFailed, pendingEditsDropped, pendingEditsExpired).
52
- - API: `api.setEditOptions({ maxPendingEdits, editTTLms, ackTimeoutMs, maxResendAttempts })`.
53
- - Edit ACK integration: pending edit removed on ACK receipt.
54
- - Health metrics: p95AckLatencyMs, editResends, editFailed.
55
-
56
- ### Improved
57
- - Safer edit pipeline: prevents uncontrolled retries, bounds memory, tracks expirations.
58
- - Enhanced HealthMetrics with percentile latency sample retention (50-sample window).
13
+ - **Documentation and Package Description:** The package description and README have undergone a comprehensive revision to align with professional standards and accurately reflect the platform’s positioning, capabilities, and intended use cases.
14
+ - **Semantic Versioning:** The major version increment to 3.0.0 signifies the platform’s maturity, stability, and readiness for mission-critical deployments. Importantly, there are no breaking changes to public APIs compared to the 2.1.x series, ensuring continuity for existing integrations.
59
15
 
60
16
  ### Notes
61
- - Durable outbound queue & metrics exporter planned next.
62
-
63
- ---
64
-
65
- ## [2.1.4] - Adaptive Backoff & Core Metrics
66
- ### Added
67
- - Adaptive reconnect backoff with jitter (caps at 5 minutes) for safer, stealthier recovery loops.
68
- - Lazy preflight session validation (skips heavy validation if a recent successful connect occurred) toggle via `api.enableLazyPreflight()`.
69
- - Health metrics collector (uptime, idle time, reconnect counts, failures, message/ack counters, synthetic keepalives) accessible with `api.getHealthMetrics()` and included in `api.healthCheck()`.
70
- - Randomized synthetic keepalive interval (55-75s) to reduce detection patterns.
71
- - Backoff configuration hook: `api.setBackoffOptions()`.
72
-
73
- ### Improved
74
- - Reduced noisy session validation on every `listenMqtt` invocation unless needed.
75
- - More structured error classification feeding metrics (`session_invalid`, `timeout_no_t_ms`, `mqtt_error`, `message_parse`, `not_logged_in`).
76
-
77
- ### Planned (Next)
78
- - ACK tracking refinement & resend logic.
79
- - Pending edits buffer with TTL and cap.
80
- - Durable outbound queue & health exporter.
81
-
82
- ---
83
-
84
- ## [2.1.2] - CONTINUOUS IDLE RECOVERY
85
- ### Added
86
- - Soft-stale probing at 2 minutes idle (ping + conditional forced reconnect if no events within 5-8s)
87
- - Wrapper around `listenMqtt` to automatically feed events into safety heartbeat (`recordEvent`) for precise idle detection
88
- - Ghost connection detection (10m silent but socket connected triggers forced reconnect after probe)
89
- - Periodic connection recycle every ~6h ±30m to prevent long-lived silent degradation
90
- - Force reconnect API: `globalSafety.forceReconnect(tag)`
91
-
92
- ### Improved
93
- - Faster recovery from silent idle states (previously required >5 min or external trigger)
94
- - Reduced chance of appearing online but unresponsive after short inactivity
95
- - Added keepalive foreground_state publishes each heartbeat
96
-
97
- ---
98
-
99
- ## [2.1.1] - 2025-08-27 - ADVANCED SESSION STABILITY
100
- ### 🛠 Added
101
- - Adaptive safe session refresh interval (dynamic based on risk level)
102
- - Heartbeat + watchdog timers to detect stale MQTT connections early
103
- - Progressive backoff with jitter for MQTT reconnect attempts
104
- - Layered post-refresh health checks (1s / 10s / 30s) to catch silent drops
105
- - Abortable refresh with timeout safeguard (25s) to prevent hangs
106
- - Automatic reconnection trigger if no events within thresholds (2m soft, 15m hard)
107
- - `destroy()` method to cleanup timers/listeners (prevents memory leaks)
108
-
109
- ### 🔄 Changed
110
- - Safe refresh now records in‑flight ID and supersedes outdated checks
111
- - Reconnect logic centralized in `_reconnectMqttWithBackoff`
112
-
113
- ### ✅ Improved
114
- - Stability after long runtimes / multiple token refresh cycles
115
- - Reduced risk of listener not resuming after refresh
116
-
117
- ---
118
-
119
- ## [2.1.0] - 2025-08-20 - SESSION RELIABILITY & PROMISE LOGIN
120
- ### 🚀 Highlights
121
- Stability-focused release improving long‑running bot sessions, reducing false `not_logged_in` events, and modernizing the login flow.
122
-
123
- ### Added
124
- - ✅ Promise support for `login()` (dual callback + Promise API)
125
- - 🆔 Persistent device fingerprint (saved to `persistent-device.json`) to reduce checkpoint / lock frequency
126
- - 🛡️ New `validateSession()` multi-endpoint heuristic (lightweight, resilient preflight)
127
- - ⚙️ New global option: `disablePreflight` (skip session validation if desired)
128
- - 🔄 Structured error types from `parseAndCheckLogin` (`login_redirect`, `html_login_page`, `network_redirect`, etc.)
129
- - 🧪 Example: `examples/echo-test.js` (Promise style, supports env credentials or appstate)
130
-
131
- ### Changed
132
- - 🔁 `listenMqtt` now performs silent initial validation; only emits `not_logged_in` after a confirmatory retry
133
- - 🧠 `parseAndCheckLogin` now robustly handles 3xx chains & HTML login fallback pages
134
- - 🔐 Default behavior: device identity no longer rotates unless explicitly overridden
135
- - 🧩 Refactored internal cookie & session utilities (centralized in `utils.js`)
136
- - 📄 Rewritten documentation (README, DOCS, CHANGELOG) for concise modern onboarding
137
-
138
- ### Fixed
139
- - ❌ Spurious `parseAndCheckLogin got status code: 302` fatal errors now classified & recovered when possible
140
- - 💤 False negatives from legacy preflight removed (no premature `not_logged_in` during transient redirects)
141
- - 🔄 Edge reconnect loop where MQTT closed before revalidation completed
142
-
143
- ### Migration Notes (2.0.x → 2.1.0)
144
- - Existing code using callbacks continues to work. To use Promises: `const api = await login(opts);`
145
- - If you previously depended on device rotation, disable persistent device via option (see README) or delete `persistent-device.json`.
146
- - Remove any custom preflight hacks; built‑in `validateSession` supersedes them.
147
-
148
- ### Developer / Internal
149
- - Centralized session validation pipeline
150
- - Added granular error classification to aid future retry/backoff strategies
151
- - Prepared foundation for upcoming metrics hooks in 2.2.x
152
-
153
- ---
154
-
155
- ## [2.0.5] - 2025-07-29 - FULLY INTEGRATED NPM EDITION
156
- ### 🎯 MAJOR: Full NPM Integration
157
- - **✅ FULLY INTEGRATED**: Entire Nexus Login System now embedded directly in main `index.js`
158
- - **📦 NPM COMPATIBLE**: Works perfectly when installed via `npm install nexus-fca` - no external folder dependencies
159
- - **⚡ ZERO CONFIG**: Everything works out of the box - no separate folder setup required
160
- - **🔄 SEAMLESS MIGRATION**: Existing code continues to work, new code benefits from integration
161
-
162
- ### Added
163
- - 🎯 **Direct exports**: `nexusLogin` and `IntegratedNexusLoginSystem` available directly from main package
164
- - 🧪 **Updated test files**: All test scripts now use integrated system (`require('nexus-fca')` instead of `./nexloginsystem`)
165
- - 📖 **New documentation**: `npm-integration-guide.md` with complete NPM usage guide
166
- - 🛠️ **NPM scripts**: Added `test:login`, `test:simple`, `test:2fa`, `test:all` for easy testing
167
- - 📦 **Enhanced package.json**: Updated keywords, description, and version for NPM integration
168
-
169
- ### Changed
170
- - 🏗️ **Architecture**: Moved entire Nexus Login System from external folder into main index.js (lines 372-860+)
171
- - 📝 **Documentation**: Updated README.md to reflect NPM installation and integrated usage
172
- - 🔧 **Test files**: Fixed all test imports to use main package instead of external folder
173
- - 📦 **Package info**: Updated to v2.0.5 with new description highlighting NPM integration
174
-
175
- ### Fixed
176
- - ❌ **NPM module errors**: Eliminated "Cannot find module './nexloginsystem'" when using as npm package
177
- - 🔗 **Import paths**: All test files and examples now use correct import paths for npm usage
178
- - 🎯 **Distribution**: Package now works identically whether used locally or installed via npm
179
-
180
- ## [2.0.4] - 2025-07-29
181
- ### Fixed
182
- - 🐛 **Missing nexloginsystem folder**: Added `nexloginsystem/` to npm package files array to fix "Cannot find module './nexloginsystem'" error
183
- - 🔄 **Legacy login fallback**: Added automatic fallback to appstate-only login when Nexus Login System is not available
184
- - 🛡️ **Backward compatibility**: Enhanced compatibility for users using nexus-fca as npm dependency without full login system
185
-
186
- ### Changed
187
- - Updated package.json to include nexloginsystem folder in published package
188
- - Enhanced error handling with graceful fallback mechanisms
189
-
190
- ## [2.0.1] - 2025-07-28
191
- ### Added
192
- - 🚀 **Nexus Login System**: Advanced, safe, and automatic Facebook login system added under `/nexloginsystem`.
193
- - 🔐 **Appstate auto-generation**: Login with username/password/2FA, auto-save appstate, and seamless bot start.
194
- - 🛡️ **Maximum safety**: Human-like device simulation, TOTP/2FA support, and advanced error handling.
195
- - 📦 **Auto-backup & validation**: Appstate backup, validation, and lifecycle management.
196
- - 📚 **Full documentation**: Usage, API, and safety docs in `/nexloginsystem/README.md`.
197
-
198
- ### Changed
199
- - Updated main `README.md` with Nexus Login System quick start and features.
17
+ - **Upgrade Path:** Transitioning from 2.1.x to 3.0.0 is seamless. All documented interfaces remain unchanged, and the upgrade process requires no code modifications for existing consumers. The major version bump reflects strategic lifecycle consolidation and a renewed focus on long-term maintainability, rather than disruptive changes.
18
+ - **Lifecycle Consolidation:** This release unifies prior stabilization efforts, setting a new baseline for future enhancements and support. Users can expect ongoing improvements in reliability, observability, and operational efficiency.
200
19
 
201
- ### Removed
202
- - Old test files and legacy appstate generator scripts (now replaced by Nexus Login System).
20
+ ### Historical Logs
21
+ Version logs for the 2.1.x series have been archived and are available upon request for reference and audit purposes.
package/README.md CHANGED
@@ -1,77 +1,30 @@
1
- # Nexus-FCA v2.1.7
2
-
3
- <!-- 2.1.7 Session Stability Patch -->
4
- > New in 2.1.7: Session Stability Patch – anchored User-Agent continuity (eliminates 20–22h silent expiry pattern), lightweight mid‑session token poke (6h ±40m) + existing adaptive safeRefresh, retains ultra‑low ban profile.
5
-
6
- <!-- 2.1.6 Memory Guard -->
7
- > 2.1.6: Memory Guard & Queue Sweeping – bounded group queues, pending edit TTL sweeper, memory metrics exporter.
8
-
9
- <!-- 2.1.5 PendingEdits -->
10
- > 2.1.5: PendingEdits buffer (cap + TTL + safe resend), edit ACK watchdog, p95 ACK latency & edit resend/failure metrics, configurable via `api.setEditOptions()`.
11
-
12
1
  <p align="center">
13
- <!-- Preview image wrapped in link (corrected ibb.co domain) -->
14
- <a href="https://ibb.co/8ymR1tw"><img src="https://i.ibb.co/Sk61FGg/Dragon-Fruit-1.jpg" alt="Nexus-FCA Dragon Fruit" width="520" border="0" /></a>
2
+ <img src="https://i.ibb.co/Sk61FGg/Dragon-Fruit-1.jpg" alt="Nexus-FCA" width="520" />
15
3
  </p>
16
4
 
17
- > Advanced, safe, modern Facebook Chat (Messenger) API with integrated secure login (ID / Password / 2FA), ultra‑low ban rate session management, adaptive MQTT resilience, memory guard, and TypeScript-ready developer experience.
5
+ # Nexus-FCA v3.0.0 Advanced Core Release
18
6
 
19
- ---
20
- ## ✨ Highlights (Core Pillars)
21
- - 🔐 Integrated secure login system (username/password + TOTP 2FA) → auto appstate
22
- - 🛡️ Ultra-low ban rate design (human timing, safety limiter, anchored UA, risk heuristics)
23
- - 🔄 Resilient MQTT listener (adaptive backoff + idle / ghost detection + periodic recycle)
24
- - ♻️ Session continuity: anchored UA + adaptive safe refresh + lightweight mid-session poke
25
- - 🧠 Smart session validation (lazy preflight, multi-endpoint retry, reduced false logouts)
26
- - 📊 Live health & memory metrics (`api.getHealthMetrics()`, `api.getMemoryMetrics()`)
27
- - 🧾 Type definitions (`index.d.ts`) & modern Promise / callback API
28
- - 🧩 Modular architecture (safety, performance, error, mqtt managers)
7
+ Modern, safe, production‑ready Messenger (Facebook Chat) API layer with integrated secure login (credentials + 2FA), adaptive session & connection resilience, delivery reliability safeguards, memory protection, and rich runtime metrics. Promise + callback compatible, TypeScript typed, minimal friction.
29
8
 
30
9
  ---
31
- ## 🚀 Recent Stability Enhancements (2.1.7 / 2.1.6 / 2.1.5)
32
- | Version | Focus | Key Additions |
33
- |---------|-------|---------------|
34
- | 2.1.7 | Session Longevity | UA continuity anchor, lightweight token poke, removal of mid-login UA drift |
35
- | 2.1.6 | Memory Safety | Group queue idle purge + overflow trim, pendingEdits TTL sweeper, memory guard metrics |
36
- | 2.1.5 | Edit Reliability | PendingEdits buffer (cap+TTL), ACK watchdog, resend limits, p95 ACK latency |
37
-
38
- ### Why UA Continuity Matters
39
- Previously, dual-phase login could swap user agents (mobile desktop) causing server-side heuristic expiry near 20–22h. Anchoring a single UA eliminates the inconsistent device fingerprint pattern and extends stable runtime under identical safety posture.
40
-
41
- ### Lightweight Mid-Session Poke
42
- A subtle `fb_dtsg` refresh every ~6h ±40m (in addition to adaptive risk-based safeRefresh) keeps tokens warm without aggressive churn, lowering validation friction while avoiding noisy traffic patterns.
10
+ ## Core Value
11
+ | Pillar | What You Get |
12
+ |--------|--------------|
13
+ | Integrated Secure Login | Username / Password / TOTP 2FA stable appstate generation & reuse |
14
+ | Session Resilience | Anchored User‑Agent continuity, adaptive safe refresh, lightweight token poke, periodic recycle |
15
+ | Connection Stability | Adaptive MQTT backoff, idle & ghost detection, layered post-refresh health probes, synthetic keepalives |
16
+ | Delivery Reliability | Multi-path message send fallback (MQTT → HTTP → direct) + delivery receipt timeout suppression |
17
+ | Memory Guard | Bounded queues, edit TTL sweeps, controlled resend limits |
18
+ | Observability | Health + memory + delivery metrics (`api.getHealthMetrics()`, `api.getMemoryMetrics()`) |
19
+ | Edit Safety | Pending edit buffer, ACK watchdog, p95 ACK latency tracking |
20
+ | Type Definitions | First-class `index.d.ts` with modern Promise signatures |
43
21
 
44
22
  ---
45
- ## 🧪 Key API Additions
46
- ```js
47
- api.setEditOptions({ maxPendingEdits, editTTLms, ackTimeoutMs, maxResendAttempts });
48
- api.setBackoffOptions({ base, factor, max, jitter });
49
- api.enableLazyPreflight(true); // Skip heavy validation if a recent good connect exists
50
- api.getHealthMetrics(); // uptime, reconnect stats, ack latency, synthetic keepalives
51
- api.getMemoryMetrics(); // queue depths, drops, guard run counters
52
- ```
23
+ ## 🔄 What Changed in 3.0.0
24
+ Major version signals maturity & consolidation. No breaking public API changes versus late 2.1.x – upgrade is drop‑in. Temporary diagnostic harness removed; internal instrumentation formalized. Delivery receipt timeouts now intelligently retried & optionally auto-suppressed to protect outbound responsiveness.
53
25
 
54
26
  ---
55
- ## 🔍 Monitoring Example
56
- ```js
57
- setInterval(() => {
58
- const h = api.getHealthMetrics();
59
- const m = api.getMemoryMetrics();
60
- console.log('[HEALTH]', h?.status, 'acks', h?.ackCount, 'p95Ack', h?.p95AckLatencyMs);
61
- console.log('[MEM]', m);
62
- }, 60000);
63
- ```
64
-
65
- ---
66
- ## 🧷 Long Session Best Practices
67
- 1. Use appstate login when possible (avoid frequent credential logins).
68
- 2. Keep `persistent-device.json` – do not rotate unless forced.
69
- 3. Avoid changing UA manually; continuity is automatic post‑2.1.7.
70
- 4. Inspect health metrics before manually forcing reconnects.
71
- 5. Let adaptive backoff handle transient network instability.
72
-
73
- ---
74
- ## ⚡ Quick Start (Appstate)
27
+ ## 🚀 Quick Start (Appstate Preferred)
75
28
  ```js
76
29
  const login = require('nexus-fca');
77
30
 
@@ -85,15 +38,14 @@ const login = require('nexus-fca');
85
38
  })();
86
39
  ```
87
40
 
88
- ## 🔐 Quick Start (Credentials + 2FA)
41
+ ### Credentials + 2FA Flow
89
42
  ```js
90
43
  const login = require('nexus-fca');
91
-
92
44
  (async () => {
93
45
  const api = await login({
94
46
  email: process.env.FB_EMAIL,
95
47
  password: process.env.FB_PASS,
96
- twofactor: process.env.FB_2FA_SECRET // optional
48
+ twofactor: process.env.FB_2FA_SECRET // optional TOTP secret
97
49
  });
98
50
  api.listen((err, msg) => {
99
51
  if (err) return console.error(err);
@@ -103,142 +55,132 @@ const login = require('nexus-fca');
103
55
  ```
104
56
 
105
57
  ---
106
- ## 🛡️ Safety Layer (Updated)
107
- | Feature | Benefit |
108
- |---------|---------|
109
- | Anchored User-Agent | Eliminates fingerprint drift (prevents 20–22h expiry) |
110
- | Adaptive Safe Refresh | Risk‑sensitive token renewal bands |
111
- | Lightweight Token Poke | Quiet longevity without churn |
112
- | Idle / Ghost Detection | Auto probe + reconnect on silent stalls |
113
- | Periodic Recycle | 6h ± jitter connection rejuvenation |
114
- | Persistent Device Profile | Fewer checkpoints / trust continuity |
115
- | Lazy Preflight | Skips heavy validation when recently healthy |
116
- | Human-like Timing | Reduces automation signal surface |
117
-
118
- Disable preflight if needed:
58
+ ## 🧪 Key Runtime APIs
119
59
  ```js
120
- await login({ appState }, { disablePreflight: true });
60
+ api.setEditOptions({ maxPendingEdits, editTTLms, ackTimeoutMs, maxResendAttempts });
61
+ api.setBackoffOptions({ base, factor, max, jitter });
62
+ api.enableLazyPreflight(true); // Skip heavy validation if recent success
63
+ api.getHealthMetrics(); // uptime, reconnects, ack latency, delivery stats
64
+ api.getMemoryMetrics(); // queue sizes & guard counters
121
65
  ```
122
66
 
123
- ---
124
- ## 🛰️ MQTT Listener Enhancements
125
- - Adaptive exponential backoff with jitter (caps 5m)
126
- - Soft-stale probing (2m30s) + hard watchdog tiers
127
- - Layered post-refresh health checks (1s / 10s / 30s) after token renewal
128
- - Synthetic keepalives (randomized 55–75s) feeding metrics
129
-
130
- ---
131
- ## 📦 Example Echo Test
132
- `examples/echo-test.js`:
133
- ```bash
134
- node examples/echo-test.js
67
+ ### Monitoring Snippet
68
+ ```js
69
+ setInterval(() => {
70
+ const h = api.getHealthMetrics();
71
+ const m = api.getMemoryMetrics();
72
+ console.log('[HEALTH]', h?.status, 'acks', h?.ackCount, 'p95Ack', h?.p95AckLatencyMs);
73
+ console.log('[DELIVERY]', {
74
+ attempts: h?.deliveryAttempts,
75
+ success: h?.deliverySuccess,
76
+ failed: h?.deliveryFailed,
77
+ timeouts: h?.deliveryTimeouts,
78
+ disabledSince: h?.deliveryDisabledSince
79
+ });
80
+ console.log('[MEM]', m);
81
+ }, 60000);
135
82
  ```
136
- Provide `appstate.json` or set `EMAIL` / `PASSWORD` env variables.
137
83
 
138
84
  ---
139
- ## 🧠 Advanced Login Flow
140
- 1. Integrated system safely generates / refreshes cookies (if credentials supplied)
141
- 2. Core consumes resulting appstate for stable API behavior
142
- 3. Persistent device JSON: `persistent-device.json`
85
+ ## 🛡️ Safety & Stability Architecture
86
+ | Layer | Mechanism | Purpose |
87
+ |-------|-----------|---------|
88
+ | UA Continuity | Single anchored fingerprint | Avoid heuristic expiry & drift |
89
+ | Adaptive Refresh | Risk-aware timing bands | Token longevity without bursts |
90
+ | Lightweight Poke | Subtle `fb_dtsg` renewal | Keeps session warm quietly |
91
+ | Collision Guard | 45m spacing window | Prevent clustered maintenance events |
92
+ | Idle / Ghost Probe | Timed silent detection | Force reconnect on stale sockets |
93
+ | Periodic Recycle | Randomized (~6h ±30m) | Pre-empt silent degradation |
94
+ | Backoff Strategy | Exponential + jitter | Graceful network recovery |
95
+ | Delivery Suppression | Disable after repeated timeouts | Preserve send latency |
143
96
 
144
- Persistent device toggle:
97
+ Disable heavy preflight if embedding inside a framework already doing checks:
145
98
  ```js
146
- const { IntegratedNexusLoginSystem } = require('nexus-fca');
147
- new IntegratedNexusLoginSystem({ persistentDevice: true });
99
+ await login({ appState }, { disablePreflight: true });
148
100
  ```
149
101
 
150
102
  ---
151
- ## 🐐 Using Nexus-FCA with GoatBot V2
152
- Nexus-FCA can act as a drop‑in enhancement for the legacy fb-chat-api layer inside GoatBot V2.
153
-
154
- ### Option 1: Non‑invasive (generate fresh appstate)
155
- 1. In a separate script, run Nexus-FCA credential login (with 2FA if needed):
156
- ```js
157
- const login = require('nexus-fca');
158
- (async () => {
159
- const api = await login({ email: process.env.FB_EMAIL, password: process.env.FB_PASS, twofactor: process.env.FB_2FA });
160
- const appState = api.getAppState();
161
- require('fs').writeFileSync('./appstate.json', JSON.stringify(appState, null, 2));
162
- console.log('Saved appstate.json');
163
- })();
164
- ```
165
- 2. Configure GoatBot to use that `appstate.json` (no credential scraping needed).
166
- 3. Repeat only when session truly expires (persistent device reduces frequency).
167
-
168
- ### Option 2: Replace internal fb-chat-api
169
- GoatBot has a local `fb-chat-api` folder. To leverage Nexus-FCA improvements globally:
170
- 1. Install Nexus-FCA inside GoatBot project:
171
- ```bash
172
- npm install nexus-fca
173
- ```
174
- 2. Rename GoatBot’s original folder for backup:
175
- ```bash
176
- mv fb-chat-api fb-chat-api.orig # (Windows: rename manually)
177
- ```
178
- 3. Create a shim folder `fb-chat-api/index.js` with:
179
- ```js
180
- module.exports = require('nexus-fca');
181
- ```
182
- 4. Start GoatBot normally. All calls (`login`, `api.listen`, send methods) now use Nexus-FCA (Promise supported).
103
+ ## 🛰️ MQTT Enhancements (Since 2.1.x)
104
+ - Adaptive reconnect curve (caps 5m)
105
+ - Layered post-refresh probes (1s / 10s / 30s)
106
+ - Synthetic randomized keepalives (55–75s)
107
+ - Structured error classification feeding metrics
183
108
 
184
- ### Option 3: Direct require patch
185
- Search GoatBot source for `require("fb-chat-api")` and change to `require("nexus-fca")`.
109
+ ---
110
+ ## ✉️ Delivery Reliability
111
+ - Multi-path send fallback (MQTT publish → HTTP send → direct fallback)
112
+ - Per-attempt timeout & retry for message delivery receipts
113
+ - Automatic classification of transient timeouts (ETIMEDOUT / ECONNRESET / EAI_AGAIN)
114
+ - Adaptive suppression of delivery receipt calls when environment unstable (protects primary send throughput)
186
115
 
187
- ### Promise usage inside GoatBot scripts
188
- Replace:
189
- ```js
190
- fbapi(loginData, (err, api) => { ... });
191
- ```
192
- with:
193
- ```js
194
- const login = require('nexus-fca');
195
- const api = await login(loginData); // supports { appState } or { email, password, twofactor }
196
- ```
116
+ ---
117
+ ## 🧠 Long Session Best Practices
118
+ 1. Prefer appstate reuse (minimal credential logins).
119
+ 2. Preserve `persistent-device.json` (only delete if forced challenge).
120
+ 3. Don’t manually rotate User-Agent – built-in continuity handles it.
121
+ 4. Inspect metrics before forcing reconnect; let backoff work.
122
+ 5. Keep dependencies updated; review CHANGELOG for operational notes.
197
123
 
198
- ### Recommended settings
199
- - Keep `persistent-device.json` at project root so repeated restarts reuse the same fingerprint.
200
- - If GoatBot already performs its own “live cookie check” loops, you can set `{ disablePreflight: true }` to avoid duplicate validation.
201
- - Handle reconnect events: listen for `error` and `listen` callbacks just like original; classified errors now have `error.type` (`login_redirect`, etc.).
124
+ ---
125
+ ## 🐐 Using with GoatBot V2 (Summary)
126
+ | Goal | Steps |
127
+ |------|-------|
128
+ | Generate appstate | Run credential login script → save `appstate.json` → configure GoatBot |
129
+ | Full replacement | Install `nexus-fca` → shim `fb-chat-api/index.js` exporting module |
130
+ | Direct require swap | Replace `require('fb-chat-api')` with `require('nexus-fca')` |
202
131
 
203
- ### Minimal integration example
132
+ Minimal example:
204
133
  ```js
205
134
  const login = require('nexus-fca');
206
135
  (async () => {
207
136
  const api = await login({ appState: require('./appstate.json') });
208
- api.listen(async (err, event) => {
209
- if (err) return console.error('[Nexus-FCA]', err);
137
+ api.listen((err, event) => {
138
+ if (err) return console.error(err);
210
139
  if (event.body === '!ping') api.sendMessage('pong', event.threadID);
211
140
  });
212
141
  })();
213
142
  ```
214
143
 
215
144
  ---
216
- ## 📚 Documentation
217
- - Full API reference: `DOCS.md`
218
- - Per-feature guides: `/docs/*.md`
219
- - Safety: `docs/account-safety.md`
220
- - Examples: `/examples`
145
+ ## 📚 Documentation Map
146
+ | Resource | Location |
147
+ |----------|----------|
148
+ | Full API Reference | `DOCS.md` |
149
+ | Feature Guides | `docs/*.md` |
150
+ | Safety Details | `docs/account-safety.md` |
151
+ | Examples | `examples/` |
152
+
153
+ ---
154
+ ## � Migrating 2.1.x → 3.0.0
155
+ | Area | Action Needed |
156
+ |------|---------------|
157
+ | Public API | None (fully compatible) |
158
+ | Diagnostics Harness | Removed (no action) |
159
+ | Delivery Metrics | Optionally surface in dashboards |
160
+ | Safety Manager (legacy) | Keep removed / unused |
221
161
 
222
162
  ---
223
- ## 🔁 Updating from 2.0.x 2.1.x
224
- | Change | Action |
225
- |--------|--------|
226
- | UA Continuity (2.1.7) | No action; auto applied |
227
- | Memory Guard (2.1.6) | Inspect `api.getMemoryMetrics()` periodically |
228
- | PendingEdits (2.1.5) | Tune via `api.setEditOptions()` if needed |
229
- | Lazy Preflight | Optionally disable when embedding in other frameworks |
230
- | Persistent Device | Keep file unless forced reset required |
163
+ ## 🗂 Previous 2.1.x Highlights (Condensed)
164
+ | Version | Focus | Key Additions |
165
+ |---------|-------|---------------|
166
+ | 2.1.10 | Stabilization | Final 2.1.x meta adjustments |
167
+ | 2.1.8 | Safety Consolidation | Unified orchestrator, collision spacing, recycle suppression |
168
+ | 2.1.7 | Session Longevity | UA continuity, lightweight poke |
169
+ | 2.1.6 | Memory Guard | Queue pruning, edit TTL sweeps |
170
+ | 2.1.5 | Edit Reliability | PendingEdits buffer, ACK watchdog |
231
171
 
232
- No breaking API changes across 2.1.x line.
172
+ Full details remain in `CHANGELOG.md`.
233
173
 
234
174
  ---
235
175
  ## ⚠️ Disclaimer
236
- This project is not affiliated with Facebook. Use responsibly. You are solely responsible for compliance with platform terms and local laws.
176
+ Not affiliated with Facebook. Use responsibly and comply with platform terms & local laws.
237
177
 
238
178
  ---
239
- ## 🤝 Contribute
240
- PRs for safety, stability, perf, and updated GraphQL doc_ids welcome.
179
+ ## 🤝 Contributing
180
+ Focused PRs improving stability, safety heuristics, protocol coverage, or typings are welcome.
241
181
 
242
182
  ---
243
183
  ## 📜 License
244
- MIT © 2025 Nexus-FCA Contributors
184
+ [Team Nexus](https://www.facebook.com/profile.php?id=61572587854836)
185
+
186
+ MIT © 2025 Nexus (Team Nexus)
@@ -11,6 +11,8 @@ Nexus-FCA Ultra-Safe Edition is designed to minimize Facebook account ban, lock,
11
11
  - **Proactive Safety Alerts:** If a risk is detected (lock, checkpoint, block), the bot will pause or stop to prevent further issues
12
12
  - **Session & Token Management:** Automatic session validation and safe token refresh keep your login secure
13
13
  - **Region & Connection Protection:** Advanced region bypass and safe reconnection logic avoid suspicious activity triggers
14
+ - **Unified Safety Orchestrator (2.1.8+):** Single scheduler coordinates safe refresh, light poke (~6h ±40m), and periodic recycle (~6h ±30m) with collision spacing (45m) to prevent clustered token actions
15
+ - **Ghost / Idle Detection:** Soft-stale probing (2m30s) and ghost recovery before full disconnect patterns emerge
14
16
 
15
17
  ---
16
18
 
@@ -24,6 +26,7 @@ Nexus-FCA Ultra-Safe Edition is designed to minimize Facebook account ban, lock,
24
26
  - Always use cookies less than 7 days old for best results
25
27
  - **Monitor Risk Level:**
26
28
  - Listen for `riskLevelHigh`, `accountLocked`, `checkpointRequired` events and take action if triggered
29
+ - **Avoid Deprecated Manager:** Do not instantiate `FacebookSafetyManager` (legacy); consolidated `FacebookSafety` handles everything automatically.
27
30
 
28
31
  ---
29
32
 
@@ -34,6 +37,8 @@ Nexus-FCA Ultra-Safe Edition is designed to minimize Facebook account ban, lock,
34
37
  - **Update your appstate.json regularly**
35
38
  - **Monitor your Facebook account for security notifications**
36
39
  - **If you see a checkpoint or lock, stop the bot and verify your account manually**
40
+ - **Do not schedule custom token poke timers; built‑in orchestrator already manages safe refresh cadence & spacing**
41
+ - **Keep `persistent-device.json` stable—don’t delete unless forced by actual session invalidation**
37
42
 
38
43
  ---
39
44
 
@@ -55,6 +60,13 @@ client.login({ appState: require('./appstate.json') });
55
60
  - `checkpointRequired` — Facebook requires manual verification
56
61
  - `riskLevelHigh` — High risk detected, bot will increase delays and reduce activity
57
62
  - `sessionExpired` — Session expired, update your appstate.json
63
+ - `safeRefresh` — Safe token refresh attempted (payload includes success/failure, duration)
64
+ - `lightPoke` — Lightweight fb_dtsg keep-alive executed
65
+ - `mqttReconnect` — Reconnect cycle triggered (reason + attempt)
66
+ - `heartbeat` — Periodic keepalive ping succeeded
67
+
68
+ ### Event Spacing (2.1.8+)
69
+ The orchestrator enforces a minimum spacing window (~45m) between maintenance actions (refresh, recycle, light poke). If a recycle is scheduled too soon after a refresh/poke it defers 20–30m automatically to avoid clustering patterns.
58
70
 
59
71
  ---
60
72
 
@@ -66,7 +78,16 @@ client.login({ appState: require('./appstate.json') });
66
78
  > Stop the bot immediately, log in to Facebook manually, and follow the verification steps. Only restart the bot after your account is fully restored.
67
79
 
68
80
  **Q: How often should I update my appstate.json?**
69
- > At least once a week, or whenever you see a session expired or checkpoint event.
81
+ > At least once a week, or whenever you see a session expired or checkpoint event (less often if persistent device + long stable runs).
82
+
83
+ **Q: How often are tokens refreshed now?**
84
+ > Adaptive. Low risk uses multi‑hour windows; high risk shortens interval. Lightweight mid-session poke (~6h ±40m) and periodic recycle (~6h ±30m) are collision‑guarded with 45m spacing.
85
+
86
+ **Q: Do I need to keep my own refresh timers?**
87
+ > No. Remove custom refresh/poke loops—duplication increases clustering risk.
88
+
89
+ **Q: Is legacy `FacebookSafetyManager` still required?**
90
+ > No. It is deprecated and only logs a warning if used. Migrate entirely to the integrated safety layer (automatic on login).
70
91
 
71
92
  ---
72
93
 
package/index.js CHANGED
@@ -271,6 +271,7 @@ function buildAPI(globalOptions, html, jar) {
271
271
  });
272
272
  },
273
273
  getHealthMetrics: function(){ return ctx.health ? ctx.health.snapshot() : null; },
274
+ getMqttDiagnostics: function(){ return ctx.getMqttDiagnostics ? ctx.getMqttDiagnostics() : (ctx._mqttDiag || null); },
274
275
  enableLazyPreflight(enable=true){ ctx.globalOptions.disablePreflight = !enable; },
275
276
  setBackoffOptions(opts={}){ ctx.globalOptions.backoff = Object.assign(ctx.globalOptions.backoff||{}, opts); },
276
277
  setEditOptions(opts={}){ Object.assign(ctx.globalOptions.editSettings, opts); },
@@ -296,6 +297,15 @@ function buildAPI(globalOptions, html, jar) {
296
297
  api[v.replace(".js", "")] = require("./src/" + v)(defaultFuncs, api, ctx);
297
298
  });
298
299
  api.listen = api.listenMqtt;
300
+ // Adaptive outbound pacing wrapper (dynamic risk + post-maintenance window)
301
+ if (!api._adaptivePacingWrapped && typeof api.sendMessage === 'function') {
302
+ const _origSend = api.sendMessage;
303
+ api.sendMessage = async function(message, threadID, callback){
304
+ try { if (globalSafety && typeof globalSafety.applyAdaptiveSendDelay === 'function') await globalSafety.applyAdaptiveSendDelay(); } catch(_) {}
305
+ return _origSend(message, threadID, callback);
306
+ };
307
+ api._adaptivePacingWrapped = true;
308
+ }
299
309
  // Safety wrapper: ensure every inbound MQTT event updates safety lastEvent timestamp
300
310
  if (!api._safetyWrappedListen) {
301
311
  const _origListen = api.listenMqtt;
@@ -505,21 +515,10 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
505
515
  logger('✅ Session authenticated successfully', 'info');
506
516
  // Initialize safety monitoring
507
517
  globalSafety.startMonitoring(ctx, api);
508
- // Schedule mid-session lightweight token poke (~ every 6h ±40m) to keep cookies warm
509
- if(!globalOptions._lightRefreshTimer){
510
- const scheduleLight = () => {
511
- const base = 6 * 60 * 60 * 1000; // 6h
512
- const jitter = (Math.random()*80 - 40) * 60 * 1000; // ±40m
513
- globalOptions._lightRefreshTimer = setTimeout(async () => {
514
- try {
515
- if(api && typeof api.refreshFb_dtsg === 'function'){
516
- await api.refreshFb_dtsg().catch(()=>{});
517
- }
518
- } catch(_) {}
519
- scheduleLight();
520
- }, base + jitter);
521
- };
522
- scheduleLight();
518
+ try { globalSafety.startDynamicSystems(); } catch(_) {}
519
+ // Consolidated: delegate light poke to unified safety module (prevents duplicate refresh scheduling)
520
+ if (globalSafety && typeof globalSafety.scheduleLightPoke === 'function') {
521
+ globalSafety.scheduleLightPoke();
523
522
  }
524
523
  // Post-login identity banner
525
524
  try {
@@ -34,6 +34,12 @@ class HealthMetrics {
34
34
  this.groupQueueExpiredQueues = 0;
35
35
  this.groupQueueDroppedMessages = 0;
36
36
  this.pendingEditSweeps = 0;
37
+ // Delivery receipt metrics
38
+ this.deliveryAttempts = 0;
39
+ this.deliverySuccess = 0;
40
+ this.deliveryFailed = 0;
41
+ this.deliveryTimeouts = 0;
42
+ this.deliveryDisabledSince = 0; // timestamp if adaptive disable engaged
37
43
  }
38
44
  onConnect() { this.lastConnectTs = Date.now(); this.consecutiveFailures = 0; }
39
45
  onDisconnect() { this.lastDisconnectTs = Date.now(); }
@@ -57,6 +63,7 @@ class HealthMetrics {
57
63
  this.p95AckLatencyMs = sorted[idx];
58
64
  }
59
65
  onError(type){ this.lastErrorTs = Date.now(); this.lastErrorType = type || 'unknown'; }
66
+ incFailure(){ this.consecutiveFailures++; }
60
67
  onReconnectScheduled(delay){ this.reconnects++; this.currentBackoffDelay = delay; if(delay > (this.maxObservedBackoff||0)) this.maxObservedBackoff = delay; }
61
68
  trackOutbound(depth){ this.outboundQueueDepth = depth; }
62
69
  incOutboundDropped(){ this.outboundQueueDropped++; }
@@ -114,6 +121,11 @@ class HealthMetrics {
114
121
  groupQueueExpiredQueues: this.groupQueueExpiredQueues,
115
122
  groupQueueDroppedMessages: this.groupQueueDroppedMessages,
116
123
  pendingEditSweeps: this.pendingEditSweeps,
124
+ deliveryAttempts: this.deliveryAttempts,
125
+ deliverySuccess: this.deliverySuccess,
126
+ deliveryFailed: this.deliveryFailed,
127
+ deliveryTimeouts: this.deliveryTimeouts,
128
+ deliveryDisabled: !!this.deliveryDisabledSince,
117
129
  healthy: this.isHealthy(idleMs)
118
130
  };
119
131
  }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ // Lightweight diagnostics helper for MQTT/WebSocket connection lifecycle.
3
+ // Captures low-level events so we can understand repeated reconnect loops.
4
+ module.exports = function attachMqttDiagnostics(ws, ctx, log){
5
+ if(!ws || typeof ws.on !== 'function') return;
6
+ const diag = ctx._mqttDiag = ctx._mqttDiag || { attempts:0, events:[] };
7
+ function push(evt){
8
+ try {
9
+ diag.events.push({ t: Date.now(), ...evt });
10
+ if(diag.events.length > 50) diag.events.shift();
11
+ } catch(_) {}
12
+ }
13
+ ws.on('upgrade', (res)=>{ push({ type:'upgrade', status: res.statusCode, headers: safeHeaders(res.headers) }); });
14
+ ws.on('unexpected-response', (req, res)=>{ push({ type:'unexpected_response', status: res && res.statusCode, headers: res && safeHeaders(res.headers) }); });
15
+ ws.on('close', (code, reason)=>{ push({ type:'close', code, reason: reason && reason.toString() }); });
16
+ ws.on('error', (err)=>{ push({ type:'error', message: err && (err.message||err.code||'').toString(), code: err && err.code }); });
17
+ function safeHeaders(h){ if(!h) return {}; const out={}; for(const k of Object.keys(h)){ if(k.startsWith('cookie')) continue; out[k]=h[k]; } return out; }
18
+ // expose a snapshot method
19
+ ctx.getMqttDiagnostics = () => ({ attempts: diag.attempts, recent: [...diag.events] });
20
+ };
@@ -76,6 +76,17 @@ class FacebookSafety {
76
76
  this._ghostChecking = false;
77
77
  // Periodic recycle timer
78
78
  this._periodicRecycleTimer = null;
79
+ // Consolidation additions
80
+ this._lastRefreshTs = 0; // track last successful refresh-like action
81
+ this._lastRecycleTs = 0;
82
+ this._lastLightPokeTs = 0;
83
+ this._timerRegistry = new Set();
84
+ this._minSpacingMs = 45 * 60 * 1000; // 45m guard between heavy/light actions
85
+ // Adaptive pacing + dynamic tuning additions
86
+ this._lastHeavyMaintenanceTs = 0; // last refresh OR successful reconnect
87
+ this._adaptivePacingWindowMs = 2 * 60 * 1000; // apply outbound pacing first 2m after heavy maintenance
88
+ this._dynamicHeartbeatTimer = null; // replaces fixed interval heartbeat for risk-tier tuning
89
+ this._riskLast = 'low';
79
90
 
80
91
  this.initSafety();
81
92
  }
@@ -282,10 +293,12 @@ class FacebookSafety {
282
293
  maxMs = 5 * 60 * 60 * 1000; // 5h
283
294
  }
284
295
  const interval = minMs + Math.random() * (maxMs - minMs);
285
- this._safeRefreshTimer = setTimeout(async () => {
296
+ const t = setTimeout(async () => {
286
297
  await this.refreshSafeSession();
287
298
  schedule();
288
299
  }, interval);
300
+ this._registerTimer(t);
301
+ this._safeRefreshTimer = t;
289
302
  };
290
303
  schedule();
291
304
  }
@@ -305,13 +318,17 @@ class FacebookSafety {
305
318
  updateRiskLevel() {
306
319
  const timeSinceLastActivity = Date.now() - this.sessionMetrics.lastActivity;
307
320
  const errorRate = this.sessionMetrics.errorCount / Math.max(1, this.sessionMetrics.requestCount);
308
-
321
+ let next;
309
322
  if (errorRate > 0.3 || timeSinceLastActivity < 1000) {
310
- this.sessionMetrics.riskLevel = 'high';
323
+ next = 'high';
311
324
  } else if (errorRate > 0.1 || timeSinceLastActivity < 5000) {
312
- this.sessionMetrics.riskLevel = 'medium';
325
+ next = 'medium';
313
326
  } else {
314
- this.sessionMetrics.riskLevel = 'low';
327
+ next = 'low';
328
+ }
329
+ if (next !== this.sessionMetrics.riskLevel) {
330
+ this.sessionMetrics.riskLevel = next;
331
+ this._onRiskLevelChanged(next);
315
332
  }
316
333
  }
317
334
 
@@ -371,10 +388,7 @@ class FacebookSafety {
371
388
  const now = Date.now();
372
389
  if (now < this._backoff.next) { return; }
373
390
  const attempt = ++this._backoff.attempt;
374
- // Stealth backoff: 1.2s * 1.8^n capped ~20s, add jitter 0-500ms
375
- const baseDelay = Math.min(20000, 1200 * Math.pow(1.8, Math.min(attempt, 6)));
376
- const jitter = Math.random() * 500;
377
- const delay = baseDelay + jitter;
391
+ const delay = this._computeBackoffDelay(attempt);
378
392
  this._backoff.next = now + delay;
379
393
  await new Promise(r => setTimeout(r, delay));
380
394
  if (this._activeListenerStop && typeof this._activeListenerStop === 'function') { try { this._activeListenerStop(); } catch(_) {} }
@@ -382,6 +396,7 @@ class FacebookSafety {
382
396
  const stop = this.api.listenMqtt((err, event) => { if (!err && event) this.recordEvent(); });
383
397
  this._activeListenerStop = stop;
384
398
  this.safetyEmit('mqttReconnect', { success: true, reason, attempt, delay });
399
+ this._markHeavyMaintenance();
385
400
  }
386
401
  setTimeout(() => {
387
402
  if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) { this._backoff.attempt = 0; }
@@ -405,11 +420,22 @@ class FacebookSafety {
405
420
  const base = 6 * 60 * 60 * 1000; // 6h
406
421
  const jitter = (Math.random() * 60 - 30) * 60 * 1000; // ±30m
407
422
  const delay = base + jitter;
408
- this._periodicRecycleTimer = setTimeout(() => {
423
+ const t = setTimeout(() => {
409
424
  if (this._destroyed) return;
425
+ // Suppress recycle if a refresh/poke just happened inside spacing window
426
+ if (Date.now() - this._lastRefreshTs < this._minSpacingMs) {
427
+ // reschedule shorter backoff (add 20m) to avoid clustering
428
+ const defer = 20 * 60 * 1000 + Math.random() * 10 * 60 * 1000; // 20–30m
429
+ const dt = setTimeout(()=> this._schedulePeriodicRecycle(), defer);
430
+ this._registerTimer(dt);
431
+ return;
432
+ }
433
+ this._lastRecycleTs = Date.now();
410
434
  this.forceReconnect('periodic');
411
435
  this._schedulePeriodicRecycle();
412
436
  }, delay);
437
+ this._registerTimer(t);
438
+ this._periodicRecycleTimer = t;
413
439
  }
414
440
 
415
441
  // Heartbeat ping & watchdog
@@ -504,6 +530,10 @@ class FacebookSafety {
504
530
  async refreshSafeSession() {
505
531
  // Improved safe session refresh implementation
506
532
  if (this._refreshing) return; // prevent concurrent refreshes
533
+ // Collision guard – skip if a refresh/poke happened very recently
534
+ if (Date.now() - this._lastRefreshTs < this._minSpacingMs / 2) {
535
+ return;
536
+ }
507
537
  this._refreshing = true;
508
538
  const refreshId = ++this._inFlightRefreshId;
509
539
  const startedAt = Date.now();
@@ -532,6 +562,8 @@ class FacebookSafety {
532
562
  durationMs: Date.now() - startedAt,
533
563
  message: 'Session tokens refreshed'
534
564
  });
565
+ this._lastRefreshTs = Date.now();
566
+ this._markHeavyMaintenance();
535
567
  // Immediate MQTT health ensure
536
568
  await this._ensureMqttAlive();
537
569
  // Schedule layered post-refresh checks (1s, 10s, 30s) to catch silent drops
@@ -570,11 +602,51 @@ class FacebookSafety {
570
602
  }
571
603
  }
572
604
 
605
+ /**
606
+ * Lightweight poke (fb_dtsg refresh only) integrated to remove duplicate logic in index.js
607
+ */
608
+ scheduleLightPoke() {
609
+ if (this._lightPokeTimer || this._destroyed) return;
610
+ const base = 6 * 60 * 60 * 1000; // 6h
611
+ const jitter = (Math.random()*80 - 40) * 60 * 1000; // ±40m
612
+ const schedule = () => {
613
+ if (this._destroyed) return;
614
+ const t = setTimeout(async () => {
615
+ if (this._destroyed) return;
616
+ // Respect spacing: skip if recent heavy refresh
617
+ if (Date.now() - this._lastRefreshTs < this._minSpacingMs / 2) {
618
+ schedule();
619
+ return;
620
+ }
621
+ try {
622
+ if (this.api && typeof this.api.refreshFb_dtsg === 'function') {
623
+ await this.api.refreshFb_dtsg().catch(()=>{});
624
+ this._lastRefreshTs = Date.now();
625
+ this._lastLightPokeTs = Date.now();
626
+ this.safetyEmit('lightPoke', { ts: Date.now() });
627
+ }
628
+ } catch(_) {}
629
+ schedule();
630
+ }, base + (Math.random()*80 - 40) * 60 * 1000);
631
+ this._registerTimer(t);
632
+ this._lightPokeTimer = t;
633
+ };
634
+ schedule();
635
+ }
636
+
637
+ _registerTimer(t){
638
+ if (!t) return;
639
+ this._timerRegistry.add(t);
640
+ }
641
+
573
642
  // Cleanup / destroy resources (to prevent dangling timers)
574
643
  destroy() {
575
644
  this._destroyed = true;
576
- const timers = [this._safeRefreshInterval, this._safeRefreshTimer, this._heartbeatTimer, this._watchdogTimer, this._periodicRecycleTimer];
645
+ const timers = [this._safeRefreshInterval, this._safeRefreshTimer, this._heartbeatTimer, this._watchdogTimer, this._periodicRecycleTimer, this._lightPokeTimer];
577
646
  timers.forEach(t => t && clearTimeout(t));
647
+ // Clear any registered anonymous timers
648
+ this._timerRegistry.forEach(t => clearTimeout(t));
649
+ this._timerRegistry.clear();
578
650
  if (this._activeListenerStop) {
579
651
  try { this._activeListenerStop(); } catch (_) {}
580
652
  this._activeListenerStop = null;
@@ -622,6 +694,99 @@ class FacebookSafety {
622
694
  }
623
695
  }
624
696
 
697
+ /* ======================== Dynamic Tuning & Pacing ======================== */
698
+ _onRiskLevelChanged(risk){
699
+ // Adjust spacing guard slightly (high risk allow earlier refresh to recover)
700
+ if (risk === 'high') this._minSpacingMs = 30 * 60 * 1000; else this._minSpacingMs = 45 * 60 * 1000;
701
+ // Reschedule heartbeat dynamically
702
+ this._scheduleDynamicHeartbeat(true);
703
+ this.safetyEmit('riskLevelChanged', { risk });
704
+ }
705
+
706
+ _computeBackoffDelay(attempt){
707
+ const risk = this.sessionMetrics.riskLevel;
708
+ const a = Math.min(attempt, 6);
709
+ let base;
710
+ if (risk === 'high') {
711
+ base = 900 * Math.pow(1.6, a); // faster recovery
712
+ } else if (risk === 'medium') {
713
+ base = 1100 * Math.pow(1.7, a);
714
+ } else { // low
715
+ base = 1500 * Math.pow(1.9, a); // slower to reduce noise
716
+ }
717
+ const cap = (risk === 'low') ? 25000 : (risk === 'medium' ? 22000 : 18000);
718
+ const delay = Math.min(cap, base) + Math.random()*600; // jitter
719
+ return delay;
720
+ }
721
+
722
+ _scheduleDynamicHeartbeat(reset){
723
+ if (reset && this._dynamicHeartbeatTimer){ clearTimeout(this._dynamicHeartbeatTimer); this._dynamicHeartbeatTimer = null; }
724
+ if (this._destroyed) return;
725
+ const interval = this._computeHeartbeatInterval();
726
+ this._dynamicHeartbeatTimer = setTimeout(()=>{
727
+ if (this._destroyed) return;
728
+ try {
729
+ if (this.ctx && this.ctx.mqttClient && this.ctx.mqttClient.connected) {
730
+ if (this.ctx.mqttClient.ping) this.ctx.mqttClient.ping();
731
+ try { this.ctx.mqttClient.publish('/foreground_state', JSON.stringify({ foreground: true })); } catch(_) {}
732
+ this.safetyEmit('heartbeat', { ts: Date.now(), dynamic: true });
733
+ }
734
+ } catch(_) {}
735
+ // Watchdog like check
736
+ this._runDynamicWatchdog();
737
+ this._scheduleDynamicHeartbeat(false);
738
+ }, interval);
739
+ this._registerTimer(this._dynamicHeartbeatTimer);
740
+ }
741
+
742
+ _computeHeartbeatInterval(){
743
+ const risk = this.sessionMetrics.riskLevel;
744
+ if (risk === 'high') return (55 + Math.random()*20) * 1000; // 55–75s
745
+ if (risk === 'medium') return (70 + Math.random()*20) * 1000; // 70–90s
746
+ return (80 + Math.random()*20) * 1000; // 80–100s
747
+ }
748
+
749
+ _runDynamicWatchdog(){
750
+ const idle = Date.now() - this._lastEventTs;
751
+ // escalate thresholds slightly by risk (high risk shorter tolerance)
752
+ const hard = (this.sessionMetrics.riskLevel === 'high') ? 8*60*1000 : 12*60*1000;
753
+ if (idle > hard) {
754
+ this._backoff.attempt = 0;
755
+ this._ensureMqttAlive();
756
+ }
757
+ }
758
+
759
+ _markHeavyMaintenance(){
760
+ this._lastHeavyMaintenanceTs = Date.now();
761
+ }
762
+
763
+ computeAdaptiveSendDelay(){
764
+ const risk = this.sessionMetrics.riskLevel;
765
+ const since = Date.now() - this._lastHeavyMaintenanceTs;
766
+ const inWindow = since < this._adaptivePacingWindowMs;
767
+ let min=0, max=0;
768
+ if (inWindow){
769
+ if (risk === 'high'){ min=600; max=1500; }
770
+ else if (risk === 'medium'){ min=200; max=800; }
771
+ else { min=0; max=300; }
772
+ } else {
773
+ // outside pacing window only high risk adds mild delay
774
+ if (risk === 'high'){ min=150; max=600; }
775
+ }
776
+ if (max<=0) return 0;
777
+ return Math.floor(min + Math.random()*(max-min));
778
+ }
779
+
780
+ applyAdaptiveSendDelay(){
781
+ const d = this.computeAdaptiveSendDelay();
782
+ if (!d) return Promise.resolve();
783
+ return new Promise(r=> setTimeout(r, d));
784
+ }
785
+
786
+ startDynamicSystems(){
787
+ this._scheduleDynamicHeartbeat(true);
788
+ }
789
+
625
790
  /**
626
791
  * Set safety event handler
627
792
  */
@@ -630,4 +795,4 @@ class FacebookSafety {
630
795
  }
631
796
  }
632
797
 
633
- module.exports = FacebookSafety;
798
+ module.exports = FacebookSafety;
@@ -13,7 +13,8 @@ class FacebookSafetyManager extends EventEmitter {
13
13
  constructor(options = {}) {
14
14
  super();
15
15
 
16
- this.options = {
16
+ console.warn('[DEPRECATION] FacebookSafetyManager is deprecated. The unified FacebookSafety module now handles all safety logic. Avoid using this manager.');
17
+ this.options = {
17
18
  // Auto re-login detection
18
19
  autoReloginEnabled: options.autoReloginEnabled !== false,
19
20
  autoReloginRetries: options.autoReloginRetries || 3,
@@ -112,7 +113,7 @@ class FacebookSafetyManager extends EventEmitter {
112
113
  this.startUserAgentRotation();
113
114
  }
114
115
 
115
- logger('🛡️ Facebook Safety Manager initialized with maximum protection', 'info');
116
+ logger('🛡️ (Deprecated) Facebook Safety Manager initialized (prefer unified FacebookSafety)', 'info');
116
117
  }
117
118
 
118
119
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nexus-fca",
3
- "version": "2.1.8",
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.",
3
+ "version": "3.0.0",
4
+ "description": "Nexus-FCA 3.0 – stable, low-risk Facebook Messenger automation API with integrated secure login (ID / Password / 2FA), adaptive MQTT core, safety orchestration, metrics, and TypeScript support.",
5
5
  "main": "index.js",
6
6
  "repository": {
7
7
  "type": "git",
package/src/listenMqtt.js CHANGED
@@ -166,9 +166,15 @@ function buildStream(options, WebSocket, Proxy) {
166
166
  return Stream;
167
167
  }
168
168
  function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
169
+ const attemptStartTs = Date.now();
169
170
  // Attach health metrics container lazily
170
171
  if(!ctx.health) ctx.health = new (require('../lib/health/HealthMetrics').HealthMetrics)();
172
+ // Ensure tasks map exists to track ls_req -> ls_resp correlations (avoid TypeError on undefined)
173
+ if(!ctx.tasks) ctx.tasks = new Map();
171
174
  const backoff = getBackoffState(ctx);
175
+ if(!ctx._mqttDiag) ctx._mqttDiag = { attempts:0, events:[] };
176
+ ctx._mqttDiag.attempts++;
177
+ log.info('listenMqtt', `Attempt #${ctx._mqttDiag.attempts} starting (backoffCurrent=${backoff.current||0})`);
172
178
  const runPreflight = shouldRunPreflight(ctx);
173
179
  if (runPreflight) {
174
180
  (async () => {
@@ -263,20 +269,27 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
263
269
  log.error("listenMqtt", `Failed to create proxy agent: ${error.message}`);
264
270
  }
265
271
  }
272
+ // Create raw WebSocket first so we can attach diagnostics hooks.
273
+ const rawWs = new WebSocket(host, options.wsOptions);
274
+ try { require('../lib/mqtt/MqttDiagnostics')(rawWs, ctx, log); } catch(_) {}
266
275
  ctx.mqttClient = new mqtt.Client(
267
- () =>
268
- buildStream(
269
- options,
270
- new WebSocket(host, options.wsOptions),
271
- buildProxy()
272
- ),
276
+ () => buildStream(options, rawWs, buildProxy()),
273
277
  options
274
278
  );
279
+ log.info('listenMqtt', `Connecting to ${host}`);
275
280
  const mqttClient = ctx.mqttClient;
276
281
  global.mqttClient = mqttClient;
277
282
  mqttClient.on('error', function (err) {
278
283
  const errMsg = (err && (err.error || err.message || "")).toString();
279
284
  ctx.health.onError(errMsg.includes('not logged in') ? 'not_logged_in' : 'mqtt_error');
285
+ // Increment failure counter for health tracking
286
+ if(ctx.health && typeof ctx.health.incFailure === 'function') ctx.health.incFailure();
287
+ if(!errMsg){
288
+ 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;},{})));
289
+ }
290
+ else {
291
+ log.error('listenMqtt', `MQTT error after ${(Date.now()-attemptStartTs)}ms: ${errMsg}`);
292
+ }
280
293
  log.error("listenMqtt", errMsg);
281
294
  try { mqttClient.end(true); } catch(_){ }
282
295
  if (/not logged in|login_redirect|html_login_page/i.test(errMsg)) {
@@ -294,6 +307,8 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
294
307
  // Ensure reconnection also triggers on unexpected close without prior error
295
308
  mqttClient.on('close', function () {
296
309
  ctx.health.onDisconnect();
310
+ if(ctx.health && typeof ctx.health.incFailure === 'function'){ ctx.health.incFailure(); }
311
+ log.warn('listenMqtt', `Socket closed after ${(Date.now()-attemptStartTs)}ms (attempt #${ctx._mqttDiag.attempts}).`);
297
312
  if (!ctx.loggedIn) return; // avoid loops if logged out
298
313
  if (ctx.globalOptions.autoReconnect) {
299
314
  scheduleAdaptiveReconnect(defaultFuncs, api, ctx, globalCallback);
@@ -301,6 +316,8 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
301
316
  });
302
317
  mqttClient.on('disconnect', function(){
303
318
  ctx.health.onDisconnect();
319
+ if(ctx.health && typeof ctx.health.incFailure === 'function'){ ctx.health.incFailure(); }
320
+ log.warn('listenMqtt', `MQTT disconnect event after ${(Date.now()-attemptStartTs)}ms (attempt #${ctx._mqttDiag.attempts}).`);
304
321
  if (!ctx.loggedIn) return;
305
322
  if (ctx.globalOptions.autoReconnect) {
306
323
  scheduleAdaptiveReconnect(defaultFuncs, api, ctx, globalCallback);
@@ -309,6 +326,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
309
326
  mqttClient.on("connect", function () {
310
327
  resetBackoff(backoff);
311
328
  ctx.health.onConnect();
329
+ log.info('listenMqtt', `Connected in ${(Date.now()-attemptStartTs)}ms (attempt #${ctx._mqttDiag.attempts}).`);
312
330
  if (ctx.globalSafety) { try { ctx.globalSafety.recordEvent(); } catch(_) {} }
313
331
  if (process.env.OnStatus === undefined) {
314
332
  logger("Nexus-FCA premium features works only with Nexus-Bot framework(Kidding)", "info");
@@ -418,7 +436,8 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
418
436
  } else if (topic == "/ls_resp") {
419
437
  const parsedPayload = JSON.parse(jsonMessage.payload);
420
438
  const reqID = jsonMessage.request_id;
421
- if (ctx["tasks"].has(reqID)) {
439
+ // Guard: ctx.tasks may be empty; only proceed if it's a Map and contains the reqID
440
+ if (ctx.tasks && typeof ctx.tasks.has === 'function' && ctx.tasks.has(reqID)) {
422
441
  const taskData = ctx["tasks"].get(reqID);
423
442
  const { type: taskType, callback: taskCallback } = taskData;
424
443
  const taskRespData = getTaskResponseData(taskType, parsedPayload);
@@ -30,28 +30,61 @@ module.exports = function (defaultFuncs, api, ctx) {
30
30
  form["message_ids[0]"] = messageID;
31
31
  form["thread_ids[" + threadID + "][0]"] = messageID;
32
32
 
33
- defaultFuncs
34
- .post(
35
- "https://www.facebook.com/ajax/mercury/delivery_receipts.php",
36
- ctx.jar,
37
- form
38
- )
39
- .then(utils.saveCookies(ctx.jar))
40
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
41
- .then(function (resData) {
42
- if (resData.error) {
43
- throw resData;
44
- }
45
-
33
+ // Lightweight retry with exponential backoff for transient network timeouts.
34
+ const maxAttempts = 3;
35
+ let attempt = 0;
36
+ const baseDelay = 500; // ms
37
+ const transientCodes = ['ETIMEDOUT','ECONNRESET','EAI_AGAIN'];
38
+ if(ctx.health){
39
+ ctx.health.deliveryAttempts++;
40
+ // If we previously disabled delivery receipts due to repeated timeouts, short-circuit success.
41
+ if(ctx.health.deliveryDisabledSince){
46
42
  return callback();
47
- })
48
- .catch(function (err) {
49
- log.error("markAsDelivered", err);
50
- if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
51
- ctx.loggedIn = false;
52
- }
53
- return callback(err);
54
- });
43
+ }
44
+ }
45
+ function doPost(){
46
+ attempt++;
47
+ defaultFuncs
48
+ .post(
49
+ "https://www.facebook.com/ajax/mercury/delivery_receipts.php",
50
+ ctx.jar,
51
+ form
52
+ )
53
+ .then(utils.saveCookies(ctx.jar))
54
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
55
+ .then(function (resData) {
56
+ if (resData.error) { throw resData; }
57
+ if(ctx.health){ ctx.health.deliverySuccess++; }
58
+ return callback();
59
+ })
60
+ .catch(function (err) {
61
+ const code = err && (err.code || err.errno || (err.error && err.error.code));
62
+ const isTransient = code && transientCodes.includes(code);
63
+ if(code === 'ETIMEDOUT' && ctx.health){ ctx.health.deliveryTimeouts++; }
64
+ if(isTransient && attempt < maxAttempts){
65
+ const delay = Math.round(baseDelay * Math.pow(2, attempt-1) * (1 + Math.random()*0.2));
66
+ log.warn('markAsDelivered', `Transient ${code} attempt ${attempt}/${maxAttempts} -> retrying in ${delay}ms`);
67
+ return setTimeout(doPost, delay);
68
+ }
69
+ // Suppress noisy timeout logs after final retry unless verbose
70
+ if(!(isTransient && attempt >= maxAttempts)){
71
+ log.error("markAsDelivered", err);
72
+ }else{
73
+ log.warn('markAsDelivered', `Giving up after ${attempt} attempts (${code})`);
74
+ // Adaptive disable: if too many timeouts overall, stop calling delivery receipts for this run
75
+ if(code === 'ETIMEDOUT' && ctx.health && ctx.health.deliveryTimeouts >= 5){
76
+ ctx.health.deliveryDisabledSince = Date.now();
77
+ log.warn('markAsDelivered', 'Adaptive disable engaged after repeated ETIMEDOUT. Further receipts suppressed.');
78
+ }
79
+ }
80
+ if (utils.getType(err) == "Object" && err.error === "Not logged in.") {
81
+ ctx.loggedIn = false;
82
+ }
83
+ if(ctx.health){ ctx.health.deliveryFailed++; }
84
+ return callback(err);
85
+ });
86
+ }
87
+ doPost();
55
88
 
56
89
  return returnPromise;
57
90
  };