nexus-fca 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 (180) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/DOCS.md +2047 -0
  3. package/Fca_Database/database.sqlite +0 -0
  4. package/LICENSE-MIT +21 -0
  5. package/README.md +240 -0
  6. package/docs/README.md +9 -0
  7. package/docs/addExternalModule.md +15 -0
  8. package/docs/addUserToGroup.md +20 -0
  9. package/docs/changeAdminStatus.md +21 -0
  10. package/docs/changeArchivedStatus.md +19 -0
  11. package/docs/changeAvatar.md +21 -0
  12. package/docs/changeAvatarV2.md +18 -0
  13. package/docs/changeBio.md +15 -0
  14. package/docs/changeBlockedStatus.md +16 -0
  15. package/docs/changeBlockedStatusMqtt.md +16 -0
  16. package/docs/changeCover.md +15 -0
  17. package/docs/changeGroupImage.md +16 -0
  18. package/docs/changeName.md +18 -0
  19. package/docs/changeNickname.md +17 -0
  20. package/docs/changeThreadColor.md +16 -0
  21. package/docs/changeThreadEmoji.md +16 -0
  22. package/docs/changeUsername.md +15 -0
  23. package/docs/createCommentPost.md +16 -0
  24. package/docs/createNewGroup.md +16 -0
  25. package/docs/createPoll.md +17 -0
  26. package/docs/createPost.md +15 -0
  27. package/docs/deleteMessage.md +15 -0
  28. package/docs/deleteThread.md +15 -0
  29. package/docs/editMessage.md +16 -0
  30. package/docs/follow.md +15 -0
  31. package/docs/forwardAttachment.md +16 -0
  32. package/docs/getAccess.md +17 -0
  33. package/docs/getAvatarUser.md +15 -0
  34. package/docs/getBotInitialData.md +14 -0
  35. package/docs/getCtx.md +14 -0
  36. package/docs/getCurrentUserID.md +12 -0
  37. package/docs/getEmojiUrl.md +17 -0
  38. package/docs/getFriendsList.md +14 -0
  39. package/docs/getMessage.md +15 -0
  40. package/docs/getOptions.md +14 -0
  41. package/docs/getRegion.md +14 -0
  42. package/docs/getThreadHistory.md +17 -0
  43. package/docs/getThreadHistoryDeprecated.md +17 -0
  44. package/docs/getThreadInfo.md +15 -0
  45. package/docs/getThreadInfoDeprecated.md +17 -0
  46. package/docs/getThreadList.md +17 -0
  47. package/docs/getThreadListDeprecated.md +17 -0
  48. package/docs/getThreadPictures.md +17 -0
  49. package/docs/getUID.md +15 -0
  50. package/docs/getUserID.md +15 -0
  51. package/docs/getUserInfo.md +15 -0
  52. package/docs/handleFriendRequest.md +16 -0
  53. package/docs/handleMessageRequest.md +16 -0
  54. package/docs/httpGet.md +15 -0
  55. package/docs/httpPost.md +16 -0
  56. package/docs/httpPostFormData.md +16 -0
  57. package/docs/listenMqtt.md +17 -0
  58. package/docs/listenNotification.md +14 -0
  59. package/docs/logout.md +14 -0
  60. package/docs/markAsDelivered.md +16 -0
  61. package/docs/markAsRead.md +16 -0
  62. package/docs/markAsReadAll.md +14 -0
  63. package/docs/markAsSeen.md +15 -0
  64. package/docs/muteThread.md +16 -0
  65. package/docs/pinMessage.md +17 -0
  66. package/docs/postFormData.md +16 -0
  67. package/docs/refreshFb_dtsg.md +14 -0
  68. package/docs/removeUserFromGroup.md +16 -0
  69. package/docs/resolvePhotoUrl.md +15 -0
  70. package/docs/searchForThread.md +15 -0
  71. package/docs/searchStickers.md +15 -0
  72. package/docs/sendComment.md +16 -0
  73. package/docs/sendMessage.md +16 -0
  74. package/docs/sendMessageMqtt.md +16 -0
  75. package/docs/sendTypingIndicator.md +15 -0
  76. package/docs/setMessageReaction.md +17 -0
  77. package/docs/setMessageReactionMqtt.md +16 -0
  78. package/docs/setPostReaction.md +16 -0
  79. package/docs/setProfileGuard.md +15 -0
  80. package/docs/setStoryReaction.md +16 -0
  81. package/docs/setTitle.md +16 -0
  82. package/docs/shareContact.md +16 -0
  83. package/docs/shareLink.md +16 -0
  84. package/docs/stopListenMqtt.md +8 -0
  85. package/docs/threadColors.md +11 -0
  86. package/docs/unfriend.md +15 -0
  87. package/docs/unsendMessage.md +15 -0
  88. package/docs/uploadAttachment.md +15 -0
  89. package/fca-config.json +7 -0
  90. package/index.d.ts +618 -0
  91. package/index.js +361 -0
  92. package/lib/database/models/index.js +47 -0
  93. package/lib/database/models/thread.js +31 -0
  94. package/lib/database/threadData.js +93 -0
  95. package/lib/logger.js +24 -0
  96. package/lib/login.js +0 -0
  97. package/package.json +90 -0
  98. package/src/addExternalModule.js +19 -0
  99. package/src/addUserToGroup.js +113 -0
  100. package/src/changeAdminStatus.js +79 -0
  101. package/src/changeArchivedStatus.js +55 -0
  102. package/src/changeAvatar.js +126 -0
  103. package/src/changeAvatarV2.js +77 -0
  104. package/src/changeBio.js +77 -0
  105. package/src/changeBlockedStatus.js +47 -0
  106. package/src/changeBlockedStatusMqtt.js +71 -0
  107. package/src/changeCover.js +72 -0
  108. package/src/changeGroupImage.js +132 -0
  109. package/src/changeName.js +79 -0
  110. package/src/changeNickname.js +59 -0
  111. package/src/changeThreadColor.js +65 -0
  112. package/src/changeThreadEmoji.js +55 -0
  113. package/src/changeUsername.js +58 -0
  114. package/src/createCommentPost.js +225 -0
  115. package/src/createNewGroup.js +86 -0
  116. package/src/createPoll.js +71 -0
  117. package/src/createPost.js +276 -0
  118. package/src/deleteMessage.js +56 -0
  119. package/src/deleteThread.js +56 -0
  120. package/src/editMessage.js +57 -0
  121. package/src/follow.js +54 -0
  122. package/src/forwardAttachment.js +60 -0
  123. package/src/getAccess.js +67 -0
  124. package/src/getAvatarUser.js +56 -0
  125. package/src/getBotInitialData.js +37 -0
  126. package/src/getCtx.js +6 -0
  127. package/src/getCurrentUserID.js +7 -0
  128. package/src/getEmojiUrl.js +29 -0
  129. package/src/getFriendsList.js +83 -0
  130. package/src/getMessage.js +796 -0
  131. package/src/getOptions.js +6 -0
  132. package/src/getRegion.js +8 -0
  133. package/src/getThreadHistory.js +666 -0
  134. package/src/getThreadHistoryDeprecated.js +55 -0
  135. package/src/getThreadInfo.js +535 -0
  136. package/src/getThreadInfoDeprecated.js +49 -0
  137. package/src/getThreadList.js +192 -0
  138. package/src/getThreadListDeprecated.js +54 -0
  139. package/src/getThreadPictures.js +79 -0
  140. package/src/getUID.js +67 -0
  141. package/src/getUserID.js +66 -0
  142. package/src/getUserInfo.js +80 -0
  143. package/src/handleFriendRequest.js +61 -0
  144. package/src/handleMessageRequest.js +65 -0
  145. package/src/httpGet.js +57 -0
  146. package/src/httpPost.js +57 -0
  147. package/src/httpPostFormData.js +63 -0
  148. package/src/listenMqtt.js +1039 -0
  149. package/src/listenNotification.js +65 -0
  150. package/src/logout.js +75 -0
  151. package/src/markAsDelivered.js +58 -0
  152. package/src/markAsRead.js +80 -0
  153. package/src/markAsReadAll.js +50 -0
  154. package/src/markAsSeen.js +59 -0
  155. package/src/muteThread.js +52 -0
  156. package/src/pinMessage.js +59 -0
  157. package/src/postFormData.js +46 -0
  158. package/src/refreshFb_dtsg.js +66 -0
  159. package/src/removeUserFromGroup.js +79 -0
  160. package/src/resolvePhotoUrl.js +45 -0
  161. package/src/searchForThread.js +53 -0
  162. package/src/searchStickers.js +51 -0
  163. package/src/sendComment.js +63 -0
  164. package/src/sendMessage.js +328 -0
  165. package/src/sendMessageMqtt.js +316 -0
  166. package/src/sendTypingIndicator.js +103 -0
  167. package/src/setMessageReaction.js +119 -0
  168. package/src/setMessageReactionMqtt.js +61 -0
  169. package/src/setPostReaction.js +109 -0
  170. package/src/setProfileGuard.js +45 -0
  171. package/src/setStoryReaction.js +62 -0
  172. package/src/setTitle.js +86 -0
  173. package/src/shareContact.js +49 -0
  174. package/src/shareLink.js +59 -0
  175. package/src/stopListenMqtt.js +21 -0
  176. package/src/threadColors.js +131 -0
  177. package/src/unfriend.js +52 -0
  178. package/src/unsendMessage.js +49 -0
  179. package/src/uploadAttachment.js +95 -0
  180. package/utils.js +1432 -0
@@ -0,0 +1,55 @@
1
+ // Nexus-FCA: Advanced and Safe Facebook Chat API
2
+ // getThreadHistoryDeprecated.js - Deprecated thread history fetch
3
+
4
+ const utils = require("../utils");
5
+
6
+ module.exports = function(defaultFuncs, api, ctx) {
7
+ return function getThreadHistory(threadID, amount, timestamp, 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
+ if (!callback) {
25
+ throw { error: "getThreadHistory: need callback" };
26
+ }
27
+
28
+ const form = {
29
+ client: "mercury"
30
+ };
31
+
32
+ api.getUserInfo(threadID, function(err, res) {
33
+ if (err) {
34
+ return callback(err);
35
+ }
36
+ const key = Object.keys(res).length > 0 ? "user_ids" : "thread_fbids";
37
+ form["messages[" + key + "][" + threadID + "][offset]"] = 0;
38
+ form["messages[" + key + "][" + threadID + "][timestamp]"] = timestamp;
39
+ form["messages[" + key + "][" + threadID + "][limit]"] = amount;
40
+
41
+ if (ctx.globalOptions.pageID)
42
+ form.request_user_id = ctx.globalOptions.pageID;
43
+
44
+ defaultFuncs
45
+ .post(
46
+ "https://www.facebook.com/ajax/mercury/thread_info.php",
47
+ ctx.jar,
48
+ form
49
+ )
50
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
51
+ // ...existing code...
52
+ });
53
+ return returnPromise;
54
+ };
55
+ };
@@ -0,0 +1,535 @@
1
+ "use strict";
2
+ /*
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const logger = require("../lib/logger");
8
+ function formatEventReminders(reminder) {
9
+ return {
10
+ reminderID: reminder.id,
11
+ eventCreatorID: reminder.lightweight_event_creator.id,
12
+ time: reminder.time,
13
+ eventType: reminder.lightweight_event_type.toLowerCase(),
14
+ locationName: reminder.location_name,
15
+ locationCoordinates: reminder.location_coordinates,
16
+ locationPage: reminder.location_page,
17
+ eventStatus: reminder.lightweight_event_status.toLowerCase(),
18
+ note: reminder.note,
19
+ repeatMode: reminder.repeat_mode.toLowerCase(),
20
+ eventTitle: reminder.event_title,
21
+ triggerMessage: reminder.trigger_message,
22
+ secondsToNotifyBefore: reminder.seconds_to_notify_before,
23
+ allowsRsvp: reminder.allows_rsvp,
24
+ relatedEvent: reminder.related_event,
25
+ members: reminder.event_reminder_members.edges.map(function(member) {
26
+ return {
27
+ memberID: member.node.id,
28
+ state: member.guest_list_state.toLowerCase(),
29
+ };
30
+ }),
31
+ };
32
+ }
33
+ function formatThreadGraphQLResponse(data) {
34
+ if (!data) return;
35
+ if (data?.errors) return data.errors;
36
+ const messageThread = data.message_thread;
37
+ if (!messageThread) return null;
38
+ const threadID = messageThread.thread_key.thread_fbid
39
+ ? messageThread.thread_key.thread_fbid
40
+ : messageThread.thread_key.other_user_id;
41
+ const lastM = messageThread.last_message;
42
+ const snippetID =
43
+ lastM &&
44
+ lastM.nodes &&
45
+ lastM.nodes[0] &&
46
+ lastM.nodes[0].message_sender &&
47
+ lastM.nodes[0].message_sender.messaging_actor
48
+ ? lastM.nodes[0].message_sender.messaging_actor.id
49
+ : null;
50
+ const snippetText =
51
+ lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
52
+ const lastR = messageThread.last_read_receipt;
53
+ const lastReadTimestamp =
54
+ lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
55
+ ? lastR.nodes[0].timestamp_precise
56
+ : null;
57
+
58
+ return {
59
+ threadID: threadID,
60
+ threadName: messageThread.name,
61
+ participantIDs: messageThread.all_participants.edges.map(
62
+ (d) => d.node.messaging_actor.id
63
+ ),
64
+ userInfo: messageThread.all_participants.edges.map((d) => ({
65
+ id: d.node.messaging_actor.id,
66
+ name: d.node.messaging_actor.name,
67
+ firstName: d.node.messaging_actor.short_name,
68
+ vanity: d.node.messaging_actor.username,
69
+ url: d.node.messaging_actor.url,
70
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
71
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
72
+ gender: d.node.messaging_actor.gender,
73
+ type: d.node.messaging_actor.__typename,
74
+ isFriend: d.node.messaging_actor.is_viewer_friend,
75
+ isBirthday: !!d.node.messaging_actor.is_birthday,
76
+ })),
77
+ unreadCount: messageThread.unread_count,
78
+ messageCount: messageThread.messages_count,
79
+ timestamp: messageThread.updated_time_precise,
80
+ muteUntil: messageThread.mute_until,
81
+ isGroup: messageThread.thread_type == "GROUP",
82
+ isSubscribed: messageThread.is_viewer_subscribed,
83
+ isArchived: messageThread.has_viewer_archived,
84
+ folder: messageThread.folder,
85
+ cannotReplyReason: messageThread.cannot_reply_reason,
86
+ eventReminders: messageThread.event_reminders
87
+ ? messageThread.event_reminders.nodes.map(formatEventReminders)
88
+ : null,
89
+ emoji: messageThread.customization_info
90
+ ? messageThread.customization_info.emoji
91
+ : null,
92
+ color:
93
+ messageThread.customization_info &&
94
+ messageThread.customization_info.outgoing_bubble_color
95
+ ? messageThread.customization_info.outgoing_bubble_color.slice(2)
96
+ : null,
97
+ threadTheme: messageThread.thread_theme,
98
+ nicknames:
99
+ 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
+ )
108
+ : {},
109
+ adminIDs: messageThread.thread_admins,
110
+ approvalMode: Boolean(messageThread.approval_mode),
111
+ approvalQueue: messageThread.group_approval_queue.nodes.map((a) => ({
112
+ inviterID: a.inviter.id,
113
+ requesterID: a.requester.id,
114
+ timestamp: a.request_timestamp,
115
+ request_source: a.request_source,
116
+ })),
117
+ reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
118
+ mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
119
+ isPinProtected: messageThread.is_pin_protected,
120
+ relatedPageThread: messageThread.related_page_thread,
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
+ inviteLink: {
140
+ enable: messageThread.joinable_mode
141
+ ? messageThread.joinable_mode.mode == 1
142
+ : false,
143
+ link: messageThread.joinable_mode
144
+ ? messageThread.joinable_mode.link
145
+ : null,
146
+ },
147
+ };
148
+ }
149
+ const queue = [];
150
+ let isProcessingQueue = false;
151
+ const processingThreads = new Set();
152
+ const queuedThreads = new Set();
153
+ module.exports = function(defaultFuncs, api, ctx) {
154
+ const getMultiInfo = async function(threadIDs) {
155
+ let form = {};
156
+ let tempThreadInf = [];
157
+ threadIDs.forEach((x, y) => {
158
+ form["o" + y] = {
159
+ doc_id: "3449967031715030",
160
+ query_params: {
161
+ id: x,
162
+ message_limit: 0,
163
+ load_messages: false,
164
+ load_read_receipts: false,
165
+ before: null,
166
+ },
167
+ };
168
+ });
169
+ let Submit = {
170
+ queries: JSON.stringify(form),
171
+ batch_name: "MessengerGraphQLThreadFetcher",
172
+ };
173
+ try {
174
+ const resData = await defaultFuncs
175
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, Submit)
176
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
177
+ if (resData.error || resData[resData.length - 1].error_results !== 0)
178
+ throw new Error(
179
+ "Error: getThreadInfoGraphQL - You may be rate limited"
180
+ );
181
+ resData
182
+ .slice(0, -1)
183
+ .sort((a, b) => Object.keys(a)[0].localeCompare(Object.keys(b)[0]))
184
+ .forEach((x, y) =>
185
+ tempThreadInf.push(formatThreadGraphQLResponse(x["o" + y].data))
186
+ );
187
+ return { Success: true, Data: tempThreadInf };
188
+ } catch (err) {
189
+ return { Success: false, Data: "" };
190
+ }
191
+ };
192
+ const dbFiles = fs
193
+ .readdirSync(path.join(__dirname, "../lib/database"))
194
+ .filter((f) => path.extname(f) === ".js")
195
+ .reduce((acc, file) => {
196
+ acc[path.basename(file, ".js")] = require(path.join(
197
+ __dirname,
198
+ "../lib/database",
199
+ file
200
+ ))(api);
201
+ return acc;
202
+ }, {});
203
+ const { threadData } = dbFiles;
204
+ const { create, get, update, getAll } = threadData;
205
+ async function fetchThreadInfo(tID, isNew) {
206
+ try {
207
+ const response = await getMultiInfo([tID]);
208
+ if (!response.Success) throw new Error("Failed to fetch thread info");
209
+ const threadInfo = response.Data[0];
210
+ if (isNew) {
211
+ await create(tID, { data: threadInfo });
212
+ logger(`Success create data thread: ${tID}`, 'info');
213
+ } else {
214
+ await update(tID, { data: threadInfo });
215
+ logger(`Success update data thread: ${tID}`, 'info');
216
+ }
217
+ } catch (err) {
218
+ console.error("fetchThreadInfo", err);
219
+ } finally {
220
+ queuedThreads.delete(tID);
221
+ }
222
+ }
223
+ async function checkAndUpdateThreads() {
224
+ try {
225
+ const allThreads = await getAll("threadID");
226
+ const existingThreadIDs = new Set(allThreads.map((t) => t.threadID));
227
+ for (const t of existingThreadIDs) {
228
+ const result = await get(t);
229
+ if (!result) continue;
230
+ const now = Date.now();
231
+ const lastUpdated = new Date(result.updatedAt).getTime();
232
+ if ((now - lastUpdated) / (1000 * 60) > 10 && !queuedThreads.has(t)) {
233
+ queuedThreads.add(t);
234
+ logger(`ThreadID ${t} aready update data`, 'info');
235
+ queue.push(() => fetchThreadInfo(t, false));
236
+ }
237
+ }
238
+ } catch (err) {
239
+ console.error("checkAndUpdateThreads", err);
240
+ }
241
+ }
242
+ async function processQueue() {
243
+ if (isProcessingQueue) return;
244
+ isProcessingQueue = true;
245
+ while (queue.length > 0) {
246
+ const task = queue.shift();
247
+ try {
248
+ await task();
249
+ } catch (err) {
250
+ console.error("Queue processing error", err);
251
+ }
252
+ }
253
+ isProcessingQueue = false;
254
+ }
255
+ setInterval(() => {
256
+ checkAndUpdateThreads();
257
+ processQueue();
258
+ }, 10000);
259
+ return async function getThreadInfoGraphQL(threadID, callback) {
260
+ let resolveFunc = function() {};
261
+ let rejectFunc = function() {};
262
+ const returnPromise = new Promise(function(resolve, reject) {
263
+ resolveFunc = resolve;
264
+ rejectFunc = reject;
265
+ });
266
+ if (
267
+ utils.getType(callback) != "Function" &&
268
+ utils.getType(callback) != "AsyncFunction"
269
+ ) {
270
+ callback = function(err, data) {
271
+ if (err) {
272
+ return rejectFunc(err);
273
+ }
274
+ resolveFunc(data);
275
+ };
276
+ }
277
+ if (utils.getType(threadID) !== "Array") {
278
+ threadID = [threadID];
279
+ }
280
+ let result;
281
+ try {
282
+ result = await get(threadID[0]);
283
+ if (result) {
284
+ callback(null, result.data);
285
+ } else {
286
+ if (!processingThreads.has(threadID[0])) {
287
+ processingThreads.add(threadID[0]);
288
+ logger(`Created new thread data: ${threadID[0]}`, 'info');
289
+ const response = await getMultiInfo(threadID);
290
+ if (!response.Success) throw new Error("Failed to get thread info");
291
+ const data = response.Data[0];
292
+ await create(threadID[0], { data });
293
+ callback(null, data);
294
+ processingThreads.delete(threadID[0]);
295
+ }
296
+ }
297
+ } catch (err) {
298
+ callback(err);
299
+ }
300
+ return returnPromise;
301
+ };
302
+ };
303
+ */
304
+ "use strict";
305
+
306
+ const utils = require("../utils");
307
+ const log = require("npmlog");
308
+
309
+ function formatEventReminders(reminder) {
310
+ return {
311
+ reminderID: reminder.id,
312
+ eventCreatorID: reminder.lightweight_event_creator.id,
313
+ time: reminder.time,
314
+ eventType: reminder.lightweight_event_type.toLowerCase(),
315
+ locationName: reminder.location_name,
316
+ // @TODO verify this
317
+ locationCoordinates: reminder.location_coordinates,
318
+ locationPage: reminder.location_page,
319
+ eventStatus: reminder.lightweight_event_status.toLowerCase(),
320
+ note: reminder.note,
321
+ repeatMode: reminder.repeat_mode.toLowerCase(),
322
+ eventTitle: reminder.event_title,
323
+ triggerMessage: reminder.trigger_message,
324
+ secondsToNotifyBefore: reminder.seconds_to_notify_before,
325
+ allowsRsvp: reminder.allows_rsvp,
326
+ relatedEvent: reminder.related_event,
327
+ members: reminder.event_reminder_members.edges.map(function (member) {
328
+ return {
329
+ memberID: member.node.id,
330
+ state: member.guest_list_state.toLowerCase()
331
+ };
332
+ })
333
+ };
334
+ }
335
+
336
+ function formatThreadGraphQLResponse(data) {
337
+ if (data.errors)
338
+ return data.errors;
339
+ const messageThread = data.message_thread;
340
+ if (!messageThread)
341
+ return null;
342
+ const threadID = messageThread.thread_key.thread_fbid
343
+ ? messageThread.thread_key.thread_fbid
344
+ : messageThread.thread_key.other_user_id;
345
+
346
+ // Remove me
347
+ const lastM = messageThread.last_message;
348
+ const snippetID =
349
+ lastM &&
350
+ lastM.nodes &&
351
+ lastM.nodes[0] &&
352
+ lastM.nodes[0].message_sender &&
353
+ lastM.nodes[0].message_sender.messaging_actor
354
+ ? lastM.nodes[0].message_sender.messaging_actor.id
355
+ : null;
356
+ const snippetText =
357
+ lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
358
+ const lastR = messageThread.last_read_receipt;
359
+ const lastReadTimestamp =
360
+ lastR && lastR.nodes && lastR.nodes[0] && lastR.nodes[0].timestamp_precise
361
+ ? lastR.nodes[0].timestamp_precise
362
+ : null;
363
+
364
+ return {
365
+ threadID: threadID,
366
+ threadName: messageThread.name,
367
+ participantIDs: messageThread.all_participants.edges.map(d => d.node.messaging_actor.id),
368
+ userInfo: messageThread.all_participants.edges.map(d => ({
369
+ id: d.node.messaging_actor.id,
370
+ name: d.node.messaging_actor.name,
371
+ firstName: d.node.messaging_actor.short_name,
372
+ vanity: d.node.messaging_actor.username,
373
+ url: d.node.messaging_actor.url,
374
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
375
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
376
+ gender: d.node.messaging_actor.gender,
377
+ type: d.node.messaging_actor.__typename,
378
+ isFriend: d.node.messaging_actor.is_viewer_friend,
379
+ isBirthday: !!d.node.messaging_actor.is_birthday //not sure?
380
+ })),
381
+ unreadCount: messageThread.unread_count,
382
+ messageCount: messageThread.messages_count,
383
+ timestamp: messageThread.updated_time_precise,
384
+ muteUntil: messageThread.mute_until,
385
+ isGroup: messageThread.thread_type == "GROUP",
386
+ isSubscribed: messageThread.is_viewer_subscribed,
387
+ isArchived: messageThread.has_viewer_archived,
388
+ folder: messageThread.folder,
389
+ cannotReplyReason: messageThread.cannot_reply_reason,
390
+ eventReminders: messageThread.event_reminders
391
+ ? messageThread.event_reminders.nodes.map(formatEventReminders)
392
+ : null,
393
+ emoji: messageThread.customization_info
394
+ ? messageThread.customization_info.emoji
395
+ : null,
396
+ color:
397
+ messageThread.customization_info &&
398
+ messageThread.customization_info.outgoing_bubble_color
399
+ ? messageThread.customization_info.outgoing_bubble_color.slice(2)
400
+ : null,
401
+ threadTheme: messageThread.thread_theme,
402
+ nicknames:
403
+ messageThread.customization_info &&
404
+ messageThread.customization_info.participant_customizations
405
+ ? messageThread.customization_info.participant_customizations.reduce(
406
+ function (res, val) {
407
+ if (val.nickname) res[val.participant_id] = val.nickname;
408
+ return res;
409
+ },
410
+ {}
411
+ )
412
+ : {},
413
+ adminIDs: messageThread.thread_admins,
414
+ approvalMode: Boolean(messageThread.approval_mode),
415
+ approvalQueue: messageThread.group_approval_queue.nodes.map(a => ({
416
+ inviterID: a.inviter.id,
417
+ requesterID: a.requester.id,
418
+ timestamp: a.request_timestamp,
419
+ request_source: a.request_source // @Undocumented
420
+ })),
421
+
422
+ // @Undocumented
423
+ reactionsMuteMode: messageThread.reactions_mute_mode.toLowerCase(),
424
+ mentionsMuteMode: messageThread.mentions_mute_mode.toLowerCase(),
425
+ isPinProtected: messageThread.is_pin_protected,
426
+ relatedPageThread: messageThread.related_page_thread,
427
+
428
+ // @Legacy
429
+ name: messageThread.name,
430
+ snippet: snippetText,
431
+ snippetSender: snippetID,
432
+ snippetAttachments: [],
433
+ serverTimestamp: messageThread.updated_time_precise,
434
+ imageSrc: messageThread.image ? messageThread.image.uri : null,
435
+ isCanonicalUser: messageThread.is_canonical_neo_user,
436
+ isCanonical: messageThread.thread_type != "GROUP",
437
+ recipientsLoadable: true,
438
+ hasEmailParticipant: false,
439
+ readOnly: false,
440
+ canReply: messageThread.cannot_reply_reason == null,
441
+ lastMessageTimestamp: messageThread.last_message
442
+ ? messageThread.last_message.timestamp_precise
443
+ : null,
444
+ lastMessageType: "message",
445
+ lastReadTimestamp: lastReadTimestamp,
446
+ threadType: messageThread.thread_type == "GROUP" ? 2 : 1,
447
+
448
+ // update in Wed, 13 Jul 2022 19:41:12 +0700
449
+ inviteLink: {
450
+ enable: messageThread.joinable_mode ? messageThread.joinable_mode.mode == 1 : false,
451
+ link: messageThread.joinable_mode ? messageThread.joinable_mode.link : null
452
+ }
453
+ };
454
+ }
455
+
456
+ module.exports = function (defaultFuncs, api, ctx) {
457
+ return function getThreadInfoGraphQL(threadID, callback) {
458
+ let resolveFunc = function () { };
459
+ let rejectFunc = function () { };
460
+ const returnPromise = new Promise(function (resolve, reject) {
461
+ resolveFunc = resolve;
462
+ rejectFunc = reject;
463
+ });
464
+
465
+ if (utils.getType(callback) != "Function" && utils.getType(callback) != "AsyncFunction") {
466
+ callback = function (err, data) {
467
+ if (err) {
468
+ return rejectFunc(err);
469
+ }
470
+ resolveFunc(data);
471
+ };
472
+ }
473
+
474
+ if (utils.getType(threadID) !== "Array") {
475
+ threadID = [threadID];
476
+ }
477
+
478
+ let form = {};
479
+ // `queries` has to be a string. I couldn't tell from the dev console. This
480
+ // took me a really long time to figure out. I deserve a cookie for this.
481
+ threadID.map(function (t, i) {
482
+ form["o" + i] = {
483
+ doc_id: "3449967031715030",
484
+ query_params: {
485
+ id: t,
486
+ message_limit: 0,
487
+ load_messages: false,
488
+ load_read_receipts: false,
489
+ before: null
490
+ }
491
+ };
492
+ });
493
+
494
+ form = {
495
+ queries: JSON.stringify(form),
496
+ batch_name: "MessengerGraphQLThreadFetcher"
497
+ };
498
+
499
+ defaultFuncs
500
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
501
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
502
+ .then(function (resData) {
503
+
504
+ if (resData.error) {
505
+ throw resData;
506
+ }
507
+ // This returns us an array of things. The last one is the success /
508
+ // failure one.
509
+ // @TODO What do we do in this case?
510
+ // if (resData[resData.length - 1].error_results !== 0) {
511
+ // throw resData[0].o0.errors[0];
512
+ // }
513
+ // if (!resData[0].o0.data.message_thread) {
514
+ // throw new Error("can't find this thread");
515
+ // }
516
+ const threadInfos = {};
517
+ for (let i = resData.length - 2; i >= 0; i--) {
518
+ const threadInfo = formatThreadGraphQLResponse(resData[i][Object.keys(resData[i])[0]].data);
519
+ threadInfos[threadInfo?.threadID || threadID[threadID.length - 1 - i]] = threadInfo;
520
+ }
521
+ if (Object.values(threadInfos).length == 1) {
522
+ callback(null, Object.values(threadInfos)[0]);
523
+ }
524
+ else {
525
+ callback(null, threadInfos);
526
+ }
527
+ })
528
+ .catch(function (err) {
529
+ log.error("getThreadInfoGraphQL", err);
530
+ return callback(err);
531
+ });
532
+
533
+ return returnPromise;
534
+ };
535
+ };
@@ -0,0 +1,49 @@
1
+ // Nexus-FCA: Advanced and Safe Facebook Chat API
2
+ // getThreadInfoDeprecated.js - Deprecated thread info fetch
3
+
4
+ const utils = require("../utils");
5
+
6
+ module.exports = function(defaultFuncs, api, ctx) {
7
+ return function getThreadInfo(threadID, 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
+ const form = {
25
+ client: "mercury"
26
+ };
27
+
28
+ api.getUserInfo(threadID, function(err, userRes) {
29
+ if (err) {
30
+ return callback(err);
31
+ }
32
+ const key = Object.keys(userRes).length > 0 ? "user_ids" : "thread_fbids";
33
+ form["threads[" + key + "][0]"] = threadID;
34
+
35
+ if (ctx.globalOptions.pageId)
36
+ form.request_user_id = ctx.globalOptions.pageId;
37
+
38
+ defaultFuncs
39
+ .post(
40
+ "https://www.facebook.com/ajax/mercury/thread_info.php",
41
+ ctx.jar,
42
+ form
43
+ )
44
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
45
+ // ...existing code...
46
+ });
47
+ return returnPromise;
48
+ };
49
+ };