alicezetion 1.5.7 → 1.5.9

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