fca-nino 1.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fca-nino might be problematic. Click here for more details.

Files changed (69) hide show
  1. package/.gitattributes +2 -0
  2. package/.travis.yml +6 -0
  3. package/CHANGELOG.md +2 -0
  4. package/DOCS.md +1738 -0
  5. package/LICENSE-MIT +21 -0
  6. package/README.md +219 -0
  7. package/index.js +541 -0
  8. package/package.json +73 -0
  9. package/replit.nix +5 -0
  10. package/src/Screenshot.js +83 -0
  11. package/src/addExternalModule.js +15 -0
  12. package/src/addUserToGroup.js +77 -0
  13. package/src/changeAdminStatus.js +47 -0
  14. package/src/changeArchivedStatus.js +41 -0
  15. package/src/changeAvt.js +85 -0
  16. package/src/changeBio.js +65 -0
  17. package/src/changeBlockedStatus.js +36 -0
  18. package/src/changeGroupImage.js +106 -0
  19. package/src/changeNickname.js +45 -0
  20. package/src/changeThreadColor.js +61 -0
  21. package/src/changeThreadEmoji.js +41 -0
  22. package/src/createNewGroup.js +70 -0
  23. package/src/createPoll.js +59 -0
  24. package/src/deleteMessage.js +44 -0
  25. package/src/deleteThread.js +42 -0
  26. package/src/forwardAttachment.js +47 -0
  27. package/src/getCurrentUserID.js +7 -0
  28. package/src/getEmojiUrl.js +27 -0
  29. package/src/getFriendsList.js +73 -0
  30. package/src/getThreadHistory.js +537 -0
  31. package/src/getThreadHistoryDeprecated.js +71 -0
  32. package/src/getThreadInfo.js +171 -0
  33. package/src/getThreadInfoDeprecated.js +56 -0
  34. package/src/getThreadList.js +213 -0
  35. package/src/getThreadListDeprecated.js +46 -0
  36. package/src/getThreadPictures.js +59 -0
  37. package/src/getUserID.js +61 -0
  38. package/src/getUserInfo.js +66 -0
  39. package/src/handleFriendRequest.js +46 -0
  40. package/src/handleMessageRequest.js +47 -0
  41. package/src/httpGet.js +49 -0
  42. package/src/httpPost.js +48 -0
  43. package/src/listenMqtt.js +701 -0
  44. package/src/logout.js +68 -0
  45. package/src/markAsDelivered.js +47 -0
  46. package/src/markAsRead.js +70 -0
  47. package/src/markAsReadAll.js +40 -0
  48. package/src/markAsSeen.js +48 -0
  49. package/src/muteThread.js +45 -0
  50. package/src/removeUserFromGroup.js +45 -0
  51. package/src/resolvePhotoUrl.js +36 -0
  52. package/src/searchForThread.js +42 -0
  53. package/src/sendMessage.js +328 -0
  54. package/src/sendTypingIndicator.js +70 -0
  55. package/src/setMessageReaction.js +109 -0
  56. package/src/setPostReaction.js +102 -0
  57. package/src/setTitle.js +70 -0
  58. package/src/shareContact.js +46 -0
  59. package/src/threadColors.js +41 -0
  60. package/src/unfriend.js +42 -0
  61. package/src/unsendMessage.js +39 -0
  62. package/test/data/shareAttach.js +146 -0
  63. package/test/data/something.mov +0 -0
  64. package/test/data/test.png +0 -0
  65. package/test/data/test.txt +7 -0
  66. package/test/example-config.json +18 -0
  67. package/test/test-page.js +140 -0
  68. package/test/test.js +385 -0
  69. package/utils.js +1196 -0
@@ -0,0 +1,537 @@
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: attachment.story_attachment.description && attachment.story_attachment.description.text,
148
+ source: attachment.story_attachment.source == null ? null : attachment.story_attachment.source.text,
149
+
150
+ image: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).uri,
151
+ width: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).width,
152
+ height: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).height,
153
+ playable: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.is_playable,
154
+ duration: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.playable_duration_in_ms,
155
+ playableUrl: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.playable_url,
156
+
157
+ subattachments: attachment.story_attachment.subattachments,
158
+
159
+ // Format example:
160
+ //
161
+ // [{
162
+ // key: "width",
163
+ // value: { text: "1280" }
164
+ // }]
165
+ //
166
+ // That we turn into:
167
+ //
168
+ // {
169
+ // width: "1280"
170
+ // }
171
+ //
172
+ properties: attachment.story_attachment.properties.reduce(function (obj, cur) {
173
+ obj[cur.key] = cur.value.text;
174
+ return obj;
175
+ }, {}),
176
+
177
+ // Deprecated fields
178
+ animatedImageSize: "", // @Legacy
179
+ facebookUrl: "", // @Legacy
180
+ styleList: "", // @Legacy
181
+ target: "", // @Legacy
182
+ thumbnailUrl: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).uri, // @Legacy
183
+ thumbnailWidth: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).width, // @Legacy
184
+ thumbnailHeight: attachment.story_attachment.media == null ? null : attachment.story_attachment.media.animated_image == null && attachment.story_attachment.media.image == null ? null : (attachment.story_attachment.media.animated_image || attachment.story_attachment.media.image).height // @Legacy
185
+ };
186
+ }
187
+ else return { error: "Don't know what to do with extensible_attachment." };
188
+ }
189
+
190
+ function formatReactionsGraphQL(reaction) {
191
+ return {
192
+ reaction: reaction.reaction,
193
+ userID: reaction.user.id
194
+ };
195
+ }
196
+
197
+ function formatEventData(event) {
198
+ if (event == null) return {}
199
+
200
+ switch (event.__typename) {
201
+ case "ThemeColorExtensibleMessageAdminText":
202
+ return { color: event.theme_color };
203
+ case "ThreadNicknameExtensibleMessageAdminText":
204
+ return {
205
+ nickname: event.nickname,
206
+ participantID: event.participant_id
207
+ };
208
+ case "ThreadIconExtensibleMessageAdminText":
209
+ return { threadIcon: event.thread_icon };
210
+ case "InstantGameUpdateExtensibleMessageAdminText":
211
+ return {
212
+ gameID: (event.game == null ? null : event.game.id),
213
+ update_type: event.update_type,
214
+ collapsed_text: event.collapsed_text,
215
+ expanded_text: event.expanded_text,
216
+ instant_game_update_data: event.instant_game_update_data
217
+ };
218
+ case "GameScoreExtensibleMessageAdminText":
219
+ return { game_type: event.game_type };
220
+ case "RtcCallLogExtensibleMessageAdminText":
221
+ return {
222
+ event: event.event,
223
+ is_video_call: event.is_video_call,
224
+ server_info_data: event.server_info_data
225
+ };
226
+ case "GroupPollExtensibleMessageAdminText":
227
+ return {
228
+ event_type: event.event_type,
229
+ total_count: event.total_count,
230
+ question: event.question
231
+ };
232
+ case "AcceptPendingThreadExtensibleMessageAdminText":
233
+ return {
234
+ accepter_id: event.accepter_id,
235
+ requester_id: event.requester_id
236
+ };
237
+ case "ConfirmFriendRequestExtensibleMessageAdminText":
238
+ return {
239
+ friend_request_recipient: event.friend_request_recipient,
240
+ friend_request_sender: event.friend_request_sender
241
+ };
242
+ case "AddContactExtensibleMessageAdminText":
243
+ return {
244
+ contact_added_id: event.contact_added_id,
245
+ contact_adder_id: event.contact_adder_id
246
+ };
247
+ case "AdExtensibleMessageAdminText":
248
+ return {
249
+ ad_client_token: event.ad_client_token,
250
+ ad_id: event.ad_id,
251
+ ad_preferences_link: event.ad_preferences_link,
252
+ ad_properties: event.ad_properties
253
+ };
254
+ // never data
255
+ case "ParticipantJoinedGroupCallExtensibleMessageAdminText":
256
+ case "ThreadEphemeralTtlModeExtensibleMessageAdminText":
257
+ case "StartedSharingVideoExtensibleMessageAdminText":
258
+ case "LightweightEventCreateExtensibleMessageAdminText":
259
+ case "LightweightEventNotifyExtensibleMessageAdminText":
260
+ case "LightweightEventNotifyBeforeEventExtensibleMessageAdminText":
261
+ case "LightweightEventUpdateTitleExtensibleMessageAdminText":
262
+ case "LightweightEventUpdateTimeExtensibleMessageAdminText":
263
+ case "LightweightEventUpdateLocationExtensibleMessageAdminText":
264
+ case "LightweightEventDeleteExtensibleMessageAdminText":
265
+ return {};
266
+ default:
267
+ return { error: "Don't know what to with event data type " + event.__typename };
268
+ }
269
+ }
270
+
271
+ function formatMessagesGraphQLResponse(data) {
272
+ var messageThread = data.o0.data.message_thread;
273
+ var threadID = messageThread.thread_key.thread_fbid ? messageThread.thread_key.thread_fbid : messageThread.thread_key.other_user_id;
274
+
275
+ var messages = messageThread.messages.nodes.map(function (d) {
276
+ switch (d.__typename) {
277
+ case "UserMessage":
278
+ // Give priority to stickers. They're seen as normal messages but we've
279
+ // been considering them as attachments.
280
+ var maybeStickerAttachment;
281
+ if (d.sticker) {
282
+ maybeStickerAttachment = [
283
+ {
284
+ type: "sticker",
285
+ ID: d.sticker.id,
286
+ url: d.sticker.url,
287
+
288
+ packID: d.sticker.pack.id,
289
+ spriteUrl: d.sticker.sprite_image,
290
+ spriteUrl2x: d.sticker.sprite_image_2x,
291
+ width: d.sticker.width,
292
+ height: d.sticker.height,
293
+
294
+ caption: d.snippet, // Not sure what the heck caption was.
295
+ description: d.sticker.label, // Not sure about this one either.
296
+
297
+ frameCount: d.sticker.frame_count,
298
+ frameRate: d.sticker.frame_rate,
299
+ framesPerRow: d.sticker.frames_per_row,
300
+ framesPerCol: d.sticker.frames_per_col,
301
+
302
+ stickerID: d.sticker.id, // @Legacy
303
+ spriteURI: d.sticker.sprite_image, // @Legacy
304
+ spriteURI2x: d.sticker.sprite_image_2x // @Legacy
305
+ }
306
+ ];
307
+ }
308
+
309
+ var mentionsObj = {};
310
+ if (d.message !== null) {
311
+ d.message.ranges.forEach(e => mentionsObj[e.entity.id] = d.message.text.substr(e.offset, e.length));
312
+ }
313
+
314
+ return {
315
+ type: "message",
316
+ attachments: maybeStickerAttachment
317
+ ? maybeStickerAttachment
318
+ : d.blob_attachments && d.blob_attachments.length > 0
319
+ ? d.blob_attachments.map(formatAttachmentsGraphQLResponse)
320
+ : d.extensible_attachment
321
+ ? [formatExtensibleAttachment(d.extensible_attachment)]
322
+ : [],
323
+ body: d.message !== null ? d.message.text : '',
324
+ isGroup: messageThread.thread_type === "GROUP",
325
+ messageID: d.message_id,
326
+ senderID: d.message_sender.id,
327
+ threadID: threadID,
328
+ timestamp: d.timestamp_precise,
329
+
330
+ mentions: mentionsObj,
331
+ isUnread: d.unread,
332
+
333
+ // New
334
+ messageReactions: d.message_reactions ? d.message_reactions.map(formatReactionsGraphQL) : null,
335
+ isSponsored: d.is_sponsored,
336
+ snippet: d.snippet
337
+ };
338
+ case "ThreadNameMessage":
339
+ return {
340
+ type: "event",
341
+ messageID: d.message_id,
342
+ threadID: threadID,
343
+ isGroup: messageThread.thread_type === "GROUP",
344
+ senderID: d.message_sender.id,
345
+ timestamp: d.timestamp_precise,
346
+ eventType: "change_thread_name",
347
+ snippet: d.snippet,
348
+ eventData: { threadName: d.thread_name },
349
+
350
+ // @Legacy
351
+ author: d.message_sender.id,
352
+ logMessageType: "log:thread-name",
353
+ logMessageData: { name: d.thread_name }
354
+ };
355
+ case "ThreadImageMessage":
356
+ return {
357
+ type: "event",
358
+ messageID: d.message_id,
359
+ threadID: threadID,
360
+ isGroup: messageThread.thread_type === "GROUP",
361
+ senderID: d.message_sender.id,
362
+ timestamp: d.timestamp_precise,
363
+ eventType: "change_thread_image",
364
+ snippet: d.snippet,
365
+ eventData: d.image_with_metadata == null
366
+ ? {} /* removed image */
367
+ : {
368
+ /* image added */
369
+ threadImage: {
370
+ attachmentID: d.image_with_metadata.legacy_attachment_id,
371
+ width: d.image_with_metadata.original_dimensions.x,
372
+ height: d.image_with_metadata.original_dimensions.y,
373
+ url: d.image_with_metadata.preview.uri
374
+ }
375
+ },
376
+
377
+ // @Legacy
378
+ logMessageType: "log:thread-icon",
379
+ logMessageData: { thread_icon: d.image_with_metadata ? d.image_with_metadata.preview.uri : null }
380
+ };
381
+ case "ParticipantLeftMessage":
382
+ return {
383
+ type: "event",
384
+ messageID: d.message_id,
385
+ threadID: threadID,
386
+ isGroup: messageThread.thread_type === "GROUP",
387
+ senderID: d.message_sender.id,
388
+ timestamp: d.timestamp_precise,
389
+ eventType: "remove_participants",
390
+ snippet: d.snippet,
391
+ eventData: {
392
+ // Array of IDs.
393
+ participantsRemoved: d.participants_removed.map(function (p) {
394
+ return p.id;
395
+ })
396
+ },
397
+
398
+ // @Legacy
399
+ logMessageType: "log:unsubscribe",
400
+ logMessageData: {
401
+ leftParticipantFbId: d.participants_removed.map(function (p) {
402
+ return p.id;
403
+ })
404
+ }
405
+ };
406
+ case "ParticipantsAddedMessage":
407
+ return {
408
+ type: "event",
409
+ messageID: d.message_id,
410
+ threadID: threadID,
411
+ isGroup: messageThread.thread_type === "GROUP",
412
+ senderID: d.message_sender.id,
413
+ timestamp: d.timestamp_precise,
414
+ eventType: "add_participants",
415
+ snippet: d.snippet,
416
+ eventData: {
417
+ // Array of IDs.
418
+ participantsAdded: d.participants_added.map(function (p) {
419
+ return p.id;
420
+ })
421
+ },
422
+
423
+ // @Legacy
424
+ logMessageType: "log:subscribe",
425
+ logMessageData: {
426
+ addedParticipants: d.participants_added.map(function (p) {
427
+ return p.id;
428
+ })
429
+ }
430
+ };
431
+ case "VideoCallMessage":
432
+ return {
433
+ type: "event",
434
+ messageID: d.message_id,
435
+ threadID: threadID,
436
+ isGroup: messageThread.thread_type === "GROUP",
437
+ senderID: d.message_sender.id,
438
+ timestamp: d.timestamp_precise,
439
+ eventType: "video_call",
440
+ snippet: d.snippet,
441
+
442
+ // @Legacy
443
+ logMessageType: "other"
444
+ };
445
+ case "VoiceCallMessage":
446
+ return {
447
+ type: "event",
448
+ messageID: d.message_id,
449
+ threadID: threadID,
450
+ isGroup: messageThread.thread_type === "GROUP",
451
+ senderID: d.message_sender.id,
452
+ timestamp: d.timestamp_precise,
453
+ eventType: "voice_call",
454
+ snippet: d.snippet,
455
+
456
+ // @Legacy
457
+ logMessageType: "other"
458
+ };
459
+ case "GenericAdminTextMessage":
460
+ return {
461
+ type: "event",
462
+ messageID: d.message_id,
463
+ threadID: threadID,
464
+ isGroup: messageThread.thread_type === "GROUP",
465
+ senderID: d.message_sender.id,
466
+ timestamp: d.timestamp_precise,
467
+ snippet: d.snippet,
468
+ eventType: d.extensible_message_admin_text_type.toLowerCase(),
469
+ eventData: formatEventData(d.extensible_message_admin_text),
470
+
471
+ // @Legacy
472
+ logMessageType: utils.getAdminTextMessageType(
473
+ d.extensible_message_admin_text_type
474
+ ),
475
+ logMessageData: d.extensible_message_admin_text // Maybe different?
476
+ };
477
+ default:
478
+ return { error: "Don't know about message type " + d.__typename };
479
+ }
480
+ });
481
+ return messages;
482
+ }
483
+
484
+ module.exports = function (defaultFuncs, api, ctx) {
485
+ return function getThreadHistoryGraphQL(threadID, amount, timestamp, callback) {
486
+ var resolveFunc = function () { };
487
+ var rejectFunc = function () { };
488
+ var returnPromise = new Promise(function (resolve, reject) {
489
+ resolveFunc = resolve;
490
+ rejectFunc = reject;
491
+ });
492
+
493
+ if (!callback) {
494
+ callback = function (err, data) {
495
+ if (err) return rejectFunc(err);
496
+ resolveFunc(data);
497
+ };
498
+ }
499
+
500
+ // `queries` has to be a string. I couldn't tell from the dev console. This
501
+ // took me a really long time to figure out. I deserve a cookie for this.
502
+ var form = {
503
+ "av": ctx.globalOptions.pageID,
504
+ queries: JSON.stringify({
505
+ o0: {
506
+ // This doc_id was valid on February 2nd 2017.
507
+ doc_id: "1498317363570230",
508
+ query_params: {
509
+ id: threadID,
510
+ message_limit: amount,
511
+ load_messages: 1,
512
+ load_read_receipts: false,
513
+ before: timestamp
514
+ }
515
+ }
516
+ })
517
+ };
518
+
519
+ defaultFuncs
520
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
521
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
522
+ .then(function (resData) {
523
+ if (resData.error) throw resData;
524
+ // This returns us an array of things. The last one is the success /
525
+ // failure one.
526
+ // @TODO What do we do in this case?
527
+ if (resData[resData.length - 1].error_results !== 0) throw new Error("There was an error_result.");
528
+ callback(null, formatMessagesGraphQLResponse(resData[0]));
529
+ })
530
+ .catch(function (err) {
531
+ log.error("getThreadHistoryGraphQL", err);
532
+ return callback(err);
533
+ });
534
+
535
+ return returnPromise;
536
+ };
537
+ };
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function getThreadHistory(threadID, amount, timestamp, callback) {
8
+ var resolveFunc = function () { };
9
+ var rejectFunc = function () { };
10
+ var returnPromise = new Promise(function (resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = function (err, threadInfo) {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc(threadInfo);
19
+ };
20
+ }
21
+
22
+ if (!callback) throw { error: "getThreadHistory: need callback" };
23
+ var form = {
24
+ client: "mercury"
25
+ };
26
+
27
+ api.getUserInfo(threadID, function (err, res) {
28
+ if (err) return callback(err);
29
+ var key = Object.keys(res).length > 0 ? "user_ids" : "thread_fbids";
30
+ form["messages[" + key + "][" + threadID + "][offset]"] = 0;
31
+ form["messages[" + key + "][" + threadID + "][timestamp]"] = timestamp;
32
+ form["messages[" + key + "][" + threadID + "][limit]"] = amount;
33
+
34
+ if (ctx.globalOptions.pageID) form.request_user_id = ctx.globalOptions.pageID;
35
+
36
+ defaultFuncs
37
+ .post("https://www.facebook.com/ajax/mercury/thread_info.php", ctx.jar, form)
38
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
39
+ .then(function (resData) {
40
+ if (resData.error) throw resData;
41
+ else if (!resData.payload) throw { error: "Could not retrieve thread history." };
42
+
43
+ // Asking for message history from a thread with no message history
44
+ // will return undefined for actions here
45
+ if (!resData.payload.actions) resData.payload.actions = [];
46
+
47
+ var userIDs = {};
48
+ resData.payload.actions.forEach(v => userIDs[v.author.split(":").pop()] = "");
49
+
50
+ api.getUserInfo(Object.keys(userIDs), function (err, data) {
51
+ if (err) return callback(err); //callback({error: "Could not retrieve user information in getThreadHistory."});
52
+
53
+ resData.payload.actions.forEach(function (v) {
54
+ var sender = data[v.author.split(":").pop()];
55
+ if (sender) v.sender_name = sender.name;
56
+ else v.sender_name = "Facebook User";
57
+ v.sender_fbid = v.author;
58
+ delete v.author;
59
+ });
60
+
61
+ callback(null, resData.payload.actions.map(utils.formatHistoryMessage));
62
+ });
63
+ })
64
+ .catch(function (err) {
65
+ log.error("getThreadHistory", err);
66
+ return callback(err);
67
+ });
68
+ });
69
+ return returnPromise;
70
+ };
71
+ };