@cexy/hoonfca 1.0.7 → 2.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/examples/login-with-cookies.js +102 -0
- package/examples/verify.js +70 -0
- package/index.js +2 -8
- package/package.json +82 -61
- package/src/{api/action → apis}/addExternalModule.js +24 -25
- 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/changeThreadEmoji.js +53 -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/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/{api/users → apis}/getUserInfoV2.js +146 -134
- 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/{api/messaging → apis}/markAsRead.js +99 -88
- 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 -23
- 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 +214 -99
- 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/CHANGELOG.md +0 -181
- package/DOCS.md +0 -2636
- package/LICENSE-MIT +0 -21
- package/README.md +0 -632
- package/func/checkUpdate.js +0 -233
- package/func/logger.js +0 -48
- package/index.d.ts +0 -746
- package/module/config.js +0 -67
- package/module/login.js +0 -154
- package/module/loginHelper.js +0 -1034
- package/module/options.js +0 -54
- package/src/api/action/changeAvatar.js +0 -137
- package/src/api/action/changeBio.js +0 -75
- package/src/api/action/enableAutoSaveAppState.js +0 -73
- package/src/api/action/getCurrentUserID.js +0 -7
- package/src/api/action/handleFriendRequest.js +0 -57
- package/src/api/action/logout.js +0 -76
- package/src/api/action/refreshFb_dtsg.js +0 -48
- package/src/api/action/setPostReaction.js +0 -106
- package/src/api/action/unfriend.js +0 -54
- package/src/api/http/httpGet.js +0 -46
- package/src/api/http/httpPost.js +0 -52
- package/src/api/http/postFormData.js +0 -47
- package/src/api/messaging/addUserToGroup.js +0 -68
- package/src/api/messaging/changeAdminStatus.js +0 -122
- package/src/api/messaging/changeArchivedStatus.js +0 -55
- package/src/api/messaging/changeBlockedStatus.js +0 -48
- package/src/api/messaging/changeGroupImage.js +0 -90
- package/src/api/messaging/changeNickname.js +0 -70
- package/src/api/messaging/changeThreadColor.js +0 -79
- package/src/api/messaging/changeThreadEmoji.js +0 -106
- package/src/api/messaging/createNewGroup.js +0 -88
- package/src/api/messaging/createPoll.js +0 -43
- package/src/api/messaging/createThemeAI.js +0 -98
- package/src/api/messaging/deleteMessage.js +0 -56
- package/src/api/messaging/deleteThread.js +0 -56
- package/src/api/messaging/editMessage.js +0 -68
- package/src/api/messaging/forwardAttachment.js +0 -51
- package/src/api/messaging/getEmojiUrl.js +0 -29
- package/src/api/messaging/getFriendsList.js +0 -82
- package/src/api/messaging/getMessage.js +0 -829
- package/src/api/messaging/getThemePictures.js +0 -62
- package/src/api/messaging/handleMessageRequest.js +0 -65
- package/src/api/messaging/markAsDelivered.js +0 -57
- package/src/api/messaging/markAsReadAll.js +0 -49
- package/src/api/messaging/markAsSeen.js +0 -61
- package/src/api/messaging/muteThread.js +0 -50
- package/src/api/messaging/removeUserFromGroup.js +0 -106
- package/src/api/messaging/resolvePhotoUrl.js +0 -43
- package/src/api/messaging/scheduler.js +0 -264
- package/src/api/messaging/searchForThread.js +0 -52
- package/src/api/messaging/sendMessage.js +0 -272
- package/src/api/messaging/sendTypingIndicator.js +0 -67
- package/src/api/messaging/setMessageReaction.js +0 -76
- package/src/api/messaging/setTitle.js +0 -119
- package/src/api/messaging/shareContact.js +0 -49
- package/src/api/messaging/threadColors.js +0 -128
- package/src/api/messaging/unsendMessage.js +0 -81
- package/src/api/messaging/uploadAttachment.js +0 -94
- package/src/api/socket/core/connectMqtt.js +0 -255
- package/src/api/socket/core/emitAuth.js +0 -106
- package/src/api/socket/core/getSeqID.js +0 -40
- package/src/api/socket/core/getTaskResponseData.js +0 -22
- package/src/api/socket/core/markDelivery.js +0 -12
- package/src/api/socket/core/parseDelta.js +0 -391
- package/src/api/socket/detail/buildStream.js +0 -208
- package/src/api/socket/detail/constants.js +0 -24
- package/src/api/socket/listenMqtt.js +0 -364
- package/src/api/socket/middleware/index.js +0 -216
- package/src/api/threads/getThreadHistory.js +0 -664
- package/src/api/threads/getThreadInfo.js +0 -438
- package/src/api/threads/getThreadList.js +0 -293
- package/src/api/threads/getThreadPictures.js +0 -78
- package/src/api/users/getUserID.js +0 -65
- package/src/api/users/getUserInfo.js +0 -327
- package/src/core/sendReqMqtt.js +0 -96
- package/src/database/models/index.js +0 -87
- package/src/database/models/thread.js +0 -45
- package/src/database/models/user.js +0 -46
- package/src/database/threadData.js +0 -98
- package/src/database/userData.js +0 -89
- package/src/utils/client.js +0 -320
- package/src/utils/format.js +0 -1115
- package/src/utils/request.js +0 -305
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
6
|
+
return function setThreadThemeMqtt(threadID, themeFBID, callback) {
|
|
7
|
+
if (!ctx.mqttClient) {
|
|
8
|
+
throw new Error('Not connected to MQTT');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
ctx.wsReqNumber += 1;
|
|
12
|
+
let baseTaskNumber = ++ctx.wsTaskNumber;
|
|
13
|
+
|
|
14
|
+
const makeTask = (label, queueName, extraPayload = {}) => ({
|
|
15
|
+
failure_count: null,
|
|
16
|
+
label: String(label),
|
|
17
|
+
payload: JSON.stringify({
|
|
18
|
+
thread_key: threadID,
|
|
19
|
+
theme_fbid: themeFBID,
|
|
20
|
+
sync_group: 1,
|
|
21
|
+
...extraPayload,
|
|
22
|
+
}),
|
|
23
|
+
queue_name: typeof queueName === 'string' ? queueName : JSON.stringify(queueName),
|
|
24
|
+
task_id: baseTaskNumber++,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const messages = [
|
|
28
|
+
{
|
|
29
|
+
label: 1013,
|
|
30
|
+
queue: ['ai_generated_theme', String(threadID)],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 1037,
|
|
34
|
+
queue: ['msgr_custom_thread_theme', String(threadID)],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: 1028,
|
|
38
|
+
queue: ['thread_theme_writer', String(threadID)],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: 43,
|
|
42
|
+
queue: 'thread_theme',
|
|
43
|
+
extra: { source: null, payload: null },
|
|
44
|
+
},
|
|
45
|
+
].map(({ label, queue, extra }) => {
|
|
46
|
+
ctx.wsReqNumber += 1;
|
|
47
|
+
return {
|
|
48
|
+
app_id: '772021112871879',
|
|
49
|
+
payload: JSON.stringify({
|
|
50
|
+
epoch_id: parseInt(utils.generateOfflineThreadingID()),
|
|
51
|
+
tasks: [makeTask(label, queue, extra)],
|
|
52
|
+
version_id: '24227364673632991',
|
|
53
|
+
}),
|
|
54
|
+
request_id: ctx.wsReqNumber,
|
|
55
|
+
type: 3,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Return promise if no callback provided
|
|
60
|
+
if (!callback) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
try {
|
|
63
|
+
messages.forEach((msg, idx) => {
|
|
64
|
+
ctx.mqttClient.publish(
|
|
65
|
+
'/ls_req',
|
|
66
|
+
JSON.stringify(msg),
|
|
67
|
+
{ qos: 1, retain: false },
|
|
68
|
+
idx === messages.length - 1 ? (err) => {
|
|
69
|
+
if (err) reject(err);
|
|
70
|
+
else resolve();
|
|
71
|
+
} : undefined
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
reject(err);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Callback mode
|
|
81
|
+
try {
|
|
82
|
+
messages.forEach((msg, idx) => {
|
|
83
|
+
ctx.mqttClient.publish(
|
|
84
|
+
'/ls_req',
|
|
85
|
+
JSON.stringify(msg),
|
|
86
|
+
{ qos: 1, retain: false },
|
|
87
|
+
idx === messages.length - 1 ? callback : undefined
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
} catch (err) {
|
|
91
|
+
callback(err);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
5
|
+
function formatPreviewResult(data) {
|
|
6
|
+
if (data.errors) {
|
|
7
|
+
throw data.errors[0];
|
|
8
|
+
}
|
|
9
|
+
const previewData = data.data?.xma_preview_data;
|
|
10
|
+
if (!previewData) {
|
|
11
|
+
throw { error: "Could not generate a preview for this post." };
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
postID: previewData.post_id,
|
|
15
|
+
header: previewData.header_title,
|
|
16
|
+
subtitle: previewData.subtitle_text,
|
|
17
|
+
title: previewData.title_text,
|
|
18
|
+
previewImage: previewData.preview_url,
|
|
19
|
+
favicon: previewData.favicon_url,
|
|
20
|
+
headerImage: previewData.header_image_url
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
25
|
+
return async function getPostPreview(postID, callback) {
|
|
26
|
+
let resolveFunc, rejectFunc;
|
|
27
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
28
|
+
resolveFunc = resolve;
|
|
29
|
+
rejectFunc = reject;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const cb = (err, data) => {
|
|
33
|
+
if (callback) callback(err, data);
|
|
34
|
+
if (err) return rejectFunc(err);
|
|
35
|
+
resolveFunc(data);
|
|
36
|
+
};
|
|
37
|
+
if (!postID) {
|
|
38
|
+
cb({ error: "A postID is required to generate a preview." });
|
|
39
|
+
return returnPromise;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const variables = {
|
|
43
|
+
shareable_id: postID.toString(),
|
|
44
|
+
scale: 3,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Use configurable doc_id or default (may be outdated)
|
|
48
|
+
// To update:
|
|
49
|
+
// 1. Open Facebook Messenger in browser
|
|
50
|
+
// 2. Open DevTools (F12) → Network tab → Filter by "graphql"
|
|
51
|
+
// 3. Trigger a share/preview action
|
|
52
|
+
// 4. Look for CometXMAProxyShareablePreviewQuery request
|
|
53
|
+
// 5. Copy the doc_id value from the request payload
|
|
54
|
+
// 6. Set ctx.options.sharePreviewDocId = 'NEW_DOC_ID' when logging in
|
|
55
|
+
//
|
|
56
|
+
// Known doc_ids (may expire):
|
|
57
|
+
// - 28939050904374351 (expired as of Nov 2024)
|
|
58
|
+
// - Check ws3-fca or @dongdev packages for potential updates
|
|
59
|
+
const docId = ctx.options?.sharePreviewDocId || '28939050904374351';
|
|
60
|
+
|
|
61
|
+
const form = {
|
|
62
|
+
fb_api_caller_class: 'RelayModern',
|
|
63
|
+
fb_api_req_friendly_name: 'CometXMAProxyShareablePreviewQuery',
|
|
64
|
+
variables: JSON.stringify(variables),
|
|
65
|
+
doc_id: docId
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const resData = await defaultFuncs
|
|
70
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
71
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
72
|
+
|
|
73
|
+
// Check for persisted query not found error (case-insensitive)
|
|
74
|
+
if (resData?.errors) {
|
|
75
|
+
const persistedQueryError = resData.errors.find(e => {
|
|
76
|
+
const msg = (e.message || '').toLowerCase();
|
|
77
|
+
return msg.includes('persistedquerynotfound') ||
|
|
78
|
+
msg.includes('document') && msg.includes('not found') ||
|
|
79
|
+
msg.includes('persisted query');
|
|
80
|
+
});
|
|
81
|
+
if (persistedQueryError) {
|
|
82
|
+
const error = {
|
|
83
|
+
error: "Facebook GraphQL doc_id expired. Please update ctx.options.sharePreviewDocId",
|
|
84
|
+
details: "Capture the current doc_id from Messenger web traffic (inspect CometXMAProxyShareablePreviewQuery request)",
|
|
85
|
+
currentDocId: docId,
|
|
86
|
+
fbError: persistedQueryError.message
|
|
87
|
+
};
|
|
88
|
+
utils.error("getPostPreview", error);
|
|
89
|
+
cb(error);
|
|
90
|
+
return returnPromise;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const result = formatPreviewResult(resData);
|
|
95
|
+
cb(null, result);
|
|
96
|
+
return returnPromise;
|
|
97
|
+
} catch (err) {
|
|
98
|
+
// Add helpful context for common failure modes
|
|
99
|
+
if (err.message?.includes('UNHANDLED_REJECTION') || err.message?.includes('not found')) {
|
|
100
|
+
err.hint = "The GraphQL doc_id may be outdated. Set ctx.options.sharePreviewDocId with current value from Messenger traffic.";
|
|
101
|
+
}
|
|
102
|
+
utils.error("getPostPreview", err);
|
|
103
|
+
cb(err);
|
|
104
|
+
return returnPromise;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* eslint-disable linebreak-style */
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const utils = require('../utils');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module shareContact
|
|
8
|
+
* @param {Object} defaultFuncs - The default functions provided by the API.
|
|
9
|
+
* @param {Object} api - The full API object.
|
|
10
|
+
* @param {Object} ctx - The context object.
|
|
11
|
+
* @returns {function(text: string, senderID: string, threadID: string, callback: Function): void} - A function to share a contact.
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
14
|
+
/**
|
|
15
|
+
* Shares a user's contact information into a specific thread via MQTT.
|
|
16
|
+
* @param {string} [text] - An optional message to send along with the contact card.
|
|
17
|
+
* @param {string} senderID - The Facebook user ID of the contact you want to share.
|
|
18
|
+
* @param {string} threadID - The ID of the thread where the contact will be shared.
|
|
19
|
+
* @param {Function} [callback] - An optional callback function to be executed.
|
|
20
|
+
*/
|
|
21
|
+
return function shareContact(text, senderID, threadID, callback) {
|
|
22
|
+
if (!ctx.mqttClient) {
|
|
23
|
+
throw new Error('Not connected to MQTT');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ctx.wsReqNumber ??= 0;
|
|
27
|
+
ctx.wsTaskNumber ??= 0;
|
|
28
|
+
|
|
29
|
+
ctx.wsReqNumber += 1;
|
|
30
|
+
ctx.wsTaskNumber += 1;
|
|
31
|
+
|
|
32
|
+
const queryPayload = {
|
|
33
|
+
contact_id: senderID,
|
|
34
|
+
sync_group: 1,
|
|
35
|
+
text: text || "",
|
|
36
|
+
thread_id: threadID
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const query = {
|
|
40
|
+
failure_count: null,
|
|
41
|
+
label: '359',
|
|
42
|
+
payload: JSON.stringify(queryPayload),
|
|
43
|
+
queue_name: 'messenger_contact_sharing',
|
|
44
|
+
task_id: Math.random() * 1001 << 0,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const context = {
|
|
48
|
+
app_id: '2220391788200892',
|
|
49
|
+
payload: {
|
|
50
|
+
tasks: [query],
|
|
51
|
+
epoch_id: utils.generateOfflineThreadingID(),
|
|
52
|
+
version_id: '7214102258676893',
|
|
53
|
+
},
|
|
54
|
+
request_id: ctx.wsReqNumber,
|
|
55
|
+
type: 3,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
context.payload = JSON.stringify(context.payload);
|
|
59
|
+
|
|
60
|
+
if (typeof callback === 'function') {
|
|
61
|
+
ctx.callback_Task[ctx.wsReqNumber] = { callback, type: "shareContact" };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ctx.mqttClient.publish('/ls_req', JSON.stringify(context), { qos: 1, retain: false });
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sticker API Module
|
|
5
|
+
* Provides access to Facebook's GraphQL-based sticker endpoints.
|
|
6
|
+
* Made by @ChoruOfficial
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const utils = require('../utils');
|
|
10
|
+
/**
|
|
11
|
+
* Format the sticker pack list (store or tray)
|
|
12
|
+
* @param {object} data - Raw GraphQL response
|
|
13
|
+
* @returns {{ packs: Array<{id: string, name: string, thumbnail: string}>, page_info: object, store_id?: string }}
|
|
14
|
+
*/
|
|
15
|
+
function formatPackList(data) {
|
|
16
|
+
const trayPacks = data?.data?.picker_plugins?.sticker_picker?.sticker_store?.tray_packs?.edges;
|
|
17
|
+
const storePacks = data?.data?.viewer?.sticker_store?.available_packs?.edges;
|
|
18
|
+
|
|
19
|
+
const packData = storePacks || trayPacks;
|
|
20
|
+
if (!packData || !packData.edges) return { packs: [], page_info: { has_next_page: false } };
|
|
21
|
+
|
|
22
|
+
const formattedPacks = packData.edges.map(edge => edge.node ? ({
|
|
23
|
+
id: edge.node.id,
|
|
24
|
+
name: edge.node.name,
|
|
25
|
+
thumbnail: edge.node.thumbnail_image?.uri
|
|
26
|
+
}) : null).filter(Boolean);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
packs: formattedPacks,
|
|
30
|
+
page_info: packData.page_info,
|
|
31
|
+
store_id: data?.data?.viewer?.sticker_store?.id
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Format search result stickers
|
|
37
|
+
* @param {object} data - Raw GraphQL response
|
|
38
|
+
* @returns {Array<Object>}
|
|
39
|
+
*/
|
|
40
|
+
function formatStickerSearchResults(data) {
|
|
41
|
+
const stickers = data?.data?.sticker_search?.sticker_results?.edges;
|
|
42
|
+
if (!stickers) return [];
|
|
43
|
+
return stickers.map(edge => edge.node ? ({
|
|
44
|
+
type: "sticker",
|
|
45
|
+
ID: edge.node.id,
|
|
46
|
+
url: edge.node.image?.uri,
|
|
47
|
+
animatedUrl: edge.node.animated_image?.uri,
|
|
48
|
+
packID: edge.node.pack?.id,
|
|
49
|
+
label: edge.node.label || edge.node.accessibility_label,
|
|
50
|
+
stickerID: edge.node.id
|
|
51
|
+
}) : null).filter(Boolean);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format sticker pack content
|
|
56
|
+
* @param {object} data - Raw GraphQL response
|
|
57
|
+
* @returns {Array<Object>}
|
|
58
|
+
*/
|
|
59
|
+
function formatStickerPackResults(data) {
|
|
60
|
+
const stickers = data?.data?.sticker_pack?.stickers?.edges;
|
|
61
|
+
if (!stickers) return [];
|
|
62
|
+
return stickers.map(edge => edge.node ? ({
|
|
63
|
+
type: "sticker",
|
|
64
|
+
ID: edge.node.id,
|
|
65
|
+
url: edge.node.image?.uri,
|
|
66
|
+
animatedUrl: edge.node.animated_image?.uri,
|
|
67
|
+
packID: edge.node.pack?.id,
|
|
68
|
+
label: edge.node.label || edge.node.accessibility_label,
|
|
69
|
+
stickerID: edge.node.id
|
|
70
|
+
}) : null).filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Format AI-generated stickers
|
|
75
|
+
* @param {object} data - Raw GraphQL response
|
|
76
|
+
* @returns {Array<Object>}
|
|
77
|
+
*/
|
|
78
|
+
function formatAiStickers(data) {
|
|
79
|
+
const stickers = data?.data?.xfb_trending_generated_ai_stickers?.nodes;
|
|
80
|
+
if (!stickers) return [];
|
|
81
|
+
return stickers.map(node => ({
|
|
82
|
+
type: "sticker",
|
|
83
|
+
ID: node.id,
|
|
84
|
+
url: node.url,
|
|
85
|
+
label: node.label,
|
|
86
|
+
stickerID: node.id
|
|
87
|
+
})).filter(Boolean);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
91
|
+
/**
|
|
92
|
+
* Make a GraphQL request and handle login and error checking
|
|
93
|
+
* @param {object} form - Form data for the request
|
|
94
|
+
* @returns {Promise<object>}
|
|
95
|
+
*/
|
|
96
|
+
async function makeRequest(form) {
|
|
97
|
+
const resData = await defaultFuncs.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
|
|
98
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
99
|
+
if (!resData) throw new Error("GraphQL request returned no data.");
|
|
100
|
+
if (resData.errors) {
|
|
101
|
+
utils.error("StickerAPI GraphQL Error", resData.errors[0].message);
|
|
102
|
+
throw resData.errors[0];
|
|
103
|
+
}
|
|
104
|
+
return resData;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
/**
|
|
109
|
+
* Search for stickers by keyword
|
|
110
|
+
* @param {string} query - Search term
|
|
111
|
+
* @returns {Promise<Array<Object>>}
|
|
112
|
+
*/
|
|
113
|
+
search: async function(query) {
|
|
114
|
+
const form = {
|
|
115
|
+
fb_api_caller_class: 'RelayModern',
|
|
116
|
+
fb_api_req_friendly_name: 'CometStickerPickerSearchResultsRootQuery',
|
|
117
|
+
variables: JSON.stringify({
|
|
118
|
+
scale: 3,
|
|
119
|
+
search_query: query,
|
|
120
|
+
sticker_height: 128,
|
|
121
|
+
sticker_width: 128,
|
|
122
|
+
stickerInterface: "MESSAGES"
|
|
123
|
+
}),
|
|
124
|
+
doc_id: '24004987559125954'
|
|
125
|
+
};
|
|
126
|
+
const res = await makeRequest(form);
|
|
127
|
+
return formatStickerSearchResults(res);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* List user's sticker packs
|
|
132
|
+
* @returns {Promise<Array<Object>>}
|
|
133
|
+
*/
|
|
134
|
+
listPacks: async function() {
|
|
135
|
+
const form = {
|
|
136
|
+
fb_api_caller_class: 'RelayModern',
|
|
137
|
+
fb_api_req_friendly_name: 'CometStickerPickerCardQuery',
|
|
138
|
+
variables: JSON.stringify({ scale: 3, stickerInterface: "MESSAGES" }),
|
|
139
|
+
doc_id: '10095807770482952'
|
|
140
|
+
};
|
|
141
|
+
const res = await makeRequest(form);
|
|
142
|
+
return formatPackList(res).packs;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get all available sticker packs from the store (with pagination)
|
|
147
|
+
* @returns {Promise<Array<Object>>}
|
|
148
|
+
*/
|
|
149
|
+
getStorePacks: async function() {
|
|
150
|
+
utils.log("Starting to fetch all sticker packs from store...");
|
|
151
|
+
let allPacks = [];
|
|
152
|
+
|
|
153
|
+
const initialForm = {
|
|
154
|
+
fb_api_caller_class: 'RelayModern',
|
|
155
|
+
fb_api_req_friendly_name: 'CometStickersStoreDialogQuery',
|
|
156
|
+
variables: JSON.stringify({}),
|
|
157
|
+
doc_id: '29237828849196584'
|
|
158
|
+
};
|
|
159
|
+
let res = await makeRequest(initialForm);
|
|
160
|
+
let { packs, page_info, store_id } = formatPackList(res);
|
|
161
|
+
allPacks.push(...packs);
|
|
162
|
+
utils.log(`Fetched first page with ${packs.length} packs.`);
|
|
163
|
+
|
|
164
|
+
while (page_info && page_info.has_next_page) {
|
|
165
|
+
utils.log("Fetching next page with cursor:", page_info.end_cursor);
|
|
166
|
+
|
|
167
|
+
const paginatedForm = {
|
|
168
|
+
fb_api_caller_class: 'RelayModern',
|
|
169
|
+
fb_api_req_friendly_name: 'CometStickersStorePackListPaginationQuery',
|
|
170
|
+
variables: JSON.stringify({
|
|
171
|
+
count: 20,
|
|
172
|
+
cursor: page_info.end_cursor,
|
|
173
|
+
id: store_id
|
|
174
|
+
}),
|
|
175
|
+
doc_id: '9898634630218439'
|
|
176
|
+
};
|
|
177
|
+
res = await makeRequest(paginatedForm);
|
|
178
|
+
let paginatedResult = formatPackList(res);
|
|
179
|
+
allPacks.push(...paginatedResult.packs);
|
|
180
|
+
page_info = paginatedResult.page_info;
|
|
181
|
+
utils.log(`Fetched ${paginatedResult.packs.length} more packs. Total now: ${allPacks.length}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
utils.log(`Finished fetching. Total unique packs found: ${allPacks.length}`);
|
|
185
|
+
return allPacks;
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Merge user's and store sticker packs into one list
|
|
190
|
+
* @returns {Promise<Array<Object>>}
|
|
191
|
+
*/
|
|
192
|
+
listAllPacks: async function() {
|
|
193
|
+
const [myPacks, storePacks] = await Promise.all([
|
|
194
|
+
this.listPacks(),
|
|
195
|
+
this.getStorePacks()
|
|
196
|
+
]);
|
|
197
|
+
const allPacksMap = new Map();
|
|
198
|
+
myPacks.forEach(pack => allPacksMap.set(pack.id, pack));
|
|
199
|
+
storePacks.forEach(pack => allPacksMap.set(pack.id, pack));
|
|
200
|
+
return Array.from(allPacksMap.values());
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Add a sticker pack by ID
|
|
205
|
+
* @param {string} packID - The ID of the sticker pack
|
|
206
|
+
* @returns {Promise<Object>}
|
|
207
|
+
*/
|
|
208
|
+
addPack: async function(packID) {
|
|
209
|
+
const form = {
|
|
210
|
+
fb_api_caller_class: 'RelayModern',
|
|
211
|
+
fb_api_req_friendly_name: 'CometStickersStorePackMutationAddMutation',
|
|
212
|
+
variables: JSON.stringify({
|
|
213
|
+
input: {
|
|
214
|
+
pack_id: packID,
|
|
215
|
+
actor_id: ctx.userID,
|
|
216
|
+
client_mutation_id: Math.round(Math.random() * 10).toString()
|
|
217
|
+
}
|
|
218
|
+
}),
|
|
219
|
+
doc_id: '9877489362345320'
|
|
220
|
+
};
|
|
221
|
+
const res = await makeRequest(form);
|
|
222
|
+
return res.data.sticker_pack_add.sticker_pack;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get all stickers in a pack
|
|
227
|
+
* @param {string} packID - Sticker pack ID
|
|
228
|
+
* @returns {Promise<Array<Object>>}
|
|
229
|
+
*/
|
|
230
|
+
getStickersInPack: async function(packID) {
|
|
231
|
+
const form = {
|
|
232
|
+
fb_api_caller_class: 'RelayModern',
|
|
233
|
+
fb_api_req_friendly_name: 'CometStickerPickerPackContentRootQuery',
|
|
234
|
+
variables: JSON.stringify({ packID, stickerWidth: 128, stickerHeight: 128, scale: 3 }),
|
|
235
|
+
doc_id: '23982341384707469'
|
|
236
|
+
};
|
|
237
|
+
const res = await makeRequest(form);
|
|
238
|
+
return formatStickerPackResults(res);
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get trending AI-generated stickers
|
|
243
|
+
* @param {{ limit?: number }} options - Options object
|
|
244
|
+
* @returns {Promise<Array<Object>>}
|
|
245
|
+
*/
|
|
246
|
+
getAiStickers: async function({ limit = 10 } = {}) {
|
|
247
|
+
const form = {
|
|
248
|
+
fb_api_caller_class: 'RelayModern',
|
|
249
|
+
fb_api_req_friendly_name: 'CometStickerPickerStickerGeneratedCardQuery',
|
|
250
|
+
variables: JSON.stringify({ limit }),
|
|
251
|
+
doc_id: '24151467751156443'
|
|
252
|
+
};
|
|
253
|
+
const res = await makeRequest(form);
|
|
254
|
+
return formatAiStickers(res);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
};
|