alicezetion 1.7.8 → 1.8.0

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