bb-fca 2.0.8 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1049 +0,0 @@
1
- const url = require("url");
2
-
3
- const querystring = require("querystring");
4
-
5
- const stream = require("stream");
6
-
7
- const { getType, padZeros, NUM_TO_MONTH, NUM_TO_DAY } = require("./constants");
8
- function isReadableStream(obj) {
9
- return obj instanceof stream.Stream && typeof obj._read == "function" && getType(obj._readableState) == "Object";
10
- }
11
-
12
- function getExtension(original_extension, fullFileName = "") {
13
- if (original_extension) {
14
- return original_extension;
15
- } else {
16
- const extension = fullFileName.split(".").pop();
17
- if (extension === fullFileName) {
18
- return "";
19
- } else {
20
- return extension;
21
- }
22
- }
23
- }
24
-
25
- /**
26
- * A function for formatting incoming attachments.
27
- * @param {Object} attachment1 - The main attachment object.
28
- * @param {Object} attachment2 - A secondary attachment object that sometimes contains additional data.
29
- * @returns {Object} A formatted attachment object.
30
- */
31
- function _formatAttachment(attachment1, attachment2) {
32
- const blob_attachment = attachment1.mercury || attachment1.blob_attachment || attachment1.sticker_attachment;
33
- const type_attachment = blob_attachment && blob_attachment.__typename ? blob_attachment.__typename : attachment1.attach_type;
34
- if (type_attachment == null && attachment1.id != null && attachment1.extensible_attachment == null) {
35
- return {
36
- type: "share",
37
- ID: attachment1.id,
38
- url: attachment1.href,
39
- title: "Shared Content",
40
- description: "Unsupported shared content.",
41
- source: null,
42
- isUnrecognized: true
43
- };
44
- }
45
-
46
- if (!attachment1.attach_type && attachment1.imageMetadata) {
47
- return {
48
- type: 'photo',
49
- ID: attachment1.fbid,
50
- filename: attachment1.filename,
51
- fileSize: Number(attachment1.fileSize || 0),
52
- mimeType: attachment1.mimeType,
53
- width: attachment1.imageMetadata.width,
54
- height: attachment1.imageMetadata.height,
55
- url: null, // URLs are not provided in this delta format
56
- thumbnailUrl: null,
57
- previewUrl: null,
58
- largePreviewUrl: null,
59
- name: attachment1.filename
60
- };
61
- }
62
- // END of new code
63
-
64
- attachment2 = attachment2 || {
65
- id: "",
66
- image_data: {}
67
- };
68
- attachment1 = attachment1.mercury || attachment1;
69
- let blob = attachment1.blob_attachment || attachment1.sticker_attachment;
70
- let type =
71
- blob && blob.__typename ? blob.__typename : attachment1.attach_type;
72
- if (!type && attachment1.sticker_attachment) {
73
- type = "StickerAttachment";
74
- blob = attachment1.sticker_attachment;
75
- } else if (!type && attachment1.extensible_attachment) {
76
- if (
77
- attachment1.extensible_attachment.story_attachment &&
78
- attachment1.extensible_attachment.story_attachment.target &&
79
- attachment1.extensible_attachment.story_attachment.target.__typename &&
80
- attachment1.extensible_attachment.story_attachment.target.__typename === "MessageLocation"
81
- ) {
82
- type = "MessageLocation";
83
- } else {
84
- type = "ExtensibleAttachment";
85
- }
86
-
87
- blob = attachment1.extensible_attachment;
88
- }
89
-
90
- const fullFileName = attachment1.filename;
91
- const fileSize = Number(attachment1.fileSize || 0);
92
- const durationVideo = attachment1.genericMetadata ? Number(attachment1.genericMetadata.videoLength) : undefined;
93
- const durationAudio = attachment1.genericMetadata ? Number(attachment1.genericMetadata.duration) : undefined;
94
- const mimeType = attachment1.mimeType;
95
-
96
- switch (type) {
97
- case "sticker":
98
- return {
99
- type: "sticker",
100
- ID: attachment1.metadata.stickerID.toString(),
101
- url: attachment1.url,
102
- packID: attachment1.metadata.packID.toString(),
103
- spriteUrl: attachment1.metadata.spriteURI,
104
- spriteUrl2x: attachment1.metadata.spriteURI2x,
105
- width: attachment1.metadata.width,
106
- height: attachment1.metadata.height,
107
- caption: attachment2.caption,
108
- description: attachment2.description,
109
- frameCount: attachment1.metadata.frameCount,
110
- frameRate: attachment1.metadata.frameRate,
111
- framesPerRow: attachment1.metadata.framesPerRow,
112
- framesPerCol: attachment1.metadata.framesPerCol,
113
- stickerID: attachment1.metadata.stickerID.toString(),
114
- spriteURI: attachment1.metadata.spriteURI,
115
- spriteURI2x: attachment1.metadata.spriteURI2x
116
- };
117
- case "file":
118
- return {
119
- type: "file",
120
- ID: attachment2.id.toString(),
121
- fullFileName: fullFileName,
122
- filename: attachment1.name,
123
- fileSize: fileSize,
124
- original_extension: getExtension(attachment1.original_extension, fullFileName),
125
- mimeType: mimeType,
126
- url: attachment1.url,
127
- isMalicious: attachment2.is_malicious,
128
- contentType: attachment2.mime_type,
129
- name: attachment1.name
130
- };
131
- case "photo":
132
- return {
133
- type: "photo",
134
- ID: attachment1.metadata.fbid.toString(),
135
- filename: attachment1.fileName,
136
- fullFileName: fullFileName,
137
- fileSize: fileSize,
138
- original_extension: getExtension(attachment1.original_extension, fullFileName),
139
- mimeType: mimeType,
140
- thumbnailUrl: attachment1.thumbnail_url,
141
- previewUrl: attachment1.preview_url,
142
- previewWidth: attachment1.preview_width,
143
- previewHeight: attachment1.preview_height,
144
- largePreviewUrl: attachment1.large_preview_url,
145
- largePreviewWidth: attachment1.large_preview_width,
146
- largePreviewHeight: attachment1.large_preview_height,
147
- url: attachment1.metadata.url,
148
- width: attachment1.metadata.dimensions.split(",")[0],
149
- height: attachment1.metadata.dimensions.split(",")[1],
150
- name: fullFileName
151
- };
152
- case "animated_image":
153
- return {
154
- type: "animated_image",
155
- ID: attachment2.id.toString(),
156
- filename: attachment2.filename,
157
- fullFileName: fullFileName,
158
- original_extension: getExtension(attachment2.original_extension, fullFileName),
159
- mimeType: mimeType,
160
- previewUrl: attachment1.preview_url,
161
- previewWidth: attachment1.preview_width,
162
- previewHeight: attachment1.preview_height,
163
- url: attachment2.image_data.url,
164
- width: attachment2.image_data.width,
165
- height: attachment2.image_data.height,
166
- name: attachment1.name,
167
- facebookUrl: attachment1.url,
168
- thumbnailUrl: attachment1.thumbnail_url,
169
- rawGifImage: attachment2.image_data.raw_gif_image,
170
- rawWebpImage: attachment2.image_data.raw_webp_image,
171
- animatedGifUrl: attachment2.image_data.animated_gif_url,
172
- animatedGifPreviewUrl: attachment2.image_data.animated_gif_preview_url,
173
- animatedWebpUrl: attachment2.image_data.animated_webp_url,
174
- animatedWebpPreviewUrl: attachment2.image_data.animated_webp_preview_url
175
- };
176
- case "share":
177
- return {
178
- type: "share",
179
- ID: attachment1.share.share_id.toString(),
180
- url: attachment2.href,
181
- title: attachment1.share.title,
182
- description: attachment1.share.description,
183
- source: attachment1.share.source,
184
- image: attachment1.share.media.image,
185
- width: attachment1.share.media.image_slicewidth,
186
- height: attachment1.share.media.image_size.height,
187
- playable: attachment1.share.media.playable,
188
- duration: attachment1.share.media.duration,
189
- subattachments: attachment1.share.subattachments,
190
- properties: {},
191
- animatedImageSize: attachment1.share.media.animated_image_size,
192
- facebookUrl: attachment1.share.uri,
193
- target: attachment1.share.target,
194
- styleList: attachment1.share.style_list
195
- };
196
- case "video":
197
- return {
198
- type: "video",
199
- ID: attachment1.metadata.fbid.toString(),
200
- filename: attachment1.name,
201
- fullFileName: fullFileName,
202
- original_extension: getExtension(attachment1.original_extension, fullFileName),
203
- mimeType: mimeType,
204
- duration: durationVideo,
205
- previewUrl: attachment1.preview_url,
206
- previewWidth: attachment1.preview_width,
207
- previewHeight: attachment1.preview_height,
208
- url: attachment1.url,
209
- width: attachment1.metadata.dimensions.width,
210
- height: attachment1.metadata.dimensions.height,
211
- videoType: "unknown",
212
- thumbnailUrl: attachment1.thumbnail_url
213
- };
214
- case "error":
215
- return {
216
- type: "error",
217
- attachment1: attachment1,
218
- attachment2: attachment2
219
- };
220
- case "MessageImage":
221
- return {
222
- type: "photo",
223
- ID: blob.legacy_attachment_id,
224
- filename: blob.filename,
225
- fullFileName: fullFileName,
226
- fileSize: fileSize,
227
- original_extension: getExtension(blob.original_extension, fullFileName),
228
- mimeType: mimeType,
229
- thumbnailUrl: blob.thumbnail.uri,
230
- previewUrl: blob.preview.uri,
231
- previewWidth: blob.preview.width,
232
- previewHeight: blob.preview.height,
233
- largePreviewUrl: blob.large_preview.uri,
234
- largePreviewWidth: blob.large_preview.width,
235
- largePreviewHeight: blob.large_preview.height,
236
- url: blob.large_preview.uri,
237
- width: blob.original_dimensions.x,
238
- height: blob.original_dimensions.y,
239
- name: blob.filename
240
- };
241
- case "MessageAnimatedImage":
242
- return {
243
- type: "animated_image",
244
- ID: blob.legacy_attachment_id,
245
- filename: blob.filename,
246
- fullFileName: fullFileName,
247
- original_extension: getExtension(blob.original_extension, fullFileName),
248
- mimeType: mimeType,
249
- previewUrl: blob.preview_image.uri,
250
- previewWidth: blob.preview_image.width,
251
- previewHeight: blob.preview_image.height,
252
- url: blob.animated_image.uri,
253
- width: blob.animated_image.width,
254
- height: blob.animated_image.height,
255
- thumbnailUrl: blob.preview_image.uri,
256
- name: blob.filename,
257
- facebookUrl: blob.animated_image.uri,
258
- rawGifImage: blob.animated_image.uri,
259
- animatedGifUrl: blob.animated_image.uri,
260
- animatedGifPreviewUrl: blob.preview_image.uri,
261
- animatedWebpUrl: blob.animated_image.uri,
262
- animatedWebpPreviewUrl: blob.preview_image.uri
263
- };
264
- case "MessageVideo":
265
- return {
266
- type: "video",
267
- ID: blob.legacy_attachment_id,
268
- filename: blob.filename,
269
- fullFileName: fullFileName,
270
- original_extension: getExtension(blob.original_extension, fullFileName),
271
- fileSize: fileSize,
272
- duration: durationVideo,
273
- mimeType: mimeType,
274
- previewUrl: blob.large_image.uri,
275
- previewWidth: blob.large_image.width,
276
- previewHeight: blob.large_image.height,
277
- url: blob.playable_url,
278
- width: blob.original_dimensions.x,
279
- height: blob.original_dimensions.y,
280
- videoType: blob.video_type.toLowerCase(),
281
- thumbnailUrl: blob.large_image.uri
282
- };
283
- case "MessageAudio":
284
- return {
285
- type: "audio",
286
- ID: blob.url_shimhash,
287
- filename: blob.filename,
288
- fullFileName: fullFileName,
289
- fileSize: fileSize,
290
- duration: durationAudio,
291
- original_extension: getExtension(blob.original_extension, fullFileName),
292
- mimeType: mimeType,
293
- audioType: blob.audio_type,
294
- url: blob.playable_url,
295
- isVoiceMail: blob.is_voicemail
296
- };
297
- case "StickerAttachment":
298
- case "Sticker":
299
- return {
300
- type: "sticker",
301
- ID: blob.id,
302
- url: blob.url,
303
- packID: blob.pack ? blob.pack.id : null,
304
- spriteUrl: blob.sprite_image,
305
- spriteUrl2x: blob.sprite_image_2x,
306
- width: blob.width,
307
- height: blob.height,
308
- caption: blob.label,
309
- description: blob.label,
310
- frameCount: blob.frame_count,
311
- frameRate: blob.frame_rate,
312
- framesPerRow: blob.frames_per_row,
313
- framesPerCol: blob.frames_per_column,
314
- stickerID: blob.id,
315
- spriteURI: blob.sprite_image,
316
- spriteURI2x: blob.sprite_image_2x
317
- };
318
- case "MessageLocation":
319
- var urlAttach = blob.story_attachment.url;
320
- var mediaAttach = blob.story_attachment.media;
321
-
322
- var u = querystring.parse(url.parse(urlAttach).query).u;
323
- var where1 = querystring.parse(url.parse(u).query).where1;
324
- var address = where1.split(", ");
325
-
326
- var latitude;
327
- var longitude;
328
-
329
- try {
330
- latitude = Number.parseFloat(address[0]);
331
- longitude = Number.parseFloat(address[1]);
332
- } catch (err) {
333
- /* empty */
334
- }
335
- var imageUrl;
336
- var width;
337
- var height;
338
- if (mediaAttach && mediaAttach.image) {
339
- imageUrl = mediaAttach.image.uri;
340
- width = mediaAttach.image.width;
341
- height = mediaAttach.image.height;
342
- }
343
-
344
- return {
345
- type: "location",
346
- ID: blob.legacy_attachment_id,
347
- latitude: latitude,
348
- longitude: longitude,
349
- image: imageUrl,
350
- width: width,
351
- height: height,
352
- url: u || urlAttach,
353
- address: where1,
354
- facebookUrl: blob.story_attachment.url,
355
- target: blob.story_attachment.target,
356
- styleList: blob.story_attachment.style_list
357
- };
358
- case "ExtensibleAttachment":
359
- return {
360
- type: "share",
361
- ID: blob.legacy_attachment_id,
362
- url: blob.story_attachment.url,
363
- title: blob.story_attachment.title_with_entities.text,
364
- description:
365
- blob.story_attachment.description &&
366
- blob.story_attachment.description.text,
367
- source: blob.story_attachment.source ? blob.story_attachment.source.text : null,
368
- image:
369
- blob.story_attachment.media &&
370
- blob.story_attachment.media.image &&
371
- blob.story_attachment.media.image.uri,
372
- width:
373
- blob.story_attachment.media &&
374
- blob.story_attachment.media.image &&
375
- blob.story_attachment.media.image.width,
376
- height:
377
- blob.story_attachment.media &&
378
- blob.story_attachment.media.image &&
379
- blob.story_attachment.media.image.height,
380
- playable:
381
- blob.story_attachment.media &&
382
- blob.story_attachment.media.is_playable,
383
- duration:
384
- blob.story_attachment.media &&
385
- blob.story_attachment.media.playable_duration_in_ms,
386
- playableUrl:
387
- blob.story_attachment.media == null ? null :
388
- blob.story_attachment.media.playable_url,
389
- subattachments: blob.story_attachment.subattachments,
390
- properties: blob.story_attachment.properties.reduce(function (obj, cur) {
391
- obj[cur.key] = cur.value.text;
392
- return obj;
393
- }, {}),
394
- facebookUrl: blob.story_attachment.url,
395
- target: blob.story_attachment.target,
396
- styleList: blob.story_attachment.style_list
397
- };
398
- case "MessageFile":
399
- return {
400
- type: "file",
401
- ID: blob.message_file_fbid,
402
- fullFileName: fullFileName,
403
- filename: blob.filename,
404
- fileSize: fileSize,
405
- mimeType: blob.mimetype,
406
- original_extension: blob.original_extension || fullFileName.split(".").pop(),
407
- url: blob.url,
408
- isMalicious: blob.is_malicious,
409
- contentType: blob.content_type,
410
- name: blob.filename
411
- };
412
- default:
413
- throw new Error(
414
- "unrecognized attach_file of type " +
415
- type +
416
- "`" +
417
- JSON.stringify(attachment1, null, 4) +
418
- " attachment2: " +
419
- JSON.stringify(attachment2, null, 4) +
420
- "`"
421
- );
422
- }
423
- } function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
424
- attachmentMap = shareMap || attachmentMap;
425
- return attachments ?
426
- attachments.map(function (val, i) {
427
- if (
428
- !attachmentMap ||
429
- !attachmentIds ||
430
- !attachmentMap[attachmentIds[i]]
431
- ) {
432
- return _formatAttachment(val);
433
- }
434
- return _formatAttachment(val, attachmentMap[attachmentIds[i]]);
435
- }) : [];
436
- }
437
-
438
- /**
439
- * file: bb-fca/datas/formatters.js
440
- * Hanapin ang function na ito at palitan ng version na ito.
441
- */
442
- function formatDeltaMessage(m) {
443
- const md = m.delta.messageMetadata;
444
- const mdata =
445
- m.delta.data === undefined ?
446
- [] :
447
- m.delta.data.prng === undefined ?
448
- [] :
449
- JSON.parse(m.delta.data.prng);
450
- const m_id = mdata.map(u => u.i);
451
- const m_offset = mdata.map(u => u.o);
452
- const m_length = mdata.map(u => u.l);
453
- const mentions = {};
454
- for (let i = 0; i < m_id.length; i++) {
455
- mentions[m_id[i]] = m.delta.body.substring(
456
- m_offset[i],
457
- m_offset[i] + m_length[i]
458
- );
459
- }
460
-
461
- return {
462
- type: "message",
463
- senderID: formatID(md.actorFbId.toString()),
464
- body: m.delta.body || "",
465
- threadID: formatID(
466
- (md.threadKey.threadFbId || md.threadKey.otherUserFbId).toString()
467
- ),
468
- messageID: md.messageId,
469
- offlineThreadingId: md.offlineThreadingId,
470
- attachments: (m.delta.attachments || []).map(v => _formatAttachment(v)),
471
- mentions: mentions,
472
- timestamp: md.timestamp,
473
- isGroup: !!md.threadKey.threadFbId,
474
- participantIDs: m.delta.participants
475
- };
476
- } function formatID(id) {
477
- if (id != undefined && id != null) {
478
- return id.replace(/(fb)?id[:.]/, "");
479
- } else {
480
- return id;
481
- }
482
- }
483
-
484
- function formatMessage(m) {
485
- const originalMessage = m.message ? m.message : m;
486
- const obj = {
487
- type: "message",
488
- senderName: originalMessage.sender_name,
489
- senderID: formatID(originalMessage.sender_fbid.toString()),
490
- participantNames: originalMessage.group_thread_info ?
491
- originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(" ")[0]],
492
- participantIDs: originalMessage.group_thread_info ?
493
- originalMessage.group_thread_info.participant_ids.map(function (v) {
494
- return formatID(v.toString());
495
- }) : [formatID(originalMessage.sender_fbid)],
496
- body: originalMessage.body || "",
497
- threadID: formatID(
498
- (
499
- originalMessage.thread_fbid || originalMessage.other_user_fbid
500
- ).toString()
501
- ),
502
- threadName: originalMessage.group_thread_info ?
503
- originalMessage.group_thread_info.name : originalMessage.sender_name,
504
- location: originalMessage.coordinates ? originalMessage.coordinates : null,
505
- messageID: originalMessage.mid ?
506
- originalMessage.mid.toString() : originalMessage.message_id,
507
- attachments: formatAttachment(
508
- originalMessage.attachments,
509
- originalMessage.attachmentIds,
510
- originalMessage.attachment_map,
511
- originalMessage.share_map
512
- ),
513
- timestamp: originalMessage.timestamp,
514
- timestampAbsolute: originalMessage.timestamp_absolute,
515
- timestampRelative: originalMessage.timestamp_relative,
516
- timestampDatetime: originalMessage.timestamp_datetime,
517
- tags: originalMessage.tags,
518
- reactions: originalMessage.reactions ? originalMessage.reactions : [],
519
- isUnread: originalMessage.is_unread
520
- };
521
-
522
- if (m.type === "pages_messaging")
523
- obj.pageID = m.realtime_viewer_fbid.toString();
524
- obj.isGroup = obj.participantIDs.length > 2;
525
-
526
- return obj;
527
- }
528
-
529
- function formatEvent(m) {
530
- const originalMessage = m.message ? m.message : m;
531
- let logMessageType = originalMessage.log_message_type;
532
- let logMessageData;
533
- if (logMessageType === "log:generic-admin-text") {
534
- logMessageData = originalMessage.log_message_data.untypedData;
535
- logMessageType = getAdminTextMessageType(
536
- originalMessage.log_message_data.message_type
537
- );
538
- } else {
539
- logMessageData = originalMessage.log_message_data;
540
- }
541
-
542
- return Object.assign(formatMessage(originalMessage), {
543
- type: "event",
544
- logMessageType: logMessageType,
545
- logMessageData: logMessageData,
546
- logMessageBody: originalMessage.log_message_body
547
- });
548
- }
549
-
550
- function formatHistoryMessage(m) {
551
- switch (m.action_type) {
552
- case "ma-type:log-message":
553
- return formatEvent(m);
554
- default:
555
- return formatMessage(m);
556
- }
557
- }
558
-
559
- // Get a more readable message type for AdminTextMessages
560
- function getAdminTextMessageType(type) {
561
- switch (type) {
562
- case 'unpin_messages_v2':
563
- return 'log:unpin-message';
564
- case 'pin_messages_v2':
565
- return 'log:pin-message';
566
- case "change_thread_theme":
567
- return "log:thread-color";
568
- case "change_thread_icon":
569
- case 'change_thread_quick_reaction':
570
- return "log:thread-icon";
571
- case "change_thread_nickname":
572
- return "log:user-nickname";
573
- case "change_thread_admins":
574
- return "log:thread-admins";
575
- case "group_poll":
576
- return "log:thread-poll";
577
- case "change_thread_approval_mode":
578
- return "log:thread-approval-mode";
579
- case "messenger_call_log":
580
- case "participant_joined_group_call":
581
- return "log:thread-call";
582
- default:
583
- return type;
584
- }
585
- }
586
-
587
- function formatDeltaEvent(m) {
588
- let logMessageType;
589
- let logMessageData;
590
-
591
- // log:thread-color => {theme_color}
592
- // log:user-nickname => {participant_id, nickname}
593
- // log:thread-icon => {thread_icon}
594
- // log:thread-name => {name}
595
- // log:subscribe => {addedParticipants - [Array]}
596
- // log:unsubscribe => {leftParticipantFbId}
597
-
598
- switch (m.class) {
599
- case "AdminTextMessage":
600
- logMessageData = m.untypedData;
601
- logMessageType = getAdminTextMessageType(m.type);
602
- break;
603
- case "ThreadName":
604
- logMessageType = "log:thread-name";
605
- logMessageData = {
606
- name: m.name
607
- };
608
- break;
609
- case "ParticipantsAddedToGroupThread":
610
- logMessageType = "log:subscribe";
611
- logMessageData = {
612
- addedParticipants: m.addedParticipants
613
- };
614
- break;
615
- case "ParticipantLeftGroupThread":
616
- logMessageType = "log:unsubscribe";
617
- logMessageData = {
618
- leftParticipantFbId: m.leftParticipantFbId
619
- };
620
- break;
621
- case "ApprovalQueue":
622
- logMessageType = "log:approval-queue";
623
- logMessageData = {
624
- approvalQueue: {
625
- action: m.action,
626
- recipientFbId: m.recipientFbId,
627
- requestSource: m.requestSource,
628
- ...m.messageMetadata
629
- }
630
- };
631
- }
632
- return {
633
- type: "event",
634
- threadID: formatID(
635
- (
636
- m.messageMetadata.threadKey.threadFbId ||
637
- m.messageMetadata.threadKey.otherUserFbId
638
- ).toString()
639
- ),
640
- messageID: m.messageMetadata.messageId.toString(),
641
- logMessageType,
642
- logMessageData,
643
- logMessageBody: m.messageMetadata.adminText,
644
- timestamp: m.messageMetadata.timestamp,
645
- author: m.messageMetadata.actorFbId,
646
- participantIDs: m.participants
647
- };
648
- }
649
-
650
- function formatTyp(event) {
651
- return {
652
- isTyping: !!event.st,
653
- from: event.from.toString(),
654
- threadID: formatID(
655
- (event.to || event.thread_fbid || event.from).toString()
656
- ),
657
- // When receiving typ indication from mobile, `from_mobile` isn't set.
658
- // If it is, we just use that value.
659
- fromMobile: event.hasOwnProperty("from_mobile") ? event.from_mobile : true,
660
- userID: (event.realtime_viewer_fbid || event.from).toString(),
661
- type: "typ"
662
- };
663
- }
664
-
665
- function formatDeltaReadReceipt(delta) {
666
- // otherUserFbId seems to be used as both the readerID and the threadID in a 1-1 chat.
667
- // In a group chat actorFbId is used for the reader and threadFbId for the thread.
668
- return {
669
- reader: (delta.threadKey.otherUserFbId || delta.actorFbId).toString(),
670
- time: delta.actionTimestampMs,
671
- threadID: formatID(
672
- (delta.threadKey.otherUserFbId || delta.threadKey.threadFbId).toString()
673
- ),
674
- type: "read_receipt"
675
- };
676
- }
677
-
678
- function formatReadReceipt(event) {
679
- return {
680
- reader: event.reader.toString(),
681
- time: event.time,
682
- threadID: formatID((event.thread_fbid || event.reader).toString()),
683
- type: "read_receipt"
684
- };
685
- }
686
-
687
- function formatRead(event) {
688
- return {
689
- threadID: formatID(
690
- (
691
- (event.chat_ids && event.chat_ids[0]) ||
692
- (event.thread_fbids && event.thread_fbids[0])
693
- ).toString()
694
- ),
695
- time: event.timestamp,
696
- type: "read"
697
- };
698
- }
699
-
700
- function getFrom(str, startToken, endToken) {
701
- const start = str.indexOf(startToken) + startToken.length;
702
- if (start < startToken.length) return "";
703
-
704
- const lastHalf = str.substring(start);
705
- const end = lastHalf.indexOf(endToken);
706
- if (end === -1) {
707
- throw Error(
708
- "Could not find endTime `" + endToken + "` in the given string."
709
- );
710
- }
711
- return lastHalf.substring(0, end);
712
- }
713
-
714
- function makeParsable(html) {
715
- const withoutForLoop = html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
716
-
717
- // (What the fuck FB, why windows style newlines?)
718
- // So sometimes FB will send us base multiple objects in the same response.
719
- // They're all valid JSON, one after the other, at the top level. We detect
720
- // that and make it parse-able by JSON.parse.
721
- // Ben - July 15th 2017
722
- //
723
- // It turns out that Facebook may insert random number of spaces before
724
- // next object begins (issue #616)
725
- // rav_kr - 2018-03-19
726
- const maybeMultipleObjects = withoutForLoop.split(/\}\r\n *\{/);
727
- if (maybeMultipleObjects.length === 1) return maybeMultipleObjects;
728
-
729
- return "[" + maybeMultipleObjects.join("},{") + "]";
730
- }
731
-
732
- function arrToForm(form) {
733
- return arrayToObject(
734
- form,
735
- function (v) {
736
- return v.name;
737
- },
738
- function (v) {
739
- return v.val;
740
- }
741
- );
742
- }
743
-
744
- function arrayToObject(arr, getKey, getValue) {
745
- return arr.reduce(function (acc, val) {
746
- acc[getKey(val)] = getValue(val);
747
- return acc;
748
- }, {});
749
- }
750
-
751
- function getSignatureID() {
752
- return Math.floor(Math.random() * 2147483648).toString(16);
753
- }
754
-
755
- function generateTimestampRelative() {
756
- const d = new Date();
757
- return d.getHours() + ":" + padZeros(d.getMinutes());
758
- }
759
-
760
- function makeDefaults(html, userID, ctx) {
761
- let reqCounter = 1;
762
- const revision = getFrom(html, 'revision":', ",");
763
- function mergeWithDefaults(obj) {
764
- const newObj = {
765
- av: userID,
766
- __user: userID,
767
- __req: (reqCounter++).toString(36),
768
- __rev: revision,
769
- __a: 1,
770
- ...(ctx && {
771
- fb_dtsg: ctx.fb_dtsg,
772
- jazoest: ctx.jazoest
773
- })
774
- }
775
-
776
- if (!obj) return newObj;
777
-
778
- for (var prop in obj) {
779
- if (obj.hasOwnProperty(prop)) {
780
- if (!newObj[prop])
781
- newObj[prop] = obj[prop];
782
- }
783
- }
784
-
785
- return newObj;
786
- }
787
-
788
- return {
789
- get: (url, jar, qs, ctxx, customHeader = {}) => get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx, customHeader),
790
- post: (url, jar, form, ctxx, customHeader = {}) => post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx, customHeader),
791
- postFormData: (url, jar, form, qs, ctxx) => postFormData(url, jar, mergeWithDefaults(form), mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx)
792
- };
793
- }
794
-
795
- function parseAndCheckLogin(ctx, http, retryCount) {
796
- var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
797
- var _try = (tryData) => new Promise<any>(function (resolve, reject) {
798
- try {
799
- resolve(tryData());
800
- } catch (error) {
801
- reject(error);
802
- }
803
- });
804
- if (retryCount == undefined) retryCount = 0;
805
-
806
- return function (data) {
807
- function any() {
808
- if (data.statusCode >= 500 && data.statusCode < 600) {
809
- if (retryCount >= 5) {
810
- const err = new Error("Request retry failed. Check the `res` and `statusCode` property on this error.");
811
- err.statusCode = data.statusCode;
812
- err.res = data.body;
813
- err.error = "Request retry failed. Check the `res` and `statusCode` property on this error.";
814
- throw err;
815
- }
816
- retryCount++;
817
- const retryTime = Math.floor(Math.random() * 5000);
818
- console.warn("parseAndCheckLogin", "Got status code " + data.statusCode + " - " + retryCount + ". attempt to retry in " + retryTime + " milliseconds...");
819
- const url = data.request.uri.protocol + "//" + data.request.uri.hostname + data.request.uri.pathname;
820
- if (data.request.headers["content-type"].split(";")[0] === "multipart/form-data") {
821
- return delay(retryTime)
822
- .then(function () {
823
- return http
824
- .postFormData(url, ctx.jar, data.request.formData);
825
- })
826
- .then(parseAndCheckLogin(ctx, http, retryCount));
827
- } else {
828
- return delay(retryTime)
829
- .then(function () {
830
- return http
831
- .post(url, ctx.jar, data.request.formData);
832
- })
833
- .then(parseAndCheckLogin(ctx, http, retryCount));
834
- }
835
- }
836
-
837
- if (data.statusCode === 404) return;
838
-
839
- if (data.statusCode !== 200)
840
- throw new Error("parseAndCheckLogin got status code: " + data.statusCode + ". Bailing out of trying to parse response.");
841
-
842
- let res = null;
843
- try {
844
- res = JSON.parse(makeParsable(data.body));
845
- } catch (e) {
846
- const err = new Error("JSON.parse error. Check the `detail` property on this error.");
847
- err.error = "JSON.parse error. Check the `detail` property on this error.";
848
- err.detail = e;
849
- err.res = data.body;
850
- throw err;
851
- }
852
-
853
- // In some cases the response contains only a redirect URL which should be followed
854
- if (res.redirect && data.request.method === "GET") {
855
- return http
856
- .get(res.redirect, ctx.jar)
857
- .then(parseAndCheckLogin(ctx, http));
858
- }
859
-
860
- // TODO: handle multiple cookies?
861
- if (res.jsmods && res.jsmods.require && Array.isArray(res.jsmods.require[0]) && res.jsmods.require[0][0] === "Cookie") {
862
- res.jsmods.require[0][3][0] = res.jsmods.require[0][3][0].replace("_js_", "");
863
- const requireCookie = res.jsmods.require[0][3];
864
- ctx.jar.setCookie(formatCookie(requireCookie, "facebook"), "https://www.facebook.com");
865
- ctx.jar.setCookie(formatCookie(requireCookie, "messenger"), "https://www.messenger.com");
866
- }
867
-
868
- // On every request we check if we got a DTSG and we mutate the context so that we use the latest
869
- // one for the next requests.
870
- if (res.jsmods && Array.isArray(res.jsmods.require)) {
871
- const arr = res.jsmods.require;
872
- for (const i in arr) {
873
- if (arr[i][0] === "DTSG" && arr[i][1] === "setToken") {
874
- ctx.fb_dtsg = arr[i][3][0];
875
-
876
- // Update ttstamp since that depends on fb_dtsg
877
- ctx.ttstamp = "2";
878
- for (let j = 0; j < ctx.fb_dtsg.length; j++) {
879
- ctx.ttstamp += ctx.fb_dtsg.charCodeAt(j);
880
- }
881
- }
882
- }
883
- }
884
-
885
- if (res.error === 1357001) {
886
- const err = new Error('Facebook blocked the login');
887
- err.error = "Not logged in.";
888
- throw err;
889
- }
890
- return res;
891
- }
892
- return _try(any);
893
- };
894
- }
895
-
896
- function saveCookies(jar) {
897
- return function (res) {
898
- const cookies = res.headers["set-cookie"] || [];
899
- cookies.forEach(function (c) {
900
- if (c.indexOf(".facebook.com") > -1) {
901
- jar.setCookie(c, "https://www.facebook.com");
902
- }
903
- const c2 = c.replace(/domain=\.facebook\.com/, "domain=.messenger.com");
904
- jar.setCookie(c2, "https://www.messenger.com");
905
- });
906
- return res;
907
- };
908
- }
909
-
910
- function formatThread(data) {
911
- return {
912
- threadID: formatID(data.thread_fbid.toString()),
913
- participants: data.participants.map(formatID),
914
- participantIDs: data.participants.map(formatID),
915
- name: data.name,
916
- nicknames: data.custom_nickname,
917
- snippet: data.snippet,
918
- snippetAttachments: data.snippet_attachments,
919
- snippetSender: formatID((data.snippet_sender || "").toString()),
920
- unreadCount: data.unread_count,
921
- messageCount: data.message_count,
922
- imageSrc: data.image_src,
923
- timestamp: data.timestamp,
924
- serverTimestamp: data.server_timestamp, // what is this?
925
- muteUntil: data.mute_until,
926
- isCanonicalUser: data.is_canonical_user,
927
- isCanonical: data.is_canonical,
928
- isSubscribed: data.is_subscribed,
929
- folder: data.folder,
930
- isArchived: data.is_archived,
931
- recipientsLoadable: data.recipients_loadable,
932
- hasEmailParticipant: data.has_email_participant,
933
- readOnly: data.read_only,
934
- canReply: data.can_reply,
935
- cannotReplyReason: data.cannot_reply_reason,
936
- lastMessageTimestamp: data.last_message_timestamp,
937
- lastReadTimestamp: data.last_read_timestamp,
938
- lastMessageType: data.last_message_type,
939
- emoji: data.custom_like_icon,
940
- color: data.custom_color,
941
- adminIDs: data.admin_ids,
942
- threadType: data.thread_type
943
- };
944
- }
945
-
946
- function formatDate(date) {
947
- let d = date.getUTCDate();
948
- d = d >= 10 ? d : "0" + d;
949
- let h = date.getUTCHours();
950
- h = h >= 10 ? h : "0" + h;
951
- let m = date.getUTCMinutes();
952
- m = m >= 10 ? m : "0" + m;
953
- let s = date.getUTCSeconds();
954
- s = s >= 10 ? s : "0" + s;
955
- return (
956
- NUM_TO_DAY[date.getUTCDay()] +
957
- ", " +
958
- d +
959
- " " +
960
- NUM_TO_MONTH[date.getUTCMonth()] +
961
- " " +
962
- date.getUTCFullYear() +
963
- " " +
964
- h +
965
- ":" +
966
- m +
967
- ":" +
968
- s +
969
- " GMT"
970
- );
971
- }
972
-
973
- function formatCookie(arr, url) {
974
- return (
975
- arr[0] + "=" + arr[1] + "; Path=" + arr[3] + "; Domain=" + url + ".com"
976
- );
977
- }
978
-
979
- function formatProxyPresence(presence, userID) {
980
- if (presence.lat === undefined || presence.p === undefined) return null;
981
- return {
982
- type: "presence",
983
- timestamp: presence.lat * 1000,
984
- userID: userID,
985
- statuses: presence.p
986
- };
987
- }
988
-
989
- function formatPresence(presence, userID) {
990
- return {
991
- type: "presence",
992
- timestamp: presence.la * 1000,
993
- userID: userID,
994
- statuses: presence.a
995
- };
996
- }
997
-
998
- function decodeClientPayload(payload) {
999
- /*
1000
- Special function which Client using to "encode" clients JSON payload
1001
- */
1002
- return JSON.parse(String.fromCharCode.apply(null, payload));
1003
- }
1004
-
1005
-
1006
- module.exports = {
1007
-
1008
- isReadableStream,
1009
-
1010
- getExtension,
1011
-
1012
- _formatAttachment,
1013
-
1014
- formatAttachment,
1015
-
1016
- formatDeltaMessage,
1017
-
1018
- formatID,
1019
-
1020
- formatMessage,
1021
-
1022
- formatEvent,
1023
-
1024
- formatHistoryMessage,
1025
-
1026
- getAdminTextMessageType,
1027
-
1028
- formatDeltaEvent,
1029
-
1030
- formatTyp,
1031
-
1032
- formatDeltaReadReceipt,
1033
-
1034
- formatReadReceipt,
1035
-
1036
- formatRead,
1037
-
1038
- formatDate,
1039
-
1040
- formatCookie,
1041
-
1042
- formatThread,
1043
-
1044
- formatProxyPresence,
1045
-
1046
- formatPresence,
1047
-
1048
- decodeClientPayload,
1049
- };