@vangbanlanhat/fca-unofficial 1.4.2

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