alicezetion 1.1.8 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
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
+ };