@cexy/hoonfca 1.0.7 → 2.0.1
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/LICENSE +3 -0
- package/examples/login-with-cookies.js +102 -0
- package/examples/verify.js +70 -0
- package/index.js +2 -8
- package/package.json +82 -61
- package/src/{api/action → apis}/addExternalModule.js +24 -25
- package/src/apis/addUserToGroup.js +108 -0
- package/src/apis/changeAdminStatus.js +148 -0
- package/src/apis/changeArchivedStatus.js +61 -0
- package/src/apis/changeAvatar.js +103 -0
- package/src/apis/changeBio.js +69 -0
- package/src/apis/changeBlockedStatus.js +54 -0
- package/src/apis/changeGroupImage.js +136 -0
- package/src/apis/changeThreadColor.js +116 -0
- package/src/apis/changeThreadEmoji.js +53 -0
- package/src/apis/comment.js +207 -0
- package/src/apis/createAITheme.js +129 -0
- package/src/apis/createNewGroup.js +79 -0
- package/src/apis/createPoll.js +73 -0
- package/src/apis/deleteMessage.js +44 -0
- package/src/apis/deleteThread.js +52 -0
- package/src/apis/e2ee.js +17 -0
- package/src/apis/editMessage.js +70 -0
- package/src/apis/emoji.js +124 -0
- package/src/apis/fetchThemeData.js +82 -0
- package/src/apis/follow.js +81 -0
- package/src/apis/forwardMessage.js +52 -0
- package/src/apis/friend.js +243 -0
- package/src/apis/gcmember.js +122 -0
- package/src/apis/gcname.js +123 -0
- package/src/apis/gcrule.js +119 -0
- package/src/apis/getAccess.js +111 -0
- package/src/apis/getBotInfo.js +88 -0
- package/src/apis/getBotInitialData.js +43 -0
- package/src/apis/getFriendsList.js +79 -0
- package/src/apis/getMessage.js +423 -0
- package/src/apis/getTheme.js +95 -0
- package/src/apis/getThemeInfo.js +116 -0
- package/src/apis/getThreadHistory.js +239 -0
- package/src/apis/getThreadInfo.js +267 -0
- package/src/apis/getThreadList.js +232 -0
- package/src/apis/getThreadPictures.js +58 -0
- package/src/apis/getUserID.js +117 -0
- package/src/apis/getUserInfo.js +513 -0
- package/src/{api/users → apis}/getUserInfoV2.js +146 -134
- package/src/apis/handleMessageRequest.js +50 -0
- package/src/apis/httpGet.js +63 -0
- package/src/apis/httpPost.js +89 -0
- package/src/apis/httpPostFormData.js +69 -0
- package/src/apis/listenMqtt.js +1236 -0
- package/src/apis/listenSpeed.js +179 -0
- package/src/apis/logout.js +87 -0
- package/src/apis/markAsDelivered.js +47 -0
- package/src/{api/messaging → apis}/markAsRead.js +99 -88
- package/src/apis/markAsReadAll.js +40 -0
- package/src/apis/markAsSeen.js +70 -0
- package/src/apis/mqttDeltaValue.js +278 -0
- package/src/apis/muteThread.js +45 -0
- package/src/apis/nickname.js +132 -0
- package/src/apis/notes.js +163 -0
- package/src/apis/pinMessage.js +150 -0
- package/src/apis/produceMetaTheme.js +180 -0
- package/src/apis/realtime.js +182 -0
- package/src/apis/removeUserFromGroup.js +117 -0
- package/src/apis/resolvePhotoUrl.js +58 -0
- package/src/apis/searchForThread.js +154 -0
- package/src/apis/sendMessage.js +354 -0
- package/src/apis/sendMessageMqtt.js +249 -0
- package/src/apis/sendTypingIndicator.js +91 -0
- package/src/apis/setMessageReaction.js +27 -0
- package/src/apis/setMessageReactionMqtt.js +61 -0
- package/src/apis/setThreadTheme.js +260 -0
- package/src/apis/setThreadThemeMqtt.js +94 -0
- package/src/apis/share.js +107 -0
- package/src/apis/shareContact.js +66 -0
- package/src/apis/stickers.js +257 -0
- package/src/apis/story.js +181 -0
- package/src/apis/theme.js +233 -0
- package/src/apis/unfriend.js +47 -0
- package/src/apis/unsendMessage.js +17 -0
- package/src/engine/client.js +92 -0
- package/src/engine/models/buildAPI.js +152 -0
- package/src/engine/models/loginHelper.js +519 -0
- package/src/engine/models/setOptions.js +88 -0
- package/src/security/e2ee.js +109 -0
- package/src/types/index.d.ts +498 -0
- package/src/utils/antiSuspension.js +506 -0
- package/src/utils/auth-helpers.js +149 -0
- package/src/utils/autoReLogin.js +336 -0
- package/src/utils/axios.js +436 -0
- package/src/utils/cache.js +54 -0
- package/src/utils/clients.js +282 -0
- package/src/utils/constants.js +410 -23
- package/src/utils/formatters/data/formatAttachment.js +370 -0
- package/src/utils/formatters/data/formatDelta.js +109 -0
- package/src/utils/formatters/index.js +159 -0
- package/src/utils/formatters/value/formatCookie.js +91 -0
- package/src/utils/formatters/value/formatDate.js +36 -0
- package/src/utils/formatters/value/formatID.js +16 -0
- package/src/utils/formatters.js +1373 -0
- package/src/utils/headers.js +214 -99
- package/src/utils/index.js +153 -0
- package/src/utils/monitoring.js +333 -0
- package/src/utils/rateLimiter.js +319 -0
- package/src/utils/tokenRefresh.js +657 -0
- package/src/utils/user-agents.js +238 -0
- package/src/utils/validation.js +157 -0
- package/CHANGELOG.md +0 -181
- package/DOCS.md +0 -2636
- package/LICENSE-MIT +0 -21
- package/README.md +0 -632
- package/func/checkUpdate.js +0 -233
- package/func/logger.js +0 -48
- package/index.d.ts +0 -746
- package/module/config.js +0 -67
- package/module/login.js +0 -154
- package/module/loginHelper.js +0 -1034
- package/module/options.js +0 -54
- package/src/api/action/changeAvatar.js +0 -137
- package/src/api/action/changeBio.js +0 -75
- package/src/api/action/enableAutoSaveAppState.js +0 -73
- package/src/api/action/getCurrentUserID.js +0 -7
- package/src/api/action/handleFriendRequest.js +0 -57
- package/src/api/action/logout.js +0 -76
- package/src/api/action/refreshFb_dtsg.js +0 -48
- package/src/api/action/setPostReaction.js +0 -106
- package/src/api/action/unfriend.js +0 -54
- package/src/api/http/httpGet.js +0 -46
- package/src/api/http/httpPost.js +0 -52
- package/src/api/http/postFormData.js +0 -47
- package/src/api/messaging/addUserToGroup.js +0 -68
- package/src/api/messaging/changeAdminStatus.js +0 -122
- package/src/api/messaging/changeArchivedStatus.js +0 -55
- package/src/api/messaging/changeBlockedStatus.js +0 -48
- package/src/api/messaging/changeGroupImage.js +0 -90
- package/src/api/messaging/changeNickname.js +0 -70
- package/src/api/messaging/changeThreadColor.js +0 -79
- package/src/api/messaging/changeThreadEmoji.js +0 -106
- package/src/api/messaging/createNewGroup.js +0 -88
- package/src/api/messaging/createPoll.js +0 -43
- package/src/api/messaging/createThemeAI.js +0 -98
- package/src/api/messaging/deleteMessage.js +0 -56
- package/src/api/messaging/deleteThread.js +0 -56
- package/src/api/messaging/editMessage.js +0 -68
- package/src/api/messaging/forwardAttachment.js +0 -51
- package/src/api/messaging/getEmojiUrl.js +0 -29
- package/src/api/messaging/getFriendsList.js +0 -82
- package/src/api/messaging/getMessage.js +0 -829
- package/src/api/messaging/getThemePictures.js +0 -62
- package/src/api/messaging/handleMessageRequest.js +0 -65
- package/src/api/messaging/markAsDelivered.js +0 -57
- package/src/api/messaging/markAsReadAll.js +0 -49
- package/src/api/messaging/markAsSeen.js +0 -61
- package/src/api/messaging/muteThread.js +0 -50
- package/src/api/messaging/removeUserFromGroup.js +0 -106
- package/src/api/messaging/resolvePhotoUrl.js +0 -43
- package/src/api/messaging/scheduler.js +0 -264
- package/src/api/messaging/searchForThread.js +0 -52
- package/src/api/messaging/sendMessage.js +0 -272
- package/src/api/messaging/sendTypingIndicator.js +0 -67
- package/src/api/messaging/setMessageReaction.js +0 -76
- package/src/api/messaging/setTitle.js +0 -119
- package/src/api/messaging/shareContact.js +0 -49
- package/src/api/messaging/threadColors.js +0 -128
- package/src/api/messaging/unsendMessage.js +0 -81
- package/src/api/messaging/uploadAttachment.js +0 -94
- package/src/api/socket/core/connectMqtt.js +0 -255
- package/src/api/socket/core/emitAuth.js +0 -106
- package/src/api/socket/core/getSeqID.js +0 -40
- package/src/api/socket/core/getTaskResponseData.js +0 -22
- package/src/api/socket/core/markDelivery.js +0 -12
- package/src/api/socket/core/parseDelta.js +0 -391
- package/src/api/socket/detail/buildStream.js +0 -208
- package/src/api/socket/detail/constants.js +0 -24
- package/src/api/socket/listenMqtt.js +0 -364
- package/src/api/socket/middleware/index.js +0 -216
- package/src/api/threads/getThreadHistory.js +0 -664
- package/src/api/threads/getThreadInfo.js +0 -438
- package/src/api/threads/getThreadList.js +0 -293
- package/src/api/threads/getThreadPictures.js +0 -78
- package/src/api/users/getUserID.js +0 -65
- package/src/api/users/getUserInfo.js +0 -327
- package/src/core/sendReqMqtt.js +0 -96
- package/src/database/models/index.js +0 -87
- package/src/database/models/thread.js +0 -45
- package/src/database/models/user.js +0 -46
- package/src/database/threadData.js +0 -98
- package/src/database/userData.js +0 -89
- package/src/utils/client.js +0 -320
- package/src/utils/format.js +0 -1115
- package/src/utils/request.js +0 -305
package/module/loginHelper.js
DELETED
|
@@ -1,1034 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const models = require("../src/database/models");
|
|
5
|
-
const logger = require("../func/logger");
|
|
6
|
-
const { get, post, jar, makeDefaults } = require("../src/utils/request");
|
|
7
|
-
const { saveCookies, getAppState } = require("../src/utils/client");
|
|
8
|
-
const { getFrom } = require("../src/utils/constants");
|
|
9
|
-
const { loadConfig } = require("./config");
|
|
10
|
-
const { config } = loadConfig();
|
|
11
|
-
const { v4: uuidv4 } = require("uuid");
|
|
12
|
-
const { CookieJar } = require("tough-cookie");
|
|
13
|
-
const { wrapper } = require("axios-cookiejar-support");
|
|
14
|
-
const axiosBase = require("axios");
|
|
15
|
-
const qs = require("querystring");
|
|
16
|
-
const crypto = require("crypto");
|
|
17
|
-
const { TOTP } = require("totp-generator");
|
|
18
|
-
|
|
19
|
-
const regions = [
|
|
20
|
-
{ code: "PRN", name: "Pacific Northwest Region", location: "Khu vực Tây Bắc Thái Bình Dương" },
|
|
21
|
-
{ code: "VLL", name: "Valley Region", location: "Valley" },
|
|
22
|
-
{ code: "ASH", name: "Ashburn Region", location: "Ashburn" },
|
|
23
|
-
{ code: "DFW", name: "Dallas/Fort Worth Region", location: "Dallas/Fort Worth" },
|
|
24
|
-
{ code: "LLA", name: "Los Angeles Region", location: "Los Angeles" },
|
|
25
|
-
{ code: "FRA", name: "Frankfurt", location: "Frankfurt" },
|
|
26
|
-
{ code: "SIN", name: "Singapore", location: "Singapore" },
|
|
27
|
-
{ code: "NRT", name: "Tokyo", location: "Japan" },
|
|
28
|
-
{ code: "HKG", name: "Hong Kong", location: "Hong Kong" },
|
|
29
|
-
{ code: "SYD", name: "Sydney", location: "Sydney" },
|
|
30
|
-
{ code: "PNB", name: "Pacific Northwest - Beta", location: "Pacific Northwest " }
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
const REGION_MAP = new Map(regions.map(r => [r.code, r]));
|
|
34
|
-
|
|
35
|
-
function parseRegion(html) {
|
|
36
|
-
try {
|
|
37
|
-
const m1 = html.match(/"endpoint":"([^"]+)"/);
|
|
38
|
-
const m2 = m1 ? null : html.match(/endpoint\\":\\"([^\\"]+)\\"/);
|
|
39
|
-
const raw = (m1 && m1[1]) || (m2 && m2[1]);
|
|
40
|
-
if (!raw) return "PRN";
|
|
41
|
-
const endpoint = raw.replace(/\\\//g, "/");
|
|
42
|
-
const url = new URL(endpoint);
|
|
43
|
-
const rp = url.searchParams ? url.searchParams.get("region") : null;
|
|
44
|
-
return rp ? rp.toUpperCase() : "PRN";
|
|
45
|
-
} catch {
|
|
46
|
-
return "PRN";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function mask(s, keep = 3) {
|
|
51
|
-
if (!s) return "";
|
|
52
|
-
const n = s.length;
|
|
53
|
-
return n <= keep ? "*".repeat(n) : s.slice(0, keep) + "*".repeat(Math.max(0, n - keep));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function md5(s) {
|
|
57
|
-
return crypto.createHash("md5").update(s).digest("hex");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function randomString(length = 24) {
|
|
61
|
-
let s = "abcdefghijklmnopqrstuvwxyz";
|
|
62
|
-
let out = s.charAt(Math.floor(Math.random() * s.length));
|
|
63
|
-
for (let i = 1; i < length; i++) out += "abcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(36 * Math.random()));
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function sortObject(o) {
|
|
68
|
-
const keys = Object.keys(o).sort();
|
|
69
|
-
const x = {};
|
|
70
|
-
for (const k of keys) x[k] = o[k];
|
|
71
|
-
return x;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function encodeSig(obj) {
|
|
75
|
-
let data = "";
|
|
76
|
-
for (const k of Object.keys(obj)) data += `${k}=${obj[k]}`;
|
|
77
|
-
return md5(data + "62f8ce9f74b12f84c123cc23437a4a32");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function rand(min, max) {
|
|
81
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function choice(arr) {
|
|
85
|
-
return arr[Math.floor(Math.random() * arr.length)];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function randomBuildId() {
|
|
89
|
-
const prefixes = ["QP1A", "RP1A", "SP1A", "TP1A", "UP1A", "AP4A"];
|
|
90
|
-
return `${choice(prefixes)}.${rand(180000, 250000)}.${rand(10, 99)}`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function randomResolution() {
|
|
94
|
-
const presets = [{ w: 720, h: 1280, d: 2.0 }, { w: 1080, h: 1920, d: 2.625 }, { w: 1080, h: 2400, d: 3.0 }, { w: 1440, h: 3040, d: 3.5 }, { w: 1440, h: 3200, d: 4.0 }];
|
|
95
|
-
return choice(presets);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function randomFbav() {
|
|
99
|
-
return `${rand(390, 499)}.${rand(0, 3)}.${rand(0, 2)}.${rand(10, 60)}.${rand(100, 999)}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function randomOrcaUA() {
|
|
103
|
-
const androidVersions = ["8.1.0", "9", "10", "11", "12", "13", "14"];
|
|
104
|
-
const devices = [{ brand: "samsung", model: "SM-G996B" }, { brand: "samsung", model: "SM-S908E" }, { brand: "Xiaomi", model: "M2101K9AG" }, { brand: "OPPO", model: "CPH2219" }, { brand: "vivo", model: "V2109" }, { brand: "HUAWEI", model: "VOG-L29" }, { brand: "asus", model: "ASUS_I001DA" }, { brand: "Google", model: "Pixel 6" }, { brand: "realme", model: "RMX2170" }];
|
|
105
|
-
const carriers = ["Viettel Telecom", "Mobifone", "Vinaphone", "T-Mobile", "Verizon", "AT&T", "Telkomsel", "Jio", "NTT DOCOMO", "Vodafone", "Orange"];
|
|
106
|
-
const locales = ["vi_VN", "en_US", "en_GB", "id_ID", "th_TH", "fr_FR", "de_DE", "es_ES", "pt_BR"];
|
|
107
|
-
const archs = ["arm64-v8a", "armeabi-v7a"];
|
|
108
|
-
const a = choice(androidVersions);
|
|
109
|
-
const d = choice(devices);
|
|
110
|
-
const b = randomBuildId();
|
|
111
|
-
const r = randomResolution();
|
|
112
|
-
const fbav = randomFbav();
|
|
113
|
-
const fbbv = rand(320000000, 520000000);
|
|
114
|
-
const arch = `${choice(archs)}:${choice(archs)}`;
|
|
115
|
-
const ua = `Dalvik/2.1.0 (Linux; U; Android ${a}; ${d.model} Build/${b}) [FBAN/Orca-Android;FBAV/${fbav};FBPN/com.facebook.orca;FBLC/${choice(locales)};FBBV/${fbbv};FBCR/${choice(carriers)};FBMF/${d.brand};FBBD/${d.brand};FBDV/${d.model};FBSV/${a};FBCA/${arch};FBDM/{density=${r.d.toFixed(1)},width=${r.w},height=${r.h}};FB_FW/1;]`;
|
|
116
|
-
return ua;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const MOBILE_UA = randomOrcaUA();
|
|
120
|
-
|
|
121
|
-
function buildHeaders(url, extra = {}) {
|
|
122
|
-
const u = new URL(url);
|
|
123
|
-
return { "content-type": "application/x-www-form-urlencoded", "x-fb-http-engine": "Liger", "user-agent": MOBILE_UA, Host: u.host, Origin: "https://www.facebook.com", Referer: "https://www.facebook.com/", Connection: "keep-alive", ...extra };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const genTotp = async secret => {
|
|
127
|
-
const cleaned = String(secret || "").replace(/\s+/g, "").toUpperCase();
|
|
128
|
-
const r = await TOTP.generate(cleaned);
|
|
129
|
-
return typeof r === "object" ? r.otp : r;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
function normalizeCookieHeaderString(s) {
|
|
133
|
-
let str = String(s || "").trim();
|
|
134
|
-
if (!str) return [];
|
|
135
|
-
if (/^cookie\s*:/i.test(str)) str = str.replace(/^cookie\s*:/i, "").trim();
|
|
136
|
-
str = str.replace(/\r?\n/g, " ").replace(/\s*;\s*/g, ";");
|
|
137
|
-
const parts = str.split(";").map(v => v.trim()).filter(Boolean);
|
|
138
|
-
const out = [];
|
|
139
|
-
for (const p of parts) {
|
|
140
|
-
const eq = p.indexOf("=");
|
|
141
|
-
if (eq <= 0) continue;
|
|
142
|
-
const k = p.slice(0, eq).trim();
|
|
143
|
-
const v = p.slice(eq + 1).trim().replace(/^"(.*)"$/, "$1");
|
|
144
|
-
if (!k) continue;
|
|
145
|
-
out.push(`${k}=${v}`);
|
|
146
|
-
}
|
|
147
|
-
return out;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function setJarFromPairs(j, pairs, domain) {
|
|
151
|
-
const expires = new Date(Date.now() + 31536e6).toUTCString();
|
|
152
|
-
for (const kv of pairs) {
|
|
153
|
-
const cookieStr = `${kv}; expires=${expires}; domain=${domain}; path=/;`;
|
|
154
|
-
try {
|
|
155
|
-
if (typeof j.setCookieSync === "function") j.setCookieSync(cookieStr, "https://www.facebook.com");
|
|
156
|
-
else j.setCookie(cookieStr, "https://www.facebook.com");
|
|
157
|
-
} catch { }
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function cookieHeaderFromJar(j) {
|
|
162
|
-
const urls = ["https://www.facebook.com", "https://www.messenger.com"];
|
|
163
|
-
const seen = new Set();
|
|
164
|
-
const parts = [];
|
|
165
|
-
for (const u of urls) {
|
|
166
|
-
let s = "";
|
|
167
|
-
try {
|
|
168
|
-
s = typeof j.getCookieStringSync === "function" ? j.getCookieStringSync(u) : "";
|
|
169
|
-
} catch { }
|
|
170
|
-
if (!s) continue;
|
|
171
|
-
for (const kv of s.split(";")) {
|
|
172
|
-
const t = kv.trim();
|
|
173
|
-
const name = t.split("=")[0];
|
|
174
|
-
if (!name || seen.has(name)) continue;
|
|
175
|
-
seen.add(name);
|
|
176
|
-
parts.push(t);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return parts.join("; ");
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let uniqueIndexEnsured = false;
|
|
183
|
-
|
|
184
|
-
function getBackupModel() {
|
|
185
|
-
try {
|
|
186
|
-
if (!models || !models.sequelize || !models.Sequelize) return null;
|
|
187
|
-
const sequelize = models.sequelize;
|
|
188
|
-
|
|
189
|
-
// Validate that sequelize is a proper Sequelize instance
|
|
190
|
-
if (!sequelize || typeof sequelize.define !== "function") return null;
|
|
191
|
-
|
|
192
|
-
const { DataTypes } = models.Sequelize;
|
|
193
|
-
if (sequelize.models && sequelize.models.AppStateBackup) return sequelize.models.AppStateBackup;
|
|
194
|
-
const dialect = typeof sequelize.getDialect === "function" ? sequelize.getDialect() : "sqlite";
|
|
195
|
-
const LongText = (dialect === "mysql" || dialect === "mariadb") ? DataTypes.TEXT("long") : DataTypes.TEXT;
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const AppStateBackup = sequelize.define(
|
|
199
|
-
"AppStateBackup",
|
|
200
|
-
{
|
|
201
|
-
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
202
|
-
userID: { type: DataTypes.STRING, allowNull: false },
|
|
203
|
-
type: { type: DataTypes.STRING, allowNull: false },
|
|
204
|
-
data: { type: LongText }
|
|
205
|
-
},
|
|
206
|
-
{ tableName: "app_state_backups", timestamps: true, indexes: [{ unique: true, fields: ["userID", "type"] }] }
|
|
207
|
-
);
|
|
208
|
-
return AppStateBackup;
|
|
209
|
-
} catch (defineError) {
|
|
210
|
-
// If define fails, log and return null
|
|
211
|
-
logger(`Failed to define AppStateBackup model: ${defineError && defineError.message ? defineError.message : String(defineError)}`, "warn");
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
} catch (e) {
|
|
215
|
-
// Silently handle any errors in getBackupModel
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
async function ensureUniqueIndex(sequelize) {
|
|
221
|
-
if (uniqueIndexEnsured || !sequelize) return;
|
|
222
|
-
try {
|
|
223
|
-
if (typeof sequelize.getQueryInterface !== "function") return;
|
|
224
|
-
await sequelize.getQueryInterface().addIndex("app_state_backups", ["userID", "type"], { unique: true, name: "app_state_user_type_unique" });
|
|
225
|
-
} catch { }
|
|
226
|
-
uniqueIndexEnsured = true;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async function upsertBackup(Model, userID, type, data) {
|
|
230
|
-
const where = { userID: String(userID || ""), type };
|
|
231
|
-
const row = await Model.findOne({ where });
|
|
232
|
-
if (row) {
|
|
233
|
-
await row.update({ data });
|
|
234
|
-
logger(`Overwrote existing ${type} backup for user ${where.userID}`, "info");
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
await Model.create({ ...where, data });
|
|
238
|
-
logger(`Created new ${type} backup for user ${where.userID}`, "info");
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async function backupAppStateSQL(j, userID) {
|
|
242
|
-
try {
|
|
243
|
-
const Model = getBackupModel();
|
|
244
|
-
if (!Model) return;
|
|
245
|
-
if (!models || !models.sequelize) return;
|
|
246
|
-
await Model.sync();
|
|
247
|
-
await ensureUniqueIndex(models.sequelize);
|
|
248
|
-
const appJson = getAppState(j);
|
|
249
|
-
const ck = cookieHeaderFromJar(j);
|
|
250
|
-
await upsertBackup(Model, userID, "appstate", JSON.stringify(appJson));
|
|
251
|
-
await upsertBackup(Model, userID, "cookie", ck);
|
|
252
|
-
try {
|
|
253
|
-
const out = path.join(process.cwd(), "appstate.json");
|
|
254
|
-
fs.writeFileSync(out, JSON.stringify(appJson, null, 2));
|
|
255
|
-
} catch { }
|
|
256
|
-
logger("Backup stored (overwrite mode)", "info");
|
|
257
|
-
} catch (e) {
|
|
258
|
-
logger(`Failed to save appstate backup ${e && e.message ? e.message : String(e)}`, "warn");
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async function getLatestBackup(userID, type) {
|
|
263
|
-
try {
|
|
264
|
-
const Model = getBackupModel();
|
|
265
|
-
if (!Model) return null;
|
|
266
|
-
const row = await Model.findOne({ where: { userID: String(userID || ""), type } });
|
|
267
|
-
return row ? row.data : null;
|
|
268
|
-
} catch {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async function getLatestBackupAny(type) {
|
|
274
|
-
try {
|
|
275
|
-
const Model = getBackupModel();
|
|
276
|
-
if (!Model) return null;
|
|
277
|
-
const row = await Model.findOne({ where: { type }, order: [["updatedAt", "DESC"]] });
|
|
278
|
-
return row ? row.data : null;
|
|
279
|
-
} catch {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const MESSENGER_USER_AGENT = "Dalvik/2.1.0 (Linux; U; Android 9; ASUS_Z01QD Build/PQ3A.190605.003) [FBAN/Orca-Android;FBAV/391.2.0.20.404;FBPN/com.facebook.orca;FBLC/vi_VN;FBBV/437533963;FBCR/Viettel Telecom;FBMF/asus;FBBD/asus;FBDV/ASUS_Z01QD;FBSV/9;FBCA/x86:armeabi-v7a;FBDM/{density=1.5,width=1600,height=900};FB_FW/1;]";
|
|
285
|
-
|
|
286
|
-
function encodesig(obj) {
|
|
287
|
-
let data = "";
|
|
288
|
-
Object.keys(obj).forEach(k => { data += `${k}=${obj[k]}`; });
|
|
289
|
-
return md5(data + "62f8ce9f74b12f84c123cc23437a4a32");
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function sort(obj) {
|
|
293
|
-
const keys = Object.keys(obj).sort();
|
|
294
|
-
const out = {};
|
|
295
|
-
for (const k of keys) out[k] = obj[k];
|
|
296
|
-
return out;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async function setJarCookies(j, appstate) {
|
|
300
|
-
const tasks = [];
|
|
301
|
-
for (const c of appstate) {
|
|
302
|
-
const cookieName = c.name || c.key;
|
|
303
|
-
const cookieValue = c.value;
|
|
304
|
-
if (!cookieName || cookieValue === undefined) continue;
|
|
305
|
-
|
|
306
|
-
const cookieDomain = c.domain || ".facebook.com";
|
|
307
|
-
const cookiePath = c.path || "/";
|
|
308
|
-
const dom = cookieDomain.replace(/^\./, "");
|
|
309
|
-
|
|
310
|
-
// Handle expirationDate (can be in seconds or milliseconds)
|
|
311
|
-
let expiresStr = "";
|
|
312
|
-
if (c.expirationDate !== undefined) {
|
|
313
|
-
let expiresDate;
|
|
314
|
-
if (typeof c.expirationDate === "number") {
|
|
315
|
-
// If expirationDate is less than a year from now in seconds, treat as seconds
|
|
316
|
-
// Otherwise treat as milliseconds
|
|
317
|
-
const now = Date.now();
|
|
318
|
-
const oneYearInMs = 365 * 24 * 60 * 60 * 1000;
|
|
319
|
-
if (c.expirationDate < (now + oneYearInMs) / 1000) {
|
|
320
|
-
expiresDate = new Date(c.expirationDate * 1000);
|
|
321
|
-
} else {
|
|
322
|
-
expiresDate = new Date(c.expirationDate);
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
expiresDate = new Date(c.expirationDate);
|
|
326
|
-
}
|
|
327
|
-
expiresStr = `; expires=${expiresDate.toUTCString()}`;
|
|
328
|
-
} else if (c.expires) {
|
|
329
|
-
const expiresDate = typeof c.expires === "number" ? new Date(c.expires) : new Date(c.expires);
|
|
330
|
-
expiresStr = `; expires=${expiresDate.toUTCString()}`;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Helper function to build cookie string
|
|
334
|
-
const buildCookieString = (domainOverride = null) => {
|
|
335
|
-
const domain = domainOverride || cookieDomain;
|
|
336
|
-
let cookieParts = [`${cookieName}=${cookieValue}${expiresStr}`];
|
|
337
|
-
cookieParts.push(`Domain=${domain}`);
|
|
338
|
-
cookieParts.push(`Path=${cookiePath}`);
|
|
339
|
-
|
|
340
|
-
// Add Secure flag if secure is true
|
|
341
|
-
if (c.secure === true) {
|
|
342
|
-
cookieParts.push("Secure");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Add HttpOnly flag if httpOnly is true
|
|
346
|
-
if (c.httpOnly === true) {
|
|
347
|
-
cookieParts.push("HttpOnly");
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Add SameSite attribute if provided
|
|
351
|
-
if (c.sameSite) {
|
|
352
|
-
const sameSiteValue = String(c.sameSite).toLowerCase();
|
|
353
|
-
if (["strict", "lax", "none"].includes(sameSiteValue)) {
|
|
354
|
-
cookieParts.push(`SameSite=${sameSiteValue.charAt(0).toUpperCase() + sameSiteValue.slice(1)}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return cookieParts.join("; ");
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
// Determine target URLs and cookie strings based on domain
|
|
362
|
-
const cookieConfigs = [];
|
|
363
|
-
|
|
364
|
-
// For .facebook.com domain, set for both facebook.com and messenger.com
|
|
365
|
-
if (cookieDomain === ".facebook.com" || cookieDomain === "facebook.com") {
|
|
366
|
-
// Set for facebook.com with .facebook.com domain
|
|
367
|
-
cookieConfigs.push({ url: `http://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
368
|
-
cookieConfigs.push({ url: `https://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
369
|
-
cookieConfigs.push({ url: `http://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
370
|
-
cookieConfigs.push({ url: `https://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
371
|
-
|
|
372
|
-
// Set for messenger.com with .messenger.com domain (or without domain for host-only)
|
|
373
|
-
// Use .messenger.com domain to allow cross-subdomain sharing
|
|
374
|
-
cookieConfigs.push({ url: `http://messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
375
|
-
cookieConfigs.push({ url: `https://messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
376
|
-
cookieConfigs.push({ url: `http://www.messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
377
|
-
cookieConfigs.push({ url: `https://www.messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
378
|
-
} else if (cookieDomain === ".messenger.com" || cookieDomain === "messenger.com") {
|
|
379
|
-
// Set for messenger.com only
|
|
380
|
-
const messengerDom = cookieDomain.replace(/^\./, "");
|
|
381
|
-
cookieConfigs.push({ url: `http://${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
382
|
-
cookieConfigs.push({ url: `https://${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
383
|
-
cookieConfigs.push({ url: `http://www.${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
384
|
-
cookieConfigs.push({ url: `https://www.${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
385
|
-
} else {
|
|
386
|
-
// For other domains, set normally
|
|
387
|
-
cookieConfigs.push({ url: `http://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
388
|
-
cookieConfigs.push({ url: `https://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
389
|
-
cookieConfigs.push({ url: `http://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
390
|
-
cookieConfigs.push({ url: `https://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Set cookie for all target URLs, silently catch domain errors
|
|
394
|
-
for (const config of cookieConfigs) {
|
|
395
|
-
tasks.push(j.setCookie(config.cookieStr, config.url).catch((err) => {
|
|
396
|
-
// Silently ignore domain mismatch errors for cross-domain cookies
|
|
397
|
-
// These are expected when setting cookies across domains
|
|
398
|
-
if (err && err.message && err.message.includes("Cookie not in this host's domain")) {
|
|
399
|
-
return; // Expected error, ignore
|
|
400
|
-
}
|
|
401
|
-
// Log other errors but don't throw
|
|
402
|
-
return;
|
|
403
|
-
}));
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
await Promise.all(tasks);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async function loginViaGraph(username, password, twofactorSecretOrCode, i_user, externalJar) {
|
|
410
|
-
const cookieJar = externalJar instanceof CookieJar ? externalJar : new CookieJar();
|
|
411
|
-
const client = wrapper(axiosBase.create({ jar: cookieJar, withCredentials: true, timeout: 30000, validateStatus: () => true }));
|
|
412
|
-
const device_id = uuidv4();
|
|
413
|
-
const family_device_id = device_id;
|
|
414
|
-
const machine_id = randomString(24);
|
|
415
|
-
const base = {
|
|
416
|
-
adid: "00000000-0000-0000-0000-000000000000",
|
|
417
|
-
format: "json",
|
|
418
|
-
device_id,
|
|
419
|
-
email: username,
|
|
420
|
-
password,
|
|
421
|
-
generate_analytics_claim: "1",
|
|
422
|
-
community_id: "",
|
|
423
|
-
cpl: "true",
|
|
424
|
-
try_num: "1",
|
|
425
|
-
family_device_id,
|
|
426
|
-
secure_family_device_id: "",
|
|
427
|
-
credentials_type: "password",
|
|
428
|
-
enroll_misauth: "false",
|
|
429
|
-
generate_session_cookies: "1",
|
|
430
|
-
source: "login",
|
|
431
|
-
generate_machine_id: "1",
|
|
432
|
-
jazoest: "22297",
|
|
433
|
-
meta_inf_fbmeta: "NO_FILE",
|
|
434
|
-
advertiser_id: "00000000-0000-0000-0000-000000000000",
|
|
435
|
-
currently_logged_in_userid: "0",
|
|
436
|
-
locale: "vi_VN",
|
|
437
|
-
client_country_code: "VN",
|
|
438
|
-
fb_api_req_friendly_name: "authenticate",
|
|
439
|
-
fb_api_caller_class: "AuthOperations$PasswordAuthOperation",
|
|
440
|
-
api_key: "256002347743983",
|
|
441
|
-
access_token: "256002347743983|374e60f8b9bb6b8cbb30f78030438895"
|
|
442
|
-
};
|
|
443
|
-
const headers = {
|
|
444
|
-
"User-Agent": MESSENGER_USER_AGENT,
|
|
445
|
-
"Accept-Encoding": "gzip, deflate",
|
|
446
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
447
|
-
"x-fb-connection-quality": "EXCELLENT",
|
|
448
|
-
"x-fb-sim-hni": "45204",
|
|
449
|
-
"x-fb-net-hni": "45204",
|
|
450
|
-
"x-fb-connection-type": "WIFI",
|
|
451
|
-
"x-tigon-is-retry": "False",
|
|
452
|
-
"x-fb-friendly-name": "authenticate",
|
|
453
|
-
"x-fb-request-analytics-tags": '{"network_tags":{"retry_attempt":"0"},"application_tags":"unknown"}',
|
|
454
|
-
"x-fb-http-engine": "Liger",
|
|
455
|
-
"x-fb-client-ip": "True",
|
|
456
|
-
"x-fb-server-cluster": "True",
|
|
457
|
-
authorization: `OAuth ${base.access_token}`
|
|
458
|
-
};
|
|
459
|
-
const form1 = { ...base };
|
|
460
|
-
form1.sig = encodesig(sort(form1));
|
|
461
|
-
const res1 = await client.request({ url: "https://b-graph.facebook.com/auth/login", method: "post", data: qs.stringify(form1), headers });
|
|
462
|
-
if (res1.status === 200 && res1.data && res1.data.session_cookies) {
|
|
463
|
-
const appstate = res1.data.session_cookies.map(c => ({ key: c.name, value: c.value, domain: c.domain, path: c.path }));
|
|
464
|
-
const cUserCookie = appstate.find(c => c.key === "c_user");
|
|
465
|
-
if (i_user) appstate.push({ key: "i_user", value: i_user, domain: ".facebook.com", path: "/" });
|
|
466
|
-
else if (cUserCookie) appstate.push({ key: "i_user", value: cUserCookie.value, domain: ".facebook.com", path: "/" });
|
|
467
|
-
await setJarCookies(cookieJar, appstate);
|
|
468
|
-
let eaau = null;
|
|
469
|
-
let eaad6v7 = null;
|
|
470
|
-
try {
|
|
471
|
-
const r1 = await client.request({ url: `https://api.facebook.com/method/auth.getSessionforApp?format=json&access_token=${res1.data.access_token}&new_app_id=350685531728`, method: "get", headers: { "user-agent": MESSENGER_USER_AGENT, "x-fb-connection-type": "WIFI", authorization: `OAuth ${res1.data.access_token}` } });
|
|
472
|
-
eaau = r1.data && r1.data.access_token ? r1.data.access_token : null;
|
|
473
|
-
} catch { }
|
|
474
|
-
try {
|
|
475
|
-
const r2 = await client.request({ url: `https://api.facebook.com/method/auth.getSessionforApp?format=json&access_token=${res1.data.access_token}&new_app_id=275254692598279`, method: "get", headers: { "user-agent": MESSENGER_USER_AGENT, "x-fb-connection-type": "WIFI", authorization: `OAuth ${res1.data.access_token}` } });
|
|
476
|
-
eaad6v7 = r2.data && r2.data.access_token ? r2.data.access_token : null;
|
|
477
|
-
} catch { }
|
|
478
|
-
return { ok: true, cookies: appstate.map(c => ({ key: c.key, value: c.value })), jar: cookieJar, access_token_mess: res1.data.access_token || null, access_token: eaau, access_token_eaad6v7: eaad6v7, uid: res1.data.uid || cUserCookie?.value || null, session_key: res1.data.session_key || null };
|
|
479
|
-
}
|
|
480
|
-
const err = res1 && res1.data && res1.data.error ? res1.data.error : {};
|
|
481
|
-
if (err && err.code === 406) {
|
|
482
|
-
const data = err.error_data || {};
|
|
483
|
-
let code = null;
|
|
484
|
-
if (twofactorSecretOrCode && /^\d{6}$/.test(String(twofactorSecretOrCode))) code = String(twofactorSecretOrCode);
|
|
485
|
-
else if (twofactorSecretOrCode) {
|
|
486
|
-
try {
|
|
487
|
-
const clean = decodeURI(twofactorSecretOrCode).replace(/\s+/g, "").toUpperCase();
|
|
488
|
-
const { otp } = await TOTP.generate(clean);
|
|
489
|
-
code = otp;
|
|
490
|
-
} catch { }
|
|
491
|
-
} else if (config.credentials?.twofactor) {
|
|
492
|
-
const { otp } = await TOTP.generate(String(config.credentials.twofactor).replace(/\s+/g, "").toUpperCase());
|
|
493
|
-
code = otp;
|
|
494
|
-
}
|
|
495
|
-
if (!code) return { ok: false, message: "2FA required" };
|
|
496
|
-
const form2 = {
|
|
497
|
-
...base,
|
|
498
|
-
credentials_type: "two_factor",
|
|
499
|
-
twofactor_code: code,
|
|
500
|
-
userid: data.uid || username,
|
|
501
|
-
first_factor: data.login_first_factor || "",
|
|
502
|
-
machine_id: data.machine_id || machine_id
|
|
503
|
-
};
|
|
504
|
-
form2.sig = encodesig(sort(form2));
|
|
505
|
-
const res2 = await client.request({ url: "https://b-graph.facebook.com/auth/login", method: "post", data: qs.stringify(form2), headers });
|
|
506
|
-
if (res2.status === 200 && res2.data && res2.data.session_cookies) {
|
|
507
|
-
const appstate = res2.data.session_cookies.map(c => ({ key: c.name, value: c.value, domain: c.domain, path: c.path }));
|
|
508
|
-
const cUserCookie = appstate.find(c => c.key === "c_user");
|
|
509
|
-
if (i_user) appstate.push({ key: "i_user", value: i_user, domain: ".facebook.com", path: "/" });
|
|
510
|
-
else if (cUserCookie) appstate.push({ key: "i_user", value: cUserCookie.value, domain: ".facebook.com", path: "/" });
|
|
511
|
-
await setJarCookies(cookieJar, appstate);
|
|
512
|
-
let eaau = null;
|
|
513
|
-
let eaad6v7 = null;
|
|
514
|
-
try {
|
|
515
|
-
const r1 = await client.request({ url: `https://api.facebook.com/method/auth.getSessionforApp?format=json&access_token=${res2.data.access_token}&new_app_id=350685531728`, method: "get", headers: { "user-agent": MESSENGER_USER_AGENT, "x-fb-connection-type": "WIFI", authorization: `OAuth ${res2.data.access_token}` } });
|
|
516
|
-
eaau = r1.data && r1.data.access_token ? r1.data.access_token : null;
|
|
517
|
-
} catch { }
|
|
518
|
-
try {
|
|
519
|
-
const r2 = await client.request({ url: `https://api.facebook.com/method/auth.getSessionforApp?format=json&access_token=${res2.data.access_token}&new_app_id=275254692598279`, method: "get", headers: { "user-agent": MESSENGER_USER_AGENT, "x-fb-connection-type": "WIFI", authorization: `OAuth ${res2.data.access_token}` } });
|
|
520
|
-
eaad6v7 = r2.data && r2.data.access_token ? r2.data.access_token : null;
|
|
521
|
-
} catch { }
|
|
522
|
-
return { ok: true, cookies: appstate.map(c => ({ key: c.key, value: c.value })), jar: cookieJar, access_token_mess: res2.data.access_token || null, access_token: eaau, access_token_eaad6v7: eaad6v7, uid: res2.data.uid || cUserCookie?.value || null, session_key: res2.data.session_key || null };
|
|
523
|
-
}
|
|
524
|
-
return { ok: false, message: "2FA failed" };
|
|
525
|
-
}
|
|
526
|
-
return { ok: false, message: "Login failed" };
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
async function tokens(username, password, twofactor = null) {
|
|
530
|
-
const t0 = process.hrtime.bigint();
|
|
531
|
-
if (!username || !password) return { status: false, message: "Please provide email and password" };
|
|
532
|
-
logger(`AUTO-LOGIN: Initialize login ${mask(username, 2)}`, "info");
|
|
533
|
-
const res = await loginViaGraph(username, password, twofactor, null, jar);
|
|
534
|
-
if (res && res.ok && Array.isArray(res.cookies)) {
|
|
535
|
-
logger(`AUTO-LOGIN: Login success ${res.cookies.length} cookies`, "info");
|
|
536
|
-
const t1 = Number(process.hrtime.bigint() - t0) / 1e6;
|
|
537
|
-
logger(`Done success login ${Math.round(t1)}ms`, "info");
|
|
538
|
-
return { status: true, cookies: res.cookies };
|
|
539
|
-
}
|
|
540
|
-
if (res && res.message === "2FA required") {
|
|
541
|
-
logger("AUTO-LOGIN: 2FA required but secret missing", "warn");
|
|
542
|
-
return { status: false, message: "Please provide the 2FA secret!" };
|
|
543
|
-
}
|
|
544
|
-
return { status: false, message: res && res.message ? res.message : "Login failed" };
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
async function hydrateJarFromDB(userID) {
|
|
548
|
-
try {
|
|
549
|
-
let ck = null;
|
|
550
|
-
let app = null;
|
|
551
|
-
if (userID) {
|
|
552
|
-
ck = await getLatestBackup(userID, "cookie");
|
|
553
|
-
app = await getLatestBackup(userID, "appstate");
|
|
554
|
-
} else {
|
|
555
|
-
ck = await getLatestBackupAny("cookie");
|
|
556
|
-
app = await getLatestBackupAny("appstate");
|
|
557
|
-
}
|
|
558
|
-
if (ck) {
|
|
559
|
-
const pairs = normalizeCookieHeaderString(ck);
|
|
560
|
-
if (pairs.length) {
|
|
561
|
-
setJarFromPairs(jar, pairs, ".facebook.com");
|
|
562
|
-
return true;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
if (app) {
|
|
566
|
-
let parsed = null;
|
|
567
|
-
try {
|
|
568
|
-
parsed = JSON.parse(app);
|
|
569
|
-
} catch { }
|
|
570
|
-
if (Array.isArray(parsed)) {
|
|
571
|
-
const pairs = parsed.map(c => [c.name || c.key, c.value].join("="));
|
|
572
|
-
setJarFromPairs(jar, pairs, ".facebook.com");
|
|
573
|
-
return true;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return false;
|
|
577
|
-
} catch {
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef, hadAppStateInput = false) {
|
|
583
|
-
const getUID = cs =>
|
|
584
|
-
cs.find(c => c.key === "i_user")?.value ||
|
|
585
|
-
cs.find(c => c.key === "c_user")?.value ||
|
|
586
|
-
cs.find(c => c.name === "i_user")?.value ||
|
|
587
|
-
cs.find(c => c.name === "c_user")?.value;
|
|
588
|
-
const htmlUID = body => {
|
|
589
|
-
const s = typeof body === "string" ? body : String(body ?? "");
|
|
590
|
-
return s.match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s.match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
|
591
|
-
};
|
|
592
|
-
let userID = getUID(currentCookies);
|
|
593
|
-
// Also try to extract userID from HTML if not found in cookies
|
|
594
|
-
if (!userID) {
|
|
595
|
-
userID = htmlUID(currentHtml);
|
|
596
|
-
}
|
|
597
|
-
if (userID) return { html: currentHtml, cookies: currentCookies, userID };
|
|
598
|
-
// If appState/Cookie was provided and is not dead (not checkpointed), skip backup
|
|
599
|
-
if (hadAppStateInput) {
|
|
600
|
-
const isCheckpoint = currentHtml.includes("/checkpoint/block/?next");
|
|
601
|
-
if (!isCheckpoint) {
|
|
602
|
-
// AppState provided and not checkpointed, but userID not found
|
|
603
|
-
// This might be a temporary issue - try to refresh cookies from jar
|
|
604
|
-
try {
|
|
605
|
-
const refreshedCookies = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
|
606
|
-
userID = getUID(refreshedCookies);
|
|
607
|
-
if (userID) {
|
|
608
|
-
return { html: currentHtml, cookies: refreshedCookies, userID };
|
|
609
|
-
}
|
|
610
|
-
} catch { }
|
|
611
|
-
// If still no userID, skip backup and throw error
|
|
612
|
-
throw new Error("Missing user cookie from provided appState");
|
|
613
|
-
}
|
|
614
|
-
// AppState is dead (checkpointed), proceed to backup/email login
|
|
615
|
-
}
|
|
616
|
-
const hydrated = await hydrateJarFromDB(null);
|
|
617
|
-
if (hydrated) {
|
|
618
|
-
logger("AppState backup live — proceeding to login", "info");
|
|
619
|
-
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
620
|
-
const resB = (await ctxRef.bypassAutomation(initial, jar)) || initial;
|
|
621
|
-
const htmlB = resB && resB.data ? resB.data : "";
|
|
622
|
-
if (htmlB.includes("/checkpoint/block/?next")) throw new Error("Checkpoint");
|
|
623
|
-
const cookiesB = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
|
624
|
-
const uidB = getUID(cookiesB);
|
|
625
|
-
if (uidB) return { html: htmlB, cookies: cookiesB, userID: uidB };
|
|
626
|
-
}
|
|
627
|
-
if (config.autoLogin !== true) throw new Error("AppState backup die — Auto-login is disabled");
|
|
628
|
-
logger("AppState backup die — proceeding to email/password login", "warn");
|
|
629
|
-
const u = config.credentials?.email;
|
|
630
|
-
const p = config.credentials?.password;
|
|
631
|
-
const tf = config.credentials?.twofactor || null;
|
|
632
|
-
if (!u || !p) throw new Error("Missing user cookie");
|
|
633
|
-
const r = await tokens(u, p, tf);
|
|
634
|
-
if (!(r && r.status && Array.isArray(r.cookies))) throw new Error(r && r.message ? r.message : "Login failed");
|
|
635
|
-
const pairs = r.cookies.map(c => `${c.key || c.name}=${c.value}`);
|
|
636
|
-
setJarFromPairs(jar, pairs, ".facebook.com");
|
|
637
|
-
const initial2 = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
638
|
-
const res2 = (await ctxRef.bypassAutomation(initial2, jar)) || initial2;
|
|
639
|
-
const html2 = res2 && res2.data ? res2.data : "";
|
|
640
|
-
if (html2.includes("/checkpoint/block/?next")) throw new Error("Checkpoint");
|
|
641
|
-
const cookies2 = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
|
642
|
-
const uid2 = getUID(cookies2);
|
|
643
|
-
if (!uid2) throw new Error("Login failed");
|
|
644
|
-
return { html: html2, cookies: cookies2, userID: uid2 };
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function makeLogin(j, email, password, globalOptions) {
|
|
648
|
-
return async function () {
|
|
649
|
-
const u = email || config.credentials?.email;
|
|
650
|
-
const p = password || config.credentials?.password;
|
|
651
|
-
const tf = config.credentials?.twofactor || null;
|
|
652
|
-
if (!u || !p) return;
|
|
653
|
-
const r = await tokens(u, p, tf);
|
|
654
|
-
if (r && r.status && Array.isArray(r.cookies)) {
|
|
655
|
-
const pairs = r.cookies.map(c => `${c.key || c.name}=${c.value}`);
|
|
656
|
-
setJarFromPairs(j, pairs, ".facebook.com");
|
|
657
|
-
await get("https://www.facebook.com/", j, null, globalOptions).then(saveCookies(j));
|
|
658
|
-
} else {
|
|
659
|
-
throw new Error(r && r.message ? r.message : "Login failed");
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function loginHelper(appState, Cookie, email, password, globalOptions, callback) {
|
|
665
|
-
try {
|
|
666
|
-
const domain = ".facebook.com";
|
|
667
|
-
// Helper to extract userID from appState input
|
|
668
|
-
const extractUIDFromAppState = (appStateInput) => {
|
|
669
|
-
if (!appStateInput) return null;
|
|
670
|
-
let parsed = appStateInput;
|
|
671
|
-
if (typeof appStateInput === "string") {
|
|
672
|
-
try {
|
|
673
|
-
parsed = JSON.parse(appStateInput);
|
|
674
|
-
} catch {
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
if (Array.isArray(parsed)) {
|
|
679
|
-
const cUser = parsed.find(c => (c.key === "c_user" || c.name === "c_user"));
|
|
680
|
-
if (cUser) return cUser.value;
|
|
681
|
-
const iUser = parsed.find(c => (c.key === "i_user" || c.name === "i_user"));
|
|
682
|
-
if (iUser) return iUser.value;
|
|
683
|
-
}
|
|
684
|
-
return null;
|
|
685
|
-
};
|
|
686
|
-
let userIDFromAppState = extractUIDFromAppState(appState);
|
|
687
|
-
(async () => {
|
|
688
|
-
try {
|
|
689
|
-
if (appState) {
|
|
690
|
-
// Check and convert cookie to appState format
|
|
691
|
-
if (Array.isArray(appState) && appState.some(c => c.name)) {
|
|
692
|
-
// Convert name to key if needed
|
|
693
|
-
appState = appState.map(c => {
|
|
694
|
-
if (c.name && !c.key) {
|
|
695
|
-
c.key = c.name;
|
|
696
|
-
delete c.name;
|
|
697
|
-
}
|
|
698
|
-
return c;
|
|
699
|
-
});
|
|
700
|
-
} else if (typeof appState === "string") {
|
|
701
|
-
// Try to parse as JSON first
|
|
702
|
-
let parsed = appState;
|
|
703
|
-
try {
|
|
704
|
-
parsed = JSON.parse(appState);
|
|
705
|
-
} catch { }
|
|
706
|
-
|
|
707
|
-
if (Array.isArray(parsed)) {
|
|
708
|
-
// Already parsed as array, use it
|
|
709
|
-
appState = parsed;
|
|
710
|
-
} else {
|
|
711
|
-
// Parse string cookie format (key=value; key2=value2)
|
|
712
|
-
const arrayAppState = [];
|
|
713
|
-
appState.split(';').forEach(c => {
|
|
714
|
-
const [key, value] = c.split('=');
|
|
715
|
-
if (key && value) {
|
|
716
|
-
arrayAppState.push({
|
|
717
|
-
key: key.trim(),
|
|
718
|
-
value: value.trim(),
|
|
719
|
-
domain: ".facebook.com",
|
|
720
|
-
path: "/",
|
|
721
|
-
expires: new Date().getTime() + 1000 * 60 * 60 * 24 * 365
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
appState = arrayAppState;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// Set cookies into jar with individual domain/path
|
|
730
|
-
if (Array.isArray(appState)) {
|
|
731
|
-
await setJarCookies(jar, appState);
|
|
732
|
-
} else {
|
|
733
|
-
throw new Error("Invalid appState format");
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (Cookie) {
|
|
737
|
-
let cookiePairs = [];
|
|
738
|
-
if (typeof Cookie === "string") cookiePairs = normalizeCookieHeaderString(Cookie);
|
|
739
|
-
else if (Array.isArray(Cookie)) cookiePairs = Cookie.map(String).filter(Boolean);
|
|
740
|
-
else if (Cookie && typeof Cookie === "object") cookiePairs = Object.entries(Cookie).map(([k, v]) => `${k}=${v}`);
|
|
741
|
-
if (cookiePairs.length) setJarFromPairs(jar, cookiePairs, domain);
|
|
742
|
-
}
|
|
743
|
-
} catch (e) {
|
|
744
|
-
return callback(e);
|
|
745
|
-
}
|
|
746
|
-
const ctx = { globalOptions, options: globalOptions, reconnectAttempts: 0 };
|
|
747
|
-
ctx.bypassAutomation = async function (resp, j) {
|
|
748
|
-
global.fca = global.fca || {};
|
|
749
|
-
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
750
|
-
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
751
|
-
const u = r => r?.request?.res?.responseUrl || (r?.config?.baseURL ? new URL(r.config.url || "/", r.config.baseURL).toString() : r?.config?.url || "");
|
|
752
|
-
const isCp = r => typeof u(r) === "string" && u(r).includes("checkpoint/601051028565049");
|
|
753
|
-
const cookieUID = async () => {
|
|
754
|
-
try {
|
|
755
|
-
const cookies = typeof j?.getCookies === "function" ? await j.getCookies("https://www.facebook.com") : [];
|
|
756
|
-
return cookies.find(c => c.key === "i_user")?.value || cookies.find(c => c.key === "c_user")?.value;
|
|
757
|
-
} catch { return undefined; }
|
|
758
|
-
};
|
|
759
|
-
const htmlUID = body => s(body).match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s(body).match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
|
760
|
-
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
|
761
|
-
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
|
762
|
-
const bypass = async body => {
|
|
763
|
-
const b = s(body);
|
|
764
|
-
const UID = await getUID(b);
|
|
765
|
-
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
|
766
|
-
const jazoest = getFrom(b, 'name="jazoest" value="', '"') || getFrom(b, "jazoest=", '",') || b.match(/name="jazoest"\s+value="([^"]+)"/)?.[1];
|
|
767
|
-
const lsd = getFrom(b, '["LSD",[],{"token":"', '"}') || b.match(/name="lsd"\s+value="([^"]+)"/)?.[1];
|
|
768
|
-
const form = { av: UID, fb_dtsg, jazoest, lsd, fb_api_caller_class: "RelayModern", fb_api_req_friendly_name: "FBScrapingWarningMutation", variables: "{}", server_timestamps: true, doc_id: 6339492849481770 };
|
|
769
|
-
await post("https://www.facebook.com/api/graphql/", j, form, null, this.options).then(saveCookies(j));
|
|
770
|
-
logger("Facebook automation warning detected, handling...", "warn");
|
|
771
|
-
this.reconnectAttempts = 0;
|
|
772
|
-
};
|
|
773
|
-
try {
|
|
774
|
-
if (resp) {
|
|
775
|
-
if (isCp(resp)) {
|
|
776
|
-
await bypass(s(resp.data));
|
|
777
|
-
const refreshed = await refreshJar();
|
|
778
|
-
if (isCp(refreshed)) logger("Checkpoint still present after refresh", "warn");
|
|
779
|
-
else logger("Bypass complete, cookies refreshed", "info");
|
|
780
|
-
return refreshed;
|
|
781
|
-
}
|
|
782
|
-
return resp;
|
|
783
|
-
}
|
|
784
|
-
const first = await get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
|
785
|
-
if (isCp(first)) {
|
|
786
|
-
await bypass(s(first.data));
|
|
787
|
-
const refreshed = await refreshJar();
|
|
788
|
-
if (!isCp(refreshed)) logger("Bypass complete, cookies refreshed", "info");
|
|
789
|
-
else logger("Checkpoint still present after refresh", "warn");
|
|
790
|
-
return refreshed;
|
|
791
|
-
}
|
|
792
|
-
return first;
|
|
793
|
-
} catch (e) {
|
|
794
|
-
logger(`Bypass automation error: ${e && e.message ? e.message : String(e)}`, "error");
|
|
795
|
-
return resp;
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
if (appState || Cookie) {
|
|
799
|
-
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
800
|
-
return (await ctx.bypassAutomation(initial, jar)) || initial;
|
|
801
|
-
}
|
|
802
|
-
const hydrated = await hydrateJarFromDB(null);
|
|
803
|
-
if (hydrated) {
|
|
804
|
-
logger("AppState backup live — proceeding to login", "info");
|
|
805
|
-
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
806
|
-
return (await ctx.bypassAutomation(initial, jar)) || initial;
|
|
807
|
-
}
|
|
808
|
-
logger("AppState backup die — proceeding to email/password login", "warn");
|
|
809
|
-
return get("https://www.facebook.com/", null, null, globalOptions)
|
|
810
|
-
.then(saveCookies(jar))
|
|
811
|
-
.then(makeLogin(jar, email, password, globalOptions))
|
|
812
|
-
.then(function () {
|
|
813
|
-
return get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
814
|
-
});
|
|
815
|
-
})()
|
|
816
|
-
.then(async function (res) {
|
|
817
|
-
const ctx = {};
|
|
818
|
-
ctx.options = globalOptions;
|
|
819
|
-
ctx.bypassAutomation = async function (resp, j) {
|
|
820
|
-
global.fca = global.fca || {};
|
|
821
|
-
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
|
822
|
-
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
|
823
|
-
const u = r => r?.request?.res?.responseUrl || (r?.config?.baseURL ? new URL(r.config.url || "/", r.config.baseURL).toString() : r?.config?.url || "");
|
|
824
|
-
const isCp = r => typeof u(r) === "string" && u(r).includes("checkpoint/601051028565049");
|
|
825
|
-
const cookieUID = async () => {
|
|
826
|
-
try {
|
|
827
|
-
const cookies = typeof j?.getCookies === "function" ? await j.getCookies("https://www.facebook.com") : [];
|
|
828
|
-
return cookies.find(c => c.key === "i_user")?.value || cookies.find(c => c.key === "c_user")?.value;
|
|
829
|
-
} catch { return undefined; }
|
|
830
|
-
};
|
|
831
|
-
const htmlUID = body => s(body).match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s(body).match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
|
832
|
-
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
|
833
|
-
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
|
834
|
-
const bypass = async body => {
|
|
835
|
-
const b = s(body);
|
|
836
|
-
const UID = await getUID(b);
|
|
837
|
-
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
|
838
|
-
const jazoest = getFrom(b, 'name="jazoest" value="', '"') || getFrom(b, "jazoest=", '",') || b.match(/name="jazoest"\s+value="([^"]+)"/)?.[1];
|
|
839
|
-
const lsd = getFrom(b, '["LSD",[],{"token":"', '"}') || b.match(/name="lsd"\s+value="([^"]+)"/)?.[1];
|
|
840
|
-
const form = { av: UID, fb_dtsg, jazoest, lsd, fb_api_caller_class: "RelayModern", fb_api_req_friendly_name: "FBScrapingWarningMutation", variables: "{}", server_timestamps: true, doc_id: 6339492849481770 };
|
|
841
|
-
await post("https://www.facebook.com/api/graphql/", j, form, null, this.options).then(saveCookies(j));
|
|
842
|
-
logger("Facebook automation warning detected, handling...", "warn");
|
|
843
|
-
};
|
|
844
|
-
try {
|
|
845
|
-
if (res && isCp(res)) {
|
|
846
|
-
await bypass(s(res.data));
|
|
847
|
-
const refreshed = await refreshJar();
|
|
848
|
-
if (!isCp(refreshed)) logger("Bypass complete, cookies refreshed", "info");
|
|
849
|
-
return refreshed;
|
|
850
|
-
}
|
|
851
|
-
logger("No checkpoint detected", "info");
|
|
852
|
-
return res;
|
|
853
|
-
} catch {
|
|
854
|
-
return res;
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
|
-
const processed = (await ctx.bypassAutomation(res, jar)) || res;
|
|
858
|
-
let html = processed && processed.data ? processed.data : "";
|
|
859
|
-
let cookies = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
|
860
|
-
const getUIDFromCookies = cs =>
|
|
861
|
-
cs.find(c => c.key === "i_user")?.value ||
|
|
862
|
-
cs.find(c => c.key === "c_user")?.value ||
|
|
863
|
-
cs.find(c => c.name === "i_user")?.value ||
|
|
864
|
-
cs.find(c => c.name === "c_user")?.value;
|
|
865
|
-
const getUIDFromHTML = body => {
|
|
866
|
-
const s = typeof body === "string" ? body : String(body ?? "");
|
|
867
|
-
return s.match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s.match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
|
868
|
-
};
|
|
869
|
-
let userID = getUIDFromCookies(cookies);
|
|
870
|
-
// Also try to extract userID from HTML if not found in cookies
|
|
871
|
-
if (!userID) {
|
|
872
|
-
userID = getUIDFromHTML(html);
|
|
873
|
-
}
|
|
874
|
-
// If still not found and appState was provided, use userID from appState input as fallback
|
|
875
|
-
if (!userID && userIDFromAppState) {
|
|
876
|
-
userID = userIDFromAppState;
|
|
877
|
-
}
|
|
878
|
-
if (!userID) {
|
|
879
|
-
// Pass hadAppStateInput=true if appState/Cookie was originally provided
|
|
880
|
-
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie));
|
|
881
|
-
html = retried.html;
|
|
882
|
-
cookies = retried.cookies;
|
|
883
|
-
userID = retried.userID;
|
|
884
|
-
}
|
|
885
|
-
if (html.includes("/checkpoint/block/?next")) {
|
|
886
|
-
logger("Appstate die, vui lòng thay cái mới!", "error");
|
|
887
|
-
throw new Error("Checkpoint");
|
|
888
|
-
}
|
|
889
|
-
let mqttEndpoint;
|
|
890
|
-
let region = "PRN";
|
|
891
|
-
let fb_dtsg;
|
|
892
|
-
let irisSeqID;
|
|
893
|
-
try {
|
|
894
|
-
const m1 = html.match(/"endpoint":"([^"]+)"/);
|
|
895
|
-
const m2 = m1 ? null : html.match(/endpoint\\":\\"([^\\"]+)\\"/);
|
|
896
|
-
const raw = (m1 && m1[1]) || (m2 && m2[1]);
|
|
897
|
-
if (raw) mqttEndpoint = raw.replace(/\\\//g, "/");
|
|
898
|
-
region = parseRegion(html);
|
|
899
|
-
const rinfo = REGION_MAP.get(region);
|
|
900
|
-
if (rinfo) logger(`Server region ${region} - ${rinfo.name}`, "info");
|
|
901
|
-
else logger(`Server region ${region}`, "info");
|
|
902
|
-
} catch {
|
|
903
|
-
logger("Not MQTT endpoint", "warn");
|
|
904
|
-
}
|
|
905
|
-
try {
|
|
906
|
-
const userDataMatch = String(html).match(/\["CurrentUserInitialData",\[\],({.*?}),\d+\]/);
|
|
907
|
-
if (userDataMatch) {
|
|
908
|
-
const info = JSON.parse(userDataMatch[1]);
|
|
909
|
-
logger(`Đăng nhập tài khoản: ${info.NAME} (${info.USER_ID})`, "info");
|
|
910
|
-
} else if (userID) {
|
|
911
|
-
logger(`ID người dùng: ${userID}`, "info");
|
|
912
|
-
}
|
|
913
|
-
} catch { }
|
|
914
|
-
const tokenMatch = html.match(/DTSGInitialData.*?token":"(.*?)"/);
|
|
915
|
-
if (tokenMatch) fb_dtsg = tokenMatch[1];
|
|
916
|
-
try {
|
|
917
|
-
if (userID) await backupAppStateSQL(jar, userID);
|
|
918
|
-
} catch { }
|
|
919
|
-
Promise.resolve()
|
|
920
|
-
.then(function () {
|
|
921
|
-
if (models && models.sequelize && typeof models.sequelize.authenticate === "function") {
|
|
922
|
-
return models.sequelize.authenticate();
|
|
923
|
-
}
|
|
924
|
-
})
|
|
925
|
-
.then(function () {
|
|
926
|
-
if (models && typeof models.syncAll === "function") {
|
|
927
|
-
return models.syncAll();
|
|
928
|
-
}
|
|
929
|
-
})
|
|
930
|
-
.catch(function (error) {
|
|
931
|
-
// Silently handle database errors - they're not critical for login
|
|
932
|
-
const errorMsg = error && error.message ? error.message : String(error);
|
|
933
|
-
if (!errorMsg.includes("No Sequelize instance passed")) {
|
|
934
|
-
// Only log non-Sequelize instance errors
|
|
935
|
-
logger(`Database connection failed: ${errorMsg}`, "warn");
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
logger("FCA fix/update by DongDev (Donix-VN)", "info");
|
|
939
|
-
const ctxMain = {
|
|
940
|
-
userID,
|
|
941
|
-
jar,
|
|
942
|
-
globalOptions,
|
|
943
|
-
loggedIn: true,
|
|
944
|
-
access_token: "NONE",
|
|
945
|
-
clientMutationId: 0,
|
|
946
|
-
mqttClient: undefined,
|
|
947
|
-
lastSeqId: irisSeqID,
|
|
948
|
-
syncToken: undefined,
|
|
949
|
-
mqttEndpoint,
|
|
950
|
-
region,
|
|
951
|
-
firstListen: true,
|
|
952
|
-
fb_dtsg,
|
|
953
|
-
clientID: ((Math.random() * 2147483648) | 0).toString(16),
|
|
954
|
-
clientId: getFrom(html, '["MqttWebDeviceID",[],{"clientID":"', '"}') || "",
|
|
955
|
-
wsReqNumber: 0,
|
|
956
|
-
wsTaskNumber: 0,
|
|
957
|
-
tasks: new Map()
|
|
958
|
-
};
|
|
959
|
-
ctxMain.options = globalOptions;
|
|
960
|
-
ctxMain.bypassAutomation = ctx.bypassAutomation.bind(ctxMain);
|
|
961
|
-
ctxMain.performAutoLogin = async () => {
|
|
962
|
-
try {
|
|
963
|
-
const u = config.credentials?.email || email;
|
|
964
|
-
const p = config.credentials?.password || password;
|
|
965
|
-
const tf = config.credentials?.twofactor || null;
|
|
966
|
-
if (!u || !p) return false;
|
|
967
|
-
const r = await tokens(u, p, tf);
|
|
968
|
-
if (!(r && r.status && Array.isArray(r.cookies))) return false;
|
|
969
|
-
const pairs = r.cookies.map(c => `${c.key || c.name}=${c.value}`);
|
|
970
|
-
setJarFromPairs(jar, pairs, ".facebook.com");
|
|
971
|
-
await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
|
972
|
-
return true;
|
|
973
|
-
} catch {
|
|
974
|
-
return false;
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
const api = {
|
|
978
|
-
setOptions: require("./options").setOptions.bind(null, globalOptions),
|
|
979
|
-
getCookies: function () {
|
|
980
|
-
return cookieHeaderFromJar(jar);
|
|
981
|
-
},
|
|
982
|
-
getAppState: function () {
|
|
983
|
-
return getAppState(jar);
|
|
984
|
-
},
|
|
985
|
-
getLatestAppStateFromDB: async function (uid = userID) {
|
|
986
|
-
const data = await getLatestBackup(uid, "appstate");
|
|
987
|
-
return data ? JSON.parse(data) : null;
|
|
988
|
-
},
|
|
989
|
-
getLatestCookieFromDB: async function (uid = userID) {
|
|
990
|
-
return await getLatestBackup(uid, "cookie");
|
|
991
|
-
}
|
|
992
|
-
};
|
|
993
|
-
const defaultFuncs = makeDefaults(html, userID, ctxMain);
|
|
994
|
-
const srcRoot = path.join(__dirname, "../src/api");
|
|
995
|
-
let loaded = 0;
|
|
996
|
-
let skipped = 0;
|
|
997
|
-
fs.readdirSync(srcRoot, { withFileTypes: true }).forEach((sub) => {
|
|
998
|
-
if (!sub.isDirectory()) return;
|
|
999
|
-
const subDir = path.join(srcRoot, sub.name);
|
|
1000
|
-
fs.readdirSync(subDir, { withFileTypes: true }).forEach((entry) => {
|
|
1001
|
-
if (!entry.isFile() || !entry.name.endsWith(".js")) return;
|
|
1002
|
-
const p = path.join(subDir, entry.name);
|
|
1003
|
-
const key = path.basename(entry.name, ".js");
|
|
1004
|
-
if (api[key]) {
|
|
1005
|
-
skipped++;
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
api[key] = require(p)(defaultFuncs, api, ctxMain);
|
|
1009
|
-
loaded++;
|
|
1010
|
-
});
|
|
1011
|
-
});
|
|
1012
|
-
logger(`Loaded ${loaded} FCA API methods${skipped ? `, skipped ${skipped} duplicates` : ""}`);
|
|
1013
|
-
if (api.listenMqtt) api.listen = api.listenMqtt;
|
|
1014
|
-
if (api.refreshFb_dtsg) {
|
|
1015
|
-
setInterval(function () {
|
|
1016
|
-
api.refreshFb_dtsg().then(function () {
|
|
1017
|
-
logger("Successfully refreshed fb_dtsg");
|
|
1018
|
-
}).catch(function () {
|
|
1019
|
-
logger("An error occurred while refreshing fb_dtsg", "error");
|
|
1020
|
-
});
|
|
1021
|
-
}, 86400000);
|
|
1022
|
-
}
|
|
1023
|
-
logger("Login successful!");
|
|
1024
|
-
callback(null, api);
|
|
1025
|
-
})
|
|
1026
|
-
.catch(function (e) {
|
|
1027
|
-
callback(e);
|
|
1028
|
-
});
|
|
1029
|
-
} catch (e) {
|
|
1030
|
-
callback(e);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
module.exports = loginHelper;
|