alicezetion 1.1.7 → 1.1.9

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