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