alicezetion 1.1.6 → 1.1.8

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