@lazyneoaz/testfca 1.0.0

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 (120) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/COOKIE_LOGIN.md +208 -0
  3. package/LICENSE +3 -0
  4. package/README.md +492 -0
  5. package/index.js +2 -0
  6. package/package.json +120 -0
  7. package/scripts/build-go.mjs +54 -0
  8. package/scripts/detect-platform.mjs +36 -0
  9. package/scripts/download-prebuilt.mjs +119 -0
  10. package/scripts/package.mjs +6 -0
  11. package/scripts/postinstall.mjs +113 -0
  12. package/src/apis/addExternalModule.js +24 -0
  13. package/src/apis/addUserToGroup.js +108 -0
  14. package/src/apis/changeAdminStatus.js +148 -0
  15. package/src/apis/changeArchivedStatus.js +61 -0
  16. package/src/apis/changeAvatar.js +103 -0
  17. package/src/apis/changeBio.js +69 -0
  18. package/src/apis/changeBlockedStatus.js +54 -0
  19. package/src/apis/changeGroupImage.js +136 -0
  20. package/src/apis/changeThreadColor.js +116 -0
  21. package/src/apis/changeThreadEmoji.js +53 -0
  22. package/src/apis/comment.js +207 -0
  23. package/src/apis/createAITheme.js +129 -0
  24. package/src/apis/createNewGroup.js +79 -0
  25. package/src/apis/createPoll.js +73 -0
  26. package/src/apis/deleteMessage.js +52 -0
  27. package/src/apis/deleteThread.js +52 -0
  28. package/src/apis/e2ee.js +170 -0
  29. package/src/apis/editMessage.js +78 -0
  30. package/src/apis/emoji.js +124 -0
  31. package/src/apis/fetchThemeData.js +82 -0
  32. package/src/apis/follow.js +81 -0
  33. package/src/apis/forwardMessage.js +52 -0
  34. package/src/apis/friend.js +243 -0
  35. package/src/apis/gcmember.js +122 -0
  36. package/src/apis/gcname.js +123 -0
  37. package/src/apis/gcrule.js +119 -0
  38. package/src/apis/getAccess.js +111 -0
  39. package/src/apis/getBotInfo.js +88 -0
  40. package/src/apis/getBotInitialData.js +43 -0
  41. package/src/apis/getFriendsList.js +79 -0
  42. package/src/apis/getMessage.js +423 -0
  43. package/src/apis/getTheme.js +95 -0
  44. package/src/apis/getThemeInfo.js +116 -0
  45. package/src/apis/getThreadHistory.js +239 -0
  46. package/src/apis/getThreadInfo.js +267 -0
  47. package/src/apis/getThreadList.js +232 -0
  48. package/src/apis/getThreadPictures.js +58 -0
  49. package/src/apis/getUserID.js +117 -0
  50. package/src/apis/getUserInfo.js +513 -0
  51. package/src/apis/getUserInfoV2.js +146 -0
  52. package/src/apis/handleMessageRequest.js +50 -0
  53. package/src/apis/httpGet.js +63 -0
  54. package/src/apis/httpPost.js +89 -0
  55. package/src/apis/httpPostFormData.js +69 -0
  56. package/src/apis/listenMqtt.js +1236 -0
  57. package/src/apis/listenSpeed.js +179 -0
  58. package/src/apis/logout.js +93 -0
  59. package/src/apis/markAsDelivered.js +47 -0
  60. package/src/apis/markAsRead.js +115 -0
  61. package/src/apis/markAsReadAll.js +40 -0
  62. package/src/apis/markAsSeen.js +70 -0
  63. package/src/apis/mqttDeltaValue.js +250 -0
  64. package/src/apis/muteThread.js +45 -0
  65. package/src/apis/nickname.js +132 -0
  66. package/src/apis/notes.js +163 -0
  67. package/src/apis/pinMessage.js +150 -0
  68. package/src/apis/produceMetaTheme.js +180 -0
  69. package/src/apis/realtime.js +182 -0
  70. package/src/apis/removeUserFromGroup.js +117 -0
  71. package/src/apis/resolvePhotoUrl.js +58 -0
  72. package/src/apis/searchForThread.js +154 -0
  73. package/src/apis/sendMessage.js +346 -0
  74. package/src/apis/sendMessageMqtt.js +248 -0
  75. package/src/apis/sendTypingIndicator.js +105 -0
  76. package/src/apis/setMessageReaction.js +38 -0
  77. package/src/apis/setMessageReactionMqtt.js +61 -0
  78. package/src/apis/setThreadTheme.js +260 -0
  79. package/src/apis/setThreadThemeMqtt.js +94 -0
  80. package/src/apis/share.js +107 -0
  81. package/src/apis/shareContact.js +66 -0
  82. package/src/apis/stickers.js +257 -0
  83. package/src/apis/story.js +181 -0
  84. package/src/apis/theme.js +233 -0
  85. package/src/apis/unfriend.js +47 -0
  86. package/src/apis/unsendMessage.js +25 -0
  87. package/src/database/appStateBackup.js +298 -0
  88. package/src/database/models/index.js +56 -0
  89. package/src/database/models/thread.js +31 -0
  90. package/src/database/models/user.js +32 -0
  91. package/src/database/threadData.js +101 -0
  92. package/src/database/userData.js +90 -0
  93. package/src/e2ee/bridge.js +275 -0
  94. package/src/e2ee/index.js +60 -0
  95. package/src/engine/client.js +95 -0
  96. package/src/engine/models/buildAPI.js +152 -0
  97. package/src/engine/models/loginHelper.js +574 -0
  98. package/src/engine/models/setOptions.js +88 -0
  99. package/src/types/index.d.ts +574 -0
  100. package/src/utils/antiSuspension.js +529 -0
  101. package/src/utils/auth-helpers.js +149 -0
  102. package/src/utils/autoReLogin.js +336 -0
  103. package/src/utils/axios.js +436 -0
  104. package/src/utils/cache.js +54 -0
  105. package/src/utils/clients.js +282 -0
  106. package/src/utils/constants.js +410 -0
  107. package/src/utils/formatters/data/formatAttachment.js +370 -0
  108. package/src/utils/formatters/data/formatDelta.js +109 -0
  109. package/src/utils/formatters/index.js +159 -0
  110. package/src/utils/formatters/value/formatCookie.js +91 -0
  111. package/src/utils/formatters/value/formatDate.js +36 -0
  112. package/src/utils/formatters/value/formatID.js +16 -0
  113. package/src/utils/formatters.js +1373 -0
  114. package/src/utils/headers.js +235 -0
  115. package/src/utils/index.js +153 -0
  116. package/src/utils/monitoring.js +333 -0
  117. package/src/utils/rateLimiter.js +319 -0
  118. package/src/utils/tokenRefresh.js +680 -0
  119. package/src/utils/user-agents.js +238 -0
  120. package/src/utils/validation.js +157 -0
@@ -0,0 +1,1373 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ "use strict";/*mention fixed by Fahad*/
3
+ const url = require("url");
4
+
5
+ const querystring = require("querystring");
6
+
7
+ const stream = require("stream");
8
+
9
+ const { getType, padZeros, NUM_TO_MONTH, NUM_TO_DAY } = require("./constants");
10
+ function isReadableStream(obj) {
11
+ return obj instanceof stream.Stream && typeof obj._read == "function" && getType(obj._readableState) == "Object";
12
+ }
13
+
14
+ function getExtension(original_extension, fullFileName = "") {
15
+ if (original_extension) {
16
+ return original_extension;
17
+ } else {
18
+ const extension = fullFileName.split(".").pop();
19
+ if (extension === fullFileName) {
20
+ return "";
21
+ } else {
22
+ return extension;
23
+ }
24
+ }
25
+ }
26
+
27
+ /**
28
+ * A function for formatting incoming attachments.
29
+ * @param {Object} attachment1 - The main attachment object.
30
+ * @param {Object} attachment2 - A secondary attachment object that sometimes contains additional data.
31
+ * @returns {Object} A formatted attachment object.
32
+ */
33
+ function _formatAttachment(attachment1, attachment2) {
34
+ const blob_attachment = attachment1.mercury || attachment1.blob_attachment || attachment1.sticker_attachment;
35
+ const type_attachment = blob_attachment && blob_attachment.__typename ? blob_attachment.__typename : attachment1.attach_type;
36
+ if (type_attachment == null && attachment1.id != null && attachment1.extensible_attachment == null) {
37
+ return {
38
+ type: "share",
39
+ ID: attachment1.id,
40
+ url: attachment1.href,
41
+ title: "Shared Content",
42
+ description: "Unsupported shared content.",
43
+ source: null,
44
+ isUnrecognized: true
45
+ };
46
+ }
47
+
48
+ if (!attachment1.attach_type && attachment1.imageMetadata) {
49
+ return {
50
+ type: 'photo',
51
+ ID: attachment1.fbid,
52
+ filename: attachment1.filename,
53
+ fileSize: Number(attachment1.fileSize || 0),
54
+ mimeType: attachment1.mimeType,
55
+ width: attachment1.imageMetadata.width,
56
+ height: attachment1.imageMetadata.height,
57
+ url: null,
58
+ thumbnailUrl: null,
59
+ previewUrl: null,
60
+ largePreviewUrl: null,
61
+ name: attachment1.filename
62
+ };
63
+ }
64
+
65
+
66
+ attachment2 = attachment2 || {
67
+ id: "",
68
+ image_data: {}
69
+ };
70
+ attachment1 = attachment1.mercury || attachment1;
71
+ let blob = attachment1.blob_attachment || attachment1.sticker_attachment;
72
+ let type =
73
+ blob && blob.__typename ? blob.__typename: attachment1.attach_type;
74
+ if (!type && attachment1.sticker_attachment) {
75
+ type = "StickerAttachment";
76
+ blob = attachment1.sticker_attachment;
77
+ } else if (!type && attachment1.extensible_attachment) {
78
+ if (
79
+ attachment1.extensible_attachment.story_attachment &&
80
+ attachment1.extensible_attachment.story_attachment.target &&
81
+ attachment1.extensible_attachment.story_attachment.target.__typename &&
82
+ attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
83
+ ) {
84
+ type = "MessageLocation";
85
+ } else {
86
+ type = "ExtensibleAttachment";
87
+ }
88
+
89
+ blob = attachment1.extensible_attachment;
90
+ }
91
+
92
+ const fullFileName = attachment1.filename;
93
+ const fileSize = Number(attachment1.fileSize || 0);
94
+ const durationVideo = attachment1.genericMetadata ? Number(attachment1.genericMetadata.videoLength): undefined;
95
+ const durationAudio = attachment1.genericMetadata ? Number(attachment1.genericMetadata.duration): undefined;
96
+ const mimeType = attachment1.mimeType;
97
+
98
+ switch (type) {
99
+ case "sticker":
100
+ return {
101
+ type: "sticker",
102
+ ID: attachment1.metadata.stickerID.toString(),
103
+ url: attachment1.url,
104
+ packID: attachment1.metadata.packID.toString(),
105
+ spriteUrl: attachment1.metadata.spriteURI,
106
+ spriteUrl2x: attachment1.metadata.spriteURI2x,
107
+ width: attachment1.metadata.width,
108
+ height: attachment1.metadata.height,
109
+ caption: attachment2.caption,
110
+ description: attachment2.description,
111
+ frameCount: attachment1.metadata.frameCount,
112
+ frameRate: attachment1.metadata.frameRate,
113
+ framesPerRow: attachment1.metadata.framesPerRow,
114
+ framesPerCol: attachment1.metadata.framesPerCol,
115
+ stickerID: attachment1.metadata.stickerID.toString(),
116
+ spriteURI: attachment1.metadata.spriteURI,
117
+ spriteURI2x: attachment1.metadata.spriteURI2x
118
+ };
119
+ case "file":
120
+ return {
121
+ type: "file",
122
+ ID: attachment2.id.toString(),
123
+ fullFileName: fullFileName,
124
+ filename: attachment1.name,
125
+ fileSize: fileSize,
126
+ original_extension: getExtension(attachment1.original_extension, fullFileName),
127
+ mimeType: mimeType,
128
+ url: attachment1.url,
129
+ isMalicious: attachment2.is_malicious,
130
+ contentType: attachment2.mime_type,
131
+ name: attachment1.name
132
+ };
133
+ case "photo":
134
+ return {
135
+ type: "photo",
136
+ ID: attachment1.metadata.fbid.toString(),
137
+ filename: attachment1.fileName,
138
+ fullFileName: fullFileName,
139
+ fileSize: fileSize,
140
+ original_extension: getExtension(attachment1.original_extension, fullFileName),
141
+ mimeType: mimeType,
142
+ thumbnailUrl: attachment1.thumbnail_url,
143
+ previewUrl: attachment1.preview_url,
144
+ previewWidth: attachment1.preview_width,
145
+ previewHeight: attachment1.preview_height,
146
+ largePreviewUrl: attachment1.large_preview_url,
147
+ largePreviewWidth: attachment1.large_preview_width,
148
+ largePreviewHeight: attachment1.large_preview_height,
149
+ url: attachment1.metadata.url,
150
+ width: attachment1.metadata.dimensions.split(",")[0],
151
+ height: attachment1.metadata.dimensions.split(",")[1],
152
+ name: fullFileName
153
+ };
154
+ case "animated_image":
155
+ return {
156
+ type: "animated_image",
157
+ ID: attachment2.id.toString(),
158
+ filename: attachment2.filename,
159
+ fullFileName: fullFileName,
160
+ original_extension: getExtension(attachment2.original_extension, fullFileName),
161
+ mimeType: mimeType,
162
+ previewUrl: attachment1.preview_url,
163
+ previewWidth: attachment1.preview_width,
164
+ previewHeight: attachment1.preview_height,
165
+ url: attachment2.image_data.url,
166
+ width: attachment2.image_data.width,
167
+ height: attachment2.image_data.height,
168
+ name: attachment1.name,
169
+ facebookUrl: attachment1.url,
170
+ thumbnailUrl: attachment1.thumbnail_url,
171
+ rawGifImage: attachment2.image_data.raw_gif_image,
172
+ rawWebpImage: attachment2.image_data.raw_webp_image,
173
+ animatedGifUrl: attachment2.image_data.animated_gif_url,
174
+ animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url,
175
+ animatedWebpUrl: attachment2.image_data.animated_webp_url,
176
+ animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url
177
+ };
178
+ case "share":
179
+ return {
180
+ type: "share",
181
+ ID: attachment1.share.share_id.toString(),
182
+ url: attachment2.href,
183
+ title: attachment1.share.title,
184
+ description: attachment1.share.description,
185
+ source: attachment1.share.source,
186
+ image: attachment1.share.media.image,
187
+ width: attachment1.share.media.image_slicewidth,
188
+ height: attachment1.share.media.image_size.height,
189
+ playable: attachment1.share.media.playable,
190
+ duration: attachment1.share.media.duration,
191
+ subattachments: attachment1.share.subattachments,
192
+ properties: {},
193
+ animatedImageSize: attachment1.share.media.animated_image_size,
194
+ facebookUrl: attachment1.share.uri,
195
+ target: attachment1.share.target,
196
+ styleList: attachment1.share.style_list
197
+ };
198
+ case "video":
199
+ return {
200
+ type: "video",
201
+ ID: attachment1.metadata.fbid.toString(),
202
+ filename: attachment1.name,
203
+ fullFileName: fullFileName,
204
+ original_extension: getExtension(attachment1.original_extension, fullFileName),
205
+ mimeType: mimeType,
206
+ duration: durationVideo,
207
+ previewUrl: attachment1.preview_url,
208
+ previewWidth: attachment1.preview_width,
209
+ previewHeight: attachment1.preview_height,
210
+ url: attachment1.url,
211
+ width: attachment1.metadata.dimensions.width,
212
+ height: attachment1.metadata.dimensions.height,
213
+ videoType: "unknown",
214
+ thumbnailUrl: attachment1.thumbnail_url
215
+ };
216
+ case "error":
217
+ return {
218
+ type: "error",
219
+ attachment1: attachment1,
220
+ attachment2: attachment2
221
+ };
222
+ case "MessageImage":
223
+ return {
224
+ type: "photo",
225
+ ID: blob.legacy_attachment_id,
226
+ filename: blob.filename,
227
+ fullFileName: fullFileName,
228
+ fileSize: fileSize,
229
+ original_extension: getExtension(blob.original_extension, fullFileName),
230
+ mimeType: mimeType,
231
+ thumbnailUrl: blob.thumbnail.uri,
232
+ previewUrl: blob.preview.uri,
233
+ previewWidth: blob.preview.width,
234
+ previewHeight: blob.preview.height,
235
+ largePreviewUrl: blob.large_preview.uri,
236
+ largePreviewWidth: blob.large_preview.width,
237
+ largePreviewHeight: blob.large_preview.height,
238
+ url: blob.large_preview.uri,
239
+ width: blob.original_dimensions.x,
240
+ height: blob.original_dimensions.y,
241
+ name: blob.filename
242
+ };
243
+ case "MessageAnimatedImage":
244
+ return {
245
+ type: "animated_image",
246
+ ID: blob.legacy_attachment_id,
247
+ filename: blob.filename,
248
+ fullFileName: fullFileName,
249
+ original_extension: getExtension(blob.original_extension, fullFileName),
250
+ mimeType: mimeType,
251
+ previewUrl: blob.preview_image.uri,
252
+ previewWidth: blob.preview_image.width,
253
+ previewHeight: blob.preview_image.height,
254
+ url: blob.animated_image.uri,
255
+ width: blob.animated_image.width,
256
+ height: blob.animated_image.height,
257
+ thumbnailUrl: blob.preview_image.uri,
258
+ name: blob.filename,
259
+ facebookUrl: blob.animated_image.uri,
260
+ rawGifImage: blob.animated_image.uri,
261
+ animatedGifUrl: blob.animated_image.uri,
262
+ animatedGifPreviewUrl: blob.preview_image.uri,
263
+ animatedWebpUrl: blob.animated_image.uri,
264
+ animatedWebpPreviewUrl: blob.preview_image.uri
265
+ };
266
+ case "MessageVideo":
267
+ return {
268
+ type: "video",
269
+ ID: blob.legacy_attachment_id,
270
+ filename: blob.filename,
271
+ fullFileName: fullFileName,
272
+ original_extension: getExtension(blob.original_extension, fullFileName),
273
+ fileSize: fileSize,
274
+ duration: durationVideo,
275
+ mimeType: mimeType,
276
+ previewUrl: blob.large_image.uri,
277
+ previewWidth: blob.large_image.width,
278
+ previewHeight: blob.large_image.height,
279
+ url: blob.playable_url,
280
+ width: blob.original_dimensions.x,
281
+ height: blob.original_dimensions.y,
282
+ videoType: blob.video_type.toLowerCase(),
283
+ thumbnailUrl: blob.large_image.uri
284
+ };
285
+ case "MessageAudio":
286
+ return {
287
+ type: "audio",
288
+ ID: blob.url_shimhash,
289
+ filename: blob.filename,
290
+ fullFileName: fullFileName,
291
+ fileSize: fileSize,
292
+ duration: durationAudio,
293
+ original_extension: getExtension(blob.original_extension, fullFileName),
294
+ mimeType: mimeType,
295
+ audioType: blob.audio_type,
296
+ url: blob.playable_url,
297
+ isVoiceMail: blob.is_voicemail
298
+ };
299
+ case "StickerAttachment":
300
+ case "Sticker":
301
+ return {
302
+ type: "sticker",
303
+ ID: blob.id,
304
+ url: blob.url,
305
+ packID: blob.pack ? blob.pack.id: null,
306
+ spriteUrl: blob.sprite_image,
307
+ spriteUrl2x: blob.sprite_image_2x,
308
+ width: blob.width,
309
+ height: blob.height,
310
+ caption: blob.label,
311
+ description: blob.label,
312
+ frameCount: blob.frame_count,
313
+ frameRate: blob.frame_rate,
314
+ framesPerRow: blob.frames_per_row,
315
+ framesPerCol: blob.frames_per_column,
316
+ stickerID: blob.id,
317
+ spriteURI: blob.sprite_image,
318
+ spriteURI2x: blob.sprite_image_2x
319
+ };
320
+ case "MessageLocation":
321
+ var urlAttach = blob.story_attachment.url;
322
+ var mediaAttach = blob.story_attachment.media;
323
+
324
+ var u = querystring.parse(url.parse(urlAttach).query).u;
325
+ var where1 = querystring.parse(url.parse(u).query).where1;
326
+ var address = where1.split(", ");
327
+
328
+ var latitude;
329
+ var longitude;
330
+
331
+ try {
332
+ latitude = Number.parseFloat(address[0]);
333
+ longitude = Number.parseFloat(address[1]);
334
+ } catch (err) {
335
+ /* empty */
336
+ }
337
+ var imageUrl;
338
+ var width;
339
+ var height;
340
+ if (mediaAttach && mediaAttach.image) {
341
+ imageUrl = mediaAttach.image.uri;
342
+ width = mediaAttach.image.width;
343
+ height = mediaAttach.image.height;
344
+ }
345
+
346
+ return {
347
+ type: "location",
348
+ ID: blob.legacy_attachment_id,
349
+ latitude: latitude,
350
+ longitude: longitude,
351
+ image: imageUrl,
352
+ width: width,
353
+ height: height,
354
+ url: u || urlAttach,
355
+ address: where1,
356
+ facebookUrl: blob.story_attachment.url,
357
+ target: blob.story_attachment.target,
358
+ styleList: blob.story_attachment.style_list
359
+ };
360
+ case "ExtensibleAttachment":
361
+ return {
362
+ type: "share",
363
+ ID: blob.legacy_attachment_id,
364
+ url: blob.story_attachment.url,
365
+ title: blob.story_attachment.title_with_entities.text,
366
+ description:
367
+ blob.story_attachment.description &&
368
+ blob.story_attachment.description.text,
369
+ source: blob.story_attachment.source ? blob.story_attachment.source.text: null,
370
+ image:
371
+ blob.story_attachment.media &&
372
+ blob.story_attachment.media.image &&
373
+ blob.story_attachment.media.image.uri,
374
+ width:
375
+ blob.story_attachment.media &&
376
+ blob.story_attachment.media.image &&
377
+ blob.story_attachment.media.image.width,
378
+ height:
379
+ blob.story_attachment.media &&
380
+ blob.story_attachment.media.image &&
381
+ blob.story_attachment.media.image.height,
382
+ playable:
383
+ blob.story_attachment.media &&
384
+ blob.story_attachment.media.is_playable,
385
+ duration:
386
+ blob.story_attachment.media &&
387
+ blob.story_attachment.media.playable_duration_in_ms,
388
+ playableUrl:
389
+ blob.story_attachment.media == null ? null:
390
+ blob.story_attachment.media.playable_url,
391
+ subattachments: blob.story_attachment.subattachments,
392
+ properties: blob.story_attachment.properties.reduce(function(obj, cur) {
393
+ obj[cur.key] = cur.value.text;
394
+ return obj;
395
+ }, {}),
396
+ facebookUrl: blob.story_attachment.url,
397
+ target: blob.story_attachment.target,
398
+ styleList: blob.story_attachment.style_list
399
+ };
400
+ case "MessageFile":
401
+ return {
402
+ type: "file",
403
+ ID: blob.message_file_fbid,
404
+ fullFileName: fullFileName,
405
+ filename: blob.filename,
406
+ fileSize: fileSize,
407
+ mimeType: blob.mimetype,
408
+ original_extension: blob.original_extension || fullFileName.split(".").pop(),
409
+ url: blob.url,
410
+ isMalicious: blob.is_malicious,
411
+ contentType: blob.content_type,
412
+ name: blob.filename
413
+ };
414
+ default:
415
+ throw new Error(
416
+ "unrecognized attach_file of type " +
417
+ type +
418
+ "`" +
419
+ JSON.stringify(attachment1, null, 4) +
420
+ " attachment2: " +
421
+ JSON.stringify(attachment2, null, 4) +
422
+ "`"
423
+ );
424
+ }
425
+ }
426
+
427
+ function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
428
+ attachmentMap = shareMap || attachmentMap;
429
+ return attachments ?
430
+ attachments.map(function(val, i) {
431
+ if (
432
+ !attachmentMap ||
433
+ !attachmentIds ||
434
+ !attachmentMap[attachmentIds[i]]
435
+ ) {
436
+ return _formatAttachment(val);
437
+ }
438
+ return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
439
+ }): [];
440
+ }
441
+
442
+ /**
443
+ * Enhanced formatDeltaMessage with comprehensive mention parsing
444
+ * Supports multiple Facebook mention formats (2024+)
445
+ */
446
+ function formatDeltaMessage(m) {
447
+ const md = m.delta.messageMetadata;
448
+ const body = m.delta.body || "";
449
+ let mdata = [];
450
+
451
+ // NEW METHOD: messageMetadata.data.data.Gb.asMap.data (2025+ Facebook format)
452
+ // This is the primary format Facebook uses now
453
+ if (md?.data?.data) {
454
+ try {
455
+ const dataData = md.data.data;
456
+ // Look for mention data in Gb key (or any key with asMap structure)
457
+ for (const key of Object.keys(dataData)) {
458
+ const entry = dataData[key];
459
+ if (entry?.asMap?.data) {
460
+ const mapData = entry.asMap.data;
461
+ // Iterate through indexed entries (0, 1, 2, etc.)
462
+ for (const idx of Object.keys(mapData)) {
463
+ const mentionEntry = mapData[idx];
464
+ if (mentionEntry?.asMap?.data) {
465
+ const mentionData = mentionEntry.asMap.data;
466
+ // Extract id, offset, length from asLong wrappers
467
+ const id = mentionData.id?.asLong || mentionData.id?.asString;
468
+ const offset = parseInt(mentionData.offset?.asLong || mentionData.offset?.asString || '0', 10);
469
+ const length = parseInt(mentionData.length?.asLong || mentionData.length?.asString || '0', 10);
470
+ if (id) {
471
+ mdata.push({ i: id.toString(), o: offset, l: length });
472
+ }
473
+ }
474
+ }
475
+ }
476
+ }
477
+ } catch (e) {
478
+ utils.error('[MENTION PARSE ERROR] messageMetadata.data.data:', e.message);
479
+ }
480
+ }
481
+
482
+ // Method 1: data.prng (old format - stringified JSON array)
483
+ if (mdata.length === 0 && m.delta.data?.prng) {
484
+ try {
485
+ const parsed = JSON.parse(m.delta.data.prng);
486
+ if (Array.isArray(parsed)) {
487
+ mdata = parsed.map(item => ({
488
+ i: item.i || item.id || item.user_id,
489
+ o: item.o ?? item.offset ?? 0,
490
+ l: item.l ?? item.length ?? 0
491
+ }));
492
+ }
493
+ } catch (e) {}
494
+ }
495
+
496
+ // Method 2: data.mentions (newer format - stringified JSON)
497
+ if (mdata.length === 0 && m.delta.data?.mentions) {
498
+ try {
499
+ const parsed = JSON.parse(m.delta.data.mentions);
500
+ if (Array.isArray(parsed)) {
501
+ mdata = parsed.map(mention => ({
502
+ i: mention.i || mention.id || mention.user_id,
503
+ o: mention.o ?? mention.offset ?? 0,
504
+ l: mention.l ?? mention.length ?? 0
505
+ }));
506
+ }
507
+ } catch (e) {}
508
+ }
509
+
510
+ // Method 3: messageMetadata.ranges (GraphQL format)
511
+ if (mdata.length === 0 && md?.ranges && Array.isArray(md.ranges)) {
512
+ try {
513
+ mdata = md.ranges.map(r => ({
514
+ i: r.entity?.id || r.mentionID || r.id || r.mention_id,
515
+ o: r.offset ?? 0,
516
+ l: r.length ?? 0
517
+ }));
518
+ } catch (e) {}
519
+ }
520
+
521
+ // Method 4: delta.mentions directly (array format)
522
+ if (mdata.length === 0 && m.delta.mentions) {
523
+ try {
524
+ if (Array.isArray(m.delta.mentions)) {
525
+ mdata = m.delta.mentions.map(mention => ({
526
+ i: mention.id || mention.i || mention.user_id || mention.userId,
527
+ o: mention.offset ?? mention.o ?? 0,
528
+ l: mention.length ?? mention.l ?? 0
529
+ }));
530
+ } else if (typeof m.delta.mentions === 'object') {
531
+ mdata = Object.entries(m.delta.mentions).map(([id, tag]) => {
532
+ const offset = body.indexOf(tag);
533
+ return { i: id, o: offset >= 0 ? offset : 0, l: tag.length };
534
+ });
535
+ }
536
+ } catch (e) {}
537
+ }
538
+
539
+ // Method 5: data.platform_xmd (newer Facebook format with nested structure)
540
+ if (mdata.length === 0 && m.delta.data?.platform_xmd) {
541
+ try {
542
+ const xmd = JSON.parse(m.delta.data.platform_xmd);
543
+ if (xmd.mention_offsets) {
544
+ const ids = Object.keys(xmd.mention_offsets);
545
+ for (const uid of ids) {
546
+ const offsets = xmd.mention_offsets[uid];
547
+ if (Array.isArray(offsets)) {
548
+ for (const offsetData of offsets) {
549
+ mdata.push({
550
+ i: uid,
551
+ o: offsetData.o ?? offsetData.offset ?? 0,
552
+ l: offsetData.l ?? offsetData.length ?? 0
553
+ });
554
+ }
555
+ }
556
+ }
557
+ }
558
+ if (mdata.length === 0 && xmd.mentions && Array.isArray(xmd.mentions)) {
559
+ mdata = xmd.mentions.map(mt => ({
560
+ i: mt.id || mt.i || mt.user_id,
561
+ o: mt.offset ?? mt.o ?? 0,
562
+ l: mt.length ?? mt.l ?? 0
563
+ }));
564
+ }
565
+ } catch (e) {}
566
+ }
567
+
568
+ // Method 6: data.profile_xmd (alternative profile mentions format)
569
+ if (mdata.length === 0 && m.delta.data?.profile_xmd) {
570
+ try {
571
+ const xmd = JSON.parse(m.delta.data.profile_xmd);
572
+ if (Array.isArray(xmd)) {
573
+ mdata = xmd.map(item => ({
574
+ i: item.id || item.i || item.user_id,
575
+ o: item.offset ?? item.o ?? 0,
576
+ l: item.length ?? item.l ?? 0
577
+ }));
578
+ }
579
+ } catch (e) {}
580
+ }
581
+
582
+ // Method 7: delta.data with separate mention_ids/offsets/lengths arrays
583
+ if (mdata.length === 0 && m.delta.data?.mention_ids) {
584
+ try {
585
+ const ids = JSON.parse(m.delta.data.mention_ids);
586
+ const offsets = m.delta.data.mention_offsets ? JSON.parse(m.delta.data.mention_offsets) : [];
587
+ const lengths = m.delta.data.mention_lengths ? JSON.parse(m.delta.data.mention_lengths) : [];
588
+ for (let i = 0; i < ids.length; i++) {
589
+ mdata.push({
590
+ i: ids[i],
591
+ o: offsets[i] ?? 0,
592
+ l: lengths[i] ?? 0
593
+ });
594
+ }
595
+ } catch (e) {}
596
+ }
597
+
598
+ // Method 8: data.tagging (another Facebook format)
599
+ if (mdata.length === 0 && m.delta.data?.tagging) {
600
+ try {
601
+ const tagging = JSON.parse(m.delta.data.tagging);
602
+ if (Array.isArray(tagging)) {
603
+ mdata = tagging.map(t => ({
604
+ i: t.id || t.user_id || t.i,
605
+ o: t.offset ?? t.o ?? 0,
606
+ l: t.length ?? t.l ?? 0
607
+ }));
608
+ }
609
+ } catch (e) {}
610
+ }
611
+
612
+ // Method 9: Scan all data properties for any JSON that looks like mentions
613
+ if (mdata.length === 0 && m.delta.data && body.includes('@')) {
614
+ try {
615
+ for (const key of Object.keys(m.delta.data)) {
616
+ if (typeof m.delta.data[key] === 'string' && m.delta.data[key].startsWith('[')) {
617
+ try {
618
+ const parsed = JSON.parse(m.delta.data[key]);
619
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed[0]) {
620
+ const first = parsed[0];
621
+ if (first.i !== undefined || first.id !== undefined || first.user_id !== undefined) {
622
+ mdata = parsed.map(item => ({
623
+ i: item.i || item.id || item.user_id,
624
+ o: item.o ?? item.offset ?? 0,
625
+ l: item.l ?? item.length ?? 0
626
+ }));
627
+ break;
628
+ }
629
+ }
630
+ } catch (e) {}
631
+ }
632
+ }
633
+ } catch (e) {}
634
+ }
635
+
636
+ // Method 10: New Facebook format - data.at (2024+ format)
637
+ if (mdata.length === 0 && m.delta.data?.at) {
638
+ try {
639
+ const atData = JSON.parse(m.delta.data.at);
640
+ if (Array.isArray(atData)) {
641
+ mdata = atData.map(item => ({
642
+ i: item.i || item.id || item.uid || item.user_id,
643
+ o: item.o ?? item.offset ?? 0,
644
+ l: item.l ?? item.length ?? 0
645
+ }));
646
+ }
647
+ } catch (e) {}
648
+ }
649
+
650
+ // Method 11: messageMetadata.tags
651
+ if (mdata.length === 0 && md?.tags && Array.isArray(md.tags)) {
652
+ try {
653
+ mdata = md.tags.map(t => ({
654
+ i: t.id || t.user_id || t.fbid,
655
+ o: t.offset ?? 0,
656
+ l: t.length ?? 0
657
+ }));
658
+ } catch (e) {}
659
+ }
660
+
661
+ // Method 12: New xmd format with nested mention data
662
+ if (mdata.length === 0 && m.delta.data?.xmd) {
663
+ try {
664
+ const xmd = JSON.parse(m.delta.data.xmd);
665
+ if (xmd.mention) {
666
+ const mentionData = Array.isArray(xmd.mention) ? xmd.mention : [xmd.mention];
667
+ mdata = mentionData.map(mt => ({
668
+ i: mt.i || mt.id || mt.user_id || mt.uid,
669
+ o: mt.o ?? mt.offset ?? 0,
670
+ l: mt.l ?? mt.length ?? 0
671
+ }));
672
+ } else if (xmd.mentions) {
673
+ const mentionData = Array.isArray(xmd.mentions) ? xmd.mentions : [xmd.mentions];
674
+ mdata = mentionData.map(mt => ({
675
+ i: mt.i || mt.id || mt.user_id || mt.uid,
676
+ o: mt.o ?? mt.offset ?? 0,
677
+ l: mt.l ?? mt.length ?? 0
678
+ }));
679
+ } else if (xmd.tagged) {
680
+ const taggedData = Array.isArray(xmd.tagged) ? xmd.tagged : [xmd.tagged];
681
+ mdata = taggedData.map(t => ({
682
+ i: t.i || t.id || t.user_id || t.uid,
683
+ o: t.o ?? t.offset ?? 0,
684
+ l: t.l ?? t.length ?? 0
685
+ }));
686
+ }
687
+ } catch (e) {}
688
+ }
689
+
690
+ // Method 13: Check for mention data in requestContext
691
+ if (mdata.length === 0 && m.delta.requestContext?.mentions) {
692
+ try {
693
+ const rcMentions = m.delta.requestContext.mentions;
694
+ if (Array.isArray(rcMentions)) {
695
+ mdata = rcMentions.map(mt => ({
696
+ i: mt.id || mt.i || mt.user_id || mt.uid,
697
+ o: mt.offset ?? mt.o ?? 0,
698
+ l: mt.length ?? mt.l ?? 0
699
+ }));
700
+ }
701
+ } catch (e) {}
702
+ }
703
+
704
+ // Method 14: Parse from message_ranges in newer format
705
+ if (mdata.length === 0 && m.delta.data?.message_ranges) {
706
+ try {
707
+ const ranges = JSON.parse(m.delta.data.message_ranges);
708
+ if (Array.isArray(ranges)) {
709
+ mdata = ranges.filter(r => r.type === 'mention' || r.entity_type === 'user' || r.entity).map(r => ({
710
+ i: r.entity?.id || r.id || r.user_id || r.entity_id,
711
+ o: r.offset ?? 0,
712
+ l: r.length ?? 0
713
+ }));
714
+ }
715
+ } catch (e) {}
716
+ }
717
+
718
+ // Method 15: Blob XMD format (newer FB format)
719
+ if (mdata.length === 0 && m.delta.data?.blob_xmd) {
720
+ try {
721
+ const blobXmd = JSON.parse(m.delta.data.blob_xmd);
722
+ if (blobXmd.mentions && Array.isArray(blobXmd.mentions)) {
723
+ mdata = blobXmd.mentions.map(mt => ({
724
+ i: mt.id || mt.i || mt.user_id,
725
+ o: mt.offset ?? mt.o ?? 0,
726
+ l: mt.length ?? mt.l ?? 0
727
+ }));
728
+ }
729
+ } catch (e) {}
730
+ }
731
+
732
+ // Method 16: Check snippet_annotations for mentions
733
+ if (mdata.length === 0 && m.delta.data?.snippet_annotations) {
734
+ try {
735
+ const annotations = JSON.parse(m.delta.data.snippet_annotations);
736
+ if (Array.isArray(annotations)) {
737
+ const mentionAnnotations = annotations.filter(a =>
738
+ a.type === 'mention' || a.annotation_type === 'mention' || a.entity_type === 'user'
739
+ );
740
+ mdata = mentionAnnotations.map(a => ({
741
+ i: a.entity_id || a.id || a.user_id,
742
+ o: a.offset ?? 0,
743
+ l: a.length ?? 0
744
+ }));
745
+ }
746
+ } catch (e) {}
747
+ }
748
+
749
+ // Build mentions object
750
+ const mentions = {};
751
+ for (const mention of mdata) {
752
+ if (mention.i && body) {
753
+ mentions[mention.i] = body.substring(mention.o, mention.o + mention.l);
754
+ }
755
+ }
756
+
757
+ const messageReply = m.delta.messageReply ? {
758
+ messageID: m.delta.messageReply.messageID,
759
+ senderID: formatID(m.delta.messageReply.senderID),
760
+ body: m.delta.messageReply.body,
761
+ attachments: m.delta.messageReply.attachments,
762
+ timestamp: m.delta.messageReply.timestamp,
763
+ isReply: true
764
+ } : null;
765
+
766
+ return {
767
+ type: "message",
768
+ senderID: formatID(md.actorFbId.toString()),
769
+ body: body,
770
+ threadID: formatID(
771
+ (md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()
772
+ ),
773
+ messageID: md.messageId,
774
+ offlineThreadingId: md.offlineThreadingId,
775
+ attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
776
+ mentions: mentions,
777
+ timestamp: md.timestamp,
778
+ isGroup: !!md.threadKey.threadFbId,
779
+ participantIDs: m.delta.participants,
780
+ messageReply: messageReply
781
+ };
782
+ }
783
+
784
+ function formatID(id) {
785
+ if (id != undefined && id != null) {
786
+ return id.replace(/(fb)?id[:.]/, "");
787
+ } else {
788
+ return id;
789
+ }
790
+ }
791
+
792
+ function formatMessage(m) {
793
+ const originalMessage = m.message ? m.message: m;
794
+ const obj = {
795
+ type: "message",
796
+ senderName: originalMessage.sender_name,
797
+ senderID: formatID(originalMessage.sender_fbid.toString()),
798
+ participantNames: originalMessage.group_thread_info ?
799
+ originalMessage.group_thread_info.participant_names: [originalMessage.sender_name.split(" ")[0]],
800
+ participantIDs: originalMessage.group_thread_info ?
801
+ originalMessage.group_thread_info.participant_ids.map(function(v) {
802
+ return formatID(v.toString());
803
+ }): [formatID(originalMessage.sender_fbid)],
804
+ body: originalMessage.body || "",
805
+ threadID: formatID(
806
+ (
807
+ originalMessage.thread_fbid || originalMessage.other_user_fbid
808
+ ).toString()
809
+ ),
810
+ threadName: originalMessage.group_thread_info ?
811
+ originalMessage.group_thread_info.name: originalMessage.sender_name,
812
+ location: originalMessage.coordinates ? originalMessage.coordinates: null,
813
+ messageID: originalMessage.mid ?
814
+ originalMessage.mid.toString(): originalMessage.message_id,
815
+ attachments: formatAttachment(
816
+ originalMessage.attachments,
817
+ originalMessage.attachmentIds,
818
+ originalMessage.attachment_map,
819
+ originalMessage.share_map
820
+ ),
821
+ timestamp: originalMessage.timestamp,
822
+ timestampAbsolute: originalMessage.timestamp_absolute,
823
+ timestampRelative: originalMessage.timestamp_relative,
824
+ timestampDatetime: originalMessage.timestamp_datetime,
825
+ tags: originalMessage.tags,
826
+ reactions: originalMessage.reactions ? originalMessage.reactions: [],
827
+ isUnread: originalMessage.is_unread
828
+ };
829
+
830
+ if (m.type === "pages_messaging")
831
+ obj.pageID = m.realtime_viewer_fbid.toString();
832
+ obj.isGroup = obj.participantIDs.length > 2;
833
+
834
+ return obj;
835
+ }
836
+
837
+ function formatEvent(m) {
838
+ const originalMessage = m.message ? m.message: m;
839
+ let logMessageType = originalMessage.log_message_type;
840
+ let logMessageData;
841
+ if (logMessageType === "log:generic-admin-text") {
842
+ logMessageData = originalMessage.log_message_data.untypedData;
843
+ logMessageType = getAdminTextMessageType(
844
+ originalMessage.log_message_data.message_type
845
+ );
846
+ } else {
847
+ logMessageData = originalMessage.log_message_data;
848
+ }
849
+
850
+ return Object.assign(formatMessage(originalMessage), {
851
+ type: "event",
852
+ logMessageType: logMessageType,
853
+ logMessageData: logMessageData,
854
+ logMessageBody: originalMessage.log_message_body
855
+ });
856
+ }
857
+
858
+ function formatHistoryMessage(m) {
859
+ switch (m.action_type) {
860
+ case "ma-type:log-message":
861
+ return formatEvent(m);
862
+ default:
863
+ return formatMessage(m);
864
+ }
865
+ }
866
+
867
+
868
+ function getAdminTextMessageType(type) {
869
+ switch (type) {
870
+ case 'unpin_messages_v2':
871
+ return 'log:unpin-message';
872
+ case 'pin_messages_v2':
873
+ return 'log:pin-message';
874
+ case "change_thread_theme":
875
+ return "log:thread-color";
876
+ case "change_thread_icon":
877
+ case 'change_thread_quick_reaction':
878
+ return "log:thread-icon";
879
+ case "change_thread_nickname":
880
+ return "log:user-nickname";
881
+ case "change_thread_admins":
882
+ return "log:thread-admins";
883
+ case "group_poll":
884
+ return "log:thread-poll";
885
+ case "change_thread_approval_mode":
886
+ return "log:thread-approval-mode";
887
+ case "messenger_call_log":
888
+ case "participant_joined_group_call":
889
+ return "log:thread-call";
890
+ default:
891
+ return type;
892
+ }
893
+ }
894
+
895
+ function formatDeltaEvent(m) {
896
+ let logMessageType;
897
+ let logMessageData;
898
+
899
+
900
+
901
+
902
+
903
+
904
+
905
+
906
+ switch (m.class) {
907
+ case "AdminTextMessage":
908
+ logMessageData = m.untypedData;
909
+ logMessageType = getAdminTextMessageType(m.type);
910
+ break;
911
+ case "ThreadName":
912
+ logMessageType = "log:thread-name";
913
+ logMessageData = {
914
+ name: m.name
915
+ };
916
+ break;
917
+ case "ParticipantsAddedToGroupThread":
918
+ logMessageType = "log:subscribe";
919
+ logMessageData = {
920
+ addedParticipants: m.addedParticipants
921
+ };
922
+ break;
923
+ case "ParticipantLeftGroupThread":
924
+ logMessageType = "log:unsubscribe";
925
+ logMessageData = {
926
+ leftParticipantFbId: m.leftParticipantFbId
927
+ };
928
+ break;
929
+ case "ApprovalQueue":
930
+ logMessageType = "log:approval-queue";
931
+ logMessageData = {
932
+ approvalQueue: {
933
+ action: m.action,
934
+ recipientFbId: m.recipientFbId,
935
+ requestSource: m.requestSource,
936
+ ...m.messageMetadata
937
+ }
938
+ };
939
+ }
940
+ return {
941
+ type: "event",
942
+ threadID: formatID(
943
+ (
944
+ m.messageMetadata.threadKey.threadFbId ||
945
+ m.messageMetadata.threadKey.otherUserFbId
946
+ ).toString()
947
+ ),
948
+ messageID: m.messageMetadata.messageId.toString(),
949
+ logMessageType,
950
+ logMessageData,
951
+ logMessageBody: m.messageMetadata.adminText,
952
+ timestamp: m.messageMetadata.timestamp,
953
+ author: m.messageMetadata.actorFbId,
954
+ participantIDs: m.participants
955
+ };
956
+ }
957
+
958
+ function formatTyp(event) {
959
+ return {
960
+ isTyping: !!event.st,
961
+ from: event.from.toString(),
962
+ threadID: formatID(
963
+ (event.to || event.thread_fbid || event.from).toString()
964
+ ),
965
+
966
+
967
+ fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile: true,
968
+ userID: (event.realtime_viewer_fbid || event.from).toString(),
969
+ type: "typ"
970
+ };
971
+ }
972
+
973
+ function formatDeltaReadReceipt(delta) {
974
+
975
+
976
+ return {
977
+ reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
978
+ time: delta.actionTimestampMs,
979
+ threadID: formatID(
980
+ (delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()
981
+ ),
982
+ type: "read_receipt"
983
+ };
984
+ }
985
+
986
+ function formatReadReceipt(event) {
987
+ return {
988
+ reader: event.reader.toString(),
989
+ time: event.time,
990
+ threadID: formatID((event.thread_fbid || event.reader).toString()),
991
+ type: "read_receipt"
992
+ };
993
+ }
994
+
995
+ function formatRead(event) {
996
+ return {
997
+ threadID: formatID(
998
+ (
999
+ (event.chat_ids && event.chat_ids[0]) ||
1000
+ (event.thread_fbids && event.thread_fbids[0])
1001
+ ).toString()
1002
+ ),
1003
+ time: event.timestamp,
1004
+ type: "read"
1005
+ };
1006
+ }
1007
+
1008
+ function getFrom(str, startToken, endToken) {
1009
+ const start = str.indexOf(startToken) + startToken.length;
1010
+ if (start < startToken.length) return "";
1011
+
1012
+ const lastHalf = str.substring(start);
1013
+ const end = lastHalf.indexOf(endToken);
1014
+ if (end === -1) {
1015
+ throw Error(
1016
+ "Could not find endTime `" + endToken + "` in the given string."
1017
+ );
1018
+ }
1019
+ return lastHalf.substring(0, end);
1020
+ }
1021
+
1022
+ function makeParsable(html) {
1023
+ const withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
1024
+
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+
1034
+ const maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
1035
+ if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
1036
+
1037
+ return "[" + maybeMultipleObjects.join("},{") + "]";
1038
+ }
1039
+
1040
+ function arrToForm(form) {
1041
+ return arrayToObject(
1042
+ form,
1043
+ function(v) {
1044
+ return v.name;
1045
+ },
1046
+ function(v) {
1047
+ return v.val;
1048
+ }
1049
+ );
1050
+ }
1051
+
1052
+ function arrayToObject(arr, getKey, getValue) {
1053
+ return arr.reduce(function(acc, val) {
1054
+ acc[getKey(val)] = getValue(val);
1055
+ return acc;
1056
+ }, {});
1057
+ }
1058
+
1059
+ function getSignatureID() {
1060
+ return Math.floor(Math.random() * 2147483648).toString(16);
1061
+ }
1062
+
1063
+ function generateTimestampRelative() {
1064
+ const d = new Date();
1065
+ return d.getHours() + ":" + padZeros(d.getMinutes());
1066
+ }
1067
+
1068
+ function makeDefaults(html, userID, ctx) {
1069
+ let reqCounter = 1;
1070
+ const revision = getFrom(html, 'revision":', ",");
1071
+ function mergeWithDefaults(obj) {
1072
+ const newObj = {
1073
+ av: userID,
1074
+ __user: userID,
1075
+ __req: (reqCounter++).toString(36),
1076
+ __rev: revision,
1077
+ __a: 1,
1078
+ ...(ctx && {
1079
+ fb_dtsg: ctx.fb_dtsg,
1080
+ jazoest: ctx.jazoest
1081
+ })
1082
+ }
1083
+
1084
+ if (!obj) return newObj;
1085
+
1086
+ for (var prop in obj) {
1087
+ if (obj.hasOwnProperty(prop)) {
1088
+ if (!newObj[prop])
1089
+ newObj[prop] = obj[prop];
1090
+ }
1091
+ }
1092
+
1093
+ return newObj;
1094
+ }
1095
+
1096
+ return {
1097
+ get: (url, jar, qs, ctxx, customHeader = {}) => get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx, customHeader),
1098
+ post: (url, jar, form, ctxx, customHeader = {}) => post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx, customHeader),
1099
+ postFormData: (url, jar, form, qs, ctxx) => postFormData(url, jar, mergeWithDefaults(form), mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx)
1100
+ };
1101
+ }
1102
+
1103
+ function parseAndCheckLogin(ctx, http, retryCount) {
1104
+ var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
1105
+ var _try = (tryData) => new Promise(function(resolve, reject) {
1106
+ try {
1107
+ resolve(tryData());
1108
+ } catch (error) {
1109
+ reject(error);
1110
+ }
1111
+ });
1112
+ if (retryCount == undefined) retryCount = 0;
1113
+
1114
+ return function(data) {
1115
+ function any() {
1116
+ if (data.statusCode >= 500 && data.statusCode < 600) {
1117
+ if (retryCount >= 5) {
1118
+ const err = new Error("Request retry failed. Check the `res` and `statusCode` property on this error.");
1119
+ err.statusCode = data.statusCode;
1120
+ err.res = data.body;
1121
+ err.error = "Request retry failed. Check the `res` and `statusCode` property on this error.";
1122
+ throw err;
1123
+ }
1124
+ retryCount++;
1125
+ const retryTime = Math.floor(Math.random() * 5000);
1126
+ console.warn("parseAndCheckLogin", "Got status code " + data.statusCode + " - " + retryCount + ". attempt to retry in " + retryTime + " milliseconds...");
1127
+ const url = data.request.uri.protocol + "//" + data.request.uri.hostname + data.request.uri.pathname;
1128
+ if (data.request.headers["content-type"].split(";")[0] === "multipart/form-data") {
1129
+ return delay(retryTime)
1130
+ .then(function() {
1131
+ return http
1132
+ .postFormData(url, ctx.jar, data.request.formData);
1133
+ })
1134
+ .then(parseAndCheckLogin(ctx, http, retryCount));
1135
+ } else {
1136
+ return delay(retryTime)
1137
+ .then(function() {
1138
+ return http
1139
+ .post(url, ctx.jar, data.request.formData);
1140
+ })
1141
+ .then(parseAndCheckLogin(ctx, http, retryCount));
1142
+ }
1143
+ }
1144
+
1145
+ if (data.statusCode === 404) return;
1146
+
1147
+ if (data.statusCode !== 200)
1148
+ throw new Error("parseAndCheckLogin got status code: " + data.statusCode + ". Bailing out of trying to parse response.");
1149
+
1150
+ let res = null;
1151
+ try {
1152
+ res = JSON.parse(makeParsable(data.body));
1153
+ } catch (e) {
1154
+ const err = new Error("JSON.parse error. Check the `detail` property on this error.");
1155
+ err.error = "JSON.parse error. Check the `detail` property on this error.";
1156
+ err.detail = e;
1157
+ err.res = data.body;
1158
+ throw err;
1159
+ }
1160
+
1161
+
1162
+ if (res.redirect && data.request.method === "GET") {
1163
+ return http
1164
+ .get(res.redirect, ctx.jar)
1165
+ .then(parseAndCheckLogin(ctx, http));
1166
+ }
1167
+
1168
+
1169
+ if (res.jsmods && res.jsmods.require && Array.isArray(res.jsmods.require[0]) && res.jsmods.require[0][0] === "Cookie") {
1170
+ res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace("_js_", "");
1171
+ const requireCookie = res.jsmods.require[0][3];
1172
+ ctx.jar.setCookie(formatCookie(requireCookie, "facebook"), "https://www.facebook.com");
1173
+ ctx.jar.setCookie(formatCookie(requireCookie, "messenger"), "https://www.messenger.com");
1174
+ }
1175
+
1176
+
1177
+
1178
+ if (res.jsmods && Array.isArray(res.jsmods.require)) {
1179
+ const arr = res.jsmods.require;
1180
+ for (const i in arr) {
1181
+ if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
1182
+ ctx.fb_dtsg = arr[i][3][0];
1183
+
1184
+
1185
+ ctx.ttstamp = "2";
1186
+ for (let j = 0; j < ctx.fb_dtsg.length; j++) {
1187
+ ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+
1193
+ if (res.error === 1357001 || res.error === 1357004) {
1194
+ const errMsg = res.error === 1357004 ? 'Account temporarily locked or blocked' : 'Facebook blocked the login';
1195
+ const err = new Error(errMsg);
1196
+ err.error = "Not logged in.";
1197
+ err.requiresReLogin = true;
1198
+ err.loginBlocked = res.error === 1357004;
1199
+ throw err;
1200
+ }
1201
+
1202
+ if (res.errors && Array.isArray(res.errors)) {
1203
+ for (const errorObj of res.errors) {
1204
+ const errorMsg = errorObj.message || errorObj.description || '';
1205
+ if (/Not logged in|blocked the login|401|403/.test(errorMsg)) {
1206
+ const err = new Error('Authentication error: ' + errorMsg);
1207
+ err.error = "Not logged in.";
1208
+ err.requiresReLogin = true;
1209
+ err.loginBlocked = /blocked/.test(errorMsg);
1210
+ throw err;
1211
+ }
1212
+ }
1213
+ }
1214
+ return res;
1215
+ }
1216
+ return _try(any);
1217
+ };
1218
+ }
1219
+
1220
+ function saveCookies(jar) {
1221
+ return function(res) {
1222
+ const cookies = res.headers["set-cookie"] || [];
1223
+ cookies.forEach(function(c) {
1224
+ if (c.indexOf(".facebook.com") > -1) {
1225
+ jar.setCookie(c, "https://www.facebook.com");
1226
+ }
1227
+ const c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
1228
+ jar.setCookie(c2, "https://www.messenger.com");
1229
+ });
1230
+ return res;
1231
+ };
1232
+ }
1233
+
1234
+ function formatThread(data) {
1235
+ return {
1236
+ threadID: formatID(data.thread_fbid.toString()),
1237
+ participants: data.participants.map(formatID),
1238
+ participantIDs: data.participants.map(formatID),
1239
+ name: data.name,
1240
+ nicknames: data.custom_nickname,
1241
+ snippet: data.snippet,
1242
+ snippetAttachments: data.snippet_attachments,
1243
+ snippetSender: formatID((data.snippet_sender || "").toString()),
1244
+ unreadCount: data.unread_count,
1245
+ messageCount: data.message_count,
1246
+ imageSrc: data.image_src,
1247
+ timestamp: data.timestamp,
1248
+ serverTimestamp: data.server_timestamp,
1249
+ muteUntil: data.mute_until,
1250
+ isCanonicalUser: data.is_canonical_user,
1251
+ isCanonical: data.is_canonical,
1252
+ isSubscribed: data.is_subscribed,
1253
+ folder: data.folder,
1254
+ isArchived: data.is_archived,
1255
+ recipientsLoadable: data.recipients_loadable,
1256
+ hasEmailParticipant: data.has_email_participant,
1257
+ readOnly: data.read_only,
1258
+ canReply: data.can_reply,
1259
+ cannotReplyReason: data.cannot_reply_reason,
1260
+ lastMessageTimestamp: data.last_message_timestamp,
1261
+ lastReadTimestamp: data.last_read_timestamp,
1262
+ lastMessageType: data.last_message_type,
1263
+ emoji: data.custom_like_icon,
1264
+ color: data.custom_color,
1265
+ adminIDs: data.admin_ids,
1266
+ threadType: data.thread_type
1267
+ };
1268
+ }
1269
+
1270
+ function formatDate(date) {
1271
+ let d = date.getUTCDate();
1272
+ d = d >= 10 ? d : "0" + d;
1273
+ let h = date.getUTCHours();
1274
+ h = h >= 10 ? h : "0" + h;
1275
+ let m = date.getUTCMinutes();
1276
+ m = m >= 10 ? m : "0" + m;
1277
+ let s = date.getUTCSeconds();
1278
+ s = s >= 10 ? s : "0" + s;
1279
+ return (
1280
+ NUM_TO_DAY[date.getUTCDay()] +
1281
+ ", " +
1282
+ d +
1283
+ " " +
1284
+ NUM_TO_MONTH[date.getUTCMonth()] +
1285
+ " " +
1286
+ date.getUTCFullYear() +
1287
+ " " +
1288
+ h +
1289
+ ":" +
1290
+ m +
1291
+ ":" +
1292
+ s +
1293
+ " GMT"
1294
+ );
1295
+ }
1296
+
1297
+ function formatCookie(arr, url) {
1298
+ return (
1299
+ arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com"
1300
+ );
1301
+ }
1302
+
1303
+ function formatProxyPresence(presence, userID) {
1304
+ if (presence.lat === undefined || presence.p === undefined) return null;
1305
+ return {
1306
+ type: "presence",
1307
+ timestamp: presence.lat * 1000,
1308
+ userID: userID,
1309
+ statuses: presence.p
1310
+ };
1311
+ }
1312
+
1313
+ function formatPresence(presence, userID) {
1314
+ return {
1315
+ type: "presence",
1316
+ timestamp: presence.la * 1000,
1317
+ userID: userID,
1318
+ statuses: presence.a
1319
+ };
1320
+ }
1321
+
1322
+ function decodeClientPayload(payload) {
1323
+ /*
1324
+ Special function which Client using to "encode" clients JSON payload
1325
+ */
1326
+ return JSON.parse(String.fromCharCode.apply(null, payload));
1327
+ }
1328
+
1329
+
1330
+ module.exports = {
1331
+
1332
+ isReadableStream,
1333
+
1334
+ getExtension,
1335
+
1336
+ _formatAttachment,
1337
+
1338
+ formatAttachment,
1339
+
1340
+ formatDeltaMessage,
1341
+
1342
+ formatID,
1343
+
1344
+ formatMessage,
1345
+
1346
+ formatEvent,
1347
+
1348
+ formatHistoryMessage,
1349
+
1350
+ getAdminTextMessageType,
1351
+
1352
+ formatDeltaEvent,
1353
+
1354
+ formatTyp,
1355
+
1356
+ formatDeltaReadReceipt,
1357
+
1358
+ formatReadReceipt,
1359
+
1360
+ formatRead,
1361
+
1362
+ formatDate,
1363
+
1364
+ formatCookie,
1365
+
1366
+ formatThread,
1367
+
1368
+ formatProxyPresence,
1369
+
1370
+ formatPresence,
1371
+
1372
+ decodeClientPayload,
1373
+ };