stfca 1.0.9 → 1.0.11
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 +7 -15
- package/checkUpdate.js +23 -137
- package/package.json +1 -1
- package/src/metaTheme.js +190 -0
- package/src/setThreadTheme.js +276 -0
package/README.md
CHANGED
|
@@ -306,21 +306,13 @@ api.setOptions({
|
|
|
306
306
|
|
|
307
307
|
## 🛠️ Projects Using This API
|
|
308
308
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- **[
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
- **[matrix-puppet-facebook](https://github.com/matrix-hacks/matrix-puppet-facebook)** - Facebook bridge for Matrix
|
|
317
|
-
- **[Miscord](https://github.com/Bjornskjald/miscord)** - Easy-to-use Facebook bridge for Discord
|
|
318
|
-
- **[chat-bridge](https://github.com/rexx0520/chat-bridge)** - Messenger, Telegram and IRC chat bridge
|
|
319
|
-
- **[Botium](https://github.com/codeforequity-at/botium-core)** - The Selenium for Chatbots
|
|
320
|
-
- **[Messenger-CLI](https://github.com/AstroCB/Messenger-CLI)** - Command-line interface for Facebook Messenger
|
|
321
|
-
- **[BotCore](https://github.com/AstroCB/BotCore)** - Tools for writing and managing Facebook Messenger bots
|
|
322
|
-
|
|
323
|
-
[See more projects...](https://github.com/Donix-VN/fca-unofficial#projects-using-this-api)
|
|
309
|
+
### Primary Project
|
|
310
|
+
|
|
311
|
+
- **[ST-BOT](https://github.com/sheikhtamimlover/ST-BOT)** - Enhanced version of GoatBot V2, a powerful and customizable Facebook Messenger bot with advanced features, plugin support, and automatic updates. This is the main project that ST-FCA was designed for.
|
|
312
|
+
|
|
313
|
+
### Other Use Cases
|
|
314
|
+
|
|
315
|
+
ST-FCA can be used for any Facebook Messenger bot project or automation tool. If you want to create your own messenger bot or use this API for other purposes, feel free to integrate it into your project.
|
|
324
316
|
|
|
325
317
|
## 📚 Full API Documentation
|
|
326
318
|
|
package/checkUpdate.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const axios = require('axios');
|
|
3
2
|
const { execSync } = require('child_process');
|
|
4
3
|
const fs = require('fs');
|
|
@@ -44,6 +43,9 @@ async function checkForFCAUpdate() {
|
|
|
44
43
|
// Update npm package
|
|
45
44
|
await updateNpmPackage(latestVersion);
|
|
46
45
|
|
|
46
|
+
// Update version in user's package.json
|
|
47
|
+
await updateUserPackageJson(latestVersion);
|
|
48
|
+
|
|
47
49
|
console.log('\x1b[32m%s\x1b[0m', '✅ ST-FCA updated successfully!');
|
|
48
50
|
console.log('\x1b[33m%s\x1b[0m', '🔄 Restarting to apply changes...');
|
|
49
51
|
|
|
@@ -65,162 +67,46 @@ async function checkForFCAUpdate() {
|
|
|
65
67
|
|
|
66
68
|
async function updateNpmPackage(version) {
|
|
67
69
|
try {
|
|
68
|
-
console.log('\x1b[36m%s\x1b[0m',
|
|
70
|
+
console.log('\x1b[36m%s\x1b[0m', `📦 Running npm install stfca@${version}...`);
|
|
69
71
|
|
|
70
72
|
// Execute npm install command
|
|
71
|
-
execSync(
|
|
73
|
+
execSync(`npm install stfca@${version} --save`, {
|
|
72
74
|
cwd: process.cwd(),
|
|
73
75
|
stdio: 'inherit'
|
|
74
76
|
});
|
|
75
77
|
|
|
76
|
-
console.log('\x1b[32m%s\x1b[0m', '✅ Package
|
|
78
|
+
console.log('\x1b[32m%s\x1b[0m', '✅ Package installed successfully!');
|
|
77
79
|
return true;
|
|
78
80
|
} catch (error) {
|
|
79
|
-
console.log('\x1b[31m%s\x1b[0m', '❌ Failed to
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function performComprehensiveUpdate() {
|
|
85
|
-
try {
|
|
86
|
-
// Step 1: Get the complete file tree from GitHub
|
|
87
|
-
console.log('\x1b[36m%s\x1b[0m', '📂 Fetching complete file structure...');
|
|
88
|
-
const fileTree = await getGitHubFileTree();
|
|
89
|
-
|
|
90
|
-
// Step 2: Get local files
|
|
91
|
-
const localFiles = getLocalFiles();
|
|
92
|
-
|
|
93
|
-
// Step 3: Download/Update all files from GitHub
|
|
94
|
-
console.log('\x1b[36m%s\x1b[0m', '⬇️ Downloading files...');
|
|
95
|
-
for (const file of fileTree) {
|
|
96
|
-
await downloadFile(file);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Step 4: Delete files that don't exist in the latest version
|
|
100
|
-
console.log('\x1b[36m%s\x1b[0m', '🗑️ Cleaning up old files...');
|
|
101
|
-
const githubFilePaths = fileTree.map(f => f.path);
|
|
102
|
-
for (const localFile of localFiles) {
|
|
103
|
-
if (!githubFilePaths.includes(localFile) && !shouldKeepFile(localFile)) {
|
|
104
|
-
deleteLocalFile(localFile);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
console.log('\x1b[32m%s\x1b[0m', '✅ All files synchronized!');
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.log('\x1b[31m%s\x1b[0m', '❌ Update failed:', error.message);
|
|
81
|
+
console.log('\x1b[31m%s\x1b[0m', '❌ Failed to install package:', error.message);
|
|
111
82
|
throw error;
|
|
112
83
|
}
|
|
113
84
|
}
|
|
114
85
|
|
|
115
|
-
async function
|
|
86
|
+
async function updateUserPackageJson(version) {
|
|
116
87
|
try {
|
|
117
|
-
const
|
|
118
|
-
'https://api.github.com/repos/sheikhtamimlover/ST-FCA/git/trees/main?recursive=1'
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Filter only files (not directories)
|
|
122
|
-
return data.tree
|
|
123
|
-
.filter(item => item.type === 'blob')
|
|
124
|
-
.filter(item => !item.path.startsWith('.git'))
|
|
125
|
-
.filter(item => !shouldIgnoreFile(item.path));
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.log('\x1b[31m%s\x1b[0m', '❌ Failed to fetch file tree:', error.message);
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
88
|
+
const userPackageJsonPath = path.join(process.cwd(), 'package.json');
|
|
131
89
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
function walkDir(dir, baseDir = '') {
|
|
136
|
-
const items = fs.readdirSync(dir);
|
|
137
|
-
|
|
138
|
-
for (const item of items) {
|
|
139
|
-
const fullPath = path.join(dir, item);
|
|
140
|
-
const relativePath = baseDir ? path.join(baseDir, item) : item;
|
|
141
|
-
|
|
142
|
-
if (shouldIgnoreFile(relativePath)) continue;
|
|
143
|
-
|
|
144
|
-
const stat = fs.statSync(fullPath);
|
|
145
|
-
|
|
146
|
-
if (stat.isDirectory()) {
|
|
147
|
-
walkDir(fullPath, relativePath);
|
|
148
|
-
} else {
|
|
149
|
-
files.push(relativePath.replace(/\\/g, '/'));
|
|
150
|
-
}
|
|
90
|
+
if (!fs.existsSync(userPackageJsonPath)) {
|
|
91
|
+
console.log('\x1b[33m%s\x1b[0m', '⚠️ No package.json found in user project');
|
|
92
|
+
return;
|
|
151
93
|
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
walkDir(__dirname);
|
|
155
|
-
return files;
|
|
156
|
-
}
|
|
157
94
|
|
|
158
|
-
|
|
159
|
-
const ignorePatterns = [
|
|
160
|
-
'node_modules',
|
|
161
|
-
'.git',
|
|
162
|
-
'.env',
|
|
163
|
-
'appstate.json',
|
|
164
|
-
'fbstate.json',
|
|
165
|
-
'package-lock.json',
|
|
166
|
-
'.replit',
|
|
167
|
-
'replit.nix',
|
|
168
|
-
'.config',
|
|
169
|
-
'generated-icon.png'
|
|
170
|
-
];
|
|
171
|
-
|
|
172
|
-
return ignorePatterns.some(pattern => filePath.includes(pattern));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function shouldKeepFile(filePath) {
|
|
176
|
-
const keepPatterns = [
|
|
177
|
-
'node_modules',
|
|
178
|
-
'.env',
|
|
179
|
-
'appstate.json',
|
|
180
|
-
'fbstate.json',
|
|
181
|
-
'package-lock.json',
|
|
182
|
-
'.replit',
|
|
183
|
-
'replit.nix',
|
|
184
|
-
'.config'
|
|
185
|
-
];
|
|
186
|
-
|
|
187
|
-
return keepPatterns.some(pattern => filePath.includes(pattern));
|
|
188
|
-
}
|
|
95
|
+
const packageJson = JSON.parse(fs.readFileSync(userPackageJsonPath, 'utf-8'));
|
|
189
96
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const { data } = await axios.get(
|
|
196
|
-
`https://raw.githubusercontent.com/sheikhtamimlover/ST-FCA/main/${fileInfo.path}`,
|
|
197
|
-
{ responseType: 'arraybuffer' }
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// Ensure directory exists
|
|
201
|
-
const fileDir = path.dirname(targetPath);
|
|
202
|
-
if (!fs.existsSync(fileDir)) {
|
|
203
|
-
fs.mkdirSync(fileDir, { recursive: true });
|
|
97
|
+
// Update stfca version in dependencies
|
|
98
|
+
if (packageJson.dependencies && packageJson.dependencies.stfca) {
|
|
99
|
+
packageJson.dependencies.stfca = `^${version}`;
|
|
100
|
+
fs.writeFileSync(userPackageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
101
|
+
console.log('\x1b[32m%s\x1b[0m', `✅ Updated package.json to stfca@${version}`);
|
|
204
102
|
}
|
|
205
|
-
|
|
206
|
-
// Write file
|
|
207
|
-
fs.writeFileSync(targetPath, Buffer.from(data));
|
|
208
|
-
console.log('\x1b[32m%s\x1b[0m', ` ✓ ${fileInfo.path}`);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
console.log('\x1b[31m%s\x1b[0m', ` ✗ Failed: ${fileInfo.path}`, error.message);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
103
|
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
const fullPath = path.join(__dirname, filePath);
|
|
217
|
-
if (fs.existsSync(fullPath)) {
|
|
218
|
-
fs.unlinkSync(fullPath);
|
|
219
|
-
console.log('\x1b[33m%s\x1b[0m', ` 🗑️ Deleted: ${filePath}`);
|
|
220
|
-
}
|
|
104
|
+
return true;
|
|
221
105
|
} catch (error) {
|
|
222
|
-
console.log('\x1b[31m%s\x1b[0m',
|
|
106
|
+
console.log('\x1b[31m%s\x1b[0m', '⚠️ Failed to update user package.json:', error.message);
|
|
107
|
+
// Don't throw - this is not critical
|
|
108
|
+
return false;
|
|
223
109
|
}
|
|
224
110
|
}
|
|
225
111
|
|
|
226
|
-
module.exports = { checkForFCAUpdate,
|
|
112
|
+
module.exports = { checkForFCAUpdate, updateNpmPackage, updateUserPackageJson };
|
package/package.json
CHANGED
package/src/metaTheme.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ===========================================================
|
|
3
|
+
* 💫 META THEME GENERATOR MODULE 💫
|
|
4
|
+
* ===========================================================
|
|
5
|
+
* 🧑💻 Author: Sheikh Tamim (ST | Sheikh Tamim)
|
|
6
|
+
* 🔰 Owner & Developer
|
|
7
|
+
* 🌐 GitHub: https://github.com/sheikhtamimlover
|
|
8
|
+
* 📸 Instagram: https://instagram.com/sheikh.tamim_lover
|
|
9
|
+
* 🧠 Description:
|
|
10
|
+
* This module generates beautiful Messenger AI themes
|
|
11
|
+
* using Meta's hidden GraphQL endpoints. It allows you to
|
|
12
|
+
* create unique chat themes based on your custom prompt
|
|
13
|
+
* or optional image inspiration.
|
|
14
|
+
* -----------------------------------------------------------
|
|
15
|
+
* ⚙️ Features:
|
|
16
|
+
* • Generate AI-based Messenger chat themes.
|
|
17
|
+
* • Custom prompt & optional image URL input.
|
|
18
|
+
* • Returns structured theme data with full color mapping.
|
|
19
|
+
* -----------------------------------------------------------
|
|
20
|
+
* 🕊️ Respect the creator & give proper credits if reused.
|
|
21
|
+
* ===========================================================
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
"use strict";
|
|
25
|
+
|
|
26
|
+
const utils = require("../utils");
|
|
27
|
+
const log = require("npmlog");
|
|
28
|
+
/** © Sheikh Tamim - Please give proper credits if you copy or reuse this code. */
|
|
29
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
30
|
+
return function metaTheme(prompt, options, callback) {
|
|
31
|
+
var resolveFunc = function () { };
|
|
32
|
+
var rejectFunc = function () { };
|
|
33
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
|
34
|
+
resolveFunc = resolve;
|
|
35
|
+
rejectFunc = reject;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Handle optional parameters
|
|
39
|
+
if (typeof options === 'function') {
|
|
40
|
+
callback = options;
|
|
41
|
+
options = {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!callback) {
|
|
45
|
+
callback = function (err, data) {
|
|
46
|
+
if (err) return rejectFunc(err);
|
|
47
|
+
resolveFunc(data);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!prompt || typeof prompt !== 'string') {
|
|
52
|
+
return callback({ error: "Prompt is required and must be a string" });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Parse options
|
|
56
|
+
const numThemes = options.numThemes || 1;
|
|
57
|
+
const imageUrl = options.imageUrl || null;
|
|
58
|
+
|
|
59
|
+
const inputData = {
|
|
60
|
+
client_mutation_id: Math.floor(Math.random() * 10).toString(),
|
|
61
|
+
actor_id: ctx.userID,
|
|
62
|
+
bypass_cache: true,
|
|
63
|
+
caller: "MESSENGER",
|
|
64
|
+
num_themes: Math.min(numThemes, 5), // Limit to max 5 themes
|
|
65
|
+
prompt: prompt
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Add image URL if provided
|
|
69
|
+
if (imageUrl) {
|
|
70
|
+
inputData.image_url = imageUrl;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const form = {
|
|
74
|
+
av: ctx.userID,
|
|
75
|
+
__aaid: 0,
|
|
76
|
+
__user: ctx.userID,
|
|
77
|
+
__a: 1,
|
|
78
|
+
__req: utils.getSignatureID(),
|
|
79
|
+
__hs: "20358.HYP:comet_pkg.2.1...0",
|
|
80
|
+
dpr: 1,
|
|
81
|
+
__ccg: "EXCELLENT",
|
|
82
|
+
__rev: "1027673511",
|
|
83
|
+
__s: utils.getSignatureID(),
|
|
84
|
+
__hsi: "7554561631547849479",
|
|
85
|
+
__comet_req: 15,
|
|
86
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
87
|
+
jazoest: ctx.ttstamp,
|
|
88
|
+
lsd: ctx.fb_dtsg,
|
|
89
|
+
__spin_r: "1027673511",
|
|
90
|
+
__spin_b: "trunk",
|
|
91
|
+
__spin_t: Date.now(),
|
|
92
|
+
__crn: "comet.fbweb.MWInboxHomeRoute",
|
|
93
|
+
qpl_active_flow_ids: "25309433,521485406",
|
|
94
|
+
fb_api_caller_class: "RelayModern",
|
|
95
|
+
fb_api_req_friendly_name: "useGenerateAIThemeMutation",
|
|
96
|
+
variables: JSON.stringify({ input: inputData }),
|
|
97
|
+
server_timestamps: true,
|
|
98
|
+
doc_id: "23873748445608673",
|
|
99
|
+
fb_api_analytics_tags: JSON.stringify(["qpl_active_flow_ids=25309433,521485406"])
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
defaultFuncs
|
|
103
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
104
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
105
|
+
.then(function (resData) {
|
|
106
|
+
if (resData.errors) {
|
|
107
|
+
throw resData.errors;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (resData.data && resData.data.xfb_generate_ai_themes_from_prompt) {
|
|
111
|
+
const themeData = resData.data.xfb_generate_ai_themes_from_prompt;
|
|
112
|
+
if (themeData.success && themeData.themes && themeData.themes.length > 0) {
|
|
113
|
+
const themes = themeData.themes.map((theme, index) => ({
|
|
114
|
+
success: true,
|
|
115
|
+
themeId: theme.id,
|
|
116
|
+
name: theme.accessibility_label,
|
|
117
|
+
description: theme.description,
|
|
118
|
+
serialNumber: index + 1,
|
|
119
|
+
colors: {
|
|
120
|
+
composerBackground: theme.composer_background_color,
|
|
121
|
+
backgroundGradient: theme.background_gradient_colors,
|
|
122
|
+
titleBarButton: theme.title_bar_button_tint_color,
|
|
123
|
+
inboundMessageGradient: theme.inbound_message_gradient_colors,
|
|
124
|
+
titleBarText: theme.title_bar_text_color,
|
|
125
|
+
composerTint: theme.composer_tint_color,
|
|
126
|
+
messageText: theme.message_text_color,
|
|
127
|
+
primaryButton: theme.primary_button_background_color,
|
|
128
|
+
titleBarBackground: theme.title_bar_background_color,
|
|
129
|
+
fallback: theme.fallback_color,
|
|
130
|
+
gradient: theme.gradient_colors
|
|
131
|
+
},
|
|
132
|
+
backgroundImage: theme.background_asset ? theme.background_asset.image.uri : null,
|
|
133
|
+
iconImage: theme.icon_asset ? theme.icon_asset.image.uri : null,
|
|
134
|
+
images: {
|
|
135
|
+
background: theme.background_asset ? theme.background_asset.image.uri : null,
|
|
136
|
+
icon: theme.icon_asset ? theme.icon_asset.image.uri : null
|
|
137
|
+
},
|
|
138
|
+
alternativeThemes: theme.alternative_themes ? theme.alternative_themes.map(alt => ({
|
|
139
|
+
id: alt.id,
|
|
140
|
+
name: alt.accessibility_label,
|
|
141
|
+
backgroundImage: alt.background_asset ? alt.background_asset.image.uri : null,
|
|
142
|
+
iconImage: alt.icon_asset ? alt.icon_asset.image.uri : null
|
|
143
|
+
})) : []
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
const result = {
|
|
147
|
+
success: true,
|
|
148
|
+
count: themes.length,
|
|
149
|
+
themes: themes,
|
|
150
|
+
// For backward compatibility, include first theme data at root level
|
|
151
|
+
...themes[0]
|
|
152
|
+
};
|
|
153
|
+
return callback(null, result);
|
|
154
|
+
} else {
|
|
155
|
+
throw new Error("No themes generated for the given prompt");
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
throw new Error("Invalid response from AI theme generation");
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
.catch(function (err) {
|
|
162
|
+
log.error("metaTheme", err);
|
|
163
|
+
|
|
164
|
+
// Check for specific error conditions
|
|
165
|
+
let errorMessage = "An error occurred while generating themes";
|
|
166
|
+
|
|
167
|
+
if (err.message && err.message.includes("not authorized")) {
|
|
168
|
+
errorMessage = "Your account is not authorized to generate AI themes. This feature may not be available for your account type.";
|
|
169
|
+
} else if (err.message && err.message.includes("rate limit")) {
|
|
170
|
+
errorMessage = "Rate limit exceeded. Please wait a moment before trying again.";
|
|
171
|
+
} else if (err.message && err.message.includes("Invalid")) {
|
|
172
|
+
errorMessage = "Invalid request parameters. Please check your input.";
|
|
173
|
+
} else if (err.statusCode === 403) {
|
|
174
|
+
errorMessage = "Access denied. Your account may not support Meta AI theme generation.";
|
|
175
|
+
} else if (err.statusCode === 429) {
|
|
176
|
+
errorMessage = "Too many requests. Please wait before trying again.";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return callback({
|
|
180
|
+
error: errorMessage,
|
|
181
|
+
originalError: err.message || err,
|
|
182
|
+
statusCode: err.statusCode || null
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return returnPromise;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/** © Sheikh Tamim - Please give proper credits if you copy or reuse this code. */
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ===========================================================
|
|
3
|
+
* 🧑💻 Author: Sheikh Tamim (ST | Sheikh Tamim)
|
|
4
|
+
* 🔰 Owner & Developer
|
|
5
|
+
* 🌐 GitHub: https://github.com/sheikhtamimlover
|
|
6
|
+
* 📸 Instagram: https://instagram.com/sheikh.tamim_lover
|
|
7
|
+
* -----------------------------------------------------------
|
|
8
|
+
* 🕊️ Respect the creator & give proper credits if reused.
|
|
9
|
+
* ===========================================================
|
|
10
|
+
*/
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
var utils = require("../utils");
|
|
14
|
+
var log = require("npmlog");
|
|
15
|
+
|
|
16
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
17
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Instagram: @sheikh.tamim_lover */
|
|
18
|
+
return function setThreadTheme(threadID, themeData, callback) {
|
|
19
|
+
var resolveFunc = function () { };
|
|
20
|
+
var rejectFunc = function () { };
|
|
21
|
+
var returnPromise = new Promise(function (resolve, reject) {
|
|
22
|
+
resolveFunc = resolve;
|
|
23
|
+
rejectFunc = reject;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!callback) {
|
|
27
|
+
callback = function (err, data) {
|
|
28
|
+
if (err) return rejectFunc(err);
|
|
29
|
+
resolveFunc(data);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!threadID) {
|
|
34
|
+
return callback({ error: "threadID is required" });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function updateThreadTheme() {
|
|
38
|
+
try {
|
|
39
|
+
const timestamp = Date.now();
|
|
40
|
+
|
|
41
|
+
// Step 1: Load theme bootloader modules
|
|
42
|
+
const moduleParams = new URLSearchParams({
|
|
43
|
+
modules: "LSUpdateThreadTheme,LSUpdateThreadCustomEmoji,LSUpdateThreadThemePayloadCacheKey",
|
|
44
|
+
__aaid: 0,
|
|
45
|
+
__user: ctx.userID,
|
|
46
|
+
__a: 1,
|
|
47
|
+
__req: utils.getSignatureID(),
|
|
48
|
+
__hs: "20352.HYP:comet_pkg.2.1...0",
|
|
49
|
+
dpr: 1,
|
|
50
|
+
__ccg: "EXCELLENT",
|
|
51
|
+
__rev: "1027396270",
|
|
52
|
+
__s: utils.getSignatureID(),
|
|
53
|
+
__hsi: "7552524636527201016",
|
|
54
|
+
__comet_req: 15,
|
|
55
|
+
fb_dtsg_ag: ctx.fb_dtsg,
|
|
56
|
+
jazoest: ctx.ttstamp,
|
|
57
|
+
__spin_r: "1027396270",
|
|
58
|
+
__spin_b: "trunk",
|
|
59
|
+
__spin_t: timestamp,
|
|
60
|
+
__crn: "comet.fbweb.MWInboxHomeRoute"
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await defaultFuncs
|
|
64
|
+
.get("https://www.facebook.com/ajax/bootloader-endpoint/?" + moduleParams.toString(), ctx.jar)
|
|
65
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
66
|
+
|
|
67
|
+
// Step 2: Get available themes first
|
|
68
|
+
let availableThemes = [];
|
|
69
|
+
try {
|
|
70
|
+
const themeForm = {
|
|
71
|
+
av: ctx.userID,
|
|
72
|
+
__aaid: 0,
|
|
73
|
+
__user: ctx.userID,
|
|
74
|
+
__a: 1,
|
|
75
|
+
__req: utils.getSignatureID(),
|
|
76
|
+
__hs: "20352.HYP:comet_pkg.2.1...0",
|
|
77
|
+
dpr: 1,
|
|
78
|
+
__ccg: "EXCELLENT",
|
|
79
|
+
__rev: "1027396270",
|
|
80
|
+
__s: utils.getSignatureID(),
|
|
81
|
+
__hsi: "7552524636527201016",
|
|
82
|
+
__comet_req: 15,
|
|
83
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
84
|
+
jazoest: ctx.ttstamp,
|
|
85
|
+
lsd: ctx.fb_dtsg,
|
|
86
|
+
__spin_r: "1027396270",
|
|
87
|
+
__spin_b: "trunk",
|
|
88
|
+
__spin_t: timestamp,
|
|
89
|
+
__crn: "comet.fbweb.MWInboxHomeRoute",
|
|
90
|
+
qpl_active_flow_ids: "25308101",
|
|
91
|
+
fb_api_caller_class: "RelayModern",
|
|
92
|
+
fb_api_req_friendly_name: "MWPThreadThemeQuery_AllThemesQuery",
|
|
93
|
+
variables: JSON.stringify({
|
|
94
|
+
"version": "default"
|
|
95
|
+
}),
|
|
96
|
+
server_timestamps: true,
|
|
97
|
+
doc_id: "24474714052117636"
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const themeResult = await defaultFuncs
|
|
101
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, themeForm)
|
|
102
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
103
|
+
|
|
104
|
+
if (themeResult && themeResult.data && themeResult.data.messenger_thread_themes) {
|
|
105
|
+
availableThemes = themeResult.data.messenger_thread_themes;
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
log.warn("setThreadTheme", "Could not fetch available themes, proceeding with theme update");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Step 3: Determine theme ID based on input
|
|
112
|
+
let themeId = null;
|
|
113
|
+
let customEmoji = "👍";
|
|
114
|
+
|
|
115
|
+
if (typeof themeData === "string") {
|
|
116
|
+
// If it's a string, try to find matching theme
|
|
117
|
+
if (themeData.match(/^[0-9]+$/)) {
|
|
118
|
+
// Numeric theme ID
|
|
119
|
+
themeId = themeData;
|
|
120
|
+
} else {
|
|
121
|
+
// Search by theme name/description
|
|
122
|
+
const foundTheme = availableThemes.find(theme =>
|
|
123
|
+
theme.accessibility_label &&
|
|
124
|
+
theme.accessibility_label.toLowerCase().includes(themeData.toLowerCase())
|
|
125
|
+
);
|
|
126
|
+
if (foundTheme) {
|
|
127
|
+
themeId = foundTheme.id;
|
|
128
|
+
} else {
|
|
129
|
+
// Fallback color mapping
|
|
130
|
+
const colorMap = {
|
|
131
|
+
blue: "196241301102133",
|
|
132
|
+
purple: "370940413392601",
|
|
133
|
+
green: "169463077092846",
|
|
134
|
+
pink: "230032715012014",
|
|
135
|
+
orange: "175615189761153",
|
|
136
|
+
red: "2136751179887052",
|
|
137
|
+
yellow: "2058653964378557",
|
|
138
|
+
teal: "417639218648241",
|
|
139
|
+
black: "539927563794799",
|
|
140
|
+
white: "2873642392710980",
|
|
141
|
+
default: "196241301102133"
|
|
142
|
+
};
|
|
143
|
+
themeId = colorMap[themeData.toLowerCase()] || colorMap.default;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else if (typeof themeData === "object" && themeData !== null) {
|
|
147
|
+
themeId = themeData.themeId || themeData.theme_id || themeData.id;
|
|
148
|
+
customEmoji = themeData.emoji || themeData.customEmoji || "👍";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!themeId) {
|
|
152
|
+
themeId = "196241301102133"; // Default blue theme
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Step 4: Use direct bootloader approach for theme update
|
|
156
|
+
try {
|
|
157
|
+
// First try with the legacy changeThreadColor approach
|
|
158
|
+
const legacyForm = {
|
|
159
|
+
dpr: 1,
|
|
160
|
+
queries: JSON.stringify({
|
|
161
|
+
o0: {
|
|
162
|
+
doc_id: "1727493033983591",
|
|
163
|
+
query_params: {
|
|
164
|
+
data: {
|
|
165
|
+
actor_id: ctx.userID,
|
|
166
|
+
client_mutation_id: "0",
|
|
167
|
+
source: "SETTINGS",
|
|
168
|
+
theme_id: themeId,
|
|
169
|
+
thread_id: threadID,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const legacyResult = await defaultFuncs
|
|
177
|
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, legacyForm)
|
|
178
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
179
|
+
|
|
180
|
+
if (legacyResult && !legacyResult[0]?.o0?.errors) {
|
|
181
|
+
return callback(null, {
|
|
182
|
+
threadID: threadID,
|
|
183
|
+
themeId: themeId,
|
|
184
|
+
customEmoji: customEmoji,
|
|
185
|
+
timestamp: timestamp,
|
|
186
|
+
success: true,
|
|
187
|
+
method: "legacy",
|
|
188
|
+
availableThemes: availableThemes.length > 0 ? availableThemes.map(t => ({
|
|
189
|
+
id: t.id,
|
|
190
|
+
name: t.accessibility_label,
|
|
191
|
+
description: t.description
|
|
192
|
+
})) : null
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
} catch (legacyErr) {
|
|
196
|
+
log.warn("setThreadTheme", "Legacy method failed, trying alternative approach");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Step 5: Try alternative GraphQL mutation with updated doc_id
|
|
200
|
+
const alternativeForm = {
|
|
201
|
+
av: ctx.userID,
|
|
202
|
+
__aaid: 0,
|
|
203
|
+
__user: ctx.userID,
|
|
204
|
+
__a: 1,
|
|
205
|
+
__req: utils.getSignatureID(),
|
|
206
|
+
__hs: "20352.HYP:comet_pkg.2.1...0",
|
|
207
|
+
dpr: 1,
|
|
208
|
+
__ccg: "EXCELLENT",
|
|
209
|
+
__rev: "1027396270",
|
|
210
|
+
__s: utils.getSignatureID(),
|
|
211
|
+
__hsi: "7552524636527201016",
|
|
212
|
+
__comet_req: 15,
|
|
213
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
214
|
+
jazoest: ctx.ttstamp,
|
|
215
|
+
lsd: ctx.fb_dtsg,
|
|
216
|
+
__spin_r: "1027396270",
|
|
217
|
+
__spin_b: "trunk",
|
|
218
|
+
__spin_t: timestamp,
|
|
219
|
+
__crn: "comet.fbweb.MWInboxHomeRoute",
|
|
220
|
+
fb_api_caller_class: "RelayModern",
|
|
221
|
+
fb_api_req_friendly_name: "MessengerThreadThemeUpdateMutation",
|
|
222
|
+
variables: JSON.stringify({
|
|
223
|
+
"input": {
|
|
224
|
+
"actor_id": ctx.userID,
|
|
225
|
+
"client_mutation_id": Math.floor(Math.random() * 10000).toString(),
|
|
226
|
+
"source": "SETTINGS",
|
|
227
|
+
"thread_id": threadID.toString(),
|
|
228
|
+
"theme_id": themeId.toString(),
|
|
229
|
+
"custom_emoji": customEmoji
|
|
230
|
+
}
|
|
231
|
+
}),
|
|
232
|
+
server_timestamps: true,
|
|
233
|
+
doc_id: "9734829906576883" // Updated doc_id based on working API
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const result = await defaultFuncs
|
|
237
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, alternativeForm)
|
|
238
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
239
|
+
|
|
240
|
+
if (result && result.errors && result.errors.length > 0) {
|
|
241
|
+
throw new Error("GraphQL Error: " + JSON.stringify(result.errors));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check if the mutation was successful
|
|
245
|
+
if (result && result.data && result.data.messenger_thread_theme_update) {
|
|
246
|
+
const updateResult = result.data.messenger_thread_theme_update;
|
|
247
|
+
if (updateResult.errors && updateResult.errors.length > 0) {
|
|
248
|
+
throw new Error("Theme Update Error: " + JSON.stringify(updateResult.errors));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return callback(null, {
|
|
253
|
+
threadID: threadID,
|
|
254
|
+
themeId: themeId,
|
|
255
|
+
customEmoji: customEmoji,
|
|
256
|
+
timestamp: timestamp,
|
|
257
|
+
success: true,
|
|
258
|
+
method: "graphql",
|
|
259
|
+
availableThemes: availableThemes.length > 0 ? availableThemes.map(t => ({
|
|
260
|
+
id: t.id,
|
|
261
|
+
name: t.accessibility_label,
|
|
262
|
+
description: t.description
|
|
263
|
+
})) : null
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
} catch (err) {
|
|
267
|
+
log.error("setThreadTheme", err);
|
|
268
|
+
return callback(err);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
updateThreadTheme();
|
|
273
|
+
return returnPromise;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Please give credits if reused. */
|