alicezetion 1.0.2 → 1.0.4

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 (75) hide show
  1. package/.cache/replit/__replit_disk_meta.json +1 -1
  2. package/.cache/replit/nix/env.json +1 -1
  3. package/index.js +488 -560
  4. package/package.json +25 -25
  5. package/replit.nix +5 -0
  6. package/src/addExternalModule.js +15 -0
  7. package/{alice → src}/addUserToGroup.js +16 -52
  8. package/src/changeAdminStatus.js +47 -0
  9. package/src/changeArchivedStatus.js +41 -0
  10. package/{alice → src}/changeBio.js +6 -19
  11. package/{alice → src}/changeBlockedStatus.js +3 -14
  12. package/{alice → src}/changeGroupImage.js +16 -40
  13. package/src/changeNickname.js +43 -0
  14. package/{alice → src}/changeThreadColor.js +10 -20
  15. package/src/changeThreadEmoji.js +41 -0
  16. package/src/chat.js +315 -0
  17. package/{alice → src}/createNewGroup.js +12 -28
  18. package/{alice → src}/createPoll.js +13 -25
  19. package/src/deleteMessage.js +44 -0
  20. package/src/deleteThread.js +42 -0
  21. package/src/forwardAttachment.js +47 -0
  22. package/src/forwardMessage.js +0 -0
  23. package/{alice → src}/getCurrentUserID.js +1 -1
  24. package/{alice → src}/getEmojiUrl.js +2 -4
  25. package/{alice → src}/getFriendsList.js +10 -21
  26. package/{alice → src}/getThreadHistory.js +58 -166
  27. package/{alice → src}/getThreadHistoryDeprecated.js +20 -42
  28. package/{alice → src}/getThreadInfo.js +25 -60
  29. package/src/getThreadInfoDeprecated.js +56 -0
  30. package/{alice → src}/getThreadList.js +41 -66
  31. package/src/getThreadListDeprecated.js +46 -0
  32. package/src/getThreadPictures.js +59 -0
  33. package/{alice → src}/getUserID.js +9 -14
  34. package/{alice → src}/getUserInfo.js +13 -19
  35. package/src/handleFriendRequest.js +46 -0
  36. package/src/handleMessageRequest.js +47 -0
  37. package/{alice → src}/httpGet.js +12 -17
  38. package/{alice → src}/httpPost.js +12 -17
  39. package/src/listen.js +553 -0
  40. package/src/listenMqtt-Test.js +687 -0
  41. package/src/listenMqtt.js +677 -0
  42. package/{alice → src}/logout.js +13 -20
  43. package/{alice → src}/markAsDelivered.js +11 -22
  44. package/{alice → src}/markAsRead.js +11 -21
  45. package/{alice → src}/markAsReadAll.js +10 -20
  46. package/{alice → src}/markAsSeen.js +7 -18
  47. package/{alice → src}/muteThread.js +11 -18
  48. package/src/removeUserFromGroup.js +45 -0
  49. package/{alice → src}/resolvePhotoUrl.js +8 -17
  50. package/{alice → src}/searchForThread.js +10 -21
  51. package/{alice → src}/sendTypingIndicator.js +14 -47
  52. package/{alice → src}/setMessageReaction.js +12 -26
  53. package/{alice → src}/setPostReaction.js +13 -26
  54. package/{alice → src}/setTitle.js +13 -29
  55. package/src/threadColors.js +41 -0
  56. package/{alice → src}/unfriend.js +9 -19
  57. package/{alice → src}/unsendMessage.js +9 -19
  58. package/utils.js +1021 -1190
  59. package/alice/addExternalModule.js +0 -19
  60. package/alice/changeAdminStatus.js +0 -79
  61. package/alice/changeArchivedStatus.js +0 -55
  62. package/alice/changeNickname.js +0 -59
  63. package/alice/changeThreadEmoji.js +0 -55
  64. package/alice/deleteMessage.js +0 -56
  65. package/alice/deleteThread.js +0 -56
  66. package/alice/forwardAttachment.js +0 -60
  67. package/alice/getThreadInfoDeprecated.js +0 -80
  68. package/alice/getThreadListDeprecated.js +0 -75
  69. package/alice/getThreadPictures.js +0 -79
  70. package/alice/handleFriendRequest.js +0 -61
  71. package/alice/handleMessageRequest.js +0 -65
  72. package/alice/listenMqtt.js +0 -789
  73. package/alice/removeUserFromGroup.js +0 -79
  74. package/alice/sendMessage.js +0 -459
  75. package/alice/threadColors.js +0 -57
@@ -21,7 +21,7 @@ function formatEventReminders(reminder) {
21
21
  secondsToNotifyBefore: reminder.seconds_to_notify_before,
22
22
  allowsRsvp: reminder.allows_rsvp,
23
23
  relatedEvent: reminder.related_event,
24
- members: reminder.event_reminder_members.edges.map(function(member) {
24
+ members: reminder.event_reminder_members.edges.map(function (member) {
25
25
  return {
26
26
  memberID: member.node.id,
27
27
  state: member.guest_list_state.toLowerCase()
@@ -31,33 +31,15 @@ function formatEventReminders(reminder) {
31
31
  }
32
32
 
33
33
  function formatThreadGraphQLResponse(data) {
34
- try{
35
- var messageThread = data.o0.data.message_thread;
36
- } catch (err){
37
- console.error("GetThreadInfoGraphQL", "Can't get this thread info!");
38
- return {err: err};
39
- }
40
- var threadID = messageThread.thread_key.thread_fbid
41
- ? messageThread.thread_key.thread_fbid
42
- : messageThread.thread_key.other_user_id;
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;
43
36
 
44
37
  // Remove me
45
38
  var lastM = messageThread.last_message;
46
- var snippetID =
47
- lastM &&
48
- lastM.nodes &&
49
- lastM.nodes[0] &&
50
- lastM.nodes[0].message_sender &&
51
- lastM.nodes[0].message_sender.messaging_actor
52
- ? lastM.nodes[0].message_sender.messaging_actor.id
53
- : null;
54
- var snippetText =
55
- lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
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;
56
41
  var lastR = messageThread.last_read_receipt;
57
- var lastReadTimestamp =
58
- lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
59
- ? lastR.nodes[0].timestamp_precise
60
- : null;
42
+ var lastReadTimestamp = lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise ? lastR.nodes[0].timestamp_precise : null;
61
43
 
62
44
  return {
63
45
  threadID: threadID,
@@ -84,27 +66,16 @@ function formatThreadGraphQLResponse(data) {
84
66
  isArchived: messageThread.has_viewer_archived,
85
67
  folder: messageThread.folder,
86
68
  cannotReplyReason: messageThread.cannot_reply_reason,
87
- eventReminders: messageThread.event_reminders
88
- ? messageThread.event_reminders.nodes.map(formatEventReminders)
89
- : null,
90
- emoji: messageThread.customization_info
91
- ? messageThread.customization_info.emoji
92
- : null,
93
- color:
94
- messageThread.customization_info &&
95
- messageThread.customization_info.outgoing_bubble_color
96
- ? messageThread.customization_info.outgoing_bubble_color.slice(2)
97
- : null,
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,
98
72
  nicknames:
99
73
  messageThread.customization_info &&
100
- messageThread.customization_info.participant_customizations
101
- ? messageThread.customization_info.participant_customizations.reduce(
102
- function(res, val) {
103
- if (val.nickname) res[val.participant_id] = val.nickname;
104
- return res;
105
- },
106
- {}
107
- )
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
+ }, {})
108
79
  : {},
109
80
  adminIDs: messageThread.thread_admins,
110
81
  approvalMode: Boolean(messageThread.approval_mode),
@@ -134,19 +105,17 @@ function formatThreadGraphQLResponse(data) {
134
105
  hasEmailParticipant: false,
135
106
  readOnly: false,
136
107
  canReply: messageThread.cannot_reply_reason == null,
137
- lastMessageTimestamp: messageThread.last_message
138
- ? messageThread.last_message.timestamp_precise
139
- : null,
108
+ lastMessageTimestamp: messageThread.last_message ? messageThread.last_message.timestamp_precise : null,
140
109
  lastMessageType: "message",
141
110
  lastReadTimestamp: lastReadTimestamp,
142
111
  threadType: messageThread.thread_type == "GROUP" ? 2 : 1
143
112
  };
144
113
  }
145
114
 
146
- module.exports = function(defaultFuncs, api, ctx) {
115
+ module.exports = function (defaultFuncs, api, ctx) {
147
116
  return function getThreadInfoGraphQL(threadID, callback) {
148
- var resolveFunc = function(){};
149
- var rejectFunc = function(){};
117
+ var resolveFunc = function () { };
118
+ var rejectFunc = function () { };
150
119
  var returnPromise = new Promise(function (resolve, reject) {
151
120
  resolveFunc = resolve;
152
121
  rejectFunc = reject;
@@ -154,9 +123,7 @@ module.exports = function(defaultFuncs, api, ctx) {
154
123
 
155
124
  if (utils.getType(callback) != "Function" && utils.getType(callback) != "AsyncFunction") {
156
125
  callback = function (err, data) {
157
- if (err) {
158
- return rejectFunc(err);
159
- }
126
+ if (err) return rejectFunc(err);
160
127
  resolveFunc(data);
161
128
  };
162
129
  }
@@ -183,21 +150,19 @@ module.exports = function(defaultFuncs, api, ctx) {
183
150
  defaultFuncs
184
151
  .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
185
152
  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
186
- .then(function(resData) {
187
- if (resData.error) {
188
- throw resData;
189
- }
153
+ .then(function (resData) {
154
+ if (resData.error) throw resData;
190
155
  // This returns us an array of things. The last one is the success /
191
156
  // failure one.
192
157
  // @TODO What do we do in this case?
193
158
  if (resData[resData.length - 1].error_results !== 0) {
194
- console.error("GetThreadInfo", "Well darn there was an error_result");
159
+ console.log(resData); //Log more info
160
+ throw new Error("well darn there was an error_result");
195
161
  }
196
-
197
162
  callback(null, formatThreadGraphQLResponse(resData[0]));
198
163
  })
199
- .catch(function(err) {
200
- log.error("getThreadInfoGraphQL", err);
164
+ .catch(function (err) {
165
+ log.error("getThreadInfoGraphQL", "Can't get thread info");
201
166
  return callback(err);
202
167
  });
203
168
 
@@ -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
  });
@@ -19,7 +19,7 @@ function formatData(data) {
19
19
  gender: innerObj.gender,
20
20
  type: innerObj.type,
21
21
  isFriend: innerObj.is_friend,
22
- isBirthday: innerObj.is_birthday
22
+ isBirthday: !!innerObj.is_birthday
23
23
  };
24
24
  }
25
25
  }
@@ -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