ws3-fca
Version:
A node.js package for automating Facebook Messenger bot, and is one of the most advanced next-generation Facebook Chat API (FCA) by @NethWs3Dev & @ExocoreCommunity
258 lines (237 loc) • 9.4 kB
JavaScript
"use strict";
/**
* Sticker API Module
* Provides access to Facebook's GraphQL-based sticker endpoints.
* Made by @ChoruOfficial
*/
const utils = require('../../../utils');
/**
* Format the sticker pack list (store or tray)
* @param {object} data - Raw GraphQL response
* @returns {{ packs: Array<{id: string, name: string, thumbnail: string}>, page_info: object, store_id?: string }}
*/
function formatPackList(data) {
const trayPacks = data?.data?.picker_plugins?.sticker_picker?.sticker_store?.tray_packs?.edges;
const storePacks = data?.data?.viewer?.sticker_store?.available_packs?.edges;
const packData = storePacks || trayPacks;
if (!packData || !packData.edges) return { packs: [], page_info: { has_next_page: false } };
const formattedPacks = packData.edges.map(edge => edge.node ? ({
id: edge.node.id,
name: edge.node.name,
thumbnail: edge.node.thumbnail_image?.uri
}) : null).filter(Boolean);
return {
packs: formattedPacks,
page_info: packData.page_info,
store_id: data?.data?.viewer?.sticker_store?.id
};
}
/**
* Format search result stickers
* @param {object} data - Raw GraphQL response
* @returns {Array<Object>}
*/
function formatStickerSearchResults(data) {
const stickers = data?.data?.sticker_search?.sticker_results?.edges;
if (!stickers) return [];
return stickers.map(edge => edge.node ? ({
type: "sticker",
ID: edge.node.id,
url: edge.node.image?.uri,
animatedUrl: edge.node.animated_image?.uri,
packID: edge.node.pack?.id,
label: edge.node.label || edge.node.accessibility_label,
stickerID: edge.node.id
}) : null).filter(Boolean);
}
/**
* Format sticker pack content
* @param {object} data - Raw GraphQL response
* @returns {Array<Object>}
*/
function formatStickerPackResults(data) {
const stickers = data?.data?.sticker_pack?.stickers?.edges;
if (!stickers) return [];
return stickers.map(edge => edge.node ? ({
type: "sticker",
ID: edge.node.id,
url: edge.node.image?.uri,
animatedUrl: edge.node.animated_image?.uri,
packID: edge.node.pack?.id,
label: edge.node.label || edge.node.accessibility_label,
stickerID: edge.node.id
}) : null).filter(Boolean);
}
/**
* Format AI-generated stickers
* @param {object} data - Raw GraphQL response
* @returns {Array<Object>}
*/
function formatAiStickers(data) {
const stickers = data?.data?.xfb_trending_generated_ai_stickers?.nodes;
if (!stickers) return [];
return stickers.map(node => ({
type: "sticker",
ID: node.id,
url: node.url,
label: node.label,
stickerID: node.id
})).filter(Boolean);
}
module.exports = function(defaultFuncs, api, ctx) {
/**
* Make a GraphQL request and handle login and error checking
* @param {object} form - Form data for the request
* @returns {Promise<object>}
*/
async function makeRequest(form) {
const resData = await defaultFuncs.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
if (!resData) throw new Error("GraphQL request returned no data.");
if (resData.errors) {
utils.error("StickerAPI GraphQL Error", resData.errors[0].message);
throw resData.errors[0];
}
return resData;
}
return {
/**
* Search for stickers by keyword
* @param {string} query - Search term
* @returns {Promise<Array<Object>>}
*/
search: async function(query) {
const form = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickerPickerSearchResultsRootQuery',
variables: JSON.stringify({
scale: 3,
search_query: query,
sticker_height: 128,
sticker_width: 128,
stickerInterface: "MESSAGES"
}),
doc_id: '24004987559125954'
};
const res = await makeRequest(form);
return formatStickerSearchResults(res);
},
/**
* List user's sticker packs
* @returns {Promise<Array<Object>>}
*/
listPacks: async function() {
const form = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickerPickerCardQuery',
variables: JSON.stringify({ scale: 3, stickerInterface: "MESSAGES" }),
doc_id: '10095807770482952'
};
const res = await makeRequest(form);
return formatPackList(res).packs;
},
/**
* Get all available sticker packs from the store (with pagination)
* @returns {Promise<Array<Object>>}
*/
getStorePacks: async function() {
utils.log("Starting to fetch all sticker packs from store...");
let allPacks = [];
const initialForm = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickersStoreDialogQuery',
variables: JSON.stringify({}),
doc_id: '29237828849196584'
};
let res = await makeRequest(initialForm);
let { packs, page_info, store_id } = formatPackList(res);
allPacks.push(...packs);
utils.log(`Fetched first page with ${packs.length} packs.`);
while (page_info && page_info.has_next_page) {
utils.log("Fetching next page with cursor:", page_info.end_cursor);
const paginatedForm = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickersStorePackListPaginationQuery',
variables: JSON.stringify({
count: 20,
cursor: page_info.end_cursor,
id: store_id
}),
doc_id: '9898634630218439'
};
res = await makeRequest(paginatedForm);
let paginatedResult = formatPackList(res);
allPacks.push(...paginatedResult.packs);
page_info = paginatedResult.page_info;
utils.log(`Fetched ${paginatedResult.packs.length} more packs. Total now: ${allPacks.length}`);
}
utils.log(`Finished fetching. Total unique packs found: ${allPacks.length}`);
return allPacks;
},
/**
* Merge user's and store sticker packs into one list
* @returns {Promise<Array<Object>>}
*/
listAllPacks: async function() {
const [myPacks, storePacks] = await Promise.all([
this.listPacks(),
this.getStorePacks()
]);
const allPacksMap = new Map();
myPacks.forEach(pack => allPacksMap.set(pack.id, pack));
storePacks.forEach(pack => allPacksMap.set(pack.id, pack));
return Array.from(allPacksMap.values());
},
/**
* Add a sticker pack by ID
* @param {string} packID - The ID of the sticker pack
* @returns {Promise<Object>}
*/
addPack: async function(packID) {
const form = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickersStorePackMutationAddMutation',
variables: JSON.stringify({
input: {
pack_id: packID,
actor_id: ctx.userID,
client_mutation_id: Math.round(Math.random() * 10).toString()
}
}),
doc_id: '9877489362345320'
};
const res = await makeRequest(form);
return res.data.sticker_pack_add.sticker_pack;
},
/**
* Get all stickers in a pack
* @param {string} packID - Sticker pack ID
* @returns {Promise<Array<Object>>}
*/
getStickersInPack: async function(packID) {
const form = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickerPickerPackContentRootQuery',
variables: JSON.stringify({ packID, stickerWidth: 128, stickerHeight: 128, scale: 3 }),
doc_id: '23982341384707469'
};
const res = await makeRequest(form);
return formatStickerPackResults(res);
},
/**
* Get trending AI-generated stickers
* @param {{ limit?: number }} options - Options object
* @returns {Promise<Array<Object>>}
*/
getAiStickers: async function({ limit = 10 } = {}) {
const form = {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'CometStickerPickerStickerGeneratedCardQuery',
variables: JSON.stringify({ limit }),
doc_id: '24151467751156443'
};
const res = await makeRequest(form);
return formatAiStickers(res);
}
};
};