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
package/index.js
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Nexus-FCA: Advanced and Safe Facebook Chat API (custom build)
|
|
3
|
+
const utils = require("./utils");
|
|
4
|
+
const log = require("npmlog");
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const { promises: fsPromises, readFileSync } = require('fs');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const axios = require('axios');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const models = require("./lib/database/models");
|
|
11
|
+
const logger = require("./lib/logger");
|
|
12
|
+
const { safeMode, isUserAllowed, rateLimiter } = require('./utils');
|
|
13
|
+
let checkVerified = null;
|
|
14
|
+
const defaultLogRecordSize = 100;
|
|
15
|
+
log.maxRecordSize = defaultLogRecordSize;
|
|
16
|
+
const defaultConfig = {
|
|
17
|
+
autoUpdate: true,
|
|
18
|
+
mqtt: {
|
|
19
|
+
enabled: true,
|
|
20
|
+
reconnectInterval: 3600,
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const configPath = path.join(process.cwd(), "fca-config.json");
|
|
24
|
+
let config;
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
27
|
+
config = defaultConfig;
|
|
28
|
+
} else {
|
|
29
|
+
try {
|
|
30
|
+
const fileContent = fs.readFileSync(configPath, 'utf8');
|
|
31
|
+
config = JSON.parse(fileContent);
|
|
32
|
+
config = { ...defaultConfig, ...config };
|
|
33
|
+
} catch (err) {
|
|
34
|
+
logger("Error reading config file, using defaults", "error");
|
|
35
|
+
config = defaultConfig;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
global.fca = {
|
|
39
|
+
config: config
|
|
40
|
+
};
|
|
41
|
+
const Boolean_Option = [
|
|
42
|
+
"online",
|
|
43
|
+
"selfListen",
|
|
44
|
+
"listenEvents",
|
|
45
|
+
"updatePresence",
|
|
46
|
+
"forceLogin",
|
|
47
|
+
"autoMarkDelivery",
|
|
48
|
+
"autoMarkRead",
|
|
49
|
+
"listenTyping",
|
|
50
|
+
"autoReconnect",
|
|
51
|
+
"emitReady",
|
|
52
|
+
];
|
|
53
|
+
function setOptions(globalOptions, options) {
|
|
54
|
+
Object.keys(options).map(function (key) {
|
|
55
|
+
switch (Boolean_Option.includes(key)) {
|
|
56
|
+
case true: {
|
|
57
|
+
globalOptions[key] = Boolean(options[key]);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case false: {
|
|
61
|
+
switch (key) {
|
|
62
|
+
case "pauseLog": {
|
|
63
|
+
if (options.pauseLog) log.pause();
|
|
64
|
+
else log.resume();
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case "logLevel": {
|
|
68
|
+
log.level = options.logLevel;
|
|
69
|
+
globalOptions.logLevel = options.logLevel;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "logRecordSize": {
|
|
73
|
+
log.maxRecordSize = options.logRecordSize;
|
|
74
|
+
globalOptions.logRecordSize = options.logRecordSize;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "pageID": {
|
|
78
|
+
globalOptions.pageID = options.pageID.toString();
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "userAgent": {
|
|
82
|
+
globalOptions.userAgent =
|
|
83
|
+
options.userAgent ||
|
|
84
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "proxy": {
|
|
88
|
+
if (typeof options.proxy != "string") {
|
|
89
|
+
delete globalOptions.proxy;
|
|
90
|
+
utils.setProxy();
|
|
91
|
+
} else {
|
|
92
|
+
globalOptions.proxy = options.proxy;
|
|
93
|
+
utils.setProxy(globalOptions.proxy);
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
log.warn(
|
|
99
|
+
"setOptions",
|
|
100
|
+
"Unrecognized option given to setOptions: " + key
|
|
101
|
+
);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function buildAPI(globalOptions, html, jar) {
|
|
111
|
+
const cookies = jar.getCookies("https://www.facebook.com");
|
|
112
|
+
const userCookie = cookies.find(c => c.cookieString().startsWith("c_user="));
|
|
113
|
+
const tiktikCookie = cookies.find(c => c.cookieString().startsWith("i_user="));
|
|
114
|
+
if (userCookie.length === 0 && tiktikCookie.length === 0) {
|
|
115
|
+
return log.error('login', "Không tìm thấy cookie cho người dùng, vui lòng kiểm tra lại thông tin đăng nhập")
|
|
116
|
+
} else if (!userCookie && !tiktikCookie) {
|
|
117
|
+
return log.error('login', "Không tìm thấy cookie cho người dùng, vui lòng kiểm tra lại thông tin đăng nhập")
|
|
118
|
+
} else if (html.includes("/checkpoint/block/?next")) {
|
|
119
|
+
return log.error('login', "Appstate die, vui lòng thay cái mới!", 'error');
|
|
120
|
+
}
|
|
121
|
+
const userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
|
|
122
|
+
const i_userID = tiktikCookie ? tiktikCookie.cookieString().split("=")[1] : null;
|
|
123
|
+
logger(`Logged in as ${userID}`, 'info');
|
|
124
|
+
try {
|
|
125
|
+
clearInterval(checkVerified);
|
|
126
|
+
} catch (_) { }
|
|
127
|
+
const clientID = ((Math.random() * 2147483648) | 0).toString(16);
|
|
128
|
+
let mqttEndpoint, region, fb_dtsg, irisSeqID;
|
|
129
|
+
try {
|
|
130
|
+
const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
|
|
131
|
+
if (endpointMatch) {
|
|
132
|
+
mqttEndpoint = endpointMatch[1].replace(/\\\//g, "/");
|
|
133
|
+
const url = new URL(mqttEndpoint);
|
|
134
|
+
region = url.searchParams.get("region")?.toUpperCase() || "PRN";
|
|
135
|
+
}
|
|
136
|
+
logger(`Sever region ${region}`, 'info');
|
|
137
|
+
} catch (e) {
|
|
138
|
+
log.warning("login", "Not MQTT endpoint");
|
|
139
|
+
}
|
|
140
|
+
const tokenMatch = html.match(/DTSGInitialData.*?token":"(.*?)"/);
|
|
141
|
+
if (tokenMatch) {
|
|
142
|
+
fb_dtsg = tokenMatch[1];
|
|
143
|
+
}
|
|
144
|
+
(async () => {
|
|
145
|
+
try {
|
|
146
|
+
await models.sequelize.authenticate();
|
|
147
|
+
await models.syncAll();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(error);
|
|
150
|
+
console.error('Database connection failed:', error.message);
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
// Professional gradient banner for Nexus-FCA
|
|
154
|
+
logger('═══════════════════════════════════════════════════════════════════════════════', 'info');
|
|
155
|
+
logger(' Welcome to Nexus-FCA - Advanced & Safe Facebook Chat API', 'info');
|
|
156
|
+
logger('═══════════════════════════════════════════════════════════════════════════════', 'info');
|
|
157
|
+
logger(`Nexus-FCA`, 'info');
|
|
158
|
+
const ctx = {
|
|
159
|
+
userID: userID,
|
|
160
|
+
i_userID: i_userID,
|
|
161
|
+
jar: jar,
|
|
162
|
+
clientID: clientID,
|
|
163
|
+
globalOptions: globalOptions,
|
|
164
|
+
loggedIn: true,
|
|
165
|
+
access_token: "NONE",
|
|
166
|
+
clientMutationId: 0,
|
|
167
|
+
mqttClient: undefined,
|
|
168
|
+
lastSeqId: irisSeqID,
|
|
169
|
+
syncToken: undefined,
|
|
170
|
+
mqttEndpoint,
|
|
171
|
+
region,
|
|
172
|
+
firstListen: true,
|
|
173
|
+
fb_dtsg,
|
|
174
|
+
wsReqNumber: 0,
|
|
175
|
+
wsTaskNumber: 0
|
|
176
|
+
};
|
|
177
|
+
const api = {
|
|
178
|
+
setOptions: setOptions.bind(null, globalOptions),
|
|
179
|
+
getAppState: function getAppState() {
|
|
180
|
+
const appState = utils.getAppState(jar);
|
|
181
|
+
return appState.filter(
|
|
182
|
+
(item, index, self) =>
|
|
183
|
+
self.findIndex((t) => {
|
|
184
|
+
return t.key === item.key;
|
|
185
|
+
}) === index
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
healthCheck: function(callback) {
|
|
189
|
+
// Simple health check: returns status and safeMode info
|
|
190
|
+
callback(null, {
|
|
191
|
+
status: 'ok',
|
|
192
|
+
safeMode,
|
|
193
|
+
time: new Date().toISOString(),
|
|
194
|
+
userID: ctx.userID || null
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
const defaultFuncs = utils.makeDefaults(html, i_userID || userID, ctx);
|
|
199
|
+
require("fs")
|
|
200
|
+
.readdirSync(__dirname + "/src/")
|
|
201
|
+
.filter((v) => v.endsWith(".js"))
|
|
202
|
+
.map(function (v) {
|
|
203
|
+
api[v.replace(".js", "")] = require("./src/" + v)(defaultFuncs, api, ctx);
|
|
204
|
+
});
|
|
205
|
+
api.listen = api.listenMqtt;
|
|
206
|
+
setInterval(async () => {
|
|
207
|
+
api
|
|
208
|
+
.refreshFb_dtsg()
|
|
209
|
+
.then(() => {
|
|
210
|
+
logger("Successfully refreshed fb_dtsg", 'info');
|
|
211
|
+
})
|
|
212
|
+
.catch((err) => {
|
|
213
|
+
console.error("An error occurred while refreshing fb_dtsg", err);
|
|
214
|
+
});
|
|
215
|
+
}, 1000 * 60 * 60 * 24);
|
|
216
|
+
return {
|
|
217
|
+
ctx,
|
|
218
|
+
defaultFuncs,
|
|
219
|
+
api
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
|
|
224
|
+
let mainPromise = null;
|
|
225
|
+
const jar = utils.getJar();
|
|
226
|
+
if (appState) {
|
|
227
|
+
try {
|
|
228
|
+
appState = JSON.parse(appState);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
try {
|
|
231
|
+
appState = appState;
|
|
232
|
+
} catch (e) {
|
|
233
|
+
return callback(new Error("Failed to parse appState"));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
appState.forEach(c => {
|
|
239
|
+
const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
|
|
240
|
+
jar.setCookie(str, "http://" + c.domain);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
|
|
244
|
+
.then(utils.saveCookies(jar));
|
|
245
|
+
} catch (e) {
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
mainPromise = utils
|
|
250
|
+
.get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
|
|
251
|
+
.then(utils.saveCookies(jar))
|
|
252
|
+
.then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
|
|
253
|
+
.then(() => utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar)));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function handleRedirect(res) {
|
|
257
|
+
const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
|
|
258
|
+
const redirect = reg.exec(res.body);
|
|
259
|
+
if (redirect && redirect[1]) {
|
|
260
|
+
return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
|
|
261
|
+
}
|
|
262
|
+
return res;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let ctx, api;
|
|
266
|
+
mainPromise = mainPromise
|
|
267
|
+
.then(handleRedirect)
|
|
268
|
+
.then(res => {
|
|
269
|
+
const mobileAgentRegex = /MPageLoadClientMetrics/gs;
|
|
270
|
+
if (!mobileAgentRegex.test(res.body)) {
|
|
271
|
+
globalOptions.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36";
|
|
272
|
+
return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
|
|
273
|
+
}
|
|
274
|
+
return res;
|
|
275
|
+
})
|
|
276
|
+
.then(handleRedirect)
|
|
277
|
+
.then(res => {
|
|
278
|
+
const html = res.body;
|
|
279
|
+
const Obj = buildAPI(globalOptions, html, jar);
|
|
280
|
+
ctx = Obj.ctx;
|
|
281
|
+
api = Obj.api;
|
|
282
|
+
return res;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (globalOptions.pageID) {
|
|
286
|
+
mainPromise = mainPromise
|
|
287
|
+
.then(() => utils.get(`https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`, jar, null, globalOptions))
|
|
288
|
+
.then(resData => {
|
|
289
|
+
let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
|
|
290
|
+
url = url.substring(0, url.length - 1);
|
|
291
|
+
return utils.get('https://www.facebook.com' + url, jar, null, globalOptions);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
mainPromise
|
|
296
|
+
.then(async () => {
|
|
297
|
+
// Version check and auto-update code removed for safety and to prevent error spam.
|
|
298
|
+
logger('Login successful!', '[ Nexus-FCA ] >');
|
|
299
|
+
callback(null, api);
|
|
300
|
+
})
|
|
301
|
+
.catch(e => {
|
|
302
|
+
callback(e);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function login(loginData, options, callback) {
|
|
307
|
+
if (
|
|
308
|
+
utils.getType(options) === "Function" ||
|
|
309
|
+
utils.getType(options) === "AsyncFunction"
|
|
310
|
+
) {
|
|
311
|
+
callback = options;
|
|
312
|
+
options = {};
|
|
313
|
+
}
|
|
314
|
+
const globalOptions = {
|
|
315
|
+
selfListen: false,
|
|
316
|
+
selfListenEvent: false,
|
|
317
|
+
listenEvents: false,
|
|
318
|
+
listenTyping: false,
|
|
319
|
+
updatePresence: false,
|
|
320
|
+
forceLogin: false,
|
|
321
|
+
autoMarkDelivery: true,
|
|
322
|
+
autoMarkRead: false,
|
|
323
|
+
autoReconnect: true,
|
|
324
|
+
logRecordSize: defaultLogRecordSize,
|
|
325
|
+
online: true,
|
|
326
|
+
emitReady: false,
|
|
327
|
+
userAgent:
|
|
328
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
|
|
329
|
+
};
|
|
330
|
+
setOptions(globalOptions, options);
|
|
331
|
+
let prCallback = null;
|
|
332
|
+
if (
|
|
333
|
+
utils.getType(callback) !== "Function" &&
|
|
334
|
+
utils.getType(callback) !== "AsyncFunction"
|
|
335
|
+
) {
|
|
336
|
+
let rejectFunc = null;
|
|
337
|
+
let resolveFunc = null;
|
|
338
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
|
339
|
+
resolveFunc = resolve;
|
|
340
|
+
rejectFunc = reject;
|
|
341
|
+
});
|
|
342
|
+
prCallback = function (error, api) {
|
|
343
|
+
if (error) {
|
|
344
|
+
return rejectFunc(error);
|
|
345
|
+
}
|
|
346
|
+
return resolveFunc(api);
|
|
347
|
+
};
|
|
348
|
+
callback = prCallback;
|
|
349
|
+
}
|
|
350
|
+
loginHelper(
|
|
351
|
+
loginData.appState,
|
|
352
|
+
loginData.email,
|
|
353
|
+
loginData.password,
|
|
354
|
+
globalOptions,
|
|
355
|
+
callback,
|
|
356
|
+
prCallback
|
|
357
|
+
);
|
|
358
|
+
return returnPromise;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
module.exports = login;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { Sequelize } = require('sequelize');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const databasePath = path.join(process.cwd(), 'Fca_Database');
|
|
5
|
+
if (!fs.existsSync(databasePath)) {
|
|
6
|
+
fs.mkdirSync(databasePath, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
const sequelize = new Sequelize({
|
|
9
|
+
dialect: 'sqlite',
|
|
10
|
+
storage: path.join(databasePath, 'database.sqlite'),
|
|
11
|
+
logging: false,
|
|
12
|
+
pool: {
|
|
13
|
+
max: 5,
|
|
14
|
+
min: 0,
|
|
15
|
+
acquire: 30000,
|
|
16
|
+
idle: 10000
|
|
17
|
+
},
|
|
18
|
+
retry: {
|
|
19
|
+
max: 3
|
|
20
|
+
},
|
|
21
|
+
dialectOptions: {
|
|
22
|
+
timeout: 5000
|
|
23
|
+
},
|
|
24
|
+
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
|
|
25
|
+
});
|
|
26
|
+
const models = {};
|
|
27
|
+
fs.readdirSync(__dirname).filter(file => file.endsWith('.js') && file !== 'index.js').forEach(file => {
|
|
28
|
+
const model = require(path.join(__dirname, file))(sequelize);
|
|
29
|
+
models[model.name] = model;
|
|
30
|
+
});
|
|
31
|
+
Object.keys(models).forEach(modelName => {
|
|
32
|
+
if (models[modelName].associate) {
|
|
33
|
+
models[modelName].associate(models);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
models.sequelize = sequelize;
|
|
37
|
+
models.Sequelize = Sequelize;
|
|
38
|
+
models.syncAll = async () => {
|
|
39
|
+
try {
|
|
40
|
+
await sequelize.sync({ force: false });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Failed to synchronize models:', error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = models;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module.exports = function(sequelize) {
|
|
2
|
+
const { Model, DataTypes } = require("sequelize");
|
|
3
|
+
|
|
4
|
+
class Thread extends Model {}
|
|
5
|
+
|
|
6
|
+
Thread.init(
|
|
7
|
+
{
|
|
8
|
+
num: {
|
|
9
|
+
type: DataTypes.INTEGER,
|
|
10
|
+
allowNull: false,
|
|
11
|
+
autoIncrement: true,
|
|
12
|
+
primaryKey: true,
|
|
13
|
+
},
|
|
14
|
+
threadID: {
|
|
15
|
+
type: DataTypes.STRING,
|
|
16
|
+
allowNull: false,
|
|
17
|
+
unique: true,
|
|
18
|
+
},
|
|
19
|
+
data: {
|
|
20
|
+
type: DataTypes.JSONB,
|
|
21
|
+
allowNull: true,
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
sequelize,
|
|
26
|
+
modelName: "Thread",
|
|
27
|
+
timestamps: true,
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
return Thread;
|
|
31
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { Thread } = require('./models');
|
|
2
|
+
|
|
3
|
+
const validateThreadID = (threadID) => {
|
|
4
|
+
if (typeof threadID !== 'string' && typeof threadID !== 'number') {
|
|
5
|
+
throw new Error('Invalid threadID: must be a string or number.');
|
|
6
|
+
}
|
|
7
|
+
return String(threadID);
|
|
8
|
+
};
|
|
9
|
+
const validateData = (data) => {
|
|
10
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
11
|
+
throw new Error('Invalid data: must be a non-empty object.');
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = function (bot) {
|
|
16
|
+
return {
|
|
17
|
+
async create(threadID, data) {
|
|
18
|
+
try {
|
|
19
|
+
let thread = await Thread.findOne({ where: { threadID } });
|
|
20
|
+
if (thread) {
|
|
21
|
+
return { thread: thread.get(), created: false };
|
|
22
|
+
}
|
|
23
|
+
thread = await Thread.create({ threadID, ...data });
|
|
24
|
+
return { thread: thread.get(), created: true };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new Error(`Failed to create thread: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async get(threadID) {
|
|
31
|
+
try {
|
|
32
|
+
threadID = validateThreadID(threadID);
|
|
33
|
+
const thread = await Thread.findOne({ where: { threadID } });
|
|
34
|
+
return thread ? thread.get() : null;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new Error(`Failed to get thread: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
async update(threadID, data) {
|
|
41
|
+
try {
|
|
42
|
+
threadID = validateThreadID(threadID);
|
|
43
|
+
validateData(data);
|
|
44
|
+
const thread = await Thread.findOne({ where: { threadID } });
|
|
45
|
+
|
|
46
|
+
if (thread) {
|
|
47
|
+
await thread.update(data);
|
|
48
|
+
return { thread: thread.get(), created: false };
|
|
49
|
+
} else {
|
|
50
|
+
const newThread = await Thread.create({ ...data, threadID });
|
|
51
|
+
return { thread: newThread.get(), created: true };
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(`Failed to update thread: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
async del(threadID) {
|
|
59
|
+
try {
|
|
60
|
+
if (!threadID) {
|
|
61
|
+
throw new Error('threadID is required and cannot be undefined');
|
|
62
|
+
}
|
|
63
|
+
threadID = validateThreadID(threadID);
|
|
64
|
+
if (!threadID) {
|
|
65
|
+
throw new Error('Invalid threadID');
|
|
66
|
+
}
|
|
67
|
+
const result = await Thread.destroy({ where: { threadID } });
|
|
68
|
+
if (result === 0) {
|
|
69
|
+
throw new Error('No thread found with the specified threadID');
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new Error(`Failed to delete thread: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
async delAll() {
|
|
77
|
+
try {
|
|
78
|
+
return await Thread.destroy({ where: {} });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(`Failed to delete all threads: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
async getAll(keys = null) {
|
|
84
|
+
try {
|
|
85
|
+
const attributes = typeof keys === 'string' ? [keys] : Array.isArray(keys) ? keys : undefined;
|
|
86
|
+
const threads = await Thread.findAll({ attributes });
|
|
87
|
+
return threads.map(thread => thread.get());
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new Error(`Failed to get all threads: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const gradient = require('gradient-string');
|
|
3
|
+
|
|
4
|
+
// Use a professional gradient for info logs
|
|
5
|
+
const infoGradient = gradient(['#00c6ff', '#0072ff']); // blue-cyan gradient
|
|
6
|
+
const warnColor = chalk.yellow.bold;
|
|
7
|
+
const errorColor = chalk.red.bold;
|
|
8
|
+
|
|
9
|
+
module.exports = (text, type) => {
|
|
10
|
+
switch (type) {
|
|
11
|
+
case "warn":
|
|
12
|
+
process.stderr.write(warnColor(`\r[ NEXUS-FCA WARN ] > ${text}`) + '\n');
|
|
13
|
+
break;
|
|
14
|
+
case "error":
|
|
15
|
+
process.stderr.write(errorColor(`\r[ NEXUS-FCA ERROR ] > ${text}`) + '\n');
|
|
16
|
+
break;
|
|
17
|
+
case "info":
|
|
18
|
+
process.stderr.write(infoGradient(`\r[ NEXUS-FCA ] > ${text}`) + '\n');
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
process.stderr.write(infoGradient(`\r[ NEXUS-FCA ] > ${text}`) + '\n');
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
};
|
package/lib/login.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nexus-fca",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A modern, safe, and advanced Facebook Chat API for Node.js. Messenger automation, bots, and integrations made easy.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/AlAminNexus/nexus-fca.git"
|
|
9
|
+
},
|
|
10
|
+
"author": "Al Amin",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"axios": "^1.8.4",
|
|
14
|
+
"bluebird": "^3.7.2",
|
|
15
|
+
"chalk": "^4.1.2",
|
|
16
|
+
"cheerio": "^1.0.0-rc.10",
|
|
17
|
+
"duplexify": "^4.1.3",
|
|
18
|
+
"gradient-string": "^2.0.2",
|
|
19
|
+
"https-proxy-agent": "^4.0.0",
|
|
20
|
+
"mqtt": "^4.3.8",
|
|
21
|
+
"npmlog": "^1.2.0",
|
|
22
|
+
"request": "^2.53.0",
|
|
23
|
+
"sequelize": "^6.37.6",
|
|
24
|
+
"sqlite3": "^5.1.7",
|
|
25
|
+
"totp-generator": "^1.0.0",
|
|
26
|
+
"ws": "^8.18.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"eslint": "^7.5.0",
|
|
30
|
+
"mocha": "^10.2.0",
|
|
31
|
+
"prettier": "^1.11.1"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"test": "mocha",
|
|
35
|
+
"lint": "eslint **/*.js",
|
|
36
|
+
"prettier": "prettier utils.js src/* --write"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"facebook",
|
|
40
|
+
"chat",
|
|
41
|
+
"api",
|
|
42
|
+
"fca",
|
|
43
|
+
"facebook-chat-api"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=10.x"
|
|
47
|
+
},
|
|
48
|
+
"eslintConfig": {
|
|
49
|
+
"env": {
|
|
50
|
+
"es6": true,
|
|
51
|
+
"es2017": true,
|
|
52
|
+
"node": true
|
|
53
|
+
},
|
|
54
|
+
"extends": "eslint:recommended",
|
|
55
|
+
"parserOptions": {
|
|
56
|
+
"sourceType": "module"
|
|
57
|
+
},
|
|
58
|
+
"rules": {
|
|
59
|
+
"linebreak-style": [
|
|
60
|
+
"error",
|
|
61
|
+
"unix"
|
|
62
|
+
],
|
|
63
|
+
"semi": [
|
|
64
|
+
"error",
|
|
65
|
+
"always"
|
|
66
|
+
],
|
|
67
|
+
"no-unused-vars": [
|
|
68
|
+
1,
|
|
69
|
+
{
|
|
70
|
+
"argsIgnorePattern": "^_",
|
|
71
|
+
"varsIgnorePattern": "^_"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"no-empty": [
|
|
75
|
+
"error",
|
|
76
|
+
{
|
|
77
|
+
"allowEmptyCatch": true
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"types": "index.d.ts",
|
|
83
|
+
"homepage": "https://github.com/Nexus-016/Nexus-fCA#readme",
|
|
84
|
+
"bugs": {
|
|
85
|
+
"url": "https://github.com/Nexus-016/Nexus-fCA/issues"
|
|
86
|
+
},
|
|
87
|
+
"directories": {
|
|
88
|
+
"test": "test"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require("../utils");
|
|
4
|
+
|
|
5
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
6
|
+
return function addExternalModule(moduleObj) {
|
|
7
|
+
if (utils.getType(moduleObj) == "Object") {
|
|
8
|
+
for (const apiName in moduleObj) {
|
|
9
|
+
if (utils.getType(moduleObj[apiName]) == "Function") {
|
|
10
|
+
api[apiName] = moduleObj[apiName](defaultFuncs, api, ctx);
|
|
11
|
+
} else {
|
|
12
|
+
throw new Error(`Item "${apiName}" in moduleObj must be a function, not ${utils.getType(moduleObj[apiName])}!`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
throw new Error(`moduleObj must be an object, not ${utils.getType(moduleObj)}!`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|