alicezetion 1.0.2 → 1.0.4

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