@cexy/hoonfca 1.0.0 → 1.0.1
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/LICENSE +3 -0
- package/README.md +451 -43
- package/examples/login-with-cookies.js +102 -0
- package/examples/verify.js +70 -0
- package/index.js +1 -466
- package/package.json +98 -6
- package/src/apis/addExternalModule.js +24 -0
- package/src/apis/addUserToGroup.js +108 -0
- package/src/apis/changeAdminStatus.js +148 -0
- package/src/apis/changeArchivedStatus.js +61 -0
- package/src/apis/changeAvatar.js +103 -0
- package/src/apis/changeBio.js +69 -0
- package/src/apis/changeBlockedStatus.js +54 -0
- package/src/apis/changeGroupImage.js +136 -0
- package/src/apis/changeThreadColor.js +116 -0
- package/src/{changeThreadEmoji.js → apis/changeThreadEmoji.js} +7 -8
- package/src/apis/comment.js +207 -0
- package/src/apis/createAITheme.js +129 -0
- package/src/apis/createNewGroup.js +79 -0
- package/src/apis/createPoll.js +73 -0
- package/src/apis/deleteMessage.js +44 -0
- package/src/apis/deleteThread.js +52 -0
- package/src/apis/e2ee.js +17 -0
- package/src/apis/editMessage.js +70 -0
- package/src/apis/emoji.js +124 -0
- package/src/apis/fetchThemeData.js +82 -0
- package/src/apis/follow.js +81 -0
- package/src/apis/forwardMessage.js +52 -0
- package/src/apis/friend.js +243 -0
- package/src/apis/gcmember.js +122 -0
- package/src/apis/gcname.js +123 -0
- package/src/apis/gcrule.js +119 -0
- package/src/apis/getAccess.js +111 -0
- package/src/apis/getBotInfo.js +88 -0
- package/src/apis/getBotInitialData.js +43 -0
- package/src/apis/getFriendsList.js +79 -0
- package/src/apis/getMessage.js +423 -0
- package/src/apis/getTheme.js +95 -0
- package/src/apis/getThemeInfo.js +116 -0
- package/src/apis/getThreadHistory.js +239 -0
- package/src/apis/getThreadInfo.js +267 -0
- package/src/apis/getThreadList.js +232 -0
- package/src/apis/getThreadPictures.js +58 -0
- package/src/apis/getUserID.js +117 -0
- package/src/apis/getUserInfo.js +513 -0
- package/src/apis/getUserInfoV2.js +146 -0
- package/src/apis/handleMessageRequest.js +50 -0
- package/src/apis/httpGet.js +63 -0
- package/src/apis/httpPost.js +89 -0
- package/src/apis/httpPostFormData.js +69 -0
- package/src/apis/listenMqtt.js +1236 -0
- package/src/apis/listenSpeed.js +179 -0
- package/src/apis/logout.js +87 -0
- package/src/apis/markAsDelivered.js +47 -0
- package/src/apis/markAsRead.js +99 -0
- package/src/apis/markAsReadAll.js +40 -0
- package/src/apis/markAsSeen.js +70 -0
- package/src/apis/mqttDeltaValue.js +278 -0
- package/src/apis/muteThread.js +45 -0
- package/src/apis/nickname.js +132 -0
- package/src/apis/notes.js +163 -0
- package/src/apis/pinMessage.js +150 -0
- package/src/apis/produceMetaTheme.js +180 -0
- package/src/apis/realtime.js +182 -0
- package/src/apis/removeUserFromGroup.js +117 -0
- package/src/apis/resolvePhotoUrl.js +58 -0
- package/src/apis/searchForThread.js +154 -0
- package/src/apis/sendMessage.js +354 -0
- package/src/apis/sendMessageMqtt.js +249 -0
- package/src/apis/sendTypingIndicator.js +91 -0
- package/src/apis/setMessageReaction.js +27 -0
- package/src/apis/setMessageReactionMqtt.js +61 -0
- package/src/apis/setThreadTheme.js +260 -0
- package/src/apis/setThreadThemeMqtt.js +94 -0
- package/src/apis/share.js +107 -0
- package/src/apis/shareContact.js +66 -0
- package/src/apis/stickers.js +257 -0
- package/src/apis/story.js +181 -0
- package/src/apis/theme.js +233 -0
- package/src/apis/unfriend.js +47 -0
- package/src/apis/unsendMessage.js +17 -0
- package/src/engine/client.js +92 -0
- package/src/engine/models/buildAPI.js +152 -0
- package/src/engine/models/loginHelper.js +519 -0
- package/src/engine/models/setOptions.js +88 -0
- package/src/security/e2ee.js +109 -0
- package/src/types/index.d.ts +498 -0
- package/src/utils/antiSuspension.js +506 -0
- package/src/utils/auth-helpers.js +149 -0
- package/src/utils/autoReLogin.js +336 -0
- package/src/utils/axios.js +436 -0
- package/src/utils/cache.js +54 -0
- package/src/utils/clients.js +282 -0
- package/src/utils/constants.js +410 -0
- package/src/utils/formatters/data/formatAttachment.js +370 -0
- package/src/utils/formatters/data/formatDelta.js +109 -0
- package/src/utils/formatters/index.js +159 -0
- package/src/utils/formatters/value/formatCookie.js +91 -0
- package/src/utils/formatters/value/formatDate.js +36 -0
- package/src/utils/formatters/value/formatID.js +16 -0
- package/src/utils/formatters.js +1373 -0
- package/src/utils/headers.js +230 -0
- package/src/utils/index.js +153 -0
- package/src/utils/monitoring.js +333 -0
- package/src/utils/rateLimiter.js +319 -0
- package/src/utils/tokenRefresh.js +657 -0
- package/src/utils/user-agents.js +238 -0
- package/src/utils/validation.js +157 -0
- package/replit.nix +0 -3
- package/src/addExternalModule.js +0 -25
- package/src/addUserToGroup.js +0 -98
- package/src/changeAdminStatus.js +0 -98
- package/src/changeArchivedStatus.js +0 -51
- package/src/changeAvatar.js +0 -89
- package/src/changeBio.js +0 -74
- package/src/changeBlockedStatus.js +0 -45
- package/src/changeBlockedStatusMqtt.js +0 -65
- package/src/changeCover.js +0 -92
- package/src/changeGroupImage.js +0 -110
- package/src/changeName.js +0 -106
- package/src/changeNickname.js +0 -58
- package/src/changeThreadColor.js +0 -62
- package/src/changeUsername.js +0 -80
- package/src/createCommentPost.js +0 -196
- package/src/createNewGroup.js +0 -81
- package/src/createPoll.js +0 -64
- package/src/createPost.js +0 -285
- package/src/data/getThreadInfo.json +0 -1
- package/src/deleteMessage.js +0 -55
- package/src/deleteThread.js +0 -55
- package/src/editMessage.js +0 -66
- package/src/follow.js +0 -71
- package/src/forwardAttachment.js +0 -57
- package/src/getAccess.js +0 -130
- package/src/getAvatarUser.js +0 -88
- package/src/getBotInitialData.js +0 -59
- package/src/getCtx.js +0 -5
- package/src/getCurrentUserID.js +0 -5
- package/src/getEmojiUrl.js +0 -18
- package/src/getFriendsList.js +0 -81
- package/src/getMessage.js +0 -827
- package/src/getOptions.js +0 -5
- package/src/getRegion.js +0 -7
- package/src/getThreadHistory.js +0 -496
- package/src/getThreadHistoryDeprecated.js +0 -95
- package/src/getThreadInfo.js +0 -194
- package/src/getThreadInfoDeprecated.js +0 -77
- package/src/getThreadList.js +0 -219
- package/src/getThreadListDeprecated.js +0 -86
- package/src/getThreadPictures.js +0 -85
- package/src/getUserID.js +0 -67
- package/src/getUserInfo.js +0 -89
- package/src/handleFriendRequest.js +0 -56
- package/src/handleMessageRequest.js +0 -65
- package/src/httpGet.js +0 -53
- package/src/httpPost.js +0 -53
- package/src/httpPostFormData.js +0 -58
- package/src/listenMqtt.js +0 -683
- package/src/listenNotification.js +0 -96
- package/src/logout.js +0 -77
- package/src/markAsDelivered.js +0 -53
- package/src/markAsRead.js +0 -74
- package/src/markAsReadAll.js +0 -46
- package/src/markAsSeen.js +0 -57
- package/src/muteThread.js +0 -49
- package/src/pinMessage.js +0 -60
- package/src/refreshFb_dtsg.js +0 -75
- package/src/removeUserFromGroup.js +0 -59
- package/src/resolvePhotoUrl.js +0 -47
- package/src/searchForThread.js +0 -55
- package/src/searchStickers.js +0 -59
- package/src/sendMessage.js +0 -444
- package/src/sendMessageMqtt.js +0 -326
- package/src/sendTypingIndicator.js +0 -54
- package/src/setMessageReaction.js +0 -126
- package/src/setMessageReactionMqtt.js +0 -74
- package/src/setPostReaction.js +0 -103
- package/src/setProfileGuard.js +0 -66
- package/src/setStoryReaction.js +0 -97
- package/src/setTitle.js +0 -100
- package/src/shareContact.js +0 -101
- package/src/shareLink.js +0 -105
- package/src/stopListenMqtt.js +0 -47
- package/src/threadColors.js +0 -119
- package/src/unfriend.js +0 -58
- package/src/unsendMessage.js +0 -51
- package/src/uploadAttachment.js +0 -98
- package/utils.js +0 -919
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Quick sanity check — verifies the library loads correctly and
|
|
6
|
+
* prints the anti-suspension configuration. Run with:
|
|
7
|
+
* node examples/verify.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fca = require('../index.js');
|
|
11
|
+
const { globalAntiSuspension } = require('../src/utils/antiSuspension');
|
|
12
|
+
const { globalRateLimiter } = require('../src/utils/rateLimiter');
|
|
13
|
+
|
|
14
|
+
console.log('nkxfca Library Verification');
|
|
15
|
+
console.log('================================\n');
|
|
16
|
+
|
|
17
|
+
console.log('Library entry point:', typeof fca.login === 'function' ? 'OK' : 'FAIL');
|
|
18
|
+
|
|
19
|
+
const config = globalAntiSuspension.getConfig();
|
|
20
|
+
console.log('\nAnti-Suspension Configuration:');
|
|
21
|
+
console.log(` Message Delay : ${config.messageDelayMs}ms`);
|
|
22
|
+
console.log(` Thread Delay : ${config.threadDelayMs}ms`);
|
|
23
|
+
console.log(` Max Login Tries : ${config.maxLoginAttempts}`);
|
|
24
|
+
console.log(` Login Cooldown : ${config.loginCooldownMs}ms`);
|
|
25
|
+
console.log(` Daily Msg Limit : ${config.dailyStats.maxDailyMessages}`);
|
|
26
|
+
console.log(` Hourly Msg Limit : ${config.hourlyStats.maxPerHour}`);
|
|
27
|
+
|
|
28
|
+
console.log('\nEnabled Features:');
|
|
29
|
+
Object.entries(config.features).forEach(([feature, enabled]) => {
|
|
30
|
+
console.log(` ${enabled ? '[x]' : '[ ]'} ${feature}`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const activityPattern = globalAntiSuspension.getRealisticActivityPattern();
|
|
34
|
+
console.log('\nActivity Pattern:');
|
|
35
|
+
console.log(` Current activity : ${activityPattern.messageFrequency}`);
|
|
36
|
+
console.log(` Next action delay : ${activityPattern.nextActionDelayMs.toFixed(0)}ms`);
|
|
37
|
+
console.log(` Is active hours : ${activityPattern.isActiveHours}`);
|
|
38
|
+
|
|
39
|
+
console.log('\nCircuit Breaker:');
|
|
40
|
+
console.log(` Tripped : ${globalAntiSuspension.isCircuitBreakerTripped()}`);
|
|
41
|
+
console.log(` Signal count : ${globalAntiSuspension.suspensionCircuitBreaker.signalCount}`);
|
|
42
|
+
|
|
43
|
+
const rateLimiterStats = globalRateLimiter.getStats();
|
|
44
|
+
console.log('\nRate Limiter:');
|
|
45
|
+
console.log(` Max concurrent : ${rateLimiterStats.maxConcurrentRequests}`);
|
|
46
|
+
console.log(` Max per minute : ${rateLimiterStats.maxRequestsPerMinute}`);
|
|
47
|
+
console.log(` Requests (1 min) : ${rateLimiterStats.requestsInLastMinute}`);
|
|
48
|
+
|
|
49
|
+
// Test suspension signal detection
|
|
50
|
+
const testSignals = [
|
|
51
|
+
{ text: 'Everything is fine, message sent', expectSuspicion: false },
|
|
52
|
+
{ text: 'Your account has been suspended due to policy violation', expectSuspicion: true },
|
|
53
|
+
{ text: 'checkpoint required to verify identity', expectSuspicion: true },
|
|
54
|
+
{ text: 'Too many requests - rate limited', expectSuspicion: true },
|
|
55
|
+
{ text: 'Unusual activity detected on your account', expectSuspicion: true },
|
|
56
|
+
];
|
|
57
|
+
console.log('\nSuspension Signal Detection:');
|
|
58
|
+
testSignals.forEach(({ text, expectSuspicion }) => {
|
|
59
|
+
globalAntiSuspension.resetCircuitBreaker();
|
|
60
|
+
const detected = globalAntiSuspension.detectSuspensionSignal(text);
|
|
61
|
+
const passed = detected === expectSuspicion;
|
|
62
|
+
globalAntiSuspension.resetCircuitBreaker();
|
|
63
|
+
console.log(` ${passed ? '[x]' : '[!]'} "${text.substring(0, 40)}" → ${detected ? 'SUSPICIOUS' : 'CLEAN'}`);
|
|
64
|
+
});
|
|
65
|
+
globalAntiSuspension.resetCircuitBreaker();
|
|
66
|
+
|
|
67
|
+
console.log('\nAll checks passed. Library is ready to use.');
|
|
68
|
+
console.log('============================\n');
|
|
69
|
+
|
|
70
|
+
process.exit(0);
|
package/index.js
CHANGED
|
@@ -1,467 +1,2 @@
|
|
|
1
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;
|
|
2
|
+
module.exports = require('./src/engine/client');
|
package/package.json
CHANGED
|
@@ -1,12 +1,104 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cexy/hoonfca",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"types": "src/types/index.d.ts",
|
|
6
|
+
"description": "Hinata FCA - Advanced Facebook Chat API",
|
|
5
7
|
"main": "index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js",
|
|
10
|
+
"src/",
|
|
11
|
+
"examples/",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/c9352220sketch/h.git"
|
|
18
|
+
},
|
|
19
|
+
"author": "c9352220sketch",
|
|
20
|
+
"contributors": [
|
|
21
|
+
"NeoKEX Team"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/c9352220sketch/hinata-fca/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/c9352220sketch/hinata-fca#readme",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"facebook",
|
|
30
|
+
"messenger",
|
|
31
|
+
"chat",
|
|
32
|
+
"bot",
|
|
33
|
+
"fca",
|
|
34
|
+
"mqtt",
|
|
35
|
+
"messaging",
|
|
36
|
+
"api",
|
|
37
|
+
"automation",
|
|
38
|
+
"facebook-chat-api",
|
|
39
|
+
"nkxfca",
|
|
40
|
+
"neoaz07",
|
|
41
|
+
"anti-suspension",
|
|
42
|
+
"e2ee"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"axios": "^1.9.0",
|
|
46
|
+
"axios-cookiejar-support": "^4.0.7",
|
|
47
|
+
"bluebird": "^3.7.2",
|
|
48
|
+
"cheerio": "^1.0.0",
|
|
49
|
+
"deepdash": "^5.3.9",
|
|
50
|
+
"duplexify": "^4.1.3",
|
|
51
|
+
"form-data": "^4.0.4",
|
|
52
|
+
"https-proxy-agent": "^7.0.6",
|
|
53
|
+
"jsonpath-plus": "^10.3.0",
|
|
54
|
+
"lodash": "^4.17.21",
|
|
55
|
+
"mqtt": "^4.3.8",
|
|
56
|
+
"node-cron": "^3.0.3",
|
|
57
|
+
"npmlog": "^7.0.1",
|
|
58
|
+
"totp-generator": "^2.0.1",
|
|
59
|
+
"tough-cookie": "^5.1.2",
|
|
60
|
+
"undici": "^7.8.0",
|
|
61
|
+
"uuid": "^11.1.0",
|
|
62
|
+
"ws": "^8.18.2"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^22.15.17",
|
|
66
|
+
"eslint": "^9.26.0",
|
|
67
|
+
"mocha": "^11.3.0",
|
|
68
|
+
"prettier": "^3.5.3",
|
|
69
|
+
"ts-node": "^10.9.2",
|
|
70
|
+
"typescript": "^5.8.3"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=22.0.0"
|
|
74
|
+
},
|
|
6
75
|
"scripts": {
|
|
7
|
-
"
|
|
76
|
+
"validate": "npm pack --dry-run",
|
|
77
|
+
"prepack": "echo 'Preparing package for npm...'",
|
|
78
|
+
"test": "echo 'No tests configured yet'",
|
|
79
|
+
"lint": "eslint src/",
|
|
80
|
+
"format": "prettier --write src/**/*.js"
|
|
8
81
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
82
|
+
"overrides": {
|
|
83
|
+
"undici": "^7.8.0",
|
|
84
|
+
"ws": "^8.18.2",
|
|
85
|
+
"tough-cookie": "^5.1.2",
|
|
86
|
+
"glob": "^11.0.2",
|
|
87
|
+
"prebuild-install": "^7.1.3",
|
|
88
|
+
"npmlog": "^7.0.1",
|
|
89
|
+
"are-we-there-yet": "^4.0.2",
|
|
90
|
+
"gauge": "^5.0.2",
|
|
91
|
+
"serialize-javascript": "^6.0.2",
|
|
92
|
+
"cacache": "^18.0.4",
|
|
93
|
+
"make-fetch-happen": "^13.0.3",
|
|
94
|
+
"@tootallnate/once": "^3.0.1",
|
|
95
|
+
"mocha": {
|
|
96
|
+
"serialize-javascript": "^6.0.2",
|
|
97
|
+
"debug": "^4.4.0",
|
|
98
|
+
"diff": "^7.0.0",
|
|
99
|
+
"js-yaml": "^4.1.0",
|
|
100
|
+
"minimatch": "^10.0.1",
|
|
101
|
+
"glob": "^11.0.2"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
12
104
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const utils = require('../utils');
|
|
3
|
+
|
|
4
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
5
|
+
return function addExternalModule(moduleObj) {
|
|
6
|
+
if (utils.getType(moduleObj) == "Object") {
|
|
7
|
+
for (const apiName in moduleObj) {
|
|
8
|
+
if (utils.getType(moduleObj[apiName]) == "Function") {
|
|
9
|
+
api[apiName] = moduleObj[apiName](defaultFuncs, api, ctx);
|
|
10
|
+
} else {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Item "${apiName}" in moduleObj must be a function, not ${utils.getType(
|
|
13
|
+
moduleObj[apiName],
|
|
14
|
+
)}!`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`moduleObj must be an object, not ${utils.getType(moduleObj)}!`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|