@dongdev/fca-unofficial 0.0.6 → 0.0.8

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