UNPKG

rsshub

Version:
165 lines (143 loc) • 5.87 kB
import got from '@/utils/got'; import path from 'node:path'; import { art } from '@/utils/render'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import cache from '@/utils/cache'; const apiBaseUrl = 'https://apiv3.fansly.com'; const baseUrl = 'https://fansly.com'; const icon = `${baseUrl}/assets/images/icons/apple-touch-icon.png`; const findAccountById = (accountId, accounts) => { const account = accounts.find((account) => account.id === accountId); return { displayName: account.displayName, username: account.username, }; }; const getAccountByUsername = (username) => cache.tryGet(`fansly:account:${username.toLowerCase()}`, async () => { const { data: accountResponse } = await got(`${apiBaseUrl}/api/v1/account`, { searchParams: { usernames: username, 'ngsw-bypass': true, }, }); if (!accountResponse.response.length) { throw new InvalidParameterError('This profile or page does not exist.'); } return accountResponse.response[0]; }); const getTimelineByAccountId = async (accountId) => { const { data: timeline } = await got(`${apiBaseUrl}/api/v1/timelinenew/${accountId}`, { searchParams: { before: 0, after: 0, wallId: '', contentSearch: '', 'ngsw-bypass': true, }, }); return timeline.response; }; const getTagId = (tag) => cache.tryGet(`fansly:tag:${tag.toLowerCase()}`, async () => { const { data: tagResponse } = await got(`${apiBaseUrl}/api/v1/contentdiscovery/media/tag`, { searchParams: { tag, 'ngsw-bypass': true, }, }); if (!tagResponse.response.mediaOfferSuggestionTag) { throw new Error("Couldn't find this hashtag."); } return tagResponse.response.mediaOfferSuggestionTag.id; }); const getTagSuggestion = async (tagId) => { const { data: suggestionResponse } = await got(`${apiBaseUrl}/api/v1/contentdiscovery/media/suggestionsnew`, { searchParams: { before: 0, after: 0, tagIds: tagId, limit: 25, offset: 0, 'ngsw-bypass': true, }, }); return suggestionResponse.response; }; const parseDescription = (post, aggregationData) => post.content.replaceAll('\n', '<br>') + '<br>' + parseAttachments(post.attachments, aggregationData); const parseAttachments = (attachments, aggregationData) => attachments .map((attachment) => { switch (attachment.contentType) { case 1: // single media return parseMedia(attachment.contentId, aggregationData.accountMedia); case 2: { // media bundle let attachments = ''; const bundle = aggregationData.accountMediaBundles.find((bundle) => bundle.id === attachment.contentId); for (const mediaId of bundle.accountMediaIds) { attachments += parseMedia(mediaId, aggregationData.accountMedia); } return attachments; } case 8: { // aggregated post (repost) let attachments = '<br><br>'; const repost = aggregationData.aggregatedPosts.find((post) => post.id === attachment.contentId) || aggregationData.posts.find((post) => post.id === attachment.contentId); attachments += parseDescription(repost, aggregationData); return attachments; } case 7100: return renderTipGoal(attachment.contentId, aggregationData.tipGoals); case 32001: // unknown return ''; case 42001: return renderPoll(attachment.contentId, aggregationData.polls); default: throw new Error(`Unhandled attachment type: ${attachment.contentType} for post ${attachment.postId}`); } }) .join(''); const parseMedia = (contentId, accountMedia) => { const media = accountMedia.find((media) => media.id === contentId); if (!media) { return ''; } return renderMedia(media.preview ?? media.media); }; const renderMedia = (media) => { switch (media.mimetype) { case 'image/gif': case 'image/jpeg': case 'image/png': case 'video/mp4': case 'audio/mp4': return art(path.join(__dirname, 'templates/media.art'), { poster: media.mimetype === 'video/mp4' ? media.variants[0].locations[0] : null, src: media.locations[0], }); default: throw new Error(`Unhandled media type: ${media.mimetype}`); } }; const renderPoll = (pollId, polls) => { const poll = polls.find((poll) => poll.id === pollId); return art(path.join(__dirname, 'templates/poll.art'), { title: poll.question, options: poll.options, version: poll.version, }); }; const renderTipGoal = (tipGoalId, tipGoals) => { const goal = tipGoals.find((goal) => goal.id === tipGoalId); return art(path.join(__dirname, 'templates/tip-goal.art'), { label: goal.label, description: goal.description, currentAmount: goal.currentAmount, goalAmount: goal.goalAmount, currentPercentage: goal.currentPercentage, }); }; export { findAccountById, baseUrl, icon, getAccountByUsername, getTimelineByAccountId, getTagId, getTagSuggestion, parseAttachments, parseDescription, parseMedia, renderMedia, renderPoll, renderTipGoal };