feat(route): add gamersky.com user dynamics route (#21279)

* add user.ts

* Replace substring with slice.

* fix something
This commit is contained in:
Hualiang 2026-03-04 04:44:46 +08:00 committed by GitHub
parent bcba35b41c
commit 4dec5ee309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 0 deletions

View File

@ -0,0 +1,39 @@
import type { Context } from 'hono';
import type { Route } from '@/types';
import { getUserArticle, getUserArticleList, parseUserArticleList } from './utils';
export const route: Route = {
path: '/user/:userId/:detail?',
categories: ['game'],
example: '/gamersky/user/4009731/detail',
parameters: {
userId: '用户 ID。在用户个人主页打开“开发者工具”中的“元素”标签页搜索 data-userid 即可找到',
detail: '是否获取文章详情。只要该参数不为空,就会获取全文内容',
},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
name: '用户动态',
maintainers: ['hualiong'],
handler,
};
async function handler(ctx: Context) {
const { userId, detail } = ctx.req.param();
const body = await getUserArticleList(userId);
const articles = parseUserArticleList(body);
if (detail) {
articles.list = await Promise.all(articles.list.map((item) => getUserArticle(item)));
}
return {
title: `${articles.uname}的动态 - 游民星空`,
link: articles.link,
item: articles.list,
};
}

View File

@ -12,12 +12,19 @@ interface idNameMap {
nodeId: string;
suffix?: string;
}
interface ArticleList {
status: string;
totalPages: number;
body: string;
}
interface UserArticleList {
status: string;
body: string;
total: number;
}
export const getArticleList = async (nodeId) => {
const response = await ofetch<ArticleList>(
`https://db2.gamersky.com/LabelJsonpAjax.aspx?${new URLSearchParams({
@ -88,6 +95,47 @@ export const getArticle = (item) =>
return item satisfies DataItem;
}) as Promise<DataItem>;
export const getUserArticleList = async (userId: string) => {
const response = await ofetch<UserArticleList>(
`https://i.gamersky.com/u/api/v2/GetUserContent?${new URLSearchParams({
jsondata: JSON.stringify({ pageIndex: 1, pageSize: 20, userId }),
})}`,
{
parseResponse: (txt) =>
JSON.parse(txt.slice(1, -2)),
},
);
return response.body;
};
export const parseUserArticleList = (body: string) => {
const $ = load(body);
const list = $(".cmt-list");
const info = list.find(".uname").first();
return {
uname: info.text(),
link: info.attr("href"),
list: list.toArray().map((item) => {
const e = $(item);
const title = e.find(".qzcmt-content-tit a");
return {
title: title.text(),
link: title.attr("href"),
pubDate: parseDate(e.attr("data-time")!),
description: e.find(".qzcmt-content-txt span").text(),
};
}) as DataItem[],
};
};
export const getUserArticle = (item: DataItem) =>
cache.tryGet(item.link!, async () => {
const response = await ofetch(item.link!);
const $ = load(response);
item.description = $(".qzcmt-content-txt").html() || item.description;
return item;
});
export function mdTableBuilder(data: idNameMap[]) {
const table = '|' + data.map((item) => `${item.type}|`).join('') + '\n|' + Array.from({ length: data.length }).fill('---|').join('') + '\n|' + data.map((item) => `${item.name}|`).join('') + '\n';
return table;