fca-neokex-fix 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/CHANGELOG.md +220 -0
- package/LICENSE +26 -0
- package/README.md +346 -0
- package/THEME_FEATURES.md +137 -0
- package/examples/README.md +131 -0
- package/examples/apply-ai-theme.js +127 -0
- package/examples/check-current-theme.js +74 -0
- package/examples/simple-bot.js +114 -0
- package/examples/test-bot.js +752 -0
- package/examples/test-logging.js +85 -0
- package/examples/theme-usage-example.js +53 -0
- package/index.js +2 -0
- package/package.json +105 -0
- 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/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/editMessage.js +70 -0
- package/src/apis/emoji.js +124 -0
- package/src/apis/fetchThemeData.js +65 -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 +104 -0
- package/src/apis/getThemeInfo.js +96 -0
- package/src/apis/getThreadHistory.js +239 -0
- package/src/apis/getThreadInfo.js +257 -0
- package/src/apis/getThreadList.js +222 -0
- package/src/apis/getThreadPictures.js +58 -0
- package/src/apis/getUserID.js +83 -0
- package/src/apis/getUserInfo.js +495 -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 +796 -0
- package/src/apis/listenSpeed.js +170 -0
- package/src/apis/logout.js +63 -0
- package/src/apis/markAsDelivered.js +47 -0
- package/src/apis/markAsRead.js +95 -0
- package/src/apis/markAsReadAll.js +41 -0
- package/src/apis/markAsSeen.js +70 -0
- package/src/apis/mqttDeltaValue.js +330 -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 +141 -0
- package/src/apis/produceMetaTheme.js +180 -0
- package/src/apis/realtime.js +161 -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 +281 -0
- package/src/apis/sendMessageMqtt.js +188 -0
- package/src/apis/sendTypingIndicator.js +41 -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/database/appStateBackup.js +189 -0
- package/src/database/models/index.js +56 -0
- package/src/database/models/thread.js +31 -0
- package/src/database/models/user.js +32 -0
- package/src/database/threadData.js +101 -0
- package/src/database/userData.js +90 -0
- package/src/engine/client.js +91 -0
- package/src/engine/models/buildAPI.js +109 -0
- package/src/engine/models/loginHelper.js +326 -0
- package/src/engine/models/setOptions.js +53 -0
- package/src/utils/auth-helpers.js +149 -0
- package/src/utils/autoReLogin.js +169 -0
- package/src/utils/axios.js +290 -0
- package/src/utils/clients.js +270 -0
- package/src/utils/constants.js +396 -0
- package/src/utils/formatters/data/formatAttachment.js +370 -0
- package/src/utils/formatters/data/formatDelta.js +153 -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 +1067 -0
- package/src/utils/headers.js +199 -0
- package/src/utils/index.js +151 -0
- package/src/utils/monitoring.js +358 -0
- package/src/utils/rateLimiter.js +380 -0
- package/src/utils/tokenRefresh.js +311 -0
- package/src/utils/user-agents.js +238 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const utils = require('../src/utils/constants');
|
|
2
|
+
const { globalMonitor } = require('../src/utils/monitoring');
|
|
3
|
+
|
|
4
|
+
console.log('\n╔══════════════════════════════════════════════════════════════╗');
|
|
5
|
+
console.log('║ NeoKEX-FCA Enhanced Logging System Demo ║');
|
|
6
|
+
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
7
|
+
|
|
8
|
+
console.log('📝 Testing Different Log Levels:\n');
|
|
9
|
+
|
|
10
|
+
utils.trace('This is a trace message - most verbose level');
|
|
11
|
+
utils.debug('This is a debug message - for development info');
|
|
12
|
+
utils.info('This is an info message - general information');
|
|
13
|
+
utils.log('This is a standard log message');
|
|
14
|
+
utils.success('This is a success message - operation completed!');
|
|
15
|
+
utils.warn('This is a warning message - something needs attention');
|
|
16
|
+
utils.error('This is an error message - something went wrong');
|
|
17
|
+
utils.critical('This is a critical error - system failure!');
|
|
18
|
+
|
|
19
|
+
console.log('\n\n🎨 Testing Visual Elements:\n');
|
|
20
|
+
|
|
21
|
+
utils.banner('Welcome to NeoKEX-FCA', 'cyan');
|
|
22
|
+
|
|
23
|
+
console.log('\n');
|
|
24
|
+
|
|
25
|
+
utils.box('System Information', [
|
|
26
|
+
'Version: 4.5.1',
|
|
27
|
+
'Node Version: ' + process.version,
|
|
28
|
+
'Platform: ' + process.platform
|
|
29
|
+
], 'info');
|
|
30
|
+
|
|
31
|
+
console.log('\n');
|
|
32
|
+
|
|
33
|
+
utils.box('Success Report', [
|
|
34
|
+
'Login successful',
|
|
35
|
+
'Connection established',
|
|
36
|
+
'All systems operational'
|
|
37
|
+
], 'success');
|
|
38
|
+
|
|
39
|
+
console.log('\n');
|
|
40
|
+
|
|
41
|
+
utils.box('Warning Alert', [
|
|
42
|
+
'Rate limit approaching',
|
|
43
|
+
'High memory usage detected'
|
|
44
|
+
], 'warn');
|
|
45
|
+
|
|
46
|
+
console.log('\n');
|
|
47
|
+
|
|
48
|
+
utils.box('Error Details', [
|
|
49
|
+
'Failed to connect to server',
|
|
50
|
+
'Retrying in 5 seconds...'
|
|
51
|
+
], 'error');
|
|
52
|
+
|
|
53
|
+
console.log('\n\n📊 Testing Monitoring System:\n');
|
|
54
|
+
|
|
55
|
+
globalMonitor.trackRequest('/api/login', true, 150);
|
|
56
|
+
globalMonitor.trackRequest('/api/messages', true, 250);
|
|
57
|
+
globalMonitor.trackRequest('/api/send', false, 500, new Error('Rate limit exceeded'));
|
|
58
|
+
globalMonitor.trackRequest('/api/threads', true, 6000);
|
|
59
|
+
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
console.log('\n');
|
|
62
|
+
globalMonitor.displayHealthStatus();
|
|
63
|
+
|
|
64
|
+
console.log('\n📌 Testing Log Level Control:\n');
|
|
65
|
+
|
|
66
|
+
console.log('Setting log level to "warn" (only warnings, errors, and success will show):\n');
|
|
67
|
+
utils.setLogLevel('warn');
|
|
68
|
+
|
|
69
|
+
utils.trace('This trace will NOT be shown');
|
|
70
|
+
utils.debug('This debug will NOT be shown');
|
|
71
|
+
utils.info('This info will NOT be shown');
|
|
72
|
+
utils.log('This log will NOT be shown');
|
|
73
|
+
utils.success('This success WILL be shown (success always shows!)');
|
|
74
|
+
utils.warn('This warning WILL be shown');
|
|
75
|
+
utils.error('This error WILL be shown');
|
|
76
|
+
|
|
77
|
+
console.log('\n\nResetting log level to "info":\n');
|
|
78
|
+
utils.setLogLevel('info');
|
|
79
|
+
|
|
80
|
+
utils.info('All log levels from info and above are now visible again!');
|
|
81
|
+
|
|
82
|
+
console.log('\n\n╔══════════════════════════════════════════════════════════════╗');
|
|
83
|
+
console.log('║ Enhanced Logging Demo Complete! ✨ ║');
|
|
84
|
+
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
85
|
+
}, 100);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { login } = require("../index");
|
|
3
|
+
|
|
4
|
+
const credentials = { appState: JSON.parse(fs.readFileSync("appstate.json", "utf8")) };
|
|
5
|
+
|
|
6
|
+
login(credentials, async (err, api) => {
|
|
7
|
+
if (err) return console.error(err);
|
|
8
|
+
|
|
9
|
+
const threadID = "YOUR_THREAD_ID";
|
|
10
|
+
|
|
11
|
+
api.listenMqtt((err) => {
|
|
12
|
+
if (err) console.error(err);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
16
|
+
|
|
17
|
+
console.log("Getting all available themes...");
|
|
18
|
+
const themes = await api.getTheme(threadID);
|
|
19
|
+
console.log(`Found ${themes.length} themes!`);
|
|
20
|
+
themes.slice(0, 5).forEach(theme => {
|
|
21
|
+
console.log(`- ${theme.name} (ID: ${theme.id})`);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log("\nGetting current thread theme...");
|
|
25
|
+
const currentTheme = await api.getThemeInfo(threadID);
|
|
26
|
+
console.log(`Thread: ${currentTheme.threadName}`);
|
|
27
|
+
console.log(`Color: ${currentTheme.color}`);
|
|
28
|
+
console.log(`Emoji: ${currentTheme.emoji}`);
|
|
29
|
+
|
|
30
|
+
console.log("\nApplying a standard theme...");
|
|
31
|
+
const blueTheme = themes.find(t => t.name.includes("Blue") || t.id === "1440238847056619");
|
|
32
|
+
if (blueTheme) {
|
|
33
|
+
await api.setThreadThemeMqtt(threadID, blueTheme.id);
|
|
34
|
+
console.log(`Applied theme: ${blueTheme.name}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log("\nTrying AI theme generation...");
|
|
38
|
+
try {
|
|
39
|
+
const aiThemes = await api.createAITheme("ocean sunset vibes");
|
|
40
|
+
console.log("AI theme generated!");
|
|
41
|
+
await api.setThreadThemeMqtt(threadID, aiThemes[0].id);
|
|
42
|
+
console.log("AI theme applied!");
|
|
43
|
+
} catch (e) {
|
|
44
|
+
if (e.code === 'FEATURE_UNAVAILABLE') {
|
|
45
|
+
console.log("AI themes not available for this account.");
|
|
46
|
+
console.log("You can still use all standard themes!");
|
|
47
|
+
} else {
|
|
48
|
+
console.error("Error:", e.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fca-neokex-fix",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"types": "src/types/index.d.ts",
|
|
6
|
+
"description": "Advanced Facebook Chat API (FCA) for Node.js - Bug-fixed fork of neokex-fca with memory leak fixes, proper reconnection handling, and production-ready stability",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js",
|
|
10
|
+
"src/",
|
|
11
|
+
"examples/",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"THEME_FEATURES.md"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/NeoKEX/fca-neokex.git"
|
|
20
|
+
},
|
|
21
|
+
"author": "NeoKEX",
|
|
22
|
+
"contributors": [
|
|
23
|
+
{
|
|
24
|
+
"name": "NeoKEX Team",
|
|
25
|
+
"url": "original authors"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/NeoKEX/fca-neokex/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/NeoKEX/fca-neokex#readme",
|
|
33
|
+
"keywords": [
|
|
34
|
+
"facebook",
|
|
35
|
+
"chat",
|
|
36
|
+
"api",
|
|
37
|
+
"fca",
|
|
38
|
+
"fca-neokex",
|
|
39
|
+
"neokex-fca",
|
|
40
|
+
"messenger",
|
|
41
|
+
"bot",
|
|
42
|
+
"automation",
|
|
43
|
+
"facebook-messenger",
|
|
44
|
+
"facebook-chat",
|
|
45
|
+
"messenger-bot",
|
|
46
|
+
"ai-theme",
|
|
47
|
+
"mqtt",
|
|
48
|
+
"realtime",
|
|
49
|
+
"websocket",
|
|
50
|
+
"facebook-api",
|
|
51
|
+
"chat-bot",
|
|
52
|
+
"messaging",
|
|
53
|
+
"bug-fix",
|
|
54
|
+
"production-ready",
|
|
55
|
+
"stable"
|
|
56
|
+
],
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"axios": "^1.13.2",
|
|
59
|
+
"axios-cookiejar-support": "^6.0.4",
|
|
60
|
+
"bluebird": "^3.7.2",
|
|
61
|
+
"chalk": "^4.1.2",
|
|
62
|
+
"cheerio": "^1.1.2",
|
|
63
|
+
"deepdash": "^5.3.9",
|
|
64
|
+
"form-data": "^4.0.4",
|
|
65
|
+
"gradient-string": "^1.2.0",
|
|
66
|
+
"https-proxy-agent": "^7.0.6",
|
|
67
|
+
"jsonpath-plus": "^10.3.0",
|
|
68
|
+
"lodash": "^4.17.21",
|
|
69
|
+
"mqtt": "^3.0.0",
|
|
70
|
+
"npmlog": "^7.0.1",
|
|
71
|
+
"tough-cookie": "^6.0.0",
|
|
72
|
+
"uuid": "^11.0.3",
|
|
73
|
+
"websocket-stream": "^5.3.0",
|
|
74
|
+
"ws": "^8.18.3"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@types/axios": "^0.9.36",
|
|
78
|
+
"@types/form-data": "^2.2.1",
|
|
79
|
+
"@types/node": "^22.19.0",
|
|
80
|
+
"@types/tough-cookie": "^4.0.5",
|
|
81
|
+
"eslint": "^9.39.1",
|
|
82
|
+
"mocha": "^11.7.5",
|
|
83
|
+
"prettier": "^3.6.2",
|
|
84
|
+
"ts-node": "^10.9.2",
|
|
85
|
+
"typescript": "^5.9.3"
|
|
86
|
+
},
|
|
87
|
+
"engines": {
|
|
88
|
+
"node": ">=18.0.0"
|
|
89
|
+
},
|
|
90
|
+
"scripts": {
|
|
91
|
+
"start": "node examples/simple-bot.js",
|
|
92
|
+
"validate": "npm pack --dry-run",
|
|
93
|
+
"prepack": "echo 'Preparing package for npm...'",
|
|
94
|
+
"test": "echo 'No tests configured yet'"
|
|
95
|
+
},
|
|
96
|
+
"overrides": {
|
|
97
|
+
"websocket-stream": {
|
|
98
|
+
"ws": "^8.18.3"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"directories": {
|
|
102
|
+
"example": "examples",
|
|
103
|
+
"test": "test"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = (defaultFuncs, api, ctx) => {
|
|
6
|
+
return async 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
|
+
if (!callback) {
|
|
15
|
+
callback = (err, result) => {
|
|
16
|
+
if (err) return rejectFunc(err);
|
|
17
|
+
resolveFunc(result);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
if (!ctx.mqttClient) {
|
|
23
|
+
throw new Error("Not connected to MQTT. Please use listenMqtt first.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (utils.getType(threadID) !== "Number" && utils.getType(threadID) !== "String") {
|
|
27
|
+
throw new Error("ThreadID should be of type Number or String");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (utils.getType(userID) !== "Array") {
|
|
31
|
+
userID = [userID];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const reqID = ++ctx.wsReqNumber;
|
|
35
|
+
const taskID = ++ctx.wsTaskNumber;
|
|
36
|
+
|
|
37
|
+
const payload = {
|
|
38
|
+
epoch_id: utils.generateOfflineThreadingID(),
|
|
39
|
+
tasks: [
|
|
40
|
+
{
|
|
41
|
+
failure_count: null,
|
|
42
|
+
label: "23",
|
|
43
|
+
payload: JSON.stringify({
|
|
44
|
+
thread_key: threadID,
|
|
45
|
+
contact_ids: userID,
|
|
46
|
+
sync_group: 1
|
|
47
|
+
}),
|
|
48
|
+
queue_name: threadID.toString(),
|
|
49
|
+
task_id: taskID
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
version_id: "24502707779384158"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const form = JSON.stringify({
|
|
56
|
+
app_id: "772021112871879",
|
|
57
|
+
payload: JSON.stringify(payload),
|
|
58
|
+
request_id: reqID,
|
|
59
|
+
type: 3
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
let responseHandled = false;
|
|
63
|
+
const handleRes = (topic, message) => {
|
|
64
|
+
if (topic !== "/ls_resp" || responseHandled) return;
|
|
65
|
+
let jsonMsg;
|
|
66
|
+
try {
|
|
67
|
+
jsonMsg = JSON.parse(message.toString());
|
|
68
|
+
jsonMsg.payload = JSON.parse(jsonMsg.payload);
|
|
69
|
+
} catch {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (jsonMsg.request_id !== reqID) return;
|
|
73
|
+
responseHandled = true;
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
ctx.mqttClient.removeListener("message", handleRes);
|
|
76
|
+
callback(null, { success: true, response: jsonMsg.payload });
|
|
77
|
+
resolveFunc({ success: true, response: jsonMsg.payload });
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const timeout = setTimeout(() => {
|
|
81
|
+
if (!responseHandled) {
|
|
82
|
+
responseHandled = true;
|
|
83
|
+
ctx.mqttClient.removeListener("message", handleRes);
|
|
84
|
+
const err = new Error("MQTT request timeout");
|
|
85
|
+
callback(err);
|
|
86
|
+
rejectFunc(err);
|
|
87
|
+
}
|
|
88
|
+
}, 30000);
|
|
89
|
+
|
|
90
|
+
ctx.mqttClient.on("message", handleRes);
|
|
91
|
+
ctx.mqttClient.publish("/ls_req", form, { qos: 1, retain: false }, (err) => {
|
|
92
|
+
if (err && !responseHandled) {
|
|
93
|
+
responseHandled = true;
|
|
94
|
+
clearTimeout(timeout);
|
|
95
|
+
ctx.mqttClient.removeListener("message", handleRes);
|
|
96
|
+
callback(err);
|
|
97
|
+
rejectFunc(err);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
} catch (err) {
|
|
101
|
+
utils.error("addUserToGroup", err);
|
|
102
|
+
callback(err);
|
|
103
|
+
rejectFunc(err);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return returnPromise;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
function generateOfflineThreadingID() {
|
|
6
|
+
return Date.now().toString() + Math.floor(Math.random() * 1000000).toString();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getType(obj) {
|
|
10
|
+
return Object.prototype.toString.call(obj).slice(8, -1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
14
|
+
return function changeAdminStatus(threadID, adminID, adminStatus, callback) {
|
|
15
|
+
let resolveFunc = function() {};
|
|
16
|
+
let rejectFunc = function() {};
|
|
17
|
+
const returnPromise = new Promise(function(resolve, reject) {
|
|
18
|
+
resolveFunc = resolve;
|
|
19
|
+
rejectFunc = reject;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!callback) {
|
|
23
|
+
callback = function(err, data) {
|
|
24
|
+
if (err) return rejectFunc(err);
|
|
25
|
+
resolveFunc(data);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (getType(threadID) !== "String") {
|
|
30
|
+
return callback({ error: "changeAdminStatus: threadID must be a string" });
|
|
31
|
+
}
|
|
32
|
+
if (getType(adminID) !== "String" && getType(adminID) !== "Array") {
|
|
33
|
+
return callback({ error: "changeAdminStatus: adminID must be a string or an array" });
|
|
34
|
+
}
|
|
35
|
+
if (getType(adminStatus) !== "Boolean") {
|
|
36
|
+
return callback({ error: "changeAdminStatus: adminStatus must be true or false" });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (ctx.mqttClient) {
|
|
40
|
+
const tasks = [];
|
|
41
|
+
const isAdmin = adminStatus ? 1 : 0;
|
|
42
|
+
const epochID = generateOfflineThreadingID();
|
|
43
|
+
|
|
44
|
+
if (getType(adminID) === "Array") {
|
|
45
|
+
adminID.forEach((id, index) => {
|
|
46
|
+
tasks.push({
|
|
47
|
+
failure_count: null,
|
|
48
|
+
label: "25",
|
|
49
|
+
payload: JSON.stringify({
|
|
50
|
+
thread_key: threadID,
|
|
51
|
+
contact_id: id,
|
|
52
|
+
is_admin: isAdmin
|
|
53
|
+
}),
|
|
54
|
+
queue_name: "admin_status",
|
|
55
|
+
task_id: index + 1
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
tasks.push({
|
|
60
|
+
failure_count: null,
|
|
61
|
+
label: "25",
|
|
62
|
+
payload: JSON.stringify({
|
|
63
|
+
thread_key: threadID,
|
|
64
|
+
contact_id: adminID,
|
|
65
|
+
is_admin: isAdmin
|
|
66
|
+
}),
|
|
67
|
+
queue_name: "admin_status",
|
|
68
|
+
task_id: 1
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let count_req = 0;
|
|
73
|
+
const form = JSON.stringify({
|
|
74
|
+
app_id: "2220391788200892",
|
|
75
|
+
payload: JSON.stringify({
|
|
76
|
+
epoch_id: epochID,
|
|
77
|
+
tasks: tasks,
|
|
78
|
+
version_id: "8798795233522156"
|
|
79
|
+
}),
|
|
80
|
+
request_id: ++count_req,
|
|
81
|
+
type: 3
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
ctx.mqttClient.publish("/ls_req", form, {}, (err, _packet) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
utils.error("changeAdminStatus (MQTT)", err);
|
|
87
|
+
return callback(err);
|
|
88
|
+
} else {
|
|
89
|
+
utils.log("Admin status changed successfully via MQTT");
|
|
90
|
+
return callback(null, { success: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
utils.warn("MQTT client not available, using HTTP fallback for changeAdminStatus");
|
|
95
|
+
const tasks = [];
|
|
96
|
+
const epochID = generateOfflineThreadingID();
|
|
97
|
+
|
|
98
|
+
if (getType(adminID) === "Array") {
|
|
99
|
+
adminID.forEach((id, index) => {
|
|
100
|
+
tasks.push({
|
|
101
|
+
label: '25',
|
|
102
|
+
payload: JSON.stringify({ thread_key: threadID, contact_id: id, is_admin: adminStatus }),
|
|
103
|
+
queue_name: 'admin_status',
|
|
104
|
+
task_id: index + 1,
|
|
105
|
+
failure_count: null
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
tasks.push({
|
|
110
|
+
label: '25',
|
|
111
|
+
payload: JSON.stringify({ thread_key: threadID, contact_id: adminID, is_admin: adminStatus }),
|
|
112
|
+
queue_name: 'admin_status',
|
|
113
|
+
task_id: 1,
|
|
114
|
+
failure_count: null
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const form = {
|
|
119
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
120
|
+
request_id: 1,
|
|
121
|
+
type: 3,
|
|
122
|
+
payload: {
|
|
123
|
+
version_id: '3816854585040595',
|
|
124
|
+
tasks: tasks,
|
|
125
|
+
epoch_id: epochID,
|
|
126
|
+
data_trace_id: null
|
|
127
|
+
},
|
|
128
|
+
app_id: '772021112871879'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
form.payload = JSON.stringify(form.payload);
|
|
132
|
+
|
|
133
|
+
defaultFuncs
|
|
134
|
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
|
135
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
136
|
+
.then(() => {
|
|
137
|
+
utils.log("Admin status changed successfully via HTTP");
|
|
138
|
+
callback(null, { success: true });
|
|
139
|
+
})
|
|
140
|
+
.catch(err => {
|
|
141
|
+
utils.error("changeAdminStatus (HTTP)", err);
|
|
142
|
+
callback(err);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return returnPromise;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = (defaultFuncs, api, ctx) => {
|
|
6
|
+
return async function changeArchivedStatus(threadIDs, archive, callback) {
|
|
7
|
+
let resolveFunc = () => {};
|
|
8
|
+
let rejectFunc = () => {};
|
|
9
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
10
|
+
resolveFunc = resolve;
|
|
11
|
+
rejectFunc = reject;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!callback) {
|
|
15
|
+
callback = (err, result) => {
|
|
16
|
+
if (err) return rejectFunc(err);
|
|
17
|
+
resolveFunc(result);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
if (utils.getType(archive) === "Function") {
|
|
23
|
+
callback = archive;
|
|
24
|
+
archive = true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (utils.getType(archive) !== "Boolean") {
|
|
28
|
+
throw new Error("archive parameter must be a boolean");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!Array.isArray(threadIDs)) {
|
|
32
|
+
threadIDs = [threadIDs];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const form = {
|
|
36
|
+
should_archive: archive
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
threadIDs.forEach(id => {
|
|
40
|
+
form[`thread_fbids[${id}]`] = true;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const res = await defaultFuncs.post(
|
|
44
|
+
"https://www.facebook.com/ajax/mercury/change_archived_status.php",
|
|
45
|
+
ctx.jar,
|
|
46
|
+
form
|
|
47
|
+
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
48
|
+
|
|
49
|
+
if (res && res.error) {
|
|
50
|
+
throw res;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
callback(null, { success: true });
|
|
54
|
+
} catch (err) {
|
|
55
|
+
utils.error("changeArchivedStatus", err);
|
|
56
|
+
callback(err);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return returnPromise;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = (defaultFuncs, api, ctx) => {
|
|
6
|
+
async function handleUpload(image) {
|
|
7
|
+
const form = {
|
|
8
|
+
profile_id: ctx.userID,
|
|
9
|
+
photo_source: 57,
|
|
10
|
+
av: ctx.userID,
|
|
11
|
+
file: image
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return defaultFuncs.postFormData(
|
|
15
|
+
"https://www.facebook.com/profile/picture/upload/",
|
|
16
|
+
ctx.jar,
|
|
17
|
+
form,
|
|
18
|
+
{}
|
|
19
|
+
).then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
20
|
+
.then(resData => {
|
|
21
|
+
if (resData.error) throw resData;
|
|
22
|
+
return resData;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return async function changeAvatar(image, caption, timestamp, callback) {
|
|
27
|
+
let resolveFunc = () => {};
|
|
28
|
+
let rejectFunc = () => {};
|
|
29
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
30
|
+
resolveFunc = resolve;
|
|
31
|
+
rejectFunc = reject;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!timestamp && utils.getType(caption) === "Number") {
|
|
35
|
+
timestamp = caption;
|
|
36
|
+
caption = "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!timestamp && !callback && (utils.getType(caption) === "Function")) {
|
|
40
|
+
callback = caption;
|
|
41
|
+
caption = "";
|
|
42
|
+
timestamp = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!callback) {
|
|
46
|
+
callback = (err, data) => {
|
|
47
|
+
if (err) return rejectFunc(err);
|
|
48
|
+
resolveFunc(data);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (!utils.isReadableStream(image)) {
|
|
54
|
+
throw new Error("Image is not a readable stream");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const payload = await handleUpload(image);
|
|
58
|
+
|
|
59
|
+
const form = {
|
|
60
|
+
av: ctx.i_userID || ctx.userID,
|
|
61
|
+
fb_api_req_friendly_name: "ProfileCometProfilePictureSetMutation",
|
|
62
|
+
fb_api_caller_class: "RelayModern",
|
|
63
|
+
doc_id: "5066134240065849",
|
|
64
|
+
variables: JSON.stringify({
|
|
65
|
+
input: {
|
|
66
|
+
caption: caption || "",
|
|
67
|
+
existing_photo_id: payload.payload.fbid,
|
|
68
|
+
expiration_time: timestamp,
|
|
69
|
+
profile_id: ctx.i_userID || ctx.userID,
|
|
70
|
+
profile_pic_method: "EXISTING",
|
|
71
|
+
profile_pic_source: "TIMELINE",
|
|
72
|
+
scaled_crop_rect: {
|
|
73
|
+
height: 1,
|
|
74
|
+
width: 1,
|
|
75
|
+
x: 0,
|
|
76
|
+
y: 0
|
|
77
|
+
},
|
|
78
|
+
skip_cropping: true,
|
|
79
|
+
actor_id: ctx.i_userID || ctx.userID,
|
|
80
|
+
client_mutation_id: Math.round(Math.random() * 19).toString()
|
|
81
|
+
},
|
|
82
|
+
isPage: false,
|
|
83
|
+
isProfile: true,
|
|
84
|
+
scale: 3
|
|
85
|
+
})
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const res = await defaultFuncs.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
89
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
90
|
+
|
|
91
|
+
if (res.errors) {
|
|
92
|
+
throw res;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
callback(null, res[0].data.profile_picture_set);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
utils.error("changeAvatar", err);
|
|
98
|
+
callback(err);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return returnPromise;
|
|
102
|
+
};
|
|
103
|
+
};
|