jake-chan 0.0.1-security → 5.3.9

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

Potentially problematic release.


This version of jake-chan might be problematic. Click here for more details.

Files changed (88) 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/.config/configstore/update-notifier-npm.json +4 -0
  5. package/CHANGELOG.md +2 -0
  6. package/DOCS.md +1738 -0
  7. package/LICENSE-MIT +21 -0
  8. package/README.md +106 -3
  9. package/StateCrypt.js +28 -0
  10. package/broadcast.js +35 -0
  11. package/index.js +701 -0
  12. package/languages/languages.json +182 -0
  13. package/lib/getInfoNew.js +34 -0
  14. package/lib/getToken.js +44 -0
  15. package/logger.js +15 -0
  16. package/package.json +82 -4
  17. package/src/K2IMG.js +8 -0
  18. package/src/ReportV1.js +55 -0
  19. package/src/Screenshot.js +77 -0
  20. package/src/T2S.js +8 -0
  21. package/src/addExternalModule.js +16 -0
  22. package/src/addUserToGroup.js +79 -0
  23. package/src/changeAdminStatus.js +79 -0
  24. package/src/changeArchivedStatus.js +41 -0
  25. package/src/changeAvatar.js +127 -0
  26. package/src/changeAvt.js +85 -0
  27. package/src/changeBio.js +65 -0
  28. package/src/changeBlockedStatus.js +36 -0
  29. package/src/changeGroupImage.js +106 -0
  30. package/src/changeNickname.js +45 -0
  31. package/src/changeThreadColor.js +62 -0
  32. package/src/changeThreadEmoji.js +42 -0
  33. package/src/createNewGroup.js +70 -0
  34. package/src/createPoll.js +60 -0
  35. package/src/deleteMessage.js +45 -0
  36. package/src/deleteThread.js +43 -0
  37. package/src/desktop.ini +2 -0
  38. package/src/forwardAttachment.js +48 -0
  39. package/src/getAccessToken.js +32 -0
  40. package/src/getCurrentUserID.js +7 -0
  41. package/src/getEmojiUrl.js +27 -0
  42. package/src/getFriendsList.js +73 -0
  43. package/src/getMessage.js +80 -0
  44. package/src/getThreadHistory.js +537 -0
  45. package/src/getThreadHistoryDeprecated.js +93 -0
  46. package/src/getThreadInfo.js +346 -0
  47. package/src/getThreadInfoDeprecated.js +80 -0
  48. package/src/getThreadList.js +213 -0
  49. package/src/getThreadListDeprecated.js +75 -0
  50. package/src/getThreadMain.js +219 -0
  51. package/src/getThreadPictures.js +59 -0
  52. package/src/getUID.js +59 -0
  53. package/src/getUserID.js +62 -0
  54. package/src/getUserInfo.js +129 -0
  55. package/src/getUserInfoMain.js +65 -0
  56. package/src/getUserInfoV2.js +35 -0
  57. package/src/getUserInfoV3.js +63 -0
  58. package/src/getUserInfoV4.js +55 -0
  59. package/src/getUserInfoV5.js +61 -0
  60. package/src/handleFriendRequest.js +46 -0
  61. package/src/handleMessageRequest.js +49 -0
  62. package/src/httpGet.js +49 -0
  63. package/src/httpPost.js +48 -0
  64. package/src/httpPostFormData.js +41 -0
  65. package/src/listenMqtt.js +725 -0
  66. package/src/logout.js +68 -0
  67. package/src/markAsDelivered.js +48 -0
  68. package/src/markAsRead.js +70 -0
  69. package/src/markAsReadAll.js +43 -0
  70. package/src/markAsSeen.js +51 -0
  71. package/src/muteThread.js +47 -0
  72. package/src/removeUserFromGroup.js +49 -0
  73. package/src/resolvePhotoUrl.js +37 -0
  74. package/src/searchForThread.js +43 -0
  75. package/src/sendMessage.js +334 -0
  76. package/src/sendTypingIndicator.js +80 -0
  77. package/src/setMessageReaction.js +109 -0
  78. package/src/setPostReaction.js +102 -0
  79. package/src/setTitle.js +74 -0
  80. package/src/threadColors.js +39 -0
  81. package/src/unfriend.js +43 -0
  82. package/src/unsendMessage.js +40 -0
  83. package/test/data/shareAttach.js +146 -0
  84. package/test/data/test.txt +7 -0
  85. package/test/example-config.json +18 -0
  86. package/test/test-page.js +140 -0
  87. package/test/test.js +387 -0
  88. package/utils.js +2476 -0
package/utils.js ADDED
@@ -0,0 +1,2476 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+
3
+ "use strict";
4
+
5
+
6
+
7
+ const bluebird = require("bluebird");
8
+
9
+ var request = bluebird.promisify(require("request").defaults({ jar: true }));
10
+
11
+ var stream = require("stream");
12
+
13
+ var log = require("npmlog");
14
+
15
+ var querystring = require("querystring");
16
+
17
+ var url = require("url");
18
+
19
+
20
+
21
+ function setProxy(url) {
22
+
23
+ if (typeof url == undefined) return request = bluebird.promisify(require("request").defaults({ jar: true }));
24
+
25
+ return request = bluebird.promisify(require("request").defaults({ jar: true, proxy: url }));
26
+
27
+ }
28
+
29
+
30
+
31
+ function getHeaders(url, options, ctx, customHeader) {
32
+
33
+ var headers = {
34
+
35
+ "Content-Type": "application/x-www-form-urlencoded",
36
+
37
+ Referer: "https://www.facebook.com/",
38
+
39
+ Host: url.replace("https://", "").split("/")[0],
40
+
41
+ Origin: "https://www.facebook.com",
42
+
43
+ "User-Agent": options.userAgent,
44
+
45
+ Connection: "keep-alive",
46
+
47
+ 'sec-fetch-site': 'same-origin'
48
+
49
+ };
50
+
51
+ if (customHeader) Object.assign(headers, customHeader);
52
+
53
+
54
+
55
+ if (ctx && ctx.region) headers["X-MSGR-Region"] = ctx.region;
56
+
57
+
58
+
59
+ return headers;
60
+
61
+ }
62
+
63
+
64
+
65
+ function isReadableStream(obj) {
66
+
67
+ return (
68
+
69
+ obj instanceof stream.Stream &&
70
+
71
+ (getType(obj._read) === "Function" ||
72
+
73
+ getType(obj._read) === "AsyncFunction") &&
74
+
75
+ getType(obj._readableState) === "Object"
76
+
77
+ );
78
+
79
+ }
80
+
81
+
82
+
83
+ function get(url, jar, qs, options, ctx) {
84
+
85
+ // I'm still confused about this
86
+
87
+ if (getType(qs) === "Object")
88
+
89
+ for (var prop in qs)
90
+
91
+ if (qs.hasOwnProperty(prop) && getType(qs[prop]) === "Object") qs[prop] = JSON.stringify(qs[prop]);
92
+
93
+ var op = {
94
+
95
+ headers: getHeaders(url, options, ctx),
96
+
97
+ timeout: 60000,
98
+
99
+ qs: qs,
100
+
101
+ url: url,
102
+
103
+ method: "GET",
104
+
105
+ jar: jar,
106
+
107
+ gzip: true
108
+
109
+ };
110
+
111
+
112
+
113
+ return request(op).then(function(res) {
114
+
115
+ return res[0];
116
+
117
+ });
118
+
119
+ }
120
+
121
+
122
+
123
+ function post(url, jar, form, options, ctx, customHeader) {
124
+
125
+ let headers = getHeaders(url, options);
126
+
127
+ headers['sec-fetch-site'] = 'same-origin';
128
+
129
+ var op = {
130
+
131
+ headers: headers,
132
+
133
+ timeout: 60000,
134
+
135
+ url: url,
136
+
137
+ method: "POST",
138
+
139
+ form: form,
140
+
141
+ jar: jar,
142
+
143
+ gzip: true
144
+
145
+ };
146
+
147
+
148
+
149
+ return request(op).then(function(res) {
150
+
151
+ return res[0];
152
+
153
+ });
154
+
155
+ }
156
+
157
+
158
+
159
+ function postFormData(url, jar, form, qs, options, ctx) {
160
+
161
+ var headers = getHeaders(url, options, ctx);
162
+
163
+ headers["Content-Type"] = "multipart/form-data";
164
+
165
+ var op = {
166
+
167
+ headers: headers,
168
+
169
+ timeout: 60000,
170
+
171
+ url: url,
172
+
173
+ method: "POST",
174
+
175
+ formData: form,
176
+
177
+ qs: qs,
178
+
179
+ jar: jar,
180
+
181
+ gzip: true
182
+
183
+ };
184
+
185
+
186
+
187
+ return request(op).then(function(res) {
188
+
189
+ return res[0];
190
+
191
+ });
192
+
193
+ }
194
+
195
+
196
+
197
+ function padZeros(val, len) {
198
+
199
+ val = String(val);
200
+
201
+ len = len || 2;
202
+
203
+ while (val.length < len) val = "0" + val;
204
+
205
+ return val;
206
+
207
+ }
208
+
209
+
210
+
211
+ function generateThreadingID(clientID) {
212
+
213
+ var k = Date.now();
214
+
215
+ var l = Math.floor(Math.random() * 4294967295);
216
+
217
+ var m = clientID;
218
+
219
+ return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
220
+
221
+ }
222
+
223
+
224
+
225
+ function binaryToDecimal(data) {
226
+
227
+ var ret = "";
228
+
229
+ while (data !== "0") {
230
+
231
+ var end = 0;
232
+
233
+ var fullName = "";
234
+
235
+ var i = 0;
236
+
237
+ for (; i < data.length; i++) {
238
+
239
+ end = 2 * end + parseInt(data[i], 10);
240
+
241
+ if (end >= 10) {
242
+
243
+ fullName += "1";
244
+
245
+ end -= 10;
246
+
247
+ } else fullName += "0";
248
+
249
+ }
250
+
251
+ ret = end.toString() + ret;
252
+
253
+ data = fullName.slice(fullName.indexOf("1"));
254
+
255
+ }
256
+
257
+ return ret;
258
+
259
+ }
260
+
261
+
262
+
263
+ function generateOfflineThreadingID() {
264
+
265
+ var ret = Date.now();
266
+
267
+ var value = Math.floor(Math.random() * 4294967295);
268
+
269
+ var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
270
+
271
+ var msgs = ret.toString(2) + str;
272
+
273
+ return binaryToDecimal(msgs);
274
+
275
+ }
276
+
277
+
278
+
279
+ var h;
280
+
281
+ var i = {};
282
+
283
+ var j = {
284
+
285
+ _: "%",
286
+
287
+ A: "%2",
288
+
289
+ B: "000",
290
+
291
+ C: "%7d",
292
+
293
+ D: "%7b%22",
294
+
295
+ E: "%2c%22",
296
+
297
+ F: "%22%3a",
298
+
299
+ G: "%2c%22ut%22%3a1",
300
+
301
+ H: "%2c%22bls%22%3a",
302
+
303
+ I: "%2c%22n%22%3a%22%",
304
+
305
+ J: "%22%3a%7b%22i%22%3a0%7d",
306
+
307
+ K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
308
+
309
+ L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
310
+
311
+ M: "%7b%22v%22%3a2%2c%22time%22%3a1",
312
+
313
+ N: ".channel%22%2c%22sub%22%3a%5b",
314
+
315
+ O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
316
+
317
+ P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
318
+
319
+ Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
320
+
321
+ R: ".channel%22%2c%22sub%22%3a%5b1%5d",
322
+
323
+ S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
324
+
325
+ T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
326
+
327
+ U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
328
+
329
+ V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
330
+
331
+ W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
332
+
333
+ X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
334
+
335
+ 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",
336
+
337
+ 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"
338
+
339
+ };
340
+
341
+ (function() {
342
+
343
+ var l = [];
344
+
345
+ for (var m in j) {
346
+
347
+ i[j[m]] = m;
348
+
349
+ l.push(j[m]);
350
+
351
+ }
352
+
353
+ l.reverse();
354
+
355
+ h = new RegExp(l.join("|"), "g");
356
+
357
+ })();
358
+
359
+
360
+
361
+ function presenceEncode(str) {
362
+
363
+ return encodeURIComponent(str)
364
+
365
+ .replace(/([_A-Z])|%../g, function(m, n) {
366
+
367
+ return n ? "%" + n.charCodeAt(0).toString(16) : m;
368
+
369
+ })
370
+
371
+ .toLowerCase()
372
+
373
+ .replace(h, function(m) {
374
+
375
+ return i[m];
376
+
377
+ });
378
+
379
+ }
380
+
381
+
382
+
383
+ // eslint-disable-next-line no-unused-vars
384
+
385
+ function presenceDecode(str) {
386
+
387
+ return decodeURIComponent(
388
+
389
+ str.replace(/[_A-Z]/g, function(m) {
390
+
391
+ return j[m];
392
+
393
+ })
394
+
395
+ );
396
+
397
+ }
398
+
399
+
400
+
401
+ function generatePresence(userID) {
402
+
403
+ var time = Date.now();
404
+
405
+ return (
406
+
407
+ "E" +
408
+
409
+ presenceEncode(
410
+
411
+ JSON.stringify({
412
+
413
+ v: 3,
414
+
415
+ time: parseInt(time / 1000, 10),
416
+
417
+ user: userID,
418
+
419
+ state: {
420
+
421
+ ut: 0,
422
+
423
+ t2: [],
424
+
425
+ lm2: null,
426
+
427
+ uct2: time,
428
+
429
+ tr: null,
430
+
431
+ tw: Math.floor(Math.random() * 4294967295) + 1,
432
+
433
+ at: time
434
+
435
+ },
436
+
437
+ ch: {
438
+
439
+ ["p_" + userID]: 0
440
+
441
+ }
442
+
443
+ })
444
+
445
+ )
446
+
447
+ );
448
+
449
+ }
450
+
451
+
452
+
453
+ function generateAccessiblityCookie() {
454
+
455
+ var time = Date.now();
456
+
457
+ return encodeURIComponent(
458
+
459
+ JSON.stringify({
460
+
461
+ sr: 0,
462
+
463
+ "sr-ts": time,
464
+
465
+ jk: 0,
466
+
467
+ "jk-ts": time,
468
+
469
+ kb: 0,
470
+
471
+ "kb-ts": time,
472
+
473
+ hcm: 0,
474
+
475
+ "hcm-ts": time
476
+
477
+ })
478
+
479
+ );
480
+
481
+ }
482
+
483
+
484
+
485
+ function getGUID() {
486
+
487
+ /** @type {number} */
488
+
489
+ var sectionLength = Date.now();
490
+
491
+ /** @type {string} */
492
+
493
+ var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
494
+
495
+ /** @type {number} */
496
+
497
+ var r = Math.floor((sectionLength + Math.random() * 16) % 16);
498
+
499
+ /** @type {number} */
500
+
501
+ sectionLength = Math.floor(sectionLength / 16);
502
+
503
+ /** @type {string} */
504
+
505
+ var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
506
+
507
+ return _guid;
508
+
509
+ });
510
+
511
+ return id;
512
+
513
+ }
514
+
515
+
516
+
517
+ function _formatAttachment(attachment1, attachment2) {
518
+
519
+ // TODO: THIS IS REALLY BAD
520
+
521
+ // This is an attempt at fixing Facebook's inconsistencies. Sometimes they give us
522
+
523
+ // two attachment objects, but sometimes only one. They each contain part of the
524
+
525
+ // data that you'd want so we merge them for convenience.
526
+
527
+ // Instead of having a bunch of if statements guarding every access to image_data,
528
+
529
+ // we set it to empty object and use the fact that it'll return undefined.
530
+
531
+ attachment2 = attachment2 || { id: "", image_data: {} };
532
+
533
+ attachment1 = attachment1.mercury ? attachment1.mercury : attachment1;
534
+
535
+ var blob = attachment1.blob_attachment;
536
+
537
+ var type =
538
+
539
+ blob && blob.__typename ? blob.__typename : attachment1.attach_type;
540
+
541
+ if (!type && attachment1.sticker_attachment) {
542
+
543
+ type = "StickerAttachment";
544
+
545
+ blob = attachment1.sticker_attachment;
546
+
547
+ } else if (!type && attachment1.extensible_attachment) {
548
+
549
+ if (
550
+
551
+ attachment1.extensible_attachment.story_attachment &&
552
+
553
+ attachment1.extensible_attachment.story_attachment.target &&
554
+
555
+ attachment1.extensible_attachment.story_attachment.target.__typename &&
556
+
557
+ attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
558
+
559
+ ) type = "MessageLocation";
560
+
561
+ else type = "ExtensibleAttachment";
562
+
563
+
564
+
565
+ blob = attachment1.extensible_attachment;
566
+
567
+ }
568
+
569
+ // TODO: Determine whether "sticker", "photo", "file" etc are still used
570
+
571
+ // KEEP IN SYNC WITH getThreadHistory
572
+
573
+ switch (type) {
574
+
575
+ case "sticker":
576
+
577
+ return {
578
+
579
+ type: "sticker",
580
+
581
+ ID: attachment1.metadata.stickerID.toString(),
582
+
583
+ url: attachment1.url,
584
+
585
+
586
+
587
+ packID: attachment1.metadata.packID.toString(),
588
+
589
+ spriteUrl: attachment1.metadata.spriteURI,
590
+
591
+ spriteUrl2x: attachment1.metadata.spriteURI2x,
592
+
593
+ width: attachment1.metadata.width,
594
+
595
+ height: attachment1.metadata.height,
596
+
597
+
598
+
599
+ caption: attachment2.caption,
600
+
601
+ description: attachment2.description,
602
+
603
+
604
+
605
+ frameCount: attachment1.metadata.frameCount,
606
+
607
+ frameRate: attachment1.metadata.frameRate,
608
+
609
+ framesPerRow: attachment1.metadata.framesPerRow,
610
+
611
+ framesPerCol: attachment1.metadata.framesPerCol,
612
+
613
+
614
+
615
+ stickerID: attachment1.metadata.stickerID.toString(), // @Legacy
616
+
617
+ spriteURI: attachment1.metadata.spriteURI, // @Legacy
618
+
619
+ spriteURI2x: attachment1.metadata.spriteURI2x // @Legacy
620
+
621
+ };
622
+
623
+ case "file":
624
+
625
+ return {
626
+
627
+ type: "file",
628
+
629
+ filename: attachment1.name,
630
+
631
+ ID: attachment2.id.toString(),
632
+
633
+ url: attachment1.url,
634
+
635
+
636
+
637
+ isMalicious: attachment2.is_malicious,
638
+
639
+ contentType: attachment2.mime_type,
640
+
641
+
642
+
643
+ name: attachment1.name, // @Legacy
644
+
645
+ mimeType: attachment2.mime_type, // @Legacy
646
+
647
+ fileSize: attachment2.file_size // @Legacy
648
+
649
+ };
650
+
651
+ case "photo":
652
+
653
+ return {
654
+
655
+ type: "photo",
656
+
657
+ ID: attachment1.metadata.fbid.toString(),
658
+
659
+ filename: attachment1.fileName,
660
+
661
+ thumbnailUrl: attachment1.thumbnail_url,
662
+
663
+
664
+
665
+ previewUrl: attachment1.preview_url,
666
+
667
+ previewWidth: attachment1.preview_width,
668
+
669
+ previewHeight: attachment1.preview_height,
670
+
671
+
672
+
673
+ largePreviewUrl: attachment1.large_preview_url,
674
+
675
+ largePreviewWidth: attachment1.large_preview_width,
676
+
677
+ largePreviewHeight: attachment1.large_preview_height,
678
+
679
+
680
+
681
+ url: attachment1.metadata.url, // @Legacy
682
+
683
+ width: attachment1.metadata.dimensions.split(",")[0], // @Legacy
684
+
685
+ height: attachment1.metadata.dimensions.split(",")[1], // @Legacy
686
+
687
+ name: attachment1.fileName // @Legacy
688
+
689
+ };
690
+
691
+ case "animated_image":
692
+
693
+ return {
694
+
695
+ type: "animated_image",
696
+
697
+ ID: attachment2.id.toString(),
698
+
699
+ filename: attachment2.filename,
700
+
701
+
702
+
703
+ previewUrl: attachment1.preview_url,
704
+
705
+ previewWidth: attachment1.preview_width,
706
+
707
+ previewHeight: attachment1.preview_height,
708
+
709
+
710
+
711
+ url: attachment2.image_data.url,
712
+
713
+ width: attachment2.image_data.width,
714
+
715
+ height: attachment2.image_data.height,
716
+
717
+
718
+
719
+ name: attachment1.name, // @Legacy
720
+
721
+ facebookUrl: attachment1.url, // @Legacy
722
+
723
+ thumbnailUrl: attachment1.thumbnail_url, // @Legacy
724
+
725
+ mimeType: attachment2.mime_type, // @Legacy
726
+
727
+ rawGifImage: attachment2.image_data.raw_gif_image, // @Legacy
728
+
729
+ rawWebpImage: attachment2.image_data.raw_webp_image, // @Legacy
730
+
731
+ animatedGifUrl: attachment2.image_data.animated_gif_url, // @Legacy
732
+
733
+ animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url, // @Legacy
734
+
735
+ animatedWebpUrl: attachment2.image_data.animated_webp_url, // @Legacy
736
+
737
+ animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url // @Legacy
738
+
739
+ };
740
+
741
+ case "share":
742
+
743
+ return {
744
+
745
+ type: "share",
746
+
747
+ ID: attachment1.share.share_id.toString(),
748
+
749
+ url: attachment2.href,
750
+
751
+
752
+
753
+ title: attachment1.share.title,
754
+
755
+ description: attachment1.share.description,
756
+
757
+ source: attachment1.share.source,
758
+
759
+
760
+
761
+ image: attachment1.share.media.image,
762
+
763
+ width: attachment1.share.media.image_size.width,
764
+
765
+ height: attachment1.share.media.image_size.height,
766
+
767
+ playable: attachment1.share.media.playable,
768
+
769
+ duration: attachment1.share.media.duration,
770
+
771
+
772
+
773
+ subattachments: attachment1.share.subattachments,
774
+
775
+ properties: {},
776
+
777
+
778
+
779
+ animatedImageSize: attachment1.share.media.animated_image_size, // @Legacy
780
+
781
+ facebookUrl: attachment1.share.uri, // @Legacy
782
+
783
+ target: attachment1.share.target, // @Legacy
784
+
785
+ styleList: attachment1.share.style_list // @Legacy
786
+
787
+ };
788
+
789
+ case "video":
790
+
791
+ return {
792
+
793
+ type: "video",
794
+
795
+ ID: attachment1.metadata.fbid.toString(),
796
+
797
+ filename: attachment1.name,
798
+
799
+
800
+
801
+ previewUrl: attachment1.preview_url,
802
+
803
+ previewWidth: attachment1.preview_width,
804
+
805
+ previewHeight: attachment1.preview_height,
806
+
807
+
808
+
809
+ url: attachment1.url,
810
+
811
+ width: attachment1.metadata.dimensions.width,
812
+
813
+ height: attachment1.metadata.dimensions.height,
814
+
815
+
816
+
817
+ duration: attachment1.metadata.duration,
818
+
819
+ videoType: "unknown",
820
+
821
+
822
+
823
+ thumbnailUrl: attachment1.thumbnail_url // @Legacy
824
+
825
+ };
826
+
827
+ case "error":
828
+
829
+ return {
830
+
831
+ type: "error",
832
+
833
+
834
+
835
+ // Save error attachments because we're unsure of their format,
836
+
837
+ // and whether there are cases they contain something useful for debugging.
838
+
839
+ attachment1: attachment1,
840
+
841
+ attachment2: attachment2
842
+
843
+ };
844
+
845
+ case "MessageImage":
846
+
847
+ return {
848
+
849
+ type: "photo",
850
+
851
+ ID: blob.legacy_attachment_id,
852
+
853
+ filename: blob.filename,
854
+
855
+ thumbnailUrl: blob.thumbnail.uri,
856
+
857
+
858
+
859
+ previewUrl: blob.preview.uri,
860
+
861
+ previewWidth: blob.preview.width,
862
+
863
+ previewHeight: blob.preview.height,
864
+
865
+
866
+
867
+ largePreviewUrl: blob.large_preview.uri,
868
+
869
+ largePreviewWidth: blob.large_preview.width,
870
+
871
+ largePreviewHeight: blob.large_preview.height,
872
+
873
+
874
+
875
+ url: blob.large_preview.uri, // @Legacy
876
+
877
+ width: blob.original_dimensions.x, // @Legacy
878
+
879
+ height: blob.original_dimensions.y, // @Legacy
880
+
881
+ name: blob.filename // @Legacy
882
+
883
+ };
884
+
885
+ case "MessageAnimatedImage":
886
+
887
+ return {
888
+
889
+ type: "animated_image",
890
+
891
+ ID: blob.legacy_attachment_id,
892
+
893
+ filename: blob.filename,
894
+
895
+
896
+
897
+ previewUrl: blob.preview_image.uri,
898
+
899
+ previewWidth: blob.preview_image.width,
900
+
901
+ previewHeight: blob.preview_image.height,
902
+
903
+
904
+
905
+ url: blob.animated_image.uri,
906
+
907
+ width: blob.animated_image.width,
908
+
909
+ height: blob.animated_image.height,
910
+
911
+
912
+
913
+ thumbnailUrl: blob.preview_image.uri, // @Legacy
914
+
915
+ name: blob.filename, // @Legacy
916
+
917
+ facebookUrl: blob.animated_image.uri, // @Legacy
918
+
919
+ rawGifImage: blob.animated_image.uri, // @Legacy
920
+
921
+ animatedGifUrl: blob.animated_image.uri, // @Legacy
922
+
923
+ animatedGifPreviewUrl: blob.preview_image.uri, // @Legacy
924
+
925
+ animatedWebpUrl: blob.animated_image.uri, // @Legacy
926
+
927
+ animatedWebpPreviewUrl: blob.preview_image.uri // @Legacy
928
+
929
+ };
930
+
931
+ case "MessageVideo":
932
+
933
+ return {
934
+
935
+ type: "video",
936
+
937
+ filename: blob.filename,
938
+
939
+ ID: blob.legacy_attachment_id,
940
+
941
+
942
+
943
+ previewUrl: blob.large_image.uri,
944
+
945
+ previewWidth: blob.large_image.width,
946
+
947
+ previewHeight: blob.large_image.height,
948
+
949
+
950
+
951
+ url: blob.playable_url,
952
+
953
+ width: blob.original_dimensions.x,
954
+
955
+ height: blob.original_dimensions.y,
956
+
957
+
958
+
959
+ duration: blob.playable_duration_in_ms,
960
+
961
+ videoType: blob.video_type.toLowerCase(),
962
+
963
+
964
+
965
+ thumbnailUrl: blob.large_image.uri // @Legacy
966
+
967
+ };
968
+
969
+ case "MessageAudio":
970
+
971
+ return {
972
+
973
+ type: "audio",
974
+
975
+ filename: blob.filename,
976
+
977
+ ID: blob.url_shimhash,
978
+
979
+
980
+
981
+ audioType: blob.audio_type,
982
+
983
+ duration: blob.playable_duration_in_ms,
984
+
985
+ url: blob.playable_url,
986
+
987
+
988
+
989
+ isVoiceMail: blob.is_voicemail
990
+
991
+ };
992
+
993
+ case "StickerAttachment":
994
+
995
+ return {
996
+
997
+ type: "sticker",
998
+
999
+ ID: blob.id,
1000
+
1001
+ url: blob.url,
1002
+
1003
+
1004
+
1005
+ packID: blob.pack ? blob.pack.id : null,
1006
+
1007
+ spriteUrl: blob.sprite_image,
1008
+
1009
+ spriteUrl2x: blob.sprite_image_2x,
1010
+
1011
+ width: blob.width,
1012
+
1013
+ height: blob.height,
1014
+
1015
+
1016
+
1017
+ caption: blob.label,
1018
+
1019
+ description: blob.label,
1020
+
1021
+
1022
+
1023
+ frameCount: blob.frame_count,
1024
+
1025
+ frameRate: blob.frame_rate,
1026
+
1027
+ framesPerRow: blob.frames_per_row,
1028
+
1029
+ framesPerCol: blob.frames_per_column,
1030
+
1031
+
1032
+
1033
+ stickerID: blob.id, // @Legacy
1034
+
1035
+ spriteURI: blob.sprite_image, // @Legacy
1036
+
1037
+ spriteURI2x: blob.sprite_image_2x // @Legacy
1038
+
1039
+ };
1040
+
1041
+ case "MessageLocation":
1042
+
1043
+ var urlAttach = blob.story_attachment.url;
1044
+
1045
+ var mediaAttach = blob.story_attachment.media;
1046
+
1047
+
1048
+
1049
+ var u = querystring.parse(url.parse(urlAttach).query).u;
1050
+
1051
+ var where1 = querystring.parse(url.parse(u).query).where1;
1052
+
1053
+ var address = where1.split(", ");
1054
+
1055
+
1056
+
1057
+ var latitude;
1058
+
1059
+ var longitude;
1060
+
1061
+
1062
+
1063
+ try {
1064
+
1065
+ latitude = Number.parseFloat(address[0]);
1066
+
1067
+ longitude = Number.parseFloat(address[1]);
1068
+
1069
+ } catch (err) {
1070
+
1071
+ /* empty */
1072
+
1073
+ }
1074
+
1075
+
1076
+
1077
+ var imageUrl;
1078
+
1079
+ var width;
1080
+
1081
+ var height;
1082
+
1083
+
1084
+
1085
+ if (mediaAttach && mediaAttach.image) {
1086
+
1087
+ imageUrl = mediaAttach.image.uri;
1088
+
1089
+ width = mediaAttach.image.width;
1090
+
1091
+ height = mediaAttach.image.height;
1092
+
1093
+ }
1094
+
1095
+
1096
+
1097
+ return {
1098
+
1099
+ type: "location",
1100
+
1101
+ ID: blob.legacy_attachment_id,
1102
+
1103
+ latitude: latitude,
1104
+
1105
+ longitude: longitude,
1106
+
1107
+ image: imageUrl,
1108
+
1109
+ width: width,
1110
+
1111
+ height: height,
1112
+
1113
+ url: u || urlAttach,
1114
+
1115
+ address: where1,
1116
+
1117
+
1118
+
1119
+ facebookUrl: blob.story_attachment.url, // @Legacy
1120
+
1121
+ target: blob.story_attachment.target, // @Legacy
1122
+
1123
+ styleList: blob.story_attachment.style_list // @Legacy
1124
+
1125
+ };
1126
+
1127
+ case "ExtensibleAttachment":
1128
+
1129
+ return {
1130
+
1131
+ type: "share",
1132
+
1133
+ ID: blob.legacy_attachment_id,
1134
+
1135
+ url: blob.story_attachment.url,
1136
+
1137
+
1138
+
1139
+ title: blob.story_attachment.title_with_entities.text,
1140
+
1141
+ description: blob.story_attachment.description &&
1142
+
1143
+ blob.story_attachment.description.text,
1144
+
1145
+ source: blob.story_attachment.source ? blob.story_attachment.source.text : null,
1146
+
1147
+
1148
+
1149
+ image: blob.story_attachment.media &&
1150
+
1151
+ blob.story_attachment.media.image &&
1152
+
1153
+ blob.story_attachment.media.image.uri,
1154
+
1155
+ width: blob.story_attachment.media &&
1156
+
1157
+ blob.story_attachment.media.image &&
1158
+
1159
+ blob.story_attachment.media.image.width,
1160
+
1161
+ height: blob.story_attachment.media &&
1162
+
1163
+ blob.story_attachment.media.image &&
1164
+
1165
+ blob.story_attachment.media.image.height,
1166
+
1167
+ playable: blob.story_attachment.media &&
1168
+
1169
+ blob.story_attachment.media.is_playable,
1170
+
1171
+ duration: blob.story_attachment.media &&
1172
+
1173
+ blob.story_attachment.media.playable_duration_in_ms,
1174
+
1175
+ playableUrl: blob.story_attachment.media == null ? null : blob.story_attachment.media.playable_url,
1176
+
1177
+
1178
+
1179
+ subattachments: blob.story_attachment.subattachments,
1180
+
1181
+ properties: blob.story_attachment.properties.reduce(function(obj, cur) {
1182
+
1183
+ obj[cur.key] = cur.value.text;
1184
+
1185
+ return obj;
1186
+
1187
+ }, {}),
1188
+
1189
+
1190
+
1191
+ facebookUrl: blob.story_attachment.url, // @Legacy
1192
+
1193
+ target: blob.story_attachment.target, // @Legacy
1194
+
1195
+ styleList: blob.story_attachment.style_list // @Legacy
1196
+
1197
+ };
1198
+
1199
+ case "MessageFile":
1200
+
1201
+ return {
1202
+
1203
+ type: "file",
1204
+
1205
+ filename: blob.filename,
1206
+
1207
+ ID: blob.message_file_fbid,
1208
+
1209
+
1210
+
1211
+ url: blob.url,
1212
+
1213
+ isMalicious: blob.is_malicious,
1214
+
1215
+ contentType: blob.content_type,
1216
+
1217
+
1218
+
1219
+ name: blob.filename,
1220
+
1221
+ mimeType: "",
1222
+
1223
+ fileSize: -1
1224
+
1225
+ };
1226
+
1227
+ default:
1228
+
1229
+ throw new Error(
1230
+
1231
+ "unrecognized attach_file of type " +
1232
+
1233
+ type +
1234
+
1235
+ "`" +
1236
+
1237
+ JSON.stringify(attachment1, null, 4) +
1238
+
1239
+ " attachment2: " +
1240
+
1241
+ JSON.stringify(attachment2, null, 4) +
1242
+
1243
+ "`"
1244
+
1245
+ );
1246
+
1247
+ }
1248
+
1249
+ }
1250
+
1251
+
1252
+
1253
+ function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
1254
+
1255
+ attachmentMap = shareMap || attachmentMap;
1256
+
1257
+ return attachments ?
1258
+
1259
+ attachments.map(function(val, i) {
1260
+
1261
+ if (!attachmentMap ||
1262
+
1263
+ !attachmentIds ||
1264
+
1265
+ !attachmentMap[attachmentIds[i]]
1266
+
1267
+ ) {
1268
+
1269
+ return _formatAttachment(val);
1270
+
1271
+ }
1272
+
1273
+ return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
1274
+
1275
+ }) : [];
1276
+
1277
+ }
1278
+
1279
+
1280
+
1281
+ function formatDeltaMessage(m) {
1282
+
1283
+ var md = m.delta.messageMetadata;
1284
+
1285
+ var mdata =
1286
+
1287
+ m.delta.data === undefined ? [] :
1288
+
1289
+ m.delta.data.prng === undefined ? [] :
1290
+
1291
+ JSON.parse(m.delta.data.prng);
1292
+
1293
+ var m_id = mdata.map(u => u.i);
1294
+
1295
+ var m_offset = mdata.map(u => u.o);
1296
+
1297
+ var m_length = mdata.map(u => u.l);
1298
+
1299
+ var mentions = {};
1300
+
1301
+ var body = m.delta.body || "";
1302
+
1303
+ var args = body == "" ? [] : body.trim().split(/\s+/);
1304
+
1305
+ 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]);
1306
+
1307
+
1308
+
1309
+ return {
1310
+
1311
+ type: "message",
1312
+
1313
+ senderID: formatID(md.actorFbId.toString()),
1314
+
1315
+ threadID: formatID((md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()),
1316
+
1317
+ messageID: md.messageId,
1318
+
1319
+ args: args,
1320
+
1321
+ body: body,
1322
+
1323
+ attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
1324
+
1325
+ mentions: mentions,
1326
+
1327
+ timestamp: md.timestamp,
1328
+
1329
+ isGroup: !!md.threadKey.threadFbId,
1330
+
1331
+ participantIDs: m.delta.participants || []
1332
+
1333
+ };
1334
+
1335
+ }
1336
+
1337
+
1338
+
1339
+ function formatID(id) {
1340
+
1341
+ if (id != undefined && id != null) return id.replace(/(fb)?id[:.]/, "");
1342
+
1343
+ else return id;
1344
+
1345
+ }
1346
+
1347
+
1348
+
1349
+ function formatMessage(m) {
1350
+
1351
+ var originalMessage = m.message ? m.message : m;
1352
+
1353
+ var obj = {
1354
+
1355
+ type: "message",
1356
+
1357
+ senderName: originalMessage.sender_name,
1358
+
1359
+ senderID: formatID(originalMessage.sender_fbid.toString()),
1360
+
1361
+ participantNames: originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(" ")[0]],
1362
+
1363
+ participantIDs: originalMessage.group_thread_info ?
1364
+
1365
+ originalMessage.group_thread_info.participant_ids.map(function(v) {
1366
+
1367
+ return formatID(v.toString());
1368
+
1369
+ }) : [formatID(originalMessage.sender_fbid)],
1370
+
1371
+ body: originalMessage.body || "",
1372
+
1373
+ threadID: formatID((originalMessage.thread_fbid || originalMessage.other_user_fbid).toString()),
1374
+
1375
+ threadName: originalMessage.group_thread_info ? originalMessage.group_thread_info.name : originalMessage.sender_name,
1376
+
1377
+ location: originalMessage.coordinates ? originalMessage.coordinates : null,
1378
+
1379
+ messageID: originalMessage.mid ? originalMessage.mid.toString() : originalMessage.message_id,
1380
+
1381
+ attachments: formatAttachment(
1382
+
1383
+ originalMessage.attachments,
1384
+
1385
+ originalMessage.attachmentIds,
1386
+
1387
+ originalMessage.attachment_map,
1388
+
1389
+ originalMessage.share_map
1390
+
1391
+ ),
1392
+
1393
+ timestamp: originalMessage.timestamp,
1394
+
1395
+ timestampAbsolute: originalMessage.timestamp_absolute,
1396
+
1397
+ timestampRelative: originalMessage.timestamp_relative,
1398
+
1399
+ timestampDatetime: originalMessage.timestamp_datetime,
1400
+
1401
+ tags: originalMessage.tags,
1402
+
1403
+ reactions: originalMessage.reactions ? originalMessage.reactions : [],
1404
+
1405
+ isUnread: originalMessage.is_unread
1406
+
1407
+ };
1408
+
1409
+
1410
+
1411
+ if (m.type === "pages_messaging") obj.pageID = m.realtime_viewer_fbid.toString();
1412
+
1413
+ obj.isGroup = obj.participantIDs.length > 2;
1414
+
1415
+
1416
+
1417
+ return obj;
1418
+
1419
+ }
1420
+
1421
+
1422
+
1423
+ function formatEvent(m) {
1424
+
1425
+ var originalMessage = m.message ? m.message : m;
1426
+
1427
+ var logMessageType = originalMessage.log_message_type;
1428
+
1429
+ var logMessageData;
1430
+
1431
+ if (logMessageType === "log:generic-admin-text") {
1432
+
1433
+ logMessageData = originalMessage.log_message_data.untypedData;
1434
+
1435
+ logMessageType = getAdminTextMessageType(originalMessage.log_message_data.message_type);
1436
+
1437
+ } else logMessageData = originalMessage.log_message_data;
1438
+
1439
+
1440
+
1441
+ return Object.assign(formatMessage(originalMessage), {
1442
+
1443
+ type: "event",
1444
+
1445
+ logMessageType: logMessageType,
1446
+
1447
+ logMessageData: logMessageData,
1448
+
1449
+ logMessageBody: originalMessage.log_message_body
1450
+
1451
+ });
1452
+
1453
+ }
1454
+
1455
+
1456
+
1457
+ function formatHistoryMessage(m) {
1458
+
1459
+ switch (m.action_type) {
1460
+
1461
+ case "ma-type:log-message":
1462
+
1463
+ return formatEvent(m);
1464
+
1465
+ default:
1466
+
1467
+ return formatMessage(m);
1468
+
1469
+ }
1470
+
1471
+ }
1472
+
1473
+
1474
+
1475
+ // Get a more readable message type for AdminTextMessages
1476
+
1477
+ function getAdminTextMessageType(m) {
1478
+
1479
+ switch (m.type) {
1480
+
1481
+ case "change_thread_theme":
1482
+
1483
+ return "log:thread-color";
1484
+
1485
+ case "change_thread_icon":
1486
+
1487
+ return "log:thread-icon";
1488
+
1489
+ case "change_thread_nickname":
1490
+
1491
+ return "log:user-nickname";
1492
+
1493
+ case "change_thread_admins":
1494
+
1495
+ return "log:thread-admins";
1496
+
1497
+ case "group_poll":
1498
+
1499
+ return "log:thread-poll";
1500
+
1501
+ case "change_thread_approval_mode":
1502
+
1503
+ return "log:thread-approval-mode";
1504
+
1505
+ case "messenger_call_log":
1506
+
1507
+ case "participant_joined_group_call":
1508
+
1509
+ return "log:thread-call";
1510
+
1511
+ }
1512
+
1513
+ }
1514
+
1515
+
1516
+
1517
+ function formatDeltaEvent(m) {
1518
+
1519
+ var logMessageType;
1520
+
1521
+ var logMessageData;
1522
+
1523
+
1524
+
1525
+ // log:thread-color => {theme_color}
1526
+
1527
+ // log:user-nickname => {participant_id, nickname}
1528
+
1529
+ // log:thread-icon => {thread_icon}
1530
+
1531
+ // log:thread-name => {name}
1532
+
1533
+ // log:subscribe => {addedParticipants - [Array]}
1534
+
1535
+ //log:unsubscribe => {leftParticipantFbId}
1536
+
1537
+
1538
+
1539
+ switch (m.class) {
1540
+
1541
+ case "AdminTextMessage":
1542
+
1543
+ logMessageType = getAdminTextMessageType(m);
1544
+
1545
+ logMessageData = m.untypedData;
1546
+
1547
+ break;
1548
+
1549
+ case "ThreadName":
1550
+
1551
+ logMessageType = "log:thread-name";
1552
+
1553
+ logMessageData = { name: m.name };
1554
+
1555
+ break;
1556
+
1557
+ case "ParticipantsAddedToGroupThread":
1558
+
1559
+ logMessageType = "log:subscribe";
1560
+
1561
+ logMessageData = { addedParticipants: m.addedParticipants };
1562
+
1563
+ break;
1564
+
1565
+ case "ParticipantLeftGroupThread":
1566
+
1567
+ logMessageType = "log:unsubscribe";
1568
+
1569
+ logMessageData = { leftParticipantFbId: m.leftParticipantFbId };
1570
+
1571
+ break;
1572
+
1573
+ }
1574
+
1575
+
1576
+
1577
+ return {
1578
+
1579
+ type: "event",
1580
+
1581
+ threadID: formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),
1582
+
1583
+ logMessageType: logMessageType,
1584
+
1585
+ logMessageData: logMessageData,
1586
+
1587
+ logMessageBody: m.messageMetadata.adminText,
1588
+
1589
+ author: m.messageMetadata.actorFbId,
1590
+
1591
+ participantIDs: m.participants || []
1592
+
1593
+ };
1594
+
1595
+ }
1596
+
1597
+
1598
+
1599
+ function formatTyp(event) {
1600
+
1601
+ return {
1602
+
1603
+ isTyping: !!event.st,
1604
+
1605
+ from: event.from.toString(),
1606
+
1607
+ threadID: formatID((event.to || event.thread_fbid || event.from).toString()),
1608
+
1609
+ // When receiving typ indication from mobile, `from_mobile` isn't set.
1610
+
1611
+ // If it is, we just use that value.
1612
+
1613
+ fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
1614
+
1615
+ userID: (event.realtime_viewer_fbid || event.from).toString(),
1616
+
1617
+ type: "typ"
1618
+
1619
+ };
1620
+
1621
+ }
1622
+
1623
+
1624
+
1625
+ function formatDeltaReadReceipt(delta) {
1626
+
1627
+ // otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
1628
+
1629
+ // In a group chat actorFbId is used for the reader and threadFbId for the thread.
1630
+
1631
+ return {
1632
+
1633
+ reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
1634
+
1635
+ time: delta.actionTimestampMs,
1636
+
1637
+ threadID: formatID((delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()),
1638
+
1639
+ type: "read_receipt"
1640
+
1641
+ };
1642
+
1643
+ }
1644
+
1645
+
1646
+
1647
+ function formatReadReceipt(event) {
1648
+
1649
+ return {
1650
+
1651
+ reader: event.reader.toString(),
1652
+
1653
+ time: event.time,
1654
+
1655
+ threadID: formatID((event.thread_fbid || event.reader).toString()),
1656
+
1657
+ type: "read_receipt"
1658
+
1659
+ };
1660
+
1661
+ }
1662
+
1663
+
1664
+
1665
+ function formatRead(event) {
1666
+
1667
+ return {
1668
+
1669
+ threadID: formatID(((event.chat_ids && event.chat_ids[0]) || (event.thread_fbids && event.thread_fbids[0])).toString()),
1670
+
1671
+ time: event.timestamp,
1672
+
1673
+ type: "read"
1674
+
1675
+ };
1676
+
1677
+ }
1678
+
1679
+
1680
+
1681
+ function getFrom(str, startToken, endToken) {
1682
+
1683
+ var start = str.indexOf(startToken) + startToken.length;
1684
+
1685
+ if (start < startToken.length) return "";
1686
+
1687
+
1688
+
1689
+ var lastHalf = str.substring(start);
1690
+
1691
+ var end = lastHalf.indexOf(endToken);
1692
+
1693
+ if (end === -1) throw Error("Could not find endTime `" + endToken + "` in the given string.");
1694
+
1695
+ return lastHalf.substring(0, end);
1696
+
1697
+ }
1698
+
1699
+
1700
+
1701
+ function makeParsable(html) {
1702
+
1703
+ let withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
1704
+
1705
+
1706
+
1707
+ // (What the fuck FB, why windows style newlines?)
1708
+
1709
+ // So sometimes FB will send us base multiple objects in the same response.
1710
+
1711
+ // They're all valid JSON, one after the other, at the top level. We detect
1712
+
1713
+ // that and make it parse-able by JSON.parse.
1714
+
1715
+ // Ben - July 15th 2017
1716
+
1717
+ //
1718
+
1719
+ // It turns out that Facebook may insert random number of spaces before
1720
+
1721
+ // next object begins (issue #616)
1722
+
1723
+ // rav_kr - 2018-03-19
1724
+
1725
+ let maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
1726
+
1727
+ if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
1728
+
1729
+
1730
+
1731
+ return "[" + maybeMultipleObjects.join("},{") + "]";
1732
+
1733
+ }
1734
+
1735
+
1736
+
1737
+ function arrToForm(form) {
1738
+
1739
+ return arrayToObject(form,
1740
+
1741
+ function(v) {
1742
+
1743
+ return v.name;
1744
+
1745
+ },
1746
+
1747
+ function(v) {
1748
+
1749
+ return v.val;
1750
+
1751
+ }
1752
+
1753
+ );
1754
+
1755
+ }
1756
+
1757
+
1758
+
1759
+ function arrayToObject(arr, getKey, getValue) {
1760
+
1761
+ return arr.reduce(function(acc, val) {
1762
+
1763
+ acc[getKey(val)] = getValue(val);
1764
+
1765
+ return acc;
1766
+
1767
+ }, {});
1768
+
1769
+ }
1770
+
1771
+
1772
+
1773
+ function getSignatureID() {
1774
+
1775
+ return Math.floor(Math.random() * 2147483648).toString(16);
1776
+
1777
+ }
1778
+
1779
+
1780
+
1781
+ function generateTimestampRelative() {
1782
+
1783
+ var d = new Date();
1784
+
1785
+ return d.getHours() + ":" + padZeros(d.getMinutes());
1786
+
1787
+ }
1788
+
1789
+
1790
+
1791
+ function makeDefaults(html, userID, ctx) {
1792
+
1793
+ var reqCounter = 1;
1794
+
1795
+ var fb_dtsg = getFrom(html, 'name="fb_dtsg" value="', '"');
1796
+
1797
+
1798
+
1799
+ // @Hack Ok we've done hacky things, this is definitely on top 5.
1800
+
1801
+ // We totally assume the object is flat and try parsing until a }.
1802
+
1803
+ // If it works though it's cool because we get a bunch of extra data things.
1804
+
1805
+ //
1806
+
1807
+ // Update: we don't need this. Leaving it in in case we ever do.
1808
+
1809
+ // Ben - July 15th 2017
1810
+
1811
+
1812
+
1813
+ // var siteData = getFrom(html, "[\"SiteData\",[],", "},");
1814
+
1815
+ // try {
1816
+
1817
+ // siteData = JSON.parse(siteData + "}");
1818
+
1819
+ // } catch(e) {
1820
+
1821
+ // log.warn("makeDefaults", "Couldn't parse SiteData. Won't have access to some variables.");
1822
+
1823
+ // siteData = {};
1824
+
1825
+ // }
1826
+
1827
+
1828
+
1829
+ var ttstamp = "2";
1830
+
1831
+ for (var i = 0; i < fb_dtsg.length; i++) ttstamp += fb_dtsg.charCodeAt(i);
1832
+
1833
+ var revision = getFrom(html, 'revision":', ",");
1834
+
1835
+
1836
+
1837
+ function mergeWithDefaults(obj) {
1838
+
1839
+ // @TODO This is missing a key called __dyn.
1840
+
1841
+ // After some investigation it seems like __dyn is some sort of set that FB
1842
+
1843
+ // calls BitMap. It seems like certain responses have a "define" key in the
1844
+
1845
+ // res.jsmods arrays. I think the code iterates over those and calls `set`
1846
+
1847
+ // on the bitmap for each of those keys. Then it calls
1848
+
1849
+ // bitmap.toCompressedString() which returns what __dyn is.
1850
+
1851
+ //
1852
+
1853
+ // So far the API has been working without this.
1854
+
1855
+ //
1856
+
1857
+ // Ben - July 15th 2017
1858
+
1859
+ var newObj = {
1860
+
1861
+ __user: userID,
1862
+
1863
+ __req: (reqCounter++).toString(36),
1864
+
1865
+ __rev: revision,
1866
+
1867
+ __a: 1,
1868
+
1869
+ // __af: siteData.features,
1870
+
1871
+ fb_dtsg: ctx.fb_dtsg ? ctx.fb_dtsg : fb_dtsg,
1872
+
1873
+ jazoest: ctx.ttstamp ? ctx.ttstamp : ttstamp
1874
+
1875
+ // __spin_r: siteData.__spin_r,
1876
+
1877
+ // __spin_b: siteData.__spin_b,
1878
+
1879
+ // __spin_t: siteData.__spin_t,
1880
+
1881
+ };
1882
+
1883
+
1884
+
1885
+ // @TODO this is probably not needed.
1886
+
1887
+ // Ben - July 15th 2017
1888
+
1889
+ // if (siteData.be_key) {
1890
+
1891
+ // newObj[siteData.be_key] = siteData.be_mode;
1892
+
1893
+ // }
1894
+
1895
+ // if (siteData.pkg_cohort_key) {
1896
+
1897
+ // newObj[siteData.pkg_cohort_key] = siteData.pkg_cohort;
1898
+
1899
+ // }
1900
+
1901
+
1902
+
1903
+ if (!obj) return newObj;
1904
+
1905
+ for (var prop in obj)
1906
+
1907
+ if (obj.hasOwnProperty(prop))
1908
+
1909
+ if (!newObj[prop]) newObj[prop] = obj[prop];
1910
+
1911
+ return newObj;
1912
+
1913
+ }
1914
+
1915
+
1916
+
1917
+ function postWithDefaults(url, jar, form, ctxx) {
1918
+
1919
+ return post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx);
1920
+
1921
+ }
1922
+
1923
+
1924
+
1925
+ function getWithDefaults(url, jar, qs, ctxx) {
1926
+
1927
+ return get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
1928
+
1929
+ }
1930
+
1931
+
1932
+
1933
+ function postFormDataWithDefault(url, jar, form, qs, ctxx) {
1934
+
1935
+ return postFormData(url, jar, mergeWithDefaults(form), mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
1936
+
1937
+ }
1938
+
1939
+
1940
+
1941
+ return {
1942
+
1943
+ get: getWithDefaults,
1944
+
1945
+ post: postWithDefaults,
1946
+
1947
+ postFormData: postFormDataWithDefault
1948
+
1949
+ };
1950
+
1951
+ }
1952
+
1953
+
1954
+
1955
+ function parseAndCheckLogin(ctx, defaultFuncs, retryCount) {
1956
+
1957
+ if (retryCount == undefined) retryCount = 0;
1958
+
1959
+ return function(data) {
1960
+
1961
+ return bluebird.try(function() {
1962
+
1963
+ log.verbose("parseAndCheckLogin", data.body);
1964
+
1965
+ if (data.statusCode >= 500 && data.statusCode < 600) {
1966
+
1967
+ if (retryCount >= 5) {
1968
+
1969
+ throw {
1970
+
1971
+ error: "Request retry failed. Check the `res` and `statusCode` property on this error.",
1972
+
1973
+ statusCode: data.statusCode,
1974
+
1975
+ res: data.body
1976
+
1977
+ };
1978
+
1979
+ }
1980
+
1981
+ retryCount++;
1982
+
1983
+ var retryTime = Math.floor(Math.random() * 5000);
1984
+
1985
+ log.warn("parseAndCheckLogin", "Got status code " + data.statusCode + " - " + retryCount + ". attempt to retry in " + retryTime + " milliseconds...");
1986
+
1987
+ var url = data.request.uri.protocol + "//" + data.request.uri.hostname + data.request.uri.pathname;
1988
+
1989
+ if (data.request.headers["Content-Type"].split(";")[0] === "multipart/form-data") {
1990
+
1991
+ return bluebird.delay(retryTime).then(() => defaultFuncs.postFormData(url, ctx.jar, data.request.formData, {}))
1992
+
1993
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
1994
+
1995
+ } else {
1996
+
1997
+ return bluebird.delay(retryTime).then(() => defaultFuncs.post(url, ctx.jar, data.request.formData))
1998
+
1999
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
2000
+
2001
+ }
2002
+
2003
+ }
2004
+
2005
+ if (data.statusCode !== 200) throw new Error("parseAndCheckLogin got status code: " + data.statusCode + ". Bailing out of trying to parse response.");
2006
+
2007
+
2008
+
2009
+ var res = null;
2010
+
2011
+ try {
2012
+
2013
+ res = JSON.parse(makeParsable(data.body));
2014
+
2015
+ } catch (e) {
2016
+
2017
+ throw {
2018
+
2019
+ error: "JSON.parse error. Check the `detail` property on this error.",
2020
+
2021
+ detail: e,
2022
+
2023
+ res: data.body
2024
+
2025
+ };
2026
+
2027
+ }
2028
+
2029
+
2030
+
2031
+ // In some cases the response contains only a redirect URL which should be followed
2032
+
2033
+ if (res.redirect && data.request.method === "GET") return defaultFuncs.get(res.redirect, ctx.jar).then(parseAndCheckLogin(ctx, defaultFuncs));
2034
+
2035
+
2036
+
2037
+ // TODO: handle multiple cookies?
2038
+
2039
+ if (res.jsmods && res.jsmods.require && Array.isArray(res.jsmods.require[0]) && res.jsmods.require[0][0] === "Cookie") {
2040
+
2041
+ res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace("_js_", "");
2042
+
2043
+ var cookie = formatCookie(res.jsmods.require[0][3], "facebook");
2044
+
2045
+ var cookie2 = formatCookie(res.jsmods.require[0][3], "messenger");
2046
+
2047
+ ctx.jar.setCookie(cookie, "https://www.facebook.com");
2048
+
2049
+ ctx.jar.setCookie(cookie2, "https://www.messenger.com");
2050
+
2051
+ }
2052
+
2053
+
2054
+
2055
+ // On every request we check if we got a DTSG and we mutate the context so that we use the latest
2056
+
2057
+ // one for the next requests.
2058
+
2059
+ if (res.jsmods && Array.isArray(res.jsmods.require)) {
2060
+
2061
+ var arr = res.jsmods.require;
2062
+
2063
+ for (var i in arr) {
2064
+
2065
+ if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
2066
+
2067
+ ctx.fb_dtsg = arr[i][3][0];
2068
+
2069
+
2070
+
2071
+ // Update ttstamp since that depends on fb_dtsg
2072
+
2073
+ ctx.ttstamp = "2";
2074
+
2075
+ for (var j = 0; j < ctx.fb_dtsg.length; j++) ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
2076
+
2077
+ }
2078
+
2079
+ }
2080
+
2081
+ }
2082
+
2083
+
2084
+
2085
+ if (res.error === 1357001) throw { error: "Appstate Đã Bị Lỗi" };
2086
+
2087
+ return res;
2088
+
2089
+ });
2090
+
2091
+ };
2092
+
2093
+ }
2094
+
2095
+
2096
+
2097
+ function saveCookies(jar) {
2098
+
2099
+ return function(res) {
2100
+
2101
+ var cookies = res.headers["set-cookie"] || [];
2102
+
2103
+ cookies.forEach(function(c) {
2104
+
2105
+ if (c.indexOf(".facebook.com") > -1) jar.setCookie(c, "https://www.facebook.com");
2106
+
2107
+ var c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
2108
+
2109
+ jar.setCookie(c2, "https://www.messenger.com");
2110
+
2111
+ });
2112
+
2113
+ return res;
2114
+
2115
+ };
2116
+
2117
+ }
2118
+
2119
+
2120
+
2121
+ var NUM_TO_MONTH = [
2122
+
2123
+ "Jan",
2124
+
2125
+ "Feb",
2126
+
2127
+ "Mar",
2128
+
2129
+ "Apr",
2130
+
2131
+ "May",
2132
+
2133
+ "Jun",
2134
+
2135
+ "Jul",
2136
+
2137
+ "Aug",
2138
+
2139
+ "Sep",
2140
+
2141
+ "Oct",
2142
+
2143
+ "Nov",
2144
+
2145
+ "Dec"
2146
+
2147
+ ];
2148
+
2149
+ var NUM_TO_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2150
+
2151
+
2152
+
2153
+ function formatDate(date) {
2154
+
2155
+ var d = date.getUTCDate();
2156
+
2157
+ d = d >= 10 ? d : "0" + d;
2158
+
2159
+ var h = date.getUTCHours();
2160
+
2161
+ h = h >= 10 ? h : "0" + h;
2162
+
2163
+ var m = date.getUTCMinutes();
2164
+
2165
+ m = m >= 10 ? m : "0" + m;
2166
+
2167
+ var s = date.getUTCSeconds();
2168
+
2169
+ s = s >= 10 ? s : "0" + s;
2170
+
2171
+ return (NUM_TO_DAY[date.getUTCDay()] + ", " + d + " " + NUM_TO_MONTH[date.getUTCMonth()] + " " + date.getUTCFullYear() + " " + h + ":" + m + ":" + s + " GMT");
2172
+
2173
+ }
2174
+
2175
+
2176
+
2177
+ function formatCookie(arr, url) {
2178
+
2179
+ return arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com";
2180
+
2181
+ }
2182
+
2183
+
2184
+
2185
+ function formatThread(data) {
2186
+
2187
+ return {
2188
+
2189
+ threadID: formatID(data.thread_fbid.toString()),
2190
+
2191
+ participants: data.participants.map(formatID),
2192
+
2193
+ participantIDs: data.participants.map(formatID),
2194
+
2195
+ name: data.name,
2196
+
2197
+ nicknames: data.custom_nickname,
2198
+
2199
+ snippet: data.snippet,
2200
+
2201
+ snippetAttachments: data.snippet_attachments,
2202
+
2203
+ snippetSender: formatID((data.snippet_sender || "").toString()),
2204
+
2205
+ unreadCount: data.unread_count,
2206
+
2207
+ messageCount: data.message_count,
2208
+
2209
+ imageSrc: data.image_src,
2210
+
2211
+ timestamp: data.timestamp,
2212
+
2213
+ serverTimestamp: data.server_timestamp, // what is this?
2214
+
2215
+ muteUntil: data.mute_until,
2216
+
2217
+ isCanonicalUser: data.is_canonical_user,
2218
+
2219
+ isCanonical: data.is_canonical,
2220
+
2221
+ isSubscribed: data.is_subscribed,
2222
+
2223
+ folder: data.folder,
2224
+
2225
+ isArchived: data.is_archived,
2226
+
2227
+ recipientsLoadable: data.recipients_loadable,
2228
+
2229
+ hasEmailParticipant: data.has_email_participant,
2230
+
2231
+ readOnly: data.read_only,
2232
+
2233
+ canReply: data.can_reply,
2234
+
2235
+ cannotReplyReason: data.cannot_reply_reason,
2236
+
2237
+ lastMessageTimestamp: data.last_message_timestamp,
2238
+
2239
+ lastReadTimestamp: data.last_read_timestamp,
2240
+
2241
+ lastMessageType: data.last_message_type,
2242
+
2243
+ emoji: data.custom_like_icon,
2244
+
2245
+ color: data.custom_color,
2246
+
2247
+ adminIDs: data.admin_ids,
2248
+
2249
+ threadType: data.thread_type
2250
+
2251
+ };
2252
+
2253
+ }
2254
+
2255
+
2256
+
2257
+ function getType(obj) {
2258
+
2259
+ return Object.prototype.toString.call(obj).slice(8, -1);
2260
+
2261
+ }
2262
+
2263
+
2264
+
2265
+ function formatProxyPresence(presence, userID) {
2266
+
2267
+ if (presence.lat === undefined || presence.p === undefined) return null;
2268
+
2269
+ return {
2270
+
2271
+ type: "presence",
2272
+
2273
+ timestamp: presence.lat * 1000,
2274
+
2275
+ userID: userID || '',
2276
+
2277
+ statuses: presence.p
2278
+
2279
+ };
2280
+
2281
+ }
2282
+
2283
+
2284
+
2285
+ function formatPresence(presence, userID) {
2286
+
2287
+ return {
2288
+
2289
+ type: "presence",
2290
+
2291
+ timestamp: presence.la * 1000,
2292
+
2293
+ userID: userID || '',
2294
+
2295
+ statuses: presence.a
2296
+
2297
+ };
2298
+
2299
+ }
2300
+
2301
+
2302
+
2303
+ function decodeClientPayload(payload) {
2304
+
2305
+ /*
2306
+
2307
+ Special function which Client using to "encode" clients JSON payload
2308
+
2309
+ */
2310
+
2311
+ function Utf8ArrayToStr(array) {
2312
+
2313
+ var out, i, len, c;
2314
+
2315
+ var char2, char3;
2316
+
2317
+ out = "";
2318
+
2319
+ len = array.length;
2320
+
2321
+ i = 0;
2322
+
2323
+ while (i < len) {
2324
+
2325
+ c = array[i++];
2326
+
2327
+ switch (c >> 4) {
2328
+
2329
+ case 0:
2330
+
2331
+ case 1:
2332
+
2333
+ case 2:
2334
+
2335
+ case 3:
2336
+
2337
+ case 4:
2338
+
2339
+ case 5:
2340
+
2341
+ case 6:
2342
+
2343
+ case 7:
2344
+
2345
+ out += String.fromCharCode(c);
2346
+
2347
+ break;
2348
+
2349
+ case 12:
2350
+
2351
+ case 13:
2352
+
2353
+ char2 = array[i++];
2354
+
2355
+ out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
2356
+
2357
+ break;
2358
+
2359
+ case 14:
2360
+
2361
+ char2 = array[i++];
2362
+
2363
+ char3 = array[i++];
2364
+
2365
+ out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
2366
+
2367
+ break;
2368
+
2369
+ }
2370
+
2371
+ }
2372
+
2373
+ return out;
2374
+
2375
+ }
2376
+
2377
+ return JSON.parse(Utf8ArrayToStr(payload));
2378
+
2379
+ }
2380
+
2381
+ function getAppState(jar) {
2382
+ var appstate = jar.getCookies("https://www.facebook.com").concat(jar.getCookies("https://facebook.com")).concat(jar.getCookies("https://www.messenger.com"));
2383
+ if (global.fca.ObjFcaConfig['encryptSt']) {
2384
+ var StateCrypt = require('./StateCrypt');
2385
+ var logger = require('./logger');
2386
+ logger(global.fca.languages.encAppstate,'[ FCA-JAKE ]');
2387
+ var keyy = process.env['FBKEY'];
2388
+
2389
+ if (process.env['FBKEY']) {
2390
+
2391
+ return StateCrypt.encryptState(JSON.stringify(appstate),process.env['FBKEY']);
2392
+
2393
+ }
2394
+ }
2395
+ else return appstate;
2396
+ }
2397
+
2398
+ module.exports = {
2399
+
2400
+ isReadableStream:isReadableStream,
2401
+
2402
+ get:get,
2403
+
2404
+ post:post,
2405
+
2406
+ postFormData:postFormData,
2407
+
2408
+ generateThreadingID:generateThreadingID,
2409
+
2410
+ generateOfflineThreadingID:generateOfflineThreadingID,
2411
+
2412
+ getGUID:getGUID,
2413
+
2414
+ getFrom:getFrom,
2415
+
2416
+ makeParsable:makeParsable,
2417
+
2418
+ arrToForm:arrToForm,
2419
+
2420
+ getSignatureID:getSignatureID,
2421
+
2422
+ getJar: request.jar,
2423
+
2424
+ generateTimestampRelative:generateTimestampRelative,
2425
+
2426
+ makeDefaults:makeDefaults,
2427
+
2428
+ parseAndCheckLogin:parseAndCheckLogin,
2429
+
2430
+ saveCookies,
2431
+
2432
+ getType,
2433
+
2434
+ _formatAttachment,
2435
+
2436
+ formatHistoryMessage,
2437
+
2438
+ formatID,
2439
+
2440
+ formatMessage,
2441
+
2442
+ formatDeltaEvent,
2443
+
2444
+ formatDeltaMessage,
2445
+
2446
+ formatProxyPresence,
2447
+
2448
+ formatPresence,
2449
+
2450
+ formatTyp,
2451
+
2452
+ formatDeltaReadReceipt,
2453
+
2454
+ formatCookie,
2455
+
2456
+ formatThread,
2457
+
2458
+ formatReadReceipt,
2459
+
2460
+ formatRead,
2461
+
2462
+ generatePresence,
2463
+
2464
+ generateAccessiblityCookie,
2465
+
2466
+ formatDate,
2467
+
2468
+ decodeClientPayload,
2469
+
2470
+ getAppState,
2471
+
2472
+ getAdminTextMessageType,
2473
+
2474
+ setProxy
2475
+
2476
+ };