hridoy-ultimate 9.0.3 → 9.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/CHANGELOG.md +45 -0
- package/DOCS.md +14 -6
- package/README.md +3 -2
- package/index.d.ts +5 -1
- package/module/config.js +4 -0
- package/module/loginHelper.js +23 -2
- package/package.json +11 -8
- package/src/api/action/getSafetyStatus.js +27 -0
- package/src/api/action/logout.js +70 -59
- package/src/api/action/logoutFromFacebook.js +86 -0
- package/src/api/action/refreshFb_dtsg.js +1 -1
- package/src/api/messaging/changeGroupImage.js +1 -0
- package/src/api/socket/OldMessage.js +4 -1
- package/src/api/socket/sendMessage.js +4 -1
- package/src/api/users/getUserID.js +1 -0
- package/src/app/createFcaClient.js +1 -0
- package/src/utils/antiSuspension.js +84 -24
- package/src/utils/cookies.js +29 -1
- package/src/utils/loginParser/parseAndCheckLogin.js +1 -19
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [9.0.5] - 2026-10-30
|
|
10
|
+
|
|
11
|
+
### Fixed (account / browser session safety)
|
|
12
|
+
|
|
13
|
+
- `api.logout()` used to POST `facebook.com/logout.php`. Facebook then ends that session everywhere, so the same account was also logged out in your normal browser whenever a bot called `logout()` (e.g. on shutdown/restart). `api.logout()` is now a **local stop only**: it stops the MQTT listener and the internal timers and sends nothing to Facebook. It resolves with `{ localOnly: true }`.
|
|
14
|
+
- The real logout is a separate method, `api.logoutFromFacebook()`. If you really want `api.logout()` to log out of Facebook, set `"allowFacebookLogout": true` in `fca-config.json` (default `false`).
|
|
15
|
+
- Cookie mirroring (`facebook.com` <-> `messenger.com`): a *delete this cookie* answer from `messenger.com` for a login cookie (`c_user`, `xs`, `i_user`, `datr`, `sb`, `fr`) is no longer copied onto `facebook.com`. Before, a rejected mirrored value could wipe a perfectly good `facebook.com` login cookie from the bot's jar and make a healthy session look expired.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `api.logoutFromFacebook()` (also `client.account.logoutFromFacebook` from `createFcaClient`).
|
|
20
|
+
- `allowFacebookLogout` option in `fca-config.json`.
|
|
21
|
+
- `test/logout.test.js`, `test/cookies.test.js`.
|
|
22
|
+
|
|
23
|
+
### Not changed
|
|
24
|
+
|
|
25
|
+
- Facebook can still end a session it considers suspicious (cookies used from a different IP / User-Agent than the browser they came from). Pass the exact User-Agent of that browser with the `userAgent` login option (or `FCA_USER_AGENT`) and run the bot from the same network where you can.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## [9.0.4] - 2026-10-27
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- `api.getUserID()` and `api.changeGroupImage()` always threw `ReferenceError: parseAndCheckLogin is not defined` (missing import). Both work again.
|
|
34
|
+
- `api.refreshFb_dtsg()` threw `ReferenceError: CustomError is not defined` on a bad argument; it now throws a normal `Error`.
|
|
35
|
+
- `sendMessage`: when the safety guard blocked a send, the log call was `logger(msg, "error")` but the logger is an object, so it threw `TypeError: logger is not a function`. The send callback was never called and the caller hung. Now logs correctly and the callback / promise is rejected with `ACCOUNT_SAFETY_PAUSED`.
|
|
36
|
+
- Safety guard: rate-limit style text ("too many requests", "rate limited", "temporarily blocked", "action blocked", ...) used to switch sending off until restart. Only real account signals (checkpoint, automated-behavior warning, locked/disabled/suspended, identity check) still fail closed. Rate-limit style signals now only count as strikes (2 within 30 min = slow-down cooldown).
|
|
37
|
+
- `package.json` now declares `tough-cookie`, `form-data` and `request`, which the code already `require()`s. A clean `npm install` of this package alone used to crash on first use.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- `api.getSafetyStatus()` -> `{ blocked, reason, coolingDown, warmup, hourSendCount, hourlyCap, daySendCount, dailyCap }` so a bot can detect `ACCOUNT_SAFETY_PAUSED` instead of retrying.
|
|
42
|
+
- `test/antiSuspension.test.js` (`npm test`, uses `node --test`).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## [9.0.3] - 2026-10-25 (entry added retroactively)
|
|
47
|
+
|
|
48
|
+
### Account safety
|
|
49
|
+
|
|
50
|
+
- Outbound sending is paused (`ACCOUNT_SAFETY_PAUSED`) after a Facebook checkpoint / automated-behavior signal. Automatic dismissal of the automated-behavior warning stays off.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
9
54
|
## [9.0.2] - 2026-10-22
|
|
10
55
|
|
|
11
56
|
### Session stability
|
package/DOCS.md
CHANGED
|
@@ -1711,24 +1711,32 @@ api.searchForThread("Study Group", (err, threads) => {
|
|
|
1711
1711
|
|
|
1712
1712
|
---
|
|
1713
1713
|
|
|
1714
|
-
### 3.29. logout - Logout
|
|
1714
|
+
### 3.29. logout / logoutFromFacebook - Stop the bot / Logout
|
|
1715
1715
|
|
|
1716
|
-
|
|
1716
|
+
`api.logout()` **stops the bot locally** (MQTT listener and internal timers). It sends nothing to
|
|
1717
|
+
Facebook, so your normal browser session stays logged in. It resolves with `{ localOnly: true }`.
|
|
1718
|
+
|
|
1719
|
+
`api.logoutFromFacebook()` is the **real** Facebook logout. Facebook ends that session everywhere,
|
|
1720
|
+
so you are also logged out in your browser. Use it only when that is what you want.
|
|
1721
|
+
|
|
1722
|
+
If you want `api.logout()` itself to perform the real logout, set `"allowFacebookLogout": true` in
|
|
1723
|
+
`fca-config.json` (default `false`).
|
|
1717
1724
|
|
|
1718
1725
|
#### Syntax:
|
|
1719
1726
|
```javascript
|
|
1720
|
-
api.logout(callback);
|
|
1727
|
+
api.logout(callback); // local stop only (default)
|
|
1728
|
+
api.logoutFromFacebook(callback); // real Facebook logout
|
|
1721
1729
|
```
|
|
1722
1730
|
|
|
1723
1731
|
#### Example:
|
|
1724
1732
|
|
|
1725
1733
|
```javascript
|
|
1726
|
-
api.logout((err) => {
|
|
1734
|
+
api.logout((err, result) => {
|
|
1727
1735
|
if (err) {
|
|
1728
|
-
console.error("
|
|
1736
|
+
console.error("Stop error:", err);
|
|
1729
1737
|
return;
|
|
1730
1738
|
}
|
|
1731
|
-
console.log("
|
|
1739
|
+
console.log("Bot stopped.", result); // { localOnly: true }
|
|
1732
1740
|
});
|
|
1733
1741
|
```
|
|
1734
1742
|
|
package/README.md
CHANGED
|
@@ -492,7 +492,8 @@ api.sharePost(postID, "Check this!");
|
|
|
492
492
|
api.getCurrentUserID();
|
|
493
493
|
api.getAppState();
|
|
494
494
|
api.setOptions({ listenTyping: true });
|
|
495
|
-
api.logout();
|
|
495
|
+
api.logout(); // local stop only; your browser stays logged in
|
|
496
|
+
api.logoutFromFacebook(); // real Facebook logout (ends your browser session too)
|
|
496
497
|
api.refreshFb_dtsg();
|
|
497
498
|
api.addExternalModule("myFunc", (defaultFuncs, api, ctx) => {
|
|
498
499
|
return function(text, threadID) {
|
|
@@ -534,7 +535,7 @@ api.uploadImageToImgbb(imageUrl);
|
|
|
534
535
|
|
|
535
536
|
MIT License
|
|
536
537
|
|
|
537
|
-
**hridoy-ultimate** v9.0.
|
|
538
|
+
**hridoy-ultimate** v9.0.4 by [HR ID OY](https://github.com/hridoyhossen48360)
|
|
538
539
|
|
|
539
540
|
> The E2EE native media engine (`src/api/socket/e2ee/native/`) bundles a
|
|
540
541
|
> precompiled binary built from **mautrix-go** / **mautrix-meta**, © Tulir
|
package/index.d.ts
CHANGED
|
@@ -168,6 +168,7 @@ declare module '@dongdev/fca-unofficial' {
|
|
|
168
168
|
getUserID: (name: string, callback?: (err: Error | null, obj: IFCAU_UserIDResponse) => void) => Promise<IFCAU_UserIDResponse>;
|
|
169
169
|
getFriendsList: (callback?: (err: Error | null, friends: IFCAU_Friend[]) => void) => Promise<IFCAU_Friend[]>;
|
|
170
170
|
getCurrentUserID: () => string;
|
|
171
|
+
getSafetyStatus: () => { blocked: boolean; reason: string | null; coolingDown: boolean; warmup: boolean; hourSendCount: number; hourlyCap: number; daySendCount: number; dailyCap: number };
|
|
171
172
|
|
|
172
173
|
// Utilities
|
|
173
174
|
getAppState: () => any;
|
|
@@ -200,7 +201,10 @@ declare module '@dongdev/fca-unofficial' {
|
|
|
200
201
|
|
|
201
202
|
// Configuration & Session
|
|
202
203
|
setOptions: (options: Partial<IFCAU_Options>) => void;
|
|
203
|
-
|
|
204
|
+
/** Stops the bot locally (listener + timers). Does NOT log out of Facebook unless `allowFacebookLogout` is true in fca-config.json. */
|
|
205
|
+
logout: (callback?: (err?: Error | null, result?: { localOnly: true }) => void) => Promise<{ localOnly: true } | void>;
|
|
206
|
+
/** Real Facebook logout (POST logout.php). Also ends the session in your browser. */
|
|
207
|
+
logoutFromFacebook: (callback?: (err?: Error) => void) => Promise<void>;
|
|
204
208
|
|
|
205
209
|
// Message Scheduler
|
|
206
210
|
scheduler: {
|
package/module/config.js
CHANGED
|
@@ -5,6 +5,10 @@ const defaultConfig = {
|
|
|
5
5
|
autoUpdate: false,
|
|
6
6
|
// Opt-in: tell the user when a newer npm version exists (one small request per login).
|
|
7
7
|
checkForUpdates: false,
|
|
8
|
+
// api.logout() only stops the bot locally. Set true ONLY if you want api.logout() to
|
|
9
|
+
// really log the account out of Facebook, which also logs you out in your browser.
|
|
10
|
+
// The real logout is always available as api.logoutFromFacebook().
|
|
11
|
+
allowFacebookLogout: false,
|
|
8
12
|
// Opt-in: when Facebook shows its "automated behavior" warning, press Dismiss for the
|
|
9
13
|
// user. Off by default: silently clicking through Facebook's own bot warning is itself
|
|
10
14
|
// an automated action on the account. Dismiss it yourself in the browser instead.
|
package/module/loginHelper.js
CHANGED
|
@@ -828,6 +828,16 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
828
828
|
}
|
|
829
829
|
const ctx = { globalOptions, options: globalOptions, reconnectAttempts: 0 };
|
|
830
830
|
ctx.bypassAutomation = async function (resp, j) {
|
|
831
|
+
// Never submit mutations intended to dismiss/bypass Facebook security warnings.
|
|
832
|
+
// Preserve the response so upstream login validation can fail closed.
|
|
833
|
+
const body = typeof resp?.data === "string" ? resp.data : String(resp?.data ?? "");
|
|
834
|
+
const url = resp?.request?.res?.responseUrl || resp?.config?.url || "";
|
|
835
|
+
if (/601051028565049|XCheckpointFBScrapingWarningController|automated.?behavior|checkpoint\//i.test(`${url} ${body}`)) {
|
|
836
|
+
logger("Facebook security/automation checkpoint detected; not bypassing it. Stop the bot and review the account manually.", "error");
|
|
837
|
+
const err = new Error("Facebook Checkpoint/security warning detected; automatic recovery stopped");
|
|
838
|
+
err.code = "FACEBOOK_SECURITY_CHECKPOINT";
|
|
839
|
+
throw err;
|
|
840
|
+
}
|
|
831
841
|
global.fca = global.fca || {};
|
|
832
842
|
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
833
843
|
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
@@ -878,7 +888,8 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
878
888
|
}
|
|
879
889
|
return first;
|
|
880
890
|
} catch (e) {
|
|
881
|
-
logger(`
|
|
891
|
+
logger(`Facebook security handling stopped: ${e && e.message ? e.message : String(e)}`, "error");
|
|
892
|
+
if (e && e.code === "FACEBOOK_SECURITY_CHECKPOINT") throw e;
|
|
882
893
|
return resp;
|
|
883
894
|
}
|
|
884
895
|
};
|
|
@@ -917,6 +928,15 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
917
928
|
const ctx = {};
|
|
918
929
|
ctx.options = globalOptions;
|
|
919
930
|
ctx.bypassAutomation = async function (resp, j) {
|
|
931
|
+
// Never submit mutations intended to dismiss/bypass Facebook security warnings.
|
|
932
|
+
const body = typeof resp?.data === "string" ? resp.data : String(resp?.data ?? "");
|
|
933
|
+
const url = resp?.request?.res?.responseUrl || resp?.config?.url || "";
|
|
934
|
+
if (/601051028565049|XCheckpointFBScrapingWarningController|automated.?behavior|checkpoint\//i.test(`${url} ${body}`)) {
|
|
935
|
+
logger("Facebook security/automation checkpoint detected; not bypassing it. Stop the bot and review the account manually.", "error");
|
|
936
|
+
const err = new Error("Facebook Checkpoint/security warning detected; automatic recovery stopped");
|
|
937
|
+
err.code = "FACEBOOK_SECURITY_CHECKPOINT";
|
|
938
|
+
throw err;
|
|
939
|
+
}
|
|
920
940
|
global.fca = global.fca || {};
|
|
921
941
|
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
922
942
|
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
@@ -954,7 +974,8 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
954
974
|
}
|
|
955
975
|
logger("✅ No checkpoint detected — session is clean.", "info");
|
|
956
976
|
return res;
|
|
957
|
-
} catch {
|
|
977
|
+
} catch (e) {
|
|
978
|
+
if (e && e.code === "FACEBOOK_SECURITY_CHECKPOINT") throw e;
|
|
958
979
|
return res;
|
|
959
980
|
}
|
|
960
981
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hridoy-ultimate",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.4",
|
|
4
4
|
"description": "Facebook Chat API by HR ID OY | Stable • Auto Re-login • Full E2EE Support — send messages, media, reactions & more in encrypted chats, hassle-free",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"src/app/"
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
|
-
"test": "
|
|
28
|
+
"test": "node --test",
|
|
29
29
|
"lint": "eslint ."
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
@@ -76,19 +76,22 @@
|
|
|
76
76
|
"chalk": "^4.1.2",
|
|
77
77
|
"cheerio": "1.0.0-rc.12",
|
|
78
78
|
"duplexify": "^4.1.3",
|
|
79
|
+
"form-data": "^4.0.0",
|
|
79
80
|
"gradient-string": "^2.0.2",
|
|
80
81
|
"https-proxy-agent": "^4.0.0",
|
|
82
|
+
"koffi": "^3.0.2",
|
|
83
|
+
"mime": "^3.0.0",
|
|
81
84
|
"mqtt": "^5.10.1",
|
|
85
|
+
"@noble/curves": "^2.2.0",
|
|
86
|
+
"@noble/hashes": "^2.2.0",
|
|
87
|
+
"protobufjs": "^8.4.0",
|
|
88
|
+
"request": "^2.88.2",
|
|
82
89
|
"sequelize": "^6.37.6",
|
|
90
|
+
"@signalapp/libsignal-client": "0.70.0",
|
|
83
91
|
"sqlite3": "^5.1.7",
|
|
84
92
|
"totp-generator": "^1.0.0",
|
|
93
|
+
"tough-cookie": "^5.0.0",
|
|
85
94
|
"ws": "^8.18.0",
|
|
86
|
-
"@noble/curves": "^2.2.0",
|
|
87
|
-
"@noble/hashes": "^2.2.0",
|
|
88
|
-
"@signalapp/libsignal-client": "0.70.0",
|
|
89
|
-
"mime": "^3.0.0",
|
|
90
|
-
"protobufjs": "^8.4.0",
|
|
91
|
-
"koffi": "^3.0.2",
|
|
92
95
|
"yumi-json-bigint": "^1.0.0"
|
|
93
96
|
},
|
|
94
97
|
"devDependencies": {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { globalAntiSuspension } = require("../../utils/antiSuspension");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* api.getSafetyStatus() -> {
|
|
7
|
+
* blocked, reason, coolingDown, warmup, hourSendCount, hourlyCap, daySendCount, dailyCap
|
|
8
|
+
* }
|
|
9
|
+
* `blocked` is true after Facebook showed a checkpoint / automated-behavior / disabled
|
|
10
|
+
* signal: sendMessage then rejects with err.code === "ACCOUNT_SAFETY_PAUSED" until the
|
|
11
|
+
* process is restarted after you reviewed the account by hand.
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
14
|
+
return function getSafetyStatus() {
|
|
15
|
+
const st = globalAntiSuspension.getStats();
|
|
16
|
+
return {
|
|
17
|
+
blocked: !!st.safetyBlocked,
|
|
18
|
+
reason: st.safetyReason || null,
|
|
19
|
+
coolingDown: !!st.coolingDown,
|
|
20
|
+
warmup: !!st.warmup,
|
|
21
|
+
hourSendCount: st.hourSendCount,
|
|
22
|
+
hourlyCap: st.hourlyCap,
|
|
23
|
+
daySendCount: st.daySendCount,
|
|
24
|
+
dailyCap: st.dailyCap
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
package/src/api/action/logout.js
CHANGED
|
@@ -1,75 +1,86 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* logout - stop this bot session WITHOUT logging the account out of Facebook.
|
|
5
|
+
*
|
|
6
|
+
* Older FCA builds POSTed facebook.com/logout.php here. That ends the session
|
|
7
|
+
* server-side, so the same account is logged out in your own browser as well,
|
|
8
|
+
* which is the opposite of what a bot owner wants when they just stop the bot.
|
|
9
|
+
*
|
|
10
|
+
* Default behaviour (since 9.0.5): local stop only
|
|
11
|
+
* - stops the MQTT listener,
|
|
12
|
+
* - cancels the fb_dtsg refresh timer,
|
|
13
|
+
* - stops sessionGuard's timer,
|
|
14
|
+
* - never sends anything to Facebook.
|
|
15
|
+
*
|
|
16
|
+
* The real Facebook logout is api.logoutFromFacebook(). It can also be made the
|
|
17
|
+
* behaviour of api.logout() by setting `"allowFacebookLogout": true` in
|
|
18
|
+
* fca-config.json (explicit opt-in).
|
|
19
|
+
*/
|
|
20
|
+
|
|
3
21
|
const log = require("../../../func/logAdapter");
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
22
|
+
const { loadConfig } = require("../../../module/config");
|
|
23
|
+
|
|
24
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
25
|
+
function stopListener() {
|
|
26
|
+
return new Promise(resolve => {
|
|
27
|
+
let done = false;
|
|
28
|
+
const finish = () => { if (!done) { done = true; resolve(); } };
|
|
29
|
+
try {
|
|
30
|
+
if (typeof api.stopListening === "function") {
|
|
31
|
+
api.stopListening(finish);
|
|
32
|
+
// never hang a shutdown on a listener that was never started
|
|
33
|
+
setTimeout(finish, 6000).unref?.();
|
|
34
|
+
} else {
|
|
35
|
+
finish();
|
|
36
|
+
}
|
|
37
|
+
} catch (_) {
|
|
38
|
+
finish();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function stopTimers() {
|
|
44
|
+
try {
|
|
45
|
+
if (ctx._dtsgRefreshInterval) {
|
|
46
|
+
clearInterval(ctx._dtsgRefreshInterval);
|
|
47
|
+
ctx._dtsgRefreshInterval = null;
|
|
48
|
+
}
|
|
49
|
+
} catch (_) { /* ignore */ }
|
|
50
|
+
try {
|
|
51
|
+
if (typeof api.stopSessionGuard === "function") api.stopSessionGuard();
|
|
52
|
+
} catch (_) { /* ignore */ }
|
|
53
|
+
}
|
|
54
|
+
|
|
8
55
|
return function logout(callback) {
|
|
9
|
-
let resolveFunc = function() {};
|
|
10
|
-
let rejectFunc = function() {};
|
|
11
|
-
const returnPromise = new Promise(function(resolve, reject) {
|
|
56
|
+
let resolveFunc = function () {};
|
|
57
|
+
let rejectFunc = function () {};
|
|
58
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
12
59
|
resolveFunc = resolve;
|
|
13
60
|
rejectFunc = reject;
|
|
14
61
|
});
|
|
15
62
|
|
|
16
|
-
if (
|
|
17
|
-
callback = function(err,
|
|
18
|
-
if (err)
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
resolveFunc(friendList);
|
|
63
|
+
if (typeof callback !== "function") {
|
|
64
|
+
callback = function (err, result) {
|
|
65
|
+
if (err) return rejectFunc(err);
|
|
66
|
+
resolveFunc(result);
|
|
22
67
|
};
|
|
23
68
|
}
|
|
24
69
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"https://www.facebook.com/bluebar/modern_settings_menu/?help_type=364455653583099&show_contextual_help=1",
|
|
32
|
-
ctx.jar,
|
|
33
|
-
form
|
|
34
|
-
)
|
|
35
|
-
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
36
|
-
.then(function(resData) {
|
|
37
|
-
const elem = resData.jsmods.instances[0][2][0].filter(function(v) {
|
|
38
|
-
return v.value === "logout";
|
|
39
|
-
})[0];
|
|
40
|
-
|
|
41
|
-
const html = resData.jsmods.markup.filter(function(v) {
|
|
42
|
-
return v[0] === elem.markup.__m;
|
|
43
|
-
})[0][1].__html;
|
|
44
|
-
|
|
45
|
-
const form = {
|
|
46
|
-
fb_dtsg: getFrom(html, '"fb_dtsg" value="', '"'),
|
|
47
|
-
ref: getFrom(html, '"ref" value="', '"'),
|
|
48
|
-
h: getFrom(html, '"h" value="', '"')
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return defaultFuncs
|
|
52
|
-
.post("https://www.facebook.com/logout.php", ctx.jar, form)
|
|
53
|
-
.then(saveCookies(ctx.jar));
|
|
54
|
-
})
|
|
55
|
-
.then(function(res) {
|
|
56
|
-
if (!res.headers) {
|
|
57
|
-
throw { error: "An error occurred when logging out." };
|
|
58
|
-
}
|
|
70
|
+
const { config } = loadConfig();
|
|
71
|
+
if (config && config.allowFacebookLogout === true && typeof api.logoutFromFacebook === "function") {
|
|
72
|
+
log.warn("logout", "allowFacebookLogout is true: logging out of Facebook for real (your browser session ends too).");
|
|
73
|
+
api.logoutFromFacebook(callback);
|
|
74
|
+
return returnPromise;
|
|
75
|
+
}
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
ctx.loggedIn = false;
|
|
66
|
-
log.info("logout", "Logged out successfully.");
|
|
67
|
-
callback();
|
|
77
|
+
log.info("logout", "Stopping locally. The Facebook session is NOT ended (use api.logoutFromFacebook() for a real logout).");
|
|
78
|
+
stopListener()
|
|
79
|
+
.then(() => {
|
|
80
|
+
stopTimers();
|
|
81
|
+
callback(null, { localOnly: true });
|
|
68
82
|
})
|
|
69
|
-
.catch(
|
|
70
|
-
log.error("logout", err);
|
|
71
|
-
return callback(err);
|
|
72
|
-
});
|
|
83
|
+
.catch(err => callback(err));
|
|
73
84
|
|
|
74
85
|
return returnPromise;
|
|
75
86
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* logoutFromFacebook - REAL Facebook logout.
|
|
5
|
+
*
|
|
6
|
+
* This POSTs facebook.com/logout.php with the session cookies. Facebook then
|
|
7
|
+
* invalidates that session everywhere it is used, so the same account is also
|
|
8
|
+
* logged out in your normal browser. It is NOT called by api.logout() unless you
|
|
9
|
+
* opt in with `allowFacebookLogout: true` in fca-config.json. Call it directly
|
|
10
|
+
* only when you really want to end the Facebook session.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const log = require("../../../func/logAdapter");
|
|
14
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
|
15
|
+
const { getFrom } = require("../../utils/constants");
|
|
16
|
+
const { saveCookies } = require("../../utils/client");
|
|
17
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
18
|
+
return function logoutFromFacebook(callback) {
|
|
19
|
+
let resolveFunc = function() {};
|
|
20
|
+
let rejectFunc = function() {};
|
|
21
|
+
const returnPromise = new Promise(function(resolve, reject) {
|
|
22
|
+
resolveFunc = resolve;
|
|
23
|
+
rejectFunc = reject;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!callback) {
|
|
27
|
+
callback = function(err, friendList) {
|
|
28
|
+
if (err) {
|
|
29
|
+
return rejectFunc(err);
|
|
30
|
+
}
|
|
31
|
+
resolveFunc(friendList);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const form = {
|
|
36
|
+
pmid: "0"
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
defaultFuncs
|
|
40
|
+
.post(
|
|
41
|
+
"https://www.facebook.com/bluebar/modern_settings_menu/?help_type=364455653583099&show_contextual_help=1",
|
|
42
|
+
ctx.jar,
|
|
43
|
+
form
|
|
44
|
+
)
|
|
45
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
46
|
+
.then(function(resData) {
|
|
47
|
+
const elem = resData.jsmods.instances[0][2][0].filter(function(v) {
|
|
48
|
+
return v.value === "logout";
|
|
49
|
+
})[0];
|
|
50
|
+
|
|
51
|
+
const html = resData.jsmods.markup.filter(function(v) {
|
|
52
|
+
return v[0] === elem.markup.__m;
|
|
53
|
+
})[0][1].__html;
|
|
54
|
+
|
|
55
|
+
const form = {
|
|
56
|
+
fb_dtsg: getFrom(html, '"fb_dtsg" value="', '"'),
|
|
57
|
+
ref: getFrom(html, '"ref" value="', '"'),
|
|
58
|
+
h: getFrom(html, '"h" value="', '"')
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return defaultFuncs
|
|
62
|
+
.post("https://www.facebook.com/logout.php", ctx.jar, form)
|
|
63
|
+
.then(saveCookies(ctx.jar));
|
|
64
|
+
})
|
|
65
|
+
.then(function(res) {
|
|
66
|
+
if (!res.headers) {
|
|
67
|
+
throw { error: "An error occurred when logging out." };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return defaultFuncs
|
|
71
|
+
.get(res.headers.location, ctx.jar)
|
|
72
|
+
.then(saveCookies(ctx.jar));
|
|
73
|
+
})
|
|
74
|
+
.then(function() {
|
|
75
|
+
ctx.loggedIn = false;
|
|
76
|
+
log.info("logoutFromFacebook", "Logged out of Facebook (this also ends the browser session).");
|
|
77
|
+
callback();
|
|
78
|
+
})
|
|
79
|
+
.catch(function(err) {
|
|
80
|
+
log.error("logoutFromFacebook", err);
|
|
81
|
+
return callback(err);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return returnPromise;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
@@ -12,7 +12,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
12
12
|
}
|
|
13
13
|
if (!obj) obj = {};
|
|
14
14
|
if (getType(obj) !== "Object") {
|
|
15
|
-
throw new
|
|
15
|
+
throw new Error("The first parameter must be an object or a callback function");
|
|
16
16
|
}
|
|
17
17
|
let resolveFunc, rejectFunc;
|
|
18
18
|
const returnPromise = new Promise((resolve, reject) => {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { generateOfflineThreadingID } = require("../../utils/format");
|
|
4
4
|
const log = require("../../../func/logAdapter");
|
|
5
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
|
5
6
|
|
|
6
7
|
module.exports = function (defaultFuncs, api, ctx) {
|
|
7
8
|
function handleUpload(image) {
|
|
@@ -119,7 +119,10 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
119
119
|
form.signatureID = utils.getGUID().replace(/-/g, "").slice(0, 8);
|
|
120
120
|
const doSend = () => sendContent(form, threadID, isSingleUser, messageAndOTID, callback);
|
|
121
121
|
if (!ctx.globalOptions || ctx.globalOptions.antiBan !== false) {
|
|
122
|
-
globalAntiSuspension.prepareBeforeMessage(threadID, form.body || "").then(doSend).catch(
|
|
122
|
+
globalAntiSuspension.prepareBeforeMessage(threadID, form.body || "").then(doSend).catch(err => {
|
|
123
|
+
if (typeof callback === "function") return callback(err);
|
|
124
|
+
return undefined;
|
|
125
|
+
});
|
|
123
126
|
} else {
|
|
124
127
|
doSend();
|
|
125
128
|
}
|
|
@@ -254,7 +254,10 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
254
254
|
try {
|
|
255
255
|
await globalAntiSuspension.prepareBeforeMessage(threadID, msg.body || "");
|
|
256
256
|
} catch (err) {
|
|
257
|
-
logger("
|
|
257
|
+
logger.error("sendMessage", "Outbound send blocked by safety guard: " + (err && err.message ? err.message : err));
|
|
258
|
+
if (callback) callback(err);
|
|
259
|
+
else reject(err);
|
|
260
|
+
return promise;
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
|
|
@@ -88,6 +88,7 @@ function createFallbackNamespaces(api) {
|
|
|
88
88
|
getCurrentUserID: bindOptionalMethod(api, "getCurrentUserID"),
|
|
89
89
|
handleFriendRequest: bindOptionalMethod(api, "handleFriendRequest"),
|
|
90
90
|
logout: bindOptionalMethod(api, "logout"),
|
|
91
|
+
logoutFromFacebook: bindOptionalMethod(api, "logoutFromFacebook"),
|
|
91
92
|
refreshDtsg: bindOptionalMethod(api, "refreshFb_dtsg"),
|
|
92
93
|
changeBlockedStatus: bindOptionalMethod(api, "changeBlockedStatus"),
|
|
93
94
|
setOptions: bindOptionalMethod(api, "setOptions"),
|
|
@@ -6,17 +6,21 @@ const log = {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// Phrases Facebook responses use when it thinks this account is being
|
|
9
|
-
// operated by a bot / script rather than a human
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
9
|
+
// operated by a bot / script rather than a human, or when it is restricting it.
|
|
10
|
+
//
|
|
11
|
+
// HARD signals mean the account itself is flagged / locked / needs a human
|
|
12
|
+
// (checkpoint, automated-behavior warning, disabled, identity check). On these
|
|
13
|
+
// the guard FAILS CLOSED: outbound sending stops until the process is restarted
|
|
14
|
+
// after a manual review of the account.
|
|
15
|
+
//
|
|
16
|
+
// SOFT signals are rate-limit style responses ("too many requests", "rate
|
|
17
|
+
// limited", "temporarily blocked" ...). They are usually transient, so they only
|
|
18
|
+
// count as strikes and trigger the slow-down cooldown. Treating them as hard
|
|
19
|
+
// signals made one passing 429-style error silence the bot for good.
|
|
20
|
+
const HARD_SUSPICION_PHRASES = [
|
|
13
21
|
"checkpoint",
|
|
14
22
|
"automated behavior",
|
|
15
23
|
"automated_behavior",
|
|
16
|
-
"unusual activity",
|
|
17
|
-
"unusual_activity",
|
|
18
|
-
"suspicious activity",
|
|
19
|
-
"action_required",
|
|
20
24
|
"account_locked",
|
|
21
25
|
"account locked",
|
|
22
26
|
"account has been disabled",
|
|
@@ -26,6 +30,17 @@ const SUSPICION_PHRASES = [
|
|
|
26
30
|
"bot detected",
|
|
27
31
|
"bot_detected",
|
|
28
32
|
"not a human",
|
|
33
|
+
"verify your account",
|
|
34
|
+
"confirm your identity",
|
|
35
|
+
"confirm it's you",
|
|
36
|
+
"security check required"
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const SOFT_SUSPICION_PHRASES = [
|
|
40
|
+
"unusual activity",
|
|
41
|
+
"unusual_activity",
|
|
42
|
+
"suspicious activity",
|
|
43
|
+
"action_required",
|
|
29
44
|
"spam detected",
|
|
30
45
|
"spam_detected",
|
|
31
46
|
"looks like spam",
|
|
@@ -35,15 +50,14 @@ const SUSPICION_PHRASES = [
|
|
|
35
50
|
"too_many_requests",
|
|
36
51
|
"temporarily blocked",
|
|
37
52
|
"feature temporarily unavailable",
|
|
38
|
-
"verify your account",
|
|
39
|
-
"confirm your identity",
|
|
40
|
-
"confirm it's you",
|
|
41
|
-
"security check required",
|
|
42
53
|
"policy violation",
|
|
43
54
|
"action blocked",
|
|
44
55
|
"you're blocked from"
|
|
45
56
|
];
|
|
46
57
|
|
|
58
|
+
// Kept for backwards compatibility with anything that imported the old list.
|
|
59
|
+
const SUSPICION_PHRASES = HARD_SUSPICION_PHRASES.concat(SOFT_SUSPICION_PHRASES);
|
|
60
|
+
|
|
47
61
|
/**
|
|
48
62
|
* Tracks send volume and paces outbound traffic so it doesn't look like a
|
|
49
63
|
* script firing messages as fast as the network allows. Three layers:
|
|
@@ -72,13 +86,18 @@ class SendPacer {
|
|
|
72
86
|
this.dayWindowStart = Date.now();
|
|
73
87
|
this.daySendCount = 0;
|
|
74
88
|
|
|
89
|
+
this.accountSafetyBlocked = false;
|
|
90
|
+
this.accountSafetyReason = null;
|
|
91
|
+
|
|
75
92
|
this.cooldown = {
|
|
76
93
|
active: false,
|
|
77
94
|
since: null,
|
|
78
95
|
durationMs: 45 * 60 * 1000,
|
|
79
96
|
strikes: 0,
|
|
80
|
-
strikesToActivate: 2
|
|
97
|
+
strikesToActivate: 2,
|
|
98
|
+
strikeWindowMs: 30 * 60 * 1000
|
|
81
99
|
};
|
|
100
|
+
this.softStrikeTimes = [];
|
|
82
101
|
|
|
83
102
|
this._gcTimer = setInterval(() => this._rollWindows(), 60 * 1000);
|
|
84
103
|
if (this._gcTimer.unref) this._gcTimer.unref();
|
|
@@ -108,12 +127,30 @@ class SendPacer {
|
|
|
108
127
|
return this._inWarmup() ? this.warmupHourlyCap : this.normalHourlyCap;
|
|
109
128
|
}
|
|
110
129
|
|
|
111
|
-
/**
|
|
112
|
-
|
|
113
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Records that a suspicious signal was seen.
|
|
132
|
+
* hard=true (default): fail closed - stop sending until a human reviews the account.
|
|
133
|
+
* hard=false: count a strike; two strikes inside strikeWindowMs start the cooldown.
|
|
134
|
+
*/
|
|
135
|
+
noteSuspicionSignal(source, hard) {
|
|
136
|
+
const isHard = hard !== false;
|
|
137
|
+
if (isHard) {
|
|
138
|
+
// Fail closed: once Facebook signals checkpoint/automation/disabled status,
|
|
139
|
+
// do not continue sending or retrying in this process. Human review is required.
|
|
140
|
+
if (!this.accountSafetyBlocked) {
|
|
141
|
+
log.warn("AntiSuspension", `Outbound sending paused: ${source || "Facebook security signal"}. Review the account manually, then restart the bot.`);
|
|
142
|
+
}
|
|
143
|
+
this.accountSafetyBlocked = true;
|
|
144
|
+
this.accountSafetyReason = String(source || "Facebook security signal");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
this.softStrikeTimes = this.softStrikeTimes.filter(t => now - t < this.cooldown.strikeWindowMs);
|
|
149
|
+
this.softStrikeTimes.push(now);
|
|
150
|
+
this.cooldown.strikes = this.softStrikeTimes.length;
|
|
114
151
|
if (!this.cooldown.active && this.cooldown.strikes >= this.cooldown.strikesToActivate) {
|
|
115
152
|
this.cooldown.active = true;
|
|
116
|
-
this.cooldown.since =
|
|
153
|
+
this.cooldown.since = now;
|
|
117
154
|
log.warn("AntiSuspension", `Cooldown activated after repeated suspicion signals (last: ${source || "unknown"}). Backing off for ${(this.cooldown.durationMs / 60000).toFixed(0)} min.`);
|
|
118
155
|
}
|
|
119
156
|
}
|
|
@@ -122,9 +159,17 @@ class SendPacer {
|
|
|
122
159
|
scanForSuspicion(text) {
|
|
123
160
|
if (!text || typeof text !== "string") return false;
|
|
124
161
|
const lower = text.toLowerCase();
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
|
|
162
|
+
const hardHit = HARD_SUSPICION_PHRASES.find(p => lower.includes(p));
|
|
163
|
+
if (hardHit) {
|
|
164
|
+
this.noteSuspicionSignal(hardHit, true);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
const softHit = SOFT_SUSPICION_PHRASES.find(p => lower.includes(p));
|
|
168
|
+
if (softHit) {
|
|
169
|
+
this.noteSuspicionSignal(softHit, false);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
128
173
|
}
|
|
129
174
|
|
|
130
175
|
/** Inspects a thrown error's message for suspicion phrases. */
|
|
@@ -138,6 +183,7 @@ class SendPacer {
|
|
|
138
183
|
if (Date.now() - this.cooldown.since >= this.cooldown.durationMs) {
|
|
139
184
|
this.cooldown.active = false;
|
|
140
185
|
this.cooldown.strikes = 0;
|
|
186
|
+
this.softStrikeTimes = [];
|
|
141
187
|
this.cooldown.since = null;
|
|
142
188
|
return false;
|
|
143
189
|
}
|
|
@@ -151,6 +197,11 @@ class SendPacer {
|
|
|
151
197
|
* rather than blocking a real message.
|
|
152
198
|
*/
|
|
153
199
|
async waitForTurn(threadID) {
|
|
200
|
+
if (this.accountSafetyBlocked) {
|
|
201
|
+
const err = new Error(`Outbound messaging paused after Facebook security signal: ${this.accountSafetyReason}. Review the account manually before restarting.`);
|
|
202
|
+
err.code = "ACCOUNT_SAFETY_PAUSED";
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
154
205
|
try {
|
|
155
206
|
this._rollWindows();
|
|
156
207
|
|
|
@@ -185,8 +236,10 @@ class SendPacer {
|
|
|
185
236
|
this.daySendCount++;
|
|
186
237
|
const threadCount = (this.threadSendCounts.get(key) || 0) + 1;
|
|
187
238
|
this.threadSendCounts.set(key, threadCount);
|
|
188
|
-
} catch {
|
|
189
|
-
//
|
|
239
|
+
} catch (err) {
|
|
240
|
+
// Safety-related errors must not be swallowed.
|
|
241
|
+
if (err && err.code === "ACCOUNT_SAFETY_PAUSED") throw err;
|
|
242
|
+
log.warn("AntiSuspension", `Pacing check failed: ${err && err.message ? err.message : String(err)}`);
|
|
190
243
|
}
|
|
191
244
|
}
|
|
192
245
|
|
|
@@ -201,7 +254,9 @@ class SendPacer {
|
|
|
201
254
|
hourlyCap: this._hourlyCap(),
|
|
202
255
|
daySendCount: this.daySendCount,
|
|
203
256
|
dailyCap: this.dailyCap,
|
|
204
|
-
coolingDown: this.isCoolingDown()
|
|
257
|
+
coolingDown: this.isCoolingDown(),
|
|
258
|
+
safetyBlocked: this.accountSafetyBlocked,
|
|
259
|
+
safetyReason: this.accountSafetyReason
|
|
205
260
|
};
|
|
206
261
|
}
|
|
207
262
|
}
|
|
@@ -210,10 +265,15 @@ const pacer = new SendPacer();
|
|
|
210
265
|
|
|
211
266
|
module.exports = {
|
|
212
267
|
SendPacer,
|
|
268
|
+
HARD_SUSPICION_PHRASES,
|
|
269
|
+
SOFT_SUSPICION_PHRASES,
|
|
270
|
+
SUSPICION_PHRASES,
|
|
213
271
|
globalAntiSuspension: {
|
|
214
272
|
prepareBeforeMessage: (threadID) => pacer.waitForTurn(threadID),
|
|
215
273
|
detectSuspensionSignal: (text) => pacer.scanForSuspicion(text),
|
|
216
274
|
checkAccountHealth: (err) => pacer.noteSendError(err),
|
|
217
|
-
getStats: () => pacer.stats()
|
|
275
|
+
getStats: () => pacer.stats(),
|
|
276
|
+
isAccountSafetyBlocked: () => pacer.accountSafetyBlocked,
|
|
277
|
+
getAccountSafetyReason: () => pacer.accountSafetyReason
|
|
218
278
|
}
|
|
219
279
|
};
|
package/src/utils/cookies.js
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
// Cookie helpers extracted from client.js
|
|
4
4
|
|
|
5
|
+
const AUTH_COOKIES = new Set(["c_user", "xs", "i_user", "datr", "sb", "fr"]);
|
|
6
|
+
|
|
7
|
+
// True for a Set-Cookie header that deletes one of the login cookies
|
|
8
|
+
// (empty / "deleted" value, Max-Age<=0, or an expiry in the past).
|
|
9
|
+
function isAuthCookieDeletion(setCookieHeader) {
|
|
10
|
+
const first = String(setCookieHeader || "").split(";")[0];
|
|
11
|
+
const eq = first.indexOf("=");
|
|
12
|
+
if (eq <= 0) return false;
|
|
13
|
+
const name = first.slice(0, eq).trim();
|
|
14
|
+
if (!AUTH_COOKIES.has(name)) return false;
|
|
15
|
+
const value = first.slice(eq + 1).trim().replace(/^"|"$/g, "");
|
|
16
|
+
if (value === "" || /^deleted$/i.test(value)) return true;
|
|
17
|
+
const maxAge = /;\s*max-age\s*=\s*(-?\d+)/i.exec(setCookieHeader);
|
|
18
|
+
if (maxAge && parseInt(maxAge[1], 10) <= 0) return true;
|
|
19
|
+
const expires = /;\s*expires\s*=\s*([^;]+)/i.exec(setCookieHeader);
|
|
20
|
+
if (expires) {
|
|
21
|
+
const t = Date.parse(expires[1]);
|
|
22
|
+
if (!Number.isNaN(t) && t <= Date.now()) return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
5
27
|
function saveCookies(jar) {
|
|
6
28
|
return res => {
|
|
7
29
|
try {
|
|
@@ -29,6 +51,11 @@ function saveCookies(jar) {
|
|
|
29
51
|
const mirrored = c.replace(/domain=\.?facebook\.com/i, "domain=.messenger.com");
|
|
30
52
|
jar.setCookieSync(mirrored, "https://www.messenger.com");
|
|
31
53
|
} else if (/domain=\.?messenger\.com/i.test(c)) {
|
|
54
|
+
// A messenger.com "delete this cookie" answer must never wipe the matching
|
|
55
|
+
// facebook.com login cookie in our jar: that would silently kill a perfectly
|
|
56
|
+
// good facebook.com session (the cookie values are mirrored, so messenger.com
|
|
57
|
+
// may well reject them while facebook.com is fine).
|
|
58
|
+
if (isAuthCookieDeletion(c)) continue;
|
|
32
59
|
const mirrored = c.replace(/domain=\.?messenger\.com/i, "domain=.facebook.com");
|
|
33
60
|
jar.setCookieSync(mirrored, "https://www.facebook.com");
|
|
34
61
|
}
|
|
@@ -80,6 +107,7 @@ function getAppState(jar) {
|
|
|
80
107
|
|
|
81
108
|
module.exports = {
|
|
82
109
|
saveCookies,
|
|
83
|
-
getAppState
|
|
110
|
+
getAppState,
|
|
111
|
+
isAuthCookieDeletion
|
|
84
112
|
};
|
|
85
113
|
|
|
@@ -186,25 +186,7 @@ function parseAndCheckLogin(ctx, http, retryCount = 0) {
|
|
|
186
186
|
) {
|
|
187
187
|
globalAntiSuspension.detectSuspensionSignal("automated behavior checkpoint scraping warning");
|
|
188
188
|
emit("checkpoint", { type: "scraping_warning", res: resData });
|
|
189
|
-
|
|
190
|
-
if (fcaCfg.autoDismissAutomationWarning === true) {
|
|
191
|
-
// Opt-in only. Fire-and-forget: must never block or fail the response being parsed.
|
|
192
|
-
http
|
|
193
|
-
.post("https://www.facebook.com/api/graphql/", ctx.jar, {
|
|
194
|
-
av: ctx.userID,
|
|
195
|
-
__user: ctx.userID,
|
|
196
|
-
fb_dtsg: ctx.fb_dtsg || "",
|
|
197
|
-
fb_api_caller_class: "RelayModern",
|
|
198
|
-
fb_api_req_friendly_name: "FBScrapingWarningMutation",
|
|
199
|
-
server_timestamps: "true",
|
|
200
|
-
doc_id: "6339492849481770",
|
|
201
|
-
variables: "{}"
|
|
202
|
-
}, ctx.globalOptions, ctx)
|
|
203
|
-
.then(() => logger("Dismissed automated-behavior warning (autoDismissAutomationWarning=true)", "info"))
|
|
204
|
-
.catch((e) => logger(`Dismissing automated-behavior warning failed (non-fatal): ${e && e.message ? e.message : String(e)}`, "warn"));
|
|
205
|
-
} else {
|
|
206
|
-
logger("Facebook showed its automated-behavior warning. Open facebook.com in your browser, dismiss it yourself, then restart the bot.", "warn");
|
|
207
|
-
}
|
|
189
|
+
logger("Facebook automated-behavior warning detected. Automatic dismissal is disabled; stop the bot and review the account manually.", "error");
|
|
208
190
|
// This is NOT a logged-out session. The old code ran the "session expired" path here,
|
|
209
191
|
// which made the bot declare itself logged out and restart (a fresh login) for nothing.
|
|
210
192
|
const err = new Error("Facebook showed an automated-behavior warning (needs a human to dismiss it)");
|