Files
RSSHub/lib/v2/threads/utils.js
T
Tony 6d901cb7dc style(eslint): add eslint-unicorn (#14257)
* style: add eslint-unicorn

* style: fix unicorn/no-useless-spread

* style: fix unicorn/no-useless-promise-resolve-reject

* style: fix unicorn/no-for-loop

* fix: codeql bad HTML filtering regexp

* fix: codeql incomplete replace

* fix: unicorn/no-abusive-eslint-disable

* style: fix unicorn/no-new-array

* style: fix unicorn/no-typeof-undefined

* style: fix unicorn/no-zero-fractions

* style: fix unicorn/no-empty-file

* style: fix unicorn/prefer-date-now

* revert: auto fix unicorn/prefer-switch on lib/v2/kuaidi100/utils.js

* style: fix unicorn/prefer-array-find

* style: fix unicorn/prefer-array-flat

* style: fix unicorn/prefer-array-flat-map

* style: fix unicorn/prefer-at

* style: fix unicorn/prefer-string-starts-ends-with

* style: fix unicorn/prefer-includes

* fix: codeql URL substring sanitization

* style: fix unicorn/prefer-optional-catch-binding

* style: fix unicorn/catch-error-name

* style: fix unicorn/escape-case

* style: fix unicorn/prefer-native-coercion-functions

* style: fix unicorn/prefer-regexp-test

* style: fix unicorn/require-array-join-separator

* style: fix unicorn/prefer-math-trunc

* style: fix unicorn/prefer-negative-index

* style: fix unicorn/prefer-dom-node-dataset

* style: fix unicorn/prefer-dom-node-text-content

* style: fix unicorn/prefer-query-selector

* style: fix unicorn/no-array-for-each

* style: fix unicorn/no-negated-condition

* style: fix unicorn/prefer-add-event-listener

* style: fix unicorn/import-style

* style: fix prefer-regex-literals

* style: disable unicorn/no-useless-switch-case

* style: disable unicorn/text-encoding-identifier-case

* style: fix unicorn/prefer-set-has

* style: fix unicorn/prefer-spread

* revert: auto fix on lib/routes/universities/ynnu/edu/base64.js

* style: fix unicorn/no-useless-undefined

* style: fix unicorn/no-array-push-push

* style: fix unicorn/no-useless-undefined again

* style: fix unicorn/no-lonely-if

* style: fix unicorn/prefer-reflect-apply

* style: fix unicorn/switch-case-braces

* style: fix unicorn/prefer-switch

* style: fix unicorn/prefer-array-some

* fix: deepscan UNUSED_VAR_ASSIGN

* style: fix unicorn/prefer-ternary

* fix: follow-up of unicorn/prefer-ternary

* revert: auto fix of unicorn/prefer-string-slice for substring()

* style: disable unicorn/prefer-string-slice

fix: auto fix slice over deprecated substr

* style: fix unicorn/throw-new-error

* style: fix unicorn/filename-case

* test: fix dateParser renaming

* style: fix unicorn/better-regex

* style: fix unicorn/prefer-string-replace-all

* fix(deps): add sanitize-html

* style: fix no-prototype-builtins

* style: fix unicorn/consistent-destructuring

* style: fix unicorn/consistent-function-scoping

* style: fix unicorn/prefer-regexp-test

* style: fix unicorn/prefer-logical-operator-over-ternary

* style: fix unicorn/no-array-callback-reference

* style: add prefer-object-has-own

* style: warn unicorn/no-empty-file

* style: fix unicorn/prefer-number-properties

* style: fix no-useless-undefined again

* style: fix unicorn/numeric-separators-style

* style: disable unicorn/no-array-callback-reference

false postive with cheerio
2024-01-18 20:43:40 +08:00

148 lines
4.8 KiB
JavaScript

const got = require('@/utils/got');
const cheerio = require('cheerio');
const dayjs = require('dayjs');
const profileUrl = (user) => `https://www.threads.net/@${user}`;
const threadUrl = (code) => `https://www.threads.net/t/${code}`;
const apiUrl = 'https://www.threads.net/api/graphql';
const PROFILE_QUERY = 23_996_318_473_300_828;
const THREADS_QUERY = 6_232_751_443_445_612;
const REPLIES_QUERY = 6_307_072_669_391_286;
const USER_AGENT = 'Barcelona 289.0.0.77.109 Android';
const appId = '238260118697367';
const extractTokens = async (user, ctx) => {
const { data: response } = await got(profileUrl(user), {
headers: {
'User-Agent': USER_AGENT,
'X-IG-App-ID': appId,
},
});
const $ = cheerio.load(response);
const data = $('script:contains("LSD"):first').text();
const lsd = data.match(/"LSD",\[],{"token":"([\w@-]+)"},/)?.[1];
const userId = data.match(/{"user_id":"(\d+)"},/)?.[1];
ctx.state.json = { lsd, userId };
return { lsd, userId };
};
const makeHeader = (user, lsd) => ({
Accept: 'application/json',
Host: 'www.threads.net',
Origin: 'https://www.threads.net',
Referer: profileUrl(user),
'User-Agent': USER_AGENT,
'X-FB-LSD': lsd,
'X-IG-App-ID': appId,
});
const hasMedia = (post) => post.image_versions2 || post.carousel_media || post.video_versions;
const buildMedia = (post) => {
let html = '';
if (post.carousel_media) {
for (const media of post.carousel_media) {
const firstImage = media.image_versions2?.candidates[0];
const firstVideo = media.video_versions?.[0];
if (firstVideo) {
html += `<video controls autoplay loop poster="${firstImage.url}">`;
html += `<source src="${firstVideo.url}"/>`;
html += '</video>';
} else {
html += `<img src="${firstImage.url}"/>`;
}
}
} else {
const mainImage = post.image_versions2?.candidates?.[0];
const mainVideo = post.video_versions?.[0];
if (mainImage) {
if (mainVideo) {
html += `<video controls autoplay loop poster="${mainImage.url}">`;
html += `<source src="${mainVideo.url}"/>`;
html += '</video>';
} else {
html += `<img src="${mainImage.url}"/>`;
}
}
}
return html;
};
const buildContent = (item, options) => {
let title = '';
let description = '';
const quotedPost = item.post.text_post_app_info?.share_info?.quoted_post;
const repostedPost = item.post.text_post_app_info?.share_info?.reposted_post;
const isReply = item.post.text_post_app_info?.reply_to_author;
const embededPost = quotedPost ?? repostedPost;
if (options.showAuthorInTitle) {
title += `@${item.post.user?.username}: `;
}
if (options.showAuthorInDesc) {
description += '<p>';
if (options.showAuthorAvatarInDesc) {
description += `<img src="${item.post.user?.profile_pic_url}" width="48px" height="48px"> `;
}
description += `<strong>@${item.post.user?.username}</strong>`;
if (embededPost) {
description += options.showEmojiForQuotesAndReply ? ' 🔁' : ' quoted';
} else if (isReply) {
description += options.showEmojiForQuotesAndReply ? ' ↩️' : ' replied';
}
description += ':</p>';
}
if (item.post.caption?.text) {
title += item.post.caption?.text;
description += `<p>${item.post.caption?.text}</p>`;
}
if (hasMedia(item.post)) {
description += `<p>${buildMedia(item.post)}</p>`;
}
if (embededPost) {
if (options.showQuotedInTitle) {
title += options.showEmojiForQuotesAndReply ? ' 🔁 ' : ' QT: ';
title += `@${embededPost.user?.username}: `;
title += `"${embededPost.caption?.text}"`;
}
description += '<blockquote>';
description += `<p>${embededPost.caption?.text}</p>`;
if (hasMedia(embededPost)) {
description += `<p>${buildMedia(embededPost)}</p>`;
}
description += '— ';
if (options.showQuotedAuthorAvatarInDesc) {
description += `<img src="${embededPost.user?.profile_pic_url}" width="24px" height="24px"> `;
}
description += `@${embededPost.user?.username} — `;
description += `<a href="${threadUrl(embededPost.code)}">${dayjs(embededPost.taken_at, 'X').toString()}</a>`;
description += '</blockquote>';
}
return { title, description };
};
module.exports = {
apiUrl,
profileUrl,
threadUrl,
PROFILE_QUERY,
THREADS_QUERY,
REPLIES_QUERY,
USER_AGENT,
extractTokens,
makeHeader,
hasMedia,
buildMedia,
buildContent,
};