meta-fca 2.5.0 → 2.5.5

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