meta-fca 2.5.5 → 2.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/utils.js +1469 -1191
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,1302 +134,1552 @@ 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
- /** @type {number} */
263
- var r = Math.floor((sectionLength + Math.random() * 16) % 16);
264
307
  /** @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
+ }
826
905
 
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}
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
+ */
833
909
 
834
- switch (m.class) {
910
+ function formatDeltaEvent(m) {
911
+ var { updateData,getData,hasData } = require('./Extra/ExtraGetThread');
912
+ var logMessageType;
913
+ var logMessageData;
914
+
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
- }
1552
+ return JSON.parse(Utf8ArrayToStr(payload));
1553
+ }
1358
1554
 
1359
- return JSON.parse(Utf8ArrayToStr(payload));
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;
1360
1590
  }
1361
1591
 
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"));
1592
+ function getData_Path(Obj , Arr, Stt) {
1593
+ //default stt = 0
1594
+ if (Arr.length === 0 && Obj != undefined) {
1595
+ return Obj; //object
1596
+ }
1597
+ else if (Obj == undefined) {
1598
+ return Stt;
1599
+ }
1600
+ const head = Arr[0];
1601
+ if (head == undefined) {
1602
+ return Stt;
1603
+ }
1604
+ const tail = Arr.slice(1);
1605
+ return getData_Path(Obj[head], tail, Stt++);
1606
+ }
1607
+
1608
+
1609
+ function setData_Path(obj, path, value) {
1610
+ if (!path.length) {
1611
+ return obj;
1612
+ }
1613
+ const currentKey = path[0];
1614
+ let currentObj = obj[currentKey];
1615
+
1616
+ if (!currentObj) {
1617
+ obj[currentKey] = value;
1618
+ currentObj = obj[currentKey];
1619
+ }
1620
+ path.shift();
1621
+ if (!path.length) {
1622
+ currentObj = value;
1623
+ } else {
1624
+ currentObj = setData_Path(currentObj, path, value);
1625
+ }
1626
+
1627
+ return obj;
1628
+ }
1629
+
1630
+ function getPaths(obj, parentPath = []) {
1631
+ let paths = [];
1632
+ for (let prop in obj) {
1633
+ if (typeof obj[prop] === "object") {
1634
+ paths = paths.concat(getPaths(obj[prop], [...parentPath, prop]));
1635
+ } else {
1636
+ paths.push([...parentPath, prop]);
1637
+ }
1638
+ }
1639
+ return paths;
1367
1640
  }
1641
+
1368
1642
  module.exports = {
1369
- isReadableStream,
1370
- get,
1371
- post,
1372
- postFormData,
1373
- generateThreadingID,
1374
- generateOfflineThreadingID,
1375
- getGUID,
1376
- getFrom,
1377
- makeParsable,
1378
- arrToForm,
1379
- getSignatureID,
1380
- getJar: request.jar,
1381
- generateTimestampRelative,
1382
- makeDefaults,
1383
- parseAndCheckLogin,
1384
- saveCookies,
1385
- getType,
1386
- _formatAttachment,
1387
- formatHistoryMessage,
1388
- formatID,
1389
- formatMessage,
1390
- formatDeltaEvent,
1391
- formatDeltaMessage,
1392
- formatProxyPresence,
1393
- formatPresence,
1394
- formatTyp,
1395
- formatDeltaReadReceipt,
1396
- formatCookie,
1397
- formatThread,
1398
- formatReadReceipt,
1399
- formatRead,
1400
- generatePresence,
1401
- generateAccessiblityCookie,
1402
- formatDate,
1403
- decodeClientPayload,
1404
- getAppState,
1405
- getAdminTextMessageType,
1406
- setProxy
1643
+ isReadableStream:isReadableStream,
1644
+ get:get,
1645
+ post:post,
1646
+ postFormData:postFormData,
1647
+ generateThreadingID:generateThreadingID,
1648
+ generateOfflineThreadingID:generateOfflineThreadingID,
1649
+ getGUID:getGUID,
1650
+ getFrom:getFrom,
1651
+ makeParsable:makeParsable,
1652
+ arrToForm:arrToForm,
1653
+ getSignatureID:getSignatureID,
1654
+ getJar: request.jar,
1655
+ generateTimestampRelative:generateTimestampRelative,
1656
+ makeDefaults:makeDefaults,
1657
+ parseAndCheckLogin:parseAndCheckLogin,
1658
+ getGender: getGenderByPhysicalMethod,
1659
+ getData_Path,
1660
+ setData_Path,
1661
+ getPaths,
1662
+ saveCookies,
1663
+ getType,
1664
+ _formatAttachment,
1665
+ formatHistoryMessage,
1666
+ formatID,
1667
+ formatMessage,
1668
+ formatDeltaEvent,
1669
+ formatDeltaMessage,
1670
+ formatProxyPresence,
1671
+ formatPresence,
1672
+ formatTyp,
1673
+ formatDeltaReadReceipt,
1674
+ formatCookie,
1675
+ formatThread,
1676
+ formatReadReceipt,
1677
+ formatRead,
1678
+ generatePresence,
1679
+ generateAccessiblityCookie,
1680
+ formatDate,
1681
+ decodeClientPayload,
1682
+ getAppState,
1683
+ getAdminTextMessageType,
1684
+ setProxy
1407
1685
  };