alicezetion 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/.cache/replit/__replit_disk_meta.json +1 -1
  2. package/.cache/replit/nix/env.json +1 -1
  3. package/alice/addExternalModule.js +19 -19
  4. package/alice/addUserToGroup.js +113 -113
  5. package/alice/changeAdminStatus.js +79 -79
  6. package/alice/changeArchivedStatus.js +55 -55
  7. package/alice/changeBio.js +77 -77
  8. package/alice/changeBlockedStatus.js +47 -47
  9. package/alice/changeGroupImage.js +129 -129
  10. package/alice/changeNickname.js +59 -59
  11. package/alice/changeThreadColor.js +71 -71
  12. package/alice/changeThreadEmoji.js +55 -55
  13. package/alice/{sendMessage.js → chat.js} +459 -459
  14. package/alice/createNewGroup.js +86 -86
  15. package/alice/createPoll.js +71 -71
  16. package/alice/deleteMessage.js +56 -56
  17. package/alice/deleteThread.js +56 -56
  18. package/alice/forwardAttachment.js +60 -60
  19. package/alice/getCurrentUserID.js +7 -7
  20. package/alice/getEmojiUrl.js +29 -29
  21. package/alice/getFriendsList.js +84 -84
  22. package/alice/getThreadHistory.js +645 -645
  23. package/alice/getThreadHistoryDeprecated.js +93 -93
  24. package/alice/getThreadInfo.js +206 -206
  25. package/alice/getThreadInfoDeprecated.js +80 -80
  26. package/alice/getThreadList.js +238 -238
  27. package/alice/getThreadListDeprecated.js +75 -75
  28. package/alice/getThreadPictures.js +79 -79
  29. package/alice/getUserID.js +66 -66
  30. package/alice/getUserInfo.js +72 -72
  31. package/alice/handleFriendRequest.js +61 -61
  32. package/alice/handleMessageRequest.js +65 -65
  33. package/alice/httpGet.js +52 -52
  34. package/alice/httpPost.js +52 -52
  35. package/alice/listenMqtt.js +789 -789
  36. package/alice/logout.js +75 -75
  37. package/alice/markAsDelivered.js +58 -58
  38. package/alice/markAsRead.js +80 -80
  39. package/alice/markAsSeen.js +59 -59
  40. package/alice/muteThread.js +52 -52
  41. package/alice/{setMessageReaction.js → react.js} +117 -117
  42. package/alice/removeUserFromGroup.js +79 -79
  43. package/alice/resolvePhotoUrl.js +45 -45
  44. package/alice/searchForThread.js +53 -53
  45. package/alice/{markAsReadAll.js → seen.js} +49 -49
  46. package/alice/sendTypingIndicator.js +103 -103
  47. package/alice/setPostReaction.js +76 -76
  48. package/alice/setTitle.js +86 -86
  49. package/alice/threadColors.js +57 -57
  50. package/alice/unfriend.js +52 -52
  51. package/alice/unsendMessage.js +49 -49
  52. package/index.js +604 -604
  53. package/package.json +74 -77
  54. package/test/data/shareAttach.js +146 -0
  55. package/test/data/something.mov +0 -0
  56. package/test/data/test.png +0 -0
  57. package/test/data/test.txt +7 -0
  58. package/test/example-config.json +18 -0
  59. package/test/test-page.js +140 -0
  60. package/test/test.js +385 -0
  61. package/utils.js +1357 -1359
  62. package/.cache/replit/modules.stamp +0 -0
  63. package/.travis.yml +0 -6
@@ -1,645 +1,645 @@
1
- "use strict";
2
-
3
- var utils = require("../utils");
4
- var log = require("npmlog");
5
-
6
- function formatAttachmentsGraphQLResponse(attachment) {
7
- switch (attachment.__typename) {
8
- case "MessageImage":
9
- return {
10
- type: "photo",
11
- ID: attachment.legacy_attachment_id,
12
- filename: attachment.filename,
13
- thumbnailUrl: attachment.thumbnail.uri,
14
-
15
- previewUrl: attachment.preview.uri,
16
- previewWidth: attachment.preview.width,
17
- previewHeight: attachment.preview.height,
18
-
19
- largePreviewUrl: attachment.large_preview.uri,
20
- largePreviewHeight: attachment.large_preview.height,
21
- largePreviewWidth: attachment.large_preview.width,
22
-
23
- // You have to query for the real image. See below.
24
- url: attachment.large_preview.uri, // @Legacy
25
- width: attachment.large_preview.width, // @Legacy
26
- height: attachment.large_preview.height, // @Legacy
27
- name: attachment.filename, // @Legacy
28
-
29
- // @Undocumented
30
- attributionApp: attachment.attribution_app
31
- ? {
32
- attributionAppID: attachment.attribution_app.id,
33
- name: attachment.attribution_app.name,
34
- logo: attachment.attribution_app.square_logo
35
- }
36
- : null
37
-
38
- // @TODO No idea what this is, should we expose it?
39
- // Ben - July 15th 2017
40
- // renderAsSticker: attachment.render_as_sticker,
41
-
42
- // This is _not_ the real URI, this is still just a large preview.
43
- // To get the URL we'll need to support a POST query to
44
- //
45
- // https://www.facebook.com/webgraphql/query/
46
- //
47
- // With the following query params:
48
- //
49
- // query_id:728987990612546
50
- // variables:{"id":"100009069356507","photoID":"10213724771692996"}
51
- // dpr:1
52
- //
53
- // No special form though.
54
- };
55
- case "MessageAnimatedImage":
56
- return {
57
- type: "animated_image",
58
- ID: attachment.legacy_attachment_id,
59
- filename: attachment.filename,
60
-
61
- previewUrl: attachment.preview_image.uri,
62
- previewWidth: attachment.preview_image.width,
63
- previewHeight: attachment.preview_image.height,
64
-
65
- url: attachment.animated_image.uri,
66
- width: attachment.animated_image.width,
67
- height: attachment.animated_image.height,
68
-
69
- thumbnailUrl: attachment.preview_image.uri, // @Legacy
70
- name: attachment.filename, // @Legacy
71
- facebookUrl: attachment.animated_image.uri, // @Legacy
72
- rawGifImage: attachment.animated_image.uri, // @Legacy
73
- animatedGifUrl: attachment.animated_image.uri, // @Legacy
74
- animatedGifPreviewUrl: attachment.preview_image.uri, // @Legacy
75
- animatedWebpUrl: attachment.animated_image.uri, // @Legacy
76
- animatedWebpPreviewUrl: attachment.preview_image.uri, // @Legacy
77
-
78
- // @Undocumented
79
- attributionApp: attachment.attribution_app
80
- ? {
81
- attributionAppID: attachment.attribution_app.id,
82
- name: attachment.attribution_app.name,
83
- logo: attachment.attribution_app.square_logo
84
- }
85
- : null
86
- };
87
- case "MessageVideo":
88
- return {
89
- type: "video",
90
- filename: attachment.filename,
91
- ID: attachment.legacy_attachment_id,
92
-
93
- thumbnailUrl: attachment.large_image.uri, // @Legacy
94
-
95
- previewUrl: attachment.large_image.uri,
96
- previewWidth: attachment.large_image.width,
97
- previewHeight: attachment.large_image.height,
98
-
99
- url: attachment.playable_url,
100
- width: attachment.original_dimensions.x,
101
- height: attachment.original_dimensions.y,
102
-
103
- duration: attachment.playable_duration_in_ms,
104
- videoType: attachment.video_type.toLowerCase()
105
- };
106
- case "MessageFile":
107
- return {
108
- type: "file",
109
- filename: attachment.filename,
110
- ID: attachment.message_file_fbid,
111
-
112
- url: attachment.url,
113
- isMalicious: attachment.is_malicious,
114
- contentType: attachment.content_type,
115
-
116
- name: attachment.filename, // @Legacy
117
- mimeType: "", // @Legacy
118
- fileSize: -1 // @Legacy
119
- };
120
- case "MessageAudio":
121
- return {
122
- type: "audio",
123
- filename: attachment.filename,
124
- ID: attachment.url_shimhash, // Not fowardable
125
-
126
- audioType: attachment.audio_type,
127
- duration: attachment.playable_duration_in_ms,
128
- url: attachment.playable_url,
129
-
130
- isVoiceMail: attachment.is_voicemail
131
- };
132
- default:
133
- return {
134
- error: "Don't know about attachment type " + attachment.__typename
135
- };
136
- }
137
- }
138
-
139
- function formatExtensibleAttachment(attachment) {
140
- if (attachment.story_attachment) {
141
- return {
142
- type: "share",
143
- ID: attachment.legacy_attachment_id,
144
- url: attachment.story_attachment.url,
145
-
146
- title: attachment.story_attachment.title_with_entities.text,
147
- description:
148
- attachment.story_attachment.description &&
149
- attachment.story_attachment.description.text,
150
- source:
151
- attachment.story_attachment.source == null
152
- ? null
153
- : attachment.story_attachment.source.text,
154
-
155
- image:
156
- attachment.story_attachment.media == null
157
- ? null
158
- : attachment.story_attachment.media.animated_image == null &&
159
- attachment.story_attachment.media.image == null
160
- ? null
161
- : (
162
- attachment.story_attachment.media.animated_image ||
163
- attachment.story_attachment.media.image
164
- ).uri,
165
- width:
166
- attachment.story_attachment.media == null
167
- ? null
168
- : attachment.story_attachment.media.animated_image == null &&
169
- attachment.story_attachment.media.image == null
170
- ? null
171
- : (
172
- attachment.story_attachment.media.animated_image ||
173
- attachment.story_attachment.media.image
174
- ).width,
175
- height:
176
- attachment.story_attachment.media == null
177
- ? null
178
- : attachment.story_attachment.media.animated_image == null &&
179
- attachment.story_attachment.media.image == null
180
- ? null
181
- : (
182
- attachment.story_attachment.media.animated_image ||
183
- attachment.story_attachment.media.image
184
- ).height,
185
- playable:
186
- attachment.story_attachment.media == null
187
- ? null
188
- : attachment.story_attachment.media.is_playable,
189
- duration:
190
- attachment.story_attachment.media == null
191
- ? null
192
- : attachment.story_attachment.media.playable_duration_in_ms,
193
- playableUrl:
194
- attachment.story_attachment.media == null
195
- ? null
196
- : attachment.story_attachment.media.playable_url,
197
-
198
- subattachments: attachment.story_attachment.subattachments,
199
-
200
- // Format example:
201
- //
202
- // [{
203
- // key: "width",
204
- // value: { text: "1280" }
205
- // }]
206
- //
207
- // That we turn into:
208
- //
209
- // {
210
- // width: "1280"
211
- // }
212
- //
213
- properties: attachment.story_attachment.properties.reduce(function(
214
- obj,
215
- cur
216
- ) {
217
- obj[cur.key] = cur.value.text;
218
- return obj;
219
- },
220
- {}),
221
-
222
- // Deprecated fields
223
- animatedImageSize: "", // @Legacy
224
- facebookUrl: "", // @Legacy
225
- styleList: "", // @Legacy
226
- target: "", // @Legacy
227
- thumbnailUrl:
228
- attachment.story_attachment.media == null
229
- ? null
230
- : attachment.story_attachment.media.animated_image == null &&
231
- attachment.story_attachment.media.image == null
232
- ? null
233
- : (
234
- attachment.story_attachment.media.animated_image ||
235
- attachment.story_attachment.media.image
236
- ).uri, // @Legacy
237
- thumbnailWidth:
238
- attachment.story_attachment.media == null
239
- ? null
240
- : attachment.story_attachment.media.animated_image == null &&
241
- attachment.story_attachment.media.image == null
242
- ? null
243
- : (
244
- attachment.story_attachment.media.animated_image ||
245
- attachment.story_attachment.media.image
246
- ).width, // @Legacy
247
- thumbnailHeight:
248
- attachment.story_attachment.media == null
249
- ? null
250
- : attachment.story_attachment.media.animated_image == null &&
251
- attachment.story_attachment.media.image == null
252
- ? null
253
- : (
254
- attachment.story_attachment.media.animated_image ||
255
- attachment.story_attachment.media.image
256
- ).height // @Legacy
257
- };
258
- } else {
259
- return { error: "Don't know what to do with extensible_attachment." };
260
- }
261
- }
262
-
263
- function formatReactionsGraphQL(reaction) {
264
- return {
265
- reaction: reaction.reaction,
266
- userID: reaction.user.id
267
- };
268
- }
269
-
270
- function formatEventData(event) {
271
- if (event == null) {
272
- return {};
273
- }
274
-
275
- switch (event.__typename) {
276
- case "ThemeColorExtensibleMessageAdminText":
277
- return {
278
- color: event.theme_color
279
- };
280
- case "ThreadNicknameExtensibleMessageAdminText":
281
- return {
282
- nickname: event.nickname,
283
- participantID: event.participant_id
284
- };
285
- case "ThreadIconExtensibleMessageAdminText":
286
- return {
287
- threadIcon: event.thread_icon
288
- };
289
- case "InstantGameUpdateExtensibleMessageAdminText":
290
- return {
291
- gameID: (event.game == null ? null : event.game.id),
292
- update_type: event.update_type,
293
- collapsed_text: event.collapsed_text,
294
- expanded_text: event.expanded_text,
295
- instant_game_update_data: event.instant_game_update_data
296
- };
297
- case "GameScoreExtensibleMessageAdminText":
298
- return {
299
- game_type: event.game_type
300
- };
301
- case "RtcCallLogExtensibleMessageAdminText":
302
- return {
303
- event: event.event,
304
- is_video_call: event.is_video_call,
305
- server_info_data: event.server_info_data
306
- };
307
- case "GroupPollExtensibleMessageAdminText":
308
- return {
309
- event_type: event.event_type,
310
- total_count: event.total_count,
311
- question: event.question
312
- };
313
- case "AcceptPendingThreadExtensibleMessageAdminText":
314
- return {
315
- accepter_id: event.accepter_id,
316
- requester_id: event.requester_id
317
- };
318
- case "ConfirmFriendRequestExtensibleMessageAdminText":
319
- return {
320
- friend_request_recipient: event.friend_request_recipient,
321
- friend_request_sender: event.friend_request_sender
322
- };
323
- case "AddContactExtensibleMessageAdminText":
324
- return {
325
- contact_added_id: event.contact_added_id,
326
- contact_adder_id: event.contact_adder_id
327
- };
328
- case "AdExtensibleMessageAdminText":
329
- return {
330
- ad_client_token: event.ad_client_token,
331
- ad_id: event.ad_id,
332
- ad_preferences_link: event.ad_preferences_link,
333
- ad_properties: event.ad_properties
334
- };
335
- // never data
336
- case "ParticipantJoinedGroupCallExtensibleMessageAdminText":
337
- case "ThreadEphemeralTtlModeExtensibleMessageAdminText":
338
- case "StartedSharingVideoExtensibleMessageAdminText":
339
- case "LightweightEventCreateExtensibleMessageAdminText":
340
- case "LightweightEventNotifyExtensibleMessageAdminText":
341
- case "LightweightEventNotifyBeforeEventExtensibleMessageAdminText":
342
- case "LightweightEventUpdateTitleExtensibleMessageAdminText":
343
- case "LightweightEventUpdateTimeExtensibleMessageAdminText":
344
- case "LightweightEventUpdateLocationExtensibleMessageAdminText":
345
- case "LightweightEventDeleteExtensibleMessageAdminText":
346
- return {};
347
- default:
348
- return {
349
- error: "Don't know what to with event data type " + event.__typename
350
- };
351
- }
352
- }
353
-
354
- function formatMessagesGraphQLResponse(data) {
355
- var messageThread = data.o0.data.message_thread;
356
- var threadID = messageThread.thread_key.thread_fbid
357
- ? messageThread.thread_key.thread_fbid
358
- : messageThread.thread_key.other_user_id;
359
-
360
- var messages = messageThread.messages.nodes.map(function(d) {
361
- switch (d.__typename) {
362
- case "UserMessage":
363
- // Give priority to stickers. They're seen as normal messages but we've
364
- // been considering them as attachments.
365
- var maybeStickerAttachment;
366
- if (d.sticker) {
367
- maybeStickerAttachment = [
368
- {
369
- type: "sticker",
370
- ID: d.sticker.id,
371
- url: d.sticker.url,
372
-
373
- packID: d.sticker.pack.id,
374
- spriteUrl: d.sticker.sprite_image,
375
- spriteUrl2x: d.sticker.sprite_image_2x,
376
- width: d.sticker.width,
377
- height: d.sticker.height,
378
-
379
- caption: d.snippet, // Not sure what the heck caption was.
380
- description: d.sticker.label, // Not sure about this one either.
381
-
382
- frameCount: d.sticker.frame_count,
383
- frameRate: d.sticker.frame_rate,
384
- framesPerRow: d.sticker.frames_per_row,
385
- framesPerCol: d.sticker.frames_per_col,
386
-
387
- stickerID: d.sticker.id, // @Legacy
388
- spriteURI: d.sticker.sprite_image, // @Legacy
389
- spriteURI2x: d.sticker.sprite_image_2x // @Legacy
390
- }
391
- ];
392
- }
393
-
394
- var mentionsObj = {};
395
- if (d.message !== null) {
396
- d.message.ranges.forEach(e => {
397
- mentionsObj[e.entity.id] = d.message.text.substr(e.offset, e.length);
398
- });
399
- }
400
-
401
- return {
402
- type: "message",
403
- attachments: maybeStickerAttachment
404
- ? maybeStickerAttachment
405
- : d.blob_attachments && d.blob_attachments.length > 0
406
- ? d.blob_attachments.map(formatAttachmentsGraphQLResponse)
407
- : d.extensible_attachment
408
- ? [formatExtensibleAttachment(d.extensible_attachment)]
409
- : [],
410
- body: d.message !== null ? d.message.text : '',
411
- isGroup: messageThread.thread_type === "GROUP",
412
- messageID: d.message_id,
413
- senderID: d.message_sender.id,
414
- threadID: threadID,
415
- timestamp: d.timestamp_precise,
416
-
417
- mentions: mentionsObj,
418
- isUnread: d.unread,
419
-
420
- // New
421
- messageReactions: d.message_reactions
422
- ? d.message_reactions.map(formatReactionsGraphQL)
423
- : null,
424
- isSponsored: d.is_sponsored,
425
- snippet: d.snippet
426
- };
427
- case "ThreadNameMessage":
428
- return {
429
- type: "event",
430
- messageID: d.message_id,
431
- threadID: threadID,
432
- isGroup: messageThread.thread_type === "GROUP",
433
- senderID: d.message_sender.id,
434
- timestamp: d.timestamp_precise,
435
- eventType: "change_thread_name",
436
- snippet: d.snippet,
437
- eventData: {
438
- threadName: d.thread_name
439
- },
440
-
441
- // @Legacy
442
- author: d.message_sender.id,
443
- logMessageType: "log:thread-name",
444
- logMessageData: { name: d.thread_name }
445
- };
446
- case "ThreadImageMessage":
447
- return {
448
- type: "event",
449
- messageID: d.message_id,
450
- threadID: threadID,
451
- isGroup: messageThread.thread_type === "GROUP",
452
- senderID: d.message_sender.id,
453
- timestamp: d.timestamp_precise,
454
- eventType: "change_thread_image",
455
- snippet: d.snippet,
456
- eventData:
457
- d.image_with_metadata == null
458
- ? {} /* removed image */
459
- : {
460
- /* image added */
461
- threadImage: {
462
- attachmentID: d.image_with_metadata.legacy_attachment_id,
463
- width: d.image_with_metadata.original_dimensions.x,
464
- height: d.image_with_metadata.original_dimensions.y,
465
- url: d.image_with_metadata.preview.uri
466
- }
467
- },
468
-
469
- // @Legacy
470
- logMessageType: "log:thread-icon",
471
- logMessageData: {
472
- thread_icon: d.image_with_metadata
473
- ? d.image_with_metadata.preview.uri
474
- : null
475
- }
476
- };
477
- case "ParticipantLeftMessage":
478
- return {
479
- type: "event",
480
- messageID: d.message_id,
481
- threadID: threadID,
482
- isGroup: messageThread.thread_type === "GROUP",
483
- senderID: d.message_sender.id,
484
- timestamp: d.timestamp_precise,
485
- eventType: "remove_participants",
486
- snippet: d.snippet,
487
- eventData: {
488
- // Array of IDs.
489
- participantsRemoved: d.participants_removed.map(function(p) {
490
- return p.id;
491
- })
492
- },
493
-
494
- // @Legacy
495
- logMessageType: "log:unsubscribe",
496
- logMessageData: {
497
- leftParticipantFbId: d.participants_removed.map(function(p) {
498
- return p.id;
499
- })
500
- }
501
- };
502
- case "ParticipantsAddedMessage":
503
- return {
504
- type: "event",
505
- messageID: d.message_id,
506
- threadID: threadID,
507
- isGroup: messageThread.thread_type === "GROUP",
508
- senderID: d.message_sender.id,
509
- timestamp: d.timestamp_precise,
510
- eventType: "add_participants",
511
- snippet: d.snippet,
512
- eventData: {
513
- // Array of IDs.
514
- participantsAdded: d.participants_added.map(function(p) {
515
- return p.id;
516
- })
517
- },
518
-
519
- // @Legacy
520
- logMessageType: "log:subscribe",
521
- logMessageData: {
522
- addedParticipants: d.participants_added.map(function(p) {
523
- return p.id;
524
- })
525
- }
526
- };
527
- case "VideoCallMessage":
528
- return {
529
- type: "event",
530
- messageID: d.message_id,
531
- threadID: threadID,
532
- isGroup: messageThread.thread_type === "GROUP",
533
- senderID: d.message_sender.id,
534
- timestamp: d.timestamp_precise,
535
- eventType: "video_call",
536
- snippet: d.snippet,
537
-
538
- // @Legacy
539
- logMessageType: "other"
540
- };
541
- case "VoiceCallMessage":
542
- return {
543
- type: "event",
544
- messageID: d.message_id,
545
- threadID: threadID,
546
- isGroup: messageThread.thread_type === "GROUP",
547
- senderID: d.message_sender.id,
548
- timestamp: d.timestamp_precise,
549
- eventType: "voice_call",
550
- snippet: d.snippet,
551
-
552
- // @Legacy
553
- logMessageType: "other"
554
- };
555
- case "GenericAdminTextMessage":
556
- return {
557
- type: "event",
558
- messageID: d.message_id,
559
- threadID: threadID,
560
- isGroup: messageThread.thread_type === "GROUP",
561
- senderID: d.message_sender.id,
562
- timestamp: d.timestamp_precise,
563
- snippet: d.snippet,
564
- eventType: d.extensible_message_admin_text_type.toLowerCase(),
565
- eventData: formatEventData(d.extensible_message_admin_text),
566
-
567
- // @Legacy
568
- logMessageType: utils.getAdminTextMessageType(
569
- d.extensible_message_admin_text_type
570
- ),
571
- logMessageData: d.extensible_message_admin_text // Maybe different?
572
- };
573
- default:
574
- return { error: "Don't know about message type " + d.__typename };
575
- }
576
- });
577
- return messages;
578
- }
579
-
580
- module.exports = function(defaultFuncs, api, ctx) {
581
- return function getThreadHistoryGraphQL(
582
- threadID,
583
- amount,
584
- timestamp,
585
- callback
586
- ) {
587
- var resolveFunc = function(){};
588
- var rejectFunc = function(){};
589
- var returnPromise = new Promise(function (resolve, reject) {
590
- resolveFunc = resolve;
591
- rejectFunc = reject;
592
- });
593
-
594
- if (!callback) {
595
- callback = function (err, data) {
596
- if (err) {
597
- return rejectFunc(err);
598
- }
599
- resolveFunc(data);
600
- };
601
- }
602
-
603
- // `queries` has to be a string. I couldn't tell from the dev console. This
604
- // took me a really long time to figure out. I deserve a cookie for this.
605
- var form = {
606
- "av": ctx.globalOptions.pageID,
607
- queries: JSON.stringify({
608
- o0: {
609
- // This doc_id was valid on February 2nd 2017.
610
- doc_id: "1498317363570230",
611
- query_params: {
612
- id: threadID,
613
- message_limit: amount,
614
- load_messages: 1,
615
- load_read_receipts: false,
616
- before: timestamp
617
- }
618
- }
619
- })
620
- };
621
-
622
- defaultFuncs
623
- .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
624
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
625
- .then(function(resData) {
626
- if (resData.error) {
627
- throw resData;
628
- }
629
- // This returns us an array of things. The last one is the success /
630
- // failure one.
631
- // @TODO What do we do in this case?
632
- if (resData[resData.length - 1].error_results !== 0) {
633
- throw new Error("There was an error_result.");
634
- }
635
-
636
- callback(null, formatMessagesGraphQLResponse(resData[0]));
637
- })
638
- .catch(function(err) {
639
- log.error("getThreadHistoryGraphQL", err);
640
- return callback(err);
641
- });
642
-
643
- return returnPromise;
644
- };
645
- };
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ function formatAttachmentsGraphQLResponse(attachment) {
7
+ switch (attachment.__typename) {
8
+ case "MessageImage":
9
+ return {
10
+ type: "photo",
11
+ ID: attachment.legacy_attachment_id,
12
+ filename: attachment.filename,
13
+ thumbnailUrl: attachment.thumbnail.uri,
14
+
15
+ previewUrl: attachment.preview.uri,
16
+ previewWidth: attachment.preview.width,
17
+ previewHeight: attachment.preview.height,
18
+
19
+ largePreviewUrl: attachment.large_preview.uri,
20
+ largePreviewHeight: attachment.large_preview.height,
21
+ largePreviewWidth: attachment.large_preview.width,
22
+
23
+ // You have to query for the real image. See below.
24
+ url: attachment.large_preview.uri, // @Legacy
25
+ width: attachment.large_preview.width, // @Legacy
26
+ height: attachment.large_preview.height, // @Legacy
27
+ name: attachment.filename, // @Legacy
28
+
29
+ // @Undocumented
30
+ attributionApp: attachment.attribution_app
31
+ ? {
32
+ attributionAppID: attachment.attribution_app.id,
33
+ name: attachment.attribution_app.name,
34
+ logo: attachment.attribution_app.square_logo
35
+ }
36
+ : null
37
+
38
+ // @TODO No idea what this is, should we expose it?
39
+ // Ben - July 15th 2017
40
+ // renderAsSticker: attachment.render_as_sticker,
41
+
42
+ // This is _not_ the real URI, this is still just a large preview.
43
+ // To get the URL we'll need to support a POST query to
44
+ //
45
+ // https://www.facebook.com/webgraphql/query/
46
+ //
47
+ // With the following query params:
48
+ //
49
+ // query_id:728987990612546
50
+ // variables:{"id":"100009069356507","photoID":"10213724771692996"}
51
+ // dpr:1
52
+ //
53
+ // No special form though.
54
+ };
55
+ case "MessageAnimatedImage":
56
+ return {
57
+ type: "animated_image",
58
+ ID: attachment.legacy_attachment_id,
59
+ filename: attachment.filename,
60
+
61
+ previewUrl: attachment.preview_image.uri,
62
+ previewWidth: attachment.preview_image.width,
63
+ previewHeight: attachment.preview_image.height,
64
+
65
+ url: attachment.animated_image.uri,
66
+ width: attachment.animated_image.width,
67
+ height: attachment.animated_image.height,
68
+
69
+ thumbnailUrl: attachment.preview_image.uri, // @Legacy
70
+ name: attachment.filename, // @Legacy
71
+ facebookUrl: attachment.animated_image.uri, // @Legacy
72
+ rawGifImage: attachment.animated_image.uri, // @Legacy
73
+ animatedGifUrl: attachment.animated_image.uri, // @Legacy
74
+ animatedGifPreviewUrl: attachment.preview_image.uri, // @Legacy
75
+ animatedWebpUrl: attachment.animated_image.uri, // @Legacy
76
+ animatedWebpPreviewUrl: attachment.preview_image.uri, // @Legacy
77
+
78
+ // @Undocumented
79
+ attributionApp: attachment.attribution_app
80
+ ? {
81
+ attributionAppID: attachment.attribution_app.id,
82
+ name: attachment.attribution_app.name,
83
+ logo: attachment.attribution_app.square_logo
84
+ }
85
+ : null
86
+ };
87
+ case "MessageVideo":
88
+ return {
89
+ type: "video",
90
+ filename: attachment.filename,
91
+ ID: attachment.legacy_attachment_id,
92
+
93
+ thumbnailUrl: attachment.large_image.uri, // @Legacy
94
+
95
+ previewUrl: attachment.large_image.uri,
96
+ previewWidth: attachment.large_image.width,
97
+ previewHeight: attachment.large_image.height,
98
+
99
+ url: attachment.playable_url,
100
+ width: attachment.original_dimensions.x,
101
+ height: attachment.original_dimensions.y,
102
+
103
+ duration: attachment.playable_duration_in_ms,
104
+ videoType: attachment.video_type.toLowerCase()
105
+ };
106
+ case "MessageFile":
107
+ return {
108
+ type: "file",
109
+ filename: attachment.filename,
110
+ ID: attachment.message_file_fbid,
111
+
112
+ url: attachment.url,
113
+ isMalicious: attachment.is_malicious,
114
+ contentType: attachment.content_type,
115
+
116
+ name: attachment.filename, // @Legacy
117
+ mimeType: "", // @Legacy
118
+ fileSize: -1 // @Legacy
119
+ };
120
+ case "MessageAudio":
121
+ return {
122
+ type: "audio",
123
+ filename: attachment.filename,
124
+ ID: attachment.url_shimhash, // Not fowardable
125
+
126
+ audioType: attachment.audio_type,
127
+ duration: attachment.playable_duration_in_ms,
128
+ url: attachment.playable_url,
129
+
130
+ isVoiceMail: attachment.is_voicemail
131
+ };
132
+ default:
133
+ return {
134
+ error: "Don't know about attachment type " + attachment.__typename
135
+ };
136
+ }
137
+ }
138
+
139
+ function formatExtensibleAttachment(attachment) {
140
+ if (attachment.story_attachment) {
141
+ return {
142
+ type: "share",
143
+ ID: attachment.legacy_attachment_id,
144
+ url: attachment.story_attachment.url,
145
+
146
+ title: attachment.story_attachment.title_with_entities.text,
147
+ description:
148
+ attachment.story_attachment.description &&
149
+ attachment.story_attachment.description.text,
150
+ source:
151
+ attachment.story_attachment.source == null
152
+ ? null
153
+ : attachment.story_attachment.source.text,
154
+
155
+ image:
156
+ attachment.story_attachment.media == null
157
+ ? null
158
+ : attachment.story_attachment.media.animated_image == null &&
159
+ attachment.story_attachment.media.image == null
160
+ ? null
161
+ : (
162
+ attachment.story_attachment.media.animated_image ||
163
+ attachment.story_attachment.media.image
164
+ ).uri,
165
+ width:
166
+ attachment.story_attachment.media == null
167
+ ? null
168
+ : attachment.story_attachment.media.animated_image == null &&
169
+ attachment.story_attachment.media.image == null
170
+ ? null
171
+ : (
172
+ attachment.story_attachment.media.animated_image ||
173
+ attachment.story_attachment.media.image
174
+ ).width,
175
+ height:
176
+ attachment.story_attachment.media == null
177
+ ? null
178
+ : attachment.story_attachment.media.animated_image == null &&
179
+ attachment.story_attachment.media.image == null
180
+ ? null
181
+ : (
182
+ attachment.story_attachment.media.animated_image ||
183
+ attachment.story_attachment.media.image
184
+ ).height,
185
+ playable:
186
+ attachment.story_attachment.media == null
187
+ ? null
188
+ : attachment.story_attachment.media.is_playable,
189
+ duration:
190
+ attachment.story_attachment.media == null
191
+ ? null
192
+ : attachment.story_attachment.media.playable_duration_in_ms,
193
+ playableUrl:
194
+ attachment.story_attachment.media == null
195
+ ? null
196
+ : attachment.story_attachment.media.playable_url,
197
+
198
+ subattachments: attachment.story_attachment.subattachments,
199
+
200
+ // Format example:
201
+ //
202
+ // [{
203
+ // key: "width",
204
+ // value: { text: "1280" }
205
+ // }]
206
+ //
207
+ // That we turn into:
208
+ //
209
+ // {
210
+ // width: "1280"
211
+ // }
212
+ //
213
+ properties: attachment.story_attachment.properties.reduce(function(
214
+ obj,
215
+ cur
216
+ ) {
217
+ obj[cur.key] = cur.value.text;
218
+ return obj;
219
+ },
220
+ {}),
221
+
222
+ // Deprecated fields
223
+ animatedImageSize: "", // @Legacy
224
+ facebookUrl: "", // @Legacy
225
+ styleList: "", // @Legacy
226
+ target: "", // @Legacy
227
+ thumbnailUrl:
228
+ attachment.story_attachment.media == null
229
+ ? null
230
+ : attachment.story_attachment.media.animated_image == null &&
231
+ attachment.story_attachment.media.image == null
232
+ ? null
233
+ : (
234
+ attachment.story_attachment.media.animated_image ||
235
+ attachment.story_attachment.media.image
236
+ ).uri, // @Legacy
237
+ thumbnailWidth:
238
+ attachment.story_attachment.media == null
239
+ ? null
240
+ : attachment.story_attachment.media.animated_image == null &&
241
+ attachment.story_attachment.media.image == null
242
+ ? null
243
+ : (
244
+ attachment.story_attachment.media.animated_image ||
245
+ attachment.story_attachment.media.image
246
+ ).width, // @Legacy
247
+ thumbnailHeight:
248
+ attachment.story_attachment.media == null
249
+ ? null
250
+ : attachment.story_attachment.media.animated_image == null &&
251
+ attachment.story_attachment.media.image == null
252
+ ? null
253
+ : (
254
+ attachment.story_attachment.media.animated_image ||
255
+ attachment.story_attachment.media.image
256
+ ).height // @Legacy
257
+ };
258
+ } else {
259
+ return { error: "Don't know what to do with extensible_attachment." };
260
+ }
261
+ }
262
+
263
+ function formatReactionsGraphQL(reaction) {
264
+ return {
265
+ reaction: reaction.reaction,
266
+ userID: reaction.user.id
267
+ };
268
+ }
269
+
270
+ function formatEventData(event) {
271
+ if (event == null) {
272
+ return {};
273
+ }
274
+
275
+ switch (event.__typename) {
276
+ case "ThemeColorExtensibleMessageAdminText":
277
+ return {
278
+ color: event.theme_color
279
+ };
280
+ case "ThreadNicknameExtensibleMessageAdminText":
281
+ return {
282
+ nickname: event.nickname,
283
+ participantID: event.participant_id
284
+ };
285
+ case "ThreadIconExtensibleMessageAdminText":
286
+ return {
287
+ threadIcon: event.thread_icon
288
+ };
289
+ case "InstantGameUpdateExtensibleMessageAdminText":
290
+ return {
291
+ gameID: (event.game == null ? null : event.game.id),
292
+ update_type: event.update_type,
293
+ collapsed_text: event.collapsed_text,
294
+ expanded_text: event.expanded_text,
295
+ instant_game_update_data: event.instant_game_update_data
296
+ };
297
+ case "GameScoreExtensibleMessageAdminText":
298
+ return {
299
+ game_type: event.game_type
300
+ };
301
+ case "RtcCallLogExtensibleMessageAdminText":
302
+ return {
303
+ event: event.event,
304
+ is_video_call: event.is_video_call,
305
+ server_info_data: event.server_info_data
306
+ };
307
+ case "GroupPollExtensibleMessageAdminText":
308
+ return {
309
+ event_type: event.event_type,
310
+ total_count: event.total_count,
311
+ question: event.question
312
+ };
313
+ case "AcceptPendingThreadExtensibleMessageAdminText":
314
+ return {
315
+ accepter_id: event.accepter_id,
316
+ requester_id: event.requester_id
317
+ };
318
+ case "ConfirmFriendRequestExtensibleMessageAdminText":
319
+ return {
320
+ friend_request_recipient: event.friend_request_recipient,
321
+ friend_request_sender: event.friend_request_sender
322
+ };
323
+ case "AddContactExtensibleMessageAdminText":
324
+ return {
325
+ contact_added_id: event.contact_added_id,
326
+ contact_adder_id: event.contact_adder_id
327
+ };
328
+ case "AdExtensibleMessageAdminText":
329
+ return {
330
+ ad_client_token: event.ad_client_token,
331
+ ad_id: event.ad_id,
332
+ ad_preferences_link: event.ad_preferences_link,
333
+ ad_properties: event.ad_properties
334
+ };
335
+ // never data
336
+ case "ParticipantJoinedGroupCallExtensibleMessageAdminText":
337
+ case "ThreadEphemeralTtlModeExtensibleMessageAdminText":
338
+ case "StartedSharingVideoExtensibleMessageAdminText":
339
+ case "LightweightEventCreateExtensibleMessageAdminText":
340
+ case "LightweightEventNotifyExtensibleMessageAdminText":
341
+ case "LightweightEventNotifyBeforeEventExtensibleMessageAdminText":
342
+ case "LightweightEventUpdateTitleExtensibleMessageAdminText":
343
+ case "LightweightEventUpdateTimeExtensibleMessageAdminText":
344
+ case "LightweightEventUpdateLocationExtensibleMessageAdminText":
345
+ case "LightweightEventDeleteExtensibleMessageAdminText":
346
+ return {};
347
+ default:
348
+ return {
349
+ error: "Don't know what to with event data type " + event.__typename
350
+ };
351
+ }
352
+ }
353
+
354
+ function formatMessagesGraphQLResponse(data) {
355
+ var messageThread = data.o0.data.message_thread;
356
+ var threadID = messageThread.thread_key.thread_fbid
357
+ ? messageThread.thread_key.thread_fbid
358
+ : messageThread.thread_key.other_user_id;
359
+
360
+ var messages = messageThread.messages.nodes.map(function(d) {
361
+ switch (d.__typename) {
362
+ case "UserMessage":
363
+ // Give priority to stickers. They're seen as normal messages but we've
364
+ // been considering them as attachments.
365
+ var maybeStickerAttachment;
366
+ if (d.sticker) {
367
+ maybeStickerAttachment = [
368
+ {
369
+ type: "sticker",
370
+ ID: d.sticker.id,
371
+ url: d.sticker.url,
372
+
373
+ packID: d.sticker.pack.id,
374
+ spriteUrl: d.sticker.sprite_image,
375
+ spriteUrl2x: d.sticker.sprite_image_2x,
376
+ width: d.sticker.width,
377
+ height: d.sticker.height,
378
+
379
+ caption: d.snippet, // Not sure what the heck caption was.
380
+ description: d.sticker.label, // Not sure about this one either.
381
+
382
+ frameCount: d.sticker.frame_count,
383
+ frameRate: d.sticker.frame_rate,
384
+ framesPerRow: d.sticker.frames_per_row,
385
+ framesPerCol: d.sticker.frames_per_col,
386
+
387
+ stickerID: d.sticker.id, // @Legacy
388
+ spriteURI: d.sticker.sprite_image, // @Legacy
389
+ spriteURI2x: d.sticker.sprite_image_2x // @Legacy
390
+ }
391
+ ];
392
+ }
393
+
394
+ var mentionsObj = {};
395
+ if (d.message !== null) {
396
+ d.message.ranges.forEach(e => {
397
+ mentionsObj[e.entity.id] = d.message.text.substr(e.offset, e.length);
398
+ });
399
+ }
400
+
401
+ return {
402
+ type: "message",
403
+ attachments: maybeStickerAttachment
404
+ ? maybeStickerAttachment
405
+ : d.blob_attachments && d.blob_attachments.length > 0
406
+ ? d.blob_attachments.map(formatAttachmentsGraphQLResponse)
407
+ : d.extensible_attachment
408
+ ? [formatExtensibleAttachment(d.extensible_attachment)]
409
+ : [],
410
+ body: d.message !== null ? d.message.text : '',
411
+ isGroup: messageThread.thread_type === "GROUP",
412
+ messageID: d.message_id,
413
+ senderID: d.message_sender.id,
414
+ threadID: threadID,
415
+ timestamp: d.timestamp_precise,
416
+
417
+ mentions: mentionsObj,
418
+ isUnread: d.unread,
419
+
420
+ // New
421
+ messageReactions: d.message_reactions
422
+ ? d.message_reactions.map(formatReactionsGraphQL)
423
+ : null,
424
+ isSponsored: d.is_sponsored,
425
+ snippet: d.snippet
426
+ };
427
+ case "ThreadNameMessage":
428
+ return {
429
+ type: "event",
430
+ messageID: d.message_id,
431
+ threadID: threadID,
432
+ isGroup: messageThread.thread_type === "GROUP",
433
+ senderID: d.message_sender.id,
434
+ timestamp: d.timestamp_precise,
435
+ eventType: "change_thread_name",
436
+ snippet: d.snippet,
437
+ eventData: {
438
+ threadName: d.thread_name
439
+ },
440
+
441
+ // @Legacy
442
+ author: d.message_sender.id,
443
+ logMessageType: "log:thread-name",
444
+ logMessageData: { name: d.thread_name }
445
+ };
446
+ case "ThreadImageMessage":
447
+ return {
448
+ type: "event",
449
+ messageID: d.message_id,
450
+ threadID: threadID,
451
+ isGroup: messageThread.thread_type === "GROUP",
452
+ senderID: d.message_sender.id,
453
+ timestamp: d.timestamp_precise,
454
+ eventType: "change_thread_image",
455
+ snippet: d.snippet,
456
+ eventData:
457
+ d.image_with_metadata == null
458
+ ? {} /* removed image */
459
+ : {
460
+ /* image added */
461
+ threadImage: {
462
+ attachmentID: d.image_with_metadata.legacy_attachment_id,
463
+ width: d.image_with_metadata.original_dimensions.x,
464
+ height: d.image_with_metadata.original_dimensions.y,
465
+ url: d.image_with_metadata.preview.uri
466
+ }
467
+ },
468
+
469
+ // @Legacy
470
+ logMessageType: "log:thread-icon",
471
+ logMessageData: {
472
+ thread_icon: d.image_with_metadata
473
+ ? d.image_with_metadata.preview.uri
474
+ : null
475
+ }
476
+ };
477
+ case "ParticipantLeftMessage":
478
+ return {
479
+ type: "event",
480
+ messageID: d.message_id,
481
+ threadID: threadID,
482
+ isGroup: messageThread.thread_type === "GROUP",
483
+ senderID: d.message_sender.id,
484
+ timestamp: d.timestamp_precise,
485
+ eventType: "remove_participants",
486
+ snippet: d.snippet,
487
+ eventData: {
488
+ // Array of IDs.
489
+ participantsRemoved: d.participants_removed.map(function(p) {
490
+ return p.id;
491
+ })
492
+ },
493
+
494
+ // @Legacy
495
+ logMessageType: "log:unsubscribe",
496
+ logMessageData: {
497
+ leftParticipantFbId: d.participants_removed.map(function(p) {
498
+ return p.id;
499
+ })
500
+ }
501
+ };
502
+ case "ParticipantsAddedMessage":
503
+ return {
504
+ type: "event",
505
+ messageID: d.message_id,
506
+ threadID: threadID,
507
+ isGroup: messageThread.thread_type === "GROUP",
508
+ senderID: d.message_sender.id,
509
+ timestamp: d.timestamp_precise,
510
+ eventType: "add_participants",
511
+ snippet: d.snippet,
512
+ eventData: {
513
+ // Array of IDs.
514
+ participantsAdded: d.participants_added.map(function(p) {
515
+ return p.id;
516
+ })
517
+ },
518
+
519
+ // @Legacy
520
+ logMessageType: "log:subscribe",
521
+ logMessageData: {
522
+ addedParticipants: d.participants_added.map(function(p) {
523
+ return p.id;
524
+ })
525
+ }
526
+ };
527
+ case "VideoCallMessage":
528
+ return {
529
+ type: "event",
530
+ messageID: d.message_id,
531
+ threadID: threadID,
532
+ isGroup: messageThread.thread_type === "GROUP",
533
+ senderID: d.message_sender.id,
534
+ timestamp: d.timestamp_precise,
535
+ eventType: "video_call",
536
+ snippet: d.snippet,
537
+
538
+ // @Legacy
539
+ logMessageType: "other"
540
+ };
541
+ case "VoiceCallMessage":
542
+ return {
543
+ type: "event",
544
+ messageID: d.message_id,
545
+ threadID: threadID,
546
+ isGroup: messageThread.thread_type === "GROUP",
547
+ senderID: d.message_sender.id,
548
+ timestamp: d.timestamp_precise,
549
+ eventType: "voice_call",
550
+ snippet: d.snippet,
551
+
552
+ // @Legacy
553
+ logMessageType: "other"
554
+ };
555
+ case "GenericAdminTextMessage":
556
+ return {
557
+ type: "event",
558
+ messageID: d.message_id,
559
+ threadID: threadID,
560
+ isGroup: messageThread.thread_type === "GROUP",
561
+ senderID: d.message_sender.id,
562
+ timestamp: d.timestamp_precise,
563
+ snippet: d.snippet,
564
+ eventType: d.extensible_message_admin_text_type.toLowerCase(),
565
+ eventData: formatEventData(d.extensible_message_admin_text),
566
+
567
+ // @Legacy
568
+ logMessageType: utils.getAdminTextMessageType(
569
+ d.extensible_message_admin_text_type
570
+ ),
571
+ logMessageData: d.extensible_message_admin_text // Maybe different?
572
+ };
573
+ default:
574
+ return { error: "Don't know about message type " + d.__typename };
575
+ }
576
+ });
577
+ return messages;
578
+ }
579
+
580
+ module.exports = function(defaultFuncs, api, ctx) {
581
+ return function getThreadHistoryGraphQL(
582
+ threadID,
583
+ amount,
584
+ timestamp,
585
+ callback
586
+ ) {
587
+ var resolveFunc = function(){};
588
+ var rejectFunc = function(){};
589
+ var returnPromise = new Promise(function (resolve, reject) {
590
+ resolveFunc = resolve;
591
+ rejectFunc = reject;
592
+ });
593
+
594
+ if (!callback) {
595
+ callback = function (err, data) {
596
+ if (err) {
597
+ return rejectFunc(err);
598
+ }
599
+ resolveFunc(data);
600
+ };
601
+ }
602
+
603
+ // `queries` has to be a string. I couldn't tell from the dev console. This
604
+ // took me a really long time to figure out. I deserve a cookie for this.
605
+ var form = {
606
+ "av": ctx.globalOptions.pageID,
607
+ queries: JSON.stringify({
608
+ o0: {
609
+ // This doc_id was valid on February 2nd 2017.
610
+ doc_id: "1498317363570230",
611
+ query_params: {
612
+ id: threadID,
613
+ message_limit: amount,
614
+ load_messages: 1,
615
+ load_read_receipts: false,
616
+ before: timestamp
617
+ }
618
+ }
619
+ })
620
+ };
621
+
622
+ defaultFuncs
623
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
624
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
625
+ .then(function(resData) {
626
+ if (resData.error) {
627
+ throw resData;
628
+ }
629
+ // This returns us an array of things. The last one is the success /
630
+ // failure one.
631
+ // @TODO What do we do in this case?
632
+ if (resData[resData.length - 1].error_results !== 0) {
633
+ throw new Error("There was an error_result.");
634
+ }
635
+
636
+ callback(null, formatMessagesGraphQLResponse(resData[0]));
637
+ })
638
+ .catch(function(err) {
639
+ log.error("getThreadHistoryGraphQL", err);
640
+ return callback(err);
641
+ });
642
+
643
+ return returnPromise;
644
+ };
645
+ };