@dongdev/fca-unofficial 2.0.19 → 2.0.21
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 +6 -0
- package/module/loginHelper.js +111 -12
- package/package.json +1 -1
- package/src/api/messaging/editMessage.js +68 -0
- package/src/utils/client.js +2 -2
package/CHANGELOG.md
CHANGED
package/module/loginHelper.js
CHANGED
@@ -298,7 +298,7 @@ async function tokens(username, password, twofactor = null) {
|
|
298
298
|
return { status: false, message: "Please provide the 2FA secret!" };
|
299
299
|
}
|
300
300
|
try {
|
301
|
-
const dataErr = e && e.data && e.data.error && e.data.error.error_data ? e.data.
|
301
|
+
const dataErr = e && e.data && e.data.error && e.data.error.error_data ? e.data.error_data : {};
|
302
302
|
const codeTotp = await genTotp(config.credentials.twofactor);
|
303
303
|
logger(`AUTO-LOGIN: Performing 2FA ${mask(codeTotp, 2)}`, "info");
|
304
304
|
const form2 = { ...baseForm, twofactor_code: codeTotp, encrypted_msisdn: "", userid: dataErr.uid || "", machine_id: dataErr.machine_id || baseForm.machine_id, first_factor: dataErr.login_first_factor || "", credentials_type: "two_factor" };
|
@@ -355,7 +355,7 @@ async function hydrateJarFromDB(userID) {
|
|
355
355
|
}
|
356
356
|
}
|
357
357
|
|
358
|
-
async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions) {
|
358
|
+
async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef) {
|
359
359
|
const getUID = cs =>
|
360
360
|
cs.find(c => c.key === "i_user")?.value ||
|
361
361
|
cs.find(c => c.key === "c_user")?.value ||
|
@@ -366,7 +366,8 @@ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions)
|
|
366
366
|
const hydrated = await hydrateJarFromDB(null);
|
367
367
|
if (hydrated) {
|
368
368
|
logger("AppState backup live — proceeding to login", "info");
|
369
|
-
const
|
369
|
+
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
370
|
+
const resB = (await ctxRef.bypassAutomation(initial, jar)) || initial;
|
370
371
|
const htmlB = resB && resB.data ? resB.data : "";
|
371
372
|
if (htmlB.includes("/checkpoint/block/?next")) throw new Error("Checkpoint");
|
372
373
|
const cookiesB = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
@@ -383,7 +384,8 @@ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions)
|
|
383
384
|
if (!(r && r.status && Array.isArray(r.cookies))) throw new Error(r && r.message ? r.message : "Login failed");
|
384
385
|
const pairs = r.cookies.map(c => `${c.key || c.name}=${c.value}`);
|
385
386
|
setJarFromPairs(jar, pairs, ".facebook.com");
|
386
|
-
const
|
387
|
+
const initial2 = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
388
|
+
const res2 = (await ctxRef.bypassAutomation(initial2, jar)) || initial2;
|
387
389
|
const html2 = res2 && res2.data ? res2.data : "";
|
388
390
|
if (html2.includes("/checkpoint/block/?next")) throw new Error("Checkpoint");
|
389
391
|
const cookies2 = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
@@ -447,13 +449,67 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
447
449
|
return callback(e);
|
448
450
|
}
|
449
451
|
(async () => {
|
452
|
+
const ctx = { globalOptions, options: globalOptions, reconnectAttempts: 0 };
|
453
|
+
ctx.bypassAutomation = async function (resp, j) {
|
454
|
+
global.fca = global.fca || {};
|
455
|
+
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
456
|
+
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
457
|
+
const u = r => r?.request?.res?.responseUrl || (r?.config?.baseURL ? new URL(r.config.url || "/", r.config.baseURL).toString() : r?.config?.url || "");
|
458
|
+
const isCp = r => typeof u(r) === "string" && u(r).includes("checkpoint/601051028565049");
|
459
|
+
const cookieUID = async () => {
|
460
|
+
try {
|
461
|
+
const cookies = typeof j?.getCookies === "function" ? await j.getCookies("https://www.facebook.com") : [];
|
462
|
+
return cookies.find(c => c.key === "i_user")?.value || cookies.find(c => c.key === "c_user")?.value;
|
463
|
+
} catch { return undefined; }
|
464
|
+
};
|
465
|
+
const htmlUID = body => s(body).match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s(body).match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
466
|
+
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
467
|
+
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
468
|
+
const bypass = async body => {
|
469
|
+
const b = s(body);
|
470
|
+
const UID = await getUID(b);
|
471
|
+
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
472
|
+
const jazoest = getFrom(b, 'name="jazoest" value="', '"') || getFrom(b, "jazoest=", '",') || b.match(/name="jazoest"\s+value="([^"]+)"/)?.[1];
|
473
|
+
const lsd = getFrom(b, '["LSD",[],{"token":"', '"}') || b.match(/name="lsd"\s+value="([^"]+)"/)?.[1];
|
474
|
+
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 };
|
475
|
+
await post("https://www.facebook.com/api/graphql/", j, form, null, this.options).then(saveCookies(j));
|
476
|
+
logger("Facebook automation warning detected, handling...", "warn");
|
477
|
+
this.reconnectAttempts = 0;
|
478
|
+
};
|
479
|
+
try {
|
480
|
+
if (resp) {
|
481
|
+
if (isCp(resp)) {
|
482
|
+
await bypass(s(resp.data));
|
483
|
+
const refreshed = await refreshJar();
|
484
|
+
if (isCp(refreshed)) logger("Checkpoint still present after refresh", "warn");
|
485
|
+
else logger("Bypass complete, cookies refreshed", "info");
|
486
|
+
return refreshed;
|
487
|
+
}
|
488
|
+
return resp;
|
489
|
+
}
|
490
|
+
const first = await get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
491
|
+
if (isCp(first)) {
|
492
|
+
await bypass(s(first.data));
|
493
|
+
const refreshed = await refreshJar();
|
494
|
+
if (!isCp(refreshed)) logger("Bypass complete, cookies refreshed", "info");
|
495
|
+
else logger("Checkpoint still present after refresh", "warn");
|
496
|
+
return refreshed;
|
497
|
+
}
|
498
|
+
return first;
|
499
|
+
} catch (e) {
|
500
|
+
logger(`Bypass automation error: ${e && e.message ? e.message : String(e)}`, "error");
|
501
|
+
return resp;
|
502
|
+
}
|
503
|
+
};
|
450
504
|
if (appState || Cookie) {
|
451
|
-
|
505
|
+
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
506
|
+
return (await ctx.bypassAutomation(initial, jar)) || initial;
|
452
507
|
}
|
453
508
|
const hydrated = await hydrateJarFromDB(null);
|
454
509
|
if (hydrated) {
|
455
510
|
logger("AppState backup live — proceeding to login", "info");
|
456
|
-
|
511
|
+
const initial = await get("https://www.facebook.com/", jar, null, globalOptions).then(saveCookies(jar));
|
512
|
+
return (await ctx.bypassAutomation(initial, jar)) || initial;
|
457
513
|
}
|
458
514
|
logger("AppState backup die — proceeding to email/password login", "warn");
|
459
515
|
return get("https://www.facebook.com/", null, null, globalOptions)
|
@@ -464,7 +520,48 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
464
520
|
});
|
465
521
|
})()
|
466
522
|
.then(async function (res) {
|
467
|
-
|
523
|
+
const ctx = {};
|
524
|
+
ctx.options = globalOptions;
|
525
|
+
ctx.bypassAutomation = async function (resp, j) {
|
526
|
+
global.fca = global.fca || {};
|
527
|
+
global.fca.BypassAutomationNotification = this.bypassAutomation.bind(this);
|
528
|
+
const s = x => (typeof x === "string" ? x : String(x ?? ""));
|
529
|
+
const u = r => r?.request?.res?.responseUrl || (r?.config?.baseURL ? new URL(r.config.url || "/", r.config.baseURL).toString() : r?.config?.url || "");
|
530
|
+
const isCp = r => typeof u(r) === "string" && u(r).includes("checkpoint/601051028565049");
|
531
|
+
const cookieUID = async () => {
|
532
|
+
try {
|
533
|
+
const cookies = typeof j?.getCookies === "function" ? await j.getCookies("https://www.facebook.com") : [];
|
534
|
+
return cookies.find(c => c.key === "i_user")?.value || cookies.find(c => c.key === "c_user")?.value;
|
535
|
+
} catch { return undefined; }
|
536
|
+
};
|
537
|
+
const htmlUID = body => s(body).match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] || s(body).match(/\["CurrentUserInitialData",\[\],\{.*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
|
538
|
+
const getUID = async body => (await cookieUID()) || htmlUID(body);
|
539
|
+
const refreshJar = async () => get("https://www.facebook.com/", j, null, this.options).then(saveCookies(j));
|
540
|
+
const bypass = async body => {
|
541
|
+
const b = s(body);
|
542
|
+
const UID = await getUID(b);
|
543
|
+
const fb_dtsg = getFrom(b, '"DTSGInitData",[],{"token":"', '",') || b.match(/name="fb_dtsg"\s+value="([^"]+)"/)?.[1];
|
544
|
+
const jazoest = getFrom(b, 'name="jazoest" value="', '"') || getFrom(b, "jazoest=", '",') || b.match(/name="jazoest"\s+value="([^"]+)"/)?.[1];
|
545
|
+
const lsd = getFrom(b, '["LSD",[],{"token":"', '"}') || b.match(/name="lsd"\s+value="([^"]+)"/)?.[1];
|
546
|
+
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 };
|
547
|
+
await post("https://www.facebook.com/api/graphql/", j, form, null, this.options).then(saveCookies(j));
|
548
|
+
logger("Facebook automation warning detected, handling...", "warn");
|
549
|
+
};
|
550
|
+
try {
|
551
|
+
if (res && isCp(res)) {
|
552
|
+
await bypass(s(res.data));
|
553
|
+
const refreshed = await refreshJar();
|
554
|
+
if (!isCp(refreshed)) logger("Bypass complete, cookies refreshed", "info");
|
555
|
+
return refreshed;
|
556
|
+
}
|
557
|
+
logger("No checkpoint detected", "info");
|
558
|
+
return res;
|
559
|
+
} catch {
|
560
|
+
return res;
|
561
|
+
}
|
562
|
+
};
|
563
|
+
const processed = (await ctx.bypassAutomation(res, jar)) || res;
|
564
|
+
let html = processed && processed.data ? processed.data : "";
|
468
565
|
let cookies = await Promise.resolve(jar.getCookies("https://www.facebook.com"));
|
469
566
|
let userID =
|
470
567
|
cookies.find(c => c.key === "i_user")?.value ||
|
@@ -472,7 +569,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
472
569
|
cookies.find(c => c.name === "i_user")?.value ||
|
473
570
|
cookies.find(c => c.name === "c_user")?.value;
|
474
571
|
if (!userID) {
|
475
|
-
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions);
|
572
|
+
const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx);
|
476
573
|
html = retried.html;
|
477
574
|
cookies = retried.cookies;
|
478
575
|
userID = retried.userID;
|
@@ -527,7 +624,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
527
624
|
console.error("Database connection failed:", error && error.message ? error.message : String(error));
|
528
625
|
});
|
529
626
|
logger("FCA fix/update by DongDev (Donix-VN)", "info");
|
530
|
-
const
|
627
|
+
const ctxMain = {
|
531
628
|
userID,
|
532
629
|
jar,
|
533
630
|
globalOptions,
|
@@ -547,7 +644,9 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
547
644
|
wsTaskNumber: 0,
|
548
645
|
tasks: new Map()
|
549
646
|
};
|
550
|
-
|
647
|
+
ctxMain.options = globalOptions;
|
648
|
+
ctxMain.bypassAutomation = ctx.bypassAutomation.bind(ctxMain);
|
649
|
+
ctxMain.performAutoLogin = async () => {
|
551
650
|
try {
|
552
651
|
const u = config.credentials?.email || email;
|
553
652
|
const p = config.credentials?.password || password;
|
@@ -579,7 +678,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
579
678
|
return await getLatestBackup(uid, "cookie");
|
580
679
|
}
|
581
680
|
};
|
582
|
-
const defaultFuncs = makeDefaults(html, userID,
|
681
|
+
const defaultFuncs = makeDefaults(html, userID, ctxMain);
|
583
682
|
const srcRoot = path.join(__dirname, "../src/api");
|
584
683
|
let loaded = 0;
|
585
684
|
let skipped = 0;
|
@@ -594,7 +693,7 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
594
693
|
skipped++;
|
595
694
|
return;
|
596
695
|
}
|
597
|
-
api[key] = require(p)(defaultFuncs, api,
|
696
|
+
api[key] = require(p)(defaultFuncs, api, ctxMain);
|
598
697
|
loaded++;
|
599
698
|
});
|
600
699
|
});
|
package/package.json
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
"use_strict";
|
2
|
+
|
3
|
+
const { generateOfflineThreadingID } = require("../../utils/format.js");
|
4
|
+
module.exports = (defaultFuncs, api, ctx) => {
|
5
|
+
return (text, messageID, callback) => {
|
6
|
+
let reqID = ctx.wsReqNumber + 1;
|
7
|
+
var resolveFunc = () => { };
|
8
|
+
var rejectFunc = () => { };
|
9
|
+
var returnPromise = new Promise((resolve, reject) => {
|
10
|
+
resolveFunc = resolve;
|
11
|
+
rejectFunc = reject;
|
12
|
+
});
|
13
|
+
if (!callback) {
|
14
|
+
callback = (err, data) => {
|
15
|
+
if (err) {
|
16
|
+
return rejectFunc(err);
|
17
|
+
}
|
18
|
+
resolveFunc(data);
|
19
|
+
};
|
20
|
+
}
|
21
|
+
const content = {
|
22
|
+
app_id: '2220391788200892',
|
23
|
+
payload: JSON.stringify({
|
24
|
+
data_trace_id: null,
|
25
|
+
epoch_id: parseInt(generateOfflineThreadingID()),
|
26
|
+
tasks: [{
|
27
|
+
failure_count: null,
|
28
|
+
label: '742',
|
29
|
+
payload: JSON.stringify({
|
30
|
+
message_id: messageID,
|
31
|
+
text: text,
|
32
|
+
}),
|
33
|
+
queue_name: 'edit_message',
|
34
|
+
task_id: ++ctx.wsTaskNumber,
|
35
|
+
}],
|
36
|
+
version_id: '6903494529735864',
|
37
|
+
}),
|
38
|
+
request_id: ++ctx.wsReqNumber,
|
39
|
+
type: 3
|
40
|
+
}
|
41
|
+
ctx.mqttClient.publish('/ls_req', JSON.stringify(content), {
|
42
|
+
qos: 1,
|
43
|
+
retain: false
|
44
|
+
});
|
45
|
+
const handleRes = (topic, message) => {
|
46
|
+
if (topic === "/ls_resp") {
|
47
|
+
let jsonMsg = JSON.parse(message.toString());
|
48
|
+
jsonMsg.payload = JSON.parse(jsonMsg.payload);
|
49
|
+
if (jsonMsg.request_id != reqID) return;
|
50
|
+
ctx.mqttClient.removeListener('message', handleRes);
|
51
|
+
let msgID = jsonMsg.payload.step[1][2][2][1][2];
|
52
|
+
let msgReplace = jsonMsg.payload.step[1][2][2][1][4];
|
53
|
+
const bodies = {
|
54
|
+
body: msgReplace,
|
55
|
+
messageID: msgID
|
56
|
+
};
|
57
|
+
if (msgReplace != text) {
|
58
|
+
return callback({
|
59
|
+
error: "The message is too old or not from you!"
|
60
|
+
}, bodies);
|
61
|
+
}
|
62
|
+
return callback(undefined, bodies);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
ctx.mqttClient.on('message', handleRes);
|
66
|
+
return returnPromise;
|
67
|
+
};
|
68
|
+
}
|
package/src/utils/client.js
CHANGED
@@ -99,10 +99,10 @@ function parseAndCheckLogin(ctx, http, retryCount = 0) {
|
|
99
99
|
throw e;
|
100
100
|
}
|
101
101
|
ctx.auto_login = true;
|
102
|
-
|
102
|
+
logger("Login session expired", "warn");
|
103
103
|
const ok = await ctx.performAutoLogin();
|
104
104
|
if (ok) {
|
105
|
-
|
105
|
+
logger("Auto login successful! Restarting...");
|
106
106
|
ctx.auto_login = false;
|
107
107
|
process.exit(1);
|
108
108
|
} else {
|