fca-rqzax-remake 0.0.1-security → 2.6.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fca-rqzax-remake might be problematic. Click here for more details.

Files changed (70) hide show
  1. package/.cache/replit/__replit_disk_meta.json +1 -0
  2. package/.cache/replit/modules.stamp +0 -0
  3. package/.cache/replit/nix/env.json +1 -0
  4. package/.gitattributes +2 -0
  5. package/.travis.yml +6 -0
  6. package/CHANGELOG.md +2 -0
  7. package/DOCS.md +1738 -0
  8. package/LICENSE-MIT +21 -0
  9. package/README.md +213 -3
  10. package/index.html +1200 -0
  11. package/index.js +542 -0
  12. package/package.json +65 -3
  13. package/replit.nix +9 -0
  14. package/src/addExternalModule.js +16 -0
  15. package/src/addUserToGroup.js +78 -0
  16. package/src/changeAdminStatus.js +79 -0
  17. package/src/changeArchivedStatus.js +41 -0
  18. package/src/changeBio.js +65 -0
  19. package/src/changeBlockedStatus.js +36 -0
  20. package/src/changeGroupImage.js +106 -0
  21. package/src/changeNickname.js +45 -0
  22. package/src/changeThreadColor.js +62 -0
  23. package/src/changeThreadEmoji.js +42 -0
  24. package/src/createNewGroup.js +70 -0
  25. package/src/createPoll.js +60 -0
  26. package/src/deleteMessage.js +45 -0
  27. package/src/deleteThread.js +43 -0
  28. package/src/forwardAttachment.js +48 -0
  29. package/src/getCurrentUserID.js +7 -0
  30. package/src/getEmojiUrl.js +27 -0
  31. package/src/getFriendsList.js +73 -0
  32. package/src/getThreadHistory.js +537 -0
  33. package/src/getThreadHistoryDeprecated.js +71 -0
  34. package/src/getThreadInfo.js +171 -0
  35. package/src/getThreadInfoDeprecated.js +56 -0
  36. package/src/getThreadList.js +213 -0
  37. package/src/getThreadListDeprecated.js +46 -0
  38. package/src/getThreadPictures.js +59 -0
  39. package/src/getUserID.js +62 -0
  40. package/src/getUserInfo.js +66 -0
  41. package/src/handleFriendRequest.js +46 -0
  42. package/src/handleMessageRequest.js +49 -0
  43. package/src/httpGet.js +49 -0
  44. package/src/httpPost.js +48 -0
  45. package/src/listenMqtt.js +663 -0
  46. package/src/logout.js +68 -0
  47. package/src/markAsDelivered.js +48 -0
  48. package/src/markAsRead.js +70 -0
  49. package/src/markAsReadAll.js +43 -0
  50. package/src/markAsSeen.js +51 -0
  51. package/src/muteThread.js +47 -0
  52. package/src/removeUserFromGroup.js +49 -0
  53. package/src/resolvePhotoUrl.js +37 -0
  54. package/src/searchForThread.js +43 -0
  55. package/src/sendMessage.js +321 -0
  56. package/src/sendTypingIndicator.js +80 -0
  57. package/src/setMessageReaction.js +109 -0
  58. package/src/setPostReaction.js +64 -0
  59. package/src/setTitle.js +74 -0
  60. package/src/threadColors.js +41 -0
  61. package/src/unfriend.js +43 -0
  62. package/src/unsendMessage.js +40 -0
  63. package/test/data/shareAttach.js +146 -0
  64. package/test/data/something.mov +0 -0
  65. package/test/data/test.png +0 -0
  66. package/test/data/test.txt +7 -0
  67. package/test/example-config.json +18 -0
  68. package/test/test-page.js +140 -0
  69. package/test/test.js +385 -0
  70. package/utils.js +1234 -0
package/utils.js ADDED
@@ -0,0 +1,1234 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ "use strict";
3
+
4
+ const bluebird = require("bluebird");
5
+ var request = bluebird.promisify(require("request").defaults({ jar: true }));
6
+ var stream = require("stream");
7
+ var log = require("npmlog");
8
+ var querystring = require("querystring");
9
+ var url = require("url");
10
+
11
+ function setProxy(url) {
12
+ if (typeof url == undefined) return request = bluebird.promisify(require("request").defaults({ jar: true }));
13
+ return request = bluebird.promisify(require("request").defaults({ jar: true, proxy: url }));
14
+ }
15
+
16
+ function getHeaders(url, options, ctx, customHeader) {
17
+ var headers = {
18
+ "Content-Type": "application/x-www-form-urlencoded",
19
+ Referer: "https://www.facebook.com/",
20
+ Host: url.replace("https://", "").split("/")[0],
21
+ Origin: "https://www.facebook.com",
22
+ "User-Agent": options.userAgent,
23
+ Connection: "keep-alive"
24
+ };
25
+ if (customHeader) Object.assign(headers, customHeader);
26
+
27
+ if (ctx && ctx.region) headers["X-MSGR-Region"] = ctx.region;
28
+
29
+ return headers;
30
+ }
31
+
32
+ function isReadableStream(obj) {
33
+ return (
34
+ obj instanceof stream.Stream &&
35
+ (getType(obj._read) === "Function" ||
36
+ getType(obj._read) === "AsyncFunction") &&
37
+ getType(obj._readableState) === "Object"
38
+ );
39
+ }
40
+
41
+ function get(url, jar, qs, options, ctx) {
42
+ // I'm still confused about this
43
+ if (getType(qs) === "Object")
44
+ for (var prop in qs)
45
+ if (qs.hasOwnProperty(prop) && getType(qs[prop]) === "Object") qs[prop] = JSON.stringify(qs[prop]);
46
+ var op = {
47
+ headers: getHeaders(url, options, ctx),
48
+ timeout: 60000,
49
+ qs: qs,
50
+ url: url,
51
+ method: "GET",
52
+ jar: jar,
53
+ gzip: true
54
+ };
55
+
56
+ return request(op).then(function(res) {
57
+ return res[0];
58
+ });
59
+ }
60
+
61
+ function post(url, jar, form, options, ctx, customHeader) {
62
+ var op = {
63
+ headers: getHeaders(url, options, ctx, customHeader),
64
+ timeout: 60000,
65
+ url: url,
66
+ method: "POST",
67
+ form: form,
68
+ jar: jar,
69
+ gzip: true
70
+ };
71
+
72
+ return request(op).then(function(res) {
73
+ return res[0];
74
+ });
75
+ }
76
+
77
+ function postFormData(url, jar, form, qs, options, ctx) {
78
+ var headers = getHeaders(url, options, ctx);
79
+ headers["Content-Type"] = "multipart/form-data";
80
+ var op = {
81
+ headers: headers,
82
+ timeout: 60000,
83
+ url: url,
84
+ method: "POST",
85
+ formData: form,
86
+ qs: qs,
87
+ jar: jar,
88
+ gzip: true
89
+ };
90
+
91
+ return request(op).then(function(res) {
92
+ return res[0];
93
+ });
94
+ }
95
+
96
+ function padZeros(val, len) {
97
+ val = String(val);
98
+ len = len || 2;
99
+ while (val.length < len) val = "0" + val;
100
+ return val;
101
+ }
102
+
103
+ function generateThreadingID(clientID) {
104
+ var k = Date.now();
105
+ var l = Math.floor(Math.random() * 4294967295);
106
+ var m = clientID;
107
+ return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
108
+ }
109
+
110
+ function binaryToDecimal(data) {
111
+ var ret = "";
112
+ while (data !== "0") {
113
+ var end = 0;
114
+ var fullName = "";
115
+ var i = 0;
116
+ for (; i < data.length; i++) {
117
+ end = 2 * end + parseInt(data[i], 10);
118
+ if (end >= 10) {
119
+ fullName += "1";
120
+ end -= 10;
121
+ } else fullName += "0";
122
+ }
123
+ ret = end.toString() + ret;
124
+ data = fullName.slice(fullName.indexOf("1"));
125
+ }
126
+ return ret;
127
+ }
128
+
129
+ function generateOfflineThreadingID() {
130
+ var ret = Date.now();
131
+ var value = Math.floor(Math.random() * 4294967295);
132
+ var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
133
+ var msgs = ret.toString(2) + str;
134
+ return binaryToDecimal(msgs);
135
+ }
136
+
137
+ var h;
138
+ var i = {};
139
+ var j = {
140
+ _: "%",
141
+ A: "%2",
142
+ B: "000",
143
+ C: "%7d",
144
+ D: "%7b%22",
145
+ E: "%2c%22",
146
+ F: "%22%3a",
147
+ G: "%2c%22ut%22%3a1",
148
+ H: "%2c%22bls%22%3a",
149
+ I: "%2c%22n%22%3a%22%",
150
+ J: "%22%3a%7b%22i%22%3a0%7d",
151
+ K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
152
+ L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
153
+ M: "%7b%22v%22%3a2%2c%22time%22%3a1",
154
+ N: ".channel%22%2c%22sub%22%3a%5b",
155
+ O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
156
+ P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
157
+ Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
158
+ R: ".channel%22%2c%22sub%22%3a%5b1%5d",
159
+ S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
160
+ T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
161
+ U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
162
+ V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
163
+ W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
164
+ X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
165
+ Y: "%2c%22pt%22%3a0%2c%22vis%22%3a1%2c%22bls%22%3a0%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
166
+ Z: "%2c%22sb%22%3a1%2c%22t%22%3a%5b%5d%2c%22f%22%3anull%2c%22uct%22%3a0%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a"
167
+ };
168
+ (function() {
169
+ var l = [];
170
+ for (var m in j) {
171
+ i[j[m]] = m;
172
+ l.push(j[m]);
173
+ }
174
+ l.reverse();
175
+ h = new RegExp(l.join("|"), "g");
176
+ })();
177
+
178
+ function presenceEncode(str) {
179
+ return encodeURIComponent(str)
180
+ .replace(/([_A-Z])|%../g, function(m, n) {
181
+ return n ? "%" + n.charCodeAt(0).toString(16) : m;
182
+ })
183
+ .toLowerCase()
184
+ .replace(h, function(m) {
185
+ return i[m];
186
+ });
187
+ }
188
+
189
+ // eslint-disable-next-line no-unused-vars
190
+ function presenceDecode(str) {
191
+ return decodeURIComponent(
192
+ str.replace(/[_A-Z]/g, function(m) {
193
+ return j[m];
194
+ })
195
+ );
196
+ }
197
+
198
+ function generatePresence(userID) {
199
+ var time = Date.now();
200
+ return (
201
+ "E" +
202
+ presenceEncode(
203
+ JSON.stringify({
204
+ v: 3,
205
+ time: parseInt(time / 1000, 10),
206
+ user: userID,
207
+ state: {
208
+ ut: 0,
209
+ t2: [],
210
+ lm2: null,
211
+ uct2: time,
212
+ tr: null,
213
+ tw: Math.floor(Math.random() * 4294967295) + 1,
214
+ at: time
215
+ },
216
+ ch: {
217
+ ["p_" + userID]: 0
218
+ }
219
+ })
220
+ )
221
+ );
222
+ }
223
+
224
+ function generateAccessiblityCookie() {
225
+ var time = Date.now();
226
+ return encodeURIComponent(
227
+ JSON.stringify({
228
+ sr: 0,
229
+ "sr-ts": time,
230
+ jk: 0,
231
+ "jk-ts": time,
232
+ kb: 0,
233
+ "kb-ts": time,
234
+ hcm: 0,
235
+ "hcm-ts": time
236
+ })
237
+ );
238
+ }
239
+
240
+ function getGUID() {
241
+ /** @type {number} */
242
+ var sectionLength = Date.now();
243
+ /** @type {string} */
244
+ var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
245
+ /** @type {number} */
246
+ var r = Math.floor((sectionLength + Math.random() * 16) % 16);
247
+ /** @type {number} */
248
+ sectionLength = Math.floor(sectionLength / 16);
249
+ /** @type {string} */
250
+ var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
251
+ return _guid;
252
+ });
253
+ return id;
254
+ }
255
+
256
+ function _formatAttachment(attachment1, attachment2) {
257
+ // TODO: THIS IS REALLY BAD
258
+ // This is an attempt at fixing Facebook's inconsistencies. Sometimes they give us
259
+ // two attachment objects, but sometimes only one. They each contain part of the
260
+ // data that you'd want so we merge them for convenience.
261
+ // Instead of having a bunch of if statements guarding every access to image_data,
262
+ // we set it to empty object and use the fact that it'll return undefined.
263
+ attachment2 = attachment2 || { id: "", image_data: {} };
264
+ attachment1 = attachment1.mercury ? attachment1.mercury : attachment1;
265
+ var blob = attachment1.blob_attachment;
266
+ var type =
267
+ blob && blob.__typename ? blob.__typename : attachment1.attach_type;
268
+ if (!type && attachment1.sticker_attachment) {
269
+ type = "StickerAttachment";
270
+ blob = attachment1.sticker_attachment;
271
+ } else if (!type && attachment1.extensible_attachment) {
272
+ if (
273
+ attachment1.extensible_attachment.story_attachment &&
274
+ attachment1.extensible_attachment.story_attachment.target &&
275
+ attachment1.extensible_attachment.story_attachment.target.__typename &&
276
+ attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
277
+ ) type = "MessageLocation";
278
+ else type = "ExtensibleAttachment";
279
+
280
+ blob = attachment1.extensible_attachment;
281
+ }
282
+ // TODO: Determine whether "sticker", "photo", "file" etc are still used
283
+ // KEEP IN SYNC WITH getThreadHistory
284
+ switch (type) {
285
+ case "sticker":
286
+ return {
287
+ type: "sticker",
288
+ ID: attachment1.metadata.stickerID.toString(),
289
+ url: attachment1.url,
290
+
291
+ packID: attachment1.metadata.packID.toString(),
292
+ spriteUrl: attachment1.metadata.spriteURI,
293
+ spriteUrl2x: attachment1.metadata.spriteURI2x,
294
+ width: attachment1.metadata.width,
295
+ height: attachment1.metadata.height,
296
+
297
+ caption: attachment2.caption,
298
+ description: attachment2.description,
299
+
300
+ frameCount: attachment1.metadata.frameCount,
301
+ frameRate: attachment1.metadata.frameRate,
302
+ framesPerRow: attachment1.metadata.framesPerRow,
303
+ framesPerCol: attachment1.metadata.framesPerCol,
304
+
305
+ stickerID: attachment1.metadata.stickerID.toString(), // @Legacy
306
+ spriteURI: attachment1.metadata.spriteURI, // @Legacy
307
+ spriteURI2x: attachment1.metadata.spriteURI2x // @Legacy
308
+ };
309
+ case "file":
310
+ return {
311
+ type: "file",
312
+ filename: attachment1.name,
313
+ ID: attachment2.id.toString(),
314
+ url: attachment1.url,
315
+
316
+ isMalicious: attachment2.is_malicious,
317
+ contentType: attachment2.mime_type,
318
+
319
+ name: attachment1.name, // @Legacy
320
+ mimeType: attachment2.mime_type, // @Legacy
321
+ fileSize: attachment2.file_size // @Legacy
322
+ };
323
+ case "photo":
324
+ return {
325
+ type: "photo",
326
+ ID: attachment1.metadata.fbid.toString(),
327
+ filename: attachment1.fileName,
328
+ thumbnailUrl: attachment1.thumbnail_url,
329
+
330
+ previewUrl: attachment1.preview_url,
331
+ previewWidth: attachment1.preview_width,
332
+ previewHeight: attachment1.preview_height,
333
+
334
+ largePreviewUrl: attachment1.large_preview_url,
335
+ largePreviewWidth: attachment1.large_preview_width,
336
+ largePreviewHeight: attachment1.large_preview_height,
337
+
338
+ url: attachment1.metadata.url, // @Legacy
339
+ width: attachment1.metadata.dimensions.split(",")[0], // @Legacy
340
+ height: attachment1.metadata.dimensions.split(",")[1], // @Legacy
341
+ name: attachment1.fileName // @Legacy
342
+ };
343
+ case "animated_image":
344
+ return {
345
+ type: "animated_image",
346
+ ID: attachment2.id.toString(),
347
+ filename: attachment2.filename,
348
+
349
+ previewUrl: attachment1.preview_url,
350
+ previewWidth: attachment1.preview_width,
351
+ previewHeight: attachment1.preview_height,
352
+
353
+ url: attachment2.image_data.url,
354
+ width: attachment2.image_data.width,
355
+ height: attachment2.image_data.height,
356
+
357
+ name: attachment1.name, // @Legacy
358
+ facebookUrl: attachment1.url, // @Legacy
359
+ thumbnailUrl: attachment1.thumbnail_url, // @Legacy
360
+ mimeType: attachment2.mime_type, // @Legacy
361
+ rawGifImage: attachment2.image_data.raw_gif_image, // @Legacy
362
+ rawWebpImage: attachment2.image_data.raw_webp_image, // @Legacy
363
+ animatedGifUrl: attachment2.image_data.animated_gif_url, // @Legacy
364
+ animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url, // @Legacy
365
+ animatedWebpUrl: attachment2.image_data.animated_webp_url, // @Legacy
366
+ animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url // @Legacy
367
+ };
368
+ case "share":
369
+ return {
370
+ type: "share",
371
+ ID: attachment1.share.share_id.toString(),
372
+ url: attachment2.href,
373
+
374
+ title: attachment1.share.title,
375
+ description: attachment1.share.description,
376
+ source: attachment1.share.source,
377
+
378
+ image: attachment1.share.media.image,
379
+ width: attachment1.share.media.image_size.width,
380
+ height: attachment1.share.media.image_size.height,
381
+ playable: attachment1.share.media.playable,
382
+ duration: attachment1.share.media.duration,
383
+
384
+ subattachments: attachment1.share.subattachments,
385
+ properties: {},
386
+
387
+ animatedImageSize: attachment1.share.media.animated_image_size, // @Legacy
388
+ facebookUrl: attachment1.share.uri, // @Legacy
389
+ target: attachment1.share.target, // @Legacy
390
+ styleList: attachment1.share.style_list // @Legacy
391
+ };
392
+ case "video":
393
+ return {
394
+ type: "video",
395
+ ID: attachment1.metadata.fbid.toString(),
396
+ filename: attachment1.name,
397
+
398
+ previewUrl: attachment1.preview_url,
399
+ previewWidth: attachment1.preview_width,
400
+ previewHeight: attachment1.preview_height,
401
+
402
+ url: attachment1.url,
403
+ width: attachment1.metadata.dimensions.width,
404
+ height: attachment1.metadata.dimensions.height,
405
+
406
+ duration: attachment1.metadata.duration,
407
+ videoType: "unknown",
408
+
409
+ thumbnailUrl: attachment1.thumbnail_url // @Legacy
410
+ };
411
+ case "error":
412
+ return {
413
+ type: "error",
414
+
415
+ // Save error attachments because we're unsure of their format,
416
+ // and whether there are cases they contain something useful for debugging.
417
+ attachment1: attachment1,
418
+ attachment2: attachment2
419
+ };
420
+ case "MessageImage":
421
+ return {
422
+ type: "photo",
423
+ ID: blob.legacy_attachment_id,
424
+ filename: blob.filename,
425
+ thumbnailUrl: blob.thumbnail.uri,
426
+
427
+ previewUrl: blob.preview.uri,
428
+ previewWidth: blob.preview.width,
429
+ previewHeight: blob.preview.height,
430
+
431
+ largePreviewUrl: blob.large_preview.uri,
432
+ largePreviewWidth: blob.large_preview.width,
433
+ largePreviewHeight: blob.large_preview.height,
434
+
435
+ url: blob.large_preview.uri, // @Legacy
436
+ width: blob.original_dimensions.x, // @Legacy
437
+ height: blob.original_dimensions.y, // @Legacy
438
+ name: blob.filename // @Legacy
439
+ };
440
+ case "MessageAnimatedImage":
441
+ return {
442
+ type: "animated_image",
443
+ ID: blob.legacy_attachment_id,
444
+ filename: blob.filename,
445
+
446
+ previewUrl: blob.preview_image.uri,
447
+ previewWidth: blob.preview_image.width,
448
+ previewHeight: blob.preview_image.height,
449
+
450
+ url: blob.animated_image.uri,
451
+ width: blob.animated_image.width,
452
+ height: blob.animated_image.height,
453
+
454
+ thumbnailUrl: blob.preview_image.uri, // @Legacy
455
+ name: blob.filename, // @Legacy
456
+ facebookUrl: blob.animated_image.uri, // @Legacy
457
+ rawGifImage: blob.animated_image.uri, // @Legacy
458
+ animatedGifUrl: blob.animated_image.uri, // @Legacy
459
+ animatedGifPreviewUrl: blob.preview_image.uri, // @Legacy
460
+ animatedWebpUrl: blob.animated_image.uri, // @Legacy
461
+ animatedWebpPreviewUrl: blob.preview_image.uri // @Legacy
462
+ };
463
+ case "MessageVideo":
464
+ return {
465
+ type: "video",
466
+ filename: blob.filename,
467
+ ID: blob.legacy_attachment_id,
468
+
469
+ previewUrl: blob.large_image.uri,
470
+ previewWidth: blob.large_image.width,
471
+ previewHeight: blob.large_image.height,
472
+
473
+ url: blob.playable_url,
474
+ width: blob.original_dimensions.x,
475
+ height: blob.original_dimensions.y,
476
+
477
+ duration: blob.playable_duration_in_ms,
478
+ videoType: blob.video_type.toLowerCase(),
479
+
480
+ thumbnailUrl: blob.large_image.uri // @Legacy
481
+ };
482
+ case "MessageAudio":
483
+ return {
484
+ type: "audio",
485
+ filename: blob.filename,
486
+ ID: blob.url_shimhash,
487
+
488
+ audioType: blob.audio_type,
489
+ duration: blob.playable_duration_in_ms,
490
+ url: blob.playable_url,
491
+
492
+ isVoiceMail: blob.is_voicemail
493
+ };
494
+ case "StickerAttachment":
495
+ return {
496
+ type: "sticker",
497
+ ID: blob.id,
498
+ url: blob.url,
499
+
500
+ packID: blob.pack ? blob.pack.id : null,
501
+ spriteUrl: blob.sprite_image,
502
+ spriteUrl2x: blob.sprite_image_2x,
503
+ width: blob.width,
504
+ height: blob.height,
505
+
506
+ caption: blob.label,
507
+ description: blob.label,
508
+
509
+ frameCount: blob.frame_count,
510
+ frameRate: blob.frame_rate,
511
+ framesPerRow: blob.frames_per_row,
512
+ framesPerCol: blob.frames_per_column,
513
+
514
+ stickerID: blob.id, // @Legacy
515
+ spriteURI: blob.sprite_image, // @Legacy
516
+ spriteURI2x: blob.sprite_image_2x // @Legacy
517
+ };
518
+ case "MessageLocation":
519
+ var urlAttach = blob.story_attachment.url;
520
+ var mediaAttach = blob.story_attachment.media;
521
+
522
+ var u = querystring.parse(url.parse(urlAttach).query).u;
523
+ var where1 = querystring.parse(url.parse(u).query).where1;
524
+ var address = where1.split(", ");
525
+
526
+ var latitude;
527
+ var longitude;
528
+
529
+ try {
530
+ latitude = Number.parseFloat(address[0]);
531
+ longitude = Number.parseFloat(address[1]);
532
+ } catch (err) {
533
+ /* empty */
534
+ }
535
+
536
+ var imageUrl;
537
+ var width;
538
+ var height;
539
+
540
+ if (mediaAttach && mediaAttach.image) {
541
+ imageUrl = mediaAttach.image.uri;
542
+ width = mediaAttach.image.width;
543
+ height = mediaAttach.image.height;
544
+ }
545
+
546
+ return {
547
+ type: "location",
548
+ ID: blob.legacy_attachment_id,
549
+ latitude: latitude,
550
+ longitude: longitude,
551
+ image: imageUrl,
552
+ width: width,
553
+ height: height,
554
+ url: u || urlAttach,
555
+ address: where1,
556
+
557
+ facebookUrl: blob.story_attachment.url, // @Legacy
558
+ target: blob.story_attachment.target, // @Legacy
559
+ styleList: blob.story_attachment.style_list // @Legacy
560
+ };
561
+ case "ExtensibleAttachment":
562
+ return {
563
+ type: "share",
564
+ ID: blob.legacy_attachment_id,
565
+ url: blob.story_attachment.url,
566
+
567
+ title: blob.story_attachment.title_with_entities.text,
568
+ description: blob.story_attachment.description &&
569
+ blob.story_attachment.description.text,
570
+ source: blob.story_attachment.source ? blob.story_attachment.source.text : null,
571
+
572
+ image: blob.story_attachment.media &&
573
+ blob.story_attachment.media.image &&
574
+ blob.story_attachment.media.image.uri,
575
+ width: blob.story_attachment.media &&
576
+ blob.story_attachment.media.image &&
577
+ blob.story_attachment.media.image.width,
578
+ height: blob.story_attachment.media &&
579
+ blob.story_attachment.media.image &&
580
+ blob.story_attachment.media.image.height,
581
+ playable: blob.story_attachment.media &&
582
+ blob.story_attachment.media.is_playable,
583
+ duration: blob.story_attachment.media &&
584
+ blob.story_attachment.media.playable_duration_in_ms,
585
+ playableUrl: blob.story_attachment.media == null ? null : blob.story_attachment.media.playable_url,
586
+
587
+ subattachments: blob.story_attachment.subattachments,
588
+ properties: blob.story_attachment.properties.reduce(function(obj, cur) {
589
+ obj[cur.key] = cur.value.text;
590
+ return obj;
591
+ }, {}),
592
+
593
+ facebookUrl: blob.story_attachment.url, // @Legacy
594
+ target: blob.story_attachment.target, // @Legacy
595
+ styleList: blob.story_attachment.style_list // @Legacy
596
+ };
597
+ case "MessageFile":
598
+ return {
599
+ type: "file",
600
+ filename: blob.filename,
601
+ ID: blob.message_file_fbid,
602
+
603
+ url: blob.url,
604
+ isMalicious: blob.is_malicious,
605
+ contentType: blob.content_type,
606
+
607
+ name: blob.filename,
608
+ mimeType: "",
609
+ fileSize: -1
610
+ };
611
+ default:
612
+ throw new Error(
613
+ "unrecognized attach_file of type " +
614
+ type +
615
+ "`" +
616
+ JSON.stringify(attachment1, null, 4) +
617
+ " attachment2: " +
618
+ JSON.stringify(attachment2, null, 4) +
619
+ "`"
620
+ );
621
+ }
622
+ }
623
+
624
+ function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
625
+ attachmentMap = shareMap || attachmentMap;
626
+ return attachments ?
627
+ attachments.map(function(val, i) {
628
+ if (!attachmentMap ||
629
+ !attachmentIds ||
630
+ !attachmentMap[attachmentIds[i]]
631
+ ) {
632
+ return _formatAttachment(val);
633
+ }
634
+ return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
635
+ }) : [];
636
+ }
637
+
638
+ function formatDeltaMessage(m) {
639
+ var md = m.delta.messageMetadata;
640
+ var mdata =
641
+ m.delta.data === undefined ? [] :
642
+ m.delta.data.prng === undefined ? [] :
643
+ JSON.parse(m.delta.data.prng);
644
+ var m_id = mdata.map(u => u.i);
645
+ var m_offset = mdata.map(u => u.o);
646
+ var m_length = mdata.map(u => u.l);
647
+ var mentions = {};
648
+ var body = m.delta.body || "";
649
+ var args = body == "" ? [] : body.trim().split(/\s+/);
650
+ for (var i = 0; i < m_id.length; i++) mentions[m_id[i]] = m.delta.body.substring(m_offset[i], m_offset[i] + m_length[i]);
651
+
652
+ return {
653
+ type: "message",
654
+ senderID: formatID(md.actorFbId.toString()),
655
+ threadID: formatID((md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()),
656
+ messageID: md.messageId,
657
+ args: args,
658
+ body: body,
659
+ attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
660
+ mentions: mentions,
661
+ timestamp: md.timestamp,
662
+ isGroup: !!md.threadKey.threadFbId,
663
+ participantIDs: m.delta.participants || []
664
+ };
665
+ }
666
+
667
+ function formatID(id) {
668
+ if (id != undefined && id != null) return id.replace(/(fb)?id[:.]/, "");
669
+ else return id;
670
+ }
671
+
672
+ function formatMessage(m) {
673
+ var originalMessage = m.message ? m.message : m;
674
+ var obj = {
675
+ type: "message",
676
+ senderName: originalMessage.sender_name,
677
+ senderID: formatID(originalMessage.sender_fbid.toString()),
678
+ participantNames: originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(" ")[0]],
679
+ participantIDs: originalMessage.group_thread_info ?
680
+ originalMessage.group_thread_info.participant_ids.map(function(v) {
681
+ return formatID(v.toString());
682
+ }) : [formatID(originalMessage.sender_fbid)],
683
+ body: originalMessage.body || "",
684
+ threadID: formatID((originalMessage.thread_fbid || originalMessage.other_user_fbid).toString()),
685
+ threadName: originalMessage.group_thread_info ? originalMessage.group_thread_info.name : originalMessage.sender_name,
686
+ location: originalMessage.coordinates ? originalMessage.coordinates : null,
687
+ messageID: originalMessage.mid ? originalMessage.mid.toString() : originalMessage.message_id,
688
+ attachments: formatAttachment(
689
+ originalMessage.attachments,
690
+ originalMessage.attachmentIds,
691
+ originalMessage.attachment_map,
692
+ originalMessage.share_map
693
+ ),
694
+ timestamp: originalMessage.timestamp,
695
+ timestampAbsolute: originalMessage.timestamp_absolute,
696
+ timestampRelative: originalMessage.timestamp_relative,
697
+ timestampDatetime: originalMessage.timestamp_datetime,
698
+ tags: originalMessage.tags,
699
+ reactions: originalMessage.reactions ? originalMessage.reactions : [],
700
+ isUnread: originalMessage.is_unread
701
+ };
702
+
703
+ if (m.type === "pages_messaging") obj.pageID = m.realtime_viewer_fbid.toString();
704
+ obj.isGroup = obj.participantIDs.length > 2;
705
+
706
+ return obj;
707
+ }
708
+
709
+ function formatEvent(m) {
710
+ var originalMessage = m.message ? m.message : m;
711
+ var logMessageType = originalMessage.log_message_type;
712
+ var logMessageData;
713
+ if (logMessageType === "log:generic-admin-text") {
714
+ logMessageData = originalMessage.log_message_data.untypedData;
715
+ logMessageType = getAdminTextMessageType(originalMessage.log_message_data.message_type);
716
+ } else logMessageData = originalMessage.log_message_data;
717
+
718
+ return Object.assign(formatMessage(originalMessage), {
719
+ type: "event",
720
+ logMessageType: logMessageType,
721
+ logMessageData: logMessageData,
722
+ logMessageBody: originalMessage.log_message_body
723
+ });
724
+ }
725
+
726
+ function formatHistoryMessage(m) {
727
+ switch (m.action_type) {
728
+ case "ma-type:log-message":
729
+ return formatEvent(m);
730
+ default:
731
+ return formatMessage(m);
732
+ }
733
+ }
734
+
735
+ // Get a more readable message type for AdminTextMessages
736
+ function getAdminTextMessageType(m) {
737
+ switch (m.type) {
738
+ case "change_thread_theme":
739
+ return "log:thread-color";
740
+ case "change_thread_icon":
741
+ return "log:thread-icon";
742
+ case "change_thread_nickname":
743
+ return "log:user-nickname";
744
+ case "change_thread_admins":
745
+ return "log:thread-admins";
746
+ case "group_poll":
747
+ return "log:thread-poll";
748
+ case "change_thread_approval_mode":
749
+ return "log:thread-approval-mode";
750
+ case "messenger_call_log":
751
+ case "participant_joined_group_call":
752
+ return "log:thread-call";
753
+ }
754
+ }
755
+
756
+ function formatDeltaEvent(m) {
757
+ var logMessageType;
758
+ var logMessageData;
759
+
760
+ // log:thread-color => {theme_color}
761
+ // log:user-nickname => {participant_id, nickname}
762
+ // log:thread-icon => {thread_icon}
763
+ // log:thread-name => {name}
764
+ // log:subscribe => {addedParticipants - [Array]}
765
+ // log:unsubscribe => {leftParticipantFbId}
766
+
767
+ switch (m.class) {
768
+ case "AdminTextMessage":
769
+ logMessageType = getAdminTextMessageType(m);
770
+ logMessageData = m.untypedData;
771
+ break;
772
+ case "ThreadName":
773
+ logMessageType = "log:thread-name";
774
+ logMessageData = { name: m.name };
775
+ break;
776
+ case "ParticipantsAddedToGroupThread":
777
+ logMessageType = "log:subscribe";
778
+ logMessageData = { addedParticipants: m.addedParticipants };
779
+ break;
780
+ case "ParticipantLeftGroupThread":
781
+ logMessageType = "log:unsubscribe";
782
+ logMessageData = { leftParticipantFbId: m.leftParticipantFbId };
783
+ break;
784
+ }
785
+
786
+ return {
787
+ type: "event",
788
+ threadID: formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),
789
+ logMessageType: logMessageType,
790
+ logMessageData: logMessageData,
791
+ logMessageBody: m.messageMetadata.adminText,
792
+ author: m.messageMetadata.actorFbId,
793
+ participantIDs: m.participants || []
794
+ };
795
+ }
796
+
797
+ function formatTyp(event) {
798
+ return {
799
+ isTyping: !!event.st,
800
+ from: event.from.toString(),
801
+ threadID: formatID((event.to || event.thread_fbid || event.from).toString()),
802
+ // When receiving typ indication from mobile, `from_mobile` isn't set.
803
+ // If it is, we just use that value.
804
+ fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
805
+ userID: (event.realtime_viewer_fbid || event.from).toString(),
806
+ type: "typ"
807
+ };
808
+ }
809
+
810
+ function formatDeltaReadReceipt(delta) {
811
+ // otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
812
+ // In a group chat actorFbId is used for the reader and threadFbId for the thread.
813
+ return {
814
+ reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
815
+ time: delta.actionTimestampMs,
816
+ threadID: formatID((delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()),
817
+ type: "read_receipt"
818
+ };
819
+ }
820
+
821
+ function formatReadReceipt(event) {
822
+ return {
823
+ reader: event.reader.toString(),
824
+ time: event.time,
825
+ threadID: formatID((event.thread_fbid || event.reader).toString()),
826
+ type: "read_receipt"
827
+ };
828
+ }
829
+
830
+ function formatRead(event) {
831
+ return {
832
+ threadID: formatID(((event.chat_ids && event.chat_ids[0]) || (event.thread_fbids && event.thread_fbids[0])).toString()),
833
+ time: event.timestamp,
834
+ type: "read"
835
+ };
836
+ }
837
+
838
+ function getFrom(str, startToken, endToken) {
839
+ var start = str.indexOf(startToken) + startToken.length;
840
+ if (start < startToken.length) return "";
841
+
842
+ var lastHalf = str.substring(start);
843
+ var end = lastHalf.indexOf(endToken);
844
+ if (end === -1) throw Error("Could not find endTime `" + endToken + "` in the given string.");
845
+ return lastHalf.substring(0, end);
846
+ }
847
+
848
+ function makeParsable(html) {
849
+ let withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
850
+
851
+ // (What the fuck FB, why windows style newlines?)
852
+ // So sometimes FB will send us base multiple objects in the same response.
853
+ // They're all valid JSON, one after the other, at the top level. We detect
854
+ // that and make it parse-able by JSON.parse.
855
+ // Ben - July 15th 2017
856
+ //
857
+ // It turns out that Facebook may insert random number of spaces before
858
+ // next object begins (issue #616)
859
+ // rav_kr - 2018-03-19
860
+ let maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
861
+ if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
862
+
863
+ return "[" + maybeMultipleObjects.join("},{") + "]";
864
+ }
865
+
866
+ function arrToForm(form) {
867
+ return arrayToObject(form,
868
+ function(v) {
869
+ return v.name;
870
+ },
871
+ function(v) {
872
+ return v.val;
873
+ }
874
+ );
875
+ }
876
+
877
+ function arrayToObject(arr, getKey, getValue) {
878
+ return arr.reduce(function(acc, val) {
879
+ acc[getKey(val)] = getValue(val);
880
+ return acc;
881
+ }, {});
882
+ }
883
+
884
+ function getSignatureID() {
885
+ return Math.floor(Math.random() * 2147483648).toString(16);
886
+ }
887
+
888
+ function generateTimestampRelative() {
889
+ var d = new Date();
890
+ return d.getHours() + ":" + padZeros(d.getMinutes());
891
+ }
892
+
893
+ function makeDefaults(html, userID, ctx) {
894
+ var reqCounter = 1;
895
+ var fb_dtsg = getFrom(html, 'name="fb_dtsg" value="', '"');
896
+
897
+ // @Hack Ok we've done hacky things, this is definitely on top 5.
898
+ // We totally assume the object is flat and try parsing until a }.
899
+ // If it works though it's cool because we get a bunch of extra data things.
900
+ //
901
+ // Update: we don't need this. Leaving it in in case we ever do.
902
+ // Ben - July 15th 2017
903
+
904
+ // var siteData = getFrom(html, "[\"SiteData\",[],", "},");
905
+ // try {
906
+ // siteData = JSON.parse(siteData + "}");
907
+ // } catch(e) {
908
+ // log.warn("makeDefaults", "Couldn't parse SiteData. Won't have access to some variables.");
909
+ // siteData = {};
910
+ // }
911
+
912
+ var ttstamp = "2";
913
+ for (var i = 0; i < fb_dtsg.length; i++) ttstamp += fb_dtsg.charCodeAt(i);
914
+ var revision = getFrom(html, 'revision":', ",");
915
+
916
+ function mergeWithDefaults(obj) {
917
+ // @TODO This is missing a key called __dyn.
918
+ // After some investigation it seems like __dyn is some sort of set that FB
919
+ // calls BitMap. It seems like certain responses have a "define" key in the
920
+ // res.jsmods arrays. I think the code iterates over those and calls `set`
921
+ // on the bitmap for each of those keys. Then it calls
922
+ // bitmap.toCompressedString() which returns what __dyn is.
923
+ //
924
+ // So far the API has been working without this.
925
+ //
926
+ // Ben - July 15th 2017
927
+ var newObj = {
928
+ __user: userID,
929
+ __req: (reqCounter++).toString(36),
930
+ __rev: revision,
931
+ __a: 1,
932
+ // __af: siteData.features,
933
+ fb_dtsg: ctx.fb_dtsg ? ctx.fb_dtsg : fb_dtsg,
934
+ jazoest: ctx.ttstamp ? ctx.ttstamp : ttstamp
935
+ // __spin_r: siteData.__spin_r,
936
+ // __spin_b: siteData.__spin_b,
937
+ // __spin_t: siteData.__spin_t,
938
+ };
939
+
940
+ // @TODO this is probably not needed.
941
+ // Ben - July 15th 2017
942
+ // if (siteData.be_key) {
943
+ // newObj[siteData.be_key] = siteData.be_mode;
944
+ // }
945
+ // if (siteData.pkg_cohort_key) {
946
+ // newObj[siteData.pkg_cohort_key] = siteData.pkg_cohort;
947
+ // }
948
+
949
+ if (!obj) return newObj;
950
+ for (var prop in obj)
951
+ if (obj.hasOwnProperty(prop))
952
+ if (!newObj[prop]) newObj[prop] = obj[prop];
953
+ return newObj;
954
+ }
955
+
956
+ function postWithDefaults(url, jar, form, ctxx) {
957
+ return post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx);
958
+ }
959
+
960
+ function getWithDefaults(url, jar, qs, ctxx) {
961
+ return get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
962
+ }
963
+
964
+ function postFormDataWithDefault(url, jar, form, qs, ctxx) {
965
+ return postFormData(url, jar, mergeWithDefaults(form), mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
966
+ }
967
+
968
+ return {
969
+ get: getWithDefaults,
970
+ post: postWithDefaults,
971
+ postFormData: postFormDataWithDefault
972
+ };
973
+ }
974
+
975
+ function parseAndCheckLogin(ctx, defaultFuncs, retryCount) {
976
+ if (retryCount == undefined) retryCount = 0;
977
+ return function(data) {
978
+ return bluebird.try(function() {
979
+ log.verbose("parseAndCheckLogin", data.body);
980
+ if (data.statusCode >= 500 && data.statusCode < 600) {
981
+ if (retryCount >= 5) {
982
+ throw {
983
+ error: "Request retry failed. Check the `res` and `statusCode` property on this error.",
984
+ statusCode: data.statusCode,
985
+ res: data.body
986
+ };
987
+ }
988
+ retryCount++;
989
+ var retryTime = Math.floor(Math.random() * 5000);
990
+ log.warn("parseAndCheckLogin", "Got status code " + data.statusCode + " - " + retryCount + ". attempt to retry in " + retryTime + " milliseconds...");
991
+ var url = data.request.uri.protocol + "//" + data.request.uri.hostname + data.request.uri.pathname;
992
+ if (data.request.headers["Content-Type"].split(";")[0] === "multipart/form-data") {
993
+ return bluebird.delay(retryTime).then(() => defaultFuncs.postFormData(url, ctx.jar, data.request.formData, {}))
994
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
995
+ } else {
996
+ return bluebird.delay(retryTime).then(() => defaultFuncs.post(url, ctx.jar, data.request.formData))
997
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
998
+ }
999
+ }
1000
+ if (data.statusCode !== 200) throw new Error("parseAndCheckLogin got status code: " + data.statusCode + ". Bailing out of trying to parse response.");
1001
+
1002
+ var res = null;
1003
+ try {
1004
+ res = JSON.parse(makeParsable(data.body));
1005
+ } catch (e) {
1006
+ throw {
1007
+ error: "JSON.parse error. Check the `detail` property on this error.",
1008
+ detail: e,
1009
+ res: data.body
1010
+ };
1011
+ }
1012
+
1013
+ // In some cases the response contains only a redirect URL which should be followed
1014
+ if (res.redirect && data.request.method === "GET") return defaultFuncs.get(res.redirect, ctx.jar).then(parseAndCheckLogin(ctx, defaultFuncs));
1015
+
1016
+ // TODO: handle multiple cookies?
1017
+ if (res.jsmods && res.jsmods.require && Array.isArray(res.jsmods.require[0]) && res.jsmods.require[0][0] === "Cookie") {
1018
+ res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace("_js_", "");
1019
+ var cookie = formatCookie(res.jsmods.require[0][3], "facebook");
1020
+ var cookie2 = formatCookie(res.jsmods.require[0][3], "messenger");
1021
+ ctx.jar.setCookie(cookie, "https://www.facebook.com");
1022
+ ctx.jar.setCookie(cookie2, "https://www.messenger.com");
1023
+ }
1024
+
1025
+ // On every request we check if we got a DTSG and we mutate the context so that we use the latest
1026
+ // one for the next requests.
1027
+ if (res.jsmods && Array.isArray(res.jsmods.require)) {
1028
+ var arr = res.jsmods.require;
1029
+ for (var i in arr) {
1030
+ if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
1031
+ ctx.fb_dtsg = arr[i][3][0];
1032
+
1033
+ // Update ttstamp since that depends on fb_dtsg
1034
+ ctx.ttstamp = "2";
1035
+ for (var j = 0; j < ctx.fb_dtsg.length; j++) ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ if (res.error === 1357001) throw { error: "Chưa Đăng Nhập Được - Appstate Đã Bị Lỗi" };
1041
+ return res;
1042
+ });
1043
+ };
1044
+ }
1045
+
1046
+ function saveCookies(jar) {
1047
+ return function(res) {
1048
+ var cookies = res.headers["set-cookie"] || [];
1049
+ cookies.forEach(function(c) {
1050
+ if (c.indexOf(".facebook.com") > -1) jar.setCookie(c, "https://www.facebook.com");
1051
+ var c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
1052
+ jar.setCookie(c2, "https://www.messenger.com");
1053
+ });
1054
+ return res;
1055
+ };
1056
+ }
1057
+
1058
+ var NUM_TO_MONTH = [
1059
+ "Jan",
1060
+ "Feb",
1061
+ "Mar",
1062
+ "Apr",
1063
+ "May",
1064
+ "Jun",
1065
+ "Jul",
1066
+ "Aug",
1067
+ "Sep",
1068
+ "Oct",
1069
+ "Nov",
1070
+ "Dec"
1071
+ ];
1072
+ var NUM_TO_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1073
+
1074
+ function formatDate(date) {
1075
+ var d = date.getUTCDate();
1076
+ d = d >= 10 ? d : "0" + d;
1077
+ var h = date.getUTCHours();
1078
+ h = h >= 10 ? h : "0" + h;
1079
+ var m = date.getUTCMinutes();
1080
+ m = m >= 10 ? m : "0" + m;
1081
+ var s = date.getUTCSeconds();
1082
+ s = s >= 10 ? s : "0" + s;
1083
+ return (NUM_TO_DAY[date.getUTCDay()] + ", " + d + " " + NUM_TO_MONTH[date.getUTCMonth()] + " " + date.getUTCFullYear() + " " + h + ":" + m + ":" + s + " GMT");
1084
+ }
1085
+
1086
+ function formatCookie(arr, url) {
1087
+ return arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com";
1088
+ }
1089
+
1090
+ function formatThread(data) {
1091
+ return {
1092
+ threadID: formatID(data.thread_fbid.toString()),
1093
+ participants: data.participants.map(formatID),
1094
+ participantIDs: data.participants.map(formatID),
1095
+ name: data.name,
1096
+ nicknames: data.custom_nickname,
1097
+ snippet: data.snippet,
1098
+ snippetAttachments: data.snippet_attachments,
1099
+ snippetSender: formatID((data.snippet_sender || "").toString()),
1100
+ unreadCount: data.unread_count,
1101
+ messageCount: data.message_count,
1102
+ imageSrc: data.image_src,
1103
+ timestamp: data.timestamp,
1104
+ serverTimestamp: data.server_timestamp, // what is this?
1105
+ muteUntil: data.mute_until,
1106
+ isCanonicalUser: data.is_canonical_user,
1107
+ isCanonical: data.is_canonical,
1108
+ isSubscribed: data.is_subscribed,
1109
+ folder: data.folder,
1110
+ isArchived: data.is_archived,
1111
+ recipientsLoadable: data.recipients_loadable,
1112
+ hasEmailParticipant: data.has_email_participant,
1113
+ readOnly: data.read_only,
1114
+ canReply: data.can_reply,
1115
+ cannotReplyReason: data.cannot_reply_reason,
1116
+ lastMessageTimestamp: data.last_message_timestamp,
1117
+ lastReadTimestamp: data.last_read_timestamp,
1118
+ lastMessageType: data.last_message_type,
1119
+ emoji: data.custom_like_icon,
1120
+ color: data.custom_color,
1121
+ adminIDs: data.admin_ids,
1122
+ threadType: data.thread_type
1123
+ };
1124
+ }
1125
+
1126
+ function getType(obj) {
1127
+ return Object.prototype.toString.call(obj).slice(8, -1);
1128
+ }
1129
+
1130
+ function formatProxyPresence(presence, userID) {
1131
+ if (presence.lat === undefined || presence.p === undefined) return null;
1132
+ return {
1133
+ type: "presence",
1134
+ timestamp: presence.lat * 1000,
1135
+ userID: userID || '',
1136
+ statuses: presence.p
1137
+ };
1138
+ }
1139
+
1140
+ function formatPresence(presence, userID) {
1141
+ return {
1142
+ type: "presence",
1143
+ timestamp: presence.la * 1000,
1144
+ userID: userID || '',
1145
+ statuses: presence.a
1146
+ };
1147
+ }
1148
+
1149
+ function decodeClientPayload(payload) {
1150
+ /*
1151
+ Special function which Client using to "encode" clients JSON payload
1152
+ */
1153
+ function Utf8ArrayToStr(array) {
1154
+ var out, i, len, c;
1155
+ var char2, char3;
1156
+ out = "";
1157
+ len = array.length;
1158
+ i = 0;
1159
+ while (i < len) {
1160
+ c = array[i++];
1161
+ switch (c >> 4) {
1162
+ case 0:
1163
+ case 1:
1164
+ case 2:
1165
+ case 3:
1166
+ case 4:
1167
+ case 5:
1168
+ case 6:
1169
+ case 7:
1170
+ out += String.fromCharCode(c);
1171
+ break;
1172
+ case 12:
1173
+ case 13:
1174
+ char2 = array[i++];
1175
+ out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
1176
+ break;
1177
+ case 14:
1178
+ char2 = array[i++];
1179
+ char3 = array[i++];
1180
+ out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
1181
+ break;
1182
+ }
1183
+ }
1184
+ return out;
1185
+ }
1186
+ return JSON.parse(Utf8ArrayToStr(payload));
1187
+ }
1188
+
1189
+ function getAppState(jar) {
1190
+ return jar
1191
+ .getCookies("https://www.facebook.com")
1192
+ .concat(jar.getCookies("https://facebook.com"))
1193
+ .concat(jar.getCookies("https://www.messenger.com"));
1194
+ }
1195
+ module.exports = {
1196
+ isReadableStream:isReadableStream,
1197
+ get:get,
1198
+ post:post,
1199
+ postFormData:postFormData,
1200
+ generateThreadingID:generateThreadingID,
1201
+ generateOfflineThreadingID:generateOfflineThreadingID,
1202
+ getGUID:getGUID,
1203
+ getFrom:getFrom,
1204
+ makeParsable:makeParsable,
1205
+ arrToForm:arrToForm,
1206
+ getSignatureID:getSignatureID,
1207
+ getJar: request.jar,
1208
+ generateTimestampRelative:generateTimestampRelative,
1209
+ makeDefaults:makeDefaults,
1210
+ parseAndCheckLogin:parseAndCheckLogin,
1211
+ saveCookies,
1212
+ getType,
1213
+ _formatAttachment,
1214
+ formatHistoryMessage,
1215
+ formatID,
1216
+ formatMessage,
1217
+ formatDeltaEvent,
1218
+ formatDeltaMessage,
1219
+ formatProxyPresence,
1220
+ formatPresence,
1221
+ formatTyp,
1222
+ formatDeltaReadReceipt,
1223
+ formatCookie,
1224
+ formatThread,
1225
+ formatReadReceipt,
1226
+ formatRead,
1227
+ generatePresence,
1228
+ generateAccessiblityCookie,
1229
+ formatDate,
1230
+ decodeClientPayload,
1231
+ getAppState,
1232
+ getAdminTextMessageType,
1233
+ setProxy
1234
+ };