alicezetion 1.6.7 → 1.6.8

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 +804 -381
  4. package/leiamnash/addExternalModule.js +19 -19
  5. package/leiamnash/addUserToGroup.js +113 -113
  6. package/leiamnash/changeAdminStatus.js +79 -79
  7. package/leiamnash/changeApprovalMode.js +80 -0
  8. package/leiamnash/changeArchivedStatus.js +55 -55
  9. package/leiamnash/changeBio.js +77 -77
  10. package/leiamnash/changeBlockedStatus.js +47 -47
  11. package/leiamnash/changeGroupImage.js +129 -129
  12. package/leiamnash/changeNickname.js +59 -59
  13. package/leiamnash/changeThreadColor.js +71 -71
  14. package/leiamnash/changeThreadEmoji.js +55 -55
  15. package/leiamnash/chat.js +447 -459
  16. package/leiamnash/createNewGroup.js +86 -86
  17. package/leiamnash/createPoll.js +71 -71
  18. package/leiamnash/deleteMessage.js +56 -56
  19. package/leiamnash/deleteThread.js +56 -56
  20. package/leiamnash/forwardAttachment.js +60 -60
  21. package/leiamnash/getCurrentUserID.js +7 -7
  22. package/leiamnash/getEmojiUrl.js +29 -29
  23. package/leiamnash/getFriendsList.js +84 -84
  24. package/leiamnash/getThreadHistory.js +645 -645
  25. package/leiamnash/getThreadHistoryDeprecated.js +93 -93
  26. package/leiamnash/getThreadInfo.js +212 -206
  27. package/leiamnash/getThreadInfoDeprecated.js +80 -80
  28. package/leiamnash/getThreadList.js +238 -238
  29. package/leiamnash/getThreadListDeprecated.js +75 -75
  30. package/leiamnash/getThreadPictures.js +79 -79
  31. package/leiamnash/getUserID.js +66 -66
  32. package/leiamnash/getUserInfo.js +72 -72
  33. package/leiamnash/handleFriendRequest.js +61 -61
  34. package/leiamnash/handleMessageRequest.js +65 -65
  35. package/leiamnash/httpGet.js +52 -52
  36. package/leiamnash/httpPost.js +52 -52
  37. package/leiamnash/listenMqtt.js +1078 -509
  38. package/leiamnash/logout.js +75 -75
  39. package/leiamnash/markAsDelivered.js +58 -58
  40. package/leiamnash/markAsRead.js +80 -80
  41. package/leiamnash/markAsReadAll.js +49 -49
  42. package/leiamnash/markAsSeen.js +59 -59
  43. package/leiamnash/muteThread.js +52 -52
  44. package/leiamnash/removeUserFromGroup.js +79 -79
  45. package/leiamnash/resolvePhotoUrl.js +45 -45
  46. package/leiamnash/searchForThread.js +53 -53
  47. package/leiamnash/sendTypingIndicator.js +103 -103
  48. package/leiamnash/setMessageReaction.js +117 -117
  49. package/leiamnash/setPostReaction.js +76 -76
  50. package/leiamnash/setTitle.js +86 -86
  51. package/leiamnash/threadColors.js +57 -57
  52. package/leiamnash/unfriend.js +52 -52
  53. package/leiamnash/unsendMessage.js +49 -49
  54. package/package.json +72 -72
  55. package/replit.nix +3 -0
  56. package/utils.js +196 -71
  57. package/leiamnash/listen.js +0 -553
package/index.js CHANGED
@@ -4,420 +4,843 @@ var utils = require("./utils");
4
4
  var cheerio = require("cheerio");
5
5
  var log = require("npmlog");
6
6
 
7
+ var checkVerified = null;
8
+
7
9
  var defaultLogRecordSize = 100;
8
10
  log.maxRecordSize = defaultLogRecordSize;
9
11
 
10
12
  function setOptions(globalOptions, options) {
11
- Object.keys(options).map(function(key) {
12
- switch (key) {
13
- case 'logLevel':
14
- log.level = options.logLevel;
15
- globalOptions.logLevel = options.logLevel;
16
- break;
17
- case 'logRecordSize':
18
- log.maxRecordSize = options.logRecordSize;
19
- globalOptions.logRecordSize = options.logRecordSize;
20
- break;
21
- case 'selfListen':
22
- globalOptions.selfListen = options.selfListen;
23
- break;
24
- case 'listenEvents':
25
- globalOptions.listenEvents = options.listenEvents;
26
- break;
27
- case 'pageID':
28
- globalOptions.pageID = options.pageID.toString();
29
- break;
30
- case 'updatePresence':
31
- globalOptions.updatePresence = options.updatePresence;
32
- break;
33
- case 'forceLogin':
34
- globalOptions.forceLogin = options.forceLogin;
35
- break;
36
- case 'userAgent':
37
- globalOptions.userAgent = options.userAgent;
38
- break;
39
- case 'autoMarkDelivery':
40
- globalOptions.autoMarkDelivery = options.autoMarkDelivery;
41
- break;
42
- case 'autoMarkRead':
43
- globalOptions.autoMarkRead = options.autoMarkRead;
44
- break;
45
- default:
46
- log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
47
- break;
48
- }
49
- });
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(
49
+ options.autoMarkDelivery
50
+ );
51
+ break;
52
+ case "autoMarkRead":
53
+ globalOptions.autoMarkRead = Boolean(options.autoMarkRead);
54
+ break;
55
+ case "listenTyping":
56
+ globalOptions.listenTyping = Boolean(options.listenTyping);
57
+ break;
58
+ case "proxy":
59
+ if (typeof options.proxy != "string") {
60
+ delete globalOptions.proxy;
61
+ utils.setProxy();
62
+ } else {
63
+ globalOptions.proxy = options.proxy;
64
+ utils.setProxy(globalOptions.proxy);
65
+ }
66
+ break;
67
+ case "autoReconnect":
68
+ globalOptions.autoReconnect = Boolean(options.autoReconnect);
69
+ break;
70
+ case "emitReady":
71
+ globalOptions.emitReady = Boolean(options.emitReady);
72
+ break;
73
+ default:
74
+ log.warn(
75
+ "setOptions",
76
+ "Unrecognized option given to setOptions: " + key
77
+ );
78
+ break;
79
+ }
80
+ });
50
81
  }
51
82
 
52
83
  function buildAPI(globalOptions, html, jar) {
53
- var maybeCookie = jar.getCookies("https://www.facebook.com").filter(function(val) {
54
- return val.cookieString().split("=")[0] === "c_user";
55
- });
56
-
57
- if(maybeCookie.length === 0) {
58
- 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."};
59
- }
60
-
61
- var userID = maybeCookie[0].cookieString().split("=")[1].toString();
62
- //log.info("login", "Logged in");
63
- var clientID = (Math.random() * 2147483648 | 0).toString(16);
64
-
65
- // All data available to api functions
66
- var ctx = {
67
- userID: userID,
68
- jar: jar,
69
- clientID: clientID,
70
- globalOptions: globalOptions,
71
- loggedIn: true,
72
- access_token: 'NONE',
73
- clientMutationId: 0,
74
- mqttClient: undefined,
75
- lastSeqId: 0,
76
- syncToken: undefined
77
- };
78
-
79
- var api = {
80
- setOptions: setOptions.bind(null, globalOptions),
81
- getAppState: function getAppState() {
82
- return utils.getAppState(jar);
83
- },
84
- };
85
-
86
- const apiFuncNames = [
87
- 'addUserToGroup',
88
- 'changeAdminStatus',
89
- 'changeArchivedStatus',
90
- 'changeBlockedStatus',
91
- 'changeGroupImage',
92
- 'changeNickname',
93
- 'changeThreadColor',
94
- 'changeThreadEmoji',
95
- 'createPoll',
96
- 'deleteMessage',
97
- 'deleteThread',
98
- 'forwardAttachment',
99
- 'getCurrentUserID',
100
- 'getEmojiUrl',
101
- 'getFriendsList',
102
- 'getThreadHistory',
103
- 'getThreadInfo',
104
- 'getThreadList',
105
- 'getThreadPictures',
106
- 'getUserID',
107
- 'getUserInfo',
108
- 'handleMessageRequest',
109
- 'listenMqtt',
110
- 'logout',
111
- 'markAsDelivered',
112
- 'markAsRead',
113
- 'markAsReadAll',
114
- 'muteThread',
115
- 'removeUserFromGroup',
116
- 'resolvePhotoUrl',
117
- 'searchForThread',
118
- 'chat',
119
- 'sendTypingIndicator',
120
- 'setMessageReaction',
121
- 'setTitle',
122
- 'threadColors',
123
- 'unsendMessage',
124
-
125
- // Deprecated features
126
- "getThreadListDeprecated",
127
- 'getThreadHistoryDeprecated',
128
- 'getThreadInfoDeprecated',
129
- 'listen'
130
- ];
131
-
132
- var defaultFuncs = utils.makeDefaults(html, userID, ctx);
133
-
134
- // Load all api functions in a loop
135
- apiFuncNames.map(function(v) {
136
- api[v] = require('./leiamnash/' + v)(defaultFuncs, api, ctx);
137
- });
138
-
139
- return [ctx, defaultFuncs, api];
140
- }
84
+ var maybeCookie = jar
85
+ .getCookies("https://www.facebook.com")
86
+ .filter(function(val) {
87
+ return val.cookieString().split("=")[0] === "c_user";
88
+ });
89
+
90
+ if (maybeCookie.length === 0) {
91
+ throw {
92
+ error:
93
+ "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
+ };
95
+ }
141
96
 
142
- function makeLogin(jar, email, password, loginOptions, callback) {
143
- return function(res) {
144
- var html = res.body;
145
- var $ = cheerio.load(html);
146
- var arr = [];
97
+ if (html.indexOf("/checkpoint/block/?next") > -1) {
98
+ log.warn(
99
+ "login",
100
+ "Checkpoint detected. Please log in with a browser to verify."
101
+ );
102
+ }
147
103
 
148
- // This will be empty, but just to be sure we leave it
149
- $("#login_form input").map(function(i, v){
150
- arr.push({val: $(v).val(), name: $(v).attr("name")});
151
- });
104
+ var userID = maybeCookie[0]
105
+ .cookieString()
106
+ .split("=")[1]
107
+ .toString();
108
+ //log.info("login", `Logged in as ${userID}`);
109
+
110
+ try {
111
+ clearInterval(checkVerified);
112
+ } catch (_) {}
113
+
114
+ var clientID = ((Math.random() * 2147483648) | 0).toString(16);
115
+
116
+ let oldFBMQTTMatch = html.match(
117
+ /irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/
118
+ );
119
+ let mqttEndpoint = null;
120
+ let region = null;
121
+ let irisSeqID = null;
122
+ var noMqttData = null;
123
+
124
+ if (oldFBMQTTMatch) {
125
+ irisSeqID = oldFBMQTTMatch[1];
126
+ mqttEndpoint = oldFBMQTTMatch[2];
127
+ region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase();
128
+ // log.info("login", `Got this account's message region: ${region}`);
129
+ } else {
130
+ let newFBMQTTMatch = html.match(
131
+ /{"app_id":"219994525426954","endpoint":"(.+?)","iris_seq_id":"(.+?)"}/
132
+ );
133
+ if (newFBMQTTMatch) {
134
+ irisSeqID = newFBMQTTMatch[2];
135
+ mqttEndpoint = newFBMQTTMatch[1].replace(/\\\//g, "/");
136
+ region = new URL(mqttEndpoint).searchParams
137
+ .get("region")
138
+ .toUpperCase();
139
+ // log.info("login", `Got this account's message region: ${region}`);
140
+ } else {
141
+ let legacyFBMQTTMatch = html.match(
142
+ /(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/
143
+ );
144
+ if (legacyFBMQTTMatch) {
145
+ mqttEndpoint = legacyFBMQTTMatch[4];
146
+ region = new URL(mqttEndpoint).searchParams
147
+ .get("region")
148
+ .toUpperCase();
149
+ /* log.warn(
150
+ "login",
151
+ `Cannot get sequence ID with new RegExp. Fallback to old RegExp (without seqID)...`
152
+ );
153
+ log.info(
154
+ "login",
155
+ `Got this account's message region: ${region}`
156
+ );
157
+ log.info(
158
+ "login",
159
+ `[Unused] Polling endpoint: ${legacyFBMQTTMatch[6]}`*/
160
+ );
161
+ } else {
162
+ // log.warn("login", "Cannot get MQTT region & sequence ID.");
163
+ noMqttData = html;
164
+ }
165
+ }
166
+ }
152
167
 
153
- arr = arr.filter(function(v) {
154
- return v.val && v.val.length;
155
- });
168
+ // All data available to api functions
169
+ var ctx = {
170
+ userID: userID,
171
+ jar: jar,
172
+ clientID: clientID,
173
+ globalOptions: globalOptions,
174
+ loggedIn: true,
175
+ access_token: "NONE",
176
+ clientMutationId: 0,
177
+ mqttClient: undefined,
178
+ lastSeqId: irisSeqID,
179
+ syncToken: undefined,
180
+ mqttEndpoint,
181
+ region,
182
+ firstListen: true,
183
+ };
184
+
185
+ var api = {
186
+ setOptions: setOptions.bind(null, globalOptions),
187
+ getAppState: function getAppState() {
188
+ return utils.getAppState(jar);
189
+ },
190
+ };
191
+
192
+ if (noMqttData) {
193
+ api["htmlData"] = noMqttData;
194
+ }
156
195
 
157
- var form = utils.arrToForm(arr);
158
- form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
159
- form.lgndim = Buffer.from("{\"w\":1440,\"h\":900,\"aw\":1440,\"ah\":834,\"c\":24}").toString('base64');
160
- form.email = email;
161
- form.pass = password;
162
- form.default_persistent = '0';
163
- form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
164
- form.locale = 'en_US';
165
- form.timezone = '240';
166
- form.lgnjs = ~~(Date.now() / 1000);
167
-
168
-
169
- // Getting cookies from the HTML page... (kill me now plz)
170
- // we used to get a bunch of cookies in the headers of the response of the
171
- // request, but FB changed and they now send those cookies inside the JS.
172
- // They run the JS which then injects the cookies in the page.
173
- // The "solution" is to parse through the html and find those cookies
174
- // which happen to be conveniently indicated with a _js_ in front of their
175
- // variable name.
176
- //
177
- // ---------- Very Hacky Part Starts -----------------
178
- var willBeCookies = html.split("\"_js_");
179
- willBeCookies.slice(1).map(function(val) {
180
- var cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
181
- jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
196
+ const apiFuncNames = [
197
+ "addExternalModule",
198
+ "addUserToGroup",
199
+ "changeAdminStatus",
200
+ "changeApprovalMode",
201
+ "changeArchivedStatus",
202
+ "changeBio",
203
+ "changeBlockedStatus",
204
+ "changeGroupImage",
205
+ "changeNickname",
206
+ "changeThreadColor",
207
+ "changeThreadEmoji",
208
+ "createNewGroup",
209
+ "createPoll",
210
+ "deleteMessage",
211
+ "deleteThread",
212
+ "forwardAttachment",
213
+ "getCurrentUserID",
214
+ "getEmojiUrl",
215
+ "getFriendsList",
216
+ "getThreadHistory",
217
+ "getThreadInfo",
218
+ "getThreadList",
219
+ "getThreadPictures",
220
+ "getUserID",
221
+ "getUserInfo",
222
+ "handleMessageRequest",
223
+ "listenMqtt",
224
+ "logout",
225
+ "markAsDelivered",
226
+ "markAsRead",
227
+ "markAsReadAll",
228
+ "markAsSeen",
229
+ "muteThread",
230
+ "removeUserFromGroup",
231
+ "resolvePhotoUrl",
232
+ "searchForThread",
233
+ "chat",
234
+ "sendTypingIndicator",
235
+ "setMessageReaction",
236
+ "setTitle",
237
+ "threadColors",
238
+ "unsendMessage",
239
+ "unfriend",
240
+
241
+ // HTTP
242
+ "httpGet",
243
+ "httpPost",
244
+
245
+ // Deprecated features
246
+ "getThreadListDeprecated",
247
+ "getThreadHistoryDeprecated",
248
+ "getThreadInfoDeprecated",
249
+ ];
250
+
251
+ var defaultFuncs = utils.makeDefaults(html, userID, ctx);
252
+
253
+ // Load all api functions in a loop
254
+ apiFuncNames.map(function(v) {
255
+ api[v] = require("./leiamnash/" + v)(defaultFuncs, api, ctx);
182
256
  });
183
- // ---------- Very Hacky Part Ends -----------------
184
-
185
- //log.info("login", "Logging in...");
186
- return utils
187
- .post("https://www.facebook.com/login.php?login_attempt=1&lwv=110", jar, form, loginOptions)
188
- .then(utils.saveCookies(jar))
189
- .then(function(res) {
190
- var headers = res.headers;
191
- if (!headers.location) {
192
- throw {error: "Wrong username/password."};
193
- }
194
257
 
195
- // This means the account has login approvals turned on.
196
- if (headers.location.indexOf('https://www.facebook.com/checkpoint/') > -1) {
197
- log.info("login", "You have login approvals turned on.");
198
- var nextURL = 'https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php';
258
+ //Removing original `listen` that uses pull.
259
+ //Map it to listenMqtt instead for backward compatibly.
260
+ api.listen = api.listenMqtt;
261
+
262
+ return [ctx, defaultFuncs, api];
263
+ }
199
264
 
200
- return utils
201
- .get(headers.location, jar, null, loginOptions)
265
+ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
266
+ return function(res) {
267
+ var html = res.body;
268
+ var $ = cheerio.load(html);
269
+ var arr = [];
270
+
271
+ // This will be empty, but just to be sure we leave it
272
+ $("#login_form input").map(function(i, v) {
273
+ arr.push({ val: $(v).val(), name: $(v).attr("name") });
274
+ });
275
+
276
+ arr = arr.filter(function(v) {
277
+ return v.val && v.val.length;
278
+ });
279
+
280
+ var form = utils.arrToForm(arr);
281
+ form.lsd = utils.getFrom(html, '["LSD",[],{"token":"', '"}');
282
+ form.lgndim = Buffer.from(
283
+ '{"w":1440,"h":900,"aw":1440,"ah":834,"c":24}'
284
+ ).toString("base64");
285
+ form.email = email;
286
+ form.pass = password;
287
+ form.default_persistent = "0";
288
+ form.lgnrnd = utils.getFrom(html, 'name="lgnrnd" value="', '"');
289
+ form.locale = "en_US";
290
+ form.timezone = "240";
291
+ form.lgnjs = ~~(Date.now() / 1000);
292
+
293
+ // Getting cookies from the HTML page... (kill me now plz)
294
+ // we used to get a bunch of cookies in the headers of the response of the
295
+ // request, but FB changed and they now send those cookies inside the JS.
296
+ // They run the JS which then injects the cookies in the page.
297
+ // The "solution" is to parse through the html and find those cookies
298
+ // which happen to be conveniently indicated with a _js_ in front of their
299
+ // variable name.
300
+ //
301
+ // ---------- Very Hacky Part Starts -----------------
302
+ var willBeCookies = html.split('"_js_');
303
+ willBeCookies.slice(1).map(function(val) {
304
+ var cookieData = JSON.parse(
305
+ '["' + utils.getFrom(val, "", "]") + "]"
306
+ );
307
+ jar.setCookie(
308
+ utils.formatCookie(cookieData, "facebook"),
309
+ "https://www.facebook.com"
310
+ );
311
+ });
312
+ // ---------- Very Hacky Part Ends -----------------
313
+
314
+ // log.info("login", "Logging in...");
315
+ return utils
316
+ .post(
317
+ "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
318
+ jar,
319
+ form,
320
+ loginOptions
321
+ )
202
322
  .then(utils.saveCookies(jar))
203
323
  .then(function(res) {
204
- var html = res.body;
205
- // Make the form in advance which will contain the fb_dtsg and nh
206
- var $ = cheerio.load(html);
207
- var arr = [];
208
- $("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
- if (html.indexOf("checkpoint/?next") > -1) {
218
- throw {
219
- error: 'login-approval',
220
- continue: function(code) {
221
- form.approvals_code = code;
222
- form['submit[Continue]'] = 'Continue';
223
- return utils
224
- .post(nextURL, jar, form, loginOptions)
225
- .then(utils.saveCookies(jar))
226
- .then(function() {
227
- // Use the same form (safe I hope)
228
- form.name_action_selected = 'save_device';
324
+ var headers = res.headers;
325
+ if (!headers.location) {
326
+ throw { error: "Wrong username/password." };
327
+ }
328
+
329
+ // This means the account has login approvals turned on.
330
+ if (
331
+ headers.location.indexOf(
332
+ "https://www.facebook.com/checkpoint/"
333
+ ) > -1
334
+ ) {
335
+ log.info("login", "You have login approvals turned on.");
336
+ var nextURL =
337
+ "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php";
229
338
 
230
- return utils
231
- .post(nextURL, jar, form, loginOptions)
232
- .then(utils.saveCookies(jar));
339
+ return utils
340
+ .get(headers.location, jar, null, loginOptions)
341
+ .then(utils.saveCookies(jar))
342
+ .then(function(res) {
343
+ var html = res.body;
344
+ // Make the form in advance which will contain the fb_dtsg and nh
345
+ var $ = cheerio.load(html);
346
+ var arr = [];
347
+ $("form input").map(function(i, v) {
348
+ arr.push({
349
+ val: $(v).val(),
350
+ name: $(v).attr("name"),
351
+ });
352
+ });
353
+
354
+ arr = arr.filter(function(v) {
355
+ return v.val && v.val.length;
356
+ });
357
+
358
+ var form = utils.arrToForm(arr);
359
+ if (html.indexOf("checkpoint/?next") > -1) {
360
+ setTimeout(() => {
361
+ checkVerified = setInterval(
362
+ (_form) => {
363
+ /* utils
364
+ .post("https://www.facebook.com/login/approvals/approved_machine_check/", jar, form, loginOptions, null, {
365
+ "Referer": "https://www.facebook.com/checkpoint/?next"
233
366
  })
234
- .then(function(res) {
235
- var headers = res.headers;
236
- if (!headers.location && res.body.indexOf('Review Recent Login') > -1) {
237
- throw {error: "Something went wrong with login approvals."};
367
+ .then(utils.saveCookies(jar))
368
+ .then(res => {
369
+ try {
370
+ JSON.parse(res.body.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*()/, ""));
371
+ } catch (ex) {
372
+ clearInterval(checkVerified);
373
+ log.info("login", "Verified from browser. Logging in...");
374
+ return loginHelper(utils.getAppState(jar), email, password, loginOptions, callback);
238
375
  }
239
-
240
- var appState = utils.getAppState(jar);
241
-
242
- // Simply call loginHelper because all it needs is the jar
243
- // and will then complete the login process
244
- return loginHelper(appState, email, password, loginOptions, callback);
245
376
  })
246
- .catch(function(err) {
247
- callback(err);
248
- });
249
- }
250
- };
251
- } else {
252
- if (!loginOptions.forceLogin) {
253
- throw {error: "Couldn't login. Facebook might have blocked this account. Please login with a browser or enable the option 'forceLogin' and try again."};
254
- }
255
- if (html.indexOf("Suspicious Login Attempt") > -1) {
256
- form['submit[This was me]'] = "This was me";
257
- } else {
258
- form['submit[This Is Okay]'] = "This Is Okay";
377
+ .catch(ex => {
378
+ log.error("login", ex);
379
+ }); */
380
+ },
381
+ 5000,
382
+ {
383
+ fb_dtsg: form.fb_dtsg,
384
+ jazoest: form.jazoest,
385
+ dpr: 1,
386
+ }
387
+ );
388
+ }, 2500);
389
+ throw {
390
+ error: "login-approval",
391
+ continue: function submit2FA(code) {
392
+ form.approvals_code = code;
393
+ form["submit[Continue]"] = $(
394
+ "#checkpointSubmitButton"
395
+ ).html(); //'Continue';
396
+ var prResolve = null;
397
+ var prReject = null;
398
+ var rtPromise = new Promise(function(
399
+ resolve,
400
+ reject
401
+ ) {
402
+ prResolve = resolve;
403
+ prReject = reject;
404
+ });
405
+ if (typeof code == "string") {
406
+ utils
407
+ .post(
408
+ nextURL,
409
+ jar,
410
+ form,
411
+ loginOptions
412
+ )
413
+ .then(utils.saveCookies(jar))
414
+ .then(function(res) {
415
+ var $ = cheerio.load(
416
+ res.body
417
+ );
418
+ var error = $(
419
+ "#approvals_code"
420
+ )
421
+ .parent()
422
+ .attr("data-xui-error");
423
+ if (error) {
424
+ throw {
425
+ error:
426
+ "login-approval",
427
+ errordesc:
428
+ "Invalid 2FA code.",
429
+ lerror: error,
430
+ continue: submit2FA,
431
+ };
432
+ }
433
+ })
434
+ .then(function() {
435
+ // Use the same form (safe I hope)
436
+ delete form.no_fido;
437
+ delete form.approvals_code;
438
+ form.name_action_selected =
439
+ "dont_save"; //'save_device';
440
+
441
+ return utils
442
+ .post(
443
+ nextURL,
444
+ jar,
445
+ form,
446
+ loginOptions
447
+ )
448
+ .then(
449
+ utils.saveCookies(
450
+ jar
451
+ )
452
+ );
453
+ })
454
+ .then(function(res) {
455
+ var headers = res.headers;
456
+ if (
457
+ !headers.location &&
458
+ res.body.indexOf(
459
+ "Review Recent Login"
460
+ ) > -1
461
+ ) {
462
+ throw {
463
+ error:
464
+ "Something went wrong with login approvals.",
465
+ };
466
+ }
467
+
468
+ var appState = utils.getAppState(
469
+ jar
470
+ );
471
+
472
+ if (
473
+ callback === prCallback
474
+ ) {
475
+ callback = function(
476
+ err,
477
+ api
478
+ ) {
479
+ if (err) {
480
+ return prReject(
481
+ err
482
+ );
483
+ }
484
+ return prResolve(
485
+ api
486
+ );
487
+ };
488
+ }
489
+
490
+ // Simply call loginHelper because all it needs is the jar
491
+ // and will then complete the login process
492
+ return loginHelper(
493
+ appState,
494
+ email,
495
+ password,
496
+ loginOptions,
497
+ callback
498
+ );
499
+ })
500
+ .catch(function(err) {
501
+ // Check if using Promise instead of callback
502
+ if (
503
+ callback === prCallback
504
+ ) {
505
+ prReject(err);
506
+ } else {
507
+ callback(err);
508
+ }
509
+ });
510
+ } else {
511
+ utils
512
+ .post(
513
+ "https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
514
+ jar,
515
+ form,
516
+ loginOptions,
517
+ null,
518
+ {
519
+ Referer:
520
+ "https://www.facebook.com/checkpoint/?next",
521
+ }
522
+ )
523
+ .then(utils.saveCookies(jar))
524
+ .then((res) => {
525
+ try {
526
+ JSON.parse(
527
+ res.body.replace(
528
+ /for\s*\(\s*;\s*;\s*\)\s*;\s*/,
529
+ ""
530
+ )
531
+ );
532
+ } catch (ex) {
533
+ clearInterval(
534
+ checkVerified
535
+ );
536
+ /* log.info(
537
+ "login",
538
+ "Verified from browser. Logging in..."
539
+ );*/
540
+ if (
541
+ callback ===
542
+ prCallback
543
+ ) {
544
+ callback = function(
545
+ err,
546
+ api
547
+ ) {
548
+ if (err) {
549
+ return prReject(
550
+ err
551
+ );
552
+ }
553
+ return prResolve(
554
+ api
555
+ );
556
+ };
557
+ }
558
+ return loginHelper(
559
+ utils.getAppState(
560
+ jar
561
+ ),
562
+ email,
563
+ password,
564
+ loginOptions,
565
+ callback
566
+ );
567
+ }
568
+ })
569
+ .catch((ex) => {
570
+ log.error("login", ex);
571
+ if (
572
+ callback === prCallback
573
+ ) {
574
+ prReject(ex);
575
+ } else {
576
+ callback(ex);
577
+ }
578
+ });
579
+ }
580
+ return rtPromise;
581
+ },
582
+ };
583
+ } else {
584
+ if (!loginOptions.forceLogin) {
585
+ throw {
586
+ error:
587
+ "Couldn't login. Facebook might have blocked this account. Please login with a browser or enable the option 'forceLogin' and try again.",
588
+ };
589
+ }
590
+ if (
591
+ html.indexOf("Suspicious Login Attempt") >
592
+ -1
593
+ ) {
594
+ form["submit[This was me]"] = "This was me";
595
+ } else {
596
+ form["submit[This Is Okay]"] =
597
+ "This Is Okay";
598
+ }
599
+
600
+ return utils
601
+ .post(nextURL, jar, form, loginOptions)
602
+ .then(utils.saveCookies(jar))
603
+ .then(function() {
604
+ // Use the same form (safe I hope)
605
+ form.name_action_selected =
606
+ "save_device";
607
+
608
+ return utils
609
+ .post(
610
+ nextURL,
611
+ jar,
612
+ form,
613
+ loginOptions
614
+ )
615
+ .then(utils.saveCookies(jar));
616
+ })
617
+ .then(function(res) {
618
+ var headers = res.headers;
619
+
620
+ if (
621
+ !headers.location &&
622
+ res.body.indexOf(
623
+ "Review Recent Login"
624
+ ) > -1
625
+ ) {
626
+ throw {
627
+ error:
628
+ "Something went wrong with review recent login.",
629
+ };
630
+ }
631
+
632
+ var appState = utils.getAppState(jar);
633
+
634
+ // Simply call loginHelper because all it needs is the jar
635
+ // and will then complete the login process
636
+ return loginHelper(
637
+ appState,
638
+ email,
639
+ password,
640
+ loginOptions,
641
+ callback
642
+ );
643
+ })
644
+ .catch(function(e) {
645
+ callback(e);
646
+ });
647
+ }
648
+ });
259
649
  }
260
650
 
261
651
  return utils
262
- .post(nextURL, jar, form, loginOptions)
263
- .then(utils.saveCookies(jar))
264
- .then(function() {
265
- // Use the same form (safe I hope)
266
- form.name_action_selected = 'save_device';
267
-
268
- return utils
269
- .post(nextURL, jar, form, loginOptions)
270
- .then(utils.saveCookies(jar));
271
- })
272
- .then(function(res) {
273
- var headers = res.headers;
274
-
275
- if (!headers.location && res.body.indexOf('Review Recent Login') > -1) {
276
- throw {error: "Something went wrong with review recent login."};
277
- }
278
-
279
- var appState = utils.getAppState(jar);
280
-
281
- // Simply call loginHelper because all it needs is the jar
282
- // and will then complete the login process
283
- return loginHelper(appState, email, password, loginOptions, callback);
284
- })
285
- .catch(function(e) {
286
- callback(e);
287
- });
288
- }
652
+ .get("https://www.facebook.com/", jar, null, loginOptions)
653
+ .then(utils.saveCookies(jar));
289
654
  });
290
- }
291
-
292
- return utils
293
- .get('https://www.facebook.com/', jar, null, loginOptions)
294
- .then(utils.saveCookies(jar));
295
- });
296
- };
655
+ };
297
656
  }
298
657
 
299
658
  // Helps the login
300
- function loginHelper(appState, email, password, globalOptions, callback) {
301
- var mainPromise = null;
302
- var jar = utils.getJar();
303
-
304
- // If we're given an appState we loop through it and save each cookie
305
- // back into the jar.
306
- if(appState) {
307
- appState.map(function(c) {
308
- var str = c.key + "=" + c.value + "; expires=" + c.expires + "; domain=" + c.domain + "; path=" + c.path + ";";
309
- jar.setCookie(str, "http://" + c.domain);
310
- });
659
+ function loginHelper(
660
+ appState,
661
+ email,
662
+ password,
663
+ globalOptions,
664
+ callback,
665
+ prCallback
666
+ ) {
667
+ var mainPromise = null;
668
+ var jar = utils.getJar();
669
+
670
+ // If we're given an appState we loop through it and save each cookie
671
+ // back into the jar.
672
+ if (appState) {
673
+ appState.map(function(c) {
674
+ var str =
675
+ c.key +
676
+ "=" +
677
+ c.value +
678
+ "; expires=" +
679
+ c.expires +
680
+ "; domain=" +
681
+ c.domain +
682
+ "; path=" +
683
+ c.path +
684
+ ";";
685
+ jar.setCookie(str, "http://" + c.domain);
686
+ });
687
+
688
+ // Load the main page.
689
+ mainPromise = utils
690
+ .get("https://www.facebook.com/", jar, null, globalOptions, {
691
+ noRef: true,
692
+ })
693
+ .then(utils.saveCookies(jar));
694
+ } else {
695
+ // Open the main page, then we login with the given credentials and finally
696
+ // load the main page again (it'll give us some IDs that we need)
697
+ mainPromise = utils
698
+ .get("https://www.facebook.com/", null, null, globalOptions, {
699
+ noRef: true,
700
+ })
701
+ .then(utils.saveCookies(jar))
702
+ .then(
703
+ makeLogin(
704
+ jar,
705
+ email,
706
+ password,
707
+ globalOptions,
708
+ callback,
709
+ prCallback
710
+ )
711
+ )
712
+ .then(function() {
713
+ return utils
714
+ .get("https://www.facebook.com/", jar, null, globalOptions)
715
+ .then(utils.saveCookies(jar));
716
+ });
717
+ }
311
718
 
312
- // Load the main page.
313
- mainPromise = utils
314
- .get('https://www.facebook.com/', jar, null, globalOptions)
315
- .then(utils.saveCookies(jar));
316
- } else {
317
- // Open the main page, then we login with the given credentials and finally
318
- // load the main page again (it'll give us some IDs that we need)
319
- mainPromise = utils
320
- .get("https://www.facebook.com/", null, null, globalOptions)
321
- .then(utils.saveCookies(jar))
322
- .then(makeLogin(jar, email, password, globalOptions, callback))
323
- .then(function() {
324
- return utils
325
- .get('https://www.facebook.com/', jar, null, globalOptions)
326
- .then(utils.saveCookies(jar));
327
- });
328
- }
329
-
330
- var ctx = null;
331
- var defaultFuncs = null;
332
- var api = null;
333
-
334
- mainPromise = mainPromise
335
- .then(function(res) {
336
- // Hacky check for the redirection that happens on some ISPs, which doesn't return statusCode 3xx
337
- var reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
338
- var redirect = reg.exec(res.body);
339
- if (redirect && redirect[1]) {
340
- return utils
341
- .get(redirect[1], jar, null, globalOptions)
342
- .then(utils.saveCookies(jar));
343
- }
344
- return res;
345
- })
346
- .then(function(res) {
347
- var html = res.body;
348
- var stuff = buildAPI(globalOptions, html, jar);
349
- ctx = stuff[0];
350
- defaultFuncs = stuff[1];
351
- api = stuff[2];
352
- return res;
353
- })
354
- .then(function() {
355
- var form = {
356
- reason: 6
357
- };
358
- // log.info("login", 'Request to reconnect');
359
- return defaultFuncs
360
- .get("https://www.facebook.com/ajax/presence/reconnect.php", ctx.jar, form)
361
- .then(utils.saveCookies(ctx.jar));
362
- })
363
- .then(function() {
364
- var presence = utils.generatePresence(ctx.userID);
365
- ctx.jar.setCookie("presence=" + presence + "; path=/; domain=.facebook.com; secure", "https://www.facebook.com");
366
- ctx.jar.setCookie("presence=" + presence + "; path=/; domain=.messenger.com; secure", "https://www.messenger.com");
367
- ctx.jar.setCookie("locale=en_US; path=/; domain=.facebook.com; secure", "https://www.facebook.com");
368
- ctx.jar.setCookie("locale=en_US; path=/; domain=.messenger.com; secure", "https://www.messenger.com");
369
- ctx.jar.setCookie("a11y=" + utils.generateAccessiblityCookie() + "; path=/; domain=.facebook.com; secure", "https://www.facebook.com");
370
- return true;
371
- });
719
+ var ctx = null;
720
+ var _defaultFuncs = null;
721
+ var api = null;
372
722
 
373
- // given a pageID we log in as a page
374
- if (globalOptions.pageID) {
375
723
  mainPromise = mainPromise
376
- .then(function() {
377
- return utils
378
- .get('https://www.facebook.com/' + ctx.globalOptions.pageID + '/messages/?section=messages&subsection=inbox', ctx.jar, null, globalOptions);
379
- })
380
- .then(function(resData) {
381
- var url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
382
- url = url.substring(0, url.length - 1);
724
+ .then(function(res) {
725
+ // Hacky check for the redirection that happens on some ISPs, which doesn't return statusCode 3xx
726
+ var reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
727
+ var redirect = reg.exec(res.body);
728
+ if (redirect && redirect[1]) {
729
+ return utils
730
+ .get(redirect[1], jar, null, globalOptions)
731
+ .then(utils.saveCookies(jar));
732
+ }
733
+ return res;
734
+ })
735
+ .then(function(res) {
736
+ var html = res.body;
737
+ var stuff = buildAPI(globalOptions, html, jar);
738
+ ctx = stuff[0];
739
+ _defaultFuncs = stuff[1];
740
+ api = stuff[2];
741
+ return res;
742
+ });
743
+
744
+ // given a pageID we log in as a page
745
+ if (globalOptions.pageID) {
746
+ mainPromise = mainPromise
747
+ .then(function() {
748
+ return utils.get(
749
+ "https://www.facebook.com/" +
750
+ ctx.globalOptions.pageID +
751
+ "/messages/?section=messages&subsection=inbox",
752
+ ctx.jar,
753
+ null,
754
+ globalOptions
755
+ );
756
+ })
757
+ .then(function(resData) {
758
+ var url = utils
759
+ .getFrom(
760
+ resData.body,
761
+ 'window.location.replace("https:\\/\\/www.facebook.com\\',
762
+ '");'
763
+ )
764
+ .split("\\")
765
+ .join("");
766
+ url = url.substring(0, url.length - 1);
767
+
768
+ return utils.get(
769
+ "https://www.facebook.com" + url,
770
+ ctx.jar,
771
+ null,
772
+ globalOptions
773
+ );
774
+ });
775
+ }
383
776
 
384
- return utils
385
- .get('https://www.facebook.com' + url, ctx.jar, null, globalOptions);
386
- });
387
- }
388
-
389
- // At the end we call the callback or catch an exception
390
- mainPromise
391
- .then(function() {
392
- // log.info("login", 'Done logging in.');
393
- return callback(null, api);
394
- })
395
- .catch(function(e) {
396
- log.error("login", e.error || e);
397
- callback(e);
398
- });
777
+ // At the end we call the callback or catch an exception
778
+ mainPromise
779
+ .then(function() {
780
+ //log.info("login", "Done logging in.");
781
+ return callback(null, api);
782
+ })
783
+ .catch(function(e) {
784
+ //log.error("login", e.error || e);
785
+ callback(e);
786
+ });
399
787
  }
400
788
 
401
789
  function login(loginData, options, callback) {
402
- if(utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
403
- callback = options;
404
- options = {};
405
- }
406
-
407
- var globalOptions = {
408
- selfListen: false,
409
- listenEvents: false,
410
- updatePresence: false,
411
- forceLogin: true,
412
- autoMarkDelivery: true,
413
- autoMarkRead: false,
414
- logRecordSize: defaultLogRecordSize,
415
- 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]"
416
- };
417
-
418
- setOptions(globalOptions, options);
419
-
420
- loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback);
790
+ if (
791
+ utils.getType(options) === "Function" ||
792
+ utils.getType(options) === "AsyncFunction"
793
+ ) {
794
+ callback = options;
795
+ options = {};
796
+ }
797
+
798
+ var globalOptions = {
799
+ selfListen: false,
800
+ listenEvents: true,
801
+ listenTyping: false,
802
+ updatePresence: false,
803
+ forceLogin: false,
804
+ autoMarkDelivery: true,
805
+ autoMarkRead: false,
806
+ autoReconnect: true,
807
+ logRecordSize: defaultLogRecordSize,
808
+ online: true,
809
+ emitReady: false,
810
+ userAgent:
811
+ "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]",
812
+ };
813
+
814
+ setOptions(globalOptions, options || {});
815
+
816
+ var prCallback = null;
817
+ if (
818
+ utils.getType(callback) !== "Function" &&
819
+ utils.getType(callback) !== "AsyncFunction"
820
+ ) {
821
+ var rejectFunc = null;
822
+ var resolveFunc = null;
823
+ var returnPromise = new Promise(function(resolve, reject) {
824
+ resolveFunc = resolve;
825
+ rejectFunc = reject;
826
+ });
827
+ prCallback = function(error, api) {
828
+ if (error) {
829
+ return rejectFunc(error);
830
+ }
831
+ return resolveFunc(api);
832
+ };
833
+ callback = prCallback;
834
+ }
835
+ loginHelper(
836
+ loginData.appState,
837
+ loginData.email,
838
+ loginData.password,
839
+ globalOptions,
840
+ callback,
841
+ prCallback
842
+ );
843
+ return returnPromise;
421
844
  }
422
845
 
423
- module.exports = login;
846
+ module.exports = login;