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