shadowx-fca 2.5.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,355 +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
- '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
299
 
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');
300
+ const originalSendMessage = api.sendMessage;
295
301
 
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" }
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
+ }
304
309
  };
305
310
 
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: ''
311
+ api.sendMessageDM = function(msg, threadID, callback, replyToMessage) {
312
+ return api.OldMessage(msg, threadID, callback, replyToMessage, true);
346
313
  };
347
314
 
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
315
+ api.listen = api.listenMqtt;
316
+ return {
317
+ ctx,
318
+ defaultFuncs,
319
+ api
387
320
  };
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
-
413
- let checkpointForm = [];
414
- $("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
415
- checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
416
- const form = utils.arrToForm(checkpointForm);
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;
435
- }
436
-
437
- form.name_action_selected = 'save_device';
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);
449
- }
450
- };
451
-
452
- reject({
453
- error: 'login-approval',
454
- continue: submit2FA
455
- });
456
- });
457
321
  }
458
322
 
459
323
  function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
460
324
  return async function (res) {
461
325
  try {
462
326
  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
- );
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
+ };
485
399
  });
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
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);
543
408
  }
544
-
545
- // Final verification
546
409
  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
-
551
410
  return await utils.saveCookies(jar);
552
-
553
411
  } catch (error) {
554
- if (error.error === 'login-approval') {
555
- callback(error);
556
- return;
557
- }
558
412
  callback(error);
559
413
  }
560
414
  };
561
415
  }
562
416
 
417
+
563
418
  function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
564
419
  let mainPromise = null;
565
420
  const jar = utils.getJar();
566
-
567
421
  if (appState) {
568
422
  try {
569
423
  appState = JSON.parse(appState);
@@ -609,7 +463,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
609
463
  .then(res => {
610
464
  const mobileAgentRegex = /MPageLoadClientMetrics/gs;
611
465
  if (!mobileAgentRegex.test(res.body)) {
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";
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";
613
467
  return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
614
468
  }
615
469
  return res;
@@ -635,11 +489,8 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
635
489
 
636
490
  mainPromise
637
491
  .then(async () => {
638
- log.info('Login successful');
639
-
640
- // Start session keeper to prevent logout
641
- startSessionKeeper(api, ctx);
642
-
492
+ console.log('Shadowx-FCA: Connected ✔');
493
+ console.log('Shadowx-FCA: Listening...');
643
494
  callback(null, api);
644
495
  })
645
496
  .catch(e => {
@@ -647,6 +498,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
647
498
  });
648
499
  }
649
500
 
501
+
650
502
  function login(loginData, options, callback) {
651
503
  if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
652
504
  callback = options;
@@ -655,7 +507,7 @@ function login(loginData, options, callback) {
655
507
 
656
508
  var globalOptions = {
657
509
  selfListen: false,
658
- listenEvents: false,
510
+ listenEvents: true,
659
511
  listenTyping: false,
660
512
  updatePresence: false,
661
513
  forceLogin: false,
@@ -663,18 +515,16 @@ function login(loginData, options, callback) {
663
515
  autoMarkRead: false,
664
516
  autoReconnect: true,
665
517
  logRecordSize: 100,
666
- online: true,
518
+ online: false,
667
519
  emitReady: false,
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"
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"
669
521
  };
670
522
 
671
523
  var prCallback = null;
672
- var returnPromise = null;
673
-
674
524
  if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
675
525
  var rejectFunc = null;
676
526
  var resolveFunc = null;
677
- returnPromise = new Promise(function (resolve, reject) {
527
+ var returnPromise = new Promise(function (resolve, reject) {
678
528
  resolveFunc = resolve;
679
529
  rejectFunc = reject;
680
530
  });
@@ -689,7 +539,7 @@ function login(loginData, options, callback) {
689
539
  setOptions(globalOptions, {
690
540
  logLevel: "silent",
691
541
  forceLogin: true,
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"
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"
693
543
  });
694
544
  loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
695
545
  } else if (loginData.appState) {
@@ -699,4 +549,4 @@ function login(loginData, options, callback) {
699
549
  return returnPromise;
700
550
  }
701
551
 
702
- module.exports = login;
552
+ module.exports = login;