shadowx-fca 2.2.0 → 2.3.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.
Files changed (3) hide show
  1. package/README.md +3 -11
  2. package/index.js +41 -188
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,14 +1,6 @@
1
- Here's a comprehensive README.md for your shadowx-fca package:
2
-
3
1
  ```markdown
4
2
  # shadowx-fca
5
-
6
- <div align="center">
7
- <img src="https://img.shields.io/npm/v/shadowx-fca.svg?style=for-the-badge" alt="NPM Version">
8
- <img src="https://img.shields.io/npm/dt/shadowx-fca.svg?style=for-the-badge" alt="NPM Downloads">
9
- <img src="https://img.shields.io/github/license/mueidmursalinrifat/shadowx-fca.svg?style=for-the-badge" alt="License">
10
- <img src="https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg?style=for-the-badge" alt="Node Version">
11
- </div>
3
+ #Mueid Mursalin Rifat
12
4
 
13
5
  <p align="center">
14
6
  <strong>Unofficial Facebook Chat API for Node.js with Auto-Update System</strong><br>
@@ -51,8 +43,8 @@ npm install shadowx-fca
51
43
 
52
44
  Requirements
53
45
 
54
- · Node.js >= 14.0.0
55
- · npm >= 6.0.0
46
+ · Node.js >= 16.0.0
47
+ · npm >= 7.0.0
56
48
 
57
49
  🚀 Quick Start
58
50
 
package/index.js CHANGED
@@ -3,35 +3,11 @@
3
3
  var utils = require("./utils");
4
4
  var cheerio = require("cheerio");
5
5
  var log = require("npmlog");
6
- var { checkForFCAUpdate } = require("./checkUpdate");
7
- const fs = require('fs');
8
- const path = require('path');
9
6
 
10
7
  log.maxRecordSize = 100;
11
8
  var checkVerified = null;
12
-
13
9
  const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
14
-
15
10
  global.ditconmemay = false;
16
- global.stfcaUpdateChecked = false;
17
-
18
- // Rotating user agents to avoid detection
19
- const USER_AGENTS = [
20
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
21
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
22
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
23
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
24
- 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
25
- ];
26
-
27
- function getRandomUserAgent() {
28
- return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
29
- }
30
-
31
- // Delay function to avoid rate limiting
32
- function delay(ms) {
33
- return new Promise(resolve => setTimeout(resolve, ms));
34
- }
35
11
 
36
12
  function setOptions(globalOptions, options) {
37
13
  Object.keys(options).map(function (key) {
@@ -62,7 +38,7 @@ function setOptions(globalOptions, options) {
62
38
  break;
63
39
  }
64
40
  case 'userAgent': {
65
- globalOptions.userAgent = options.userAgent || getRandomUserAgent();
41
+ globalOptions.userAgent = (options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36');
66
42
  break;
67
43
  }
68
44
  case 'proxy': {
@@ -75,10 +51,6 @@ function setOptions(globalOptions, options) {
75
51
  }
76
52
  break;
77
53
  }
78
- case 'delayBetweenRequests': {
79
- globalOptions.delayBetweenRequests = options.delayBetweenRequests || 1000;
80
- break;
81
- }
82
54
  default: {
83
55
  log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
84
56
  break;
@@ -93,7 +65,6 @@ function setOptions(globalOptions, options) {
93
65
  function buildAPI(globalOptions, html, jar) {
94
66
  let fb_dtsg = null;
95
67
  let irisSeqID = null;
96
-
97
68
  function extractFromHTML() {
98
69
  try {
99
70
  const $ = cheerio.load(html);
@@ -141,43 +112,35 @@ function buildAPI(globalOptions, html, jar) {
141
112
  }
142
113
  } catch { }
143
114
  if (fb_dtsg) {
144
- log.info("Found fb_dtsg token");
115
+ console.log("Found fb_dtsg!");
145
116
  }
146
117
  } catch (e) {
147
- log.error("Error finding fb_dtsg:", e);
118
+ console.log("Error finding fb_dtsg:", e);
148
119
  }
149
120
  }
150
-
151
121
  extractFromHTML();
152
-
153
122
  var userID;
154
123
  var cookies = jar.getCookies("https://www.facebook.com");
155
124
  var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
156
125
  var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
157
-
158
126
  if (!userCookie && !tiktikCookie) {
159
- log.error("Error! Your cookiestate is not valid!");
160
- throw new Error("Invalid appState");
127
+ return log.error("Error! Your cookiestate is not valid!");
161
128
  }
162
-
163
129
  if (html.includes("/checkpoint/block/?next")) {
164
- log.error('error', "Appstate is dead, please refresh it!", 'error');
165
- throw new Error("AppState expired");
130
+ return log.error('error', "Appstate is dead rechange it!", 'error');
166
131
  }
167
-
168
132
  userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
169
-
133
+ //logger.log(`${cra(`[ CONNECT ]`)} Logged in as ${userID}`, "DATABASE");
170
134
  try { clearInterval(checkVerified); } catch (_) { }
171
-
172
135
  const clientID = (Math.random() * 2147483648 | 0).toString(16);
173
136
  let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb&sid=${userID}`;
174
137
  let region = "PNB";
175
138
 
176
139
  try {
177
140
  const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
178
- if (endpointMatch && endpointMatch.input && endpointMatch.input.includes("601051028565049")) {
179
- log.error(`Login error detected`);
180
- global.ditconmemay = true;
141
+ if (endpointMatch.input.includes("601051028565049")) {
142
+ console.log(`login error.`);
143
+ ditconmemay = true;
181
144
  }
182
145
  if (endpointMatch) {
183
146
  mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
@@ -185,11 +148,9 @@ function buildAPI(globalOptions, html, jar) {
185
148
  region = url.searchParams.get('region')?.toUpperCase() || "PNB";
186
149
  }
187
150
  } catch (e) {
188
- log.warn('Using default MQTT endpoint');
151
+ console.log('Using default MQTT endpoint');
189
152
  }
190
-
191
- log.info('Building API...');
192
-
153
+ log.info('Logging in...');
193
154
  var ctx = {
194
155
  userID: userID,
195
156
  jar: jar,
@@ -210,92 +171,19 @@ function buildAPI(globalOptions, html, jar) {
210
171
  wsReqNumber: 0,
211
172
  wsTaskNumber: 0,
212
173
  reqCallbacks: {},
213
- threadTypes: {}
174
+ threadTypes: {} // Store thread type (dm/group) for each thread
214
175
  };
215
-
216
- let config = { enableTypingIndicator: false, typingDuration: 4000 };
217
-
218
- try {
219
- const rootConfigPath = path.join(process.cwd(), 'config.json');
220
- if (fs.existsSync(rootConfigPath)) {
221
- const rootConfig = JSON.parse(fs.readFileSync(rootConfigPath, 'utf8'));
222
- if (rootConfig && typeof rootConfig === 'object') {
223
- if (typeof rootConfig.enableTypingIndicator !== 'undefined') config.enableTypingIndicator = rootConfig.enableTypingIndicator;
224
- if (typeof rootConfig.typingDuration !== 'undefined') config.typingDuration = rootConfig.typingDuration;
225
- }
226
- }
227
-
228
- const fcaConfigPath = path.join(__dirname, 'config.json');
229
- if (fs.existsSync(fcaConfigPath)) {
230
- const fcaConfig = JSON.parse(fs.readFileSync(fcaConfigPath, 'utf8'));
231
- if (fcaConfig && typeof fcaConfig === 'object') {
232
- if (typeof fcaConfig.enableTypingIndicator !== 'undefined') config.enableTypingIndicator = fcaConfig.enableTypingIndicator;
233
- if (typeof fcaConfig.typingDuration !== 'undefined') config.typingDuration = fcaConfig.typingDuration;
234
- }
235
- }
236
-
237
- if (global.GoatBot && global.GoatBot.config) {
238
- if (typeof global.GoatBot.config.enableTypingIndicator !== 'undefined') config.enableTypingIndicator = global.GoatBot.config.enableTypingIndicator;
239
- if (typeof global.GoatBot.config.typingDuration !== 'undefined') config.typingDuration = global.GoatBot.config.typingDuration;
240
- }
241
- } catch (e) {
242
- log.warn('Error loading config.json:', e.message);
243
- }
244
-
245
- const refreshFcaConfig = () => {
246
- try {
247
- const updatedConfig = { enableTypingIndicator: false, typingDuration: 4000 };
248
-
249
- if (fs.existsSync(path.join(process.cwd(), 'config.json'))) {
250
- const rootConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'config.json'), 'utf8'));
251
- if (rootConfig && typeof rootConfig === 'object') {
252
- if (typeof rootConfig.enableTypingIndicator !== 'undefined') updatedConfig.enableTypingIndicator = rootConfig.enableTypingIndicator;
253
- if (typeof rootConfig.typingDuration !== 'undefined') updatedConfig.typingDuration = rootConfig.typingDuration;
254
- }
255
- }
256
-
257
- if (fs.existsSync(path.join(__dirname, 'config.json'))) {
258
- const fcaConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'), 'utf8'));
259
- if (fcaConfig && typeof fcaConfig === 'object') {
260
- if (typeof fcaConfig.enableTypingIndicator !== 'undefined') updatedConfig.enableTypingIndicator = fcaConfig.enableTypingIndicator;
261
- if (typeof fcaConfig.typingDuration !== 'undefined') updatedConfig.typingDuration = fcaConfig.typingDuration;
262
- }
263
- }
264
-
265
- if (global.GoatBot && global.GoatBot.config) {
266
- if (typeof global.GoatBot.config.enableTypingIndicator !== 'undefined') updatedConfig.enableTypingIndicator = global.GoatBot.config.enableTypingIndicator;
267
- if (typeof global.GoatBot.config.typingDuration !== 'undefined') updatedConfig.typingDuration = global.GoatBot.config.typingDuration;
268
- }
269
-
270
- ctx.config = updatedConfig;
271
- config = updatedConfig;
272
- } catch (e) {
273
- log.warn('Failed to refresh fca config:', e.message);
274
- }
275
- };
276
-
277
- refreshFcaConfig();
278
- ctx.refreshFcaConfig = refreshFcaConfig;
279
- if (global.GoatBot) {
280
- global.GoatBot.refreshFcaConfig = refreshFcaConfig;
281
- }
282
-
283
- ctx.config = config;
284
-
285
176
  var api = {
286
177
  setOptions: setOptions.bind(null, globalOptions),
287
178
  getAppState: () => utils.getAppState(jar),
288
179
  postFormData: (url, body) => utils.makeDefaults(html, userID, ctx).postFormData(url, ctx.jar, body)
289
180
  };
290
-
291
181
  var defaultFuncs = utils.makeDefaults(html, userID, ctx);
292
182
  api.postFormData = function (url, body) {
293
183
  return defaultFuncs.postFormData(url, ctx.jar, body);
294
184
  };
295
-
296
185
  api.getFreshDtsg = async function () {
297
186
  try {
298
- await delay(globalOptions.delayBetweenRequests || 1000);
299
187
  const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
300
188
  const $ = cheerio.load(res.body);
301
189
  let newDtsg;
@@ -325,18 +213,12 @@ function buildAPI(globalOptions, html, jar) {
325
213
 
326
214
  return newDtsg;
327
215
  } catch (e) {
328
- log.error("Error getting fresh dtsg:", e.message);
216
+ console.log("Error getting fresh dtsg:", e);
329
217
  return null;
330
218
  }
331
219
  };
332
-
333
- // Load API modules
334
- const srcPath = path.join(__dirname, 'src');
335
- if (fs.existsSync(srcPath)) {
336
- fs.readdirSync(srcPath).filter(v => v.endsWith('.js')).forEach(v => {
337
- api[v.replace('.js', '')] = require(`./src/${v}`)(utils.makeDefaults(html, userID, ctx), api, ctx);
338
- });
339
- }
220
+ //if (noMqttData) api.htmlData = noMqttData;
221
+ 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); });
340
222
 
341
223
  // Store original sendMessage as the primary method
342
224
  const originalSendMessage = api.sendMessage;
@@ -346,7 +228,8 @@ function buildAPI(globalOptions, html, jar) {
346
228
  try {
347
229
  return await originalSendMessage(msg, threadID, callback, replyToMessage, isSingleUser);
348
230
  } catch (error) {
349
- log.warn('sendMessage failed, using OldMessage fallback:', error.message);
231
+ // If modern method fails, fallback to OldMessage
232
+ console.log('sendMessage failed, using OldMessage fallback:', error.message);
350
233
  return api.OldMessage(msg, threadID, callback, replyToMessage, isSingleUser);
351
234
  }
352
235
  };
@@ -357,7 +240,6 @@ function buildAPI(globalOptions, html, jar) {
357
240
  };
358
241
 
359
242
  api.listen = api.listenMqtt;
360
-
361
243
  return {
362
244
  ctx,
363
245
  defaultFuncs,
@@ -383,32 +265,23 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
383
265
  form.locale = 'en_US';
384
266
  form.timezone = '240';
385
267
  form.lgnjs = Math.floor(Date.now() / 1000);
386
-
387
268
  const willBeCookies = html.split("\"_js_");
388
269
  willBeCookies.slice(1).forEach(val => {
389
270
  const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
390
271
  jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
391
272
  });
392
-
393
- log.info("Logging in with email/password...");
394
- await delay(2000); // Delay to avoid rate limiting
395
-
273
+ log.info("Logging in...");
396
274
  const loginRes = await utils.post(
397
275
  "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
398
276
  jar,
399
277
  form,
400
278
  loginOptions
401
279
  );
402
-
403
280
  await utils.saveCookies(jar)(loginRes);
404
281
  const headers = loginRes.headers;
405
-
406
- if (!headers.location) {
407
- throw new Error("Wrong username/password or account locked.");
408
- }
409
-
282
+ if (!headers.location) throw new Error("Wrong username/password.");
410
283
  if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
411
- log.info("login", "Login approval required. Checking checkpoint...");
284
+ log.info("login", "You have login approvals turned on.");
412
285
  const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
413
286
  await utils.saveCookies(jar)(checkpointRes);
414
287
  const checkpointHtml = checkpointRes.body;
@@ -417,7 +290,6 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
417
290
  $("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
418
291
  checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
419
292
  const form = utils.arrToForm(checkpointForm);
420
-
421
293
  if (checkpointHtml.includes("checkpoint/?next")) {
422
294
  return new Promise((resolve, reject) => {
423
295
  const submit2FA = async (code) => {
@@ -453,11 +325,7 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
453
325
  };
454
326
  });
455
327
  }
456
-
457
- if (!loginOptions.forceLogin) {
458
- throw new Error("Couldn't login. Facebook might have blocked this account or requires verification.");
459
- }
460
-
328
+ if (!loginOptions.forceLogin) throw new Error("Couldn't login. Facebook might have blocked this account.");
461
329
  form['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
462
330
  await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
463
331
  form.name_action_selected = 'save_device';
@@ -465,40 +333,39 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
465
333
  const appState = utils.getAppState(jar);
466
334
  return await loginHelper(appState, email, password, loginOptions, callback);
467
335
  }
468
-
469
336
  await utils.get('https://www.facebook.com/', jar, null, loginOptions);
470
337
  return await utils.saveCookies(jar);
471
338
  } catch (error) {
472
- log.error("Login error:", error.message);
473
339
  callback(error);
474
340
  }
475
341
  };
476
342
  }
477
343
 
344
+
478
345
  function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
479
346
  let mainPromise = null;
480
347
  const jar = utils.getJar();
481
-
482
348
  if (appState) {
483
349
  try {
484
- if (typeof appState === 'string') {
485
- appState = JSON.parse(appState);
486
- }
350
+ appState = JSON.parse(appState);
487
351
  } catch (e) {
488
- return callback(new Error("Failed to parse appState: " + e.message));
352
+ try {
353
+ appState = appState;
354
+ } catch (e) {
355
+ return callback(new Error("Failed to parse appState"));
356
+ }
489
357
  }
490
358
 
491
359
  try {
492
360
  appState.forEach(c => {
493
- const str = `${c.key}=${c.value}; ${c.expires ? 'expires=' + c.expires + ';' : ''} domain=${c.domain}; path=${c.path};`;
361
+ const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
494
362
  jar.setCookie(str, "http://" + c.domain);
495
363
  });
496
364
 
497
365
  mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
498
366
  .then(utils.saveCookies(jar));
499
367
  } catch (e) {
500
- log.error("Error setting cookies:", e.message);
501
- return callback(new Error("Failed to set appState cookies"));
368
+ process.exit(0);
502
369
  }
503
370
  } else {
504
371
  mainPromise = utils
@@ -523,7 +390,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
523
390
  .then(res => {
524
391
  const mobileAgentRegex = /MPageLoadClientMetrics/gs;
525
392
  if (!mobileAgentRegex.test(res.body)) {
526
- globalOptions.userAgent = getRandomUserAgent();
393
+ 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";
527
394
  return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
528
395
  }
529
396
  return res;
@@ -553,20 +420,12 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
553
420
  callback(null, api);
554
421
  })
555
422
  .catch(e => {
556
- log.error("Login helper error:", e.message);
557
423
  callback(e);
558
424
  });
559
425
  }
560
426
 
561
- function login(loginData, options, callback) {
562
- // Check for updates (non-blocking, only once per session)
563
- if (!global.stfcaUpdateChecked) {
564
- global.stfcaUpdateChecked = true;
565
- checkForFCAUpdate().catch(err => {
566
- // Silently ignore update check errors to not block login
567
- });
568
- }
569
427
 
428
+ function login(loginData, options, callback) {
570
429
  if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
571
430
  callback = options;
572
431
  options = {};
@@ -584,8 +443,7 @@ function login(loginData, options, callback) {
584
443
  logRecordSize: 100,
585
444
  online: false,
586
445
  emitReady: false,
587
- delayBetweenRequests: 1500,
588
- userAgent: getRandomUserAgent()
446
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
589
447
  };
590
448
 
591
449
  var prCallback = null;
@@ -603,24 +461,19 @@ function login(loginData, options, callback) {
603
461
  callback = prCallback;
604
462
  }
605
463
 
606
- // Handle different login methods
607
- if (loginData.appState) {
608
- setOptions(globalOptions, options);
609
- loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
610
- } else if (loginData.email && loginData.password) {
464
+ if (loginData.email && loginData.password) {
611
465
  setOptions(globalOptions, {
466
+ logLevel: "silent",
612
467
  forceLogin: true,
613
- userAgent: getRandomUserAgent(),
614
- delayBetweenRequests: 1500
468
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
615
469
  });
616
- loginHelper(null, loginData.email, loginData.password, globalOptions, callback, prCallback);
617
- } else {
618
- const error = new Error("Invalid login data. Provide either appState or email/password");
619
- if (callback) callback(error);
620
- else throw error;
470
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
471
+ } else if (loginData.appState) {
472
+ setOptions(globalOptions, options);
473
+ return loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
621
474
  }
622
-
623
475
  return returnPromise;
624
476
  }
625
477
 
478
+
626
479
  module.exports = login;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadowx-fca",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Unofficial Facebook Chat API for Node.js with Auto-Update System - modify by Mueid Mursalin Rifat",
5
5
  "main": "index.js",
6
6
  "files": [