@dongdev/fca-unofficial 1.0.20 → 2.0.1

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