nexus-fca 2.0.2 โ†’ 2.0.4

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.
@@ -0,0 +1,611 @@
1
+ /**
2
+ * Nexus Login System - Advanced & Safe Facebook Login
3
+ * Automatic appstate generation and management
4
+ * Built for Nexus-FCA with maximum account safety
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { v4: uuidv4 } = require('uuid');
10
+ const { TOTP } = require("totp-generator");
11
+ const axios = require("axios");
12
+ const crypto = require('crypto');
13
+
14
+ class NexusLoginSystem {
15
+ constructor(options = {}) {
16
+ this.options = {
17
+ appstatePath: options.appstatePath || path.join(__dirname, 'appstate.json'),
18
+ credentialsPath: options.credentialsPath || path.join(__dirname, 'credentials.json'),
19
+ backupPath: options.backupPath || path.join(__dirname, 'backups'),
20
+ autoLogin: options.autoLogin !== false, // Default true
21
+ autoSave: options.autoSave !== false, // Default true
22
+ safeMode: options.safeMode !== false, // Default true
23
+ maxRetries: options.maxRetries || 3,
24
+ retryDelay: options.retryDelay || 5000,
25
+ ...options
26
+ };
27
+
28
+ this.deviceCache = new Map();
29
+ this.loginAttempts = 0;
30
+ this.lastLoginTime = 0;
31
+
32
+ // Create directories
33
+ this.ensureDirectories();
34
+
35
+ this.logger('Nexus Login System initialized', '๐Ÿš€');
36
+ }
37
+
38
+ logger(message, emoji = '๐Ÿ“') {
39
+ const timestamp = new Date().toLocaleString();
40
+ console.log(`${emoji} [${timestamp}] ${message}`);
41
+ }
42
+
43
+ ensureDirectories() {
44
+ const dirs = [
45
+ path.dirname(this.options.appstatePath),
46
+ path.dirname(this.options.credentialsPath),
47
+ this.options.backupPath
48
+ ];
49
+
50
+ dirs.forEach(dir => {
51
+ if (!fs.existsSync(dir)) {
52
+ fs.mkdirSync(dir, { recursive: true });
53
+ }
54
+ });
55
+ }
56
+
57
+ // Enhanced device simulation with better fingerprinting
58
+ getRandomDevice() {
59
+ const devices = [
60
+ { model: "Pixel 6", build: "SP2A.220505.002", sdk: "30", release: "11" },
61
+ { model: "Pixel 5", build: "RQ3A.210805.001.A1", sdk: "30", release: "11" },
62
+ { model: "Samsung Galaxy S21", build: "G991USQU4AUDA", sdk: "30", release: "11" },
63
+ { model: "OnePlus 9", build: "LE2115_11_C.48", sdk: "30", release: "11" },
64
+ { model: "Xiaomi Mi 11", build: "RKQ1.200826.002", sdk: "30", release: "11" },
65
+ { model: "Pixel 7", build: "TD1A.220804.031", sdk: "33", release: "13" },
66
+ { model: "Samsung Galaxy S22", build: "S901USQU2AVB3", sdk: "32", release: "12" }
67
+ ];
68
+
69
+ const device = devices[Math.floor(Math.random() * devices.length)];
70
+ const deviceId = this.generateConsistentDeviceId(device);
71
+
72
+ return {
73
+ userAgent: `Dalvik/2.1.0 (Linux; U; Android ${device.release}; ${device.model} Build/${device.build})`,
74
+ device,
75
+ deviceId,
76
+ familyDeviceId: uuidv4(),
77
+ androidId: this.generateAndroidId()
78
+ };
79
+ }
80
+
81
+ generateConsistentDeviceId(device) {
82
+ const key = `${device.model}_${device.build}`;
83
+ if (this.deviceCache.has(key)) {
84
+ return this.deviceCache.get(key);
85
+ }
86
+
87
+ const deviceId = uuidv4();
88
+ this.deviceCache.set(key, deviceId);
89
+ return deviceId;
90
+ }
91
+
92
+ generateAndroidId() {
93
+ return crypto.randomBytes(8).toString('hex');
94
+ }
95
+
96
+ randomString(length = 10) {
97
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
98
+ let result = 'abcdefghijklmnopqrstuvwxyz'.charAt(Math.floor(Math.random() * 26));
99
+ for (let i = 0; i < length - 1; i++) {
100
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
101
+ }
102
+ return result;
103
+ }
104
+
105
+ encodesig(object) {
106
+ let data = '';
107
+ Object.keys(object).forEach(key => {
108
+ data += `${key}=${object[key]}`;
109
+ });
110
+ return crypto.createHash('md5').update(data + '62f8ce9f74b12f84c123cc23437a4a32').digest('hex');
111
+ }
112
+
113
+ sort(object) {
114
+ const sortedKeys = Object.keys(object).sort();
115
+ let sortedObject = {};
116
+ for (const key of sortedKeys) {
117
+ sortedObject[key] = object[key];
118
+ }
119
+ return sortedObject;
120
+ }
121
+
122
+ // Safe delay between requests
123
+ async safeDelay(min = 1000, max = 3000) {
124
+ if (!this.options.safeMode) return;
125
+
126
+ const delay = Math.floor(Math.random() * (max - min + 1)) + min;
127
+ this.logger(`Safety delay: ${delay}ms`, 'โณ');
128
+ await new Promise(resolve => setTimeout(resolve, delay));
129
+ }
130
+
131
+ // Check if appstate exists and is valid
132
+ hasValidAppstate() {
133
+ try {
134
+ if (!fs.existsSync(this.options.appstatePath)) {
135
+ return false;
136
+ }
137
+
138
+ const appstate = JSON.parse(fs.readFileSync(this.options.appstatePath, 'utf8'));
139
+
140
+ if (!Array.isArray(appstate) || appstate.length === 0) {
141
+ return false;
142
+ }
143
+
144
+ // Check if cookies are not too old
145
+ const now = Date.now();
146
+ const validCookies = appstate.filter(cookie => {
147
+ if (!cookie.expires && !cookie.expirationDate) return true;
148
+
149
+ const expires = new Date(cookie.expires || cookie.expirationDate).getTime();
150
+ return expires > now;
151
+ });
152
+
153
+ return validCookies.length > appstate.length * 0.7; // 70% valid cookies required
154
+ } catch (error) {
155
+ this.logger(`Appstate validation failed: ${error.message}`, 'โŒ');
156
+ return false;
157
+ }
158
+ }
159
+
160
+ // Load existing appstate
161
+ loadAppstate() {
162
+ try {
163
+ if (!fs.existsSync(this.options.appstatePath)) {
164
+ return null;
165
+ }
166
+
167
+ const appstate = JSON.parse(fs.readFileSync(this.options.appstatePath, 'utf8'));
168
+ this.logger(`Loaded appstate with ${appstate.length} cookies`, 'โœ…');
169
+ return appstate;
170
+ } catch (error) {
171
+ this.logger(`Failed to load appstate: ${error.message}`, 'โŒ');
172
+ return null;
173
+ }
174
+ }
175
+
176
+ // Save appstate with backup
177
+ saveAppstate(appstate, metadata = {}) {
178
+ try {
179
+ if (!this.options.autoSave) return;
180
+
181
+ // Create backup first
182
+ if (fs.existsSync(this.options.appstatePath)) {
183
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
184
+ const backupFile = path.join(this.options.backupPath, `appstate-backup-${timestamp}.json`);
185
+ fs.copyFileSync(this.options.appstatePath, backupFile);
186
+ this.logger(`Backup created: ${backupFile}`, '๐Ÿ“ฆ');
187
+ }
188
+
189
+ // Save new appstate
190
+ const dataToSave = {
191
+ appstate,
192
+ metadata: {
193
+ generated: new Date().toISOString(),
194
+ deviceInfo: metadata.deviceInfo || {},
195
+ ...metadata
196
+ },
197
+ version: '1.0'
198
+ };
199
+
200
+ fs.writeFileSync(this.options.appstatePath, JSON.stringify(appstate, null, 2));
201
+ fs.writeFileSync(
202
+ this.options.appstatePath.replace('.json', '-full.json'),
203
+ JSON.stringify(dataToSave, null, 2)
204
+ );
205
+
206
+ this.logger(`Appstate saved with ${appstate.length} cookies`, '๐Ÿ’พ');
207
+ return true;
208
+ } catch (error) {
209
+ this.logger(`Failed to save appstate: ${error.message}`, 'โŒ');
210
+ return false;
211
+ }
212
+ }
213
+
214
+ // Enhanced login with better error handling
215
+ async generateAppstate(credentials) {
216
+ try {
217
+ if (this.options.safeMode) {
218
+ // Rate limiting check
219
+ const timeSinceLastLogin = Date.now() - this.lastLoginTime;
220
+ if (timeSinceLastLogin < 30000) { // 30 seconds minimum
221
+ this.logger('Rate limiting: Please wait before next login attempt', 'โš ๏ธ');
222
+ await new Promise(resolve => setTimeout(resolve, 30000 - timeSinceLastLogin));
223
+ }
224
+ }
225
+
226
+ this.lastLoginTime = Date.now();
227
+ this.loginAttempts++;
228
+
229
+ const androidDevice = this.getRandomDevice();
230
+ const machineId = this.randomString(24);
231
+
232
+ await this.safeDelay(1000, 2000);
233
+
234
+ const form = {
235
+ adid: uuidv4(),
236
+ email: credentials.username,
237
+ password: credentials.password,
238
+ format: 'json',
239
+ device_id: androidDevice.deviceId,
240
+ cpl: 'true',
241
+ family_device_id: androidDevice.familyDeviceId,
242
+ locale: 'en_US',
243
+ client_country_code: 'US',
244
+ credentials_type: 'device_based_login_password',
245
+ generate_session_cookies: '1',
246
+ generate_analytics_claim: '1',
247
+ generate_machine_id: '1',
248
+ currently_logged_in_userid: '0',
249
+ irisSeqID: 1,
250
+ try_num: "1",
251
+ enroll_misauth: "false",
252
+ meta_inf_fbmeta: "NO_FILE",
253
+ source: 'login',
254
+ machine_id: machineId,
255
+ fb_api_req_friendly_name: 'authenticate',
256
+ fb_api_caller_class: 'com.facebook.account.login.protocol.Fb4aAuthHandler',
257
+ api_key: '882a8490361da98702bf97a021ddc14d',
258
+ access_token: '350685531728|62f8ce9f74b12f84c123cc23437a4a32',
259
+ advertiser_id: uuidv4(),
260
+ device_platform: 'android',
261
+ app_version: '392.0.0.0.66',
262
+ network_type: 'WIFI'
263
+ };
264
+
265
+ form.sig = this.encodesig(this.sort(form));
266
+
267
+ const options = {
268
+ url: 'https://b-graph.facebook.com/auth/login',
269
+ method: 'post',
270
+ data: form,
271
+ transformRequest: [(data) => require('querystring').stringify(data)],
272
+ headers: {
273
+ 'content-type': 'application/x-www-form-urlencoded',
274
+ 'x-fb-friendly-name': form["fb_api_req_friendly_name"],
275
+ 'x-fb-http-engine': 'Liger',
276
+ 'user-agent': androidDevice.userAgent,
277
+ 'x-fb-connection-type': 'WIFI',
278
+ 'x-fb-net-hni': '',
279
+ 'x-fb-sim-hni': '',
280
+ 'x-fb-device-group': '5120',
281
+ 'x-tigon-is-retry': 'False',
282
+ 'x-fb-rmd': 'cached=0;state=NO_MATCH',
283
+ 'x-fb-request-analytics-tags': 'unknown',
284
+ 'authorization': `OAuth ${form.access_token}`,
285
+ 'accept-language': 'en-US,en;q=0.9',
286
+ 'x-fb-client-ip': 'True',
287
+ 'x-fb-server-cluster': 'True'
288
+ },
289
+ timeout: 30000
290
+ };
291
+
292
+ this.logger('Attempting login with enhanced security...', '๐Ÿ”');
293
+
294
+ return new Promise((resolve) => {
295
+ axios.request(options).then(async (response) => {
296
+ try {
297
+ const appstate = response.data.session_cookies.map(cookie => ({
298
+ key: cookie.name,
299
+ value: cookie.value,
300
+ domain: cookie.domain,
301
+ path: cookie.path,
302
+ expires: cookie.expires,
303
+ httpOnly: cookie.httpOnly,
304
+ secure: cookie.secure
305
+ }));
306
+
307
+ if (credentials.i_user) {
308
+ appstate.push({
309
+ key: 'i_user',
310
+ value: credentials.i_user,
311
+ domain: '.facebook.com',
312
+ path: '/'
313
+ });
314
+ }
315
+
316
+ await this.safeDelay(500, 1500);
317
+
318
+ // Get additional token
319
+ const tokenOptions = {
320
+ url: `https://api.facebook.com/method/auth.getSessionforApp?format=json&access_token=${response.data.access_token}&new_app_id=275254692598279`,
321
+ method: 'get',
322
+ headers: {
323
+ 'user-agent': androidDevice.userAgent,
324
+ 'x-fb-connection-type': 'WIFI',
325
+ 'authorization': `OAuth ${response.data.access_token}`
326
+ },
327
+ timeout: 15000
328
+ };
329
+
330
+ try {
331
+ const tokenResponse = await axios.request(tokenOptions);
332
+
333
+ const result = {
334
+ success: true,
335
+ appstate: appstate,
336
+ access_token: response.data.access_token,
337
+ access_token_eaad6v7: tokenResponse.data.access_token,
338
+ device_info: {
339
+ model: androidDevice.device.model,
340
+ user_agent: androidDevice.userAgent,
341
+ device_id: androidDevice.deviceId
342
+ },
343
+ generated_at: new Date().toISOString()
344
+ };
345
+
346
+ this.saveAppstate(appstate, result);
347
+ this.logger('Login successful! Appstate generated and saved', '๐ŸŽ‰');
348
+
349
+ resolve(result);
350
+ } catch (tokenError) {
351
+ // Token fetch failed, but we still have appstate
352
+ const result = {
353
+ success: true,
354
+ appstate: appstate,
355
+ access_token: response.data.access_token,
356
+ device_info: {
357
+ model: androidDevice.device.model,
358
+ user_agent: androidDevice.userAgent
359
+ },
360
+ warning: 'Additional token generation failed',
361
+ generated_at: new Date().toISOString()
362
+ };
363
+
364
+ this.saveAppstate(appstate, result);
365
+ this.logger('Login successful! (Token generation had issues)', 'โš ๏ธ');
366
+
367
+ resolve(result);
368
+ }
369
+
370
+ } catch (e) {
371
+ this.logger(`Login processing error: ${e.message}`, 'โŒ');
372
+ resolve({
373
+ success: false,
374
+ message: "Login processing failed. Please try again."
375
+ });
376
+ }
377
+ }).catch(async (error) => {
378
+ // Handle 2FA requirement
379
+ try {
380
+ const errorData = error.response?.data?.error?.error_data;
381
+ if (!errorData) {
382
+ throw new Error('Unknown login error');
383
+ }
384
+
385
+ let twoFactorCode;
386
+
387
+ if (credentials._2fa && credentials._2fa !== "0") {
388
+ twoFactorCode = credentials._2fa;
389
+ } else if (credentials.twofactor && credentials.twofactor !== "0") {
390
+ try {
391
+ this.logger('Processing 2FA with TOTP...', '๐Ÿ”');
392
+ const cleanSecret = decodeURI(credentials.twofactor).replace(/\s+/g, '').toUpperCase();
393
+ const { otp } = TOTP.generate(cleanSecret);
394
+ twoFactorCode = otp;
395
+ this.logger(`Generated 2FA code: ${otp}`, '๐Ÿ”‘');
396
+ } catch (e) {
397
+ return resolve({
398
+ success: false,
399
+ message: 'Invalid 2FA secret key format'
400
+ });
401
+ }
402
+ } else {
403
+ return resolve({
404
+ success: false,
405
+ message: 'Two-factor authentication required. Please provide 2FA secret or code.'
406
+ });
407
+ }
408
+
409
+ await this.safeDelay(2000, 4000);
410
+
411
+ const twoFactorForm = {
412
+ ...form,
413
+ twofactor_code: twoFactorCode,
414
+ encrypted_msisdn: "",
415
+ userid: errorData.uid,
416
+ machine_id: errorData.machine_id || machineId,
417
+ first_factor: errorData.login_first_factor,
418
+ credentials_type: "two_factor"
419
+ };
420
+
421
+ twoFactorForm.sig = this.encodesig(this.sort(twoFactorForm));
422
+ options.data = twoFactorForm;
423
+
424
+ this.logger('Attempting 2FA login...', '๐Ÿ”');
425
+
426
+ try {
427
+ const twoFactorResponse = await axios.request(options);
428
+
429
+ const appstate = twoFactorResponse.data.session_cookies.map(cookie => ({
430
+ key: cookie.name,
431
+ value: cookie.value,
432
+ domain: cookie.domain,
433
+ path: cookie.path,
434
+ expires: cookie.expires,
435
+ httpOnly: cookie.httpOnly,
436
+ secure: cookie.secure
437
+ }));
438
+
439
+ if (credentials.i_user) {
440
+ appstate.push({
441
+ key: 'i_user',
442
+ value: credentials.i_user,
443
+ domain: '.facebook.com',
444
+ path: '/'
445
+ });
446
+ }
447
+
448
+ const result = {
449
+ success: true,
450
+ appstate: appstate,
451
+ access_token: twoFactorResponse.data.access_token,
452
+ device_info: {
453
+ model: androidDevice.device.model,
454
+ user_agent: androidDevice.userAgent
455
+ },
456
+ method: '2FA',
457
+ generated_at: new Date().toISOString()
458
+ };
459
+
460
+ this.saveAppstate(appstate, result);
461
+ this.logger('2FA login successful! Appstate saved', '๐ŸŽ‰');
462
+
463
+ resolve(result);
464
+
465
+ } catch (requestError) {
466
+ this.logger(`2FA request failed: ${requestError.message}`, 'โŒ');
467
+ resolve({
468
+ success: false,
469
+ message: '2FA verification failed. Check your code and try again.'
470
+ });
471
+ }
472
+
473
+ } catch (twoFactorError) {
474
+ this.logger(`2FA error: ${twoFactorError.message}`, 'โŒ');
475
+ resolve({
476
+ success: false,
477
+ message: 'Login failed. Check credentials and try again.'
478
+ });
479
+ }
480
+ });
481
+ });
482
+
483
+ } catch (e) {
484
+ this.logger(`Unexpected error: ${e.message}`, '๐Ÿ’ฅ');
485
+ return {
486
+ success: false,
487
+ message: 'Unexpected error occurred. Please try again.'
488
+ };
489
+ }
490
+ }
491
+
492
+ // Main login method with auto-detection
493
+ async login(credentials = null) {
494
+ try {
495
+ this.logger('Starting Nexus Login System...', '๐Ÿš€');
496
+
497
+ // Check for existing valid appstate first
498
+ if (this.options.autoLogin && this.hasValidAppstate()) {
499
+ this.logger('Valid appstate found, loading...', 'โœ…');
500
+ const appstate = this.loadAppstate();
501
+
502
+ if (appstate) {
503
+ return {
504
+ success: true,
505
+ appstate: appstate,
506
+ method: 'existing_appstate',
507
+ message: 'Login successful using existing appstate'
508
+ };
509
+ }
510
+ }
511
+
512
+ // No valid appstate, need credentials
513
+ if (!credentials) {
514
+ // Try to load from credentials file
515
+ if (fs.existsSync(this.options.credentialsPath)) {
516
+ try {
517
+ credentials = JSON.parse(fs.readFileSync(this.options.credentialsPath, 'utf8'));
518
+ this.logger('Credentials loaded from file', '๐Ÿ“');
519
+ } catch (error) {
520
+ this.logger('Failed to load credentials file', 'โŒ');
521
+ }
522
+ }
523
+
524
+ if (!credentials) {
525
+ return {
526
+ success: false,
527
+ message: 'No valid appstate found and no credentials provided'
528
+ };
529
+ }
530
+ }
531
+
532
+ // Validate credentials
533
+ if (!credentials.username || !credentials.password) {
534
+ return {
535
+ success: false,
536
+ message: 'Username and password are required'
537
+ };
538
+ }
539
+
540
+ this.logger('Generating new appstate...', '๐Ÿ”„');
541
+
542
+ // Generate new appstate
543
+ const result = await this.generateAppstate(credentials);
544
+
545
+ if (result.success) {
546
+ // Save credentials for future use (optional)
547
+ if (this.options.autoSave && !fs.existsSync(this.options.credentialsPath)) {
548
+ try {
549
+ const credentialsToSave = { ...credentials };
550
+ delete credentialsToSave.password; // Don't save password for security
551
+ fs.writeFileSync(this.options.credentialsPath, JSON.stringify(credentialsToSave, null, 2));
552
+ } catch (error) {
553
+ this.logger('Failed to save credentials (non-critical)', 'โš ๏ธ');
554
+ }
555
+ }
556
+ }
557
+
558
+ return result;
559
+
560
+ } catch (error) {
561
+ this.logger(`Login system error: ${error.message}`, '๐Ÿ’ฅ');
562
+ return {
563
+ success: false,
564
+ message: `System error: ${error.message}`
565
+ };
566
+ }
567
+ }
568
+
569
+ // Cleanup old backups
570
+ cleanupBackups(maxAge = 7 * 24 * 60 * 60 * 1000) { // 7 days
571
+ try {
572
+ if (!fs.existsSync(this.options.backupPath)) return;
573
+
574
+ const files = fs.readdirSync(this.options.backupPath);
575
+ const now = Date.now();
576
+ let cleaned = 0;
577
+
578
+ files.forEach(file => {
579
+ const filePath = path.join(this.options.backupPath, file);
580
+ const stats = fs.statSync(filePath);
581
+
582
+ if (now - stats.mtime.getTime() > maxAge) {
583
+ fs.unlinkSync(filePath);
584
+ cleaned++;
585
+ }
586
+ });
587
+
588
+ if (cleaned > 0) {
589
+ this.logger(`Cleaned up ${cleaned} old backup files`, '๐Ÿงน');
590
+ }
591
+ } catch (error) {
592
+ this.logger(`Backup cleanup failed: ${error.message}`, 'โš ๏ธ');
593
+ }
594
+ }
595
+
596
+ // Get login status
597
+ getStatus() {
598
+ const hasAppstate = this.hasValidAppstate();
599
+ const hasCredentials = fs.existsSync(this.options.credentialsPath);
600
+
601
+ return {
602
+ hasValidAppstate: hasAppstate,
603
+ hasCredentials: hasCredentials,
604
+ appstatePath: this.options.appstatePath,
605
+ lastLogin: hasAppstate ? fs.statSync(this.options.appstatePath).mtime : null,
606
+ loginAttempts: this.loginAttempts
607
+ };
608
+ }
609
+ }
610
+
611
+ module.exports = NexusLoginSystem;