@lazyneoaz/metachat 1.0.3 → 1.0.5
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.
|
|
3
|
+
"version": "1.0.5",
|
|
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
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
|
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
|
|
450
|
-
|
|
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);
|
|
@@ -512,6 +523,7 @@ module.exports = {
|
|
|
512
523
|
AntiSuspension,
|
|
513
524
|
globalAntiSuspension,
|
|
514
525
|
SUSPENSION_SIGNALS,
|
|
526
|
+
SESSION_EXPIRY_SIGNALS,
|
|
515
527
|
initAntiSuspension: () => globalAntiSuspension,
|
|
516
528
|
getAntiSuspensionConfig: () => globalAntiSuspension.getConfig()
|
|
517
529
|
};
|
package/src/utils/autoReLogin.js
CHANGED
|
@@ -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
|
|
package/src/utils/axios.js
CHANGED
|
@@ -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('"
|
|
97
|
+
bodyStr.includes('id="email" name="email"');
|
|
96
98
|
|
|
97
99
|
const isLoginBlocked =
|
|
98
100
|
typeof body === 'object' && body !== null && body.error === 1357001;
|
package/src/utils/clients.js
CHANGED
|
@@ -197,7 +197,11 @@ function parseAndCheckLogin(ctx, http, retryCount = 0) {
|
|
|
197
197
|
log("Please verify your Facebook account status in a browser.");
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
// Only treat a redirect to login.php as a session expiry if the server
|
|
201
|
+
// explicitly told us to go there via res.redirect — not if login.php appears
|
|
202
|
+
// anywhere in the body JSON, which it does on authenticated pages as a
|
|
203
|
+
// navigation/share link, causing false-positive session expiry errors.
|
|
204
|
+
if (String(res.redirect || "").includes("login.php")) {
|
|
201
205
|
warn("Session Status", "Redirected to login page - Session expired");
|
|
202
206
|
const err = new Error("Session expired - Redirected to login page");
|
|
203
207
|
err.error = 401;
|
|
@@ -230,11 +234,19 @@ function saveCookies(jar) {
|
|
|
230
234
|
return function (res) {
|
|
231
235
|
const cookies = res.headers["set-cookie"] || [];
|
|
232
236
|
cookies.forEach(function (c) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
// Always attempt to save every Set-Cookie header to both domains.
|
|
238
|
+
// The old guard `c.indexOf(".facebook.com") > -1` silently dropped
|
|
239
|
+
// cookies whose domain attribute was `facebook.com` (no dot),
|
|
240
|
+
// `www.facebook.com`, or absent entirely. Facebook rotates critical
|
|
241
|
+
// session cookies (xs, c_user, fr, etc.) continuously — missing a
|
|
242
|
+
// single update causes the jar to go stale and Facebook forces logout.
|
|
243
|
+
try { jar.setCookie(c, "https://www.facebook.com"); } catch (_) {}
|
|
244
|
+
|
|
245
|
+
// Mirror to messenger.com so MQTT / Messenger API calls stay authed.
|
|
246
|
+
const c2 = c
|
|
247
|
+
.replace(/domain=[^;]*/i, "domain=.messenger.com")
|
|
248
|
+
.replace(/secure;?\s*/i, "");
|
|
249
|
+
try { jar.setCookie(c2, "https://www.messenger.com"); } catch (_) {}
|
|
238
250
|
});
|
|
239
251
|
return res;
|
|
240
252
|
};
|