rsshub
Version:
Make RSS Great Again!
134 lines (132 loc) • 4.91 kB
JavaScript
import "./config-C37vj7VH.mjs";
import "./logger-Czu8UMNd.mjs";
import { t as parseDate } from "./parse-date-BrP7mxXf.mjs";
import { t as cache_default } from "./cache-Bo__VnGm.mjs";
import "./proxy-Db7uGcYb.mjs";
import { n as puppeteer_default } from "./puppeteer-DGmvuGvT.mjs";
import "./puppeteer-utils-BK3JC9qW.mjs";
import { t as parseToken } from "./cookies-ctB_rRKe.mjs";
import sanitizeHtml from "sanitize-html";
//#region lib/routes/xueqiu/user.ts
const rootUrl = "https://xueqiu.com";
const route = {
path: "/user/:id/:type?",
categories: ["finance"],
example: "/xueqiu/user/8152922548",
parameters: {
id: "用户 id, 可在用户主页 URL 中找到",
type: "动态的类型, 不填则默认全部"
},
features: {
requireConfig: false,
requirePuppeteer: true,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false
},
radar: [{
source: ["xueqiu.com/u/:id"],
target: "/user/:id"
}],
name: "用户动态",
maintainers: ["imlonghao"],
handler,
description: `| 原发布 | 长文 | 问答 | 热门 | 交易 |
| ------ | ---- | ---- | ---- | ---- |
| 0 | 2 | 4 | 9 | 11 |`
};
async function handler(ctx) {
const id = ctx.req.param("id");
const type = ctx.req.param("type") || 10;
const typename = {
10: "全部",
0: "原发布",
2: "长文",
4: "问答",
9: "热门",
11: "交易"
};
const link = `${rootUrl}/u/${id}`;
const token = await parseToken(link);
const browser = await puppeteer_default();
try {
const mainPage = await browser.newPage();
await mainPage.setExtraHTTPHeaders({
Cookie: token,
Referer: link
});
await mainPage.goto(link, { waitUntil: "domcontentloaded" });
await mainPage.waitForFunction(() => document.readyState === "complete");
const apiUrl = `${rootUrl}/v4/statuses/user_timeline.json?user_id=${id}&type=${type}`;
const response = await mainPage.evaluate(async (url) => {
return (await fetch(url)).json();
}, apiUrl);
if (!response?.statuses) throw new Error("获取用户动态数据失败");
const data = response.statuses.filter((s) => s.mark !== 1);
if (!data.length) throw new Error("未找到有效的动态数据");
const items = await Promise.all(data.map((item) => cache_default.tryGet(item.target, async () => {
const detailUrl = rootUrl + item.target;
try {
await mainPage.goto(detailUrl, { waitUntil: "domcontentloaded" });
await mainPage.waitForFunction(() => document.readyState === "complete");
const content = await mainPage.evaluate(() => {
const articleContent = document.querySelector(".article__bd")?.innerHTML || "";
const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?});/);
return {
articleContent,
statusData: statusMatch ? statusMatch[1] : null
};
});
if (content.statusData) item.text = JSON.parse(content.statusData).text;
const retweetedStatus = item.retweeted_status ? `<blockquote>${item.retweeted_status.user.screen_name}: ${item.retweeted_status.description}</blockquote>` : "";
const description = content.articleContent || item.description + retweetedStatus;
return {
title: item.title || sanitizeHtml(description, {
allowedTags: [],
allowedAttributes: {}
}),
description: item.text ? item.text + retweetedStatus : description,
pubDate: parseDate(item.created_at),
link: rootUrl + item.target
};
} catch (error) {
if (error instanceof Error && !error.message?.includes("ERR_ABORTED")) throw error;
const retweetedStatus = item.retweeted_status ? `<blockquote>${item.retweeted_status.user.screen_name}: ${item.retweeted_status.description}</blockquote>` : "";
const description = item.description + retweetedStatus;
return {
title: item.title || sanitizeHtml(description, {
allowedTags: [],
allowedAttributes: {}
}),
description: item.description,
pubDate: parseDate(item.created_at),
link: rootUrl + item.target
};
}
})));
const extractProfileImage = (user) => {
if (!user?.profile_image_url || !user?.photo_domain) return;
const imageUrls = user.profile_image_url.split(",").filter(Boolean);
if (imageUrls.length === 0) return;
const selectedImageUrl = [
"!180x180.png",
"!50x50.png",
"!30x30.png"
].map((size) => imageUrls.find((url) => url.includes(size))).find(Boolean) || imageUrls[0];
return `${user.photo_domain.startsWith("//") ? `https:${user.photo_domain}` : user.photo_domain}${selectedImageUrl}`;
};
const profileImage = extractProfileImage(data[0].user);
return {
title: `${data[0].user.screen_name} 的雪球${typename[type]}动态`,
link,
description: `${data[0].user.screen_name} 的雪球${typename[type]}动态`,
image: profileImage,
item: items
};
} finally {
await browser.close();
}
}
//#endregion
export { route };