alicezetion 1.5.3 → 1.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. package/.cache/replit/__replit_disk_meta.json +1 -1
  2. package/.cache/replit/nix/env.json +1 -1
  3. package/index.js +576 -382
  4. package/leiamnash/addExternalModule.js +19 -16
  5. package/leiamnash/addUserToGroup.js +113 -79
  6. package/leiamnash/changeAdminStatus.js +79 -79
  7. package/leiamnash/changeArchivedStatus.js +55 -41
  8. package/leiamnash/changeBio.js +77 -65
  9. package/leiamnash/changeBlockedStatus.js +47 -36
  10. package/leiamnash/changeGroupImage.js +129 -106
  11. package/leiamnash/changeNickname.js +59 -45
  12. package/leiamnash/changeThreadColor.js +71 -62
  13. package/leiamnash/changeThreadEmoji.js +55 -42
  14. package/leiamnash/chat.js +459 -374
  15. package/leiamnash/createNewGroup.js +86 -70
  16. package/leiamnash/createPoll.js +71 -60
  17. package/leiamnash/deleteMessage.js +56 -45
  18. package/leiamnash/deleteThread.js +56 -43
  19. package/leiamnash/forwardAttachment.js +60 -48
  20. package/leiamnash/getCurrentUserID.js +7 -7
  21. package/leiamnash/getEmojiUrl.js +29 -27
  22. package/leiamnash/getFriendsList.js +84 -73
  23. package/leiamnash/getThreadHistory.js +645 -537
  24. package/leiamnash/getThreadHistoryDeprecated.js +93 -0
  25. package/leiamnash/getThreadInfo.js +206 -425
  26. package/leiamnash/getThreadInfoDeprecated.js +80 -0
  27. package/leiamnash/getThreadList.js +238 -213
  28. package/leiamnash/getThreadListDeprecated.js +75 -0
  29. package/leiamnash/getThreadPictures.js +79 -59
  30. package/leiamnash/getUserID.js +66 -62
  31. package/leiamnash/getUserInfo.js +72 -113
  32. package/leiamnash/handleFriendRequest.js +61 -46
  33. package/leiamnash/handleMessageRequest.js +65 -49
  34. package/leiamnash/httpGet.js +52 -49
  35. package/leiamnash/httpPost.js +52 -48
  36. package/leiamnash/listenMqtt.js +789 -787
  37. package/leiamnash/logout.js +75 -68
  38. package/leiamnash/markAsDelivered.js +58 -48
  39. package/leiamnash/markAsRead.js +80 -70
  40. package/leiamnash/markAsReadAll.js +49 -42
  41. package/leiamnash/markAsSeen.js +59 -51
  42. package/leiamnash/muteThread.js +52 -47
  43. package/leiamnash/removeUserFromGroup.js +79 -49
  44. package/leiamnash/resolvePhotoUrl.js +45 -37
  45. package/leiamnash/searchForThread.js +53 -43
  46. package/leiamnash/sendTypingIndicator.js +103 -80
  47. package/leiamnash/setMessageReaction.js +117 -109
  48. package/leiamnash/setPostReaction.js +76 -102
  49. package/leiamnash/setTitle.js +86 -74
  50. package/leiamnash/threadColors.js +57 -39
  51. package/leiamnash/unfriend.js +52 -43
  52. package/leiamnash/unsendMessage.js +49 -40
  53. package/package.json +72 -90
  54. package/utils.js +1356 -1684
  55. package/Extra/Database/index.js +0 -469
  56. package/Extra/ExtraAddons.js +0 -82
  57. package/Extra/ExtraFindUID.js +0 -62
  58. package/Extra/ExtraGetThread.js +0 -340
  59. package/Extra/ExtraScreenShot.js +0 -430
  60. package/Extra/ExtraUptimeRobot.js +0 -38
  61. package/Extra/Html/Classic/script.js +0 -119
  62. package/Extra/Html/Classic/style.css +0 -8
  63. package/Extra/Security/AES_256_GCM/index.js +0 -0
  64. package/Extra/Security/Base/Step_1.js +0 -6
  65. package/Extra/Security/Base/Step_2.js +0 -22
  66. package/Extra/Security/Base/Step_3.js +0 -22
  67. package/Extra/Security/Base/index.js +0 -174
  68. package/Extra/Security/Index.js +0 -5
  69. package/Extra/Security/Step_1.js +0 -6
  70. package/Extra/Security/Step_2.js +0 -22
  71. package/Extra/Security/Step_3.js +0 -22
  72. package/Extra/Src/Change_Environment.js +0 -24
  73. package/Extra/Src/Check_Update.js +0 -67
  74. package/Extra/Src/History.js +0 -115
  75. package/Extra/Src/Instant_Update.js +0 -65
  76. package/Extra/Src/Last-Run.js +0 -65
  77. package/Extra/Src/Premium.js +0 -81
  78. package/Extra/Src/Release_Memory.js +0 -41
  79. package/Extra/Src/Websocket.js +0 -213
  80. package/Extra/Src/image/checkmate.jpg +0 -0
  81. package/Extra/Src/uuid.js +0 -137
  82. package/Func/AcceptAgreement.js +0 -31
  83. package/Func/ClearCache.js +0 -64
  84. package/Func/ReportV1.js +0 -54
  85. package/Language/index.json +0 -217
  86. package/Main.js +0 -1270
  87. package/broadcast.js +0 -40
  88. package/leiamnash/Dev_Horizon_Data.js +0 -125
  89. package/leiamnash/Premium.js +0 -25
  90. package/leiamnash/Screenshot.js +0 -83
  91. package/leiamnash/changeAvt.js +0 -85
  92. package/leiamnash/getAccessToken.js +0 -28
  93. package/leiamnash/getMessage.js +0 -80
  94. package/leiamnash/getThreadMain.js +0 -220
  95. package/leiamnash/getUID.js +0 -59
  96. package/leiamnash/getUserInfoMain.js +0 -65
  97. package/leiamnash/getUserInfoV2.js +0 -32
  98. package/leiamnash/getUserInfoV3.js +0 -63
  99. package/leiamnash/getUserInfoV4.js +0 -55
  100. package/leiamnash/getUserInfoV5.js +0 -61
  101. package/leiamnash/httpPostFormData.js +0 -41
  102. package/leiamnash/sendMessage.js +0 -379
  103. package/logger.js +0 -66
package/utils.js CHANGED
@@ -1,1684 +1,1356 @@
1
- // @ts-nocheck
2
- /* eslint-disable no-undef */
3
-
4
- /* eslint-disable no-prototype-builtins */
5
-
6
- "use strict";
7
- var url = require("url");
8
- var log = require("npmlog");
9
- var stream = require("stream");
10
- var bluebird = require("bluebird");
11
- var querystring = require("querystring");
12
- var request = bluebird.promisify(require("request").defaults({ jar: true }));
13
-
14
- /**
15
- * @param {any} url
16
- */
17
-
18
- function setProxy(url) {
19
- if (typeof url == undefined) return request = bluebird.promisify(require("request").defaults({ jar: true }));
20
- return request = bluebird.promisify(require("request").defaults({ jar: true, proxy: url }));
21
- }
22
-
23
- /**
24
- * @param {string | URL} url
25
- * @param {{ userAgent: any; }} options
26
- * @param {{ region: any; }} [ctx]
27
- * @param {undefined} [customHeader]
28
- */
29
-
30
- function getHeaders(url, options, ctx, customHeader) {
31
- var headers = {
32
- "Content-Type": "application/x-www-form-urlencoded",
33
- Referer: "https://www.facebook.com/",
34
- Host: url.replace("https://", "").split("/")[0],
35
- Origin: "https://www.facebook.com",
36
- "user-agent": (options.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"),
37
- Connection: "keep-alive",
38
- "sec-fetch-site": 'same-origin',
39
- "sec-fetch-mode": 'cors'
40
- };
41
- if (customHeader) Object.assign(headers, customHeader);
42
- if (ctx && ctx.region) headers["X-MSGR-Region"] = ctx.region;
43
-
44
- return headers;
45
- }
46
-
47
- /**
48
- * @param {{ _read: any; _readableState: any; }} obj
49
- */
50
-
51
- function isReadableStream(obj) {
52
- return (
53
- obj instanceof stream.Stream &&
54
- (getType(obj._read) === "Function" ||
55
- getType(obj._read) === "AsyncFunction") &&
56
- getType(obj._readableState) === "Object"
57
- );
58
- }
59
-
60
- /**
61
- * @param {any} url
62
- * @param {any} jar
63
- * @param {{ [x: string]: any; fb_dtsg?: any; jazoest?: any; hasOwnProperty?: any; }} qs
64
- * @param {any} options
65
- * @param {any} ctx
66
- */
67
-
68
- function get(url, jar, qs, options, ctx) {
69
- // I'm still confused about this
70
- if (getType(qs) === "Object")
71
- for (var prop in qs)
72
- if (qs.hasOwnProperty(prop) && getType(qs[prop]) === "Object") qs[prop] = JSON.stringify(qs[prop]);
73
- var op = {
74
- headers: getHeaders(url, options, ctx),
75
- timeout: 60000,
76
- qs: qs,
77
- url: url,
78
- method: "GET",
79
- jar: jar,
80
- gzip: true
81
- };
82
-
83
- return request(op).then(function(res) {
84
- return res;
85
- });
86
- }
87
-
88
- function post(url, jar, form, options, ctx, customHeader) {
89
- var op = {
90
- headers: getHeaders(url, options),
91
- timeout: 60000,
92
- url: url,
93
- method: "POST",
94
- form: form,
95
- jar: jar,
96
- gzip: true
97
- };
98
- return request(op).then(function(res) {
99
- return res;
100
- });
101
- }
102
-
103
- /**
104
- * @param {any} url
105
- * @param {any} jar
106
- * @param {{ __user: any; __req: string; __rev: any; __a: number;
107
- // __af: siteData.features,
108
- fb_dtsg: any; jazoest: any; }} form
109
- * @param {{ __user: any; __req: string; __rev: any; __a: number;
110
- // __af: siteData.features,
111
- fb_dtsg: any; jazoest: any; }} qs
112
- * @param {any} options
113
- * @param {any} ctx
114
- */
115
-
116
- function postFormData(url, jar, form, qs, options, ctx) {
117
- var headers = getHeaders(url, options, ctx);
118
- headers["Content-Type"] = "multipart/form-data";
119
- var op = {
120
- headers: headers,
121
- timeout: 60000,
122
- url: url,
123
- method: "POST",
124
- formData: form,
125
- qs: qs,
126
- jar: jar,
127
- gzip: true
128
- };
129
-
130
- return request(op).then(function(res) {
131
- return res;
132
- });
133
- }
134
-
135
- /**
136
- * @param {string | number | any[]} val
137
- * @param {number} [len]
138
- */
139
-
140
- function padZeros(val, len) {
141
- val = String(val);
142
- len = len || 2;
143
- while (val.length < len) val = "0" + val;
144
- return val;
145
- }
146
-
147
- /**
148
- * @param {any} clientID
149
- */
150
-
151
- function generateThreadingID(clientID) {
152
- var k = Date.now();
153
- var l = Math.floor(Math.random() * 4294967295);
154
- var m = clientID;
155
- return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
156
- }
157
-
158
- /**
159
- * @param {string | any[]} data
160
- */
161
-
162
- function binaryToDecimal(data) {
163
- var ret = "";
164
- while (data !== "0") {
165
- var end = 0;
166
- var fullName = "";
167
- var i = 0;
168
- for (; i < data.length; i++) {
169
- end = 2 * end + parseInt(data[i], 10);
170
- if (end >= 10) {
171
- fullName += "1";
172
- end -= 10;
173
- } else fullName += "0";
174
- }
175
- ret = end.toString() + ret;
176
- data = fullName.slice(fullName.indexOf("1"));
177
- }
178
- return ret;
179
- }
180
-
181
- function generateOfflineThreadingID() {
182
- var ret = Date.now();
183
- var value = Math.floor(Math.random() * 4294967295);
184
- var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
185
- var msgs = ret.toString(2) + str;
186
- return binaryToDecimal(msgs);
187
- }
188
-
189
- var h;
190
- var i = {};
191
- var j = {
192
- _: "%",
193
- A: "%2",
194
- B: "000",
195
- C: "%7d",
196
- D: "%7b%22",
197
- E: "%2c%22",
198
- F: "%22%3a",
199
- G: "%2c%22ut%22%3a1",
200
- H: "%2c%22bls%22%3a",
201
- I: "%2c%22n%22%3a%22%",
202
- J: "%22%3a%7b%22i%22%3a0%7d",
203
- K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
204
- L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
205
- M: "%7b%22v%22%3a2%2c%22time%22%3a1",
206
- N: ".channel%22%2c%22sub%22%3a%5b",
207
- O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
208
- P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
209
- Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
210
- R: ".channel%22%2c%22sub%22%3a%5b1%5d",
211
- S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
212
- T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
213
- U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
214
- V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
215
- W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
216
- X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
217
- 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",
218
- 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"
219
- };
220
- (function() {
221
- var l = [];
222
- for (var m in j) {
223
- i[j[m]] = m;
224
- l.push(j[m]);
225
- }
226
- l.reverse();
227
- h = new RegExp(l.join("|"), "g");
228
- })();
229
-
230
- /**
231
- * @param {string | number | boolean} str
232
- */
233
-
234
- function presenceEncode(str) {
235
- return encodeURIComponent(str)
236
- .replace(/([_A-Z])|%../g, function(m, n) {
237
- return n ? "%" + n.charCodeAt(0).toString(16) : m;
238
- })
239
- .toLowerCase()
240
- .replace(h, function(m) {
241
- return i[m];
242
- });
243
- }
244
-
245
- // eslint-disable-next-line no-unused-vars
246
- /**
247
- * @param {string} str
248
- */
249
-
250
- function presenceDecode(str) {
251
- return decodeURIComponent(
252
- str.replace(/[_A-Z]/g, function(/** @type {string | number} */m) {
253
- return j[m];
254
- })
255
- );
256
- }
257
-
258
- /**
259
- * @param {string} userID
260
- */
261
-
262
- function generatePresence(userID) {
263
- var time = Date.now();
264
- return (
265
- "E" +
266
- presenceEncode(
267
- JSON.stringify({
268
- v: 3,
269
- time: parseInt(time / 1000, 10),
270
- user: userID,
271
- state: {
272
- ut: 0,
273
- t2: [],
274
- lm2: null,
275
- uct2: time,
276
- tr: null,
277
- tw: Math.floor(Math.random() * 4294967295) + 1,
278
- at: time
279
- },
280
- ch: {
281
- ["p_" + userID]: 0
282
- }
283
- })
284
- )
285
- );
286
- }
287
-
288
- function generateAccessiblityCookie() {
289
- var time = Date.now();
290
- return encodeURIComponent(
291
- JSON.stringify({
292
- sr: 0,
293
- "sr-ts": time,
294
- jk: 0,
295
- "jk-ts": time,
296
- kb: 0,
297
- "kb-ts": time,
298
- hcm: 0,
299
- "hcm-ts": time
300
- })
301
- );
302
- }
303
-
304
- function getGUID() {
305
- /** @type {number} */
306
-
307
- var sectionLength = Date.now();
308
- /** @type {string} */
309
-
310
- var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
311
- /** @type {number} */
312
-
313
- var r = Math.floor((sectionLength + Math.random() * 16) % 16);
314
- /** @type {number} */
315
-
316
- sectionLength = Math.floor(sectionLength / 16);
317
- /** @type {string} */
318
-
319
- var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
320
- return _guid;
321
- });
322
- return id;
323
- }
324
-
325
- /**
326
- * @param {{ mercury: any; blob_attachment: any; attach_type: any; sticker_attachment: any; extensible_attachment: { story_attachment: { target: { __typename: string; }; }; }; metadata: { stickerID: { toString: () => any; }; packID: { toString: () => any; }; spriteURI: any; spriteURI2x: any; width: any; height: any; frameCount: any; frameRate: any; framesPerRow: any; framesPerCol: any; fbid: { toString: () => any; }; url: any; dimensions: { split: (arg0: string) => any[]; width: any; height: any; }; duration: any; }; url: any; name: any; fileName: any; thumbnail_url: any; preview_url: any; preview_width: any; preview_height: any; large_preview_url: any; large_preview_width: any; large_preview_height: any; share: { share_id: { toString: () => any; }; title: any; description: any; source: any; media: { image: any; image_size: { width: any; height: any; }; playable: any; duration: any; animated_image_size: any; }; subattachments: any; uri: any; target: any; style_list: any; }; }} attachment1
327
- * @param {{ caption?: any; description?: any; id: any; is_malicious?: any; mime_type?: any; file_size?: any; filename?: any; image_data: any; href?: any; }} [attachment2]
328
- */
329
-
330
- function _formatAttachment(attachment1, attachment2) {
331
- // TODO: THIS IS REALLY BAD
332
- // This is an attempt at fixing Facebook's inconsistencies. Sometimes they give us
333
- // two attachment objects, but sometimes only one. They each contain part of the
334
- // data that you'd want so we merge them for convenience.
335
- // Instead of having a bunch of if statements guarding every access to image_data,
336
- // we set it to empty object and use the fact that it'll return undefined.
337
-
338
- attachment2 = attachment2 || { id: "", image_data: {} };
339
- attachment1 = attachment1.mercury ? attachment1.mercury : attachment1;
340
- var blob = attachment1.blob_attachment;
341
- var type =
342
- blob && blob.__typename ? blob.__typename : attachment1.attach_type;
343
- if (!type && attachment1.sticker_attachment) {
344
- type = "StickerAttachment";
345
- blob = attachment1.sticker_attachment;
346
- } else if (!type && attachment1.extensible_attachment) {
347
- if (
348
- attachment1.extensible_attachment.story_attachment &&
349
- attachment1.extensible_attachment.story_attachment.target &&
350
- attachment1.extensible_attachment.story_attachment.target.__typename &&
351
- attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
352
- ) type = "MessageLocation";
353
- else type = "ExtensibleAttachment";
354
-
355
- blob = attachment1.extensible_attachment;
356
- }
357
- // TODO: Determine whether "sticker", "photo", "file" etc are still used
358
- // KEEP IN SYNC WITH getThreadHistory
359
- switch (type) {
360
- case "sticker":
361
- return {
362
- type: "sticker",
363
- ID: attachment1.metadata.stickerID.toString(),
364
- url: attachment1.url,
365
-
366
- packID: attachment1.metadata.packID.toString(),
367
- spriteUrl: attachment1.metadata.spriteURI,
368
- spriteUrl2x: attachment1.metadata.spriteURI2x,
369
- width: attachment1.metadata.width,
370
- height: attachment1.metadata.height,
371
-
372
- caption: attachment2.caption,
373
- description: attachment2.description,
374
-
375
- frameCount: attachment1.metadata.frameCount,
376
- frameRate: attachment1.metadata.frameRate,
377
- framesPerRow: attachment1.metadata.framesPerRow,
378
- framesPerCol: attachment1.metadata.framesPerCol,
379
-
380
- stickerID: attachment1.metadata.stickerID.toString(), // @Legacy
381
- spriteURI: attachment1.metadata.spriteURI, // @Legacy
382
- spriteURI2x: attachment1.metadata.spriteURI2x // @Legacy
383
- };
384
- case "file":
385
- return {
386
- type: "file",
387
- filename: attachment1.name,
388
- ID: attachment2.id.toString(),
389
- url: attachment1.url,
390
-
391
- isMalicious: attachment2.is_malicious,
392
- contentType: attachment2.mime_type,
393
-
394
- name: attachment1.name, // @Legacy
395
- mimeType: attachment2.mime_type, // @Legacy
396
- fileSize: attachment2.file_size // @Legacy
397
- };
398
- case "photo":
399
- return {
400
- type: "photo",
401
- ID: attachment1.metadata.fbid.toString(),
402
- filename: attachment1.fileName,
403
- thumbnailUrl: attachment1.thumbnail_url,
404
-
405
- previewUrl: attachment1.preview_url,
406
- previewWidth: attachment1.preview_width,
407
- previewHeight: attachment1.preview_height,
408
-
409
- largePreviewUrl: attachment1.large_preview_url,
410
- largePreviewWidth: attachment1.large_preview_width,
411
- largePreviewHeight: attachment1.large_preview_height,
412
-
413
- url: attachment1.metadata.url, // @Legacy
414
- width: attachment1.metadata.dimensions.split(",")[0], // @Legacy
415
- height: attachment1.metadata.dimensions.split(",")[1], // @Legacy
416
- name: attachment1.fileName // @Legacy
417
- };
418
- case "animated_image":
419
- return {
420
- type: "animated_image",
421
- ID: attachment2.id.toString(),
422
- filename: attachment2.filename,
423
-
424
- previewUrl: attachment1.preview_url,
425
- previewWidth: attachment1.preview_width,
426
- previewHeight: attachment1.preview_height,
427
-
428
- url: attachment2.image_data.url,
429
- width: attachment2.image_data.width,
430
- height: attachment2.image_data.height,
431
-
432
- name: attachment1.name, // @Legacy
433
- facebookUrl: attachment1.url, // @Legacy
434
- thumbnailUrl: attachment1.thumbnail_url, // @Legacy
435
- mimeType: attachment2.mime_type, // @Legacy
436
- rawGifImage: attachment2.image_data.raw_gif_image, // @Legacy
437
- rawWebpImage: attachment2.image_data.raw_webp_image, // @Legacy
438
- animatedGifUrl: attachment2.image_data.animated_gif_url, // @Legacy
439
- animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url, // @Legacy
440
- animatedWebpUrl: attachment2.image_data.animated_webp_url, // @Legacy
441
- animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url // @Legacy
442
- };
443
- case "share":
444
- return {
445
- type: "share",
446
- ID: attachment1.share.share_id.toString(),
447
- url: attachment2.href,
448
-
449
- title: attachment1.share.title,
450
- description: attachment1.share.description,
451
- source: attachment1.share.source,
452
-
453
- image: attachment1.share.media.image,
454
- width: attachment1.share.media.image_size.width,
455
- height: attachment1.share.media.image_size.height,
456
- playable: attachment1.share.media.playable,
457
- duration: attachment1.share.media.duration,
458
-
459
- subattachments: attachment1.share.subattachments,
460
- properties: {},
461
-
462
- animatedImageSize: attachment1.share.media.animated_image_size, // @Legacy
463
- facebookUrl: attachment1.share.uri, // @Legacy
464
- target: attachment1.share.target, // @Legacy
465
- styleList: attachment1.share.style_list // @Legacy
466
- };
467
- case "video":
468
- return {
469
- type: "video",
470
- ID: attachment1.metadata.fbid.toString(),
471
- filename: attachment1.name,
472
-
473
- previewUrl: attachment1.preview_url,
474
- previewWidth: attachment1.preview_width,
475
- previewHeight: attachment1.preview_height,
476
-
477
- url: attachment1.url,
478
- width: attachment1.metadata.dimensions.width,
479
- height: attachment1.metadata.dimensions.height,
480
-
481
- duration: attachment1.metadata.duration,
482
- videoType: "unknown",
483
-
484
- thumbnailUrl: attachment1.thumbnail_url // @Legacy
485
- };
486
- case "error":
487
- return {
488
- type: "error",
489
-
490
- // Save error attachments because we're unsure of their format,
491
- // and whether there are cases they contain something useful for debugging.
492
- attachment1: attachment1,
493
- attachment2: attachment2
494
- };
495
- case "MessageImage":
496
- return {
497
- type: "photo",
498
- ID: blob.legacy_attachment_id,
499
- filename: blob.filename,
500
- thumbnailUrl: blob.thumbnail.uri,
501
-
502
- previewUrl: blob.preview.uri,
503
- previewWidth: blob.preview.width,
504
- previewHeight: blob.preview.height,
505
-
506
- largePreviewUrl: blob.large_preview.uri,
507
- largePreviewWidth: blob.large_preview.width,
508
- largePreviewHeight: blob.large_preview.height,
509
-
510
- url: blob.large_preview.uri, // @Legacy
511
- width: blob.original_dimensions.x, // @Legacy
512
- height: blob.original_dimensions.y, // @Legacy
513
- name: blob.filename // @Legacy
514
- };
515
- case "MessageAnimatedImage":
516
- return {
517
- type: "animated_image",
518
- ID: blob.legacy_attachment_id,
519
- filename: blob.filename,
520
-
521
- previewUrl: blob.preview_image.uri,
522
- previewWidth: blob.preview_image.width,
523
- previewHeight: blob.preview_image.height,
524
-
525
- url: blob.animated_image.uri,
526
- width: blob.animated_image.width,
527
- height: blob.animated_image.height,
528
-
529
- thumbnailUrl: blob.preview_image.uri, // @Legacy
530
- name: blob.filename, // @Legacy
531
- facebookUrl: blob.animated_image.uri, // @Legacy
532
- rawGifImage: blob.animated_image.uri, // @Legacy
533
- animatedGifUrl: blob.animated_image.uri, // @Legacy
534
- animatedGifPreviewUrl: blob.preview_image.uri, // @Legacy
535
- animatedWebpUrl: blob.animated_image.uri, // @Legacy
536
- animatedWebpPreviewUrl: blob.preview_image.uri // @Legacy
537
- };
538
- case "MessageVideo":
539
- return {
540
- type: "video",
541
- filename: blob.filename,
542
- ID: blob.legacy_attachment_id,
543
-
544
- previewUrl: blob.large_image.uri,
545
- previewWidth: blob.large_image.width,
546
- previewHeight: blob.large_image.height,
547
-
548
- url: blob.playable_url,
549
- width: blob.original_dimensions.x,
550
- height: blob.original_dimensions.y,
551
-
552
- duration: blob.playable_duration_in_ms,
553
- videoType: blob.video_type.toLowerCase(),
554
-
555
- thumbnailUrl: blob.large_image.uri // @Legacy
556
- };
557
- case "MessageAudio":
558
- return {
559
- type: "audio",
560
- filename: blob.filename,
561
- ID: blob.url_shimhash,
562
-
563
- audioType: blob.audio_type,
564
- duration: blob.playable_duration_in_ms,
565
- url: blob.playable_url,
566
-
567
- isVoiceMail: blob.is_voicemail
568
- };
569
- case "StickerAttachment":
570
- return {
571
- type: "sticker",
572
- ID: blob.id,
573
- url: blob.url,
574
-
575
- packID: blob.pack ? blob.pack.id : null,
576
- spriteUrl: blob.sprite_image,
577
- spriteUrl2x: blob.sprite_image_2x,
578
- width: blob.width,
579
- height: blob.height,
580
-
581
- caption: blob.label,
582
- description: blob.label,
583
-
584
- frameCount: blob.frame_count,
585
- frameRate: blob.frame_rate,
586
- framesPerRow: blob.frames_per_row,
587
- framesPerCol: blob.frames_per_column,
588
-
589
- stickerID: blob.id, // @Legacy
590
- spriteURI: blob.sprite_image, // @Legacy
591
- spriteURI2x: blob.sprite_image_2x // @Legacy
592
- };
593
- case "MessageLocation":
594
- var urlAttach = blob.story_attachment.url;
595
- var mediaAttach = blob.story_attachment.media;
596
-
597
- var u = querystring.parse(url.parse(urlAttach).query).u;
598
- var where1 = querystring.parse(url.parse(u).query).where1;
599
- var address = where1.split(", ");
600
-
601
- var latitude;
602
- var longitude;
603
-
604
- try {
605
- latitude = Number.parseFloat(address[0]);
606
- longitude = Number.parseFloat(address[1]);
607
- } catch (err) {
608
- /* empty */
609
-
610
- }
611
-
612
- var imageUrl;
613
- var width;
614
- var height;
615
-
616
- if (mediaAttach && mediaAttach.image) {
617
- imageUrl = mediaAttach.image.uri;
618
- width = mediaAttach.image.width;
619
- height = mediaAttach.image.height;
620
- }
621
-
622
- return {
623
- type: "location",
624
- ID: blob.legacy_attachment_id,
625
- latitude: latitude,
626
- longitude: longitude,
627
- image: imageUrl,
628
- width: width,
629
- height: height,
630
- url: u || urlAttach,
631
- address: where1,
632
-
633
- facebookUrl: blob.story_attachment.url, // @Legacy
634
- target: blob.story_attachment.target, // @Legacy
635
- styleList: blob.story_attachment.style_list // @Legacy
636
- };
637
- case "ExtensibleAttachment":
638
- return {
639
- type: "share",
640
- ID: blob.legacy_attachment_id,
641
- url: blob.story_attachment.url,
642
-
643
- title: blob.story_attachment.title_with_entities.text,
644
- description: blob.story_attachment.description &&
645
- blob.story_attachment.description.text,
646
- source: blob.story_attachment.source ? blob.story_attachment.source.text : null,
647
-
648
- image: blob.story_attachment.media &&
649
- blob.story_attachment.media.image &&
650
- blob.story_attachment.media.image.uri,
651
- width: blob.story_attachment.media &&
652
- blob.story_attachment.media.image &&
653
- blob.story_attachment.media.image.width,
654
- height: blob.story_attachment.media &&
655
- blob.story_attachment.media.image &&
656
- blob.story_attachment.media.image.height,
657
- playable: blob.story_attachment.media &&
658
- blob.story_attachment.media.is_playable,
659
- duration: blob.story_attachment.media &&
660
- blob.story_attachment.media.playable_duration_in_ms,
661
- playableUrl: blob.story_attachment.media == null ? null : blob.story_attachment.media.playable_url,
662
-
663
- subattachments: blob.story_attachment.subattachments,
664
- properties: blob.story_attachment.properties.reduce(function(/** @type {{ [x: string]: any; }} */obj, /** @type {{ key: string | number; value: { text: any; }; }} */cur) {
665
- obj[cur.key] = cur.value.text;
666
- return obj;
667
- }, {}),
668
-
669
- facebookUrl: blob.story_attachment.url, // @Legacy
670
- target: blob.story_attachment.target, // @Legacy
671
- styleList: blob.story_attachment.style_list // @Legacy
672
- };
673
- case "MessageFile":
674
- return {
675
- type: "file",
676
- filename: blob.filename,
677
- ID: blob.message_file_fbid,
678
-
679
- url: blob.url,
680
- isMalicious: blob.is_malicious,
681
- contentType: blob.content_type,
682
-
683
- name: blob.filename,
684
- mimeType: "",
685
- fileSize: -1
686
- };
687
- default:
688
- throw new Error(
689
- "unrecognized attach_file of type " +
690
- type +
691
- "`" +
692
- JSON.stringify(attachment1, null, 4) +
693
- " attachment2: " +
694
- JSON.stringify(attachment2, null, 4) +
695
- "`"
696
- );
697
- }
698
- }
699
-
700
- /**
701
- * @param {any[]} attachments
702
- * @param {{ [x: string]: string | number; }} attachmentIds
703
- * @param {{ [x: string]: any; }} attachmentMap
704
- * @param {any} shareMap
705
- */
706
-
707
- function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
708
- attachmentMap = shareMap || attachmentMap;
709
- return attachments ?
710
- attachments.map(function(/** @type {any} */val, /** @type {string | number} */i) {
711
- if (!attachmentMap ||
712
- !attachmentIds ||
713
- !attachmentMap[attachmentIds[i]]
714
- ) {
715
- return _formatAttachment(val);
716
- }
717
- return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
718
- }) : [];
719
- }
720
-
721
- /**
722
- * @param {{ delta: { messageMetadata: any; data: { prng: string; }; body: string; attachments: any; participants: any; }; }} m
723
- */
724
-
725
- function formatDeltaMessage(m) {
726
- var md = m.delta.messageMetadata;
727
- var mdata =
728
- m.delta.data === undefined ? [] :
729
- m.delta.data.prng === undefined ? [] :
730
- JSON.parse(m.delta.data.prng);
731
- var m_id = mdata.map((/** @type {{ i: any; }} */u) => u.i);
732
- var m_offset = mdata.map((/** @type {{ o: any; }} */u) => u.o);
733
- var m_length = mdata.map((/** @type {{ l: any; }} */u) => u.l);
734
- var mentions = {};
735
- var body = m.delta.body || "";
736
- var args = body == "" ? [] : body.trim().split(/\s+/);
737
- 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]);
738
-
739
- return {
740
- type: "message",
741
- senderID: formatID(md.actorFbId.toString()),
742
- threadID: formatID((md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()),
743
- messageID: md.messageId,
744
- args: args,
745
- body: body,
746
- attachments: (m.delta.attachments || []).map((/** @type {any} */v) => _formatAttachment(v)),
747
- mentions: mentions,
748
- timestamp: md.timestamp,
749
- isGroup: !!md.threadKey.threadFbId,
750
- participantIDs: m.delta.participants || []
751
- };
752
- }
753
-
754
- /**
755
- * @param {string} id
756
- */
757
-
758
- function formatID(id) {
759
- if (id != undefined && id != null) return id.replace(/(fb)?id[:.]/, "");
760
- else return id;
761
- }
762
-
763
- /**
764
- * @param {{ message: any; type: string; realtime_viewer_fbid: { toString: () => any; }; }} m
765
- */
766
-
767
- function formatMessage(m) {
768
- var originalMessage = m.message ? m.message : m;
769
- var obj = {
770
- type: "message",
771
- senderName: originalMessage.sender_name,
772
- senderID: formatID(originalMessage.sender_fbid.toString()),
773
- participantNames: originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(" ")[0]],
774
- participantIDs: originalMessage.group_thread_info ?
775
- originalMessage.group_thread_info.participant_ids.map(function(/** @type {{ toString: () => any; }} */v) {
776
- return formatID(v.toString());
777
- }) : [formatID(originalMessage.sender_fbid)],
778
- body: originalMessage.body || "",
779
- threadID: formatID((originalMessage.thread_fbid || originalMessage.other_user_fbid).toString()),
780
- threadName: originalMessage.group_thread_info ? originalMessage.group_thread_info.name : originalMessage.sender_name,
781
- location: originalMessage.coordinates ? originalMessage.coordinates : null,
782
- messageID: originalMessage.mid ? originalMessage.mid.toString() : originalMessage.message_id,
783
- attachments: formatAttachment(
784
- originalMessage.attachments,
785
- originalMessage.attachmentIds,
786
- originalMessage.attachment_map,
787
- originalMessage.share_map
788
- ),
789
- timestamp: originalMessage.timestamp,
790
- timestampAbsolute: originalMessage.timestamp_absolute,
791
- timestampRelative: originalMessage.timestamp_relative,
792
- timestampDatetime: originalMessage.timestamp_datetime,
793
- tags: originalMessage.tags,
794
- reactions: originalMessage.reactions ? originalMessage.reactions : [],
795
- isUnread: originalMessage.is_unread
796
- };
797
-
798
- if (m.type === "pages_messaging") obj.pageID = m.realtime_viewer_fbid.toString();
799
- obj.isGroup = obj.participantIDs.length > 2;
800
-
801
- return obj;
802
- }
803
-
804
- /**
805
- * @param {{ message: any; }} m
806
- */
807
-
808
- function formatEvent(m) {
809
- var originalMessage = m.message ? m.message : m;
810
- var logMessageType = originalMessage.log_message_type;
811
- var logMessageData;
812
- if (logMessageType === "log:generic-admin-text") {
813
- logMessageData = originalMessage.log_message_data.untypedData;
814
- logMessageType = getAdminTextMessageType(originalMessage.log_message_data.message_type);
815
- } else logMessageData = originalMessage.log_message_data;
816
-
817
- return Object.assign(formatMessage(originalMessage), {
818
- type: "event",
819
- logMessageType: logMessageType,
820
- logMessageData: logMessageData,
821
- logMessageBody: originalMessage.log_message_body
822
- });
823
- }
824
-
825
- /**
826
- * @param {{ action_type: any; }} m
827
- */
828
-
829
- function formatHistoryMessage(m) {
830
- switch (m.action_type) {
831
- case "ma-type:log-message":
832
- return formatEvent(m);
833
- default:
834
- return formatMessage(m);
835
- }
836
- }
837
-
838
- // Get a more readable message type for AdminTextMessages
839
- /**
840
- * @param {{ type: any; }} m
841
- */
842
-
843
- function getAdminTextMessageType(m) {
844
- switch (m.type) {
845
- case "joinable_group_link_mode_change":
846
- return "log:link-status";
847
- case "magic_words":
848
- return "log:magic-words";
849
- case "change_thread_theme":
850
- return "log:thread-color";
851
- case "change_thread_icon":
852
- return "log:thread-icon";
853
- case "change_thread_nickname":
854
- return "log:user-nickname";
855
- case "change_thread_admins":
856
- return "log:thread-admins";
857
- case "group_poll":
858
- return "log:thread-poll";
859
- case "change_thread_approval_mode":
860
- return "log:thread-approval-mode";
861
- case "messenger_call_log":
862
- case "participant_joined_group_call":
863
- return "log:thread-call";
864
- case "pin_messages_v2":
865
- return "log:thread-pinned";
866
- }
867
- }
868
-
869
- /**
870
- * @param {string} name
871
- */
872
-
873
- function getGenderByPhysicalMethod(name) {
874
- var GirlName = ["LAN", "HÂN", "LINH", "MAI", "HOA", "THU", "BĂNG", "MỸ", "CHÂU", "THẢO", "THOA", "MẪN", "THÙY", "THỦY", "NGA", "NGÂN", "NGHI", "THƯ", "NGỌC", "BÍCH", "VÂN", "DIỆP", "CHI", "TIÊN", "XUÂN", "GIANG", "NHUNG", "DUNG", "NHƯ", "YẾN", "QUYÊN", "YẾN", "TƯỜNG", "VY", "PHƯƠNG", "LIÊN", "LAN", "HÀ", "MAI", "ĐAN", "HẠ", "QUYÊN", "LY", "HÒA", "OANH", "HƯƠNG", "HẰNG", "QUỲNH", "HẠNH", "NHIÊN", "NHẠN"];
875
-
876
- var BoyName = ["HƯNG", "HUY", "KHẢI", "KHANG", "KHOA", "KHÔI", "KIÊN", "KIỆT", "LONG", "MINH", "ÂN", "BẢO", "BÌNH", "CƯỜNG", "ĐẠT", "ĐỨC", "DŨNG", "DUY", "HOÀNG", "HÙNG", "HƯNG", "NGHĨA", "NGUYÊN", "THẮNG", "THIỆN", "THỊNH", "TÒA", "TRIẾT", "TRUNG", "TRƯỜNG", "TUẤN", "NHÂN", "VŨ", "VINH", "PHONG", "PHÚC", "QUÂN", "QUANG", "SƠN", "TÀI", "THẮNG", "ĐĂNG", "VĂN", "VĨ", "QUANG", "MẠNH"];
877
-
878
- var OtherName = ["ANH", "THANH", "TÂM", "DƯƠNG", "AN", "LÂM", "MIÊN", "TÚ", "LÂM", "BẰNG", "KHÁNH", "NHẬT", "VỸ", ".",",","/","%", "&","*","-","+"];
879
-
880
- try {
881
- var NameArray = name.split(" ");
882
- name = NameArray[NameArray.length - 1];
883
- var Name;
884
- if (name == " " || name == null) return "UNKNOWN";
885
- switch (GirlName.includes(name.toUpperCase())) {
886
- case true: {
887
- if (!OtherName.includes(name.toUpperCase()) && !BoyName.includes(name.toUpperCase())) Name = "FEMALE";
888
- else Name = ['FEMALE','MALE'][Math.floor(Math.random() * 2)]; // just temp 🌚
889
- }
890
- break;
891
- case false: {
892
- if (!OtherName.includes(name.toUpperCase()) && !GirlName.includes(name.toUpperCase())) Name = "MALE";
893
- else Name = ['FEMALE','MALE'][Math.floor(Math.random() * 2)]; // just temp 🌚
894
- }
895
- break;
896
- }
897
- }
898
- catch (e) {
899
- return "UNKNOWN";
900
- }
901
- return Name || "UNKNOWN";
902
- }
903
-
904
- /**
905
- * @param {{ [x: string]: { [x: string]: { [x: string]: any; }; }; class: any; untypedData: any; name: any; addedParticipants: any; leftParticipantFbId: any; messageMetadata: { threadKey: { threadFbId: any; otherUserFbId: any; }; adminText: any; actorFbId: any; }; participants: any; }} m
906
- */
907
-
908
- function formatDeltaEvent(m) {
909
- var { updateData,getData,hasData } = require('./Extra/ExtraGetThread');
910
- var logMessageType;
911
- var logMessageData;
912
-
913
- switch (m.class) {
914
- case "AdminTextMessage":
915
- logMessageType = getAdminTextMessageType(m);
916
- logMessageData = m.untypedData;
917
- break;
918
- case "ThreadName":
919
- logMessageType = "log:thread-name";
920
- logMessageData = { name: m.name };
921
- break;
922
- case "ParticipantsAddedToGroupThread":
923
- logMessageType = "log:subscribe";
924
- logMessageData = { addedParticipants: m.addedParticipants };
925
- break;
926
- case "ParticipantLeftGroupThread":
927
- logMessageType = "log:unsubscribe";
928
- logMessageData = { leftParticipantFbId: m.leftParticipantFbId };
929
- break;
930
- case "UserLocation": {
931
- logMessageType = "log:user-location";
932
- logMessageData = {
933
- Image: m.attachments[0].mercury.extensible_attachment.story_attachment.media.image,
934
- Location: m.attachments[0].mercury.extensible_attachment.story_attachment.target.location_title,
935
- coordinates: m.attachments[0].mercury.extensible_attachment.story_attachment.target.coordinate,
936
- url: m.attachments[0].mercury.extensible_attachment.story_attachment.url
937
- };
938
- }
939
- }
940
- switch (hasData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()))) {
941
- case true: {
942
- switch (logMessageType) {
943
- case "log:thread-color": {
944
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
945
- x.emoji = (logMessageData.theme_emoji || x.emoji);
946
- x.color = (logMessageData['theme_color'] || x.color);
947
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
948
- }
949
- break;
950
- case "log:thread-icon": {
951
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
952
- x.emoji = (logMessageData['thread_icon'] || x.emoji);
953
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
954
- }
955
- break;
956
- case "log:user-nickname": {
957
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
958
- x.nicknames[logMessageData.participant_id] = (logMessageData.nickname.length == 0 ? x.userInfo.find(i => i.id == String(logMessageData.participant_id)).name : logMessageData.nickname);
959
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
960
- }
961
- break;
962
- case "log:thread-admins": {
963
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
964
- switch (logMessageData.ADMIN_EVENT) {
965
- case "add_admin": {
966
- x.adminIDs.push({ id: logMessageData.TARGET_ID });
967
- }
968
- break;
969
- case "remove_admin": {
970
- x.adminIDs = x.adminIDs.filter(item => item.id != logMessageData.TARGET_ID);
971
- }
972
- break;
973
- }
974
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
975
- }
976
- break;
977
- case "log:thread-approval-mode": {
978
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
979
- if (x.approvalMode == true) {
980
- x.approvalMode = false;
981
- }
982
- else {
983
- x.approvalMode = true;
984
- }
985
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
986
- }
987
- break;
988
- case "log:thread-name": {
989
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
990
- x.threadName = (logMessageData.name || formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
991
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
992
- }
993
- break;
994
- case "log:subscribe": {
995
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
996
- for (let o of logMessageData.addedParticipants) {
997
- if (x.userInfo.some(i => i.id == o.userFbId)) continue;
998
- else {
999
- x.userInfo.push({
1000
- id: o.userFbId,
1001
- name: o.fullName,
1002
- gender: getGenderByPhysicalMethod(o.fullName)
1003
- });
1004
- x.participantIDs.push(o.userFbId);
1005
- }
1006
- }
1007
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
1008
- }
1009
- break;
1010
- case "log:unsubscribe": {
1011
- let x = getData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()));
1012
- x.participantIDs = x.participantIDs.filter(item => item != logMessageData.leftParticipantFbId);
1013
- x.userInfo = x.userInfo.filter(item => item.id != logMessageData.leftParticipantFbId);
1014
- if (x.adminIDs.some(i => i.id == logMessageData.leftParticipantFbId)) {
1015
- x.adminIDs = x.adminIDs.filter(item => item.id != logMessageData.leftParticipantFbId);
1016
- }
1017
- updateData(formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),x);
1018
- }
1019
- break;
1020
- }
1021
- }
1022
- }
1023
-
1024
- return {
1025
- type: "event",
1026
- threadID: formatID((m.messageMetadata.threadKey.threadFbId || m.messageMetadata.threadKey.otherUserFbId).toString()),
1027
- logMessageType: logMessageType,
1028
- logMessageData: logMessageData,
1029
- logMessageBody: m.messageMetadata.adminText,
1030
- author: m.messageMetadata.actorFbId,
1031
- participantIDs: m.participants || []
1032
- };
1033
- }
1034
-
1035
- /**
1036
- * @param {{ st: any; from: { toString: () => any; }; to: any; thread_fbid: any; hasOwnProperty: (arg0: string) => any; from_mobile: any; realtime_viewer_fbid: any; }} event
1037
- */
1038
-
1039
- function formatTyp(event) {
1040
- return {
1041
- isTyping: !!event.st,
1042
- from: event.from.toString(),
1043
- threadID: formatID((event.to || event.thread_fbid || event.from).toString()),
1044
- // When receiving typ indication from mobile, `from_mobile` isn't set.
1045
- // If it is, we just use that value.
1046
- fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
1047
- userID: (event.realtime_viewer_fbid || event.from).toString(),
1048
- type: "typ"
1049
- };
1050
- }
1051
-
1052
- /**
1053
- * @param {{ threadKey: { otherUserFbId: any; threadFbId: any; }; actorFbId: any; actionTimestampMs: any; }} delta
1054
- */
1055
-
1056
- function formatDeltaReadReceipt(delta) {
1057
- // otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
1058
- // In a group chat actorFbId is used for the reader and threadFbId for the thread.
1059
- return {
1060
- reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
1061
- time: delta.actionTimestampMs,
1062
- threadID: formatID((delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()),
1063
- type: "read_receipt"
1064
- };
1065
- }
1066
-
1067
- /**
1068
- * @param {{ reader: { toString: () => any; }; time: any; thread_fbid: any; }} event
1069
- */
1070
-
1071
- function formatReadReceipt(event) {
1072
- return {
1073
- reader: event.reader.toString(),
1074
- time: event.time,
1075
- threadID: formatID((event.thread_fbid || event.reader).toString()),
1076
- type: "read_receipt"
1077
- };
1078
- }
1079
-
1080
- /**
1081
- * @param {{ chat_ids: any[]; thread_fbids: any[]; timestamp: any; }} event
1082
- */
1083
-
1084
- function formatRead(event) {
1085
- return {
1086
- threadID: formatID(((event.chat_ids && event.chat_ids[0]) || (event.thread_fbids && event.thread_fbids[0])).toString()),
1087
- time: event.timestamp,
1088
- type: "read"
1089
- };
1090
- }
1091
-
1092
- /**
1093
- * @param {string} str
1094
- * @param {string | any[]} startToken
1095
- * @param {string} endToken
1096
- */
1097
-
1098
- function getFrom(str, startToken, endToken) {
1099
- var start = str.indexOf(startToken) + startToken.length;
1100
- if (start < startToken.length) return "";
1101
-
1102
- var lastHalf = str.substring(start);
1103
- var end = lastHalf.indexOf(endToken);
1104
- if (end === -1) throw Error("Could not find endTime `" + endToken + "` in the given string.");
1105
- return lastHalf.substring(0, end);
1106
- }
1107
-
1108
- /**
1109
- * @param {string} html
1110
- */
1111
-
1112
- function makeParsable(html) {
1113
- let withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/
1114
- , "");
1115
-
1116
- // (What the fuck FB, why windows style newlines?)
1117
- // So sometimes FB will send us base multiple objects in the same response.
1118
- // They're all valid JSON, one after the other, at the top level. We detect
1119
- // that and make it parse-able by JSON.parse.
1120
- // Ben - July 15th 2017
1121
- //
1122
- // It turns out that Facebook may insert random number of spaces before
1123
- // next object begins (issue #616)
1124
- // rav_kr - 2018-03-19
1125
- let maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
1126
- if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
1127
-
1128
- return "[" + maybeMultipleObjects.join("},{") + "]";
1129
- }
1130
-
1131
- /**
1132
- * @param {any} form
1133
- */
1134
-
1135
- function arrToForm(form) {
1136
- return arrayToObject(form,
1137
- function(/** @type {{ name: any; }} */v) {
1138
- return v.name;
1139
- },
1140
- function(/** @type {{ val: any; }} */v) {
1141
- return v.val;
1142
- }
1143
- );
1144
- }
1145
-
1146
- /**
1147
- * @param {any[]} arr
1148
- * @param {{ (v: any): any; (arg0: any): string | number; }} getKey
1149
- * @param {{ (v: any): any; (arg0: any): any; }} getValue
1150
- */
1151
-
1152
- function arrayToObject(arr, getKey, getValue) {
1153
- return arr.reduce(function(/** @type {{ [x: string]: any; }} */
1154
- acc, /** @type {any} */val) {
1155
- acc[getKey(val)] = getValue(val);
1156
- return acc;
1157
- }, {});
1158
- }
1159
-
1160
- function getSignatureID() {
1161
- return Math.floor(Math.random() * 2147483648).toString(16);
1162
- }
1163
-
1164
- function generateTimestampRelative() {
1165
- var d = new Date();
1166
- return d.getHours() + ":" + padZeros(d.getMinutes());
1167
- }
1168
-
1169
- /**
1170
- * @param {any} html
1171
- * @param {any} userID
1172
- * @param {{ fb_dtsg: any; ttstamp: any; globalOptions: any; }} ctx
1173
- */
1174
-
1175
- function makeDefaults(html, userID, ctx) {
1176
- var reqCounter = 1;
1177
- var fb_dtsg = getFrom(html, 'name="fb_dtsg" value="', '"');
1178
-
1179
- // @Hack Ok we've done hacky things, this is definitely on top 5.
1180
- // We totally assume the object is flat and try parsing until a }.
1181
- // If it works though it's cool because we get a bunch of extra data things.
1182
- //
1183
- // Update: we don't need this. Leaving it in in case we ever do.
1184
- // Ben - July 15th 2017
1185
-
1186
- // var siteData = getFrom(html, "[\"SiteData\",[],", "},");
1187
- // try {
1188
- // siteData = JSON.parse(siteData + "}");
1189
- // } catch(e) {
1190
- // log.warn("makeDefaults", "Couldn't parse SiteData. Won't have access to some variables.");
1191
- // siteData = {};
1192
- // }
1193
-
1194
- var ttstamp = "2";
1195
- for (var i = 0; i < fb_dtsg.length; i++) ttstamp += fb_dtsg.charCodeAt(i);
1196
- var revision = getFrom(html, 'revision":', ",");
1197
-
1198
- /**
1199
- * @param {{ [x: string]: any; hasOwnProperty: (arg0: string) => any; }} obj
1200
- */
1201
-
1202
- function mergeWithDefaults(obj) {
1203
- // @TODO This is missing a key called __dyn.
1204
- // After some investigation it seems like __dyn is some sort of set that FB
1205
- // calls BitMap. It seems like certain responses have a "define" key in the
1206
- // res.jsmods arrays. I think the code iterates over those and calls `set`
1207
- // on the bitmap for each of those keys. Then it calls
1208
- // bitmap.toCompressedString() which returns what __dyn is.
1209
- //
1210
- // So far the API has been working without this.
1211
- //
1212
- // Ben - July 15th 2017
1213
- var newObj = {
1214
- __user: userID,
1215
- __req: (reqCounter++).toString(36),
1216
- __rev: revision,
1217
- __a: 1,
1218
- // __af: siteData.features,
1219
- fb_dtsg: ctx.fb_dtsg ? ctx.fb_dtsg : fb_dtsg,
1220
- jazoest: ctx.ttstamp ? ctx.ttstamp : ttstamp
1221
- // __spin_r: siteData.__spin_r,
1222
- // __spin_b: siteData.__spin_b,
1223
- // __spin_t: siteData.__spin_t,
1224
- };
1225
-
1226
- // @TODO this is probably not needed.
1227
- // Ben - July 15th 2017
1228
- // if (siteData.be_key) {
1229
- // newObj[siteData.be_key] = siteData.be_mode;
1230
- // }
1231
- // if (siteData.pkg_cohort_key) {
1232
- // newObj[siteData.pkg_cohort_key] = siteData.pkg_cohort;
1233
- // }
1234
-
1235
- if (!obj) return newObj;
1236
- for (var prop in obj)
1237
- if (obj.hasOwnProperty(prop))
1238
- if (!newObj[prop]) newObj[prop] = obj[prop];
1239
- return newObj;
1240
- }
1241
-
1242
- /**
1243
- * @param {any} url
1244
- * @param {any} jar
1245
- * @param {any} form
1246
- * @param {any} ctxx
1247
- */
1248
-
1249
- function postWithDefaults(url, jar, form, ctxx) {
1250
- return post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx);
1251
- }
1252
-
1253
- /**
1254
- * @param {any} url
1255
- * @param {any} jar
1256
- * @param {any} qs
1257
- * @param {any} ctxx
1258
- */
1259
-
1260
- function getWithDefaults(url, jar, qs, ctxx) {
1261
- return get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
1262
- }
1263
-
1264
- /**
1265
- * @param {any} url
1266
- * @param {any} jar
1267
- * @param {any} form
1268
- * @param {any} qs
1269
- * @param {any} ctxx
1270
- */
1271
-
1272
- function postFormDataWithDefault(url, jar, form, qs, ctxx) {
1273
- return postFormData(url, jar, mergeWithDefaults(form), mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
1274
- }
1275
-
1276
- return {
1277
- get: getWithDefaults,
1278
- post: postWithDefaults,
1279
- postFormData: postFormDataWithDefault
1280
- };
1281
- }
1282
-
1283
- /**
1284
- * @param {{ jar: { setCookie: (arg0: string, arg1: string) => void; }; fb_dtsg: string; ttstamp: string; }} ctx
1285
- * @param {{ postFormData: (arg0: string, arg1: any, arg2: any, arg3: {}) => any; post: (arg0: string, arg1: any, arg2: any) => any; get: (arg0: any, arg1: any) => Promise<any>; }} defaultFuncs
1286
- * @param {string | number} [retryCount]
1287
- */
1288
-
1289
- function parseAndCheckLogin(ctx, defaultFuncs, retryCount) {
1290
- if (retryCount == undefined) retryCount = 0;
1291
- return function(/** @type {{ body: string; statusCode: string | number; request: { uri: { protocol: string; hostname: string; pathname: string; }; headers: { [x: string]: string; }; formData: any; method: string; }; }} */data) {
1292
- return bluebird.try(function() {
1293
- log.verbose("parseAndCheckLogin", data.body);
1294
- if (data.statusCode >= 500 && data.statusCode < 600) {
1295
- if (retryCount >= 5) {
1296
- throw {
1297
- error: "Request retry failed. Check the `res` and `statusCode` property on this error.",
1298
- statusCode: data.statusCode,
1299
- res: data.body
1300
- };
1301
- }
1302
- retryCount++;
1303
- var retryTime = Math.floor(Math.random() * 5000);
1304
- log.warn("parseAndCheckLogin", "Got status code " + data.statusCode + " - " + retryCount + ". attempt to retry in " + retryTime + " milliseconds...");
1305
- var url = data.request.uri.protocol + "//" + data.request.uri.hostname + data.request.uri.pathname;
1306
- if (data.request.headers["Content-Type"].split(";")[0] === "multipart/form-data") {
1307
- return bluebird.delay(retryTime).then(() => defaultFuncs.postFormData(url, ctx.jar, data.request.formData, {}))
1308
- .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
1309
- } else {
1310
- return bluebird.delay(retryTime).then(() => defaultFuncs.post(url, ctx.jar, data.request.formData))
1311
- .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
1312
- }
1313
- }
1314
- if (data.statusCode !== 200) throw new Error("parseAndCheckLogin got status code: " + data.statusCode + ". Bailing out of trying to parse response.");
1315
-
1316
- var res = null;
1317
- try {
1318
- res = JSON.parse(makeParsable(data.body));
1319
- } catch (e) {
1320
- throw {
1321
- error: "JSON.parse error. Check the `detail` property on this error.",
1322
- detail: e,
1323
- res: data.body
1324
- };
1325
- }
1326
-
1327
- // In some cases the response contains only a redirect URL which should be followed
1328
- if (res.redirect && data.request.method === "GET") return defaultFuncs.get(res.redirect, ctx.jar).then(parseAndCheckLogin(ctx, defaultFuncs));
1329
-
1330
- // TODO: handle multiple cookies?
1331
- if (res.jsmods && res.jsmods.require && Array.isArray(res.jsmods.require[0]) && res.jsmods.require[0][0] === "Cookie") {
1332
- res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace("_js_", "");
1333
- var cookie = formatCookie(res.jsmods.require[0][3], "facebook");
1334
- var cookie2 = formatCookie(res.jsmods.require[0][3], "messenger");
1335
- ctx.jar.setCookie(cookie, "https://www.facebook.com");
1336
- ctx.jar.setCookie(cookie2, "https://www.messenger.com");
1337
- }
1338
-
1339
- // On every request we check if we got a DTSG and we mutate the context so that we use the latest
1340
- // one for the next requests.
1341
- if (res.jsmods && Array.isArray(res.jsmods.require)) {
1342
- var arr = res.jsmods.require;
1343
- for (var i in arr) {
1344
- if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
1345
- ctx.fb_dtsg = arr[i][3][0];
1346
-
1347
- // Update ttstamp since that depends on fb_dtsg
1348
- ctx.ttstamp = "2";
1349
- for (var j = 0; j < ctx.fb_dtsg.length; j++) ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
1350
- }
1351
- }
1352
- }
1353
-
1354
- if (res.error === 1357001) {
1355
- if (global.Fca.Require.FastConfig.AutoLogin) {
1356
- return global.Fca.Require.logger.Warning(global.Fca.Require.Language.Index.AutoLogin, function() {
1357
- return global.Fca.Action('AutoLogin');
1358
- });
1359
- }
1360
- else if (!global.Fca.Require.FastConfig.AutoLogin) {
1361
- return global.Fca.Require.logger.Error(global.Fca.Require.Language.Index.ErrAppState);
1362
- }
1363
- return;
1364
- }
1365
- else return res;
1366
- });
1367
- };
1368
- }
1369
-
1370
- /**
1371
- * @param {{ setCookie: (arg0: any, arg1: string) => void; }} jar
1372
- */
1373
-
1374
- function saveCookies(jar) {
1375
- return function(/** @type {{ headers: { [x: string]: any[]; }; }} */res) {
1376
- /*
1377
- var cookies = res.headers["set-cookie"] || [];
1378
- cookies.forEach(function(/** @type {string} c) { /*c
1379
- if (c.indexOf(".facebook.com") > -1) { // yo wtf is this?
1380
- jar.setCookie(c, "https://www.facebook.com");
1381
- jar.setCookie(c.replace(/domain=\.facebook\.com/, "domain=.messenger.com"), "https://www.messenger.com");
1382
- }
1383
- });*/
1384
- return res;
1385
- };
1386
- }
1387
-
1388
- var NUM_TO_MONTH = [
1389
- "Jan",
1390
- "Feb",
1391
- "Mar",
1392
- "Apr",
1393
- "May",
1394
- "Jun",
1395
- "Jul",
1396
- "Aug",
1397
- "Sep",
1398
- "Oct",
1399
- "Nov",
1400
- "Dec"
1401
- ];
1402
- var NUM_TO_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1403
-
1404
- /**
1405
- * @param {{ getUTCDate: () => any; getUTCHours: () => any; getUTCMinutes: () => any; getUTCSeconds: () => any; getUTCDay: () => string | number; getUTCMonth: () => string | number; getUTCFullYear: () => string; }} date
1406
- */
1407
-
1408
- function formatDate(date) {
1409
- var d = date.getUTCDate();
1410
- d = d >= 10 ? d : "0" + d;
1411
- var h = date.getUTCHours();
1412
- h = h >= 10 ? h : "0" + h;
1413
- var m = date.getUTCMinutes();
1414
- m = m >= 10 ? m : "0" + m;
1415
- var s = date.getUTCSeconds();
1416
- s = s >= 10 ? s : "0" + s;
1417
- return (NUM_TO_DAY[date.getUTCDay()] + ", " + d + " " + NUM_TO_MONTH[date.getUTCMonth()] + " " + date.getUTCFullYear() + " " + h + ":" + m + ":" + s + " GMT");
1418
- }
1419
-
1420
- /**
1421
- * @param {string[]} arr
1422
- * @param {string} url
1423
- */
1424
-
1425
- function formatCookie(arr, url) {
1426
- return arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com";
1427
- }
1428
-
1429
- /**
1430
- * @param {{ thread_fbid: { toString: () => any; }; participants: any[]; name: any; custom_nickname: any; snippet: any; snippet_attachments: any; snippet_sender: any; unread_count: any; message_count: any; image_src: any; timestamp: any; mute_until: any; is_canonical_user: any; is_canonical: any; is_subscribed: any; folder: any; is_archived: any; recipients_loadable: any; has_email_participant: any; read_only: any; can_reply: any; cannot_reply_reason: any; last_message_timestamp: any; last_read_timestamp: any; last_message_type: any; custom_like_icon: any; custom_color: any; admin_ids: any; thread_type: any; }} data
1431
- */
1432
-
1433
- function formatThread(data) {
1434
- return {
1435
- threadID: formatID(data.thread_fbid.toString()),
1436
- participants: data.participants.map(formatID),
1437
- participantIDs: data.participants.map(formatID),
1438
- name: data.name,
1439
- nicknames: data.custom_nickname,
1440
- snippet: data.snippet,
1441
- snippetAttachments: data.snippet_attachments,
1442
- snippetSender: formatID((data.snippet_sender || "").toString()),
1443
- unreadCount: data.unread_count,
1444
- messageCount: data.message_count,
1445
- imageSrc: data.image_src,
1446
- timestamp: data.timestamp,
1447
- muteUntil: data.mute_until,
1448
- isCanonicalUser: data.is_canonical_user,
1449
- isCanonical: data.is_canonical,
1450
- isSubscribed: data.is_subscribed,
1451
- folder: data.folder,
1452
- isArchived: data.is_archived,
1453
- recipientsLoadable: data.recipients_loadable,
1454
- hasEmailParticipant: data.has_email_participant,
1455
- readOnly: data.read_only,
1456
- canReply: data.can_reply,
1457
- cannotReplyReason: data.cannot_reply_reason,
1458
- lastMessageTimestamp: data.last_message_timestamp,
1459
- lastReadTimestamp: data.last_read_timestamp,
1460
- lastMessageType: data.last_message_type,
1461
- emoji: data.custom_like_icon,
1462
- color: data.custom_color,
1463
- adminIDs: data.admin_ids,
1464
- threadType: data.thread_type
1465
- };
1466
- }
1467
-
1468
- /**
1469
- * @param {any} obj
1470
- */
1471
-
1472
- function getType(obj) {
1473
- return Object.prototype.toString.call(obj).slice(8, -1);
1474
- }
1475
-
1476
- /**
1477
- * @param {{ lat: number; p: any; }} presence
1478
- * @param {any} userID
1479
- */
1480
-
1481
- function formatProxyPresence(presence, userID) {
1482
- if (presence.lat === undefined || presence.p === undefined) return null;
1483
- return {
1484
- type: "presence",
1485
- timestamp: presence.lat * 1000,
1486
- userID: userID || '',
1487
- statuses: presence.p
1488
- };
1489
- }
1490
-
1491
- /**
1492
- * @param {{ la: number; a: any; }} presence
1493
- * @param {any} userID
1494
- */
1495
-
1496
- function formatPresence(presence, userID) {
1497
- return {
1498
- type: "presence",
1499
- timestamp: presence.la * 1000,
1500
- userID: userID || '',
1501
- statuses: presence.a
1502
- };
1503
- }
1504
-
1505
- /**
1506
- * @param {any} payload
1507
- */
1508
-
1509
- function decodeClientPayload(payload) {
1510
- /*
1511
- Special function which Client using to "encode" clients JSON payload
1512
- */
1513
-
1514
- /**
1515
- * @param {string | any[]} array
1516
- */
1517
-
1518
- function Utf8ArrayToStr(array) {
1519
- var out, i, len, c;
1520
- var char2, char3;
1521
- out = "";
1522
- len = array.length;
1523
- i = 0;
1524
- while (i < len) {
1525
- c = array[i++];
1526
- switch (c >> 4) {
1527
- case 0:
1528
- case 1:
1529
- case 2:
1530
- case 3:
1531
- case 4:
1532
- case 5:
1533
- case 6:
1534
- case 7:
1535
- out += String.fromCharCode(c);
1536
- break;
1537
- case 12:
1538
- case 13:
1539
- char2 = array[i++];
1540
- out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
1541
- break;
1542
- case 14:
1543
- char2 = array[i++];
1544
- char3 = array[i++];
1545
- out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
1546
- break;
1547
- }
1548
- }
1549
- return out;
1550
- }
1551
- return JSON.parse(Utf8ArrayToStr(payload));
1552
- }
1553
-
1554
- /**
1555
- * @param {{ getCookies: (arg0: string) => string | any[]; }} jar
1556
- */
1557
-
1558
- function getAppState(jar, Encode) {
1559
- var prettyMilliseconds = require('pretty-ms');
1560
- var getText = globalThis.Fca.getText;
1561
- var Security = require("./Extra/Security/Base");
1562
- var appstate = jar.getCookies("https://www.facebook.com").concat(jar.getCookies("https://facebook.com")).concat(jar.getCookies("https://www.messenger.com"));
1563
- var logger = require('./logger'),languageFile = require('./Language/index.json');
1564
- var Language = languageFile.find(i => i.Language == globalThis.Fca.Require.FastConfig.Language).Folder.Index;
1565
- var data;
1566
- switch (require(process.cwd() + "/alice/database/alicization.json").EncryptFeature) {
1567
- case true: {
1568
- if (Encode == undefined) Encode = true;
1569
- if (process.env['FBKEY'] != undefined && Encode) {
1570
- logger.Normal(Language.EncryptSuccess);
1571
- data = Security(JSON.stringify(appstate),process.env['FBKEY'],"Encrypt");
1572
- }
1573
- else return appstate;
1574
- }
1575
- break;
1576
- case false: {
1577
- data = appstate;
1578
- }
1579
- break;
1580
- default: {
1581
- logger.Normal(getText(Language.IsNotABoolean,require(process.cwd() + "/alice/database/alicization.json").EncryptFeature));
1582
- data = appstate;
1583
- }
1584
- }
1585
- if(!globalThis.Fca.Setting.get('getAppState')) {
1586
- logger.Normal(getText(Language.ProcessDone,`${prettyMilliseconds(Date.now() - globalThis.Fca.startTime)}`),function() { globalThis.Fca.Setting.set('getAppState',true); });
1587
- }
1588
- return data;
1589
- }
1590
-
1591
- function getData_Path(Obj , Arr, Stt) {
1592
- //default stt = 0
1593
- if (Arr.length === 0 && Obj != undefined) {
1594
- return Obj; //object
1595
- }
1596
- else if (Obj == undefined) {
1597
- return Stt;
1598
- }
1599
- const head = Arr[0];
1600
- if (head == undefined) {
1601
- return Stt;
1602
- }
1603
- const tail = Arr.slice(1);
1604
- return getData_Path(Obj[head], tail, Stt++);
1605
- }
1606
-
1607
-
1608
- function setData_Path(obj, path, value) {
1609
- if (!path.length) {
1610
- return obj;
1611
- }
1612
- const currentKey = path[0];
1613
- let currentObj = obj[currentKey];
1614
-
1615
- if (!currentObj) {
1616
- obj[currentKey] = value;
1617
- currentObj = obj[currentKey];
1618
- }
1619
- path.shift();
1620
- if (!path.length) {
1621
- currentObj = value;
1622
- } else {
1623
- currentObj = setData_Path(currentObj, path, value);
1624
- }
1625
-
1626
- return obj;
1627
- }
1628
-
1629
- function getPaths(obj, parentPath = []) {
1630
- let paths = [];
1631
- for (let prop in obj) {
1632
- if (typeof obj[prop] === "object") {
1633
- paths = paths.concat(getPaths(obj[prop], [...parentPath, prop]));
1634
- } else {
1635
- paths.push([...parentPath, prop]);
1636
- }
1637
- }
1638
- return paths;
1639
- }
1640
-
1641
- module.exports = {
1642
- isReadableStream:isReadableStream,
1643
- get:get,
1644
- post:post,
1645
- postFormData:postFormData,
1646
- generateThreadingID:generateThreadingID,
1647
- generateOfflineThreadingID:generateOfflineThreadingID,
1648
- getGUID:getGUID,
1649
- getFrom:getFrom,
1650
- makeParsable:makeParsable,
1651
- arrToForm:arrToForm,
1652
- getSignatureID:getSignatureID,
1653
- getJar: request.jar,
1654
- generateTimestampRelative:generateTimestampRelative,
1655
- makeDefaults:makeDefaults,
1656
- parseAndCheckLogin:parseAndCheckLogin,
1657
- getGender: getGenderByPhysicalMethod,
1658
- getData_Path,
1659
- setData_Path,
1660
- getPaths,
1661
- saveCookies,
1662
- getType,
1663
- _formatAttachment,
1664
- formatHistoryMessage,
1665
- formatID,
1666
- formatMessage,
1667
- formatDeltaEvent,
1668
- formatDeltaMessage,
1669
- formatProxyPresence,
1670
- formatPresence,
1671
- formatTyp,
1672
- formatDeltaReadReceipt,
1673
- formatCookie,
1674
- formatThread,
1675
- formatReadReceipt,
1676
- formatRead,
1677
- generatePresence,
1678
- generateAccessiblityCookie,
1679
- formatDate,
1680
- decodeClientPayload,
1681
- getAppState,
1682
- getAdminTextMessageType,
1683
- setProxy
1684
- };
1
+ "use strict";
2
+
3
+ var bluebird = require("bluebird");
4
+ var request = bluebird.promisify(require("request").defaults({ jar: true }));
5
+ var stream = require("stream");
6
+ var log = require("npmlog");
7
+ var querystring = require("querystring");
8
+ var url = require("url");
9
+
10
+ function setProxy(url) {
11
+ if (typeof url == undefined)
12
+ return request = bluebird.promisify(require("request").defaults({
13
+ jar: true,
14
+ }));
15
+ return request = bluebird.promisify(require("request").defaults({
16
+ jar: true,
17
+ proxy: url
18
+ }));
19
+ }
20
+
21
+ function getHeaders(url, options, ctx, customHeader) {
22
+ var headers = {
23
+ "Content-Type": "application/x-www-form-urlencoded",
24
+ Referer: "https://www.facebook.com/",
25
+ Host: url.replace("https://", "").split("/")[0],
26
+ Origin: "https://www.facebook.com",
27
+ "User-Agent": options.userAgent,
28
+ Connection: "keep-alive"
29
+ };
30
+ if (customHeader) {
31
+ Object.assign(headers, customHeader);
32
+ }
33
+ if (ctx && ctx.region) {
34
+ headers["X-MSGR-Region"] = ctx.region;
35
+ }
36
+
37
+ return headers;
38
+ }
39
+
40
+ function isReadableStream(obj) {
41
+ return (
42
+ obj instanceof stream.Stream &&
43
+ (getType(obj._read) === "Function" ||
44
+ getType(obj._read) === "AsyncFunction") &&
45
+ getType(obj._readableState) === "Object"
46
+ );
47
+ }
48
+
49
+ function get(url, jar, qs, options, ctx) {
50
+ // I'm still confused about this
51
+ if (getType(qs) === "Object") {
52
+ for (var prop in qs) {
53
+ if (qs.hasOwnProperty(prop) && getType(qs[prop]) === "Object") {
54
+ qs[prop] = JSON.stringify(qs[prop]);
55
+ }
56
+ }
57
+ }
58
+ var op = {
59
+ headers: getHeaders(url, options, ctx),
60
+ timeout: 60000,
61
+ qs: qs,
62
+ url: url,
63
+ method: "GET",
64
+ jar: jar,
65
+ gzip: true
66
+ };
67
+
68
+ return request(op).then(function (res) {
69
+ return res[0];
70
+ });
71
+ }
72
+
73
+ function post(url, jar, form, options, ctx, customHeader) {
74
+ var op = {
75
+ headers: getHeaders(url, options, ctx, customHeader),
76
+ timeout: 60000,
77
+ url: url,
78
+ method: "POST",
79
+ form: form,
80
+ jar: jar,
81
+ gzip: true
82
+ };
83
+
84
+ return request(op).then(function (res) {
85
+ return res[0];
86
+ });
87
+ }
88
+
89
+ function postFormData(url, jar, form, qs, options, ctx) {
90
+ var headers = getHeaders(url, options, ctx);
91
+ headers["Content-Type"] = "multipart/form-data";
92
+ var op = {
93
+ headers: headers,
94
+ timeout: 60000,
95
+ url: url,
96
+ method: "POST",
97
+ formData: form,
98
+ qs: qs,
99
+ jar: jar,
100
+ gzip: true
101
+ };
102
+
103
+ return request(op).then(function (res) {
104
+ return res[0];
105
+ });
106
+ }
107
+
108
+ function padZeros(val, len) {
109
+ val = String(val);
110
+ len = len || 2;
111
+ while (val.length < len) val = "0" + val;
112
+ return val;
113
+ }
114
+
115
+ function generateThreadingID(clientID) {
116
+ var k = Date.now();
117
+ var l = Math.floor(Math.random() * 4294967295);
118
+ var m = clientID;
119
+ return "<" + k + ":" + l + "-" + m + "@mail.projektitan.com>";
120
+ }
121
+
122
+ function binaryToDecimal(data) {
123
+ var ret = "";
124
+ while (data !== "0") {
125
+ var end = 0;
126
+ var fullName = "";
127
+ var i = 0;
128
+ for (; i < data.length; i++) {
129
+ end = 2 * end + parseInt(data[i], 10);
130
+ if (end >= 10) {
131
+ fullName += "1";
132
+ end -= 10;
133
+ } else {
134
+ fullName += "0";
135
+ }
136
+ }
137
+ ret = end.toString() + ret;
138
+ data = fullName.slice(fullName.indexOf("1"));
139
+ }
140
+ return ret;
141
+ }
142
+
143
+ function generateOfflineThreadingID() {
144
+ var ret = Date.now();
145
+ var value = Math.floor(Math.random() * 4294967295);
146
+ var str = ("0000000000000000000000" + value.toString(2)).slice(-22);
147
+ var msgs = ret.toString(2) + str;
148
+ return binaryToDecimal(msgs);
149
+ }
150
+
151
+ var h;
152
+ var i = {};
153
+ var j = {
154
+ _: "%",
155
+ A: "%2",
156
+ B: "000",
157
+ C: "%7d",
158
+ D: "%7b%22",
159
+ E: "%2c%22",
160
+ F: "%22%3a",
161
+ G: "%2c%22ut%22%3a1",
162
+ H: "%2c%22bls%22%3a",
163
+ I: "%2c%22n%22%3a%22%",
164
+ J: "%22%3a%7b%22i%22%3a0%7d",
165
+ K: "%2c%22pt%22%3a0%2c%22vis%22%3a",
166
+ L: "%2c%22ch%22%3a%7b%22h%22%3a%22",
167
+ M: "%7b%22v%22%3a2%2c%22time%22%3a1",
168
+ N: ".channel%22%2c%22sub%22%3a%5b",
169
+ O: "%2c%22sb%22%3a1%2c%22t%22%3a%5b",
170
+ P: "%2c%22ud%22%3a100%2c%22lc%22%3a0",
171
+ Q: "%5d%2c%22f%22%3anull%2c%22uct%22%3a",
172
+ R: ".channel%22%2c%22sub%22%3a%5b1%5d",
173
+ S: "%22%2c%22m%22%3a0%7d%2c%7b%22i%22%3a",
174
+ T: "%2c%22blc%22%3a1%2c%22snd%22%3a1%2c%22ct%22%3a",
175
+ U: "%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
176
+ V: "%2c%22blc%22%3a0%2c%22snd%22%3a0%2c%22ct%22%3a",
177
+ W: "%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a",
178
+ X: "%2c%22ri%22%3a0%7d%2c%22state%22%3a%7b%22p%22%3a0%2c%22ut%22%3a1",
179
+ Y:
180
+ "%2c%22pt%22%3a0%2c%22vis%22%3a1%2c%22bls%22%3a0%2c%22blc%22%3a0%2c%22snd%22%3a1%2c%22ct%22%3a",
181
+ Z:
182
+ "%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"
183
+ };
184
+ (function () {
185
+ var l = [];
186
+ for (var m in j) {
187
+ i[j[m]] = m;
188
+ l.push(j[m]);
189
+ }
190
+ l.reverse();
191
+ h = new RegExp(l.join("|"), "g");
192
+ })();
193
+
194
+ function presenceEncode(str) {
195
+ return encodeURIComponent(str)
196
+ .replace(/([_A-Z])|%../g, function (m, n) {
197
+ return n ? "%" + n.charCodeAt(0).toString(16) : m;
198
+ })
199
+ .toLowerCase()
200
+ .replace(h, function (m) {
201
+ return i[m];
202
+ });
203
+ }
204
+
205
+ // eslint-disable-next-line no-unused-vars
206
+ function presenceDecode(str) {
207
+ return decodeURIComponent(
208
+ str.replace(/[_A-Z]/g, function (m) {
209
+ return j[m];
210
+ })
211
+ );
212
+ }
213
+
214
+ function generatePresence(userID) {
215
+ var time = Date.now();
216
+ return (
217
+ "E" +
218
+ presenceEncode(
219
+ JSON.stringify({
220
+ v: 3,
221
+ time: parseInt(time / 1000, 10),
222
+ user: userID,
223
+ state: {
224
+ ut: 0,
225
+ t2: [],
226
+ lm2: null,
227
+ uct2: time,
228
+ tr: null,
229
+ tw: Math.floor(Math.random() * 4294967295) + 1,
230
+ at: time
231
+ },
232
+ ch: {
233
+ ["p_" + userID]: 0
234
+ }
235
+ })
236
+ )
237
+ );
238
+ }
239
+
240
+ function generateAccessiblityCookie() {
241
+ var time = Date.now();
242
+ return encodeURIComponent(
243
+ JSON.stringify({
244
+ sr: 0,
245
+ "sr-ts": time,
246
+ jk: 0,
247
+ "jk-ts": time,
248
+ kb: 0,
249
+ "kb-ts": time,
250
+ hcm: 0,
251
+ "hcm-ts": time
252
+ })
253
+ );
254
+ }
255
+
256
+ function getGUID() {
257
+ /** @type {number} */
258
+ var sectionLength = Date.now();
259
+ /** @type {string} */
260
+ var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
261
+ /** @type {number} */
262
+ var r = Math.floor((sectionLength + Math.random() * 16) % 16);
263
+ /** @type {number} */
264
+ sectionLength = Math.floor(sectionLength / 16);
265
+ /** @type {string} */
266
+ var _guid = (c == "x" ? r : (r & 7) | 8).toString(16);
267
+ return _guid;
268
+ });
269
+ return id;
270
+ }
271
+
272
+ function _formatAttachment(attachment1, attachment2) {
273
+ // TODO: THIS IS REALLY BAD
274
+ // This is an attempt at fixing Facebook's inconsistencies. Sometimes they give us
275
+ // two attachment objects, but sometimes only one. They each contain part of the
276
+ // data that you'd want so we merge them for convenience.
277
+ // Instead of having a bunch of if statements guarding every access to image_data,
278
+ // we set it to empty object and use the fact that it'll return undefined.
279
+ attachment2 = attachment2 || { id: "", image_data: {} };
280
+ attachment1 = attachment1.mercury ? attachment1.mercury : attachment1;
281
+ var blob = attachment1.blob_attachment;
282
+ var type =
283
+ blob && blob.__typename ? blob.__typename : attachment1.attach_type;
284
+ if (!type && attachment1.sticker_attachment) {
285
+ type = "StickerAttachment";
286
+ blob = attachment1.sticker_attachment;
287
+ } else if (!type && attachment1.extensible_attachment) {
288
+ if (
289
+ attachment1.extensible_attachment.story_attachment &&
290
+ attachment1.extensible_attachment.story_attachment.target &&
291
+ attachment1.extensible_attachment.story_attachment.target.__typename &&
292
+ attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
293
+ ) {
294
+ type = "MessageLocation";
295
+ } else {
296
+ type = "ExtensibleAttachment";
297
+ }
298
+
299
+ blob = attachment1.extensible_attachment;
300
+ }
301
+ // TODO: Determine whether "sticker", "photo", "file" etc are still used
302
+ // KEEP IN SYNC WITH getThreadHistory
303
+ switch (type) {
304
+ case "sticker":
305
+ return {
306
+ type: "sticker",
307
+ ID: attachment1.metadata.stickerID.toString(),
308
+ url: attachment1.url,
309
+
310
+ packID: attachment1.metadata.packID.toString(),
311
+ spriteUrl: attachment1.metadata.spriteURI,
312
+ spriteUrl2x: attachment1.metadata.spriteURI2x,
313
+ width: attachment1.metadata.width,
314
+ height: attachment1.metadata.height,
315
+
316
+ caption: attachment2.caption,
317
+ description: attachment2.description,
318
+
319
+ frameCount: attachment1.metadata.frameCount,
320
+ frameRate: attachment1.metadata.frameRate,
321
+ framesPerRow: attachment1.metadata.framesPerRow,
322
+ framesPerCol: attachment1.metadata.framesPerCol,
323
+
324
+ stickerID: attachment1.metadata.stickerID.toString(), // @Legacy
325
+ spriteURI: attachment1.metadata.spriteURI, // @Legacy
326
+ spriteURI2x: attachment1.metadata.spriteURI2x // @Legacy
327
+ };
328
+ case "file":
329
+ return {
330
+ type: "file",
331
+ filename: attachment1.name,
332
+ ID: attachment2.id.toString(),
333
+ url: attachment1.url,
334
+
335
+ isMalicious: attachment2.is_malicious,
336
+ contentType: attachment2.mime_type,
337
+
338
+ name: attachment1.name, // @Legacy
339
+ mimeType: attachment2.mime_type, // @Legacy
340
+ fileSize: attachment2.file_size // @Legacy
341
+ };
342
+ case "photo":
343
+ return {
344
+ type: "photo",
345
+ ID: attachment1.metadata.fbid.toString(),
346
+ filename: attachment1.fileName,
347
+ thumbnailUrl: attachment1.thumbnail_url,
348
+
349
+ previewUrl: attachment1.preview_url,
350
+ previewWidth: attachment1.preview_width,
351
+ previewHeight: attachment1.preview_height,
352
+
353
+ largePreviewUrl: attachment1.large_preview_url,
354
+ largePreviewWidth: attachment1.large_preview_width,
355
+ largePreviewHeight: attachment1.large_preview_height,
356
+
357
+ url: attachment1.metadata.url, // @Legacy
358
+ width: attachment1.metadata.dimensions.split(",")[0], // @Legacy
359
+ height: attachment1.metadata.dimensions.split(",")[1], // @Legacy
360
+ name: attachment1.fileName // @Legacy
361
+ };
362
+ case "animated_image":
363
+ return {
364
+ type: "animated_image",
365
+ ID: attachment2.id.toString(),
366
+ filename: attachment2.filename,
367
+
368
+ previewUrl: attachment1.preview_url,
369
+ previewWidth: attachment1.preview_width,
370
+ previewHeight: attachment1.preview_height,
371
+
372
+ url: attachment2.image_data.url,
373
+ width: attachment2.image_data.width,
374
+ height: attachment2.image_data.height,
375
+
376
+ name: attachment1.name, // @Legacy
377
+ facebookUrl: attachment1.url, // @Legacy
378
+ thumbnailUrl: attachment1.thumbnail_url, // @Legacy
379
+ mimeType: attachment2.mime_type, // @Legacy
380
+ rawGifImage: attachment2.image_data.raw_gif_image, // @Legacy
381
+ rawWebpImage: attachment2.image_data.raw_webp_image, // @Legacy
382
+ animatedGifUrl: attachment2.image_data.animated_gif_url, // @Legacy
383
+ animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url, // @Legacy
384
+ animatedWebpUrl: attachment2.image_data.animated_webp_url, // @Legacy
385
+ animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url // @Legacy
386
+ };
387
+ case "share":
388
+ return {
389
+ type: "share",
390
+ ID: attachment1.share.share_id.toString(),
391
+ url: attachment2.href,
392
+
393
+ title: attachment1.share.title,
394
+ description: attachment1.share.description,
395
+ source: attachment1.share.source,
396
+
397
+ image: attachment1.share.media.image,
398
+ width: attachment1.share.media.image_size.width,
399
+ height: attachment1.share.media.image_size.height,
400
+ playable: attachment1.share.media.playable,
401
+ duration: attachment1.share.media.duration,
402
+
403
+ subattachments: attachment1.share.subattachments,
404
+ properties: {},
405
+
406
+ animatedImageSize: attachment1.share.media.animated_image_size, // @Legacy
407
+ facebookUrl: attachment1.share.uri, // @Legacy
408
+ target: attachment1.share.target, // @Legacy
409
+ styleList: attachment1.share.style_list // @Legacy
410
+ };
411
+ case "video":
412
+ return {
413
+ type: "video",
414
+ ID: attachment1.metadata.fbid.toString(),
415
+ filename: attachment1.name,
416
+
417
+ previewUrl: attachment1.preview_url,
418
+ previewWidth: attachment1.preview_width,
419
+ previewHeight: attachment1.preview_height,
420
+
421
+ url: attachment1.url,
422
+ width: attachment1.metadata.dimensions.width,
423
+ height: attachment1.metadata.dimensions.height,
424
+
425
+ duration: attachment1.metadata.duration,
426
+ videoType: "unknown",
427
+
428
+ thumbnailUrl: attachment1.thumbnail_url // @Legacy
429
+ };
430
+ case "error":
431
+ return {
432
+ type: "error",
433
+
434
+ // Save error attachments because we're unsure of their format,
435
+ // and whether there are cases they contain something useful for debugging.
436
+ attachment1: attachment1,
437
+ attachment2: attachment2
438
+ };
439
+ case "MessageImage":
440
+ return {
441
+ type: "photo",
442
+ ID: blob.legacy_attachment_id,
443
+ filename: blob.filename,
444
+ thumbnailUrl: blob.thumbnail.uri,
445
+
446
+ previewUrl: blob.preview.uri,
447
+ previewWidth: blob.preview.width,
448
+ previewHeight: blob.preview.height,
449
+
450
+ largePreviewUrl: blob.large_preview.uri,
451
+ largePreviewWidth: blob.large_preview.width,
452
+ largePreviewHeight: blob.large_preview.height,
453
+
454
+ url: blob.large_preview.uri, // @Legacy
455
+ width: blob.original_dimensions.x, // @Legacy
456
+ height: blob.original_dimensions.y, // @Legacy
457
+ name: blob.filename // @Legacy
458
+ };
459
+ case "MessageAnimatedImage":
460
+ return {
461
+ type: "animated_image",
462
+ ID: blob.legacy_attachment_id,
463
+ filename: blob.filename,
464
+
465
+ previewUrl: blob.preview_image.uri,
466
+ previewWidth: blob.preview_image.width,
467
+ previewHeight: blob.preview_image.height,
468
+
469
+ url: blob.animated_image.uri,
470
+ width: blob.animated_image.width,
471
+ height: blob.animated_image.height,
472
+
473
+ thumbnailUrl: blob.preview_image.uri, // @Legacy
474
+ name: blob.filename, // @Legacy
475
+ facebookUrl: blob.animated_image.uri, // @Legacy
476
+ rawGifImage: blob.animated_image.uri, // @Legacy
477
+ animatedGifUrl: blob.animated_image.uri, // @Legacy
478
+ animatedGifPreviewUrl: blob.preview_image.uri, // @Legacy
479
+ animatedWebpUrl: blob.animated_image.uri, // @Legacy
480
+ animatedWebpPreviewUrl: blob.preview_image.uri // @Legacy
481
+ };
482
+ case "MessageVideo":
483
+ return {
484
+ type: "video",
485
+ filename: blob.filename,
486
+ ID: blob.legacy_attachment_id,
487
+
488
+ previewUrl: blob.large_image.uri,
489
+ previewWidth: blob.large_image.width,
490
+ previewHeight: blob.large_image.height,
491
+
492
+ url: blob.playable_url,
493
+ width: blob.original_dimensions.x,
494
+ height: blob.original_dimensions.y,
495
+
496
+ duration: blob.playable_duration_in_ms,
497
+ videoType: blob.video_type.toLowerCase(),
498
+
499
+ thumbnailUrl: blob.large_image.uri // @Legacy
500
+ };
501
+ case "MessageAudio":
502
+ return {
503
+ type: "audio",
504
+ filename: blob.filename,
505
+ ID: blob.url_shimhash,
506
+
507
+ audioType: blob.audio_type,
508
+ duration: blob.playable_duration_in_ms,
509
+ url: blob.playable_url,
510
+
511
+ isVoiceMail: blob.is_voicemail
512
+ };
513
+ case "StickerAttachment":
514
+ return {
515
+ type: "sticker",
516
+ ID: blob.id,
517
+ url: blob.url,
518
+
519
+ packID: blob.pack.id,
520
+ spriteUrl: blob.sprite_image,
521
+ spriteUrl2x: blob.sprite_image_2x,
522
+ width: blob.width,
523
+ height: blob.height,
524
+
525
+ caption: blob.label,
526
+ description: blob.label,
527
+
528
+ frameCount: blob.frame_count,
529
+ frameRate: blob.frame_rate,
530
+ framesPerRow: blob.frames_per_row,
531
+ framesPerCol: blob.frames_per_column,
532
+
533
+ stickerID: blob.id, // @Legacy
534
+ spriteURI: blob.sprite_image, // @Legacy
535
+ spriteURI2x: blob.sprite_image_2x // @Legacy
536
+ };
537
+ case "MessageLocation":
538
+ var urlAttach = blob.story_attachment.url;
539
+ var mediaAttach = blob.story_attachment.media;
540
+
541
+ var u = querystring.parse(url.parse(urlAttach).query).u;
542
+ var where1 = querystring.parse(url.parse(u).query).where1;
543
+ var address = where1.split(", ");
544
+
545
+ var latitude;
546
+ var longitude;
547
+
548
+ try {
549
+ latitude = Number.parseFloat(address[0]);
550
+ longitude = Number.parseFloat(address[1]);
551
+ } catch (err) {
552
+ /* empty */
553
+ }
554
+
555
+ var imageUrl;
556
+ var width;
557
+ var height;
558
+
559
+ if (mediaAttach && mediaAttach.image) {
560
+ imageUrl = mediaAttach.image.uri;
561
+ width = mediaAttach.image.width;
562
+ height = mediaAttach.image.height;
563
+ }
564
+
565
+ return {
566
+ type: "location",
567
+ ID: blob.legacy_attachment_id,
568
+ latitude: latitude,
569
+ longitude: longitude,
570
+ image: imageUrl,
571
+ width: width,
572
+ height: height,
573
+ url: u || urlAttach,
574
+ address: where1,
575
+
576
+ facebookUrl: blob.story_attachment.url, // @Legacy
577
+ target: blob.story_attachment.target, // @Legacy
578
+ styleList: blob.story_attachment.style_list // @Legacy
579
+ };
580
+ case "ExtensibleAttachment":
581
+ return {
582
+ type: "share",
583
+ ID: blob.legacy_attachment_id,
584
+ url: blob.story_attachment.url,
585
+
586
+ title: blob.story_attachment.title_with_entities.text,
587
+ description:
588
+ blob.story_attachment.description &&
589
+ blob.story_attachment.description.text,
590
+ source: blob.story_attachment.source
591
+ ? blob.story_attachment.source.text
592
+ : null,
593
+
594
+ image:
595
+ blob.story_attachment.media &&
596
+ blob.story_attachment.media.image &&
597
+ blob.story_attachment.media.image.uri,
598
+ width:
599
+ blob.story_attachment.media &&
600
+ blob.story_attachment.media.image &&
601
+ blob.story_attachment.media.image.width,
602
+ height:
603
+ blob.story_attachment.media &&
604
+ blob.story_attachment.media.image &&
605
+ blob.story_attachment.media.image.height,
606
+ playable:
607
+ blob.story_attachment.media &&
608
+ blob.story_attachment.media.is_playable,
609
+ duration:
610
+ blob.story_attachment.media &&
611
+ blob.story_attachment.media.playable_duration_in_ms,
612
+ playableUrl:
613
+ blob.story_attachment.media == null
614
+ ? null
615
+ : blob.story_attachment.media.playable_url,
616
+
617
+ subattachments: blob.story_attachment.subattachments,
618
+ properties: blob.story_attachment.properties.reduce(function (obj, cur) {
619
+ obj[cur.key] = cur.value.text;
620
+ return obj;
621
+ }, {}),
622
+
623
+ facebookUrl: blob.story_attachment.url, // @Legacy
624
+ target: blob.story_attachment.target, // @Legacy
625
+ styleList: blob.story_attachment.style_list // @Legacy
626
+ };
627
+ case "MessageFile":
628
+ return {
629
+ type: "file",
630
+ filename: blob.filename,
631
+ ID: blob.message_file_fbid,
632
+
633
+ url: blob.url,
634
+ isMalicious: blob.is_malicious,
635
+ contentType: blob.content_type,
636
+
637
+ name: blob.filename,
638
+ mimeType: "",
639
+ fileSize: -1
640
+ };
641
+ default:
642
+ throw new Error(
643
+ "unrecognized attach_file of type " +
644
+ type +
645
+ "`" +
646
+ JSON.stringify(attachment1, null, 4) +
647
+ " attachment2: " +
648
+ JSON.stringify(attachment2, null, 4) +
649
+ "`"
650
+ );
651
+ }
652
+ }
653
+
654
+ function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
655
+ attachmentMap = shareMap || attachmentMap;
656
+ return attachments
657
+ ? attachments.map(function (val, i) {
658
+ if (
659
+ !attachmentMap ||
660
+ !attachmentIds ||
661
+ !attachmentMap[attachmentIds[i]]
662
+ ) {
663
+ return _formatAttachment(val);
664
+ }
665
+ return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
666
+ })
667
+ : [];
668
+ }
669
+
670
+ function formatDeltaMessage(m) {
671
+ var md = m.delta.messageMetadata;
672
+
673
+ var mdata =
674
+ m.delta.data === undefined
675
+ ? []
676
+ : m.delta.data.prng === undefined
677
+ ? []
678
+ : JSON.parse(m.delta.data.prng);
679
+ var m_id = mdata.map(u => u.i);
680
+ var m_offset = mdata.map(u => u.o);
681
+ var m_length = mdata.map(u => u.l);
682
+ var mentions = {};
683
+ for (var i = 0; i < m_id.length; i++) {
684
+ mentions[m_id[i]] = m.delta.body.substring(
685
+ m_offset[i],
686
+ m_offset[i] + m_length[i]
687
+ );
688
+ }
689
+
690
+ return {
691
+ type: "message",
692
+ senderID: formatID(md.actorFbId.toString()),
693
+ body: m.delta.body || "",
694
+ threadID: formatID(
695
+ (md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()
696
+ ),
697
+ messageID: md.messageId,
698
+ attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
699
+ mentions: mentions,
700
+ timestamp: md.timestamp,
701
+ isGroup: !!md.threadKey.threadFbId
702
+ };
703
+ }
704
+
705
+ function formatID(id) {
706
+ if (id != undefined && id != null) {
707
+ return id.replace(/(fb)?id[:.]/, "");
708
+ } else {
709
+ return id;
710
+ }
711
+ }
712
+
713
+ function formatMessage(m) {
714
+ var originalMessage = m.message ? m.message : m;
715
+ var obj = {
716
+ type: "message",
717
+ senderName: originalMessage.sender_name,
718
+ senderID: formatID(originalMessage.sender_fbid.toString()),
719
+ participantNames: originalMessage.group_thread_info
720
+ ? originalMessage.group_thread_info.participant_names
721
+ : [originalMessage.sender_name.split(" ")[0]],
722
+ participantIDs: originalMessage.group_thread_info
723
+ ? originalMessage.group_thread_info.participant_ids.map(function (v) {
724
+ return formatID(v.toString());
725
+ })
726
+ : [formatID(originalMessage.sender_fbid)],
727
+ body: originalMessage.body || "",
728
+ threadID: formatID(
729
+ (
730
+ originalMessage.thread_fbid || originalMessage.other_user_fbid
731
+ ).toString()
732
+ ),
733
+ threadName: originalMessage.group_thread_info
734
+ ? originalMessage.group_thread_info.name
735
+ : originalMessage.sender_name,
736
+ location: originalMessage.coordinates ? originalMessage.coordinates : null,
737
+ messageID: originalMessage.mid
738
+ ? originalMessage.mid.toString()
739
+ : originalMessage.message_id,
740
+ attachments: formatAttachment(
741
+ originalMessage.attachments,
742
+ originalMessage.attachmentIds,
743
+ originalMessage.attachment_map,
744
+ originalMessage.share_map
745
+ ),
746
+ timestamp: originalMessage.timestamp,
747
+ timestampAbsolute: originalMessage.timestamp_absolute,
748
+ timestampRelative: originalMessage.timestamp_relative,
749
+ timestampDatetime: originalMessage.timestamp_datetime,
750
+ tags: originalMessage.tags,
751
+ reactions: originalMessage.reactions ? originalMessage.reactions : [],
752
+ isUnread: originalMessage.is_unread
753
+ };
754
+
755
+ if (m.type === "pages_messaging")
756
+ obj.pageID = m.realtime_viewer_fbid.toString();
757
+ obj.isGroup = obj.participantIDs.length > 2;
758
+
759
+ return obj;
760
+ }
761
+
762
+ function formatEvent(m) {
763
+ var originalMessage = m.message ? m.message : m;
764
+ var logMessageType = originalMessage.log_message_type;
765
+ var logMessageData;
766
+ if (logMessageType === "log:generic-admin-text") {
767
+ logMessageData = originalMessage.log_message_data.untypedData;
768
+ logMessageType = getAdminTextMessageType(
769
+ originalMessage.log_message_data.message_type
770
+ );
771
+ } else {
772
+ logMessageData = originalMessage.log_message_data;
773
+ }
774
+
775
+ return Object.assign(formatMessage(originalMessage), {
776
+ type: "event",
777
+ logMessageType: logMessageType,
778
+ logMessageData: logMessageData,
779
+ logMessageBody: originalMessage.log_message_body
780
+ });
781
+ }
782
+
783
+ function formatHistoryMessage(m) {
784
+ switch (m.action_type) {
785
+ case "ma-type:log-message":
786
+ return formatEvent(m);
787
+ default:
788
+ return formatMessage(m);
789
+ }
790
+ }
791
+
792
+ // Get a more readable message type for AdminTextMessages
793
+ function getAdminTextMessageType(type) {
794
+ switch (type) {
795
+ case "change_thread_theme":
796
+ return "log:thread-color";
797
+ case "change_thread_nickname":
798
+ return "log:user-nickname";
799
+ case "change_thread_icon":
800
+ return "log:thread-icon";
801
+ default:
802
+ return type;
803
+ }
804
+ }
805
+
806
+ function formatDeltaEvent(m) {
807
+ var logMessageType;
808
+ var logMessageData;
809
+
810
+ // log:thread-color => {theme_color}
811
+ // log:user-nickname => {participant_id, nickname}
812
+ // log:thread-icon => {thread_icon}
813
+ // log:thread-name => {name}
814
+ // log:subscribe => {addedParticipants - [Array]}
815
+ // log:unsubscribe => {leftParticipantFbId}
816
+
817
+ switch (m.class) {
818
+ case "AdminTextMessage":
819
+ logMessageData = m.untypedData;
820
+ logMessageType = getAdminTextMessageType(m.type);
821
+ break;
822
+ case "ThreadName":
823
+ logMessageType = "log:thread-name";
824
+ logMessageData = { name: m.name };
825
+ break;
826
+ case "ParticipantsAddedToGroupThread":
827
+ logMessageType = "log:subscribe";
828
+ logMessageData = { addedParticipants: m.addedParticipants };
829
+ break;
830
+ case "ParticipantLeftGroupThread":
831
+ logMessageType = "log:unsubscribe";
832
+ logMessageData = { leftParticipantFbId: m.leftParticipantFbId };
833
+ break;
834
+ }
835
+
836
+ return {
837
+ type: "event",
838
+ threadID: formatID(
839
+ (
840
+ m.messageMetadata.threadKey.threadFbId ||
841
+ m.messageMetadata.threadKey.otherUserFbId
842
+ ).toString()
843
+ ),
844
+ logMessageType: logMessageType,
845
+ logMessageData: logMessageData,
846
+ logMessageBody: m.messageMetadata.adminText,
847
+ author: m.messageMetadata.actorFbId
848
+ };
849
+ }
850
+
851
+ function formatTyp(event) {
852
+ return {
853
+ isTyping: !!event.st,
854
+ from: event.from.toString(),
855
+ threadID: formatID(
856
+ (event.to || event.thread_fbid || event.from).toString()
857
+ ),
858
+ // When receiving typ indication from mobile, `from_mobile` isn't set.
859
+ // If it is, we just use that value.
860
+ fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
861
+ userID: (event.realtime_viewer_fbid || event.from).toString(),
862
+ type: "typ"
863
+ };
864
+ }
865
+
866
+ function formatDeltaReadReceipt(delta) {
867
+ // otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
868
+ // In a group chat actorFbId is used for the reader and threadFbId for the thread.
869
+ return {
870
+ reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
871
+ time: delta.actionTimestampMs,
872
+ threadID: formatID(
873
+ (delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()
874
+ ),
875
+ type: "read_receipt"
876
+ };
877
+ }
878
+
879
+ function formatReadReceipt(event) {
880
+ return {
881
+ reader: event.reader.toString(),
882
+ time: event.time,
883
+ threadID: formatID((event.thread_fbid || event.reader).toString()),
884
+ type: "read_receipt"
885
+ };
886
+ }
887
+
888
+ function formatRead(event) {
889
+ return {
890
+ threadID: formatID(
891
+ (
892
+ (event.chat_ids && event.chat_ids[0]) ||
893
+ (event.thread_fbids && event.thread_fbids[0])
894
+ ).toString()
895
+ ),
896
+ time: event.timestamp,
897
+ type: "read"
898
+ };
899
+ }
900
+
901
+ function getFrom(str, startToken, endToken) {
902
+ var start = str.indexOf(startToken) + startToken.length;
903
+ if (start < startToken.length) return "";
904
+
905
+ var lastHalf = str.substring(start);
906
+ var end = lastHalf.indexOf(endToken);
907
+ if (end === -1) {
908
+ throw Error(
909
+ "Could not find endTime `" + endToken + "` in the given string."
910
+ );
911
+ }
912
+ return lastHalf.substring(0, end);
913
+ }
914
+
915
+ function makeParsable(html) {
916
+ let withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
917
+
918
+ // (What the fuck FB, why windows style newlines?)
919
+ // So sometimes FB will send us base multiple objects in the same response.
920
+ // They're all valid JSON, one after the other, at the top level. We detect
921
+ // that and make it parse-able by JSON.parse.
922
+ // Ben - July 15th 2017
923
+ //
924
+ // It turns out that Facebook may insert random number of spaces before
925
+ // next object begins (issue #616)
926
+ // rav_kr - 2018-03-19
927
+ let maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
928
+ if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
929
+
930
+ return "[" + maybeMultipleObjects.join("},{") + "]";
931
+ }
932
+
933
+ function arrToForm(form) {
934
+ return arrayToObject(
935
+ form,
936
+ function (v) {
937
+ return v.name;
938
+ },
939
+ function (v) {
940
+ return v.val;
941
+ }
942
+ );
943
+ }
944
+
945
+ function arrayToObject(arr, getKey, getValue) {
946
+ return arr.reduce(function (acc, val) {
947
+ acc[getKey(val)] = getValue(val);
948
+ return acc;
949
+ }, {});
950
+ }
951
+
952
+ function getSignatureID() {
953
+ return Math.floor(Math.random() * 2147483648).toString(16);
954
+ }
955
+
956
+ function generateTimestampRelative() {
957
+ var d = new Date();
958
+ return d.getHours() + ":" + padZeros(d.getMinutes());
959
+ }
960
+
961
+ function makeDefaults(html, userID, ctx) {
962
+ var reqCounter = 1;
963
+ var fb_dtsg = getFrom(html, 'name="fb_dtsg" value="', '"');
964
+
965
+ // @Hack Ok we've done hacky things, this is definitely on top 5.
966
+ // We totally assume the object is flat and try parsing until a }.
967
+ // If it works though it's cool because we get a bunch of extra data things.
968
+ //
969
+ // Update: we don't need this. Leaving it in in case we ever do.
970
+ // Ben - July 15th 2017
971
+
972
+ // var siteData = getFrom(html, "[\"SiteData\",[],", "},");
973
+ // try {
974
+ // siteData = JSON.parse(siteData + "}");
975
+ // } catch(e) {
976
+ // log.warn("makeDefaults", "Couldn't parse SiteData. Won't have access to some variables.");
977
+ // siteData = {};
978
+ // }
979
+
980
+ var ttstamp = "2";
981
+ for (var i = 0; i < fb_dtsg.length; i++) {
982
+ ttstamp += fb_dtsg.charCodeAt(i);
983
+ }
984
+ var revision = getFrom(html, 'revision":', ",");
985
+
986
+ function mergeWithDefaults(obj) {
987
+ // @TODO This is missing a key called __dyn.
988
+ // After some investigation it seems like __dyn is some sort of set that FB
989
+ // calls BitMap. It seems like certain responses have a "define" key in the
990
+ // res.jsmods arrays. I think the code iterates over those and calls `set`
991
+ // on the bitmap for each of those keys. Then it calls
992
+ // bitmap.toCompressedString() which returns what __dyn is.
993
+ //
994
+ // So far the API has been working without this.
995
+ //
996
+ // Ben - July 15th 2017
997
+ var newObj = {
998
+ __user: userID,
999
+ __req: (reqCounter++).toString(36),
1000
+ __rev: revision,
1001
+ __a: 1,
1002
+ // __af: siteData.features,
1003
+ fb_dtsg: ctx.fb_dtsg ? ctx.fb_dtsg : fb_dtsg,
1004
+ jazoest: ctx.ttstamp ? ctx.ttstamp : ttstamp
1005
+ // __spin_r: siteData.__spin_r,
1006
+ // __spin_b: siteData.__spin_b,
1007
+ // __spin_t: siteData.__spin_t,
1008
+ };
1009
+
1010
+ // @TODO this is probably not needed.
1011
+ // Ben - July 15th 2017
1012
+ // if (siteData.be_key) {
1013
+ // newObj[siteData.be_key] = siteData.be_mode;
1014
+ // }
1015
+ // if (siteData.pkg_cohort_key) {
1016
+ // newObj[siteData.pkg_cohort_key] = siteData.pkg_cohort;
1017
+ // }
1018
+
1019
+ if (!obj) return newObj;
1020
+
1021
+ for (var prop in obj) {
1022
+ if (obj.hasOwnProperty(prop)) {
1023
+ if (!newObj[prop]) {
1024
+ newObj[prop] = obj[prop];
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ return newObj;
1030
+ }
1031
+
1032
+ function postWithDefaults(url, jar, form, ctxx) {
1033
+ return post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx);
1034
+ }
1035
+
1036
+ function getWithDefaults(url, jar, qs, ctxx) {
1037
+ return get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx);
1038
+ }
1039
+
1040
+ function postFormDataWithDefault(url, jar, form, qs, ctxx) {
1041
+ return postFormData(
1042
+ url,
1043
+ jar,
1044
+ mergeWithDefaults(form),
1045
+ mergeWithDefaults(qs),
1046
+ ctx.globalOptions,
1047
+ ctxx || ctx
1048
+ );
1049
+ }
1050
+
1051
+ return {
1052
+ get: getWithDefaults,
1053
+ post: postWithDefaults,
1054
+ postFormData: postFormDataWithDefault
1055
+ };
1056
+ }
1057
+
1058
+ function parseAndCheckLogin(ctx, defaultFuncs, retryCount) {
1059
+ if (retryCount == undefined) {
1060
+ retryCount = 0;
1061
+ }
1062
+ return function (data) {
1063
+ return bluebird.try(function () {
1064
+ log.verbose("parseAndCheckLogin", data.body);
1065
+ if (data.statusCode >= 500 && data.statusCode < 600) {
1066
+ if (retryCount >= 5) {
1067
+ throw {
1068
+ error:
1069
+ "Request retry failed. Check the `res` and `statusCode` property on this error.",
1070
+ statusCode: data.statusCode,
1071
+ res: data.body
1072
+ };
1073
+ }
1074
+ retryCount++;
1075
+ var retryTime = Math.floor(Math.random() * 5000);
1076
+ log.warn(
1077
+ "parseAndCheckLogin",
1078
+ "Got status code " +
1079
+ data.statusCode +
1080
+ " - " +
1081
+ retryCount +
1082
+ ". attempt to retry in " +
1083
+ retryTime +
1084
+ " milliseconds..."
1085
+ );
1086
+ var url =
1087
+ data.request.uri.protocol +
1088
+ "//" +
1089
+ data.request.uri.hostname +
1090
+ data.request.uri.pathname;
1091
+ if (
1092
+ data.request.headers["Content-Type"].split(";")[0] ===
1093
+ "multipart/form-data"
1094
+ ) {
1095
+ return bluebird
1096
+ .delay(retryTime)
1097
+ .then(function () {
1098
+ return defaultFuncs.postFormData(
1099
+ url,
1100
+ ctx.jar,
1101
+ data.request.formData,
1102
+ {}
1103
+ );
1104
+ })
1105
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
1106
+ } else {
1107
+ return bluebird
1108
+ .delay(retryTime)
1109
+ .then(function () {
1110
+ return defaultFuncs.post(url, ctx.jar, data.request.formData);
1111
+ })
1112
+ .then(parseAndCheckLogin(ctx, defaultFuncs, retryCount));
1113
+ }
1114
+ }
1115
+ if (data.statusCode !== 200)
1116
+ throw new Error(
1117
+ "parseAndCheckLogin got status code: " +
1118
+ data.statusCode +
1119
+ ". Bailing out of trying to parse response."
1120
+ );
1121
+
1122
+ var res = null;
1123
+ try {
1124
+ res = JSON.parse(makeParsable(data.body));
1125
+ } catch (e) {
1126
+ throw {
1127
+ error: "JSON.parse error. Check the `detail` property on this error.",
1128
+ detail: e,
1129
+ res: data.body
1130
+ };
1131
+ }
1132
+
1133
+ // In some cases the response contains only a redirect URL which should be followed
1134
+ if (res.redirect && data.request.method === "GET") {
1135
+ return defaultFuncs
1136
+ .get(res.redirect, ctx.jar)
1137
+ .then(parseAndCheckLogin(ctx, defaultFuncs));
1138
+ }
1139
+
1140
+ // TODO: handle multiple cookies?
1141
+ if (
1142
+ res.jsmods &&
1143
+ res.jsmods.require &&
1144
+ Array.isArray(res.jsmods.require[0]) &&
1145
+ res.jsmods.require[0][0] === "Cookie"
1146
+ ) {
1147
+ res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace(
1148
+ "_js_",
1149
+ ""
1150
+ );
1151
+ var cookie = formatCookie(res.jsmods.require[0][3], "facebook");
1152
+ var cookie2 = formatCookie(res.jsmods.require[0][3], "messenger");
1153
+ ctx.jar.setCookie(cookie, "https://www.facebook.com");
1154
+ ctx.jar.setCookie(cookie2, "https://www.messenger.com");
1155
+ }
1156
+
1157
+ // On every request we check if we got a DTSG and we mutate the context so that we use the latest
1158
+ // one for the next requests.
1159
+ if (res.jsmods && Array.isArray(res.jsmods.require)) {
1160
+ var arr = res.jsmods.require;
1161
+ for (var i in arr) {
1162
+ if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
1163
+ ctx.fb_dtsg = arr[i][3][0];
1164
+
1165
+ // Update ttstamp since that depends on fb_dtsg
1166
+ ctx.ttstamp = "2";
1167
+ for (var j = 0; j < ctx.fb_dtsg.length; j++) {
1168
+ ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
1169
+ }
1170
+ }
1171
+ }
1172
+ }
1173
+
1174
+ if (res.error === 1357001) {
1175
+ throw { error: "Not logged in." };
1176
+ }
1177
+ return res;
1178
+ });
1179
+ };
1180
+ }
1181
+
1182
+ function saveCookies(jar) {
1183
+ return function (res) {
1184
+ var cookies = res.headers["set-cookie"] || [];
1185
+ cookies.forEach(function (c) {
1186
+ if (c.indexOf(".facebook.com") > -1) {
1187
+ jar.setCookie(c, "https://www.facebook.com");
1188
+ }
1189
+ var c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
1190
+ jar.setCookie(c2, "https://www.messenger.com");
1191
+ });
1192
+ return res;
1193
+ };
1194
+ }
1195
+
1196
+ var NUM_TO_MONTH = [
1197
+ "Jan",
1198
+ "Feb",
1199
+ "Mar",
1200
+ "Apr",
1201
+ "May",
1202
+ "Jun",
1203
+ "Jul",
1204
+ "Aug",
1205
+ "Sep",
1206
+ "Oct",
1207
+ "Nov",
1208
+ "Dec"
1209
+ ];
1210
+ var NUM_TO_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1211
+ function formatDate(date) {
1212
+ var d = date.getUTCDate();
1213
+ d = d >= 10 ? d : "0" + d;
1214
+ var h = date.getUTCHours();
1215
+ h = h >= 10 ? h : "0" + h;
1216
+ var m = date.getUTCMinutes();
1217
+ m = m >= 10 ? m : "0" + m;
1218
+ var s = date.getUTCSeconds();
1219
+ s = s >= 10 ? s : "0" + s;
1220
+ return (
1221
+ NUM_TO_DAY[date.getUTCDay()] +
1222
+ ", " +
1223
+ d +
1224
+ " " +
1225
+ NUM_TO_MONTH[date.getUTCMonth()] +
1226
+ " " +
1227
+ date.getUTCFullYear() +
1228
+ " " +
1229
+ h +
1230
+ ":" +
1231
+ m +
1232
+ ":" +
1233
+ s +
1234
+ " GMT"
1235
+ );
1236
+ }
1237
+
1238
+ function formatCookie(arr, url) {
1239
+ return (
1240
+ arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com"
1241
+ );
1242
+ }
1243
+
1244
+ function formatThread(data) {
1245
+ return {
1246
+ threadID: formatID(data.thread_fbid.toString()),
1247
+ participants: data.participants.map(formatID),
1248
+ participantIDs: data.participants.map(formatID),
1249
+ name: data.name,
1250
+ nicknames: data.custom_nickname,
1251
+ snippet: data.snippet,
1252
+ snippetAttachments: data.snippet_attachments,
1253
+ snippetSender: formatID((data.snippet_sender || "").toString()),
1254
+ unreadCount: data.unread_count,
1255
+ messageCount: data.message_count,
1256
+ imageSrc: data.image_src,
1257
+ timestamp: data.timestamp,
1258
+ serverTimestamp: data.server_timestamp, // what is this?
1259
+ muteUntil: data.mute_until,
1260
+ isCanonicalUser: data.is_canonical_user,
1261
+ isCanonical: data.is_canonical,
1262
+ isSubscribed: data.is_subscribed,
1263
+ folder: data.folder,
1264
+ isArchived: data.is_archived,
1265
+ recipientsLoadable: data.recipients_loadable,
1266
+ hasEmailParticipant: data.has_email_participant,
1267
+ readOnly: data.read_only,
1268
+ canReply: data.can_reply,
1269
+ cannotReplyReason: data.cannot_reply_reason,
1270
+ lastMessageTimestamp: data.last_message_timestamp,
1271
+ lastReadTimestamp: data.last_read_timestamp,
1272
+ lastMessageType: data.last_message_type,
1273
+ emoji: data.custom_like_icon,
1274
+ color: data.custom_color,
1275
+ adminIDs: data.admin_ids,
1276
+ threadType: data.thread_type
1277
+ };
1278
+ }
1279
+
1280
+ function getType(obj) {
1281
+ return Object.prototype.toString.call(obj).slice(8, -1);
1282
+ }
1283
+
1284
+ function formatProxyPresence(presence, userID) {
1285
+ if (presence.lat === undefined || presence.p === undefined) return null;
1286
+ return {
1287
+ type: "presence",
1288
+ timestamp: presence.lat * 1000,
1289
+ userID: userID,
1290
+ statuses: presence.p
1291
+ };
1292
+ }
1293
+
1294
+ function formatPresence(presence, userID) {
1295
+ return {
1296
+ type: "presence",
1297
+ timestamp: presence.la * 1000,
1298
+ userID: userID,
1299
+ statuses: presence.a
1300
+ };
1301
+ }
1302
+
1303
+ function decodeClientPayload(payload) {
1304
+ /*
1305
+ Special function which Client using to "encode" clients JSON payload
1306
+ */
1307
+ return JSON.parse(String.fromCharCode.apply(null, payload));
1308
+ }
1309
+
1310
+ function getAppState(jar) {
1311
+ return jar
1312
+ .getCookies("https://www.facebook.com")
1313
+ .concat(jar.getCookies("https://facebook.com"))
1314
+ .concat(jar.getCookies("https://www.messenger.com"));
1315
+ }
1316
+ module.exports = {
1317
+ isReadableStream,
1318
+ get,
1319
+ post,
1320
+ postFormData,
1321
+ generateThreadingID,
1322
+ generateOfflineThreadingID,
1323
+ getGUID,
1324
+ getFrom,
1325
+ makeParsable,
1326
+ arrToForm,
1327
+ getSignatureID,
1328
+ getJar: request.jar,
1329
+ generateTimestampRelative,
1330
+ makeDefaults,
1331
+ parseAndCheckLogin,
1332
+ saveCookies,
1333
+ getType,
1334
+ _formatAttachment,
1335
+ formatHistoryMessage,
1336
+ formatID,
1337
+ formatMessage,
1338
+ formatDeltaEvent,
1339
+ formatDeltaMessage,
1340
+ formatProxyPresence,
1341
+ formatPresence,
1342
+ formatTyp,
1343
+ formatDeltaReadReceipt,
1344
+ formatCookie,
1345
+ formatThread,
1346
+ formatReadReceipt,
1347
+ formatRead,
1348
+ generatePresence,
1349
+ generateAccessiblityCookie,
1350
+ formatDate,
1351
+ decodeClientPayload,
1352
+ getAppState,
1353
+ getAdminTextMessageType,
1354
+ setProxy
1355
+ };
1356
+