fca-zoebakaaa 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. package/.eslintrc.js +35 -0
  2. package/CHANGELOG.md +2 -0
  3. package/Controllers/Remote.js +4 -0
  4. package/DOCS.md +1738 -0
  5. package/Extra/ExtraAddons.js +80 -0
  6. package/Extra/ExtraFindUID.js +62 -0
  7. package/Extra/ExtraGetThread.js +118 -0
  8. package/Extra/ExtraTranslate.js +62 -0
  9. package/Extra/ExtraUptimeRobot.js +62 -0
  10. package/Extra/Html/Classic/script.js +233 -0
  11. package/Extra/Html/Classic/style.css +8 -0
  12. package/Extra/PM2/ecosystem.config.js +23 -0
  13. package/Extra/Security/Index.js +174 -0
  14. package/Extra/Security/Step_1.js +15 -0
  15. package/Extra/Security/Step_2.js +23 -0
  16. package/Extra/Security/Step_3.js +23 -0
  17. package/Extra/Src/History.js +117 -0
  18. package/Extra/Src/Last-Run.js +65 -0
  19. package/Extra/Src/Premium.js +88 -0
  20. package/Extra/Src/SecurityCheck.js +7 -0
  21. package/Extra/database/SyntheticDatabase.sqlite +0 -0
  22. package/Extra/database/index.js +360 -0
  23. package/Extra/logger.js +74 -0
  24. package/Func/AcceptAgreement.js +36 -0
  25. package/Func/ClearCache.js +68 -0
  26. package/Func/ReportV1.js +55 -0
  27. package/README.md +112 -0
  28. package/Settings/Database.js +21 -0
  29. package/Settings/Location.js +59 -0
  30. package/broadcast.js +40 -0
  31. package/index.js +1353 -0
  32. package/logger.js +75 -0
  33. package/package.json +56 -0
  34. package/src/Premium.js +39 -0
  35. package/src/addExternalModule.js +23 -0
  36. package/src/addUserToGroup.js +101 -0
  37. package/src/appstate.json +0 -0
  38. package/src/changeAdminStatus.js +95 -0
  39. package/src/changeArchivedStatus.js +47 -0
  40. package/src/changeAvt.js +95 -0
  41. package/src/changeBio.js +66 -0
  42. package/src/changeBlockedStatus.js +42 -0
  43. package/src/changeGroupImage.js +124 -0
  44. package/src/changeNickname.js +54 -0
  45. package/src/changeThreadColor.js +67 -0
  46. package/src/changeThreadEmoji.js +50 -0
  47. package/src/createNewGroup.js +78 -0
  48. package/src/createPoll.js +66 -0
  49. package/src/deleteMessage.js +51 -0
  50. package/src/deleteThread.js +49 -0
  51. package/src/forwardAttachment.js +53 -0
  52. package/src/getAccessToken.js +36 -0
  53. package/src/getCurrentUserID.js +8 -0
  54. package/src/getEmojiUrl.js +29 -0
  55. package/src/getFriendsList.js +81 -0
  56. package/src/getMessage.js +85 -0
  57. package/src/getThreadHistory.js +633 -0
  58. package/src/getThreadInfo.js +248 -0
  59. package/src/getThreadList.js +276 -0
  60. package/src/getThreadPictures.js +71 -0
  61. package/src/getUID.js +61 -0
  62. package/src/getUserID.js +62 -0
  63. package/src/getUserInfo.js +68 -0
  64. package/src/getUserInfoV2.js +32 -0
  65. package/src/getUserInfoV3.js +72 -0
  66. package/src/getUserInfoV4.js +57 -0
  67. package/src/getUserInfoV5.js +68 -0
  68. package/src/handleFriendRequest.js +47 -0
  69. package/src/handleMessageRequest.js +63 -0
  70. package/src/httpGet.js +54 -0
  71. package/src/httpPost.js +53 -0
  72. package/src/httpPostFormData.js +46 -0
  73. package/src/listenMqtt.js +1056 -0
  74. package/src/logout.js +73 -0
  75. package/src/markAsDelivered.js +54 -0
  76. package/src/markAsRead.js +82 -0
  77. package/src/markAsReadAll.js +47 -0
  78. package/src/markAsSeen.js +58 -0
  79. package/src/muteThread.js +51 -0
  80. package/src/removeUserFromGroup.js +72 -0
  81. package/src/resolvePhotoUrl.js +39 -0
  82. package/src/searchForThread.js +48 -0
  83. package/src/sendMessage.js +429 -0
  84. package/src/sendTypingIndicator.js +83 -0
  85. package/src/setMessageReaction.js +114 -0
  86. package/src/setPostReaction.js +103 -0
  87. package/src/setTitle.js +86 -0
  88. package/src/threadColors.js +40 -0
  89. package/src/toolspincoinmaster +89 -0
  90. package/src/unfriend.js +48 -0
  91. package/src/unsendMessage.js +40 -0
  92. package/utils.js +2244 -0
  93. package/zoebaka.code-workspace +8 -0
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+ function formatEventReminders(reminder) {
6
+ return {
7
+ reminderID: reminder.id,
8
+ eventCreatorID: reminder.lightweight_event_creator.id,
9
+ time: reminder.time,
10
+ eventType: reminder.lightweight_event_type.toLowerCase(),
11
+ locationName: reminder.location_name,
12
+ // @TODO verify this
13
+ locationCoordinates: reminder.location_coordinates,
14
+ locationPage: reminder.location_page,
15
+ eventStatus: reminder.lightweight_event_status.toLowerCase(),
16
+ note: reminder.note,
17
+ repeatMode: reminder.repeat_mode.toLowerCase(),
18
+ eventTitle: reminder.event_title,
19
+ triggerMessage: reminder.trigger_message,
20
+ secondsToNotifyBefore: reminder.seconds_to_notify_before,
21
+ allowsRsvp: reminder.allows_rsvp,
22
+ relatedEvent: reminder.related_event,
23
+ members: reminder.event_reminder_members.edges.map(function (member) {
24
+ return {
25
+ memberID: member.node.id,
26
+ state: member.guest_list_state.toLowerCase(),
27
+ };
28
+ }),
29
+ };
30
+ }
31
+
32
+ function formatThreadGraphQLResponse(data) {
33
+ try {
34
+ var messageThread = data.o0.data.message_thread;
35
+ } catch (err) {
36
+ console.error("GetThreadInfoGraphQL", "Can't get this thread info!");
37
+ return { err: err };
38
+ }
39
+ var threadID = messageThread.thread_key.thread_fbid
40
+ ? messageThread.thread_key.thread_fbid
41
+ : messageThread.thread_key.other_user_id;
42
+
43
+ // Remove me
44
+ var lastM = messageThread.last_message;
45
+ var 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
+ var snippetText =
54
+ lastM && lastM.nodes && lastM.nodes[0] ? lastM.nodes[0].snippet : null;
55
+ var lastR = messageThread.last_read_receipt;
56
+ var 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(
65
+ (d) => d.node.messaging_actor.id,
66
+ ),
67
+ userInfo: messageThread.all_participants.edges.map((d) => ({
68
+ id: d.node.messaging_actor.id,
69
+ name: d.node.messaging_actor.name,
70
+ firstName: d.node.messaging_actor.short_name,
71
+ vanity: d.node.messaging_actor.username,
72
+ thumbSrc: d.node.messaging_actor.big_image_src.uri,
73
+ profileUrl: d.node.messaging_actor.big_image_src.uri,
74
+ gender: d.node.messaging_actor.gender,
75
+ type: d.node.messaging_actor.__typename,
76
+ isFriend: d.node.messaging_actor.is_viewer_friend,
77
+ isBirthday: !!d.node.messaging_actor.is_birthday, //not sure?
78
+ })),
79
+ unreadCount: messageThread.unread_count,
80
+ messageCount: messageThread.messages_count,
81
+ timestamp: messageThread.updated_time_precise,
82
+ muteUntil: messageThread.mute_until,
83
+ isGroup: messageThread.thread_type == "GROUP",
84
+ isSubscribed: messageThread.is_viewer_subscribed,
85
+ isArchived: messageThread.has_viewer_archived,
86
+ folder: messageThread.folder,
87
+ cannotReplyReason: messageThread.cannot_reply_reason,
88
+ eventReminders: messageThread.event_reminders
89
+ ? messageThread.event_reminders.nodes.map(formatEventReminders)
90
+ : null,
91
+ emoji: messageThread.customization_info
92
+ ? messageThread.customization_info.emoji
93
+ : null,
94
+ color:
95
+ messageThread.customization_info &&
96
+ messageThread.customization_info.outgoing_bubble_color
97
+ ? messageThread.customization_info.outgoing_bubble_color.slice(2)
98
+ : null,
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
+ TimeCreate: Date.now(),
145
+ TimeUpdate: Date.now(),
146
+ };
147
+ }
148
+
149
+ module.exports = function (defaultFuncs, api, ctx) {
150
+ var {
151
+ createData,
152
+ getData,
153
+ hasData,
154
+ alreadyUpdate,
155
+ setLastRun,
156
+ updateData,
157
+ } = require("../Extra/ExtraGetThread");
158
+ var { capture } = require("../Extra/Src/Last-Run");
159
+
160
+ return function getThreadInfoGraphQL(threadID, callback) {
161
+ var resolveFunc = function () {};
162
+ var rejectFunc = function () {};
163
+ var returnPromise = new Promise(function (resolve, reject) {
164
+ resolveFunc = resolve;
165
+ rejectFunc = reject;
166
+ });
167
+
168
+ if (
169
+ utils.getType(callback) != "Function" &&
170
+ utils.getType(callback) != "AsyncFunction"
171
+ ) {
172
+ callback = function (err, data) {
173
+ if (err) {
174
+ return rejectFunc(err);
175
+ }
176
+ resolveFunc(data);
177
+ };
178
+ }
179
+ var form = {
180
+ queries: JSON.stringify({
181
+ o0: {
182
+ doc_id: "3449967031715030",
183
+ query_params: {
184
+ id: threadID,
185
+ message_limit: 0,
186
+ load_messages: false,
187
+ load_read_receipts: false,
188
+ before: null,
189
+ },
190
+ },
191
+ }),
192
+ batch_name: "MessengerGraphQLThreadFetcher",
193
+ };
194
+ var getDatathread = function (type, lastData) {
195
+ defaultFuncs
196
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
197
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
198
+ .then(function (resData) {
199
+ if (resData.error) {
200
+ return callback(null, lastData);
201
+ }
202
+ if (resData[resData.length - 1].error_results !== 0) {
203
+ return callback(null, lastData);
204
+ }
205
+ let data = formatThreadGraphQLResponse(resData[0]);
206
+ type(threadID, data);
207
+ callback(null, data);
208
+ capture(callback);
209
+ setLastRun("LastUpdate", callback);
210
+ })
211
+ .catch(function (err) {
212
+ log.error(
213
+ "getThreadInfoGraphQL",
214
+ "Lỗi: getThreadInfoGraphQL Có Thể Do Bạn Spam Quá Nhiều, Hãy Thử Lại !",
215
+ );
216
+ return callback(err);
217
+ });
218
+ };
219
+ switch (hasData(threadID)) {
220
+ case true:
221
+ {
222
+ try {
223
+ getData(threadID).TimeUpdate;
224
+ switch (alreadyUpdate(threadID)) {
225
+ case true:
226
+ {
227
+ getDatathread(updateData, getData(threadID));
228
+ }
229
+ break;
230
+ case false:
231
+ {
232
+ let x = getData(threadID);
233
+ callback(null, x);
234
+ }
235
+ break;
236
+ }
237
+ } catch (_) {
238
+ getDatathread(createData, getData(threadID));
239
+ }
240
+ }
241
+ break;
242
+ case false: {
243
+ getDatathread(createData, getData(threadID));
244
+ }
245
+ }
246
+ return returnPromise;
247
+ };
248
+ };
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const log = require("npmlog");
5
+
6
+ function createProfileUrl(url, username, id) {
7
+ if (url) return url;
8
+ return (
9
+ "https://www.facebook.com/" + (username || utils.formatID(id.toString()))
10
+ );
11
+ }
12
+
13
+ function formatParticipants(participants) {
14
+ return participants.edges.map((p) => {
15
+ p = p.node.messaging_actor;
16
+ switch (p["__typename"]) {
17
+ case "User":
18
+ return {
19
+ accountType: p["__typename"],
20
+ userID: utils.formatID(p.id.toString()), // do we need .toString()? when it is not a string?
21
+ name: p.name,
22
+ shortName: p.short_name,
23
+ gender: p.gender,
24
+ url: p.url, // how about making it profileURL
25
+ profilePicture: p.big_image_src.uri,
26
+ username: p.username || null,
27
+ // TODO: maybe better names for these?
28
+ isViewerFriend: p.is_viewer_friend, // true/false
29
+ isMessengerUser: p.is_messenger_user, // true/false
30
+ isVerified: p.is_verified, // true/false
31
+ isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
32
+ isViewerCoworker: p.is_viewer_coworker, // true/false
33
+ isEmployee: p.is_employee, // null? when it is something other? can someone check?
34
+ };
35
+ case "Page":
36
+ return {
37
+ accountType: p["__typename"],
38
+ userID: utils.formatID(p.id.toString()), // or maybe... pageID?
39
+ name: p.name,
40
+ url: p.url,
41
+ profilePicture: p.big_image_src.uri,
42
+ username: p.username || null,
43
+ // uhm... better names maybe?
44
+ acceptsMessengerUserFeedback: p.accepts_messenger_user_feedback, // true/false
45
+ isMessengerUser: p.is_messenger_user, // true/false
46
+ isVerified: p.is_verified, // true/false
47
+ isMessengerPlatformBot: p.is_messenger_platform_bot, // true/false
48
+ isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
49
+ };
50
+ case "ReducedMessagingActor":
51
+ case "UnavailableMessagingActor":
52
+ return {
53
+ accountType: p["__typename"],
54
+ userID: utils.formatID(p.id.toString()),
55
+ name: p.name,
56
+ url: createProfileUrl(p.url, p.username, p.id), // in this case p.url is null all the time
57
+ profilePicture: p.big_image_src.uri, // in this case it is default facebook photo, we could determine gender using it
58
+ username: p.username || null, // maybe we could use it to generate profile URL?
59
+ isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
60
+ };
61
+ default:
62
+ log.warn(
63
+ "getThreadList",
64
+ "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues\n" +
65
+ JSON.stringify(p, null, 2),
66
+ );
67
+ return {
68
+ accountType: p["__typename"],
69
+ userID: utils.formatID(p.id.toString()),
70
+ name: p.name || `[unknown ${p["__typename"]}]`, // probably it will always be something... but fallback to [unknown], just in case
71
+ };
72
+ }
73
+ });
74
+ }
75
+
76
+ // "FF8C0077" -> "8C0077"
77
+ function formatColor(color) {
78
+ if (color && color.match(/^(?:[0-9a-fA-F]{8})$/g)) return color.slice(2);
79
+ return color;
80
+ }
81
+
82
+ function getThreadName(t) {
83
+ if (t.name || t.thread_key.thread_fbid) return t.name;
84
+
85
+ for (let po of t.all_participants.edges) {
86
+ let p = po.node;
87
+ if (p.messaging_actor.id === t.thread_key.other_user_id)
88
+ return p.messaging_actor.name;
89
+ }
90
+ }
91
+
92
+ function mapNicknames(customizationInfo) {
93
+ return customizationInfo && customizationInfo.participant_customizations
94
+ ? customizationInfo.participant_customizations.map((u) => {
95
+ return {
96
+ userID: u.participant_id,
97
+ nickname: u.nickname,
98
+ };
99
+ })
100
+ : [];
101
+ }
102
+
103
+ function formatThreadList(data) {
104
+ return data.map((t) => {
105
+ let lastMessageNode =
106
+ t.last_message && t.last_message.nodes && t.last_message.nodes.length > 0
107
+ ? t.last_message.nodes[0]
108
+ : null;
109
+ return {
110
+ threadID: t.thread_key
111
+ ? utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id)
112
+ : null, // shall never be null
113
+ name: getThreadName(t),
114
+ unreadCount: t.unread_count,
115
+ messageCount: t.messages_count,
116
+ imageSrc: t.image ? t.image.uri : null,
117
+ emoji: t.customization_info ? t.customization_info.emoji : null,
118
+ color: formatColor(
119
+ t.customization_info
120
+ ? t.customization_info.outgoing_bubble_color
121
+ : null,
122
+ ),
123
+ nicknames: mapNicknames(t.customization_info),
124
+ muteUntil: t.mute_until,
125
+ participants: formatParticipants(t.all_participants),
126
+ adminIDs: t.thread_admins.map((a) => a.id),
127
+ folder: t.folder,
128
+ isGroup: t.thread_type === "GROUP",
129
+ // rtc_call_data: t.rtc_call_data, // TODO: format and document this
130
+ // isPinProtected: t.is_pin_protected, // feature from future? always false (2018-04-04)
131
+ customizationEnabled: t.customization_enabled, // false for ONE_TO_ONE with Page or ReducedMessagingActor
132
+ participantAddMode: t.participant_add_mode_as_string, // "ADD" if "GROUP" and null if "ONE_TO_ONE"
133
+ montageThread: t.montage_thread
134
+ ? Buffer.from(t.montage_thread.id, "base64").toString()
135
+ : null, // base64 encoded string "message_thread:0000000000000000"
136
+ // it is not userID nor any other ID known to me...
137
+ // can somebody inspect it? where is it used?
138
+ // probably Messenger Day uses it
139
+ reactionsMuteMode: t.reactions_mute_mode,
140
+ mentionsMuteMode: t.mentions_mute_mode,
141
+ isArchived: t.has_viewer_archived,
142
+ isSubscribed: t.is_viewer_subscribed,
143
+ timestamp: t.updated_time_precise, // in miliseconds
144
+ // isCanonicalUser: t.is_canonical_neo_user, // is it always false?
145
+ // TODO: how about putting snippet in another object? current implementation does not handle every possibile message type etc.
146
+ snippet: lastMessageNode ? lastMessageNode.snippet : null,
147
+ snippetAttachments: lastMessageNode
148
+ ? lastMessageNode.extensible_attachment
149
+ : null, // TODO: not sure if it works
150
+ snippetSender: lastMessageNode
151
+ ? utils.formatID(
152
+ (
153
+ lastMessageNode.message_sender.messaging_actor.id || ""
154
+ ).toString(),
155
+ )
156
+ : null,
157
+ lastMessageTimestamp: lastMessageNode
158
+ ? lastMessageNode.timestamp_precise
159
+ : null, // timestamp in miliseconds
160
+ lastReadTimestamp:
161
+ t.last_read_receipt && t.last_read_receipt.nodes.length > 0
162
+ ? t.last_read_receipt.nodes[0]
163
+ ? t.last_read_receipt.nodes[0].timestamp_precise
164
+ : null
165
+ : null, // timestamp in miliseconds
166
+ cannotReplyReason: t.cannot_reply_reason, // TODO: inspect possible values
167
+ approvalMode: Boolean(t.approval_mode),
168
+
169
+ // @Legacy
170
+ participantIDs: formatParticipants(t.all_participants).map(
171
+ (participant) => participant.userID,
172
+ ),
173
+ threadType: t.thread_type === "GROUP" ? 2 : 1, // "GROUP" or "ONE_TO_ONE"
174
+ };
175
+ });
176
+ }
177
+
178
+ module.exports = function (defaultFuncs, api, ctx) {
179
+ return function getThreadList(limit, timestamp, tags, callback) {
180
+ if (
181
+ !callback &&
182
+ (utils.getType(tags) === "Function" ||
183
+ utils.getType(tags) === "AsyncFunction")
184
+ ) {
185
+ callback = tags;
186
+ tags = [""];
187
+ }
188
+ if (
189
+ utils.getType(limit) !== "Number" ||
190
+ !Number.isInteger(limit) ||
191
+ limit <= 0
192
+ )
193
+ throw { error: "getThreadList: limit must be a positive integer" };
194
+
195
+ if (
196
+ utils.getType(timestamp) !== "Null" &&
197
+ (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))
198
+ )
199
+ throw { error: "getThreadList: timestamp must be an integer or null" };
200
+
201
+ if (utils.getType(tags) === "String") tags = [tags];
202
+ if (utils.getType(tags) !== "Array")
203
+ throw { error: "getThreadList: tags must be an array" };
204
+
205
+ var resolveFunc = function () {};
206
+ var rejectFunc = function () {};
207
+ var returnPromise = new Promise(function (resolve, reject) {
208
+ resolveFunc = resolve;
209
+ rejectFunc = reject;
210
+ });
211
+
212
+ if (
213
+ utils.getType(callback) !== "Function" &&
214
+ utils.getType(callback) !== "AsyncFunction"
215
+ ) {
216
+ callback = function (err, data) {
217
+ if (err) return rejectFunc(err);
218
+ resolveFunc(data);
219
+ };
220
+ }
221
+
222
+ const form = {
223
+ av: ctx.globalOptions.pageID,
224
+ queries: JSON.stringify({
225
+ o0: {
226
+ // This doc_id was valid on 2020-07-20
227
+ doc_id: "3336396659757871",
228
+ query_params: {
229
+ limit: limit + (timestamp ? 1 : 0),
230
+ before: timestamp,
231
+ tags: tags,
232
+ includeDeliveryReceipts: true,
233
+ includeSeqID: false,
234
+ },
235
+ },
236
+ }),
237
+ batch_name: "MessengerGraphQLThreadlistFetcher",
238
+ };
239
+
240
+ defaultFuncs
241
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
242
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
243
+ .then((resData) => {
244
+ if (resData[resData.length - 1].error_results > 0)
245
+ throw resData[0].o0.errors;
246
+
247
+ if (resData[resData.length - 1].successful_results === 0)
248
+ throw {
249
+ error: "getThreadList: there was no successful_results",
250
+ res: resData,
251
+ };
252
+
253
+ // When we ask for threads using timestamp from the previous request,
254
+ // we are getting the last thread repeated as the first thread in this response.
255
+ // .shift() gets rid of it
256
+ // It is also the reason for increasing limit by 1 when timestamp is set
257
+ // this way user asks for 10 threads, we are asking for 11,
258
+ // but after removing the duplicated one, it is again 10
259
+ if (timestamp) resData[0].o0.data.viewer.message_threads.nodes.shift();
260
+
261
+ callback(
262
+ null,
263
+ formatThreadList(resData[0].o0.data.viewer.message_threads.nodes),
264
+ );
265
+ })
266
+ .catch((err) => {
267
+ log.error(
268
+ "getThreadList",
269
+ "Lỗi: getThreadList Có Thể Do Bạn Spam Quá Nhiều, Hãy Thử Lại !",
270
+ );
271
+ return callback(err);
272
+ });
273
+
274
+ return returnPromise;
275
+ };
276
+ };
@@ -0,0 +1,71 @@
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(
30
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
31
+ ctx.jar,
32
+ form,
33
+ )
34
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
35
+ .then(function (resData) {
36
+ if (resData.error) throw resData;
37
+ return Promise.all(
38
+ resData.payload.imagesData.map(function (image) {
39
+ form = {
40
+ thread_id: threadID,
41
+ image_id: image.fbid,
42
+ };
43
+ return defaultFuncs
44
+ .post(
45
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
46
+ ctx.jar,
47
+ form,
48
+ )
49
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
50
+ .then(function (resData) {
51
+ if (resData.error) throw resData;
52
+ // the response is pretty messy
53
+ var queryThreadID =
54
+ resData.jsmods.require[0][3][1].query_metadata.query_path[0]
55
+ .message_thread;
56
+ var imageData =
57
+ resData.jsmods.require[0][3][1].query_results[queryThreadID]
58
+ .message_images.edges[0].node.image2;
59
+ return imageData;
60
+ });
61
+ }),
62
+ );
63
+ })
64
+ .then((resData) => callback(null, resData))
65
+ .catch(function (err) {
66
+ log.error("Error in getThreadPictures", err);
67
+ callback(err);
68
+ });
69
+ return returnPromise;
70
+ };
71
+ };
package/src/getUID.js ADDED
@@ -0,0 +1,61 @@
1
+ /* eslint-disable linebreak-style */
2
+ "use strict";
3
+
4
+ module.exports = function (_defaultFuncs, api, _ctx) {
5
+ return function getUID(link, callback) {
6
+ var resolveFunc = function () {};
7
+ var rejectFunc = function () {};
8
+ var returnPromise = new Promise(function (resolve, reject) {
9
+ resolveFunc = resolve;
10
+ rejectFunc = reject;
11
+ });
12
+
13
+ if (!callback) {
14
+ callback = function (err, uid) {
15
+ if (err) return rejectFunc(err);
16
+ resolveFunc(uid);
17
+ };
18
+ }
19
+
20
+ try {
21
+ var Link = String(link);
22
+ var FindUID = require("../Extra/ExtraFindUID");
23
+ if (
24
+ Link.includes("facebook.com") ||
25
+ Link.includes("Facebook.com") ||
26
+ Link.includes("fb")
27
+ ) {
28
+ var LinkSplit = Link.split("/");
29
+ if (LinkSplit.indexOf("https:") == 0) {
30
+ if (!isNaN(LinkSplit[3])) {
31
+ api.sendMessage(
32
+ "Sai Link, Link Cần Có Định Dạng Như Sau: facebook.com/Lazic.Kanzu",
33
+ globalThis.Fca.Data.event.threadID,
34
+ globalThis.Fca.Data.event.messageID,
35
+ );
36
+ callback(null, String(4));
37
+ } else {
38
+ FindUID(Link, api).then(function (data) {
39
+ callback(null, data);
40
+ });
41
+ }
42
+ } else {
43
+ var Form = `https://www.facebook.com/${LinkSplit[1]}`;
44
+ FindUID(Form, api).then(function (data) {
45
+ callback(null, data);
46
+ });
47
+ }
48
+ } else {
49
+ callback(null, null);
50
+ api.sendMessage(
51
+ "Sai Link, Link Cần Là Link Của Facebook",
52
+ globalThis.Fca.Data.event.threadID,
53
+ globalThis.Fca.Data.event.messageID,
54
+ );
55
+ }
56
+ } catch (e) {
57
+ return callback(null, e);
58
+ }
59
+ return returnPromise;
60
+ };
61
+ };