@lazyneoaz/metachat 1.0.2 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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.",
@@ -443,14 +443,12 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
443
443
  return resolve(false);
444
444
  }
445
445
 
446
- // Presence endpoint echoes the user ID when the session is live.
447
- const uid = ctx.userID.toString();
448
- const hasUserID = html.includes(uid) ||
449
- html.includes(`"actorID":"${uid}"`) ||
450
- html.includes(`"userID":"${uid}"`) ||
451
- html.includes(`"ACCOUNT_ID":"${uid}"`);
452
-
453
- resolve(hasUserID);
446
+ // If we got a non-empty response that is neither a login page nor
447
+ // a checkpoint, the session is alive. The presence endpoint does
448
+ // NOT reliably echo the user ID in every response variant — a
449
+ // UID-presence check here produces false negatives that trigger
450
+ // unnecessary re-logins on perfectly valid sessions.
451
+ resolve(!!html);
454
452
  } catch (error) {
455
453
  utils.error("Session validation failed:", error.message);
456
454
  resolve(false);
@@ -47,10 +47,8 @@ const SUSPENSION_SIGNALS = [
47
47
  'temporarily blocked',
48
48
  'temporarily_blocked',
49
49
  'your account has been temporarily blocked',
50
- 'please try again later',
51
50
  'feature temporarily blocked',
52
51
  'feature temporarily unavailable',
53
- 'something went wrong',
54
52
  'automated behavior',
55
53
  'not a human',
56
54
  'bot detected',
@@ -66,7 +64,13 @@ const SUSPENSION_SIGNALS = [
66
64
  'blocked from sending',
67
65
  'disabled for violating',
68
66
  'policy violation',
69
- 'action blocked',
67
+ 'action blocked'
68
+ ];
69
+
70
+ // Session-expiry phrases that must NOT be treated as suspension signals.
71
+ // Confusing session expiry with suspension trips the circuit breaker and
72
+ // prevents re-login, turning a normal logout into a permanent lockout.
73
+ const SESSION_EXPIRY_SIGNALS = [
70
74
  'session expired',
71
75
  'session has expired',
72
76
  'not logged in',
@@ -441,13 +445,20 @@ class AntiSuspension {
441
445
  /**
442
446
  * Prepare before sending — single delay model.
443
447
  * Enforces thread throttle and volume limits, respects circuit breaker.
444
- * If volume limits are reached, throws to protect the account.
448
+ * If the circuit breaker is tripped (checkpoint/suspension detected) or
449
+ * volume limits are reached, throws to protect the account.
445
450
  */
446
451
  async prepareBeforeMessage(threadID, message) {
447
452
  if (this.isCircuitBreakerTripped()) {
448
453
  const remaining = this.getCircuitBreakerRemainingMs();
449
- const waitMs = Math.min(remaining, 8000);
450
- if (waitMs > 0) await new Promise(resolve => setTimeout(resolve, waitMs));
454
+ const { utils } = this._getUtils();
455
+ utils && utils.warn && utils.warn("AntiSuspension",
456
+ `Circuit breaker is tripped. Blocking message send to protect account. ` +
457
+ `Cooldown remaining: ${Math.ceil(remaining / 1000)}s`);
458
+ const err = new Error(`Circuit breaker is tripped. Pausing sends to protect account. Retry in ${Math.ceil(remaining / 1000)}s.`);
459
+ err.error = 'circuit_breaker_tripped';
460
+ err.remainingMs = remaining;
461
+ throw err;
451
462
  }
452
463
 
453
464
  const volumeWarning = this.checkVolumeLimit(threadID);
@@ -459,7 +470,6 @@ class AntiSuspension {
459
470
  }
460
471
 
461
472
  await this.enforceThreadThrottling(threadID);
462
- await this.addAdaptiveDelay(threadID);
463
473
  this._incrementDailyStats(threadID);
464
474
  }
465
475
 
@@ -513,6 +523,7 @@ module.exports = {
513
523
  AntiSuspension,
514
524
  globalAntiSuspension,
515
525
  SUSPENSION_SIGNALS,
526
+ SESSION_EXPIRY_SIGNALS,
516
527
  initAntiSuspension: () => globalAntiSuspension,
517
528
  getAntiSuspensionConfig: () => globalAntiSuspension.getConfig()
518
529
  };
@@ -88,6 +88,12 @@ class AutoReLoginManager {
88
88
  if (this.onReLoginFailure) {
89
89
  this.onReLoginFailure(new Error("Max re-login retries exceeded"));
90
90
  }
91
+ // Schedule a reset so the next session-expiry event can try again
92
+ // instead of permanently blocking all future re-login attempts.
93
+ setTimeout(() => {
94
+ this.retryCount = 0;
95
+ utils.log("AutoReLogin", "Retry counter reset after cooldown — re-login re-enabled");
96
+ }, 5 * 60 * 1000); // 5-minute cooldown before allowing new attempts
91
97
  return false;
92
98
  }
93
99
 
@@ -86,13 +86,15 @@ async function inspectResponseForSessionIssues(adapted, ctx) {
86
86
  throw err;
87
87
  }
88
88
 
89
- // Detect session expiry / forced logout
89
+ // Detect session expiry / forced logout.
90
+ // Use only unambiguous structural markers that appear exclusively on the
91
+ // actual login page — NOT generic strings like '"login.php?"' or
92
+ // '"login_page"' which Facebook embeds in authenticated page payloads as
93
+ // navigation links and client-side route names, causing false positives.
90
94
  const isLoginRedirect =
91
- bodyStr.includes('"login.php?') ||
92
- bodyStr.includes('https://www.facebook.com/login.php') ||
93
95
  bodyStr.includes('<form id="login_form"') ||
94
96
  bodyStr.includes('id="loginbutton"') ||
95
- bodyStr.includes('"login_page"');
97
+ bodyStr.includes('id="email" name="email"');
96
98
 
97
99
  const isLoginBlocked =
98
100
  typeof body === 'object' && body !== null && body.error === 1357001;