alicezetion 1.0.2 → 1.0.4

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