stfca 1.0.10 → 1.0.12
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/package.json +1 -1
- package/src/getThreadTheme.js +82 -0
- package/src/metaTheme.js +190 -0
- package/src/searchFriends.js +139 -0
- package/src/sendFriendRequest.js +113 -0
- package/src/setThreadTheme.js +276 -0
package/package.json
CHANGED
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
const utils = require("../utils");
|
|
14
|
+
const 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 getThreadTheme(themeID, callback) {
|
|
19
|
+
let resolveFunc, rejectFunc;
|
|
20
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
21
|
+
resolveFunc = resolve;
|
|
22
|
+
rejectFunc = reject;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!callback) {
|
|
26
|
+
callback = function (err, data) {
|
|
27
|
+
if (err) return rejectFunc(err);
|
|
28
|
+
resolveFunc(data);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!themeID) {
|
|
33
|
+
return callback({ error: "themeID is required" });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const form = {
|
|
37
|
+
av: ctx.userID,
|
|
38
|
+
__user: ctx.userID,
|
|
39
|
+
__a: 1,
|
|
40
|
+
__req: utils.getSignatureID(),
|
|
41
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
42
|
+
jazoest: ctx.ttstamp,
|
|
43
|
+
lsd: ctx.fb_dtsg,
|
|
44
|
+
fb_api_caller_class: "RelayModern",
|
|
45
|
+
fb_api_req_friendly_name: "MWPThreadThemeProviderQuery",
|
|
46
|
+
variables: JSON.stringify({
|
|
47
|
+
id: themeID.toString() // <-- THEME ID instead of threadID
|
|
48
|
+
}),
|
|
49
|
+
server_timestamps: true,
|
|
50
|
+
doc_id: "9734829906576883"
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
defaultFuncs
|
|
54
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
55
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
56
|
+
.then(function (resData) {
|
|
57
|
+
if (resData.errors) throw resData.errors;
|
|
58
|
+
|
|
59
|
+
if (resData.data && resData.data.messenger_thread_theme) {
|
|
60
|
+
const themeData = resData.data.messenger_thread_theme;
|
|
61
|
+
return callback(null, {
|
|
62
|
+
id: themeData.id,
|
|
63
|
+
name: themeData.accessibility_label,
|
|
64
|
+
description: themeData.description,
|
|
65
|
+
colors: themeData.gradient_colors || [themeData.fallback_color],
|
|
66
|
+
backgroundImage: themeData.background_asset
|
|
67
|
+
? themeData.background_asset.image.uri
|
|
68
|
+
: null
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error("No theme data found");
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.catch(function (err) {
|
|
75
|
+
log.error("getThreadTheme", err);
|
|
76
|
+
return callback(err);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return returnPromise;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Please give credits if reused. */
|
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,139 @@
|
|
|
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
|
+
const utils = require("../utils");
|
|
14
|
+
|
|
15
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
16
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Instagram: @sheikh.tamim_lover */
|
|
17
|
+
return function searchFriends(searchQuery, callback) {
|
|
18
|
+
let resolveFunc = function () {};
|
|
19
|
+
let rejectFunc = function () {};
|
|
20
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
21
|
+
resolveFunc = resolve;
|
|
22
|
+
rejectFunc = reject;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!callback) {
|
|
26
|
+
callback = function (err, result) {
|
|
27
|
+
if (err) {
|
|
28
|
+
return rejectFunc(err);
|
|
29
|
+
}
|
|
30
|
+
resolveFunc(result);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!searchQuery || searchQuery.trim().length === 0) {
|
|
35
|
+
return callback(new Error("Search query cannot be empty"));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Enhanced form data based on captured API
|
|
39
|
+
const form = {
|
|
40
|
+
av: ctx.userID,
|
|
41
|
+
__aaid: 0,
|
|
42
|
+
__user: ctx.userID,
|
|
43
|
+
__a: 1,
|
|
44
|
+
__req: utils.getSignatureID(),
|
|
45
|
+
__hs: "20358.HYP:comet_pkg.2.1...0",
|
|
46
|
+
dpr: 1,
|
|
47
|
+
__ccg: "EXCELLENT",
|
|
48
|
+
__rev: "1027694919",
|
|
49
|
+
__s: utils.getSignatureID(),
|
|
50
|
+
__hsi: "7554748243252799467",
|
|
51
|
+
__comet_req: 15,
|
|
52
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
53
|
+
jazoest: ctx.ttstamp,
|
|
54
|
+
lsd: ctx.fb_dtsg,
|
|
55
|
+
__spin_r: "1027694919",
|
|
56
|
+
__spin_b: "trunk",
|
|
57
|
+
__spin_t: Date.now(),
|
|
58
|
+
fb_api_caller_class: "RelayModern",
|
|
59
|
+
fb_api_req_friendly_name: "ProfileCometAppCollectionSelfFriendsListRendererPaginationQuery",
|
|
60
|
+
variables: JSON.stringify({
|
|
61
|
+
count: 20, // Increased count for better results
|
|
62
|
+
cursor: null,
|
|
63
|
+
scale: 1,
|
|
64
|
+
search: searchQuery.trim(),
|
|
65
|
+
id: "YXBwX2NvbGxlY3Rpb246cGZiaWQwMkJSM3NDeXRjNkJIeVVXem9OeUxNcjNoYnVDclRFZkdCcVlEaXZuSlZYOUNLR2pXVmRyYTQ4U29FalJTVzduMm03NlhDa0xEQXAybVVUenF6RXZraGc3ZHkyaGw="
|
|
66
|
+
}),
|
|
67
|
+
server_timestamps: true,
|
|
68
|
+
doc_id: "31767020089578751"
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
defaultFuncs
|
|
72
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
73
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
74
|
+
.then(function (resData) {
|
|
75
|
+
if (!resData || !resData.data) {
|
|
76
|
+
throw { error: "searchFriends returned empty object." };
|
|
77
|
+
}
|
|
78
|
+
if (resData.error) {
|
|
79
|
+
throw resData;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const friendsData = resData.data.node?.pageItems?.edges || [];
|
|
83
|
+
const formattedFriends = friendsData.map(edge => {
|
|
84
|
+
const friend = edge.node;
|
|
85
|
+
const friendUser = friend.node || friend;
|
|
86
|
+
|
|
87
|
+
// Extract mutual friends count from subtitle
|
|
88
|
+
let mutualFriends = 0;
|
|
89
|
+
if (friend.subtitle_text?.text) {
|
|
90
|
+
const mutualMatch = friend.subtitle_text.text.match(/(\d+)\s+mutual\s+friend/i);
|
|
91
|
+
if (mutualMatch) {
|
|
92
|
+
mutualFriends = parseInt(mutualMatch[1]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
userID: friendUser.id || friend.id,
|
|
98
|
+
name: friend.title?.text || friendUser.name || friend.name,
|
|
99
|
+
profilePicture: friend.image?.uri || null,
|
|
100
|
+
profileUrl: friend.url || friendUser.url,
|
|
101
|
+
subtitle: friend.subtitle_text?.text || "",
|
|
102
|
+
mutualFriends: mutualFriends,
|
|
103
|
+
// Additional fields from the captured API
|
|
104
|
+
cursor: edge.cursor,
|
|
105
|
+
friendshipStatus: friendUser.friendship_status || "UNKNOWN",
|
|
106
|
+
gender: friendUser.gender || null,
|
|
107
|
+
shortName: friendUser.short_name || null
|
|
108
|
+
};
|
|
109
|
+
}).filter(friend => friend.userID && friend.name);
|
|
110
|
+
|
|
111
|
+
// Sort by relevance (exact matches first, then by mutual friends)
|
|
112
|
+
formattedFriends.sort((a, b) => {
|
|
113
|
+
const queryLower = searchQuery.toLowerCase();
|
|
114
|
+
const aNameLower = a.name.toLowerCase();
|
|
115
|
+
const bNameLower = b.name.toLowerCase();
|
|
116
|
+
|
|
117
|
+
// Exact matches first
|
|
118
|
+
if (aNameLower === queryLower && bNameLower !== queryLower) return -1;
|
|
119
|
+
if (bNameLower === queryLower && aNameLower !== queryLower) return 1;
|
|
120
|
+
|
|
121
|
+
// Then by starts with
|
|
122
|
+
if (aNameLower.startsWith(queryLower) && !bNameLower.startsWith(queryLower)) return -1;
|
|
123
|
+
if (bNameLower.startsWith(queryLower) && !aNameLower.startsWith(queryLower)) return 1;
|
|
124
|
+
|
|
125
|
+
// Then by mutual friends count
|
|
126
|
+
return b.mutualFriends - a.mutualFriends;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
callback(null, formattedFriends);
|
|
130
|
+
})
|
|
131
|
+
.catch(function (err) {
|
|
132
|
+
console.error("searchFriends error:", err);
|
|
133
|
+
return callback(err);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return returnPromise;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Please give credits if reused. */
|
|
@@ -0,0 +1,113 @@
|
|
|
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 sendFriendRequest(userID, 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 (!userID) {
|
|
34
|
+
return callback({ error: "User ID is required" });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var form = {
|
|
38
|
+
av: ctx.userID,
|
|
39
|
+
__aaid: 0,
|
|
40
|
+
__user: ctx.userID,
|
|
41
|
+
__a: 1,
|
|
42
|
+
__req: utils.getSignatureID(),
|
|
43
|
+
__hs: "20353.HYP:comet_pkg.2.1...0",
|
|
44
|
+
dpr: 1,
|
|
45
|
+
__ccg: "EXCELLENT",
|
|
46
|
+
__rev: "1027405870",
|
|
47
|
+
__s: utils.getSignatureID(),
|
|
48
|
+
__hsi: "7552782279085106329",
|
|
49
|
+
__comet_req: 15,
|
|
50
|
+
fb_dtsg: ctx.fb_dtsg,
|
|
51
|
+
jazoest: ctx.ttstamp,
|
|
52
|
+
lsd: ctx.fb_dtsg,
|
|
53
|
+
__spin_r: "1027405870",
|
|
54
|
+
__spin_b: "trunk",
|
|
55
|
+
__spin_t: Date.now(),
|
|
56
|
+
__crn: "comet.fbweb.CometFriendingRoute",
|
|
57
|
+
fb_api_caller_class: "RelayModern",
|
|
58
|
+
fb_api_req_friendly_name: "FriendingCometFriendRequestSendMutation",
|
|
59
|
+
variables: JSON.stringify({
|
|
60
|
+
input: {
|
|
61
|
+
click_correlation_id: Date.now().toString(),
|
|
62
|
+
click_proof_validation_result: '{"validated":true}',
|
|
63
|
+
friend_requestee_ids: [userID.toString()],
|
|
64
|
+
friending_channel: "FRIENDS_HOME_MAIN",
|
|
65
|
+
warn_ack_for_ids: [],
|
|
66
|
+
actor_id: ctx.userID,
|
|
67
|
+
client_mutation_id: Math.floor(Math.random() * 10).toString()
|
|
68
|
+
},
|
|
69
|
+
scale: 1
|
|
70
|
+
}),
|
|
71
|
+
server_timestamps: true,
|
|
72
|
+
doc_id: "24614631718227645"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
defaultFuncs
|
|
76
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
77
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
78
|
+
.then(function (resData) {
|
|
79
|
+
if (resData.error) {
|
|
80
|
+
throw resData;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (resData.data && resData.data.friend_request_send) {
|
|
84
|
+
var responseData = resData.data.friend_request_send;
|
|
85
|
+
if (responseData.friend_requestees && responseData.friend_requestees.length > 0) {
|
|
86
|
+
var requestee = responseData.friend_requestees[0];
|
|
87
|
+
var result = {
|
|
88
|
+
userID: requestee.id,
|
|
89
|
+
friendshipStatus: requestee.friendship_status,
|
|
90
|
+
success: requestee.friendship_status === "OUTGOING_REQUEST"
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (requestee.profile_action) {
|
|
94
|
+
result.actionTitle = requestee.profile_action.title ? requestee.profile_action.title.text : "";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return callback(null, result);
|
|
98
|
+
} else {
|
|
99
|
+
return callback({ error: "No friend request data received" });
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
return callback({ error: "Invalid response format" });
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
.catch(function (err) {
|
|
106
|
+
log.error("sendFriendRequest", err);
|
|
107
|
+
return callback(err);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return returnPromise;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Please give credits if reused. */
|
|
@@ -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. */
|