shadowx-fca 2.3.0 → 2.5.0
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/checkUpdate.js +1 -1
- package/index.js +345 -122
- package/package.json +1 -1
- package/src/GetBotInfo.js +57 -0
- package/src/OldMessage.js +38 -10
- package/src/comment.js +213 -0
- package/src/createThemeAI.js +129 -0
- package/src/emoji.js +124 -0
- package/src/friend.js +243 -0
- package/src/gcmember.js +122 -0
- package/src/getUserInfo.js +222 -43
- package/src/listenMqtt.js +9 -116
- package/src/nickname.js +132 -0
- package/src/postFormData.js +46 -0
- package/src/sendMessage.js +224 -235
- package/src/sendTypingIndicator.js +45 -101
- package/src/share.js +62 -0
- package/src/shareContact.js +17 -61
- package/src/stickers.js +117 -0
- package/src/story.js +181 -0
- package/src/theme.js +233 -0
- package/utils.js +24 -1311
package/checkUpdate.js
CHANGED
|
@@ -15,7 +15,7 @@ async function checkForFCAUpdate() {
|
|
|
15
15
|
const latestVersion = npmData.version;
|
|
16
16
|
|
|
17
17
|
// Check current installed version in node_modules
|
|
18
|
-
let currentVersion = '
|
|
18
|
+
let currentVersion = '2.4.0';
|
|
19
19
|
const nodeModulesPackagePath = path.join(process.cwd(), 'node_modules', 'shadowx-fca', 'package.json');
|
|
20
20
|
if (fs.existsSync(nodeModulesPackagePath)) {
|
|
21
21
|
const installedPackage = JSON.parse(fs.readFileSync(nodeModulesPackagePath, 'utf-8'));
|
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var utils = require("./utils");
|
|
4
4
|
var cheerio = require("cheerio");
|
|
5
5
|
var log = require("npmlog");
|
|
6
|
-
|
|
6
|
+
var crypto = require("crypto");
|
|
7
7
|
log.maxRecordSize = 100;
|
|
8
8
|
var checkVerified = null;
|
|
9
9
|
const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
|
|
@@ -112,7 +112,7 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
112
112
|
}
|
|
113
113
|
} catch { }
|
|
114
114
|
if (fb_dtsg) {
|
|
115
|
-
|
|
115
|
+
log.info("✅ | Found fb_Dtsg");
|
|
116
116
|
}
|
|
117
117
|
} catch (e) {
|
|
118
118
|
console.log("Error finding fb_dtsg:", e);
|
|
@@ -124,33 +124,32 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
124
124
|
var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
|
|
125
125
|
var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
|
|
126
126
|
if (!userCookie && !tiktikCookie) {
|
|
127
|
-
return log.error("
|
|
127
|
+
return log.error('login', "No cookie found for user, please check login information again");
|
|
128
128
|
}
|
|
129
129
|
if (html.includes("/checkpoint/block/?next")) {
|
|
130
|
-
return log.error('
|
|
130
|
+
return log.error('login', "Appstate dead, please replace with a new one!", 'error');
|
|
131
131
|
}
|
|
132
132
|
userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
try { clearInterval(checkVerified); } catch (_) { }
|
|
135
135
|
const clientID = (Math.random() * 2147483648 | 0).toString(16);
|
|
136
|
-
let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=
|
|
137
|
-
let region = "
|
|
136
|
+
let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=prn&sid=${userID}`;
|
|
137
|
+
let region = "PRN";
|
|
138
138
|
|
|
139
139
|
try {
|
|
140
140
|
const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
|
|
141
|
-
if (endpointMatch.input.includes("601051028565049")) {
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
if (endpointMatch && endpointMatch.input && endpointMatch.input.includes("601051028565049")) {
|
|
142
|
+
console.log(`login error due to automatic account`);
|
|
143
|
+
ditconmemay = true;
|
|
144
144
|
}
|
|
145
145
|
if (endpointMatch) {
|
|
146
146
|
mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
|
|
147
147
|
const url = new URL(mqttEndpoint);
|
|
148
|
-
region = url.searchParams.get('region')?.toUpperCase() || "
|
|
148
|
+
region = url.searchParams.get('region')?.toUpperCase() || "PRN";
|
|
149
149
|
}
|
|
150
150
|
} catch (e) {
|
|
151
151
|
console.log('Using default MQTT endpoint');
|
|
152
152
|
}
|
|
153
|
-
log.info('Logging in...');
|
|
154
153
|
var ctx = {
|
|
155
154
|
userID: userID,
|
|
156
155
|
jar: jar,
|
|
@@ -170,8 +169,7 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
170
169
|
callback_Task: {},
|
|
171
170
|
wsReqNumber: 0,
|
|
172
171
|
wsTaskNumber: 0,
|
|
173
|
-
reqCallbacks: {}
|
|
174
|
-
threadTypes: {} // Store thread type (dm/group) for each thread
|
|
172
|
+
reqCallbacks: {}
|
|
175
173
|
};
|
|
176
174
|
var api = {
|
|
177
175
|
setOptions: setOptions.bind(null, globalOptions),
|
|
@@ -217,28 +215,8 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
217
215
|
return null;
|
|
218
216
|
}
|
|
219
217
|
};
|
|
220
|
-
|
|
218
|
+
|
|
221
219
|
require('fs').readdirSync(__dirname + '/src/').filter(v => v.endsWith('.js')).forEach(v => { api[v.replace('.js', '')] = require(`./src/${v}`)(utils.makeDefaults(html, userID, ctx), api, ctx); });
|
|
222
|
-
|
|
223
|
-
// Store original sendMessage as the primary method
|
|
224
|
-
const originalSendMessage = api.sendMessage;
|
|
225
|
-
|
|
226
|
-
// Wrap sendMessage to use OldMessage as fallback on error
|
|
227
|
-
api.sendMessage = async function(msg, threadID, callback, replyToMessage, isSingleUser) {
|
|
228
|
-
try {
|
|
229
|
-
return await originalSendMessage(msg, threadID, callback, replyToMessage, isSingleUser);
|
|
230
|
-
} catch (error) {
|
|
231
|
-
// If modern method fails, fallback to OldMessage
|
|
232
|
-
console.log('sendMessage failed, using OldMessage fallback:', error.message);
|
|
233
|
-
return api.OldMessage(msg, threadID, callback, replyToMessage, isSingleUser);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// Provide explicit method for DM sending using OldMessage
|
|
238
|
-
api.sendMessageDM = function(msg, threadID, callback, replyToMessage) {
|
|
239
|
-
return api.OldMessage(msg, threadID, callback, replyToMessage, true);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
220
|
api.listen = api.listenMqtt;
|
|
243
221
|
return {
|
|
244
222
|
ctx,
|
|
@@ -247,104 +225,345 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
247
225
|
};
|
|
248
226
|
}
|
|
249
227
|
|
|
250
|
-
|
|
251
|
-
|
|
228
|
+
// Session keeper to prevent logout
|
|
229
|
+
function startSessionKeeper(api, ctx) {
|
|
230
|
+
// Keep session alive with random activity
|
|
231
|
+
const keepAliveInterval = setInterval(async () => {
|
|
252
232
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
233
|
+
const actions = [
|
|
234
|
+
async () => {
|
|
235
|
+
const res = await utils.get('https://www.facebook.com/', ctx.jar, null, ctx.globalOptions);
|
|
236
|
+
return res;
|
|
237
|
+
},
|
|
238
|
+
async () => {
|
|
239
|
+
const newDtsg = await api.getFreshDtsg();
|
|
240
|
+
if (newDtsg) ctx.fb_dtsg = newDtsg;
|
|
241
|
+
}
|
|
242
|
+
];
|
|
243
|
+
const randomAction = actions[Math.floor(Math.random() * actions.length)];
|
|
244
|
+
await randomAction();
|
|
245
|
+
log.verbose("session", "Keep-alive ping sent");
|
|
246
|
+
} catch (e) {
|
|
247
|
+
log.verbose("session", "Keep-alive failed: " + e.message);
|
|
248
|
+
}
|
|
249
|
+
}, 240000 + Math.floor(Math.random() * 120000)); // 4-6 minutes
|
|
250
|
+
|
|
251
|
+
// Refresh DTSG periodically
|
|
252
|
+
const dtsgInterval = setInterval(async () => {
|
|
253
|
+
try {
|
|
254
|
+
const newDtsg = await api.getFreshDtsg();
|
|
255
|
+
if (newDtsg) {
|
|
256
|
+
ctx.fb_dtsg = newDtsg;
|
|
257
|
+
log.info("session", "DTSG refreshed");
|
|
258
|
+
}
|
|
259
|
+
} catch (e) {
|
|
260
|
+
log.verbose("session", "DTSG refresh failed");
|
|
261
|
+
}
|
|
262
|
+
}, 1800000); // 30 minutes
|
|
263
|
+
|
|
264
|
+
// Store intervals for cleanup
|
|
265
|
+
ctx._keepAliveInterval = keepAliveInterval;
|
|
266
|
+
ctx._dtsgInterval = dtsgInterval;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Calculate jazoest for Facebook anti-bot
|
|
270
|
+
function calculateJazoest(token) {
|
|
271
|
+
let sum = 0;
|
|
272
|
+
for (let i = 0; i < token.length; i++) {
|
|
273
|
+
sum += token.charCodeAt(i);
|
|
274
|
+
}
|
|
275
|
+
return sum.toString();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Modern authentication function
|
|
279
|
+
async function modernAuthenticate(email, password, jar, loginOptions) {
|
|
280
|
+
const API_KEYS = [
|
|
281
|
+
'350685531728|62f8ce9f74b12f84c123cc23437a4a32',
|
|
282
|
+
'256002347743983|374e60f8b9bb6b8cbb30f78030438895',
|
|
283
|
+
'882a8490361da98702bf97a021ddc14d|62f8ce9f74b12f84c123cc23437a4a32'
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const randomApiKey = API_KEYS[Math.floor(Math.random() * API_KEYS.length)];
|
|
287
|
+
const [appId, clientToken] = randomApiKey.includes('|') ? randomApiKey.split('|') : ['350685531728', randomApiKey];
|
|
288
|
+
|
|
289
|
+
const pax = Math.random() > 0.5 ? "PWD_BROWSER" : "PWD_FB4A";
|
|
290
|
+
const adid = [...Array(16)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
291
|
+
const device_id = crypto.randomUUID();
|
|
292
|
+
const family_device_id = crypto.randomUUID();
|
|
293
|
+
const machine_id = crypto.randomBytes(16).toString('hex');
|
|
294
|
+
const session_id = crypto.randomBytes(12).toString('hex');
|
|
295
|
+
|
|
296
|
+
const locales = {
|
|
297
|
+
"en_US": { code: "US", tz: "-240" },
|
|
298
|
+
"en_GB": { code: "GB", tz: "0" },
|
|
299
|
+
"fr_FR": { code: "FR", tz: "60" },
|
|
300
|
+
"de_DE": { code: "DE", tz: "60" },
|
|
301
|
+
"es_ES": { code: "ES", tz: "60" },
|
|
302
|
+
"it_IT": { code: "IT", tz: "60" },
|
|
303
|
+
"pt_BR": { code: "BR", tz: "-180" }
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const localeKeys = Object.keys(locales);
|
|
307
|
+
const country_locale = localeKeys[Math.floor(Math.random() * localeKeys.length)];
|
|
308
|
+
const localeData = locales[country_locale];
|
|
309
|
+
|
|
310
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
311
|
+
const connectionToken = `${appId}|${clientToken}`;
|
|
312
|
+
const jazoest = calculateJazoest(clientToken);
|
|
313
|
+
|
|
314
|
+
const data = {
|
|
315
|
+
adid: adid,
|
|
316
|
+
format: 'json',
|
|
317
|
+
device_id: device_id,
|
|
318
|
+
email: email,
|
|
319
|
+
enc_password: `#${pax}:0:${timestamp}:${password}`,
|
|
320
|
+
generate_analytics_claims: '1',
|
|
321
|
+
generate_session_cookies: '1',
|
|
322
|
+
generate_machine_id: '1',
|
|
323
|
+
machine_id: machine_id,
|
|
324
|
+
session_id: session_id,
|
|
325
|
+
credentials_type: 'password',
|
|
326
|
+
source: 'login',
|
|
327
|
+
error_detail_type: 'button_with_disabled',
|
|
328
|
+
enroll_misauth: 'false',
|
|
329
|
+
currently_logged_in_userid: '0',
|
|
330
|
+
locale: country_locale,
|
|
331
|
+
client_country_code: localeData.code,
|
|
332
|
+
timezone_offset: localeData.tz,
|
|
333
|
+
screen_resolution: '1920x1080',
|
|
334
|
+
available_screen_resolution: '1920x1040',
|
|
335
|
+
fb_api_req_friendly_name: 'authenticate',
|
|
336
|
+
api_key: clientToken,
|
|
337
|
+
access_token: connectionToken,
|
|
338
|
+
jazoest: jazoest,
|
|
339
|
+
meta_inf_fbmeta: '',
|
|
340
|
+
skip_api_login: 'false',
|
|
341
|
+
fb_api_caller_class: 'com.facebook.fblogin',
|
|
342
|
+
cpl: 'true',
|
|
343
|
+
try_num: '1',
|
|
344
|
+
family_device_id: family_device_id,
|
|
345
|
+
community_id: ''
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const userAgents = [
|
|
349
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
|
|
350
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
351
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
|
|
352
|
+
];
|
|
353
|
+
|
|
354
|
+
const userAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
|
|
355
|
+
|
|
356
|
+
const headers = {
|
|
357
|
+
'User-Agent': userAgent,
|
|
358
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
|
359
|
+
'Accept-Language': country_locale.replace('_', '-') + ',en-US;q=0.9',
|
|
360
|
+
'Accept-Encoding': 'gzip, deflate, br',
|
|
361
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
362
|
+
'Host': 'graph.facebook.com',
|
|
363
|
+
'Origin': 'https://www.facebook.com',
|
|
364
|
+
'Referer': 'https://www.facebook.com/',
|
|
365
|
+
'Connection': 'keep-alive',
|
|
366
|
+
'Sec-Fetch-Dest': 'empty',
|
|
367
|
+
'Sec-Fetch-Mode': 'cors',
|
|
368
|
+
'Sec-Fetch-Site': 'same-site',
|
|
369
|
+
'sec-ch-ua': '"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"',
|
|
370
|
+
'sec-ch-ua-mobile': '?0',
|
|
371
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
372
|
+
'Cache-Control': 'no-cache',
|
|
373
|
+
'Pragma': 'no-cache',
|
|
374
|
+
'X-FB-Net-HNI': String(Math.floor(Math.random() * 90000) + 10000),
|
|
375
|
+
'X-FB-SIM-HNI': String(Math.floor(Math.random() * 90000) + 10000),
|
|
376
|
+
'Authorization': `OAuth ${connectionToken}`,
|
|
377
|
+
'X-FB-Connection-Type': 'WIFI',
|
|
378
|
+
'X-Tigon-Is-Retry': 'False',
|
|
379
|
+
'x-fb-session-id': `nid=${session_id};pid=Main;tid=132;nc=1;fc=0;bc=0;cid=${clientToken}`,
|
|
380
|
+
'x-fb-device-group': '5120',
|
|
381
|
+
'X-FB-Friendly-Name': 'authenticate',
|
|
382
|
+
'X-FB-Request-Analytics-Tags': 'graphservice',
|
|
383
|
+
'X-FB-HTTP-Engine': 'Liger',
|
|
384
|
+
'X-FB-Client-IP': 'True',
|
|
385
|
+
'X-FB-Server-Cluster': 'True',
|
|
386
|
+
'x-fb-connection-token': connectionToken
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
log.info("login", "Attempting modern authentication...");
|
|
390
|
+
|
|
391
|
+
const response = await utils.post(
|
|
392
|
+
"https://b-graph.facebook.com/auth/login",
|
|
393
|
+
jar,
|
|
394
|
+
data,
|
|
395
|
+
loginOptions,
|
|
396
|
+
null,
|
|
397
|
+
headers
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
return response;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Handle 2FA checkpoint
|
|
404
|
+
async function handleCheckpoint(checkpointUrl, jar, loginOptions) {
|
|
405
|
+
log.info("login", "Login approvals are on. Expect an SMS shortly with a code to use for log in");
|
|
406
|
+
|
|
407
|
+
return new Promise((resolve, reject) => {
|
|
408
|
+
const submit2FA = async (code) => {
|
|
409
|
+
try {
|
|
410
|
+
const checkpointRes = await utils.get(checkpointUrl, jar, null, loginOptions);
|
|
411
|
+
const $ = cheerio.load(checkpointRes.body);
|
|
412
|
+
|
|
289
413
|
let checkpointForm = [];
|
|
290
414
|
$("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
|
|
291
415
|
checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
|
|
292
416
|
const form = utils.arrToForm(checkpointForm);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
jar,
|
|
312
|
-
form,
|
|
313
|
-
loginOptions
|
|
314
|
-
);
|
|
315
|
-
await utils.saveCookies(jar)(finalRes);
|
|
316
|
-
const appState = utils.getAppState(jar);
|
|
317
|
-
resolve(await loginHelper(appState, email, password, loginOptions, callback));
|
|
318
|
-
} catch (error) {
|
|
319
|
-
reject(error);
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
throw {
|
|
323
|
-
error: 'login-approval',
|
|
324
|
-
continue: submit2FA
|
|
325
|
-
};
|
|
326
|
-
});
|
|
417
|
+
|
|
418
|
+
form.approvals_code = code;
|
|
419
|
+
form['submit[Continue]'] = $("#checkpointSubmitButton").html() || "Continue";
|
|
420
|
+
|
|
421
|
+
const approvalRes = await utils.post(
|
|
422
|
+
checkpointUrl,
|
|
423
|
+
jar,
|
|
424
|
+
form,
|
|
425
|
+
loginOptions
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
await utils.saveCookies(jar)(approvalRes);
|
|
429
|
+
|
|
430
|
+
const $approval = cheerio.load(approvalRes.body);
|
|
431
|
+
const approvalError = $approval("#approvals_code").parent().attr("data-xui-error");
|
|
432
|
+
if (approvalError) {
|
|
433
|
+
reject(new Error("Invalid 2FA code."));
|
|
434
|
+
return;
|
|
327
435
|
}
|
|
328
|
-
|
|
329
|
-
form['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
|
|
330
|
-
await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
|
|
436
|
+
|
|
331
437
|
form.name_action_selected = 'save_device';
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
438
|
+
const finalRes = await utils.post(
|
|
439
|
+
checkpointUrl,
|
|
440
|
+
jar,
|
|
441
|
+
form,
|
|
442
|
+
loginOptions
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
await utils.saveCookies(jar)(finalRes);
|
|
446
|
+
resolve(true);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
reject(error);
|
|
335
449
|
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
reject({
|
|
453
|
+
error: 'login-approval',
|
|
454
|
+
continue: submit2FA
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
460
|
+
return async function (res) {
|
|
461
|
+
try {
|
|
462
|
+
const html = res.body;
|
|
463
|
+
|
|
464
|
+
// Try modern authentication first
|
|
465
|
+
let loginSuccess = false;
|
|
466
|
+
let checkpointUrl = null;
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
const modernRes = await modernAuthenticate(email, password, jar, loginOptions);
|
|
470
|
+
await utils.saveCookies(jar)(modernRes);
|
|
471
|
+
|
|
472
|
+
let responseBody;
|
|
473
|
+
try {
|
|
474
|
+
responseBody = JSON.parse(modernRes.body);
|
|
475
|
+
} catch (e) {
|
|
476
|
+
responseBody = { error: true };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (responseBody.session_cookies) {
|
|
480
|
+
responseBody.session_cookies.forEach(cookie => {
|
|
481
|
+
jar.setCookie(
|
|
482
|
+
`${cookie.name}=${cookie.value}; Domain=.facebook.com; Path=/; Secure; HttpOnly`,
|
|
483
|
+
"https://www.facebook.com"
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
loginSuccess = true;
|
|
487
|
+
log.info("login", "Modern authentication successful");
|
|
488
|
+
} else if (responseBody.error && responseBody.error.code === 401) {
|
|
489
|
+
log.info("login", "Modern auth failed, falling back to traditional login");
|
|
490
|
+
} else if (responseBody.login_approval_url) {
|
|
491
|
+
checkpointUrl = responseBody.login_approval_url;
|
|
492
|
+
}
|
|
493
|
+
} catch (e) {
|
|
494
|
+
log.info("login", "Modern auth error, falling back to traditional: " + e.message);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Handle checkpoint if needed
|
|
498
|
+
if (checkpointUrl) {
|
|
499
|
+
await handleCheckpoint(checkpointUrl, jar, loginOptions);
|
|
500
|
+
loginSuccess = true;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Fallback to traditional login if modern fails
|
|
504
|
+
if (!loginSuccess) {
|
|
505
|
+
const $ = cheerio.load(html);
|
|
506
|
+
let arr = [];
|
|
507
|
+
$("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
|
|
508
|
+
arr = arr.filter(v => v.val && v.val.length);
|
|
509
|
+
let form = utils.arrToForm(arr);
|
|
510
|
+
form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"");
|
|
511
|
+
form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
|
|
512
|
+
form.email = email;
|
|
513
|
+
form.pass = password;
|
|
514
|
+
form.default_persistent = '0';
|
|
515
|
+
form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
|
|
516
|
+
form.locale = 'en_US';
|
|
517
|
+
form.timezone = '240';
|
|
518
|
+
form.lgnjs = Math.floor(Date.now() / 1000);
|
|
519
|
+
|
|
520
|
+
const willBeCookies = html.split("\"_js_");
|
|
521
|
+
willBeCookies.slice(1).forEach(val => {
|
|
522
|
+
try {
|
|
523
|
+
const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
|
|
524
|
+
jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
|
|
525
|
+
} catch (e) {}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
log.info("login", "Logging in via traditional method...");
|
|
529
|
+
const loginRes = await utils.post(
|
|
530
|
+
"https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
|
|
531
|
+
jar,
|
|
532
|
+
form,
|
|
533
|
+
loginOptions
|
|
534
|
+
);
|
|
535
|
+
await utils.saveCookies(jar)(loginRes);
|
|
536
|
+
|
|
537
|
+
const headers = loginRes.headers;
|
|
538
|
+
if (!headers.location) throw new Error("Wrong username/password.");
|
|
539
|
+
|
|
540
|
+
if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
|
|
541
|
+
await handleCheckpoint(headers.location, jar, loginOptions);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Final verification
|
|
336
546
|
await utils.get('https://www.facebook.com/', jar, null, loginOptions);
|
|
547
|
+
|
|
548
|
+
// Add delay to avoid rate limiting
|
|
549
|
+
await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 3000));
|
|
550
|
+
|
|
337
551
|
return await utils.saveCookies(jar);
|
|
552
|
+
|
|
338
553
|
} catch (error) {
|
|
554
|
+
if (error.error === 'login-approval') {
|
|
555
|
+
callback(error);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
339
558
|
callback(error);
|
|
340
559
|
}
|
|
341
560
|
};
|
|
342
561
|
}
|
|
343
562
|
|
|
344
|
-
|
|
345
563
|
function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
|
|
346
564
|
let mainPromise = null;
|
|
347
565
|
const jar = utils.getJar();
|
|
566
|
+
|
|
348
567
|
if (appState) {
|
|
349
568
|
try {
|
|
350
569
|
appState = JSON.parse(appState);
|
|
@@ -390,7 +609,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
390
609
|
.then(res => {
|
|
391
610
|
const mobileAgentRegex = /MPageLoadClientMetrics/gs;
|
|
392
611
|
if (!mobileAgentRegex.test(res.body)) {
|
|
393
|
-
globalOptions.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
612
|
+
globalOptions.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
|
|
394
613
|
return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
|
|
395
614
|
}
|
|
396
615
|
return res;
|
|
@@ -417,6 +636,10 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
417
636
|
mainPromise
|
|
418
637
|
.then(async () => {
|
|
419
638
|
log.info('Login successful');
|
|
639
|
+
|
|
640
|
+
// Start session keeper to prevent logout
|
|
641
|
+
startSessionKeeper(api, ctx);
|
|
642
|
+
|
|
420
643
|
callback(null, api);
|
|
421
644
|
})
|
|
422
645
|
.catch(e => {
|
|
@@ -424,7 +647,6 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
|
|
|
424
647
|
});
|
|
425
648
|
}
|
|
426
649
|
|
|
427
|
-
|
|
428
650
|
function login(loginData, options, callback) {
|
|
429
651
|
if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
|
|
430
652
|
callback = options;
|
|
@@ -433,7 +655,7 @@ function login(loginData, options, callback) {
|
|
|
433
655
|
|
|
434
656
|
var globalOptions = {
|
|
435
657
|
selfListen: false,
|
|
436
|
-
listenEvents:
|
|
658
|
+
listenEvents: false,
|
|
437
659
|
listenTyping: false,
|
|
438
660
|
updatePresence: false,
|
|
439
661
|
forceLogin: false,
|
|
@@ -441,16 +663,18 @@ function login(loginData, options, callback) {
|
|
|
441
663
|
autoMarkRead: false,
|
|
442
664
|
autoReconnect: true,
|
|
443
665
|
logRecordSize: 100,
|
|
444
|
-
online:
|
|
666
|
+
online: true,
|
|
445
667
|
emitReady: false,
|
|
446
|
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
668
|
+
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
|
|
447
669
|
};
|
|
448
670
|
|
|
449
671
|
var prCallback = null;
|
|
672
|
+
var returnPromise = null;
|
|
673
|
+
|
|
450
674
|
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
|
|
451
675
|
var rejectFunc = null;
|
|
452
676
|
var resolveFunc = null;
|
|
453
|
-
|
|
677
|
+
returnPromise = new Promise(function (resolve, reject) {
|
|
454
678
|
resolveFunc = resolve;
|
|
455
679
|
rejectFunc = reject;
|
|
456
680
|
});
|
|
@@ -465,7 +689,7 @@ function login(loginData, options, callback) {
|
|
|
465
689
|
setOptions(globalOptions, {
|
|
466
690
|
logLevel: "silent",
|
|
467
691
|
forceLogin: true,
|
|
468
|
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
692
|
+
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
|
|
469
693
|
});
|
|
470
694
|
loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
|
|
471
695
|
} else if (loginData.appState) {
|
|
@@ -475,5 +699,4 @@ function login(loginData, options, callback) {
|
|
|
475
699
|
return returnPromise;
|
|
476
700
|
}
|
|
477
701
|
|
|
478
|
-
|
|
479
702
|
module.exports = login;
|