node-ainzfb-new 1.7.10-11 → 1.7.10-14

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