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 +197 -347
- package/package.json +2 -2
- package/src/GetBotInfo.js +29 -20
- package/src/OldMessage.js +106 -114
- package/src/comment.js +28 -15
- package/src/getUserInfo.js +43 -222
- package/src/listenMqtt.js +116 -9
- package/src/nickname.js +89 -81
- package/src/sendMessage.js +292 -117
- package/src/sendMessage2.js +243 -0
- package/src/sendTypingIndicator.js +101 -45
- package/src/shareContact.js +78 -30
- package/utils.js +1305 -7
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
|
-
|
|
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
|
|
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(
|
|
131
|
+
return log.error("Error! Your cookiestate is not valid!");
|
|
128
132
|
}
|
|
129
133
|
if (html.includes("/checkpoint/block/?next")) {
|
|
130
|
-
return log.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=
|
|
137
|
-
let region = "
|
|
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
|
|
142
|
-
|
|
143
|
-
|
|
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() || "
|
|
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
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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/
|
|
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
|
|
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:
|
|
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:
|
|
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/
|
|
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/
|
|
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;
|