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.
- package/CHANGELOG.md +1 -0
- package/DOCS.md +2047 -0
- package/Fca_Database/database.sqlite +0 -0
- package/LICENSE-MIT +21 -0
- package/README.md +240 -0
- package/docs/README.md +9 -0
- package/docs/addExternalModule.md +15 -0
- package/docs/addUserToGroup.md +20 -0
- package/docs/changeAdminStatus.md +21 -0
- package/docs/changeArchivedStatus.md +19 -0
- package/docs/changeAvatar.md +21 -0
- package/docs/changeAvatarV2.md +18 -0
- package/docs/changeBio.md +15 -0
- package/docs/changeBlockedStatus.md +16 -0
- package/docs/changeBlockedStatusMqtt.md +16 -0
- package/docs/changeCover.md +15 -0
- package/docs/changeGroupImage.md +16 -0
- package/docs/changeName.md +18 -0
- package/docs/changeNickname.md +17 -0
- package/docs/changeThreadColor.md +16 -0
- package/docs/changeThreadEmoji.md +16 -0
- package/docs/changeUsername.md +15 -0
- package/docs/createCommentPost.md +16 -0
- package/docs/createNewGroup.md +16 -0
- package/docs/createPoll.md +17 -0
- package/docs/createPost.md +15 -0
- package/docs/deleteMessage.md +15 -0
- package/docs/deleteThread.md +15 -0
- package/docs/editMessage.md +16 -0
- package/docs/follow.md +15 -0
- package/docs/forwardAttachment.md +16 -0
- package/docs/getAccess.md +17 -0
- package/docs/getAvatarUser.md +15 -0
- package/docs/getBotInitialData.md +14 -0
- package/docs/getCtx.md +14 -0
- package/docs/getCurrentUserID.md +12 -0
- package/docs/getEmojiUrl.md +17 -0
- package/docs/getFriendsList.md +14 -0
- package/docs/getMessage.md +15 -0
- package/docs/getOptions.md +14 -0
- package/docs/getRegion.md +14 -0
- package/docs/getThreadHistory.md +17 -0
- package/docs/getThreadHistoryDeprecated.md +17 -0
- package/docs/getThreadInfo.md +15 -0
- package/docs/getThreadInfoDeprecated.md +17 -0
- package/docs/getThreadList.md +17 -0
- package/docs/getThreadListDeprecated.md +17 -0
- package/docs/getThreadPictures.md +17 -0
- package/docs/getUID.md +15 -0
- package/docs/getUserID.md +15 -0
- package/docs/getUserInfo.md +15 -0
- package/docs/handleFriendRequest.md +16 -0
- package/docs/handleMessageRequest.md +16 -0
- package/docs/httpGet.md +15 -0
- package/docs/httpPost.md +16 -0
- package/docs/httpPostFormData.md +16 -0
- package/docs/listenMqtt.md +17 -0
- package/docs/listenNotification.md +14 -0
- package/docs/logout.md +14 -0
- package/docs/markAsDelivered.md +16 -0
- package/docs/markAsRead.md +16 -0
- package/docs/markAsReadAll.md +14 -0
- package/docs/markAsSeen.md +15 -0
- package/docs/muteThread.md +16 -0
- package/docs/pinMessage.md +17 -0
- package/docs/postFormData.md +16 -0
- package/docs/refreshFb_dtsg.md +14 -0
- package/docs/removeUserFromGroup.md +16 -0
- package/docs/resolvePhotoUrl.md +15 -0
- package/docs/searchForThread.md +15 -0
- package/docs/searchStickers.md +15 -0
- package/docs/sendComment.md +16 -0
- package/docs/sendMessage.md +16 -0
- package/docs/sendMessageMqtt.md +16 -0
- package/docs/sendTypingIndicator.md +15 -0
- package/docs/setMessageReaction.md +17 -0
- package/docs/setMessageReactionMqtt.md +16 -0
- package/docs/setPostReaction.md +16 -0
- package/docs/setProfileGuard.md +15 -0
- package/docs/setStoryReaction.md +16 -0
- package/docs/setTitle.md +16 -0
- package/docs/shareContact.md +16 -0
- package/docs/shareLink.md +16 -0
- package/docs/stopListenMqtt.md +8 -0
- package/docs/threadColors.md +11 -0
- package/docs/unfriend.md +15 -0
- package/docs/unsendMessage.md +15 -0
- package/docs/uploadAttachment.md +15 -0
- package/fca-config.json +7 -0
- package/index.d.ts +618 -0
- package/index.js +361 -0
- package/lib/database/models/index.js +47 -0
- package/lib/database/models/thread.js +31 -0
- package/lib/database/threadData.js +93 -0
- package/lib/logger.js +24 -0
- package/lib/login.js +0 -0
- package/package.json +90 -0
- package/src/addExternalModule.js +19 -0
- package/src/addUserToGroup.js +113 -0
- package/src/changeAdminStatus.js +79 -0
- package/src/changeArchivedStatus.js +55 -0
- package/src/changeAvatar.js +126 -0
- package/src/changeAvatarV2.js +77 -0
- package/src/changeBio.js +77 -0
- package/src/changeBlockedStatus.js +47 -0
- package/src/changeBlockedStatusMqtt.js +71 -0
- package/src/changeCover.js +72 -0
- package/src/changeGroupImage.js +132 -0
- package/src/changeName.js +79 -0
- package/src/changeNickname.js +59 -0
- package/src/changeThreadColor.js +65 -0
- package/src/changeThreadEmoji.js +55 -0
- package/src/changeUsername.js +58 -0
- package/src/createCommentPost.js +225 -0
- package/src/createNewGroup.js +86 -0
- package/src/createPoll.js +71 -0
- package/src/createPost.js +276 -0
- package/src/deleteMessage.js +56 -0
- package/src/deleteThread.js +56 -0
- package/src/editMessage.js +57 -0
- package/src/follow.js +54 -0
- package/src/forwardAttachment.js +60 -0
- package/src/getAccess.js +67 -0
- package/src/getAvatarUser.js +56 -0
- package/src/getBotInitialData.js +37 -0
- package/src/getCtx.js +6 -0
- package/src/getCurrentUserID.js +7 -0
- package/src/getEmojiUrl.js +29 -0
- package/src/getFriendsList.js +83 -0
- package/src/getMessage.js +796 -0
- package/src/getOptions.js +6 -0
- package/src/getRegion.js +8 -0
- package/src/getThreadHistory.js +666 -0
- package/src/getThreadHistoryDeprecated.js +55 -0
- package/src/getThreadInfo.js +535 -0
- package/src/getThreadInfoDeprecated.js +49 -0
- package/src/getThreadList.js +192 -0
- package/src/getThreadListDeprecated.js +54 -0
- package/src/getThreadPictures.js +79 -0
- package/src/getUID.js +67 -0
- package/src/getUserID.js +66 -0
- package/src/getUserInfo.js +80 -0
- package/src/handleFriendRequest.js +61 -0
- package/src/handleMessageRequest.js +65 -0
- package/src/httpGet.js +57 -0
- package/src/httpPost.js +57 -0
- package/src/httpPostFormData.js +63 -0
- package/src/listenMqtt.js +1039 -0
- package/src/listenNotification.js +65 -0
- package/src/logout.js +75 -0
- package/src/markAsDelivered.js +58 -0
- package/src/markAsRead.js +80 -0
- package/src/markAsReadAll.js +50 -0
- package/src/markAsSeen.js +59 -0
- package/src/muteThread.js +52 -0
- package/src/pinMessage.js +59 -0
- package/src/postFormData.js +46 -0
- package/src/refreshFb_dtsg.js +66 -0
- package/src/removeUserFromGroup.js +79 -0
- package/src/resolvePhotoUrl.js +45 -0
- package/src/searchForThread.js +53 -0
- package/src/searchStickers.js +51 -0
- package/src/sendComment.js +63 -0
- package/src/sendMessage.js +328 -0
- package/src/sendMessageMqtt.js +316 -0
- package/src/sendTypingIndicator.js +103 -0
- package/src/setMessageReaction.js +119 -0
- package/src/setMessageReactionMqtt.js +61 -0
- package/src/setPostReaction.js +109 -0
- package/src/setProfileGuard.js +45 -0
- package/src/setStoryReaction.js +62 -0
- package/src/setTitle.js +86 -0
- package/src/shareContact.js +49 -0
- package/src/shareLink.js +59 -0
- package/src/stopListenMqtt.js +21 -0
- package/src/threadColors.js +131 -0
- package/src/unfriend.js +52 -0
- package/src/unsendMessage.js +49 -0
- package/src/uploadAttachment.js +95 -0
- package/utils.js +1432 -0
|
@@ -0,0 +1,192 @@
|
|
|
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 "https://www.facebook.com/" + (username || utils.formatID(id.toString()));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function formatParticipants(participants) {
|
|
12
|
+
return participants.edges.map((p) => {
|
|
13
|
+
p = p.node.messaging_actor;
|
|
14
|
+
switch (p["__typename"]) {
|
|
15
|
+
case "User":
|
|
16
|
+
return {
|
|
17
|
+
accountType: p["__typename"],
|
|
18
|
+
userID: utils.formatID(p.id.toString()), // do we need .toString()? when it is not a string?
|
|
19
|
+
name: p.name,
|
|
20
|
+
shortName: p.short_name,
|
|
21
|
+
gender: p.gender,
|
|
22
|
+
url: p.url, // how about making it profileURL
|
|
23
|
+
profilePicture: p.big_image_src.uri,
|
|
24
|
+
username: (p.username || null),
|
|
25
|
+
// TODO: maybe better names for these?
|
|
26
|
+
isViewerFriend: p.is_viewer_friend, // true/false
|
|
27
|
+
isMessengerUser: p.is_messenger_user, // true/false
|
|
28
|
+
isVerified: p.is_verified, // true/false
|
|
29
|
+
isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
|
|
30
|
+
isViewerCoworker: p.is_viewer_coworker, // true/false
|
|
31
|
+
isEmployee: p.is_employee // null? when it is something other? can someone check?
|
|
32
|
+
};
|
|
33
|
+
case "Page":
|
|
34
|
+
return {
|
|
35
|
+
accountType: p["__typename"],
|
|
36
|
+
userID: utils.formatID(p.id.toString()), // or maybe... pageID?
|
|
37
|
+
name: p.name,
|
|
38
|
+
url: p.url,
|
|
39
|
+
profilePicture: p.big_image_src.uri,
|
|
40
|
+
username: (p.username || null),
|
|
41
|
+
// uhm... better names maybe?
|
|
42
|
+
acceptsMessengerUserFeedback: p.accepts_messenger_user_feedback, // true/false
|
|
43
|
+
isMessengerUser: p.is_messenger_user, // true/false
|
|
44
|
+
isVerified: p.is_verified, // true/false
|
|
45
|
+
isMessengerPlatformBot: p.is_messenger_platform_bot, // true/false
|
|
46
|
+
isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
|
|
47
|
+
};
|
|
48
|
+
case "ReducedMessagingActor":
|
|
49
|
+
case "UnavailableMessagingActor":
|
|
50
|
+
return {
|
|
51
|
+
accountType: p["__typename"],
|
|
52
|
+
userID: utils.formatID(p.id.toString()),
|
|
53
|
+
name: p.name,
|
|
54
|
+
url: createProfileUrl(p.url, p.username, p.id), // in this case p.url is null all the time
|
|
55
|
+
profilePicture: p.big_image_src.uri, // in this case it is default facebook photo, we could determine gender using it
|
|
56
|
+
username: (p.username || null), // maybe we could use it to generate profile URL?
|
|
57
|
+
isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
|
|
58
|
+
};
|
|
59
|
+
default:
|
|
60
|
+
log.warn("getThreadList", "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues\n" + JSON.stringify(p, null, 2));
|
|
61
|
+
return {
|
|
62
|
+
accountType: p["__typename"],
|
|
63
|
+
userID: utils.formatID(p.id.toString()),
|
|
64
|
+
name: p.name || `[unknown ${p["__typename"]}]`, // probably it will always be something... but fallback to [unknown], just in case
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// "FF8C0077" -> "8C0077"
|
|
71
|
+
function formatColor(color) {
|
|
72
|
+
if (color && color.match(/^(?:[0-9a-fA-F]{8})$/g)) return color.slice(2);
|
|
73
|
+
return color;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getThreadName(t) {
|
|
77
|
+
if (t.name || t.thread_key.thread_fbid) return t.name;
|
|
78
|
+
|
|
79
|
+
for (let po of t.all_participants.edges) {
|
|
80
|
+
let p = po.node;
|
|
81
|
+
if (p.messaging_actor.id === t.thread_key.other_user_id) return p.messaging_actor.name;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function mapNicknames(customizationInfo) {
|
|
86
|
+
return (customizationInfo && customizationInfo.participant_customizations) ? customizationInfo.participant_customizations.map(u => {
|
|
87
|
+
return {
|
|
88
|
+
"userID": u.participant_id,
|
|
89
|
+
"nickname": u.nickname
|
|
90
|
+
};
|
|
91
|
+
}) : [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatThreadList(data) {
|
|
95
|
+
return data.map(t => {
|
|
96
|
+
let lastMessageNode = (t.last_message && t.last_message.nodes && t.last_message.nodes.length > 0) ? t.last_message.nodes[0] : null;
|
|
97
|
+
return {
|
|
98
|
+
threadID: t.thread_key ? utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id) : null, // shall never be null
|
|
99
|
+
name: getThreadName(t),
|
|
100
|
+
unreadCount: t.unread_count,
|
|
101
|
+
messageCount: t.messages_count,
|
|
102
|
+
imageSrc: t.image ? t.image.uri : null,
|
|
103
|
+
emoji: t.customization_info ? t.customization_info.emoji : null,
|
|
104
|
+
color: formatColor(t.customization_info ? t.customization_info.outgoing_bubble_color : null),
|
|
105
|
+
threadTheme: t.thread_theme,
|
|
106
|
+
nicknames: mapNicknames(t.customization_info),
|
|
107
|
+
muteUntil: t.mute_until,
|
|
108
|
+
participants: formatParticipants(t.all_participants),
|
|
109
|
+
adminIDs: t.thread_admins.map(a => a.id),
|
|
110
|
+
folder: t.folder,
|
|
111
|
+
isGroup: t.thread_type === "GROUP",
|
|
112
|
+
customizationEnabled: t.customization_enabled, // false for ONE_TO_ONE with Page or ReducedMessagingActor
|
|
113
|
+
participantAddMode: t.participant_add_mode_as_string, // "ADD" if "GROUP" and null if "ONE_TO_ONE"
|
|
114
|
+
montageThread: t.montage_thread ? Buffer.from(t.montage_thread.id, "base64").toString() : null, // base64 encoded string "message_thread:0000000000000000"
|
|
115
|
+
reactionsMuteMode: t.reactions_mute_mode,
|
|
116
|
+
mentionsMuteMode: t.mentions_mute_mode,
|
|
117
|
+
isArchived: t.has_viewer_archived,
|
|
118
|
+
isSubscribed: t.is_viewer_subscribed,
|
|
119
|
+
timestamp: t.updated_time_precise, // in miliseconds
|
|
120
|
+
snippet: lastMessageNode ? lastMessageNode.snippet : null,
|
|
121
|
+
snippetAttachments: lastMessageNode ? lastMessageNode.extensible_attachment : null, // TODO: not sure if it works
|
|
122
|
+
snippetSender: lastMessageNode ? utils.formatID((lastMessageNode.message_sender.messaging_actor.id || "").toString()) : null,
|
|
123
|
+
lastMessageTimestamp: lastMessageNode ? lastMessageNode.timestamp_precise : null, // timestamp in miliseconds
|
|
124
|
+
lastReadTimestamp: (t.last_read_receipt && t.last_read_receipt.nodes.length > 0)
|
|
125
|
+
? (t.last_read_receipt.nodes[0] ? t.last_read_receipt.nodes[0].timestamp_precise : null)
|
|
126
|
+
: null,
|
|
127
|
+
cannotReplyReason: t.cannot_reply_reason,
|
|
128
|
+
approvalMode: Boolean(t.approval_mode),
|
|
129
|
+
participantIDs: formatParticipants(t.all_participants).map(participant => participant.userID),
|
|
130
|
+
threadType: t.thread_type === "GROUP" ? 2 : 1, // "GROUP" or "ONE_TO_ONE"
|
|
131
|
+
inviteLink: {
|
|
132
|
+
enable: t.joinable_mode ? t.joinable_mode.mode == 1 : false,
|
|
133
|
+
link: t.joinable_mode ? t.joinable_mode.link : null
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
140
|
+
return function getThreadList(limit, timestamp, tags, callback) {
|
|
141
|
+
if (!callback && (utils.getType(tags) === "Function" || utils.getType(tags) === "AsyncFunction")) {
|
|
142
|
+
callback = tags;
|
|
143
|
+
tags = [""];
|
|
144
|
+
}
|
|
145
|
+
if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) throw { error: "getThreadList: limit must be a positive integer" };
|
|
146
|
+
if (utils.getType(timestamp) !== "Null" && (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) throw { error: "getThreadList: timestamp must be an integer or null" };
|
|
147
|
+
if (utils.getType(tags) === "String") tags = [tags];
|
|
148
|
+
if (utils.getType(tags) !== "Array") throw { error: "getThreadList: tags must be an array" };
|
|
149
|
+
var resolveFunc = function () { };
|
|
150
|
+
var rejectFunc = function () { };
|
|
151
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
|
152
|
+
resolveFunc = resolve;
|
|
153
|
+
rejectFunc = reject;
|
|
154
|
+
});
|
|
155
|
+
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
|
|
156
|
+
callback = function (err, data) {
|
|
157
|
+
if (err) return rejectFunc(err);
|
|
158
|
+
resolveFunc(data);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const form = {
|
|
162
|
+
"av": ctx.globalOptions.pageID,
|
|
163
|
+
"queries": JSON.stringify({
|
|
164
|
+
"o0": {
|
|
165
|
+
"doc_id": "3336396659757871",
|
|
166
|
+
"query_params": {
|
|
167
|
+
"limit": limit + (timestamp ? 1 : 0),
|
|
168
|
+
"before": timestamp,
|
|
169
|
+
"tags": tags,
|
|
170
|
+
"includeDeliveryReceipts": true,
|
|
171
|
+
"includeSeqID": false
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}),
|
|
175
|
+
"batch_name": "MessengerGraphQLThreadlistFetcher"
|
|
176
|
+
};
|
|
177
|
+
defaultFuncs
|
|
178
|
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
|
179
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
180
|
+
.then((resData) => {
|
|
181
|
+
if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
|
|
182
|
+
if (resData[resData.length - 1].successful_results === 0) throw { error: "getThreadList: there was no successful_results", res: resData };
|
|
183
|
+
if (timestamp) resData[0].o0.data.viewer.message_threads.nodes.shift();
|
|
184
|
+
callback(null, formatThreadList(resData[0].o0.data.viewer.message_threads.nodes));
|
|
185
|
+
})
|
|
186
|
+
.catch((err) => {
|
|
187
|
+
log.error("getThreadList", err);
|
|
188
|
+
return callback(err);
|
|
189
|
+
});
|
|
190
|
+
return returnPromise;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Nexus-FCA: Advanced and Safe Facebook Chat API
|
|
2
|
+
// getThreadListDeprecated.js - Deprecated thread list fetch
|
|
3
|
+
|
|
4
|
+
const utils = require("../utils");
|
|
5
|
+
|
|
6
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
7
|
+
return function getThreadList(start, end, type, callback) {
|
|
8
|
+
if (utils.getType(callback) === "Undefined") {
|
|
9
|
+
if (utils.getType(end) !== "Number") {
|
|
10
|
+
throw {
|
|
11
|
+
error: "Please pass a number as a second argument."
|
|
12
|
+
};
|
|
13
|
+
} else if (
|
|
14
|
+
utils.getType(type) === "Function" ||
|
|
15
|
+
utils.getType(type) === "AsyncFunction"
|
|
16
|
+
) {
|
|
17
|
+
callback = type;
|
|
18
|
+
type = "inbox"; //default to inbox
|
|
19
|
+
} else if (utils.getType(type) !== "String") {
|
|
20
|
+
throw {
|
|
21
|
+
error:
|
|
22
|
+
"Please pass a String as a third argument. Your options are: inbox, pending, and archived"
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
throw {
|
|
26
|
+
error: "getThreadList: need callback"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type === "archived") {
|
|
32
|
+
type = "action:archived";
|
|
33
|
+
} else if (type !== "inbox" && type !== "pending" && type !== "other") {
|
|
34
|
+
throw {
|
|
35
|
+
error:
|
|
36
|
+
"type can only be one of the following: inbox, pending, archived, other"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (end <= start) end = start + 20;
|
|
41
|
+
|
|
42
|
+
const form = {
|
|
43
|
+
client: "mercury"
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
form[type + "[offset]"] = start;
|
|
47
|
+
form[type + "[limit]"] = end - start;
|
|
48
|
+
|
|
49
|
+
if (ctx.globalOptions.pageID) {
|
|
50
|
+
form.request_user_id = ctx.globalOptions.pageID;
|
|
51
|
+
}
|
|
52
|
+
// ...existing code...
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -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
|
+
};
|
package/src/getUID.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Nexus-FCA: Advanced and Safe Facebook Chat API
|
|
2
|
+
// getUID.js - Get Facebook user ID from profile link
|
|
3
|
+
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
const FormData = require('form-data');
|
|
6
|
+
const { URL } = require('url');
|
|
7
|
+
const log = require('npmlog');
|
|
8
|
+
const utils = require('../utils');
|
|
9
|
+
|
|
10
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
11
|
+
return function getUID(link, callback) {
|
|
12
|
+
let resolveFunc = function () { };
|
|
13
|
+
let rejectFunc = function () { };
|
|
14
|
+
let returnPromise = new Promise(function (resolve, reject) {
|
|
15
|
+
resolveFunc = resolve;
|
|
16
|
+
rejectFunc = reject;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!callback) {
|
|
20
|
+
callback = function (err, uid) {
|
|
21
|
+
if (err) return rejectFunc(err);
|
|
22
|
+
resolveFunc(uid);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function getUIDFast(url) {
|
|
27
|
+
let Form = new FormData();
|
|
28
|
+
let Url = new URL(url);
|
|
29
|
+
Form.append('link', Url.href);
|
|
30
|
+
try {
|
|
31
|
+
let { data } = await axios.post('https://id.traodoisub.com/api.php', Form, {
|
|
32
|
+
headers: Form.getHeaders()
|
|
33
|
+
});
|
|
34
|
+
if (data.error) throw new Error(data.error);
|
|
35
|
+
return data.id || "Not found";
|
|
36
|
+
} catch (e) {
|
|
37
|
+
utils.error('getUID', "Error: " + e.message);
|
|
38
|
+
throw new Error(e.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function getUIDSlow(url) {
|
|
43
|
+
let Form = new FormData();
|
|
44
|
+
let Url = new URL(url);
|
|
45
|
+
Form.append('username', Url.pathname.replace(/\//g, ""));
|
|
46
|
+
try {
|
|
47
|
+
const userAgentArray = [
|
|
48
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
|
49
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
|
|
50
|
+
"Mozilla/5.0 (Linux; Android 10; SM-G977N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36",
|
|
51
|
+
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0",
|
|
52
|
+
];
|
|
53
|
+
// ...additional logic for slow fallback...
|
|
54
|
+
} catch (e) {
|
|
55
|
+
utils.error('getUID', "Error: " + e.message);
|
|
56
|
+
throw new Error(e.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Try fast method first, fallback to slow
|
|
61
|
+
getUIDFast(link)
|
|
62
|
+
.then(uid => callback(null, uid))
|
|
63
|
+
.catch(() => getUIDSlow(link).then(uid => callback(null, uid)).catch(callback));
|
|
64
|
+
|
|
65
|
+
return returnPromise;
|
|
66
|
+
};
|
|
67
|
+
};
|
package/src/getUserID.js
ADDED
|
@@ -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,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var utils = require("../utils");
|
|
3
|
+
var log = require("npmlog");
|
|
4
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
5
|
+
function formatData(data) {
|
|
6
|
+
const retObj = {};
|
|
7
|
+
for (const actor of data.messaging_actors || []) {
|
|
8
|
+
retObj[actor.id] = {
|
|
9
|
+
name: actor.name,
|
|
10
|
+
firstName: actor.short_name || null,
|
|
11
|
+
vanity: actor.username || null,
|
|
12
|
+
thumbSrc: actor.big_image_src?.uri || null,
|
|
13
|
+
profileUrl: actor.url || null,
|
|
14
|
+
gender: actor.gender || null,
|
|
15
|
+
type: actor.__typename || null,
|
|
16
|
+
isFriend: actor.is_viewer_friend || false,
|
|
17
|
+
isMessengerUser: actor.is_messenger_user || false,
|
|
18
|
+
isMessageBlockedByViewer: actor.is_message_blocked_by_viewer || false,
|
|
19
|
+
workInfo: actor.work_info || null,
|
|
20
|
+
messengerStatus: actor.messenger_account_status_category || null
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return retObj;
|
|
24
|
+
}
|
|
25
|
+
return function getUserInfoGraphQL(id, callback) {
|
|
26
|
+
let resolveFunc, rejectFunc;
|
|
27
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
28
|
+
resolveFunc = resolve;
|
|
29
|
+
rejectFunc = reject;
|
|
30
|
+
});
|
|
31
|
+
if (typeof callback !== "function") {
|
|
32
|
+
callback = (err, data) => {
|
|
33
|
+
if (err) return rejectFunc(err);
|
|
34
|
+
resolveFunc(data);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const ids = Array.isArray(id) ? id : [id];
|
|
38
|
+
var form = {
|
|
39
|
+
queries: JSON.stringify({
|
|
40
|
+
o0: {
|
|
41
|
+
doc_id: "5009315269112105",
|
|
42
|
+
query_params: {
|
|
43
|
+
ids: ids
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
batch_name: "MessengerParticipantsFetcher"
|
|
48
|
+
};
|
|
49
|
+
defaultFuncs
|
|
50
|
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
|
51
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
52
|
+
.then(function(resData) {
|
|
53
|
+
if (!resData || resData.length === 0) {
|
|
54
|
+
throw new Error("Empty response from server");
|
|
55
|
+
}
|
|
56
|
+
if (resData.error) {
|
|
57
|
+
throw resData.error;
|
|
58
|
+
}
|
|
59
|
+
const response = resData[0];
|
|
60
|
+
if (!response || !response.o0) {
|
|
61
|
+
throw new Error("Invalid response format");
|
|
62
|
+
}
|
|
63
|
+
if (response.o0.errors && response.o0.errors.length > 0) {
|
|
64
|
+
throw new Error(response.o0.errors[0].message || "GraphQL error");
|
|
65
|
+
}
|
|
66
|
+
const result = response.o0.data;
|
|
67
|
+
if (!result || !result.messaging_actors || result.messaging_actors.length === 0) {
|
|
68
|
+
log.warn("getUserInfo", "No user data found for the provided ID(s)");
|
|
69
|
+
return callback(null, {});
|
|
70
|
+
}
|
|
71
|
+
const formattedData = formatData(result);
|
|
72
|
+
return callback(null, formattedData);
|
|
73
|
+
})
|
|
74
|
+
.catch(err => {
|
|
75
|
+
log.error("getUserInfoGraphQL", "Error: " + (err.message || "Unknown error occurred"));
|
|
76
|
+
callback(err);
|
|
77
|
+
});
|
|
78
|
+
return returnPromise;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
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 handleFriendRequest(userID, accept, callback) {
|
|
8
|
+
if (utils.getType(accept) !== "Boolean") {
|
|
9
|
+
throw {
|
|
10
|
+
error: "Please pass a boolean as a second argument."
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let resolveFunc = function () { };
|
|
15
|
+
let rejectFunc = function () { };
|
|
16
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
17
|
+
resolveFunc = resolve;
|
|
18
|
+
rejectFunc = reject;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!callback) {
|
|
22
|
+
callback = function (err, friendList) {
|
|
23
|
+
if (err) {
|
|
24
|
+
return rejectFunc(err);
|
|
25
|
+
}
|
|
26
|
+
resolveFunc(friendList);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const form = {
|
|
31
|
+
viewer_id: ctx.i_userID || ctx.userID,
|
|
32
|
+
"frefs[0]": "jwl",
|
|
33
|
+
floc: "friend_center_requests",
|
|
34
|
+
ref: "/reqs.php",
|
|
35
|
+
action: (accept ? "confirm" : "reject")
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
defaultFuncs
|
|
39
|
+
.post(
|
|
40
|
+
"https://www.facebook.com/requests/friends/ajax/",
|
|
41
|
+
ctx.jar,
|
|
42
|
+
form
|
|
43
|
+
)
|
|
44
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
45
|
+
.then(function (resData) {
|
|
46
|
+
if (resData.payload.err) {
|
|
47
|
+
throw {
|
|
48
|
+
err: resData.payload.err
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return callback();
|
|
53
|
+
})
|
|
54
|
+
.catch(function (err) {
|
|
55
|
+
log.error("handleFriendRequest", err);
|
|
56
|
+
return callback(err);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return returnPromise;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
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 handleMessageRequest(threadID, accept, callback) {
|
|
8
|
+
if (utils.getType(accept) !== "Boolean") {
|
|
9
|
+
throw {
|
|
10
|
+
error: "Please pass a boolean as a second argument."
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let resolveFunc = function () { };
|
|
15
|
+
let rejectFunc = function () { };
|
|
16
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
17
|
+
resolveFunc = resolve;
|
|
18
|
+
rejectFunc = reject;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!callback) {
|
|
22
|
+
callback = function (err, friendList) {
|
|
23
|
+
if (err) {
|
|
24
|
+
return rejectFunc(err);
|
|
25
|
+
}
|
|
26
|
+
resolveFunc(friendList);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const form = {
|
|
31
|
+
client: "mercury"
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (utils.getType(threadID) !== "Array") {
|
|
35
|
+
threadID = [threadID];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const messageBox = accept ? "inbox" : "other";
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < threadID.length; i++) {
|
|
41
|
+
form[messageBox + "[" + i + "]"] = threadID[i];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
defaultFuncs
|
|
45
|
+
.post(
|
|
46
|
+
"https://www.facebook.com/ajax/mercury/move_thread.php",
|
|
47
|
+
ctx.jar,
|
|
48
|
+
form
|
|
49
|
+
)
|
|
50
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
51
|
+
.then(function (resData) {
|
|
52
|
+
if (resData.error) {
|
|
53
|
+
throw resData;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return callback();
|
|
57
|
+
})
|
|
58
|
+
.catch(function (err) {
|
|
59
|
+
log.error("handleMessageRequest", err);
|
|
60
|
+
return callback(err);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return returnPromise;
|
|
64
|
+
};
|
|
65
|
+
};
|