fca-orion-api 1.0.0

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 (57) hide show
  1. package/config.js +5 -0
  2. package/data/fcaVersion.json +0 -0
  3. package/index.js +417 -0
  4. package/instantUpdate.js +40 -0
  5. package/package.json +30 -0
  6. package/src/addExternalModule.js +19 -0
  7. package/src/addUserToGroup.js +113 -0
  8. package/src/changeAdminStatus.js +79 -0
  9. package/src/changeArchivedStatus.js +55 -0
  10. package/src/changeAvatar.js +126 -0
  11. package/src/changeBio.js +77 -0
  12. package/src/changeBlockedStatus.js +47 -0
  13. package/src/changeGroupImage.js +132 -0
  14. package/src/changeNickname.js +59 -0
  15. package/src/changeThreadColor.js +65 -0
  16. package/src/changeThreadEmoji.js +55 -0
  17. package/src/createNewGroup.js +86 -0
  18. package/src/createPoll.js +71 -0
  19. package/src/deleteMessage.js +56 -0
  20. package/src/deleteThread.js +56 -0
  21. package/src/forwardAttachment.js +60 -0
  22. package/src/getCurrentUserID.js +7 -0
  23. package/src/getEmojiUrl.js +29 -0
  24. package/src/getFriendsList.js +83 -0
  25. package/src/getMessage.js +796 -0
  26. package/src/getThreadHistory.js +666 -0
  27. package/src/getThreadInfo.js +232 -0
  28. package/src/getThreadList.js +241 -0
  29. package/src/getThreadPictures.js +79 -0
  30. package/src/getUserID.js +66 -0
  31. package/src/getUserInfo.js +74 -0
  32. package/src/handleFriendRequest.js +61 -0
  33. package/src/handleMessageRequest.js +65 -0
  34. package/src/httpGet.js +57 -0
  35. package/src/httpPost.js +57 -0
  36. package/src/httpPostFormData.js +63 -0
  37. package/src/listenMqtt.js +853 -0
  38. package/src/logout.js +75 -0
  39. package/src/markAsDelivered.js +58 -0
  40. package/src/markAsRead.js +80 -0
  41. package/src/markAsReadAll.js +50 -0
  42. package/src/markAsSeen.js +59 -0
  43. package/src/muteThread.js +52 -0
  44. package/src/refreshFb_dtsg.js +81 -0
  45. package/src/removeUserFromGroup.js +79 -0
  46. package/src/resolvePhotoUrl.js +45 -0
  47. package/src/searchForThread.js +53 -0
  48. package/src/sendMessage.js +477 -0
  49. package/src/sendTypingIndicator.js +103 -0
  50. package/src/setMessageReaction.js +121 -0
  51. package/src/setPostReaction.js +109 -0
  52. package/src/setTitle.js +86 -0
  53. package/src/threadColors.js +131 -0
  54. package/src/unfriend.js +52 -0
  55. package/src/unsendMessage.js +49 -0
  56. package/src/uploadAttachment.js +95 -0
  57. package/utils.js +1545 -0
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const 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
+ if (data.errors)
35
+ return data.errors;
36
+ const messageThread = data.message_thread;
37
+ if (!messageThread)
38
+ return null;
39
+ const threadID = messageThread.thread_key.thread_fbid
40
+ ? messageThread.thread_key.thread_fbid
41
+ : messageThread.thread_key.other_user_id;
42
+
43
+ // Remove me
44
+ const lastM = messageThread.last_message;
45
+ const snippetID =
46
+ lastM &&
47
+ lastM.nodes &&
48
+ lastM.nodes[0] &&
49
+ lastM.nodes[0].message_sender &&
50
+ lastM.nodes[0].message_sender.messaging_actor
51
+ ? lastM.nodes[0].message_sender.messaging_actor.id
52
+ : null;
53
+ const snippetText =
54
+ lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
55
+ const lastR = messageThread.last_read_receipt;
56
+ const lastReadTimestamp =
57
+ lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
58
+ ? lastR.nodes[0].timestamp_precise
59
+ : null;
60
+
61
+ return {
62
+ threadID: threadID,
63
+ threadName: messageThread.name,
64
+ participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
65
+ userInfo: messageThread.all_participants.edges.map(d => ({
66
+ id: d.node.messaging_actor.id,
67
+ name: d.node.messaging_actor.name,
68
+ firstName: d.node.messaging_actor.short_name,
69
+ vanity: d.node.messaging_actor.username,
70
+ url: d.node.messaging_actor.url,
71
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
72
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
73
+ gender: d.node.messaging_actor.gender,
74
+ type: d.node.messaging_actor.__typename,
75
+ isFriend: d.node.messaging_actor.is_viewer_friend,
76
+ isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
77
+ })),
78
+ unreadCount: messageThread.unread_count,
79
+ messageCount: messageThread.messages_count,
80
+ timestamp: messageThread.updated_time_precise,
81
+ muteUntil: messageThread.mute_until,
82
+ isGroup: messageThread.thread_type == "GROUP",
83
+ isSubscribed: messageThread.is_viewer_subscribed,
84
+ isArchived: messageThread.has_viewer_archived,
85
+ folder: messageThread.folder,
86
+ 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,
98
+ threadTheme: messageThread.thread_theme,
99
+ nicknames:
100
+ messageThread.customization_info &&
101
+ messageThread.customization_info.participant_customizations
102
+ ? messageThread.customization_info.participant_customizations.reduce(
103
+ function (res, val) {
104
+ if (val.nickname) res[val.participant_id] = val.nickname;
105
+ return res;
106
+ },
107
+ {}
108
+ )
109
+ : {},
110
+ adminIDs: messageThread.thread_admins,
111
+ approvalMode: Boolean(messageThread.approval_mode),
112
+ approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
113
+ inviterID: a.inviter.id,
114
+ requesterID: a.requester.id,
115
+ timestamp: a.request_timestamp,
116
+ request_source: a.request_source // @Undocumented
117
+ })),
118
+
119
+ // @Undocumented
120
+ reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
121
+ mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
122
+ isPinProtected: messageThread.is_pin_protected,
123
+ relatedPageThread: messageThread.related_page_thread,
124
+
125
+ // @Legacy
126
+ name: messageThread.name,
127
+ snippet: snippetText,
128
+ snippetSender: snippetID,
129
+ snippetAttachments: [],
130
+ serverTimestamp: messageThread.updated_time_precise,
131
+ imageSrc: messageThread.image ? messageThread.image.uri : null,
132
+ isCanonicalUser: messageThread.is_canonical_neo_user,
133
+ isCanonical: messageThread.thread_type != "GROUP",
134
+ recipientsLoadable: true,
135
+ hasEmailParticipant: false,
136
+ readOnly: false,
137
+ canReply: messageThread.cannot_reply_reason == null,
138
+ lastMessageTimestamp: messageThread.last_message
139
+ ? messageThread.last_message.timestamp_precise
140
+ : null,
141
+ lastMessageType: "message",
142
+ lastReadTimestamp: lastReadTimestamp,
143
+ threadType: messageThread.thread_type == "GROUP" ? 2 : 1,
144
+
145
+ // update in Wed, 13 Jul 2022 19:41:12 +0700
146
+ inviteLink: {
147
+ enable: messageThread.joinable_mode ? messageThread.joinable_mode.mode == 1 : false,
148
+ link: messageThread.joinable_mode ? messageThread.joinable_mode.link : null
149
+ }
150
+ };
151
+ }
152
+
153
+ module.exports = function (defaultFuncs, api, ctx) {
154
+ return function getThreadInfoGraphQL(threadID, callback) {
155
+ let resolveFunc = function () { };
156
+ let rejectFunc = function () { };
157
+ const returnPromise = new Promise(function (resolve, reject) {
158
+ resolveFunc = resolve;
159
+ rejectFunc = reject;
160
+ });
161
+
162
+ if (utils.getType(callback) != "Function" && utils.getType(callback) != "AsyncFunction") {
163
+ callback = function (err, data) {
164
+ if (err) {
165
+ return rejectFunc(err);
166
+ }
167
+ resolveFunc(data);
168
+ };
169
+ }
170
+
171
+ if (utils.getType(threadID) !== "Array") {
172
+ threadID = [threadID];
173
+ }
174
+
175
+ let form = {};
176
+ // `queries` has to be a string. I couldn't tell from the dev console. This
177
+ // took me a really long time to figure out. I deserve a cookie for this.
178
+ threadID.map(function (t, i) {
179
+ form["o" + i] = {
180
+ doc_id: "3449967031715030",
181
+ query_params: {
182
+ id: t,
183
+ message_limit: 0,
184
+ load_messages: false,
185
+ load_read_receipts: false,
186
+ before: null
187
+ }
188
+ };
189
+ });
190
+
191
+ form = {
192
+ queries: JSON.stringify(form),
193
+ batch_name: "MessengerGraphQLThreadFetcher"
194
+ };
195
+
196
+ defaultFuncs
197
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
198
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
199
+ .then(function (resData) {
200
+
201
+ if (resData.error) {
202
+ throw resData;
203
+ }
204
+ // This returns us an array of things. The last one is the success /
205
+ // failure one.
206
+ // @TODO What do we do in this case?
207
+ // if (resData[resData.length - 1].error_results !== 0) {
208
+ // throw resData[0].o0.errors[0];
209
+ // }
210
+ // if (!resData[0].o0.data.message_thread) {
211
+ // throw new Error("can't find this thread");
212
+ // }
213
+ const threadInfos = {};
214
+ for (let i = resData.length - 2; i >= 0; i--) {
215
+ const threadInfo = formatThreadGraphQLResponse(resData[i][Object.keys(resData[i])[0]].data);
216
+ threadInfos[threadInfo?.threadID || threadID[threadID.length - 1 - i]] = threadInfo;
217
+ }
218
+ if (Object.values(threadInfos).length == 1) {
219
+ callback(null, Object.values(threadInfos)[0]);
220
+ }
221
+ else {
222
+ callback(null, threadInfos);
223
+ }
224
+ })
225
+ .catch(function (err) {
226
+ log.error("getThreadInfoGraphQL", err);
227
+ return callback(err);
228
+ });
229
+
230
+ return returnPromise;
231
+ };
232
+ };
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const 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(messageThread) {
34
+ const threadID = messageThread.thread_key.thread_fbid
35
+ ? messageThread.thread_key.thread_fbid
36
+ : messageThread.thread_key.other_user_id;
37
+
38
+ // Remove me
39
+ const lastM = messageThread.last_message;
40
+ const snippetID =
41
+ lastM &&
42
+ lastM.nodes &&
43
+ lastM.nodes[0] &&
44
+ lastM.nodes[0].message_sender &&
45
+ lastM.nodes[0].message_sender.messaging_actor
46
+ ? lastM.nodes[0].message_sender.messaging_actor.id
47
+ : null;
48
+ const snippetText =
49
+ lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
50
+ const lastR = messageThread.last_read_receipt;
51
+ const lastReadTimestamp =
52
+ lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
53
+ ? lastR.nodes[0].timestamp_precise
54
+ : null;
55
+
56
+ return {
57
+ threadID: threadID,
58
+ threadName: messageThread.name,
59
+ participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
60
+ userInfo: messageThread.all_participants.edges.map(d => ({
61
+ id: d.node.messaging_actor.id,
62
+ name: d.node.messaging_actor.name,
63
+ firstName: d.node.messaging_actor.short_name,
64
+ vanity: d.node.messaging_actor.username,
65
+ url: d.node.messaging_actor.url,
66
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
67
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
68
+ gender: d.node.messaging_actor.gender,
69
+ type: d.node.messaging_actor.__typename,
70
+ isFriend: d.node.messaging_actor.is_viewer_friend,
71
+ isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
72
+ })),
73
+ unreadCount: messageThread.unread_count,
74
+ messageCount: messageThread.messages_count,
75
+ timestamp: messageThread.updated_time_precise,
76
+ muteUntil: messageThread.mute_until,
77
+ isGroup: messageThread.thread_type == "GROUP",
78
+ isSubscribed: messageThread.is_viewer_subscribed,
79
+ isArchived: messageThread.has_viewer_archived,
80
+ folder: messageThread.folder,
81
+ cannotReplyReason: messageThread.cannot_reply_reason,
82
+ eventReminders: messageThread.event_reminders
83
+ ? messageThread.event_reminders.nodes.map(formatEventReminders)
84
+ : null,
85
+ emoji: messageThread.customization_info
86
+ ? messageThread.customization_info.emoji
87
+ : null,
88
+ color:
89
+ messageThread.customization_info &&
90
+ messageThread.customization_info.outgoing_bubble_color
91
+ ? messageThread.customization_info.outgoing_bubble_color.slice(2)
92
+ : null,
93
+ threadTheme: messageThread.thread_theme,
94
+ nicknames:
95
+ messageThread.customization_info &&
96
+ messageThread.customization_info.participant_customizations
97
+ ? messageThread.customization_info.participant_customizations.reduce(
98
+ function (res, val) {
99
+ if (val.nickname) res[val.participant_id] = val.nickname;
100
+ return res;
101
+ },
102
+ {}
103
+ )
104
+ : {},
105
+ adminIDs: messageThread.thread_admins,
106
+ approvalMode: Boolean(messageThread.approval_mode),
107
+ approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
108
+ inviterID: a.inviter.id,
109
+ requesterID: a.requester.id,
110
+ timestamp: a.request_timestamp,
111
+ request_source: a.request_source // @Undocumented
112
+ })),
113
+
114
+ // @Undocumented
115
+ reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
116
+ mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
117
+ isPinProtected: messageThread.is_pin_protected,
118
+ relatedPageThread: messageThread.related_page_thread,
119
+
120
+ // @Legacy
121
+ name: messageThread.name,
122
+ snippet: snippetText,
123
+ snippetSender: snippetID,
124
+ snippetAttachments: [],
125
+ serverTimestamp: messageThread.updated_time_precise,
126
+ imageSrc: messageThread.image ? messageThread.image.uri : null,
127
+ isCanonicalUser: messageThread.is_canonical_neo_user,
128
+ isCanonical: messageThread.thread_type != "GROUP",
129
+ recipientsLoadable: true,
130
+ hasEmailParticipant: false,
131
+ readOnly: false,
132
+ canReply: messageThread.cannot_reply_reason == null,
133
+ lastMessageTimestamp: messageThread.last_message
134
+ ? messageThread.last_message.timestamp_precise
135
+ : null,
136
+ lastMessageType: "message",
137
+ lastReadTimestamp: lastReadTimestamp,
138
+ threadType: messageThread.thread_type == "GROUP" ? 2 : 1,
139
+
140
+ // update in Wed, 13 Jul 2022 19:41:12 +0700
141
+ inviteLink: {
142
+ enable: messageThread.joinable_mode ? messageThread.joinable_mode.mode == 1 : false,
143
+ link: messageThread.joinable_mode ? messageThread.joinable_mode.link : null
144
+ }
145
+ };
146
+ }
147
+
148
+ function formatThreadList(data) {
149
+ // console.log(JSON.stringify(data.find(t => t.thread_key.thread_fbid === "5095817367161431"), null, 2));
150
+ return data.map(t => formatThreadGraphQLResponse(t));
151
+ }
152
+
153
+ module.exports = function (defaultFuncs, api, ctx) {
154
+ return function getThreadList(limit, timestamp, tags, callback) {
155
+ if (!callback && (utils.getType(tags) === "Function" || utils.getType(tags) === "AsyncFunction")) {
156
+ callback = tags;
157
+ tags = [""];
158
+ }
159
+ if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) {
160
+ throw new utils.CustomError({ error: "getThreadList: limit must be a positive integer" });
161
+ }
162
+ if (utils.getType(timestamp) !== "Null" &&
163
+ (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) {
164
+ throw new utils.CustomError({ error: "getThreadList: timestamp must be an integer or null" });
165
+ }
166
+ if (utils.getType(tags) === "String") {
167
+ tags = [tags];
168
+ }
169
+ if (utils.getType(tags) !== "Array") {
170
+ throw new utils.CustomError({
171
+ error: "getThreadList: tags must be an array",
172
+ message: "getThreadList: tags must be an array"
173
+ });
174
+ }
175
+
176
+ let resolveFunc = function () { };
177
+ let rejectFunc = function () { };
178
+ const returnPromise = new Promise(function (resolve, reject) {
179
+ resolveFunc = resolve;
180
+ rejectFunc = reject;
181
+ });
182
+
183
+ if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
184
+ callback = function (err, data) {
185
+ if (err) {
186
+ return rejectFunc(err);
187
+ }
188
+ resolveFunc(data);
189
+ };
190
+ }
191
+
192
+ const form = {
193
+ "av": ctx.i_userID || ctx.userID,
194
+ "queries": JSON.stringify({
195
+ "o0": {
196
+ // This doc_id was valid on 2020-07-20
197
+ // "doc_id": "3336396659757871",
198
+ "doc_id": "3426149104143726",
199
+ "query_params": {
200
+ "limit": limit + (timestamp ? 1 : 0),
201
+ "before": timestamp,
202
+ "tags": tags,
203
+ "includeDeliveryReceipts": true,
204
+ "includeSeqID": false
205
+ }
206
+ }
207
+ }),
208
+ "batch_name": "MessengerGraphQLThreadlistFetcher"
209
+ };
210
+
211
+ defaultFuncs
212
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
213
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
214
+ .then((resData) => {
215
+ if (resData[resData.length - 1].error_results > 0) {
216
+ throw new utils.CustomError(resData[0].o0.errors);
217
+ }
218
+
219
+ if (resData[resData.length - 1].successful_results === 0) {
220
+ throw new utils.CustomError({ error: "getThreadList: there was no successful_results", res: resData });
221
+ }
222
+
223
+ // When we ask for threads using timestamp from the previous request,
224
+ // we are getting the last thread repeated as the first thread in this response.
225
+ // .shift() gets rid of it
226
+ // It is also the reason for increasing limit by 1 when timestamp is set
227
+ // this way user asks for 10 threads, we are asking for 11,
228
+ // but after removing the duplicated one, it is again 10
229
+ if (timestamp) {
230
+ resData[0].o0.data.viewer.message_threads.nodes.shift();
231
+ }
232
+ callback(null, formatThreadList(resData[0].o0.data.viewer.message_threads.nodes));
233
+ })
234
+ .catch((err) => {
235
+ log.error("getThreadList", err);
236
+ return callback(err);
237
+ });
238
+
239
+ return returnPromise;
240
+ };
241
+ };
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ module.exports = function (defaultFuncs, api, ctx) {
7
+ return function getThreadPictures(threadID, offset, limit, callback) {
8
+ let resolveFunc = function () { };
9
+ let rejectFunc = function () { };
10
+ const returnPromise = new Promise(function (resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = function (err, friendList) {
17
+ if (err) {
18
+ return rejectFunc(err);
19
+ }
20
+ resolveFunc(friendList);
21
+ };
22
+ }
23
+
24
+ let form = {
25
+ thread_id: threadID,
26
+ offset: offset,
27
+ limit: limit
28
+ };
29
+
30
+ defaultFuncs
31
+ .post(
32
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
33
+ ctx.jar,
34
+ form
35
+ )
36
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
37
+ .then(function (resData) {
38
+ if (resData.error) {
39
+ throw resData;
40
+ }
41
+ return Promise.all(
42
+ resData.payload.imagesData.map(function (image) {
43
+ form = {
44
+ thread_id: threadID,
45
+ image_id: image.fbid
46
+ };
47
+ return defaultFuncs
48
+ .post(
49
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
50
+ ctx.jar,
51
+ form
52
+ )
53
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
54
+ .then(function (resData) {
55
+ if (resData.error) {
56
+ throw resData;
57
+ }
58
+ // the response is pretty messy
59
+ const queryThreadID =
60
+ resData.jsmods.require[0][3][1].query_metadata.query_path[0]
61
+ .message_thread;
62
+ const imageData =
63
+ resData.jsmods.require[0][3][1].query_results[queryThreadID]
64
+ .message_images.edges[0].node.image2;
65
+ return imageData;
66
+ });
67
+ })
68
+ );
69
+ })
70
+ .then(function (resData) {
71
+ callback(null, resData);
72
+ })
73
+ .catch(function (err) {
74
+ log.error("Error in getThreadPictures", err);
75
+ callback(err);
76
+ });
77
+ return returnPromise;
78
+ };
79
+ };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ function formatData(data) {
7
+ return {
8
+ userID: utils.formatID(data.uid.toString()),
9
+ photoUrl: data.photo,
10
+ indexRank: data.index_rank,
11
+ name: data.text,
12
+ isVerified: data.is_verified,
13
+ profileUrl: data.path,
14
+ category: data.category,
15
+ score: data.score,
16
+ type: data.type
17
+ };
18
+ }
19
+
20
+ module.exports = function (defaultFuncs, api, ctx) {
21
+ return function getUserID(name, callback) {
22
+ let resolveFunc = function () { };
23
+ let rejectFunc = function () { };
24
+ const returnPromise = new Promise(function (resolve, reject) {
25
+ resolveFunc = resolve;
26
+ rejectFunc = reject;
27
+ });
28
+
29
+ if (!callback) {
30
+ callback = function (err, friendList) {
31
+ if (err) {
32
+ return rejectFunc(err);
33
+ }
34
+ resolveFunc(friendList);
35
+ };
36
+ }
37
+
38
+ const form = {
39
+ value: name.toLowerCase(),
40
+ viewer: ctx.i_userID || ctx.userID,
41
+ rsp: "search",
42
+ context: "search",
43
+ path: "/home.php",
44
+ request_id: utils.getGUID()
45
+ };
46
+
47
+ defaultFuncs
48
+ .get("https://www.facebook.com/ajax/typeahead/search.php", ctx.jar, form)
49
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
50
+ .then(function (resData) {
51
+ if (resData.error) {
52
+ throw resData;
53
+ }
54
+
55
+ const data = resData.payload.entries;
56
+
57
+ callback(null, data.map(formatData));
58
+ })
59
+ .catch(function (err) {
60
+ log.error("getUserID", err);
61
+ return callback(err);
62
+ });
63
+
64
+ return returnPromise;
65
+ };
66
+ };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ function formatData(data) {
7
+ const retObj = {};
8
+
9
+ for (const prop in data) {
10
+ // eslint-disable-next-line no-prototype-builtins
11
+ if (data.hasOwnProperty(prop)) {
12
+ const innerObj = data[prop];
13
+ retObj[prop] = {
14
+ name: innerObj.name,
15
+ firstName: innerObj.firstName,
16
+ vanity: innerObj.vanity,
17
+ thumbSrc: innerObj.thumbSrc,
18
+ profileUrl: innerObj.uri,
19
+ gender: innerObj.gender,
20
+ type: innerObj.type,
21
+ isFriend: innerObj.is_friend,
22
+ isBirthday: !!innerObj.is_birthday,
23
+ searchTokens: innerObj.searchTokens,
24
+ alternateName: innerObj.alternateName
25
+ };
26
+ }
27
+ }
28
+
29
+ return retObj;
30
+ }
31
+
32
+ module.exports = function (defaultFuncs, api, ctx) {
33
+ return function getUserInfo(id, callback) {
34
+ let resolveFunc = function () { };
35
+ let rejectFunc = function () { };
36
+ const returnPromise = new Promise(function (resolve, reject) {
37
+ resolveFunc = resolve;
38
+ rejectFunc = reject;
39
+ });
40
+
41
+ if (!callback) {
42
+ callback = function (err, friendList) {
43
+ if (err) {
44
+ return rejectFunc(err);
45
+ }
46
+ resolveFunc(friendList);
47
+ };
48
+ }
49
+
50
+ if (utils.getType(id) !== "Array") {
51
+ id = [id];
52
+ }
53
+
54
+ const form = {};
55
+ id.map(function (v, i) {
56
+ form["ids[" + i + "]"] = v;
57
+ });
58
+ defaultFuncs
59
+ .post("https://www.facebook.com/chat/user_info/", ctx.jar, form)
60
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
61
+ .then(function (resData) {
62
+ if (resData.error) {
63
+ throw resData;
64
+ }
65
+ return callback(null, formatData(resData.payload.profiles));
66
+ })
67
+ .catch(function (err) {
68
+ log.error("getUserInfo", err);
69
+ return callback(err);
70
+ });
71
+
72
+ return returnPromise;
73
+ };
74
+ };