fca-luxury 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. package/index.js +536 -512
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  "use strict";
2
3
 
3
4
  var utils = require("./utils");
@@ -10,535 +11,558 @@ var defaultLogRecordSize = 100;
10
11
  log.maxRecordSize = defaultLogRecordSize;
11
12
 
12
13
  function setOptions(globalOptions, options) {
13
- Object.keys(options).map(function (key) {
14
- switch (key) {
15
- case 'pauseLog':
16
- if (options.pauseLog) log.pause();
17
- break;
18
- case 'online':
19
- globalOptions.online = Boolean(options.online);
20
- break;
21
- case 'logLevel':
22
- log.level = options.logLevel;
23
- globalOptions.logLevel = options.logLevel;
24
- break;
25
- case 'logRecordSize':
26
- log.maxRecordSize = options.logRecordSize;
27
- globalOptions.logRecordSize = options.logRecordSize;
28
- break;
29
- case 'selfListen':
30
- globalOptions.selfListen = Boolean(options.selfListen);
31
- break;
32
- case 'listenEvents':
33
- globalOptions.listenEvents = Boolean(options.listenEvents);
34
- break;
35
- case 'pageID':
36
- globalOptions.pageID = options.pageID.toString();
37
- break;
38
- case 'updatePresence':
39
- globalOptions.updatePresence = Boolean(options.updatePresence);
40
- break;
41
- case 'forceLogin':
42
- globalOptions.forceLogin = Boolean(options.forceLogin);
43
- break;
44
- case 'userAgent':
45
- globalOptions.userAgent = options.userAgent;
46
- break;
47
- case 'autoMarkDelivery':
48
- globalOptions.autoMarkDelivery = Boolean(options.autoMarkDelivery);
49
- break;
50
- case 'autoMarkRead':
51
- globalOptions.autoMarkRead = Boolean(options.autoMarkRead);
52
- break;
53
- case 'listenTyping':
54
- globalOptions.listenTyping = Boolean(options.listenTyping);
55
- break;
56
- case 'proxy':
57
- if (typeof options.proxy != "string") {
58
- delete globalOptions.proxy;
59
- utils.setProxy();
60
- }
61
- else {
62
- globalOptions.proxy = options.proxy;
63
- utils.setProxy(globalOptions.proxy);
64
- }
65
- break;
66
- case 'autoReconnect':
67
- globalOptions.autoReconnect = Boolean(options.autoReconnect);
68
- break;
69
- case 'emitReady':
70
- globalOptions.emitReady = Boolean(options.emitReady);
71
- break;
72
- default:
73
- log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
74
- break;
75
- }
76
- });
14
+ Object.keys(options).map(function (key) {
15
+ switch (key) {
16
+ case 'pauseLog':
17
+ if (options.pauseLog) log.pause();
18
+ break;
19
+ case 'online':
20
+ globalOptions.online = Boolean(options.online);
21
+ break;
22
+ case 'logLevel':
23
+ log.level = options.logLevel;
24
+ globalOptions.logLevel = options.logLevel;
25
+ break;
26
+ case 'logRecordSize':
27
+ log.maxRecordSize = options.logRecordSize;
28
+ globalOptions.logRecordSize = options.logRecordSize;
29
+ break;
30
+ case 'selfListen':
31
+ globalOptions.selfListen = Boolean(options.selfListen);
32
+ break;
33
+ case 'listenEvents':
34
+ globalOptions.listenEvents = Boolean(options.listenEvents);
35
+ break;
36
+ case 'pageID':
37
+ globalOptions.pageID = options.pageID.toString();
38
+ break;
39
+ case 'updatePresence':
40
+ globalOptions.updatePresence = Boolean(options.updatePresence);
41
+ break;
42
+ case 'forceLogin':
43
+ globalOptions.forceLogin = Boolean(options.forceLogin);
44
+ break;
45
+ case 'userAgent':
46
+ globalOptions.userAgent = options.userAgent;
47
+ break;
48
+ case 'autoMarkDelivery':
49
+ globalOptions.autoMarkDelivery = Boolean(options.autoMarkDelivery);
50
+ break;
51
+ case 'autoMarkRead':
52
+ globalOptions.autoMarkRead = Boolean(options.autoMarkRead);
53
+ break;
54
+ case 'listenTyping':
55
+ globalOptions.listenTyping = Boolean(options.listenTyping);
56
+ break;
57
+ case 'proxy':
58
+ if (typeof options.proxy != "string") {
59
+ delete globalOptions.proxy;
60
+ utils.setProxy();
61
+ } else {
62
+ globalOptions.proxy = options.proxy;
63
+ utils.setProxy(globalOptions.proxy);
64
+ }
65
+ break;
66
+ case 'autoReconnect':
67
+ globalOptions.autoReconnect = Boolean(options.autoReconnect);
68
+ break;
69
+ case 'emitReady':
70
+ globalOptions.emitReady = Boolean(options.emitReady);
71
+ break;
72
+ default:
73
+ log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
74
+ break;
75
+ }
76
+ });
77
77
  }
78
78
 
79
79
  function buildAPI(globalOptions, html, jar) {
80
- var maybeCookie = jar.getCookies("https://www.facebook.com").filter(function (val) {
81
- return val.cookieString().split("=")[0] === "c_user";
82
- });
83
-
84
- if (maybeCookie.length === 0) throw { error: "Error retrieving userID. This can be caused by a lot of things, including getting blocked by Facebook for logging in from an unknown location. Try logging in with a browser to verify." };
85
-
86
- if (html.indexOf("/checkpoint/block/?next") > -1) log.warn("login", "Checkpoint detected. Please log in with a browser to verify.");
87
-
88
- var userID = Object.values(jar._jar.store.idx['facebook.com']['/']).map($=>$.toString()).join(';').match(/i_user=([^;]+);/)?.[1]||maybeCookie[0].cookieString().split("=")[1].toString();
89
- log.info("login", `Logged in as ${userID}`);
90
-
91
- try {
92
- clearInterval(checkVerified);
93
- }
94
- catch (_) { }
95
-
96
- var clientID = (Math.random() * 2147483648 | 0).toString(16);
97
-
98
- let oldFBMQTTMatch = html.match(/irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/);
99
- let mqttEndpoint = null;
100
- let region = null;
101
- let irisSeqID = null;
102
- var noMqttData = null;
103
-
104
- if (oldFBMQTTMatch) {
105
- irisSeqID = oldFBMQTTMatch[1];
106
- mqttEndpoint = oldFBMQTTMatch[2];
107
- region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
108
- log.info("login", `Got this account's message region: ${region}`);
109
- }
110
- else {
111
- let newFBMQTTMatch = html.match(/{"app_id":"219994525426954","endpoint":"(.+?)","iris_seq_id":"(.+?)"}/);
112
- if (newFBMQTTMatch) {
113
- irisSeqID = newFBMQTTMatch[2];
114
- mqttEndpoint = newFBMQTTMatch[1].replace(/\\\//g, "/");
115
- region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
116
- log.info("login", `Got this account's message region: ${region}`);
117
- }
118
- else {
119
- let legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/);
120
- if (legacyFBMQTTMatch) {
121
- mqttEndpoint = legacyFBMQTTMatch[4];
122
- region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
123
- log.warn("login", `Cannot get sequence ID with new RegExp. Fallback to old RegExp (without seqID)...`);
124
- log.info("login", `Got this account's message region: ${region}`);
125
- log.info("login", `[Unused] Polling endpoint: ${legacyFBMQTTMatch[6]}`);
126
- }
127
- else {
128
- log.warn("login", "Cannot get MQTT region & sequence ID.");
129
- noMqttData = html;
130
- }
131
- }
132
- }
133
-
134
- // All data available to api functions
135
- var ctx = {
136
- userID: userID,
137
- jar: jar,
138
- clientID: clientID,
139
- globalOptions: globalOptions,
140
- loggedIn: true,
141
- access_token: 'NONE',
142
- clientMutationId: 0,
143
- mqttClient: undefined,
144
- lastSeqId: irisSeqID,
145
- syncToken: undefined,
146
- mqttEndpoint,
147
- region,
148
- firstListen: true
149
- };
150
-
151
- var api = {
152
- setOptions: setOptions.bind(null, globalOptions),
153
- getAppState: function getAppState() {
154
- return utils.getAppState(jar);
155
- }
156
- };
157
-
158
- if (noMqttData) api["htmlData"] = noMqttData;
159
-
160
- const apiFuncNames = [
161
- 'addExternalModule',
162
- 'addUserToGroup',
163
- 'changeAdminStatus',
164
- 'changeArchivedStatus',
165
- 'changeBio',
166
- 'changeBlockedStatus',
167
- 'changeGroupImage',
168
- 'changeNickname',
169
- 'changeThreadColor',
170
- 'changeThreadEmoji',
171
- 'createNewGroup',
172
- 'createPoll',
173
- 'deleteMessage',
174
- 'deleteThread',
175
- 'forwardAttachment',
176
- 'getCurrentUserID',
177
- 'getEmojiUrl',
178
- 'getFriendsList',
179
- 'getThreadHistory',
180
- 'getThreadInfo',
181
- 'getThreadList',
182
- 'getThreadPictures',
183
- 'getUserID',
184
- 'getUserInfo',
185
- 'handleMessageRequest',
186
- 'listenMqtt',
187
- 'logout',
188
- 'markAsDelivered',
189
- 'markAsRead',
190
- 'markAsReadAll',
191
- 'markAsSeen',
192
- 'muteThread',
193
- 'removeUserFromGroup',
194
- 'resolvePhotoUrl',
195
- 'searchForThread',
196
- 'sendMessage',
197
- 'sendTypingIndicator',
198
- 'setMessageReaction',
199
- 'setTitle',
200
- 'threadColors',
201
- 'unsendMessage',
202
- 'unfriend',
203
- 'shareContact',
204
- 'editMessage',
205
- 'shareLink',
206
- 'shareContact',
207
-
208
- // HTTP
209
- 'httpGet',
210
- 'httpPost',
211
-
212
- // Deprecated features
213
- "getThreadListDeprecated",
214
- 'getThreadHistoryDeprecated',
215
- 'getThreadInfoDeprecated',
216
- ];
217
-
218
- var defaultFuncs = utils.makeDefaults(html, userID, ctx);
80
+ var userID;
81
+ var cookie = jar.getCookies("https://www.facebook.com");
82
+ var maybeUser = cookie.filter(function (val) { return val.cookieString().split("=")[0] === "c_user"; });
83
+ var maybeTiktik = cookie.filter(function (val) { return val.cookieString().split("=")[0] === "i_user"; });
84
+ if (maybeUser.length === 0 && maybeTiktik.length === 0) {
85
+ return logger("Không tìm thấy cookie cho người dùng, vui lòng kiểm tra lại thông tin đăng nhập", 'error');
86
+ } else {
87
+ if (html.indexOf("/checkpoint/block/?next") > -1) {
88
+ return logger("Appstate die, vui lòng thay cái mới!", 'error');
89
+ }
90
+ if (maybeTiktik[0] && maybeTiktik[0].cookieString().includes('i_user')) {
91
+ userID = maybeTiktik[0].cookieString().split("=")[1].toString();
92
+ } else {
93
+ userID = maybeUser[0].cookieString().split("=")[1].toString();
94
+ }
95
+ }
96
+
97
+ try {
98
+ clearInterval(checkVerified);
99
+ } catch (_) { }
100
+
101
+ var clientID = (Math.random() * 2147483648 | 0).toString(16);
102
+
103
+ let oldFBMQTTMatch = html.match(/irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/);
104
+ let mqttEndpoint = null;
105
+ let region = null;
106
+ let irisSeqID = null;
107
+ var noMqttData = null;
108
+
109
+ if (oldFBMQTTMatch) {
110
+ irisSeqID = oldFBMQTTMatch[1];
111
+ mqttEndpoint = oldFBMQTTMatch[2];
112
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
113
+
114
+ } else {
115
+ let newFBMQTTMatch = html.match(/{"app_id":"219994525426954","endpoint":"(.+?)","iris_seq_id":"(.+?)"}/);
116
+ if (newFBMQTTMatch) {
117
+ irisSeqID = newFBMQTTMatch[2];
118
+ mqttEndpoint = newFBMQTTMatch[1].replace(/\\\//g, "/");
119
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
120
+
121
+ } else {
122
+ let legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/);
123
+ if (legacyFBMQTTMatch) {
124
+ mqttEndpoint = legacyFBMQTTMatch[4];
125
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
126
+
127
+ } else {
128
+ log.warn("login", "Cannot get MQTT region & sequence ID.");
129
+ noMqttData = html;
130
+ }
131
+ }
132
+ }
133
+
134
+ // All data available to api functions
135
+ var ctx = {
136
+ userID: userID,
137
+ jar: jar,
138
+ clientID: clientID,
139
+ globalOptions: globalOptions,
140
+ loggedIn: true,
141
+ access_token: 'NONE',
142
+ clientMutationId: 0,
143
+ mqttClient: undefined,
144
+ lastSeqId: irisSeqID,
145
+ syncToken: undefined,
146
+ mqttEndpoint,
147
+ region,
148
+ firstListen: true
149
+ };
150
+
151
+ var api = {
152
+ setOptions: setOptions.bind(null, globalOptions),
153
+ getAppState: function getAppState() {
154
+ return utils.getAppState(jar);
155
+ }
156
+ };
157
+
158
+ if (noMqttData) api["htmlData"] = noMqttData;
159
+
160
+ const apiFuncNames = [
161
+ 'addExternalModule',
162
+ 'addUserToGroup',
163
+ 'changeAdminStatus',
164
+ 'changeArchivedStatus',
165
+ 'changeBio',
166
+ 'changeBlockedStatus',
167
+ 'changeGroupImage',
168
+ 'changeNickname',
169
+ 'changeThreadColor',
170
+ 'changeThreadEmoji',
171
+ 'createNewGroup',
172
+ 'createPoll',
173
+ 'deleteMessage',
174
+ 'deleteThread',
175
+ 'forwardAttachment',
176
+ 'getCurrentUserID',
177
+ 'getEmojiUrl',
178
+ 'getFriendsList',
179
+ 'getThreadHistory',
180
+ 'getThreadInfo',
181
+ 'getThreadList',
182
+ 'getThreadPictures',
183
+ 'getUserID',
184
+ 'getUserInfo',
185
+ 'handleMessageRequest',
186
+ 'listenMqtt',
187
+ 'logout',
188
+ 'markAsDelivered',
189
+ 'markAsRead',
190
+ 'markAsReadAll',
191
+ 'markAsSeen',
192
+ 'muteThread',
193
+ 'removeUserFromGroup',
194
+ 'resolvePhotoUrl',
195
+ 'searchForThread',
196
+ 'sendMessage',
197
+ 'sendTypingIndicator',
198
+ 'setMessageReaction',
199
+ 'setTitle',
200
+ 'threadColors',
201
+ 'unsendMessage',
202
+ 'unfriend',
203
+ 'editMessage',
204
+ 'shareContact',
205
+ 'shareLink',
206
+
207
+ // HTTP
208
+ 'httpGet',
209
+ 'httpPost',
210
+
211
+ // Deprecated features
212
+ "getThreadListDeprecated",
213
+ 'getThreadHistoryDeprecated',
214
+ 'getThreadInfoDeprecated',
215
+ ];
216
+
217
+ var defaultFuncs = utils.makeDefaults(html, userID, ctx);
219
218
  api.postFormData = function(url, body) {
220
219
  return defaultFuncs.postFormData(url, ctx.jar, body);
221
220
  };
222
- // Load all api functions in a loop
223
- apiFuncNames.map(v => api[v] = require('./src/' + v)(defaultFuncs, api, ctx));
224
221
 
225
- return [ctx, defaultFuncs, api];
222
+ // Load all api functions in a loop
223
+ apiFuncNames.map(v => api[v] = require('./src/' + v)(defaultFuncs, api, ctx));
224
+
225
+ return [ctx, defaultFuncs, api];
226
226
  }
227
227
 
228
228
  function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
229
- return function (res) {
230
- var html = res.body;
231
- var $ = cheerio.load(html);
232
- var arr = [];
233
-
234
- // This will be empty, but just to be sure we leave it
235
- $("#login_form input").map((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
236
-
237
- arr = arr.filter(function (v) {
238
- return v.val && v.val.length;
239
- });
240
-
241
- var form = utils.arrToForm(arr);
242
- form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
243
- form.lgndim = Buffer.from("{\"w\":1440,\"h\":900,\"aw\":1440,\"ah\":834,\"c\":24}").toString('base64');
244
- form.email = email;
245
- form.pass = password;
246
- form.default_persistent = '0';
247
- form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
248
- form.locale = 'en_US';
249
- form.timezone = '240';
250
- form.lgnjs = ~~(Date.now() / 1000);
251
-
252
-
253
- // Getting cookies from the HTML page... (kill me now plz)
254
- // we used to get a bunch of cookies in the headers of the response of the
255
- // request, but FB changed and they now send those cookies inside the JS.
256
- // They run the JS which then injects the cookies in the page.
257
- // The "solution" is to parse through the html and find those cookies
258
- // which happen to be conveniently indicated with a _js_ in front of their
259
- // variable name.
260
- //
261
- // ---------- Very Hacky Part Starts -----------------
262
- var willBeCookies = html.split("\"_js_");
263
- willBeCookies.slice(1).map(function (val) {
264
- var cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
265
- jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
266
- });
267
- // ---------- Very Hacky Part Ends -----------------
268
-
269
- log.info("login", "Logging in...");
270
- return utils
271
- .post("https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110", jar, form, loginOptions)
272
- .then(utils.saveCookies(jar))
273
- .then(function (res) {
274
- var headers = res.headers;
275
- if (!headers.location) throw { error: "Wrong username/password." };
276
-
277
- // This means the account has login approvals turned on.
278
- if (headers.location.indexOf('https://www.facebook.com/checkpoint/') > -1) {
279
- log.info("login", "You have login approvals turned on.");
280
- var nextURL = 'https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php';
281
-
282
- return utils
283
- .get(headers.location, jar, null, loginOptions)
284
- .then(utils.saveCookies(jar))
285
- .then(function (res) {
286
- var html = res.body;
287
- // Make the form in advance which will contain the fb_dtsg and nh
288
- var $ = cheerio.load(html);
289
- var arr = [];
290
- $("form input").map((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
291
-
292
- arr = arr.filter(function (v) {
293
- return v.val && v.val.length;
294
- });
295
-
296
- var form = utils.arrToForm(arr);
297
- if (html.indexOf("checkpoint/?next") > -1) {
298
- setTimeout(() => {
299
- checkVerified = setInterval((_form) => { }, 5000, {
300
- fb_dtsg: form.fb_dtsg,
301
- jazoest: form.jazoest,
302
- dpr: 1
303
- });
304
- }, 2500);
305
- throw {
306
- error: 'login-approval',
307
- continue: function submit2FA(code) {
308
- form.approvals_code = code;
309
- form['submit[Continue]'] = $("#checkpointSubmitButton").html(); //'Continue';
310
- var prResolve = null;
311
- var prReject = null;
312
- var rtPromise = new Promise(function (resolve, reject) {
313
- prResolve = resolve;
314
- prReject = reject;
315
- });
316
- if (typeof code == "string") {
317
- utils
318
- .post(nextURL, jar, form, loginOptions)
319
- .then(utils.saveCookies(jar))
320
- .then(function (res) {
321
- var $ = cheerio.load(res.body);
322
- var error = $("#approvals_code").parent().attr("data-xui-error");
323
- if (error) {
324
- throw {
325
- error: 'login-approval',
326
- errordesc: "Invalid 2FA code.",
327
- lerror: error,
328
- continue: submit2FA
329
- };
330
- }
331
- })
332
- .then(function () {
333
- // Use the same form (safe I hope)
334
- delete form.no_fido;
335
- delete form.approvals_code;
336
- form.name_action_selected = 'dont_save'; //'save_device';
337
-
338
- return utils.post(nextURL, jar, form, loginOptions).then(utils.saveCookies(jar));
339
- })
340
- .then(function (res) {
341
- var headers = res.headers;
342
- if (!headers.location && res.body.indexOf('Review Recent Login') > -1) throw { error: "Something went wrong with login approvals." };
343
-
344
- var appState = utils.getAppState(jar);
345
-
346
- if (callback === prCallback) {
347
- callback = function (err, api) {
348
- if (err) return prReject(err);
349
- return prResolve(api);
350
- };
351
- }
352
-
353
- // Simply call loginHelper because all it needs is the jar
354
- // and will then complete the login process
355
- return loginHelper(appState, email, password, loginOptions, callback);
356
- })
357
- .catch(function (err) {
358
- // Check if using Promise instead of callback
359
- if (callback === prCallback) prReject(err);
360
- else callback(err);
361
- });
362
- }
363
- else {
364
- utils
365
- .post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions, null, { "Referer": "https://www.facebook.com/checkpoint/?next" })
366
- .then(utils.saveCookies(jar))
367
- .then(res => {
368
- try {
369
- JSON.parse(res.body.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, ""));
370
- }
371
- catch (ex) {
372
- clearInterval(checkVerified);
373
- log.info("login", "Verified from browser. Logging in...");
374
- if (callback === prCallback) {
375
- callback = function (err, api) {
376
- if (err) return prReject(err);
377
- return prResolve(api);
378
- };
379
- }
380
- return loginHelper(utils.getAppState(jar), email, password, loginOptions, callback);
381
- }
382
- })
383
- .catch(ex => {
384
- log.error("login", ex);
385
- if (callback === prCallback) prReject(ex);
386
- else callback(ex);
387
- });
388
- }
389
- return rtPromise;
390
- }
391
- };
392
- }
393
- else {
394
- if (!loginOptions.forceLogin) throw { error: "Couldn't login. Facebook might have blocked this account. Please login with a browser or enable the option 'forceLogin' and try again." };
395
-
396
- if (html.indexOf("Suspicious Login Attempt") > -1) form['submit[This was me]'] = "This was me";
397
- else form['submit[This Is Okay]'] = "This Is Okay";
398
-
399
- return utils
400
- .post(nextURL, jar, form, loginOptions)
401
- .then(utils.saveCookies(jar))
402
- .then(function () {
403
- // Use the same form (safe I hope)
404
- form.name_action_selected = 'save_device';
405
-
406
- return utils.post(nextURL, jar, form, loginOptions).then(utils.saveCookies(jar));
407
- })
408
- .then(function (res) {
409
- var headers = res.headers;
410
-
411
- if (!headers.location && res.body.indexOf('Review Recent Login') > -1) throw { error: "Something went wrong with review recent login." };
412
-
413
- var appState = utils.getAppState(jar);
414
-
415
- // Simply call loginHelper because all it needs is the jar
416
- // and will then complete the login process
417
- return loginHelper(appState, email, password, loginOptions, callback);
418
- })
419
- .catch(e => callback(e));
420
- }
421
- });
422
- }
423
-
424
- return utils.get('https://www.facebook.com/', jar, null, loginOptions).then(utils.saveCookies(jar));
425
- });
426
- };
229
+ return function (res) {
230
+ var html = res.body;
231
+ var $ = cheerio.load(html);
232
+ var arr = [];
233
+
234
+ // This will be empty, but just to be sure we leave it
235
+ $("#login_form input").map((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
236
+
237
+ arr = arr.filter(function (v) {
238
+ return v.val && v.val.length;
239
+ });
240
+
241
+ var form = utils.arrToForm(arr);
242
+ form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
243
+ form.lgndim = Buffer.from("{\"w\":1440,\"h\":900,\"aw\":1440,\"ah\":834,\"c\":24}").toString('base64');
244
+ form.email = email;
245
+ form.pass = password;
246
+ form.default_persistent = '0';
247
+ form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
248
+ form.locale = 'en_US';
249
+ form.timezone = '240';
250
+ form.lgnjs = ~~(Date.now() / 1000);
251
+
252
+
253
+ // Getting cookies from the HTML page... (kill me now plz)
254
+ // we used to get a bunch of cookies in the headers of the response of the
255
+ // request, but FB changed and they now send those cookies inside the JS.
256
+ // They run the JS which then injects the cookies in the page.
257
+ // The "solution" is to parse through the html and find those cookies
258
+ // which happen to be conveniently indicated with a _js_ in front of their
259
+ // variable name.
260
+ //
261
+ // ---------- Very Hacky Part Starts -----------------
262
+ var willBeCookies = html.split("\"_js_");
263
+ willBeCookies.slice(1).map(function (val) {
264
+ var cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
265
+ jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
266
+ });
267
+ // ---------- Very Hacky Part Ends -----------------
268
+
269
+
270
+ return utils
271
+ .post("https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110", jar, form, loginOptions)
272
+ .then(utils.saveCookies(jar))
273
+ .then(function (res) {
274
+ var headers = res.headers;
275
+ if (!headers.location) throw { error: "Wrong username/password." };
276
+
277
+ // This means the account has login approvals turned on.
278
+ if (headers.location.indexOf('https://www.facebook.com/checkpoint/') > -1) {
279
+ log.info("login", "You have login approvals turned on.");
280
+ var nextURL = 'https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php';
281
+
282
+ return utils
283
+ .get(headers.location, jar, null, loginOptions)
284
+ .then(utils.saveCookies(jar))
285
+ .then(function (res) {
286
+ var html = res.body;
287
+ // Make the form in advance which will contain the fb_dtsg and nh
288
+ var $ = cheerio.load(html);
289
+ var arr = [];
290
+ $("form input").map((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
291
+
292
+ arr = arr.filter(function (v) {
293
+ return v.val && v.val.length;
294
+ });
295
+
296
+ var form = utils.arrToForm(arr);
297
+ if (html.indexOf("checkpoint/?next") > -1) {
298
+ setTimeout(() => {
299
+ checkVerified = setInterval((_form) => { }, 5000, {
300
+ fb_dtsg: form.fb_dtsg,
301
+ jazoest: form.jazoest,
302
+ dpr: 1
303
+ });
304
+ }, 2500);
305
+ throw {
306
+ error: 'login-approval',
307
+ continue: function submit2FA(code) {
308
+ form.approvals_code = code;
309
+ form['submit[Continue]'] = $("#checkpointSubmitButton").html(); //'Continue';
310
+ var prResolve = null;
311
+ var prReject = null;
312
+ var rtPromise = new Promise(function (resolve, reject) {
313
+ prResolve = resolve;
314
+ prReject = reject;
315
+ });
316
+ if (typeof code == "string") {
317
+ utils
318
+ .post(nextURL, jar, form, loginOptions)
319
+ .then(utils.saveCookies(jar))
320
+ .then(function (res) {
321
+ var $ = cheerio.load(res.body);
322
+ var error = $("#approvals_code").parent().attr("data-xui-error");
323
+ if (error) {
324
+ throw {
325
+ error: 'login-approval',
326
+ errordesc: "Invalid 2FA code.",
327
+ lerror: error,
328
+ continue: submit2FA
329
+ };
330
+ }
331
+ })
332
+ .then(function () {
333
+ // Use the same form (safe I hope)
334
+ delete form.no_fido;
335
+ delete form.approvals_code;
336
+ form.name_action_selected = 'dont_save'; //'save_device';
337
+
338
+ return utils.post(nextURL, jar, form, loginOptions).then(utils.saveCookies(jar));
339
+ })
340
+ .then(function (res) {
341
+ var headers = res.headers;
342
+ if (!headers.location && res.body.indexOf('Review Recent Login') > -1) throw { error: "Something went wrong with login approvals." };
343
+
344
+ var appState = utils.getAppState(jar);
345
+
346
+ if (callback === prCallback) {
347
+ callback = function (err, api) {
348
+ if (err) return prReject(err);
349
+ return prResolve(api);
350
+ };
351
+ }
352
+
353
+ // Simply call loginHelper because all it needs is the jar
354
+ // and will then complete the login process
355
+ return loginHelper(appState, email, password, loginOptions, callback);
356
+ })
357
+ .catch(function (err) {
358
+ // Check if using Promise instead of callback
359
+ if (callback === prCallback) prReject(err);
360
+ else callback(err);
361
+ });
362
+ } else {
363
+ utils
364
+ .post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions, null, { "Referer": "https://www.facebook.com/checkpoint/?next" })
365
+ .then(utils.saveCookies(jar))
366
+ .then(res => {
367
+ try {
368
+ JSON.parse(res.body.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, ""));
369
+ } catch (ex) {
370
+ clearInterval(checkVerified);
371
+ log.info("login", "Verified from browser. Logging in...");
372
+ if (callback === prCallback) {
373
+ callback = function (err, api) {
374
+ if (err) return prReject(err);
375
+ return prResolve(api);
376
+ };
377
+ }
378
+ return loginHelper(utils.getAppState(jar), email, password, loginOptions, callback);
379
+ }
380
+ })
381
+ .catch(ex => {
382
+ log.error("login", ex);
383
+ if (callback === prCallback) prReject(ex);
384
+ else callback(ex);
385
+ });
386
+ }
387
+ return rtPromise;
388
+ }
389
+ };
390
+ } else {
391
+ if (!loginOptions.forceLogin) throw { error: "Couldn't login. Facebook might have blocked this account. Please login with a browser or enable the option 'forceLogin' and try again." };
392
+
393
+ if (html.indexOf("Suspicious Login Attempt") > -1) form['submit[This was me]'] = "This was me";
394
+ else form['submit[This Is Okay]'] = "This Is Okay";
395
+
396
+ return utils
397
+ .post(nextURL, jar, form, loginOptions)
398
+ .then(utils.saveCookies(jar))
399
+ .then(function () {
400
+ // Use the same form (safe I hope)
401
+ form.name_action_selected = 'save_device';
402
+
403
+ return utils.post(nextURL, jar, form, loginOptions).then(utils.saveCookies(jar));
404
+ })
405
+ .then(function (res) {
406
+ var headers = res.headers;
407
+
408
+ if (!headers.location && res.body.indexOf('Review Recent Login') > -1) throw { error: "Something went wrong with review recent login." };
409
+
410
+ var appState = utils.getAppState(jar);
411
+
412
+ // Simply call loginHelper because all it needs is the jar
413
+ // and will then complete the login process
414
+ return loginHelper(appState, email, password, loginOptions, callback);
415
+ })
416
+ .catch(e => callback(e));
417
+ }
418
+ });
419
+ }
420
+
421
+ return utils.get('https://www.facebook.com/', jar, null, loginOptions).then(utils.saveCookies(jar));
422
+ });
423
+ };
427
424
  }
428
425
 
429
426
  // Helps the login
430
427
  function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
431
- var mainPromise = null;
432
- var jar = utils.getJar();
433
-
434
- // If we're given an appState we loop through it and save each cookie
435
- // back into the jar.
436
- if (appState) {
437
- appState.map(function (c) {
438
- var str = c.key + "=" + c.value + "; expires=" + c.expires + "; domain=" + c.domain + "; path=" + c.path + ";";
439
- jar.setCookie(str, "http://" + c.domain);
440
- });
441
-
442
- // Load the main page.
443
- mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
444
- }
445
- else {
446
- // Open the main page, then we login with the given credentials and finally
447
- // load the main page again (it'll give us some IDs that we need)
448
- mainPromise = utils
449
- .get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
450
- .then(utils.saveCookies(jar))
451
- .then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
452
- .then(function () {
453
- return utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar));
454
- });
455
- }
456
-
457
- var ctx = null;
458
- var _defaultFuncs = null;
459
- var api = null;
460
-
461
- mainPromise = mainPromise
462
- .then(function (res) {
463
- // Hacky check for the redirection that happens on some ISPs, which doesn't return statusCode 3xx
464
- var reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
465
- var redirect = reg.exec(res.body);
466
- if (redirect && redirect[1]) return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
467
- return res;
468
- })
469
- .then(function (res) {
470
- var html = res.body;
471
- var stuff = buildAPI(globalOptions, html, jar);
472
- ctx = stuff[0];
473
- _defaultFuncs = stuff[1];
474
- api = stuff[2];
475
- return res;
476
- });
477
-
478
- // given a pageID we log in as a page
479
- if (globalOptions.pageID) {
480
- mainPromise = mainPromise
481
- .then(function () {
482
- return utils.get('https://www.facebook.com/' + ctx.globalOptions.pageID + '/messages/?section=messages&subsection=inbox', ctx.jar, null, globalOptions);
483
- })
484
- .then(function (resData) {
485
- var url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
486
- url = url.substring(0, url.length - 1);
487
- return utils.get('https://www.facebook.com' + url, ctx.jar, null, globalOptions);
488
- });
489
- }
490
-
491
- // At the end we call the callback or catch an exception
492
- mainPromise
493
- .then(function () {
494
- log.info("login", 'Done logging in.');
495
- return callback(null, api);
496
- })
497
- .catch(function (e) {
498
- log.error("login", e.error || e);
499
- callback(e);
500
- });
428
+ var mainPromise = null;
429
+ var jar = utils.getJar();
430
+
431
+ // If we're given an appState we loop through it and save each cookie
432
+ // back into the jar.
433
+ if (appState) {
434
+ appState.map(function (c) {
435
+ var str = c.key + "=" + c.value + "; expires=" + c.expires + "; domain=" + c.domain + "; path=" + c.path + ";";
436
+ jar.setCookie(str, "http://" + c.domain);
437
+ });
438
+
439
+ // Load the main page.
440
+ mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
441
+ } else {
442
+ // Open the main page, then we login with the given credentials and finally
443
+ // load the main page again (it'll give us some IDs that we need)
444
+ mainPromise = utils
445
+ .get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
446
+ .then(utils.saveCookies(jar))
447
+ .then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
448
+ .then(function () {
449
+ return utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar));
450
+ });
451
+ }
452
+ let redirect = [1, "https://m.facebook.com/"], bypass_region_err = false, ctx, _defaultFuncs, api;
453
+ function CheckAndFixErr(res) {
454
+ let reg_antierr = /This browser is not supported/gs; // =))))))
455
+ if (reg_antierr.test(res.body)) {
456
+ const Data = JSON.stringify(res.body);
457
+ const Dt_Check = Data.split('2Fhome.php&amp;gfid=')[1];
458
+ if (Dt_Check == undefined) return res
459
+ const fid = Dt_Check.split("\\\\")[0];//fix sau
460
+ if (Dt_Check == undefined || Dt_Check == "") return res
461
+ const final_fid = fid.split(`\\`)[0];
462
+ if (final_fid == undefined || final_fid == '') return res;
463
+ const redirectlink = redirect[1] + "a/preferences.php?basic_site_devices=m_basic&uri=" + encodeURIComponent("https://m.facebook.com/home.php") + "&gfid=" + final_fid;
464
+ bypass_region_err = true;
465
+ return utils.get(redirectlink, jar, null, globalOptions).then(utils.saveCookies(jar));
466
+ }
467
+ else return res
468
+ }
469
+
470
+ function Redirect(res) {
471
+ var reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
472
+ redirect = reg.exec(res.body);
473
+ if (redirect && redirect[1]) return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
474
+ return res;
475
+ }
476
+ mainPromise = mainPromise
477
+ .then(res => Redirect(res))
478
+ .then(res => CheckAndFixErr(res))
479
+
480
+ //fix via login with defaut UA return WWW.facebook.com not m.facebook.com
481
+
482
+ .then(function (res) {
483
+ let Regex_Via = /MPageLoadClientMetrics/gs; //default for normal account, can easily get region, without this u can't get region in some case but u can run normal
484
+ if (!Regex_Via.test(res.body)) {
485
+ //www.facebook.com
486
+ globalOptions.userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1";
487
+ return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
488
+ }
489
+ else return res
490
+ })
491
+ .then(res => Redirect(res))
492
+ .then(res => CheckAndFixErr(res))
493
+ .then(function (res) {
494
+ var html = res.body;
495
+ var stuff = buildAPI(globalOptions, html, jar);
496
+ ctx = stuff[0];
497
+ _defaultFuncs = stuff[1];
498
+ api = stuff[2];
499
+ return res;
500
+ });
501
+
502
+ // given a pageID we log in as a page
503
+ if (globalOptions.pageID) {
504
+ mainPromise = mainPromise
505
+ .then(function () {
506
+ return utils.get('https://www.facebook.com/' + ctx.globalOptions.pageID + '/messages/?section=messages&subsection=inbox', ctx.jar, null, globalOptions);
507
+ })
508
+ .then(function (resData) {
509
+ var url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
510
+ url = url.substring(0, url.length - 1);
511
+ return utils.get('https://www.facebook.com' + url, ctx.jar, null, globalOptions);
512
+ });
513
+ }
514
+
515
+ // At the end we call the callback or catch an exception
516
+ mainPromise
517
+ .then(function () {
518
+
519
+ return callback(null, api);
520
+ })
521
+ .catch(function (e) {
522
+ log.error("login", e.error || e);
523
+ callback(e);
524
+ });
501
525
  }
502
526
 
503
527
  function login(loginData, options, callback) {
504
- if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
505
- callback = options;
506
- options = {};
507
- }
508
-
509
- var globalOptions = {
510
- selfListen: false,
511
- listenEvents: true,
512
- listenTyping: false,
513
- updatePresence: false,
514
- forceLogin: false,
515
- autoMarkDelivery: true,
516
- autoMarkRead: false,
517
- autoReconnect: true,
518
- logRecordSize: defaultLogRecordSize,
519
- online: true,
520
- emitReady: false,
521
- userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18"
522
- };
523
-
524
- setOptions(globalOptions, options);
525
-
526
- var prCallback = null;
527
- if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
528
- var rejectFunc = null;
529
- var resolveFunc = null;
530
- var returnPromise = new Promise(function (resolve, reject) {
531
- resolveFunc = resolve;
532
- rejectFunc = reject;
533
- });
534
- prCallback = function (error, api) {
535
- if (error) return rejectFunc(error);
536
- return resolveFunc(api);
537
- };
538
- callback = prCallback;
539
- }
540
- loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
541
- return returnPromise;
528
+ if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
529
+ callback = options;
530
+ options = {};
531
+ }
532
+ /// Bằng cách nào đó tắt Online sẽ đánh lừa đc Fb
533
+ var globalOptions = {
534
+ selfListen: false,
535
+ listenEvents: true,
536
+ listenTyping: false,
537
+ updatePresence: false,
538
+ forceLogin: false,
539
+ autoMarkDelivery: true,
540
+ autoMarkRead: false,
541
+ autoReconnect: true,
542
+ logRecordSize: defaultLogRecordSize,
543
+ online: false,
544
+ emitReady: false,
545
+ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18"
546
+ };
547
+
548
+ setOptions(globalOptions, options);
549
+
550
+ var prCallback = null;
551
+ if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
552
+ var rejectFunc = null;
553
+ var resolveFunc = null;
554
+ var returnPromise = new Promise(function (resolve, reject) {
555
+ resolveFunc = resolve;
556
+ rejectFunc = reject;
557
+ });
558
+ prCallback = function (error, api) {
559
+ if (error) return rejectFunc(error);
560
+ return resolveFunc(api);
561
+ };
562
+ callback = prCallback;
563
+ }
564
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
565
+ return returnPromise;
542
566
  }
543
567
 
544
568
  module.exports = login;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fca-luxury",
3
- "version": "1.3.0",
4
- "description": "A Facebook chat API that doesn't rely on XMPP. Will NOT be deprecated after April 30th 2015.",
3
+ "version": "2.0.0",
4
+ "description": "FCA bypass vùng.",
5
5
  "scripts": {
6
6
  "test": "mocha",
7
7
  "lint": "eslint **.js",