@lazyneoaz/metachat 1.0.1 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "commonjs",
5
5
  "types": "src/types/index.d.ts",
6
6
  "description": "Advanced Facebook Chat API client for building Messenger bots — real-time messaging, thread management, MQTT, and session stability.",
@@ -54,7 +54,7 @@ async function buildAPI(html, jar, netData, globalOptions, fbLinkFunc, errorRetr
54
54
 
55
55
  const dtsgResult = {
56
56
  fb_dtsg: dtsg,
57
- jazoest: `2${Array.from(dtsg).reduce((a, b) => a + b.charCodeAt(0), '')}`,
57
+ jazoest: `2${Array.from(dtsg).reduce((a, b) => a + b.charCodeAt(0), 0)}`,
58
58
  lsd: lsd
59
59
  };
60
60
 
@@ -415,9 +415,11 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
415
415
  // Use the lightweight presence endpoint instead of fetching the
416
416
  // full homepage (~400 kB). Returns 200 JSON when authenticated,
417
417
  // 302→login when the session is expired.
418
+ // Do NOT send fb_dtsg_ag= (empty) — real browsers always send a
419
+ // real token here, so an empty value is a bot fingerprint.
418
420
  const resp = await utils.get(
419
- 'https://www.facebook.com/ajax/presence/reconnect.php?reason=14&fb_dtsg_ag=&__a=1',
420
- ctx.jar, null, ctx.globalOptions, { noRef: true }
421
+ 'https://www.facebook.com/ajax/presence/reconnect.php',
422
+ ctx.jar, { reason: '14', __a: '1' }, ctx.globalOptions, { noRef: true, _skipSessionInspect: true }
421
423
  );
422
424
  const html = resp.body || '';
423
425
 
@@ -83,8 +83,8 @@ class AntiSuspension {
83
83
  this.lastActivity = new Map();
84
84
  this.typing = new Map();
85
85
 
86
- this.messageDelayMs = 100;
87
- this.threadDelayMs = 200;
86
+ this.messageDelayMs = 300;
87
+ this.threadDelayMs = 400;
88
88
  this.loginAttempts = 0;
89
89
  this.maxLoginAttempts = 3;
90
90
  this.loginCooldown = 300000;
@@ -279,9 +279,11 @@ class AntiSuspension {
279
279
  }
280
280
 
281
281
  async addSmartDelay() {
282
- const base = 30 + Math.random() * 70;
283
- const jitter = (Math.random() - 0.5) * 20;
284
- const total = Math.max(20, base + jitter);
282
+ // Minimum realistic inter-request pause short enough not to block
283
+ // high-throughput flows but long enough to avoid sub-100ms bot patterns.
284
+ const base = 500 + Math.random() * 700;
285
+ const jitter = (Math.random() - 0.5) * 150;
286
+ const total = Math.max(350, base + jitter);
285
287
  await new Promise(resolve => setTimeout(resolve, total));
286
288
  }
287
289
 
@@ -293,14 +295,14 @@ class AntiSuspension {
293
295
  const threadCount = this.dailyStats.threadStats.get(String(threadID))?.count || 0;
294
296
  const globalCount = this.dailyStats.messageCount;
295
297
 
296
- let base = 30;
297
- if (globalCount > 800) base = 150;
298
- else if (globalCount > 400) base = 80;
298
+ let base = 120;
299
+ if (globalCount > 800) base = 500;
300
+ else if (globalCount > 400) base = 250;
299
301
 
300
- if (threadCount > 50) base += 50;
302
+ if (threadCount > 50) base += 180;
301
303
 
302
304
  const jitter = Math.random() * base * 0.4;
303
- const total = Math.max(20, base + jitter);
305
+ const total = Math.max(80, base + jitter);
304
306
  await new Promise(resolve => setTimeout(resolve, total));
305
307
  }
306
308
 
@@ -320,7 +322,7 @@ class AntiSuspension {
320
322
 
321
323
  async enforceMessageRate() {
322
324
  await new Promise(resolve =>
323
- setTimeout(resolve, this.messageDelayMs + Math.random() * 50)
325
+ setTimeout(resolve, this.messageDelayMs + Math.random() * 300)
324
326
  );
325
327
  }
326
328
 
@@ -457,7 +459,6 @@ class AntiSuspension {
457
459
  }
458
460
 
459
461
  await this.enforceThreadThrottling(threadID);
460
- await this.addAdaptiveDelay(threadID);
461
462
  this._incrementDailyStats(threadID);
462
463
  }
463
464
 
@@ -111,18 +111,19 @@ async function inspectResponseForSessionIssues(adapted, ctx) {
111
111
 
112
112
  if (!ctx.auto_login && typeof ctx.performAutoLogin === 'function') {
113
113
  ctx.auto_login = true;
114
+ // Always reset the flag in a finally block so a synchronous throw
115
+ // or a rejected promise can never leave auto_login stuck at true,
116
+ // which would permanently prevent future re-login attempts.
114
117
  try {
115
118
  const ok = await ctx.performAutoLogin();
116
- ctx.auto_login = false;
117
119
  if (!ok) {
118
120
  const err = new Error('Not logged in. Auto re-login failed.');
119
121
  err.error = 'Not logged in.';
120
122
  err.res = body;
121
123
  throw err;
122
124
  }
123
- } catch (autoErr) {
125
+ } finally {
124
126
  ctx.auto_login = false;
125
- throw autoErr;
126
127
  }
127
128
  } else {
128
129
  const err = new Error('Not logged in. Session has expired.');
@@ -95,6 +95,11 @@ class TokenRefreshManager {
95
95
  probeCtx
96
96
  );
97
97
 
98
+ // Save any rotated cookies Facebook returns (fr, xs, etc.) so the
99
+ // jar stays fresh. Dropping rotated cookies causes Facebook to
100
+ // treat the session as stale and forces a logout.
101
+ try { utils.saveCookies(ctx.jar)(resp); } catch (_) {}
102
+
98
103
  const body = resp.body;
99
104
  if (!body) return false;
100
105
 
@@ -188,6 +193,9 @@ class TokenRefreshManager {
188
193
  for (let i = 0; i < ctx.fb_dtsg.length; i++) {
189
194
  ctx.ttstamp += ctx.fb_dtsg.charCodeAt(i);
190
195
  }
196
+ // Recalculate jazoest whenever fb_dtsg changes — it must stay in sync.
197
+ // jazoest = "2" + sum-of-char-codes (numeric sum, not concatenation).
198
+ ctx.jazoest = `2${Array.from(ctx.fb_dtsg).reduce((a, b) => a + b.charCodeAt(0), 0)}`;
191
199
  } else {
192
200
  throw new Error("Failed to extract fb_dtsg token");
193
201
  }