shadowx-fca 2.3.0 → 2.4.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,477 +3,454 @@
3
3
  var utils = require("./utils");
4
4
  var cheerio = require("cheerio");
5
5
  var log = require("npmlog");
6
-
7
6
  log.maxRecordSize = 100;
8
7
  var checkVerified = null;
9
8
  const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
10
9
  global.ditconmemay = false;
11
10
 
12
11
  function setOptions(globalOptions, options) {
13
- Object.keys(options).map(function (key) {
14
- switch (Boolean_Option.includes(key)) {
15
- case true: {
16
- globalOptions[key] = Boolean(options[key]);
17
- break;
18
- }
19
- case false: {
20
- switch (key) {
21
- case 'pauseLog': {
22
- if (options.pauseLog) log.pause();
23
- else log.resume();
24
- break;
25
- }
26
- case 'logLevel': {
27
- log.level = options.logLevel;
28
- globalOptions.logLevel = options.logLevel;
29
- break;
30
- }
31
- case 'logRecordSize': {
32
- log.maxRecordSize = options.logRecordSize;
33
- globalOptions.logRecordSize = options.logRecordSize;
34
- break;
35
- }
36
- case 'pageID': {
37
- globalOptions.pageID = options.pageID.toString();
38
- break;
39
- }
40
- case 'userAgent': {
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');
42
- break;
43
- }
44
- case 'proxy': {
45
- if (typeof options.proxy != "string") {
46
- delete globalOptions.proxy;
47
- utils.setProxy();
48
- } else {
49
- globalOptions.proxy = options.proxy;
50
- utils.setProxy(globalOptions.proxy);
51
- }
52
- break;
53
- }
54
- default: {
55
- log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
56
- break;
57
- }
58
- }
59
- break;
60
- }
61
- }
62
- });
12
+ Object.keys(options).map(function (key) {
13
+ switch (Boolean_Option.includes(key)) {
14
+ case true: {
15
+ globalOptions[key] = Boolean(options[key]);
16
+ break;
17
+ }
18
+ case false: {
19
+ switch (key) {
20
+ case 'pauseLog': {
21
+ if (options.pauseLog) log.pause();
22
+ else log.resume();
23
+ break;
24
+ }
25
+ case 'logLevel': {
26
+ log.level = options.logLevel;
27
+ globalOptions.logLevel = options.logLevel;
28
+ break;
29
+ }
30
+ case 'logRecordSize': {
31
+ log.maxRecordSize = options.logRecordSize;
32
+ globalOptions.logRecordSize = options.logRecordSize;
33
+ break;
34
+ }
35
+ case 'pageID': {
36
+ globalOptions.pageID = options.pageID.toString();
37
+ break;
38
+ }
39
+ case 'userAgent': {
40
+ 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');
41
+ break;
42
+ }
43
+ case 'proxy': {
44
+ if (typeof options.proxy != "string") {
45
+ delete globalOptions.proxy;
46
+ utils.setProxy();
47
+ } else {
48
+ globalOptions.proxy = options.proxy;
49
+ utils.setProxy(globalOptions.proxy);
50
+ }
51
+ break;
52
+ }
53
+ default: {
54
+ log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
55
+ break;
56
+ }
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ });
63
62
  }
64
63
 
65
64
  function buildAPI(globalOptions, html, jar) {
66
- let fb_dtsg = null;
67
- let irisSeqID = null;
68
- function extractFromHTML() {
69
- try {
70
- const $ = cheerio.load(html);
71
- $('script').each((i, script) => {
72
- if (!fb_dtsg) {
73
- const scriptText = $(script).html() || '';
74
- const patterns = [
75
- /\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
76
- /\["DTSGInitData",\[\],{"token":"([^"]+)"/,
77
- /"token":"([^"]+)"/,
78
- /{\\"token\\":\\"([^\\]+)\\"/,
79
- /,\{"token":"([^"]+)"\},\d+\]/,
80
- /"async_get_token":"([^"]+)"/,
81
- /"dtsg":\{"token":"([^"]+)"/,
82
- /DTSGInitialData[^>]+>([^<]+)/
83
- ];
84
- for (const pattern of patterns) {
85
- const match = scriptText.match(pattern);
86
- if (match && match[1]) {
87
- try {
88
- const possibleJson = match[1].replace(/\\"/g, '"');
89
- const parsed = JSON.parse(possibleJson);
90
- fb_dtsg = parsed.token || parsed;
91
- } catch {
92
- fb_dtsg = match[1];
93
- }
94
- if (fb_dtsg) break;
95
- }
96
- }
97
- }
98
- });
99
- if (!fb_dtsg) {
100
- const dtsgInput = $('input[name="fb_dtsg"]').val();
101
- if (dtsgInput) fb_dtsg = dtsgInput;
102
- }
103
- const seqMatches = html.match(/irisSeqID":"([^"]+)"/);
104
- if (seqMatches && seqMatches[1]) {
105
- irisSeqID = seqMatches[1];
106
- }
107
- try {
108
- const jsonMatches = html.match(/\{"dtsg":({[^}]+})/);
109
- if (jsonMatches && jsonMatches[1]) {
110
- const dtsgData = JSON.parse(jsonMatches[1]);
111
- if (dtsgData.token) fb_dtsg = dtsgData.token;
112
- }
113
- } catch { }
114
- if (fb_dtsg) {
115
- console.log("Found fb_dtsg!");
116
- }
117
- } catch (e) {
118
- console.log("Error finding fb_dtsg:", e);
119
- }
120
- }
121
- extractFromHTML();
122
- var userID;
123
- var cookies = jar.getCookies("https://www.facebook.com");
124
- var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
125
- var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
126
- if (!userCookie && !tiktikCookie) {
127
- return log.error("Error! Your cookiestate is not valid!");
128
- }
129
- if (html.includes("/checkpoint/block/?next")) {
130
- return log.error('error', "Appstate is dead rechange it!", 'error');
131
- }
132
- userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
133
- //logger.log(`${cra(`[ CONNECT ]`)} Logged in as ${userID}`, "DATABASE");
134
- try { clearInterval(checkVerified); } catch (_) { }
135
- const clientID = (Math.random() * 2147483648 | 0).toString(16);
136
- let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb&sid=${userID}`;
137
- let region = "PNB";
65
+ let fb_dtsg = null;
66
+ let irisSeqID = null;
67
+ function extractFromHTML() {
68
+ try {
69
+ const $ = cheerio.load(html);
70
+ $('script').each((i, script) => {
71
+ if (!fb_dtsg) {
72
+ const scriptText = $(script).html() || '';
73
+ const patterns = [
74
+ /\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
75
+ /\["DTSGInitData",\[\],{"token":"([^"]+)"/,
76
+ /"token":"([^"]+)"/,
77
+ /{\\"token\\":\\"([^\\]+)\\"/,
78
+ /,\{"token":"([^"]+)"\},\d+\]/,
79
+ /"async_get_token":"([^"]+)"/,
80
+ /"dtsg":\{"token":"([^"]+)"/,
81
+ /DTSGInitialData[^>]+>([^<]+)/
82
+ ];
83
+ for (const pattern of patterns) {
84
+ const match = scriptText.match(pattern);
85
+ if (match && match[1]) {
86
+ try {
87
+ const possibleJson = match[1].replace(/\\"/g, '"');
88
+ const parsed = JSON.parse(possibleJson);
89
+ fb_dtsg = parsed.token || parsed;
90
+ } catch {
91
+ fb_dtsg = match[1];
92
+ }
93
+ if (fb_dtsg) break;
94
+ }
95
+ }
96
+ }
97
+ });
98
+ if (!fb_dtsg) {
99
+ const dtsgInput = $('input[name="fb_dtsg"]').val();
100
+ if (dtsgInput) fb_dtsg = dtsgInput;
101
+ }
102
+ const seqMatches = html.match(/irisSeqID":"([^"]+)"/);
103
+ if (seqMatches && seqMatches[1]) {
104
+ irisSeqID = seqMatches[1];
105
+ }
106
+ try {
107
+ const jsonMatches = html.match(/\{"dtsg":({[^}]+})/);
108
+ if (jsonMatches && jsonMatches[1]) {
109
+ const dtsgData = JSON.parse(jsonMatches[1]);
110
+ if (dtsgData.token) fb_dtsg = dtsgData.token;
111
+ }
112
+ } catch { }
113
+ if (fb_dtsg) {
114
+ log.info("✅ | Found fb_Dtsg");
115
+ }
116
+ } catch (e) {
117
+ console.log("Error finding fb_dtsg:", e);
118
+ }
119
+ }
120
+ extractFromHTML();
121
+ var userID;
122
+ var cookies = jar.getCookies("https://www.facebook.com");
123
+ var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
124
+ var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
125
+ if (!userCookie && !tiktikCookie) {
126
+ return log.error('login', "No cookie found for user, please check login information again");
127
+ }
128
+ if (html.includes("/checkpoint/block/?next")) {
129
+ return log.error('login', "Appstate dead, please replace with a new one!", 'error');
130
+ }
131
+ userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
132
+
133
+ try { clearInterval(checkVerified); } catch (_) { }
134
+ const clientID = (Math.random() * 2147483648 | 0).toString(16);
135
+ let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=prn&sid=${userID}`;
136
+ let region = "PRN";
138
137
 
139
- try {
140
- const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
141
- if (endpointMatch.input.includes("601051028565049")) {
142
- console.log(`login error.`);
143
- ditconmemay = true;
144
- }
145
- if (endpointMatch) {
146
- mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
147
- const url = new URL(mqttEndpoint);
148
- region = url.searchParams.get('region')?.toUpperCase() || "PNB";
149
- }
150
- } catch (e) {
151
- console.log('Using default MQTT endpoint');
152
- }
153
- log.info('Logging in...');
154
- var ctx = {
155
- userID: userID,
156
- jar: jar,
157
- clientID: clientID,
158
- globalOptions: globalOptions,
159
- loggedIn: true,
160
- access_token: 'NONE',
161
- clientMutationId: 0,
162
- mqttClient: undefined,
163
- lastSeqId: irisSeqID,
164
- syncToken: undefined,
165
- mqttEndpoint: mqttEndpoint,
166
- region: region,
167
- firstListen: true,
168
- fb_dtsg: fb_dtsg,
169
- req_ID: 0,
170
- callback_Task: {},
171
- wsReqNumber: 0,
172
- wsTaskNumber: 0,
173
- reqCallbacks: {},
174
- threadTypes: {} // Store thread type (dm/group) for each thread
175
- };
176
- var api = {
177
- setOptions: setOptions.bind(null, globalOptions),
178
- getAppState: () => utils.getAppState(jar),
179
- postFormData: (url, body) => utils.makeDefaults(html, userID, ctx).postFormData(url, ctx.jar, body)
180
- };
181
- var defaultFuncs = utils.makeDefaults(html, userID, ctx);
182
- api.postFormData = function (url, body) {
183
- return defaultFuncs.postFormData(url, ctx.jar, body);
184
- };
185
- api.getFreshDtsg = async function () {
186
- try {
187
- const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
188
- const $ = cheerio.load(res.body);
189
- let newDtsg;
190
- const patterns = [
191
- /\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
192
- /\["DTSGInitData",\[\],{"token":"([^"]+)"/,
193
- /"token":"([^"]+)"/,
194
- /name="fb_dtsg" value="([^"]+)"/
195
- ];
138
+ try {
139
+ const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
140
+ if (endpointMatch.input.includes("601051028565049")) {
141
+ console.log(`login error due to automatic account`);
142
+ ditconmemay = true;
143
+ }
144
+ if (endpointMatch) {
145
+ mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
146
+ const url = new URL(mqttEndpoint);
147
+ region = url.searchParams.get('region')?.toUpperCase() || "PRN";
148
+ }
149
+ } catch (e) {
150
+ console.log('Using default MQTT endpoint');
151
+ }
152
+ var ctx = {
153
+ userID: userID,
154
+ jar: jar,
155
+ clientID: clientID,
156
+ globalOptions: globalOptions,
157
+ loggedIn: true,
158
+ access_token: 'NONE',
159
+ clientMutationId: 0,
160
+ mqttClient: undefined,
161
+ lastSeqId: irisSeqID,
162
+ syncToken: undefined,
163
+ mqttEndpoint: mqttEndpoint,
164
+ region: region,
165
+ firstListen: true,
166
+ fb_dtsg: fb_dtsg,
167
+ req_ID: 0,
168
+ callback_Task: {},
169
+ wsReqNumber: 0,
170
+ wsTaskNumber: 0,
171
+ reqCallbacks: {}
172
+ };
173
+ var api = {
174
+ setOptions: setOptions.bind(null, globalOptions),
175
+ getAppState: () => utils.getAppState(jar),
176
+ postFormData: (url, body) => utils.makeDefaults(html, userID, ctx).postFormData(url, ctx.jar, body)
177
+ };
178
+ var defaultFuncs = utils.makeDefaults(html, userID, ctx);
179
+ api.postFormData = function (url, body) {
180
+ return defaultFuncs.postFormData(url, ctx.jar, body);
181
+ };
182
+ api.getFreshDtsg = async function () {
183
+ try {
184
+ const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
185
+ const $ = cheerio.load(res.body);
186
+ let newDtsg;
187
+ const patterns = [
188
+ /\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
189
+ /\["DTSGInitData",\[\],{"token":"([^"]+)"/,
190
+ /"token":"([^"]+)"/,
191
+ /name="fb_dtsg" value="([^"]+)"/
192
+ ];
196
193
 
197
- $('script').each((i, script) => {
198
- if (!newDtsg) {
199
- const scriptText = $(script).html() || '';
200
- for (const pattern of patterns) {
201
- const match = scriptText.match(pattern);
202
- if (match && match[1]) {
203
- newDtsg = match[1];
204
- break;
205
- }
206
- }
207
- }
208
- });
194
+ $('script').each((i, script) => {
195
+ if (!newDtsg) {
196
+ const scriptText = $(script).html() || '';
197
+ for (const pattern of patterns) {
198
+ const match = scriptText.match(pattern);
199
+ if (match && match[1]) {
200
+ newDtsg = match[1];
201
+ break;
202
+ }
203
+ }
204
+ }
205
+ });
209
206
 
210
- if (!newDtsg) {
211
- newDtsg = $('input[name="fb_dtsg"]').val();
212
- }
207
+ if (!newDtsg) {
208
+ newDtsg = $('input[name="fb_dtsg"]').val();
209
+ }
213
210
 
214
- return newDtsg;
215
- } catch (e) {
216
- console.log("Error getting fresh dtsg:", e);
217
- return null;
218
- }
219
- };
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); });
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
- api.listen = api.listenMqtt;
243
- return {
244
- ctx,
245
- defaultFuncs,
246
- api
247
- };
211
+ return newDtsg;
212
+ } catch (e) {
213
+ console.log("Error getting fresh dtsg:", e);
214
+ return null;
215
+ }
216
+ };
217
+
218
+ 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); });
219
+ api.listen = api.listenMqtt;
220
+ return {
221
+ ctx,
222
+ defaultFuncs,
223
+ api
224
+ };
248
225
  }
249
226
 
250
227
  function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
251
- return async function (res) {
252
- try {
253
- const html = res.body;
254
- const $ = cheerio.load(html);
255
- let arr = [];
256
- $("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
257
- arr = arr.filter(v => v.val && v.val.length);
258
- let form = utils.arrToForm(arr);
259
- form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
260
- form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
261
- form.email = email;
262
- form.pass = password;
263
- form.default_persistent = '0';
264
- form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
265
- form.locale = 'en_US';
266
- form.timezone = '240';
267
- form.lgnjs = Math.floor(Date.now() / 1000);
268
- const willBeCookies = html.split("\"_js_");
269
- willBeCookies.slice(1).forEach(val => {
270
- const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
271
- jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
272
- });
273
- log.info("Logging in...");
274
- const loginRes = await utils.post(
275
- "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
276
- jar,
277
- form,
278
- loginOptions
279
- );
280
- await utils.saveCookies(jar)(loginRes);
281
- const headers = loginRes.headers;
282
- if (!headers.location) throw new Error("Wrong username/password.");
283
- if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
284
- log.info("login", "You have login approvals turned on.");
285
- const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
286
- await utils.saveCookies(jar)(checkpointRes);
287
- const checkpointHtml = checkpointRes.body;
288
- const $ = cheerio.load(checkpointHtml);
289
- let checkpointForm = [];
290
- $("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
291
- checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
292
- const form = utils.arrToForm(checkpointForm);
293
- if (checkpointHtml.includes("checkpoint/?next")) {
294
- return new Promise((resolve, reject) => {
295
- const submit2FA = async (code) => {
296
- try {
297
- form.approvals_code = code;
298
- form['submit[Continue]'] = $("#checkpointSubmitButton").html();
299
- const approvalRes = await utils.post(
300
- "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
301
- jar,
302
- form,
303
- loginOptions
304
- );
305
- await utils.saveCookies(jar)(approvalRes);
306
- const approvalError = $("#approvals_code").parent().attr("data-xui-error");
307
- if (approvalError) throw new Error("Invalid 2FA code.");
308
- form.name_action_selected = 'dont_save';
309
- const finalRes = await utils.post(
310
- "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
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
- });
327
- }
328
- if (!loginOptions.forceLogin) throw new Error("Couldn't login. Facebook might have blocked this account.");
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);
331
- form.name_action_selected = 'save_device';
332
- const reviewRes = await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
333
- const appState = utils.getAppState(jar);
334
- return await loginHelper(appState, email, password, loginOptions, callback);
335
- }
336
- await utils.get('https://www.facebook.com/', jar, null, loginOptions);
337
- return await utils.saveCookies(jar);
338
- } catch (error) {
339
- callback(error);
340
- }
341
- };
228
+ return async function (res) {
229
+ try {
230
+ const html = res.body;
231
+ const $ = cheerio.load(html);
232
+ let arr = [];
233
+ $("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
234
+ arr = arr.filter(v => v.val && v.val.length);
235
+ let form = utils.arrToForm(arr);
236
+ form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
237
+ form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
238
+ form.email = email;
239
+ form.pass = password;
240
+ form.default_persistent = '0';
241
+ form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
242
+ form.locale = 'en_US';
243
+ form.timezone = '240';
244
+ form.lgnjs = Math.floor(Date.now() / 1000);
245
+ const willBeCookies = html.split("\"_js_");
246
+ willBeCookies.slice(1).forEach(val => {
247
+ const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
248
+ jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
249
+ });
250
+ log.info("login", "Logging in...");
251
+ const loginRes = await utils.post(
252
+ "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
253
+ jar,
254
+ form,
255
+ loginOptions
256
+ );
257
+ await utils.saveCookies(jar)(loginRes);
258
+ const headers = loginRes.headers;
259
+ if (!headers.location) throw new Error("Wrong username/password.");
260
+ if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
261
+ log.info("login", "You have login approvals turned on.");
262
+ const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
263
+ await utils.saveCookies(jar)(checkpointRes);
264
+ const checkpointHtml = checkpointRes.body;
265
+ const $ = cheerio.load(checkpointHtml);
266
+ let checkpointForm = [];
267
+ $("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
268
+ checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
269
+ const form = utils.arrToForm(checkpointForm);
270
+ if (checkpointHtml.includes("checkpoint/?next")) {
271
+ return new Promise((resolve, reject) => {
272
+ const submit2FA = async (code) => {
273
+ try {
274
+ form.approvals_code = code;
275
+ form['submit[Continue]'] = $("#checkpointSubmitButton").html();
276
+ const approvalRes = await utils.post(
277
+ "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
278
+ jar,
279
+ form,
280
+ loginOptions
281
+ );
282
+ await utils.saveCookies(jar)(approvalRes);
283
+ const approvalError = $("#approvals_code").parent().attr("data-xui-error");
284
+ if (approvalError) throw new Error("Invalid 2FA code.");
285
+ form.name_action_selected = 'dont_save';
286
+ const finalRes = await utils.post(
287
+ "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
288
+ jar,
289
+ form,
290
+ loginOptions
291
+ );
292
+ await utils.saveCookies(jar)(finalRes);
293
+ const appState = utils.getAppState(jar);
294
+ resolve(await loginHelper(appState, email, password, loginOptions, callback));
295
+ } catch (error) {
296
+ reject(error);
297
+ }
298
+ };
299
+ throw {
300
+ error: 'login-approval',
301
+ continue: submit2FA
302
+ };
303
+ });
304
+ }
305
+ if (!loginOptions.forceLogin) throw new Error("Couldn't login. Facebook might have blocked this account.");
306
+ form['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
307
+ await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
308
+ form.name_action_selected = 'save_device';
309
+ const reviewRes = await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
310
+ const appState = utils.getAppState(jar);
311
+ return await loginHelper(appState, email, password, loginOptions, callback);
312
+ }
313
+ await utils.get('https://www.facebook.com/', jar, null, loginOptions);
314
+ return await utils.saveCookies(jar);
315
+ } catch (error) {
316
+ callback(error);
317
+ }
318
+ };
342
319
  }
343
320
 
344
321
 
345
322
  function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
346
- let mainPromise = null;
347
- const jar = utils.getJar();
348
- if (appState) {
349
- try {
350
- appState = JSON.parse(appState);
351
- } catch (e) {
352
- try {
353
- appState = appState;
354
- } catch (e) {
355
- return callback(new Error("Failed to parse appState"));
356
- }
357
- }
323
+ let mainPromise = null;
324
+ const jar = utils.getJar();
325
+ if (appState) {
326
+ try {
327
+ appState = JSON.parse(appState);
328
+ } catch (e) {
329
+ try {
330
+ appState = appState;
331
+ } catch (e) {
332
+ return callback(new Error("Failed to parse appState"));
333
+ }
334
+ }
358
335
 
359
- try {
360
- appState.forEach(c => {
361
- const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
362
- jar.setCookie(str, "http://" + c.domain);
363
- });
336
+ try {
337
+ appState.forEach(c => {
338
+ const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
339
+ jar.setCookie(str, "http://" + c.domain);
340
+ });
364
341
 
365
- mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
366
- .then(utils.saveCookies(jar));
367
- } catch (e) {
368
- process.exit(0);
369
- }
370
- } else {
371
- mainPromise = utils
372
- .get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
373
- .then(utils.saveCookies(jar))
374
- .then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
375
- .then(() => utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar)));
376
- }
342
+ mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
343
+ .then(utils.saveCookies(jar));
344
+ } catch (e) {
345
+ process.exit(0);
346
+ }
347
+ } else {
348
+ mainPromise = utils
349
+ .get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
350
+ .then(utils.saveCookies(jar))
351
+ .then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
352
+ .then(() => utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar)));
353
+ }
377
354
 
378
- function handleRedirect(res) {
379
- const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
380
- const redirect = reg.exec(res.body);
381
- if (redirect && redirect[1]) {
382
- return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
383
- }
384
- return res;
385
- }
355
+ function handleRedirect(res) {
356
+ const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
357
+ const redirect = reg.exec(res.body);
358
+ if (redirect && redirect[1]) {
359
+ return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
360
+ }
361
+ return res;
362
+ }
386
363
 
387
- let ctx, api;
388
- mainPromise = mainPromise
389
- .then(handleRedirect)
390
- .then(res => {
391
- const mobileAgentRegex = /MPageLoadClientMetrics/gs;
392
- if (!mobileAgentRegex.test(res.body)) {
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";
394
- return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
395
- }
396
- return res;
397
- })
398
- .then(handleRedirect)
399
- .then(res => {
400
- const html = res.body;
401
- const Obj = buildAPI(globalOptions, html, jar);
402
- ctx = Obj.ctx;
403
- api = Obj.api;
404
- return res;
405
- });
364
+ let ctx, api;
365
+ mainPromise = mainPromise
366
+ .then(handleRedirect)
367
+ .then(res => {
368
+ const mobileAgentRegex = /MPageLoadClientMetrics/gs;
369
+ if (!mobileAgentRegex.test(res.body)) {
370
+ 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";
371
+ return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
372
+ }
373
+ return res;
374
+ })
375
+ .then(handleRedirect)
376
+ .then(res => {
377
+ const html = res.body;
378
+ const Obj = buildAPI(globalOptions, html, jar);
379
+ ctx = Obj.ctx;
380
+ api = Obj.api;
381
+ return res;
382
+ });
406
383
 
407
- if (globalOptions.pageID) {
408
- mainPromise = mainPromise
409
- .then(() => utils.get(`https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`, jar, null, globalOptions))
410
- .then(resData => {
411
- let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
412
- url = url.substring(0, url.length - 1);
413
- return utils.get('https://www.facebook.com' + url, jar, null, globalOptions);
414
- });
415
- }
384
+ if (globalOptions.pageID) {
385
+ mainPromise = mainPromise
386
+ .then(() => utils.get(`https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`, jar, null, globalOptions))
387
+ .then(resData => {
388
+ let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
389
+ url = url.substring(0, url.length - 1);
390
+ return utils.get('https://www.facebook.com' + url, jar, null, globalOptions);
391
+ });
392
+ }
416
393
 
417
- mainPromise
418
- .then(async () => {
419
- log.info('Login successful');
420
- callback(null, api);
421
- })
422
- .catch(e => {
423
- callback(e);
424
- });
394
+ mainPromise
395
+ .then(async () => {
396
+ log.info('Login successful');
397
+ callback(null, api);
398
+ })
399
+ .catch(e => {
400
+ callback(e);
401
+ });
425
402
  }
426
403
 
427
404
 
428
405
  function login(loginData, options, callback) {
429
- if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
430
- callback = options;
431
- options = {};
432
- }
406
+ if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
407
+ callback = options;
408
+ options = {};
409
+ }
433
410
 
434
- var globalOptions = {
435
- selfListen: false,
436
- listenEvents: true,
437
- listenTyping: false,
438
- updatePresence: false,
439
- forceLogin: false,
440
- autoMarkDelivery: false,
441
- autoMarkRead: false,
442
- autoReconnect: true,
443
- logRecordSize: 100,
444
- online: false,
445
- emitReady: false,
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"
447
- };
411
+ var globalOptions = {
412
+ selfListen: false,
413
+ listenEvents: false,
414
+ listenTyping: false,
415
+ updatePresence: false,
416
+ forceLogin: false,
417
+ autoMarkDelivery: false,
418
+ autoMarkRead: false,
419
+ autoReconnect: true,
420
+ logRecordSize: 100,
421
+ online: true,
422
+ emitReady: false,
423
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
424
+ };
448
425
 
449
- var prCallback = null;
450
- if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
451
- var rejectFunc = null;
452
- var resolveFunc = null;
453
- var returnPromise = new Promise(function (resolve, reject) {
454
- resolveFunc = resolve;
455
- rejectFunc = reject;
456
- });
457
- prCallback = function (error, api) {
458
- if (error) return rejectFunc(error);
459
- return resolveFunc(api);
460
- };
461
- callback = prCallback;
462
- }
426
+ var prCallback = null;
427
+ if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
428
+ var rejectFunc = null;
429
+ var resolveFunc = null;
430
+ var returnPromise = new Promise(function (resolve, reject) {
431
+ resolveFunc = resolve;
432
+ rejectFunc = reject;
433
+ });
434
+ prCallback = function (error, api) {
435
+ if (error) return rejectFunc(error);
436
+ return resolveFunc(api);
437
+ };
438
+ callback = prCallback;
439
+ }
463
440
 
464
- if (loginData.email && loginData.password) {
465
- setOptions(globalOptions, {
466
- logLevel: "silent",
467
- forceLogin: true,
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"
469
- });
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);
474
- }
475
- return returnPromise;
441
+ if (loginData.email && loginData.password) {
442
+ setOptions(globalOptions, {
443
+ logLevel: "silent",
444
+ forceLogin: true,
445
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
446
+ });
447
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
448
+ } else if (loginData.appState) {
449
+ setOptions(globalOptions, options);
450
+ return loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
451
+ }
452
+ return returnPromise;
476
453
  }
477
454
 
478
455
 
479
- module.exports = login;
456
+ module.exports = login;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadowx-fca",
3
- "version": "2.3.0",
3
+ "version": "2.4.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": [
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @by Allou Mohamed
3
+ * do not remove the author name to get more updates
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const utils = require("../utils");
9
+
10
+ module.exports = function (defaultFuncs, api, ctx) {
11
+ return function createAITheme(prompt, numThemes, callback) {
12
+ if (typeof numThemes === 'function') {
13
+ callback = numThemes;
14
+ numThemes = 3;
15
+ }
16
+ if (typeof numThemes !== 'number' || numThemes < 1) {
17
+ numThemes = 3;
18
+ }
19
+ if (numThemes > 10) {
20
+ numThemes = 10;
21
+ }
22
+
23
+ const form = {
24
+ av: ctx.i_userID || ctx.userID,
25
+ qpl_active_flow_ids: "25308101,25309433,521482085",
26
+ fb_api_caller_class: "RelayModern",
27
+ fb_api_req_friendly_name: "useGenerateAIThemeMutation",
28
+ variables: JSON.stringify({
29
+ input: {
30
+ client_mutation_id: "1",
31
+ actor_id: ctx.i_userID || ctx.userID,
32
+ bypass_cache: true,
33
+ caller: "MESSENGER",
34
+ num_themes: numThemes,
35
+ prompt: prompt
36
+ }
37
+ }),
38
+ server_timestamps: true,
39
+ doc_id: "23873748445608673",
40
+ fb_api_analytics_tags: JSON.stringify([
41
+ "qpl_active_flow_ids=25308101,25309433,521482085"
42
+ ]),
43
+ fb_dtsg: ctx.fb_dtsg
44
+ };
45
+
46
+ const extractUrl = (obj) => {
47
+ if (!obj) return null;
48
+ if (typeof obj === 'string') return obj;
49
+ return obj.uri || obj.url || null;
50
+ };
51
+
52
+ const normalizeTheme = (theme) => {
53
+ const normalized = { ...theme };
54
+
55
+ let lightUrl = null;
56
+ let darkUrl = null;
57
+
58
+ const previewUrls = theme.preview_image_urls || theme.preview_images || theme.preview_urls;
59
+
60
+ if (previewUrls) {
61
+ if (typeof previewUrls === 'string') {
62
+ lightUrl = darkUrl = previewUrls;
63
+ } else if (Array.isArray(previewUrls)) {
64
+ lightUrl = extractUrl(previewUrls[0]) || null;
65
+ darkUrl = extractUrl(previewUrls[1]) || lightUrl;
66
+ } else if (typeof previewUrls === 'object') {
67
+ lightUrl = extractUrl(previewUrls.light_mode) || extractUrl(previewUrls.light) || null;
68
+ darkUrl = extractUrl(previewUrls.dark_mode) || extractUrl(previewUrls.dark) || null;
69
+ }
70
+ }
71
+
72
+ if (!lightUrl && theme.background_asset && theme.background_asset.image) {
73
+ lightUrl = extractUrl(theme.background_asset.image);
74
+ }
75
+ if (!lightUrl && theme.icon_asset && theme.icon_asset.image) {
76
+ lightUrl = extractUrl(theme.icon_asset.image);
77
+ }
78
+
79
+ if (!darkUrl && theme.alternative_themes && theme.alternative_themes.length > 0) {
80
+ const darkTheme = theme.alternative_themes[0];
81
+ if (darkTheme.background_asset && darkTheme.background_asset.image) {
82
+ darkUrl = extractUrl(darkTheme.background_asset.image);
83
+ }
84
+ if (!darkUrl && darkTheme.icon_asset && darkTheme.icon_asset.image) {
85
+ darkUrl = extractUrl(darkTheme.icon_asset.image);
86
+ }
87
+ }
88
+
89
+ if (lightUrl && !darkUrl) {
90
+ darkUrl = lightUrl;
91
+ } else if (darkUrl && !lightUrl) {
92
+ lightUrl = darkUrl;
93
+ }
94
+
95
+ if (lightUrl || darkUrl) {
96
+ normalized.preview_image_urls = {
97
+ light_mode: lightUrl,
98
+ dark_mode: darkUrl
99
+ };
100
+ }
101
+
102
+ return normalized;
103
+ };
104
+
105
+ const promise = defaultFuncs
106
+ .post("https://web.facebook.com/api/graphql/", ctx.jar, form)
107
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
108
+ .then(resData => {
109
+ if (resData.errors) {
110
+ throw resData.errors;
111
+ }
112
+ const themes = resData.data.xfb_generate_ai_themes_from_prompt.themes;
113
+ return themes.map(normalizeTheme);
114
+ });
115
+
116
+ if (callback) {
117
+ promise.then(data => callback(null, data)).catch(err => {
118
+ utils.error("createAITheme", err.message || err);
119
+ callback(err);
120
+ });
121
+ return;
122
+ }
123
+
124
+ return promise.catch(err => {
125
+ utils.error("createAITheme", err.message || err);
126
+ throw err;
127
+ });
128
+ };
129
+ };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function postFormData(url, form, callback) {
8
+ var resolveFunc = function () {};
9
+ var rejectFunc = function () {};
10
+
11
+ var returnPromise = new Promise(function (resolve, reject) {
12
+ resolveFunc = resolve;
13
+ rejectFunc = reject;
14
+ });
15
+
16
+ if (
17
+ !callback &&
18
+ (utils.getType(form) == "Function" ||
19
+ utils.getType(form) == "AsyncFunction")
20
+ ) {
21
+ callback = form;
22
+ form = {};
23
+ }
24
+
25
+ form = form || {};
26
+
27
+ callback =
28
+ callback ||
29
+ function (err, data) {
30
+ if (err) return rejectFunc(err);
31
+ resolveFunc(data);
32
+ };
33
+
34
+ defaultFuncs
35
+ .postFormData(url, ctx.jar, form, {})
36
+ .then(function (resData) {
37
+ callback(null, resData.body.toString());
38
+ })
39
+ .catch(function (err) {
40
+ log.error("postFormData", err);
41
+ return callback(err);
42
+ });
43
+
44
+ return returnPromise;
45
+ };
46
+ };