hridoy-ultimate 9.0.2 → 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 +14 -2
- package/module/login.js +15 -5
- package/module/loginHelper.js +48 -9
- package/package.json +18 -12
- package/src/api/action/enableAutoSaveAppState.js +8 -5
- 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/core/connectMqtt.js +18 -1
- package/src/api/socket/core/getSeqID.js +13 -2
- package/src/api/socket/listenMqtt.js +24 -15
- 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/fingerprint.js +75 -36
- package/src/utils/loginParser/parseAndCheckLogin.js +7 -18
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
|
@@ -2,13 +2,25 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const logger = require("../func/logger");
|
|
4
4
|
const defaultConfig = {
|
|
5
|
-
autoUpdate:
|
|
5
|
+
autoUpdate: false,
|
|
6
|
+
// Opt-in: tell the user when a newer npm version exists (one small request per login).
|
|
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,
|
|
12
|
+
// Opt-in: when Facebook shows its "automated behavior" warning, press Dismiss for the
|
|
13
|
+
// user. Off by default: silently clicking through Facebook's own bot warning is itself
|
|
14
|
+
// an automated action on the account. Dismiss it yourself in the browser instead.
|
|
15
|
+
autoDismissAutomationWarning: false,
|
|
6
16
|
mqtt: { enabled: true, reconnectInterval: 3600 },
|
|
7
17
|
// Opt-in only: auto re-login with credentials (via the third-party API)
|
|
8
18
|
// after a checkpoint/session failure is itself a strong suspicious-login
|
|
9
19
|
// signal and invalidates every other live session on the account.
|
|
10
20
|
autoLogin: false,
|
|
11
|
-
|
|
21
|
+
// No default third-party host: your e-mail/password must never be sent anywhere you
|
|
22
|
+
// did not choose. autoLogin stays off unless you set both autoLogin and apiServer.
|
|
23
|
+
apiServer: "",
|
|
12
24
|
apiKey: "",
|
|
13
25
|
credentials: { email: "", password: "", twofactor: "" },
|
|
14
26
|
antiGetInfo: {
|
package/module/login.js
CHANGED
|
@@ -11,7 +11,12 @@ global.fca = { config };
|
|
|
11
11
|
|
|
12
12
|
// Global error handlers to prevent bot crashes
|
|
13
13
|
// Handle unhandled promise rejections (e.g., fetch timeouts, network errors)
|
|
14
|
-
|
|
14
|
+
// A library must not own the process: the host app (the bot) installs its own handlers and
|
|
15
|
+
// decides what a crash means. These stay as a fallback ONLY for plain scripts that have none,
|
|
16
|
+
// and they no longer swallow everything silently (see the counter below).
|
|
17
|
+
if (!global.fca._errorHandlersInstalled &&
|
|
18
|
+
process.listenerCount("uncaughtException") === 0 &&
|
|
19
|
+
process.listenerCount("unhandledRejection") === 0) {
|
|
15
20
|
global.fca._errorHandlersInstalled = true;
|
|
16
21
|
|
|
17
22
|
process.on("unhandledRejection", (reason, promise) => {
|
|
@@ -73,10 +78,15 @@ if (!global.fca._errorHandlersInstalled) {
|
|
|
73
78
|
return; // Don't crash
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
// Other uncaught exceptions: continuing after one is unsafe (state may be half-updated),
|
|
82
|
+
// so log it and exit after repeated ones; the supervisor restarts the process.
|
|
83
|
+
logger(`Uncaught exception: ${errorMessage}`, "error");
|
|
84
|
+
global.fca._uncaught = (global.fca._uncaught || []).filter(t => Date.now() - t < 10 * 60 * 1000);
|
|
85
|
+
global.fca._uncaught.push(Date.now());
|
|
86
|
+
if (global.fca._uncaught.length >= 5) {
|
|
87
|
+
logger("5 uncaught exceptions in 10 minutes - exiting so the supervisor can restart cleanly", "error");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
80
90
|
} catch (e) {
|
|
81
91
|
// Fallback if logger fails - silent
|
|
82
92
|
}
|
package/module/loginHelper.js
CHANGED
|
@@ -59,7 +59,10 @@ function mask(s, keep = 3) {
|
|
|
59
59
|
*/
|
|
60
60
|
async function loginViaAPI(email, password, twoFactor = null, apiBaseUrl = null, apiKey = null) {
|
|
61
61
|
try {
|
|
62
|
-
const baseUrl = apiBaseUrl || config.apiServer
|
|
62
|
+
const baseUrl = apiBaseUrl || config.apiServer;
|
|
63
|
+
if (!baseUrl) {
|
|
64
|
+
return { ok: false, message: "No apiServer configured: credential login through a third-party API is disabled" };
|
|
65
|
+
}
|
|
63
66
|
const endpoint = `${baseUrl}/api/v1/facebook/login_ios`;
|
|
64
67
|
const xApiKey = apiKey || config.apiKey || null;
|
|
65
68
|
|
|
@@ -505,7 +508,7 @@ async function hydrateJarFromDB(userID) {
|
|
|
505
508
|
}
|
|
506
509
|
}
|
|
507
510
|
|
|
508
|
-
async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef, hadAppStateInput = false) {
|
|
511
|
+
async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef, hadAppStateInput = false, expectedUID = null) {
|
|
509
512
|
// Helper to validate UID - must be a non-zero positive number string
|
|
510
513
|
const isValidUID = uid => uid && uid !== "0" && /^\d+$/.test(uid) && parseInt(uid, 10) > 0;
|
|
511
514
|
|
|
@@ -547,7 +550,9 @@ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions,
|
|
|
547
550
|
}
|
|
548
551
|
|
|
549
552
|
// Try to hydrate from DB backup
|
|
550
|
-
|
|
553
|
+
// Only restore a backup that belongs to the account the caller asked for. Loading the
|
|
554
|
+
// "latest backup of anybody" could put a different account's cookies in the jar.
|
|
555
|
+
const hydrated = (hadAppStateInput && !expectedUID) ? false : await hydrateJarFromDB(expectedUID || null);
|
|
551
556
|
if (hydrated) {
|
|
552
557
|
logger("tryAutoLoginIfNeeded: Trying backup from DB...", "info");
|
|
553
558
|
try {
|
|
@@ -721,6 +726,9 @@ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions,
|
|
|
721
726
|
|
|
722
727
|
function makeLogin(j, email, password, globalOptions) {
|
|
723
728
|
return async function () {
|
|
729
|
+
if (config.autoLogin !== true && config.autoLogin !== "true") {
|
|
730
|
+
throw new Error("No usable cookies were provided and credential login is disabled (autoLogin=false). Provide a fresh appstate/cookie export.");
|
|
731
|
+
}
|
|
724
732
|
const u = email || config.credentials?.email;
|
|
725
733
|
const p = password || config.credentials?.password;
|
|
726
734
|
const tf = config.credentials?.twofactor || null;
|
|
@@ -820,6 +828,16 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
820
828
|
}
|
|
821
829
|
const ctx = { globalOptions, options: globalOptions, reconnectAttempts: 0 };
|
|
822
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
|
+
}
|
|
823
841
|
global.fca = global.fca || {};
|
|
824
842
|
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
825
843
|
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
@@ -835,6 +853,10 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
835
853
|
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
|
836
854
|
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
|
837
855
|
const bypass = async body => {
|
|
856
|
+
if (config.autoDismissAutomationWarning !== true) {
|
|
857
|
+
logger("Facebook is showing its automated-behavior warning. Not dismissing it automatically: open facebook.com in your browser, dismiss it yourself, then restart. (Set autoDismissAutomationWarning=true in fca-config.json to dismiss it automatically.)", "warn");
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
838
860
|
const b = s(body);
|
|
839
861
|
const UID = await getUID(b);
|
|
840
862
|
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
|
@@ -866,7 +888,8 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
866
888
|
}
|
|
867
889
|
return first;
|
|
868
890
|
} catch (e) {
|
|
869
|
-
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;
|
|
870
893
|
return resp;
|
|
871
894
|
}
|
|
872
895
|
};
|
|
@@ -905,6 +928,15 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
905
928
|
const ctx = {};
|
|
906
929
|
ctx.options = globalOptions;
|
|
907
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
|
+
}
|
|
908
940
|
global.fca = global.fca || {};
|
|
909
941
|
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
910
942
|
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
@@ -920,6 +952,10 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
920
952
|
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
|
921
953
|
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
|
922
954
|
const bypass = async body => {
|
|
955
|
+
if (config.autoDismissAutomationWarning !== true) {
|
|
956
|
+
logger("Facebook is showing its automated-behavior warning. Not dismissing it automatically (see autoDismissAutomationWarning in fca-config.json).", "warn");
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
923
959
|
const b = s(body);
|
|
924
960
|
const UID = await getUID(b);
|
|
925
961
|
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
|
@@ -938,7 +974,8 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
938
974
|
}
|
|
939
975
|
logger("✅ No checkpoint detected — session is clean.", "info");
|
|
940
976
|
return res;
|
|
941
|
-
} catch {
|
|
977
|
+
} catch (e) {
|
|
978
|
+
if (e && e.code === "FACEBOOK_SECURITY_CHECKPOINT") throw e;
|
|
942
979
|
return res;
|
|
943
980
|
}
|
|
944
981
|
};
|
|
@@ -970,7 +1007,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
970
1007
|
if (!isValidUID(userID)) {
|
|
971
1008
|
logger("Invalid userID detected (missing or 0), attempting auto-login...", "warn");
|
|
972
1009
|
// Pass hadAppStateInput=true if appState/Cookie was originally provided
|
|
973
|
-
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie));
|
|
1010
|
+
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie), userIDFromAppState);
|
|
974
1011
|
html = retried.html;
|
|
975
1012
|
cookies = retried.cookies;
|
|
976
1013
|
userID = retried.userID;
|
|
@@ -1089,7 +1126,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
1089
1126
|
if (!isValidUID(info.USER_ID)) {
|
|
1090
1127
|
logger("Facebook response shows invalid USER_ID (0 or empty), session is dead!", "warn");
|
|
1091
1128
|
// Force trigger auto-login
|
|
1092
|
-
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie));
|
|
1129
|
+
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie), userIDFromAppState);
|
|
1093
1130
|
html = retried.html;
|
|
1094
1131
|
cookies = retried.cookies;
|
|
1095
1132
|
userID = retried.userID;
|
|
@@ -1358,8 +1395,10 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
1358
1395
|
}
|
|
1359
1396
|
|
|
1360
1397
|
try {
|
|
1361
|
-
|
|
1362
|
-
|
|
1398
|
+
if (config.checkForUpdates === true) {
|
|
1399
|
+
const { checkForUpdate } = require("../src/utils/versionCheck");
|
|
1400
|
+
checkForUpdate(logger);
|
|
1401
|
+
}
|
|
1363
1402
|
} catch (_) {}
|
|
1364
1403
|
logger("🚀 Login successful! Bot is ready.");
|
|
1365
1404
|
callback(null, api);
|
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",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"require": "./index.js",
|
|
10
10
|
"default": "./index.js",
|
|
11
11
|
"types": "./index.d.ts"
|
|
12
|
-
}
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"index.js",
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
"src/app/"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
27
|
-
"test": "
|
|
28
|
+
"test": "node --test",
|
|
28
29
|
"lint": "eslint ."
|
|
29
30
|
},
|
|
30
31
|
"repository": {
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
},
|
|
67
68
|
"homepage": "https://github.com/hridoyhossen48360/hridoy-ultimate#readme",
|
|
68
69
|
"engines": {
|
|
69
|
-
"node": ">=
|
|
70
|
+
"node": ">=18"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"axios": "^1.13.5",
|
|
@@ -75,21 +76,23 @@
|
|
|
75
76
|
"chalk": "^4.1.2",
|
|
76
77
|
"cheerio": "1.0.0-rc.12",
|
|
77
78
|
"duplexify": "^4.1.3",
|
|
79
|
+
"form-data": "^4.0.0",
|
|
78
80
|
"gradient-string": "^2.0.2",
|
|
79
81
|
"https-proxy-agent": "^4.0.0",
|
|
82
|
+
"koffi": "^3.0.2",
|
|
83
|
+
"mime": "^3.0.0",
|
|
80
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",
|
|
81
89
|
"sequelize": "^6.37.6",
|
|
90
|
+
"@signalapp/libsignal-client": "0.70.0",
|
|
82
91
|
"sqlite3": "^5.1.7",
|
|
83
92
|
"totp-generator": "^1.0.0",
|
|
93
|
+
"tough-cookie": "^5.0.0",
|
|
84
94
|
"ws": "^8.18.0",
|
|
85
|
-
"
|
|
86
|
-
"@noble/hashes": "^2.2.0",
|
|
87
|
-
"@signalapp/libsignal-client": "0.70.0",
|
|
88
|
-
"mime": "^3.0.0",
|
|
89
|
-
"protobufjs": "^8.4.0",
|
|
90
|
-
"koffi": "^3.0.2",
|
|
91
|
-
"yumi-json-bigint": "^1.0.0",
|
|
92
|
-
"fca-unofficial": "github:VangBanLaNhat/fca-unofficial"
|
|
95
|
+
"yumi-json-bigint": "^1.0.0"
|
|
93
96
|
},
|
|
94
97
|
"devDependencies": {
|
|
95
98
|
"eslint": "^8.50.0",
|
|
@@ -98,5 +101,8 @@
|
|
|
98
101
|
"publishConfig": {
|
|
99
102
|
"access": "public",
|
|
100
103
|
"registry": "https://registry.npmjs.org/"
|
|
104
|
+
},
|
|
105
|
+
"optionalDependencies": {
|
|
106
|
+
"fca-unofficial": "github:VangBanLaNhat/fca-unofficial"
|
|
101
107
|
}
|
|
102
108
|
}
|
|
@@ -20,14 +20,17 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
20
20
|
// Save function
|
|
21
21
|
function saveAppState() {
|
|
22
22
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const raw = api.getAppState();
|
|
24
|
+
const appState = Array.isArray(raw) ? raw : (raw && Array.isArray(raw.appState) ? raw.appState : null);
|
|
25
|
+
if (!appState || appState.length < 5) {
|
|
26
|
+
logger("AppState is empty or too small, skipping save", "warn");
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
// write-then-rename so a crash mid-write can never leave a truncated cookie file
|
|
31
|
+
const tmp = filePath + ".tmp";
|
|
32
|
+
fs.writeFileSync(tmp, JSON.stringify(appState, null, 2), "utf8");
|
|
33
|
+
fs.renameSync(tmp, filePath);
|
|
31
34
|
logger(`AppState saved to ${filePath}`, "info");
|
|
32
35
|
} catch (error) {
|
|
33
36
|
logger(`Error saving AppState: ${error && error.message ? error.message : String(error)}`, "error");
|
|
@@ -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
|
};
|