meta-fca 2.5.7 → 2.5.9

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