@cexy/hoonfca 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/README.md +83 -0
- package/index.js +467 -0
- package/package.json +12 -0
- package/replit.nix +3 -0
- package/src/addExternalModule.js +25 -0
- package/src/addUserToGroup.js +98 -0
- package/src/changeAdminStatus.js +98 -0
- package/src/changeArchivedStatus.js +51 -0
- package/src/changeAvatar.js +89 -0
- package/src/changeBio.js +74 -0
- package/src/changeBlockedStatus.js +45 -0
- package/src/changeBlockedStatusMqtt.js +65 -0
- package/src/changeCover.js +92 -0
- package/src/changeGroupImage.js +110 -0
- package/src/changeName.js +106 -0
- package/src/changeNickname.js +58 -0
- package/src/changeThreadColor.js +62 -0
- package/src/changeThreadEmoji.js +54 -0
- package/src/changeUsername.js +80 -0
- package/src/createCommentPost.js +196 -0
- package/src/createNewGroup.js +81 -0
- package/src/createPoll.js +64 -0
- package/src/createPost.js +285 -0
- package/src/data/getThreadInfo.json +1 -0
- package/src/deleteMessage.js +55 -0
- package/src/deleteThread.js +55 -0
- package/src/editMessage.js +66 -0
- package/src/follow.js +71 -0
- package/src/forwardAttachment.js +57 -0
- package/src/getAccess.js +130 -0
- package/src/getAvatarUser.js +88 -0
- package/src/getBotInitialData.js +59 -0
- package/src/getCtx.js +5 -0
- package/src/getCurrentUserID.js +5 -0
- package/src/getEmojiUrl.js +18 -0
- package/src/getFriendsList.js +81 -0
- package/src/getMessage.js +827 -0
- package/src/getOptions.js +5 -0
- package/src/getRegion.js +7 -0
- package/src/getThreadHistory.js +496 -0
- package/src/getThreadHistoryDeprecated.js +95 -0
- package/src/getThreadInfo.js +194 -0
- package/src/getThreadInfoDeprecated.js +77 -0
- package/src/getThreadList.js +219 -0
- package/src/getThreadListDeprecated.js +86 -0
- package/src/getThreadPictures.js +85 -0
- package/src/getUserID.js +67 -0
- package/src/getUserInfo.js +89 -0
- package/src/handleFriendRequest.js +56 -0
- package/src/handleMessageRequest.js +65 -0
- package/src/httpGet.js +53 -0
- package/src/httpPost.js +53 -0
- package/src/httpPostFormData.js +58 -0
- package/src/listenMqtt.js +683 -0
- package/src/listenNotification.js +96 -0
- package/src/logout.js +77 -0
- package/src/markAsDelivered.js +53 -0
- package/src/markAsRead.js +74 -0
- package/src/markAsReadAll.js +46 -0
- package/src/markAsSeen.js +57 -0
- package/src/muteThread.js +49 -0
- package/src/pinMessage.js +60 -0
- package/src/refreshFb_dtsg.js +75 -0
- package/src/removeUserFromGroup.js +59 -0
- package/src/resolvePhotoUrl.js +47 -0
- package/src/searchForThread.js +55 -0
- package/src/searchStickers.js +59 -0
- package/src/sendMessage.js +444 -0
- package/src/sendMessageMqtt.js +326 -0
- package/src/sendTypingIndicator.js +54 -0
- package/src/setMessageReaction.js +126 -0
- package/src/setMessageReactionMqtt.js +74 -0
- package/src/setPostReaction.js +103 -0
- package/src/setProfileGuard.js +66 -0
- package/src/setStoryReaction.js +97 -0
- package/src/setTitle.js +100 -0
- package/src/shareContact.js +101 -0
- package/src/shareLink.js +105 -0
- package/src/stopListenMqtt.js +47 -0
- package/src/threadColors.js +119 -0
- package/src/unfriend.js +58 -0
- package/src/unsendMessage.js +51 -0
- package/src/uploadAttachment.js +98 -0
- package/utils.js +919 -0
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
@cexy/hoonfca
|
|
2
|
+
|
|
3
|
+
A modern, fast, and actively maintained Facebook Chat API for Node.js.
|
|
4
|
+
Built for Messenger bots with improved stability, performance, and compatibility.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
📦 Installation
|
|
9
|
+
|
|
10
|
+
Install the package using npm:
|
|
11
|
+
|
|
12
|
+
npm install @cexy/rakibfca
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
🚀 Usage
|
|
17
|
+
|
|
18
|
+
const login = require("@cexy/rakibfca");
|
|
19
|
+
|
|
20
|
+
login(
|
|
21
|
+
{
|
|
22
|
+
appState: require("./appstate.json")
|
|
23
|
+
},
|
|
24
|
+
(err, api) => {
|
|
25
|
+
if (err) return console.error(err);
|
|
26
|
+
|
|
27
|
+
console.log("Logged in as:", api.getCurrentUserID());
|
|
28
|
+
|
|
29
|
+
api.listenMqtt((err, event) => {
|
|
30
|
+
if (err) return console.error(err);
|
|
31
|
+
|
|
32
|
+
console.log(event);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
✨ Features
|
|
40
|
+
|
|
41
|
+
- ⚡ Fast and lightweight
|
|
42
|
+
- 🔒 AppState login support
|
|
43
|
+
- 💬 Messenger API support
|
|
44
|
+
- 📩 MQTT listener
|
|
45
|
+
- 🛠️ Regular bug fixes
|
|
46
|
+
- 🚀 Performance improvements
|
|
47
|
+
- 📦 Easy integration with Node.js projects
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
📋 Requirements
|
|
52
|
+
|
|
53
|
+
- Node.js 20+
|
|
54
|
+
- npm 10+
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
📖 Installation Example
|
|
59
|
+
|
|
60
|
+
mkdir my-bot
|
|
61
|
+
cd my-bot
|
|
62
|
+
npm init -y
|
|
63
|
+
npm install @cexy/rakibfca
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
🤝 Contributing
|
|
68
|
+
|
|
69
|
+
Contributions, bug reports, and feature requests are always welcome.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
📄 License
|
|
74
|
+
|
|
75
|
+
MIT License
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
👨💻 Author
|
|
80
|
+
|
|
81
|
+
Rakib Hasan
|
|
82
|
+
|
|
83
|
+
NPM: @cexy/hoonfca
|
package/index.js
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require("./utils");
|
|
4
|
+
const cheerio = require("cheerio");
|
|
5
|
+
const log = require("npmlog");
|
|
6
|
+
|
|
7
|
+
log.maxRecordSize = 100;
|
|
8
|
+
const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
|
|
9
|
+
global.ditconmemay = false;
|
|
10
|
+
|
|
11
|
+
function setOptions(globalOptions, options) {
|
|
12
|
+
Object.keys(options).forEach(function (key) {
|
|
13
|
+
switch (Boolean_Option.includes(key)) {
|
|
14
|
+
case true: {
|
|
15
|
+
globalOptions[key] = Boolean(options[key]);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
case false: {
|
|
19
|
+
switch (key) {
|
|
20
|
+
case 'pauseLog': {
|
|
21
|
+
if (options.pauseLog) log.pause();
|
|
22
|
+
else log.resume();
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
case 'logLevel': {
|
|
26
|
+
log.level = options.logLevel;
|
|
27
|
+
globalOptions.logLevel = options.logLevel;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case 'logRecordSize': {
|
|
31
|
+
log.maxRecordSize = options.logRecordSize;
|
|
32
|
+
globalOptions.logRecordSize = options.logRecordSize;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case 'pageID': {
|
|
36
|
+
globalOptions.pageID = options.pageID.toString();
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case 'userAgent': {
|
|
40
|
+
globalOptions.userAgent = (options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36');
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case 'proxy': {
|
|
44
|
+
if (typeof options.proxy !== "string") {
|
|
45
|
+
delete globalOptions.proxy;
|
|
46
|
+
utils.setProxy();
|
|
47
|
+
} else {
|
|
48
|
+
globalOptions.proxy = options.proxy;
|
|
49
|
+
utils.setProxy(globalOptions.proxy);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
default: {
|
|
54
|
+
log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildAPI(globalOptions, html, jar) {
|
|
65
|
+
let fb_dtsg = null;
|
|
66
|
+
let irisSeqID = null;
|
|
67
|
+
|
|
68
|
+
function extractFromHTML() {
|
|
69
|
+
try {
|
|
70
|
+
const $ = cheerio.load(html);
|
|
71
|
+
const patterns = [
|
|
72
|
+
/\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
|
|
73
|
+
/\["DTSGInitData",\[\],{"token":"([^"]+)"/,
|
|
74
|
+
/"token":"([^"]+)"/,
|
|
75
|
+
/{\\"token\\":\\"([^\\]+)\\"/,
|
|
76
|
+
/,\{"token":"([^"]+)"\},\d+\]/,
|
|
77
|
+
/"async_get_token":"([^"]+)"/,
|
|
78
|
+
/"dtsg":\{"token":"([^"]+)"/,
|
|
79
|
+
/DTSGInitialData[^>]+>([^<]+)/
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const scripts = $('script').get();
|
|
83
|
+
for (const script of scripts) {
|
|
84
|
+
const scriptText = $(script).html() || '';
|
|
85
|
+
for (const pattern of patterns) {
|
|
86
|
+
const match = scriptText.match(pattern);
|
|
87
|
+
if (match && match[1]) {
|
|
88
|
+
fb_dtsg = match[1].includes('\\"') ? match[1].replace(/\\"/g, '"') : match[1];
|
|
89
|
+
if (fb_dtsg.startsWith('{')) {
|
|
90
|
+
try { fb_dtsg = JSON.parse(fb_dtsg).token || fb_dtsg; } catch(e){}
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (fb_dtsg) break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!fb_dtsg) {
|
|
99
|
+
const dtsgInput = $('input[name="fb_dtsg"]').val();
|
|
100
|
+
if (dtsgInput) fb_dtsg = dtsgInput;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const seqMatches = html.match(/irisSeqID":"([^"]+)"/);
|
|
104
|
+
if (seqMatches && seqMatches[1]) {
|
|
105
|
+
irisSeqID = seqMatches[1];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!fb_dtsg) {
|
|
109
|
+
const jsonMatches = html.match(/\{"dtsg":({[^}]+})/);
|
|
110
|
+
if (jsonMatches && jsonMatches[1]) {
|
|
111
|
+
try {
|
|
112
|
+
const dtsgData = JSON.parse(jsonMatches[1]);
|
|
113
|
+
if (dtsgData.token) fb_dtsg = dtsgData.token;
|
|
114
|
+
} catch(e){}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (fb_dtsg) {
|
|
119
|
+
console.log("Đã tìm thấy fb_dtsg");
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.log("Lỗi khi tìm fb_dtsg:", e);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
extractFromHTML();
|
|
127
|
+
let userID;
|
|
128
|
+
const cookies = jar.getCookies("https://www.facebook.com");
|
|
129
|
+
const userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
|
|
130
|
+
const tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
|
|
131
|
+
|
|
132
|
+
if (!userCookie && !tiktikCookie) {
|
|
133
|
+
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");
|
|
134
|
+
}
|
|
135
|
+
if (html.includes("/checkpoint/block/?next")) {
|
|
136
|
+
return log.error('login', "Appstate die, vui lòng thay cái mới!", 'error');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
|
|
140
|
+
const clientID = (Math.random() * 2147483648 | 0).toString(16);
|
|
141
|
+
let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=prn&sid=${userID}`;
|
|
142
|
+
let region = "PRN";
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
if (html.includes("601051028565049")) {
|
|
146
|
+
console.log(`lỗi login vì dính tài khoản tự động`);
|
|
147
|
+
global.ditconmemay = true;
|
|
148
|
+
}
|
|
149
|
+
const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
|
|
150
|
+
if (endpointMatch && endpointMatch[1]) {
|
|
151
|
+
mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
|
|
152
|
+
const url = new URL(mqttEndpoint);
|
|
153
|
+
region = url.searchParams.get('region')?.toUpperCase() || "PRN";
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.log('Using default MQTT endpoint');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log.info('login', 'Fix fca by DongDev x Satoru, published By Team Calyx');
|
|
160
|
+
|
|
161
|
+
const ctx = {
|
|
162
|
+
userID: userID,
|
|
163
|
+
jar: jar,
|
|
164
|
+
clientID: clientID,
|
|
165
|
+
globalOptions: globalOptions,
|
|
166
|
+
loggedIn: true,
|
|
167
|
+
access_token: 'NONE',
|
|
168
|
+
clientMutationId: 0,
|
|
169
|
+
mqttClient: undefined,
|
|
170
|
+
lastSeqId: irisSeqID,
|
|
171
|
+
syncToken: undefined,
|
|
172
|
+
mqttEndpoint: mqttEndpoint,
|
|
173
|
+
region: region,
|
|
174
|
+
firstListen: true,
|
|
175
|
+
fb_dtsg: fb_dtsg,
|
|
176
|
+
req_ID: 0,
|
|
177
|
+
callback_Task: {},
|
|
178
|
+
wsReqNumber: 0,
|
|
179
|
+
wsTaskNumber: 0,
|
|
180
|
+
reqCallbacks: {}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const defaultFuncs = utils.makeDefaults(html, userID, ctx);
|
|
184
|
+
const api = {
|
|
185
|
+
setOptions: setOptions.bind(null, globalOptions),
|
|
186
|
+
getAppState: () => utils.getAppState(jar),
|
|
187
|
+
postFormData: function (url, body) {
|
|
188
|
+
return defaultFuncs.postFormData(url, ctx.jar, body);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
api.getFreshDtsg = async function () {
|
|
193
|
+
try {
|
|
194
|
+
const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
|
|
195
|
+
const $ = cheerio.load(res.body);
|
|
196
|
+
let newDtsg;
|
|
197
|
+
const patterns = [
|
|
198
|
+
/\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
|
|
199
|
+
/\["DTSGInitData",\[\],{"token":"([^"]+)"/,
|
|
200
|
+
/"token":"([^"]+)"/,
|
|
201
|
+
/name="fb_dtsg" value="([^"]+)"/
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
const scripts = $('script').get();
|
|
205
|
+
for (const script of scripts) {
|
|
206
|
+
const scriptText = $(script).html() || '';
|
|
207
|
+
for (const pattern of patterns) {
|
|
208
|
+
const match = scriptText.match(pattern);
|
|
209
|
+
if (match && match[1]) {
|
|
210
|
+
newDtsg = match[1];
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (newDtsg) break;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!newDtsg) {
|
|
218
|
+
newDtsg = $('input[name="fb_dtsg"]').val();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return newDtsg;
|
|
222
|
+
} catch (e) {
|
|
223
|
+
console.log("Error getting fresh dtsg:", e);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
require('fs').readdirSync(__dirname + '/src/')
|
|
229
|
+
.filter(v => v.endsWith('.js'))
|
|
230
|
+
.forEach(v => {
|
|
231
|
+
api[v.replace('.js', '')] = require(`./src/${v}`)(defaultFuncs, api, ctx);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
api.listen = api.listenMqtt;
|
|
235
|
+
return { ctx, defaultFuncs, api };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
|
|
239
|
+
return async function (res) {
|
|
240
|
+
try {
|
|
241
|
+
const html = res.body;
|
|
242
|
+
const $ = cheerio.load(html);
|
|
243
|
+
let arr = [];
|
|
244
|
+
$("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
|
|
245
|
+
arr = arr.filter(v => v.val && v.val.length);
|
|
246
|
+
const form = utils.arrToForm(arr);
|
|
247
|
+
form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
|
|
248
|
+
form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
|
|
249
|
+
form.email = email;
|
|
250
|
+
form.pass = password;
|
|
251
|
+
form.default_persistent = '0';
|
|
252
|
+
form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
|
|
253
|
+
form.locale = 'en_US';
|
|
254
|
+
form.timezone = '240';
|
|
255
|
+
form.lgnjs = Math.floor(Date.now() / 1000);
|
|
256
|
+
|
|
257
|
+
const willBeCookies = html.split("\"_js_");
|
|
258
|
+
willBeCookies.slice(1).forEach(val => {
|
|
259
|
+
try {
|
|
260
|
+
const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
|
|
261
|
+
jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
|
|
262
|
+
} catch(e){}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
log.info("login", "Logging in...");
|
|
266
|
+
const loginRes = await utils.post(
|
|
267
|
+
"https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
|
|
268
|
+
jar,
|
|
269
|
+
form,
|
|
270
|
+
loginOptions
|
|
271
|
+
);
|
|
272
|
+
await utils.saveCookies(jar)(loginRes);
|
|
273
|
+
const headers = loginRes.headers;
|
|
274
|
+
if (!headers.location) throw new Error("Wrong username/password.");
|
|
275
|
+
|
|
276
|
+
if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
|
|
277
|
+
log.info("login", "You have login approvals turned on.");
|
|
278
|
+
const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
|
|
279
|
+
await utils.saveCookies(jar)(checkpointRes);
|
|
280
|
+
const checkpointHtml = checkpointRes.body;
|
|
281
|
+
const $c = cheerio.load(checkpointHtml);
|
|
282
|
+
let checkpointForm = [];
|
|
283
|
+
$c("form input").each((i, v) => checkpointForm.push({ val: $c(v).val(), name: $c(v).attr("name") }));
|
|
284
|
+
checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
|
|
285
|
+
const form2fa = utils.arrToForm(checkpointForm);
|
|
286
|
+
|
|
287
|
+
if (checkpointHtml.includes("checkpoint/?next")) {
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
const submit2FA = async (code) => {
|
|
290
|
+
try {
|
|
291
|
+
form2fa.approvals_code = code;
|
|
292
|
+
form2fa['submit[Continue]'] = $c("#checkpointSubmitButton").html();
|
|
293
|
+
const approvalRes = await utils.post(
|
|
294
|
+
"https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
|
|
295
|
+
jar,
|
|
296
|
+
form2fa,
|
|
297
|
+
loginOptions
|
|
298
|
+
);
|
|
299
|
+
await utils.saveCookies(jar)(approvalRes);
|
|
300
|
+
const $a = cheerio.load(approvalRes.body);
|
|
301
|
+
const approvalError = $a("#approvals_code").parent().attr("data-xui-error");
|
|
302
|
+
if (approvalError) throw new Error("Invalid 2FA code.");
|
|
303
|
+
|
|
304
|
+
form2fa.name_action_selected = 'dont_save';
|
|
305
|
+
const finalRes = await utils.post(
|
|
306
|
+
"https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
|
|
307
|
+
jar,
|
|
308
|
+
form2fa,
|
|
309
|
+
loginOptions
|
|
310
|
+
);
|
|
311
|
+
await utils.saveCookies(jar)(finalRes);
|
|
312
|
+
const appState = utils.getAppState(jar);
|
|
313
|
+
resolve(await loginHelper(appState, email, password, loginOptions, callback, prCallback));
|
|
314
|
+
} catch (error) {
|
|
315
|
+
reject(error);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
throw {
|
|
319
|
+
error: 'login-approval',
|
|
320
|
+
continue: submit2FA
|
|
321
|
+
};
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (!loginOptions.forceLogin) throw new Error("Couldn't login. Facebook might have blocked this account.");
|
|
325
|
+
form2fa['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
|
|
326
|
+
await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form2fa, loginOptions);
|
|
327
|
+
form2fa.name_action_selected = 'save_device';
|
|
328
|
+
await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form2fa, loginOptions);
|
|
329
|
+
const appState = utils.getAppState(jar);
|
|
330
|
+
return await loginHelper(appState, email, password, loginOptions, callback, prCallback);
|
|
331
|
+
}
|
|
332
|
+
await utils.get('https://www.facebook.com/', jar, null, loginOptions);
|
|
333
|
+
return await utils.saveCookies(jar)(loginRes);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
callback(error);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
|
|
341
|
+
let mainPromise = null;
|
|
342
|
+
const jar = utils.getJar();
|
|
343
|
+
if (appState) {
|
|
344
|
+
try {
|
|
345
|
+
if (typeof appState === 'string') {
|
|
346
|
+
appState = JSON.parse(appState);
|
|
347
|
+
}
|
|
348
|
+
} catch (e) {
|
|
349
|
+
return callback(new Error("Failed to parse appState"));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
appState.forEach(c => {
|
|
354
|
+
const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
|
|
355
|
+
jar.setCookie(str, "https://" + c.domain);
|
|
356
|
+
});
|
|
357
|
+
mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
|
|
358
|
+
.then(utils.saveCookies(jar));
|
|
359
|
+
} catch (e) {
|
|
360
|
+
return callback(new Error("Failed to load appState cookies: " + e.message));
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
mainPromise = utils
|
|
364
|
+
.get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
|
|
365
|
+
.then(utils.saveCookies(jar))
|
|
366
|
+
.then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
|
|
367
|
+
.then(() => utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar)));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function handleRedirect(res) {
|
|
371
|
+
const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
|
|
372
|
+
const redirect = reg.exec(res.body);
|
|
373
|
+
if (redirect && redirect[1]) {
|
|
374
|
+
return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
|
|
375
|
+
}
|
|
376
|
+
return res;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
mainPromise = mainPromise
|
|
380
|
+
.then(handleRedirect)
|
|
381
|
+
.then(res => {
|
|
382
|
+
if (!/MPageLoadClientMetrics/gs.test(res.body)) {
|
|
383
|
+
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";
|
|
384
|
+
return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
|
|
385
|
+
}
|
|
386
|
+
return res;
|
|
387
|
+
})
|
|
388
|
+
.then(handleRedirect)
|
|
389
|
+
.then(res => {
|
|
390
|
+
const Obj = buildAPI(globalOptions, res.body, jar);
|
|
391
|
+
return Obj ? Obj.api : null;
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
if (globalOptions.pageID) {
|
|
395
|
+
mainPromise = mainPromise
|
|
396
|
+
.then((api) => utils.get(`https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`, jar, null, globalOptions).then(resData => ({api, resData})))
|
|
397
|
+
.then(({api, resData}) => {
|
|
398
|
+
let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
|
|
399
|
+
url = url.substring(0, url.length - 1);
|
|
400
|
+
return utils.get('https://www.facebook.com' + url, jar, null, globalOptions).then(() => api);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
mainPromise
|
|
405
|
+
.then((api) => {
|
|
406
|
+
if(api) {
|
|
407
|
+
log.info('login', 'Đăng nhập thành công');
|
|
408
|
+
callback(null, api);
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
.catch(e => {
|
|
412
|
+
callback(e);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function login(loginData, options, callback) {
|
|
417
|
+
if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
|
|
418
|
+
callback = options;
|
|
419
|
+
options = {};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const globalOptions = {
|
|
423
|
+
selfListen: false,
|
|
424
|
+
listenEvents: true,
|
|
425
|
+
listenTyping: false,
|
|
426
|
+
updatePresence: false,
|
|
427
|
+
forceLogin: false,
|
|
428
|
+
autoMarkDelivery: false,
|
|
429
|
+
autoMarkRead: false,
|
|
430
|
+
autoReconnect: true,
|
|
431
|
+
logRecordSize: 100,
|
|
432
|
+
online: false,
|
|
433
|
+
emitReady: false,
|
|
434
|
+
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
let prCallback = null;
|
|
438
|
+
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
|
|
439
|
+
let rejectFunc = null;
|
|
440
|
+
let resolveFunc = null;
|
|
441
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
442
|
+
resolveFunc = resolve;
|
|
443
|
+
rejectFunc = reject;
|
|
444
|
+
});
|
|
445
|
+
prCallback = function (error, api) {
|
|
446
|
+
if (error) return rejectFunc(error);
|
|
447
|
+
return resolveFunc(api);
|
|
448
|
+
};
|
|
449
|
+
callback = prCallback;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (loginData.email && loginData.password) {
|
|
453
|
+
setOptions(globalOptions, {
|
|
454
|
+
logLevel: "silent",
|
|
455
|
+
forceLogin: true,
|
|
456
|
+
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
|
457
|
+
});
|
|
458
|
+
loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
|
|
459
|
+
} else if (loginData.appState) {
|
|
460
|
+
setOptions(globalOptions, options);
|
|
461
|
+
loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return prCallback ? returnPromise : null;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
module.exports = login;
|
package/package.json
ADDED
package/replit.nix
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require("../utils");
|
|
4
|
+
|
|
5
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
6
|
+
return function addExternalModule(moduleObj) {
|
|
7
|
+
const moduleType = utils.getType(moduleObj);
|
|
8
|
+
|
|
9
|
+
if (moduleType !== "Object") {
|
|
10
|
+
throw new Error(`moduleObj must be an object, not ${moduleType}!`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Object.keys(moduleObj).forEach((apiName) => {
|
|
14
|
+
const functionType = utils.getType(moduleObj[apiName]);
|
|
15
|
+
|
|
16
|
+
if (functionType === "Function") {
|
|
17
|
+
api[apiName] = moduleObj[apiName](defaultFuncs, api, ctx);
|
|
18
|
+
} else {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Item "${apiName}" in moduleObj must be a function, not ${functionType}!`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require("../utils");
|
|
4
|
+
|
|
5
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
6
|
+
return function addUserToGroup(userID, threadID, callback) {
|
|
7
|
+
let resolveFunc = () => {};
|
|
8
|
+
let rejectFunc = () => {};
|
|
9
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
10
|
+
resolveFunc = resolve;
|
|
11
|
+
rejectFunc = reject;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const threadIDType = utils.getType(threadID);
|
|
15
|
+
|
|
16
|
+
if (!callback && (threadIDType === "Function" || threadIDType === "AsyncFunction")) {
|
|
17
|
+
throw new utils.CustomError({
|
|
18
|
+
error: "please pass a threadID as a second argument.",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!callback) {
|
|
23
|
+
callback = function (err) {
|
|
24
|
+
if (err) return rejectFunc(err);
|
|
25
|
+
resolveFunc();
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (threadIDType !== "Number" && threadIDType !== "String") {
|
|
30
|
+
throw new utils.CustomError({
|
|
31
|
+
error: `ThreadID should be of type Number or String and not ${threadIDType}.`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// userID অ্যারে না হলে অ্যারেতে কনভার্ট করা হচ্ছে
|
|
36
|
+
if (utils.getType(userID) !== "Array") {
|
|
37
|
+
userID = [userID];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const messageAndOTID = utils.generateOfflineThreadingID();
|
|
41
|
+
const form = {
|
|
42
|
+
client: "mercury",
|
|
43
|
+
action_type: "ma-type:log-message",
|
|
44
|
+
author: "fbid:" + ctx.userID,
|
|
45
|
+
thread_id: "",
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
timestamp_absolute: "Today",
|
|
48
|
+
timestamp_relative: utils.generateTimestampRelative(),
|
|
49
|
+
timestamp_time_passed: "0",
|
|
50
|
+
is_unread: false,
|
|
51
|
+
is_cleared: false,
|
|
52
|
+
is_forward: false,
|
|
53
|
+
is_filtered_content: false,
|
|
54
|
+
is_filtered_content_bh: false,
|
|
55
|
+
is_filtered_content_account: false,
|
|
56
|
+
is_spoof_warning: false,
|
|
57
|
+
source: "source:chat:web",
|
|
58
|
+
"source_tags[0]": "source:chat",
|
|
59
|
+
log_message_type: "log:subscribe",
|
|
60
|
+
status: "0",
|
|
61
|
+
offline_threading_id: messageAndOTID,
|
|
62
|
+
message_id: messageAndOTID,
|
|
63
|
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
|
64
|
+
manual_retry_cnt: "0",
|
|
65
|
+
thread_fbid: threadID,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// userID ভ্যালিডেশন এবং ফর্ম ডেটা বিল্ড
|
|
69
|
+
userID.forEach((id, index) => {
|
|
70
|
+
const idType = utils.getType(id);
|
|
71
|
+
if (idType !== "Number" && idType !== "String") {
|
|
72
|
+
throw new utils.CustomError({
|
|
73
|
+
error: `Elements of userID should be of type Number or String and not ${idType}.`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
form[`log_message_data[added_participants][${index}]`] = "fbid:" + id;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
defaultFuncs
|
|
80
|
+
.post("https://www.facebook.com/messaging/send/", ctx.jar, form)
|
|
81
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
82
|
+
.then(function (resData) {
|
|
83
|
+
if (!resData) {
|
|
84
|
+
throw new utils.CustomError({ error: "Add to group failed." });
|
|
85
|
+
}
|
|
86
|
+
if (resData.error) {
|
|
87
|
+
throw new utils.CustomError(resData);
|
|
88
|
+
}
|
|
89
|
+
return callback();
|
|
90
|
+
})
|
|
91
|
+
.catch(function (err) {
|
|
92
|
+
utils.error("addUserToGroup", err);
|
|
93
|
+
return callback(err);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return returnPromise;
|
|
97
|
+
};
|
|
98
|
+
};
|