alicezetion 1.7.0 → 1.7.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 (86) hide show
  1. package/.cache/replit/__replit_disk_meta.json +1 -1
  2. package/.cache/replit/nix/env.json +1 -1
  3. package/.travis.yml +6 -0
  4. package/index.js +182 -495
  5. package/package.json +10 -8
  6. package/replit.nix +4 -6
  7. package/src/addExternalModule.js +15 -0
  8. package/{leiamnash → src}/addUserToGroup.js +16 -52
  9. package/src/changeAdminStatus.js +47 -0
  10. package/src/changeArchivedStatus.js +41 -0
  11. package/{leiamnash → src}/changeBio.js +6 -19
  12. package/{leiamnash → src}/changeBlockedStatus.js +3 -14
  13. package/{leiamnash → src}/changeGroupImage.js +16 -40
  14. package/src/changeNickname.js +43 -0
  15. package/{leiamnash → src}/changeThreadColor.js +10 -20
  16. package/src/changeThreadEmoji.js +41 -0
  17. package/src/chat.js +315 -0
  18. package/{leiamnash → src}/createNewGroup.js +12 -28
  19. package/{leiamnash → src}/createPoll.js +13 -25
  20. package/src/deleteMessage.js +44 -0
  21. package/src/deleteThread.js +42 -0
  22. package/src/forwardAttachment.js +47 -0
  23. package/src/forwardMessage.js +0 -0
  24. package/{leiamnash → src}/getCurrentUserID.js +1 -1
  25. package/{leiamnash → src}/getEmojiUrl.js +2 -4
  26. package/{leiamnash → src}/getFriendsList.js +10 -21
  27. package/{leiamnash → src}/getThreadHistory.js +58 -166
  28. package/{leiamnash → src}/getThreadHistoryDeprecated.js +20 -42
  29. package/src/getThreadInfo.js +171 -0
  30. package/src/getThreadInfoDeprecated.js +56 -0
  31. package/{leiamnash → src}/getThreadList.js +41 -66
  32. package/src/getThreadListDeprecated.js +46 -0
  33. package/src/getThreadPictures.js +59 -0
  34. package/{leiamnash → src}/getUserID.js +9 -14
  35. package/{leiamnash → src}/getUserInfo.js +12 -18
  36. package/src/handleFriendRequest.js +46 -0
  37. package/src/handleMessageRequest.js +47 -0
  38. package/{leiamnash → src}/httpGet.js +12 -17
  39. package/{leiamnash → src}/httpPost.js +12 -17
  40. package/src/listen.js +553 -0
  41. package/src/listenMqtt-Test.js +687 -0
  42. package/src/listenMqtt.js +677 -0
  43. package/{leiamnash → src}/logout.js +13 -20
  44. package/{leiamnash → src}/markAsDelivered.js +11 -22
  45. package/{leiamnash → src}/markAsRead.js +11 -21
  46. package/{leiamnash → src}/markAsReadAll.js +10 -20
  47. package/{leiamnash → src}/markAsSeen.js +7 -18
  48. package/{leiamnash → src}/muteThread.js +11 -18
  49. package/src/removeUserFromGroup.js +45 -0
  50. package/{leiamnash → src}/resolvePhotoUrl.js +8 -17
  51. package/{leiamnash → src}/searchForThread.js +10 -21
  52. package/src/sendMessage.js +315 -0
  53. package/{leiamnash → src}/sendTypingIndicator.js +14 -47
  54. package/{leiamnash → src}/setMessageReaction.js +12 -26
  55. package/{leiamnash → src}/setPostReaction.js +13 -26
  56. package/{leiamnash → src}/setTitle.js +13 -29
  57. package/src/threadColors.js +41 -0
  58. package/{leiamnash → src}/unfriend.js +9 -19
  59. package/{leiamnash → src}/unsendMessage.js +9 -19
  60. package/test/data/shareAttach.js +146 -0
  61. package/test/data/something.mov +0 -0
  62. package/test/data/test.png +0 -0
  63. package/test/data/test.txt +7 -0
  64. package/test/example-config.json +18 -0
  65. package/test/test-page.js +140 -0
  66. package/test/test.js +385 -0
  67. package/utils.js +1021 -1238
  68. package/leiamnash/addExternalModule.js +0 -19
  69. package/leiamnash/changeAdminStatus.js +0 -79
  70. package/leiamnash/changeApprovalMode.js +0 -80
  71. package/leiamnash/changeArchivedStatus.js +0 -55
  72. package/leiamnash/changeNickname.js +0 -59
  73. package/leiamnash/changeThreadEmoji.js +0 -55
  74. package/leiamnash/chat.js +0 -447
  75. package/leiamnash/deleteMessage.js +0 -56
  76. package/leiamnash/deleteThread.js +0 -56
  77. package/leiamnash/forwardAttachment.js +0 -60
  78. package/leiamnash/getThreadInfo.js +0 -212
  79. package/leiamnash/getThreadInfoDeprecated.js +0 -80
  80. package/leiamnash/getThreadListDeprecated.js +0 -75
  81. package/leiamnash/getThreadPictures.js +0 -79
  82. package/leiamnash/handleFriendRequest.js +0 -61
  83. package/leiamnash/handleMessageRequest.js +0 -65
  84. package/leiamnash/listenMqtt.js +0 -1129
  85. package/leiamnash/removeUserFromGroup.js +0 -79
  86. package/leiamnash/threadColors.js +0 -57
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+
6
+ function formatEventReminders(reminder) {
7
+ return {
8
+ reminderID: reminder.id,
9
+ eventCreatorID: reminder.lightweight_event_creator.id,
10
+ time: reminder.time,
11
+ eventType: reminder.lightweight_event_type.toLowerCase(),
12
+ locationName: reminder.location_name,
13
+ // @TODO verify this
14
+ locationCoordinates: reminder.location_coordinates,
15
+ locationPage: reminder.location_page,
16
+ eventStatus: reminder.lightweight_event_status.toLowerCase(),
17
+ note: reminder.note,
18
+ repeatMode: reminder.repeat_mode.toLowerCase(),
19
+ eventTitle: reminder.event_title,
20
+ triggerMessage: reminder.trigger_message,
21
+ secondsToNotifyBefore: reminder.seconds_to_notify_before,
22
+ allowsRsvp: reminder.allows_rsvp,
23
+ relatedEvent: reminder.related_event,
24
+ members: reminder.event_reminder_members.edges.map(function (member) {
25
+ return {
26
+ memberID: member.node.id,
27
+ state: member.guest_list_state.toLowerCase()
28
+ };
29
+ })
30
+ };
31
+ }
32
+
33
+ function formatThreadGraphQLResponse(data) {
34
+ var messageThread = data.o0.data.message_thread;
35
+ var threadID = messageThread.thread_key.thread_fbid ? messageThread.thread_key.thread_fbid : messageThread.thread_key.other_user_id;
36
+
37
+ // Remove me
38
+ var lastM = messageThread.last_message;
39
+ var snippetID = lastM && lastM.nodes && lastM.nodes[0] && lastM.nodes[0].message_sender && lastM.nodes[0].message_sender.messaging_actor ? lastM.nodes[0].message_sender.messaging_actor.id : null;
40
+ var snippetText = lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
41
+ var lastR = messageThread.last_read_receipt;
42
+ var lastReadTimestamp = lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise ? lastR.nodes[0].timestamp_precise : null;
43
+
44
+ return {
45
+ threadID: threadID,
46
+ threadName: messageThread.name,
47
+ participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
48
+ userInfo: messageThread.all_participants.edges.map(d => ({
49
+ id: d.node.messaging_actor.id,
50
+ name: d.node.messaging_actor.name,
51
+ firstName: d.node.messaging_actor.short_name,
52
+ vanity: d.node.messaging_actor.username,
53
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
54
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
55
+ gender: d.node.messaging_actor.gender,
56
+ type: d.node.messaging_actor.__typename,
57
+ isFriend: d.node.messaging_actor.is_viewer_friend,
58
+ isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
59
+ })),
60
+ unreadCount: messageThread.unread_count,
61
+ messageCount: messageThread.messages_count,
62
+ timestamp: messageThread.updated_time_precise,
63
+ muteUntil: messageThread.mute_until,
64
+ isGroup: messageThread.thread_type == "GROUP",
65
+ isSubscribed: messageThread.is_viewer_subscribed,
66
+ isArchived: messageThread.has_viewer_archived,
67
+ folder: messageThread.folder,
68
+ cannotReplyReason: messageThread.cannot_reply_reason,
69
+ eventReminders: messageThread.event_reminders ? messageThread.event_reminders.nodes.map(formatEventReminders) : null,
70
+ emoji: messageThread.customization_info ? messageThread.customization_info.emoji : null,
71
+ color: messageThread.customization_info && messageThread.customization_info.outgoing_bubble_color ? messageThread.customization_info.outgoing_bubble_color.slice(2) : null,
72
+ nicknames:
73
+ messageThread.customization_info &&
74
+ messageThread.customization_info.participant_customizations
75
+ ? messageThread.customization_info.participant_customizations.reduce(function (res, val) {
76
+ if (val.nickname) res[val.participant_id] = val.nickname;
77
+ return res;
78
+ }, {})
79
+ : {},
80
+ adminIDs: messageThread.thread_admins,
81
+ approvalMode: Boolean(messageThread.approval_mode),
82
+ approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
83
+ inviterID: a.inviter.id,
84
+ requesterID: a.requester.id,
85
+ timestamp: a.request_timestamp,
86
+ request_source: a.request_source // @Undocumented
87
+ })),
88
+
89
+ // @Undocumented
90
+ reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
91
+ mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
92
+ isPinProtected: messageThread.is_pin_protected,
93
+ relatedPageThread: messageThread.related_page_thread,
94
+
95
+ // @Legacy
96
+ name: messageThread.name,
97
+ snippet: snippetText,
98
+ snippetSender: snippetID,
99
+ snippetAttachments: [],
100
+ serverTimestamp: messageThread.updated_time_precise,
101
+ imageSrc: messageThread.image ? messageThread.image.uri : null,
102
+ isCanonicalUser: messageThread.is_canonical_neo_user,
103
+ isCanonical: messageThread.thread_type != "GROUP",
104
+ recipientsLoadable: true,
105
+ hasEmailParticipant: false,
106
+ readOnly: false,
107
+ canReply: messageThread.cannot_reply_reason == null,
108
+ lastMessageTimestamp: messageThread.last_message ? messageThread.last_message.timestamp_precise : null,
109
+ lastMessageType: "message",
110
+ lastReadTimestamp: lastReadTimestamp,
111
+ threadType: messageThread.thread_type == "GROUP" ? 2 : 1
112
+ };
113
+ }
114
+
115
+ module.exports = function (defaultFuncs, api, ctx) {
116
+ return function getThreadInfoGraphQL(threadID, callback) {
117
+ var resolveFunc = function () { };
118
+ var rejectFunc = function () { };
119
+ var returnPromise = new Promise(function (resolve, reject) {
120
+ resolveFunc = resolve;
121
+ rejectFunc = reject;
122
+ });
123
+
124
+ if (utils.getType(callback) != "Function" && utils.getType(callback) != "AsyncFunction") {
125
+ callback = function (err, data) {
126
+ if (err) return rejectFunc(err);
127
+ resolveFunc(data);
128
+ };
129
+ }
130
+
131
+ // `queries` has to be a string. I couldn't tell from the dev console. This
132
+ // took me a really long time to figure out. I deserve a cookie for this.
133
+ var form = {
134
+ queries: JSON.stringify({
135
+ o0: {
136
+ // This doc_id is valid as of July 20th, 2020
137
+ doc_id: "3449967031715030",
138
+ query_params: {
139
+ id: threadID,
140
+ message_limit: 0,
141
+ load_messages: false,
142
+ load_read_receipts: false,
143
+ before: null
144
+ }
145
+ }
146
+ }),
147
+ batch_name: "MessengerGraphQLThreadFetcher"
148
+ };
149
+
150
+ defaultFuncs
151
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
152
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
153
+ .then(function (resData) {
154
+ if (resData.error) throw resData;
155
+ // This returns us an array of things. The last one is the success /
156
+ // failure one.
157
+ // @TODO What do we do in this case?
158
+ if (resData[resData.length - 1].error_results !== 0) {
159
+ console.log(resData); //Log more info
160
+ throw new Error("well darn there was an error_result");
161
+ }
162
+ callback(null, formatThreadGraphQLResponse(resData[0]));
163
+ })
164
+ .catch(function (err) {
165
+ log.error("getThreadInfoGraphQL", "Can't get thread info");
166
+ return callback(err);
167
+ });
168
+
169
+ return returnPromise;
170
+ };
171
+ };
@@ -0,0 +1,56 @@
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 getThreadInfo(threadID, 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, data) {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc(data);
19
+ };
20
+ }
21
+
22
+ var form = {
23
+ client: "mercury"
24
+ };
25
+
26
+ api.getUserInfo(threadID, function (err, userRes) {
27
+ if (err) return callback(err);
28
+ var key = Object.keys(userRes).length > 0 ? "user_ids" : "thread_fbids";
29
+ form["threads[" + key + "][0]"] = threadID;
30
+
31
+ if (ctx.globalOptions.pageId) form.request_user_id = ctx.globalOptions.pageId;
32
+
33
+ defaultFuncs
34
+ .post("https://www.facebook.com/ajax/mercury/thread_info.php", ctx.jar, form)
35
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
36
+ .then(function (resData) {
37
+ if (resData.error) throw resData;
38
+ else if (!resData.payload) throw { error: "Could not retrieve thread Info." };
39
+
40
+ var threadData = resData.payload.threads[0];
41
+ var userData = userRes[threadID];
42
+
43
+ if (threadData == null) throw { error: "ThreadData is null" };
44
+
45
+ threadData.name = userData != null && userData.name != null ? userData.name : threadData.name;
46
+ threadData.image_src = userData != null && userData.thumbSrc != null ? userData.thumbSrc : threadData.image_src;
47
+ callback(null, utils.formatThread(threadData));
48
+ })
49
+ .catch(function (err) {
50
+ log.error("getThreadInfo", err);
51
+ return callback(err);
52
+ });
53
+ });
54
+ return returnPromise;
55
+ };
56
+ };
@@ -9,7 +9,7 @@ function createProfileUrl(url, username, id) {
9
9
  }
10
10
 
11
11
  function formatParticipants(participants) {
12
- return participants.edges.map((p)=>{
12
+ return participants.edges.map((p) => {
13
13
  p = p.node.messaging_actor;
14
14
  switch (p["__typename"]) {
15
15
  case "User":
@@ -21,7 +21,7 @@ function formatParticipants(participants) {
21
21
  gender: p.gender,
22
22
  url: p.url, // how about making it profileURL
23
23
  profilePicture: p.big_image_src.uri,
24
- username: (p.username||null),
24
+ username: (p.username || null),
25
25
  // TODO: maybe better names for these?
26
26
  isViewerFriend: p.is_viewer_friend, // true/false
27
27
  isMessengerUser: p.is_messenger_user, // true/false
@@ -37,7 +37,7 @@ function formatParticipants(participants) {
37
37
  name: p.name,
38
38
  url: p.url,
39
39
  profilePicture: p.big_image_src.uri,
40
- username: (p.username||null),
40
+ username: (p.username || null),
41
41
  // uhm... better names maybe?
42
42
  acceptsMessengerUserFeedback: p.accepts_messenger_user_feedback, // true/false
43
43
  isMessengerUser: p.is_messenger_user, // true/false
@@ -46,27 +46,18 @@ function formatParticipants(participants) {
46
46
  isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
47
47
  };
48
48
  case "ReducedMessagingActor":
49
+ case "UnavailableMessagingActor":
49
50
  return {
50
51
  accountType: p["__typename"],
51
52
  userID: utils.formatID(p.id.toString()),
52
53
  name: p.name,
53
54
  url: createProfileUrl(p.url, p.username, p.id), // in this case p.url is null all the time
54
55
  profilePicture: p.big_image_src.uri, // in this case it is default facebook photo, we could determine gender using it
55
- username: (p.username||null), // maybe we could use it to generate profile URL?
56
- isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
57
- };
58
- case "UnavailableMessagingActor":
59
- return {
60
- accountType: p["__typename"],
61
- userID: utils.formatID(p.id.toString()),
62
- name: p.name, // "Facebook User" in user's language
63
- url: createProfileUrl(p.url, p.username, p.id), // in this case p.url is null all the time
64
- profilePicture: p.big_image_src.uri, // default male facebook photo
65
- username: (p.username||null), // maybe we could use it to generate profile URL?
56
+ username: (p.username || null), // maybe we could use it to generate profile URL?
66
57
  isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
67
58
  };
68
59
  default:
69
- log.warn("getThreadList", "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/fca-unofficial/issues\n" + JSON.stringify(p, null, 2));
60
+ log.warn("getThreadList", "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues\n" + JSON.stringify(p, null, 2));
70
61
  return {
71
62
  accountType: p["__typename"],
72
63
  userID: utils.formatID(p.id.toString()),
@@ -78,9 +69,7 @@ function formatParticipants(participants) {
78
69
 
79
70
  // "FF8C0077" -> "8C0077"
80
71
  function formatColor(color) {
81
- if (color && color.match(/^(?:[0-9a-fA-F]{8})$/g)) {
82
- return color.slice(2);
83
- }
72
+ if (color && color.match(/^(?:[0-9a-fA-F]{8})$/g)) return color.slice(2);
84
73
  return color;
85
74
  }
86
75
 
@@ -95,24 +84,24 @@ function getThreadName(t) {
95
84
 
96
85
  function mapNicknames(customizationInfo) {
97
86
  return (customizationInfo && customizationInfo.participant_customizations) ? customizationInfo.participant_customizations.map(u => {
98
- return {
99
- "userID": u.participant_id,
100
- "nickname": u.nickname
101
- };
102
- }):[];
87
+ return {
88
+ "userID": u.participant_id,
89
+ "nickname": u.nickname
90
+ };
91
+ }) : [];
103
92
  }
104
93
 
105
94
  function formatThreadList(data) {
106
95
  return data.map(t => {
107
- let lastMessageNode = (t.last_message&&t.last_message.nodes&&t.last_message.nodes.length>0)?t.last_message.nodes[0]:null;
96
+ let lastMessageNode = (t.last_message && t.last_message.nodes && t.last_message.nodes.length > 0) ? t.last_message.nodes[0] : null;
108
97
  return {
109
- threadID: t.thread_key?utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id):null, // shall never be null
98
+ threadID: t.thread_key ? utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id) : null, // shall never be null
110
99
  name: getThreadName(t),
111
100
  unreadCount: t.unread_count,
112
101
  messageCount: t.messages_count,
113
- imageSrc: t.image?t.image.uri:null,
114
- emoji: t.customization_info?t.customization_info.emoji:null,
115
- color: formatColor(t.customization_info?t.customization_info.outgoing_bubble_color:null),
102
+ imageSrc: t.image ? t.image.uri : null,
103
+ emoji: t.customization_info ? t.customization_info.emoji : null,
104
+ color: formatColor(t.customization_info ? t.customization_info.outgoing_bubble_color : null),
116
105
  nicknames: mapNicknames(t.customization_info),
117
106
  muteUntil: t.mute_until,
118
107
  participants: formatParticipants(t.all_participants),
@@ -123,7 +112,7 @@ function formatThreadList(data) {
123
112
  // isPinProtected: t.is_pin_protected, // feature from future? always false (2018-04-04)
124
113
  customizationEnabled: t.customization_enabled, // false for ONE_TO_ONE with Page or ReducedMessagingActor
125
114
  participantAddMode: t.participant_add_mode_as_string, // "ADD" if "GROUP" and null if "ONE_TO_ONE"
126
- montageThread: t.montage_thread?Buffer.from(t.montage_thread.id,"base64").toString():null, // base64 encoded string "message_thread:0000000000000000"
115
+ montageThread: t.montage_thread ? Buffer.from(t.montage_thread.id, "base64").toString() : null, // base64 encoded string "message_thread:0000000000000000"
127
116
  // it is not userID nor any other ID known to me...
128
117
  // can somebody inspect it? where is it used?
129
118
  // probably Messenger Day uses it
@@ -134,13 +123,13 @@ function formatThreadList(data) {
134
123
  timestamp: t.updated_time_precise, // in miliseconds
135
124
  // isCanonicalUser: t.is_canonical_neo_user, // is it always false?
136
125
  // TODO: how about putting snippet in another object? current implementation does not handle every possibile message type etc.
137
- snippet: lastMessageNode?lastMessageNode.snippet:null,
138
- snippetAttachments: lastMessageNode?lastMessageNode.extensible_attachment:null, // TODO: not sure if it works
139
- snippetSender: lastMessageNode?utils.formatID((lastMessageNode.message_sender.messaging_actor.id || "").toString()):null,
140
- lastMessageTimestamp: lastMessageNode?lastMessageNode.timestamp_precise:null, // timestamp in miliseconds
141
- lastReadTimestamp: (t.last_read_receipt&&t.last_read_receipt.nodes.length>0)
142
- ? (t.last_read_receipt.nodes[0]?t.last_read_receipt.nodes[0].timestamp_precise:null)
143
- : null, // timestamp in miliseconds
126
+ snippet: lastMessageNode ? lastMessageNode.snippet : null,
127
+ snippetAttachments: lastMessageNode ? lastMessageNode.extensible_attachment : null, // TODO: not sure if it works
128
+ snippetSender: lastMessageNode ? utils.formatID((lastMessageNode.message_sender.messaging_actor.id || "").toString()) : null,
129
+ lastMessageTimestamp: lastMessageNode ? lastMessageNode.timestamp_precise : null, // timestamp in miliseconds
130
+ lastReadTimestamp: (t.last_read_receipt && t.last_read_receipt.nodes.length > 0)
131
+ ? (t.last_read_receipt.nodes[0] ? t.last_read_receipt.nodes[0].timestamp_precise : null)
132
+ : null, // timestamp in miliseconds
144
133
  cannotReplyReason: t.cannot_reply_reason, // TODO: inspect possible values
145
134
  approvalMode: Boolean(t.approval_mode),
146
135
 
@@ -151,28 +140,21 @@ function formatThreadList(data) {
151
140
  });
152
141
  }
153
142
 
154
- module.exports = function(defaultFuncs, api, ctx) {
143
+ module.exports = function (defaultFuncs, api, ctx) {
155
144
  return function getThreadList(limit, timestamp, tags, callback) {
156
145
  if (!callback && (utils.getType(tags) === "Function" || utils.getType(tags) === "AsyncFunction")) {
157
146
  callback = tags;
158
147
  tags = [""];
159
148
  }
160
- if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) {
161
- throw {error: "getThreadList: limit must be a positive integer"};
162
- }
163
- if (utils.getType(timestamp) !== "Null" &&
164
- (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) {
165
- throw {error: "getThreadList: timestamp must be an integer or null"};
166
- }
167
- if (utils.getType(tags) === "String") {
168
- tags = [tags];
169
- }
170
- if (utils.getType(tags) !== "Array") {
171
- throw {error: "getThreadList: tags must be an array"};
172
- }
149
+ if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) throw { error: "getThreadList: limit must be a positive integer" };
150
+
151
+ if (utils.getType(timestamp) !== "Null" && (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) throw { error: "getThreadList: timestamp must be an integer or null" };
152
+
153
+ if (utils.getType(tags) === "String") tags = [tags];
154
+ if (utils.getType(tags) !== "Array") throw { error: "getThreadList: tags must be an array" };
173
155
 
174
- var resolveFunc = function(){};
175
- var rejectFunc = function(){};
156
+ var resolveFunc = function () { };
157
+ var rejectFunc = function () { };
176
158
  var returnPromise = new Promise(function (resolve, reject) {
177
159
  resolveFunc = resolve;
178
160
  rejectFunc = reject;
@@ -180,9 +162,7 @@ module.exports = function(defaultFuncs, api, ctx) {
180
162
 
181
163
  if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
182
164
  callback = function (err, data) {
183
- if (err) {
184
- return rejectFunc(err);
185
- }
165
+ if (err) return rejectFunc(err);
186
166
  resolveFunc(data);
187
167
  };
188
168
  }
@@ -194,7 +174,7 @@ module.exports = function(defaultFuncs, api, ctx) {
194
174
  // This doc_id was valid on 2020-07-20
195
175
  "doc_id": "3336396659757871",
196
176
  "query_params": {
197
- "limit": limit+(timestamp?1:0),
177
+ "limit": limit + (timestamp ? 1 : 0),
198
178
  "before": timestamp,
199
179
  "tags": tags,
200
180
  "includeDeliveryReceipts": true,
@@ -209,13 +189,9 @@ module.exports = function(defaultFuncs, api, ctx) {
209
189
  .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
210
190
  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
211
191
  .then((resData) => {
212
- if (resData[resData.length - 1].error_results > 0) {
213
- throw resData[0].o0.errors;
214
- }
192
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
215
193
 
216
- if (resData[resData.length - 1].successful_results === 0) {
217
- throw {error: "getThreadList: there was no successful_results", res: resData};
218
- }
194
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "getThreadList: there was no successful_results", res: resData };
219
195
 
220
196
  // When we ask for threads using timestamp from the previous request,
221
197
  // we are getting the last thread repeated as the first thread in this response.
@@ -223,13 +199,12 @@ module.exports = function(defaultFuncs, api, ctx) {
223
199
  // It is also the reason for increasing limit by 1 when timestamp is set
224
200
  // this way user asks for 10 threads, we are asking for 11,
225
201
  // but after removing the duplicated one, it is again 10
226
- if (timestamp) {
227
- resData[0].o0.data.viewer.message_threads.nodes.shift();
228
- }
202
+ if (timestamp) resData[0].o0.data.viewer.message_threads.nodes.shift();
203
+
229
204
  callback(null, formatThreadList(resData[0].o0.data.viewer.message_threads.nodes));
230
205
  })
231
206
  .catch((err) => {
232
- log.error("getThreadList", err);
207
+ log.error("getThreadList", "ParseAndCheckLogin got status code: 404. Bailing out of trying to parse response.");
233
208
  return callback(err);
234
209
  });
235
210
 
@@ -0,0 +1,46 @@
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 getThreadList(start, end, type, callback) {
8
+ if (utils.getType(callback) === "Undefined") {
9
+ if (utils.getType(end) !== "Number") throw { error: "Please pass a number as a second argument." };
10
+ else if (utils.getType(type) === "Function" || utils.getType(type) === "AsyncFunction") {
11
+ callback = type;
12
+ type = "inbox"; //default to inbox
13
+ }
14
+ else if (utils.getType(type) !== "String") throw { error: "Please pass a String as a third argument. Your options are: inbox, pending, and archived" };
15
+ else throw { error: "getThreadList: need callback" };
16
+ }
17
+
18
+ if (type === "archived") type = "action:archived";
19
+ else if (type !== "inbox" && type !== "pending" && type !== "other") throw { error: "type can only be one of the following: inbox, pending, archived, other" };
20
+
21
+
22
+ if (end <= start) end = start + 20;
23
+
24
+ var form = {
25
+ client: "mercury"
26
+ };
27
+
28
+ form[type + "[offset]"] = start;
29
+ form[type + "[limit]"] = end - start;
30
+
31
+ if (ctx.globalOptions.pageID) form.request_user_id = ctx.globalOptions.pageID;
32
+
33
+ defaultFuncs
34
+ .post("https://www.facebook.com/ajax/mercury/threadlist_info.php", ctx.jar, form)
35
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
36
+ .then(function (resData) {
37
+ if (resData.error) throw resData;
38
+ log.verbose("getThreadList", JSON.stringify(resData.payload.threads));
39
+ return callback(null, (resData.payload.threads || []).map(utils.formatThread));
40
+ })
41
+ .catch(function (err) {
42
+ log.error("getThreadList", err);
43
+ return callback(err);
44
+ });
45
+ };
46
+ };
@@ -0,0 +1,59 @@
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 getThreadPictures(threadID, offset, limit, 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, data) {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc(data);
19
+ };
20
+ }
21
+
22
+ var form = {
23
+ thread_id: threadID,
24
+ offset: offset,
25
+ limit: limit
26
+ };
27
+
28
+ defaultFuncs
29
+ .post("https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php", ctx.jar, form)
30
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
31
+ .then(function (resData) {
32
+ if (resData.error) throw resData;
33
+ return Promise.all(
34
+ resData.payload.imagesData.map(function (image) {
35
+ form = {
36
+ thread_id: threadID,
37
+ image_id: image.fbid
38
+ };
39
+ return defaultFuncs
40
+ .post("https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php", ctx.jar, form)
41
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
42
+ .then(function (resData) {
43
+ if (resData.error) throw resData;
44
+ // the response is pretty messy
45
+ var queryThreadID = resData.jsmods.require[0][3][1].query_metadata.query_path[0].message_thread;
46
+ var imageData = resData.jsmods.require[0][3][1].query_results[queryThreadID].message_images.edges[0].node.image2;
47
+ return imageData;
48
+ });
49
+ })
50
+ );
51
+ })
52
+ .then(resData => callback(null, resData))
53
+ .catch(function (err) {
54
+ log.error("Error in getThreadPictures", err);
55
+ callback(err);
56
+ });
57
+ return returnPromise;
58
+ };
59
+ };
@@ -17,21 +17,19 @@ function formatData(data) {
17
17
  };
18
18
  }
19
19
 
20
- module.exports = function(defaultFuncs, api, ctx) {
20
+ module.exports = function (defaultFuncs, api, ctx) {
21
21
  return function getUserID(name, callback) {
22
- var resolveFunc = function(){};
23
- var rejectFunc = function(){};
22
+ var resolveFunc = function () { };
23
+ var rejectFunc = function () { };
24
24
  var returnPromise = new Promise(function (resolve, reject) {
25
25
  resolveFunc = resolve;
26
26
  rejectFunc = reject;
27
27
  });
28
28
 
29
29
  if (!callback) {
30
- callback = function (err, friendList) {
31
- if (err) {
32
- return rejectFunc(err);
33
- }
34
- resolveFunc(friendList);
30
+ callback = function (err, data) {
31
+ if (err) return rejectFunc(err);
32
+ resolveFunc(data);
35
33
  };
36
34
  }
37
35
 
@@ -47,16 +45,13 @@ module.exports = function(defaultFuncs, api, ctx) {
47
45
  defaultFuncs
48
46
  .get("https://www.facebook.com/ajax/typeahead/search.php", ctx.jar, form)
49
47
  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
50
- .then(function(resData) {
51
- if (resData.error) {
52
- throw resData;
53
- }
48
+ .then(function (resData) {
49
+ if (resData.error) throw resData;
54
50
 
55
51
  var data = resData.payload.entries;
56
-
57
52
  callback(null, data.map(formatData));
58
53
  })
59
- .catch(function(err) {
54
+ .catch(function (err) {
60
55
  log.error("getUserID", err);
61
56
  return callback(err);
62
57
  });
@@ -27,43 +27,37 @@ function formatData(data) {
27
27
  return retObj;
28
28
  }
29
29
 
30
- module.exports = function(defaultFuncs, api, ctx) {
30
+ module.exports = function (defaultFuncs, api, ctx) {
31
31
  return function getUserInfo(id, callback) {
32
- var resolveFunc = function(){};
33
- var rejectFunc = function(){};
32
+ var resolveFunc = function () { };
33
+ var rejectFunc = function () { };
34
34
  var returnPromise = new Promise(function (resolve, reject) {
35
35
  resolveFunc = resolve;
36
36
  rejectFunc = reject;
37
37
  });
38
38
 
39
39
  if (!callback) {
40
- callback = function (err, friendList) {
41
- if (err) {
42
- return rejectFunc(err);
43
- }
44
- resolveFunc(friendList);
40
+ callback = function (err, userInfo) {
41
+ if (err) return rejectFunc(err);
42
+ resolveFunc(userInfo);
45
43
  };
46
44
  }
47
45
 
48
- if (utils.getType(id) !== "Array") {
49
- id = [id];
50
- }
46
+ if (utils.getType(id) !== "Array") id = [id];
51
47
 
52
48
  var form = {};
53
- id.map(function(v, i) {
49
+ id.map(function (v, i) {
54
50
  form["ids[" + i + "]"] = v;
55
51
  });
56
52
  defaultFuncs
57
53
  .post("https://www.facebook.com/chat/user_info/", ctx.jar, form)
58
54
  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
59
- .then(function(resData) {
60
- if (resData.error) {
61
- throw resData;
62
- }
55
+ .then(function (resData) {
56
+ if (resData.error) throw resData;
63
57
  return callback(null, formatData(resData.payload.profiles));
64
58
  })
65
- .catch(function(err) {
66
- log.error("getUserInfo", err);
59
+ .catch(function (err) {
60
+ log.error("getUserInfo", "Can't get user info");
67
61
  return callback(err);
68
62
  });
69
63