rsshub
Version:
Make RSS Great Again!
142 lines (133 loc) • 4.99 kB
text/typescript
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import logger from '@/utils/logger';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { HOST, NEW_LIST, OFFICIAL_PAGE_TYPE, POST_FULL, LINK, PUBLIC_IMG, PRIVATE_IMG } from './constant';
import { getI18nGameInfo, getI18nType } from './utils';
const getEventList = async ({ type, gids, size, language }) => {
const query = new URLSearchParams({
type,
gids,
page_size: size,
}).toString();
const url = `${HOST}${NEW_LIST}?${query}`;
const res = await got({
method: 'get',
url,
headers: {
'X-Rpc-Language': language,
},
});
const list = res?.data?.data?.list || [];
return list;
};
const replaceImgDomain = (content) => content.replaceAll(PRIVATE_IMG, PUBLIC_IMG);
const getPostContent = (list, { language }) =>
Promise.all(
list.map(async (row) => {
const post = row.post;
const post_id = post.post_id;
const query = new URLSearchParams({
post_id,
language, // language为了区分缓存,对接口并无意义
}).toString();
const url = `${HOST}${POST_FULL}?${query}`;
return await cache.tryGet(url, async () => {
const res = await got({
method: 'get',
url,
headers: {
'X-Rpc-Language': language,
},
});
const author = res?.data?.data?.post?.user?.nickname || '';
let content = res?.data?.data?.post?.post?.content || '';
if (content === language || !content) {
content = post.content;
}
const description = art(path.join(__dirname, 'templates/post.art'), {
hasCover: post.has_cover,
coverList: row.cover_list,
content: replaceImgDomain(content),
});
return {
// 文章标题
title: post.subject,
// 文章链接
link: `${LINK}/article/${post_id}`,
// 文章正文
description,
// 文章发布日期
pubDate: parseDate(post.created_at * 1000),
author,
};
});
})
);
export const route: Route = {
path: '/news/:language/:gids/:type',
categories: ['game'],
example: '/hoyolab/news/zh-cn/2/2',
parameters: { language: 'Language', gids: 'Game ID', type: 'Announcement type' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
name: 'Official Announcement',
maintainers: ['ZenoTian'],
handler,
description: `| Language | Code |
| ---------------- | ----- |
| 简体中文 | zh-cn |
| 繁體中文 | zh-tw |
| 日本語 | ja-jp |
| 한국어 | ko-kr |
| English (US) | en-us |
| Español (EU) | es-es |
| Français | fr-fr |
| Deutsch | de-de |
| Русский | ru-ru |
| Português | pt-pt |
| Español (Latino) | es-mx |
| Indonesia | id-id |
| Tiếng Việt | vi-vn |
| ภาษาไทย | th-th |
| Honkai Impact 3rd | Genshin Impact | Tears of Themis | HoYoLAB | Honkai: Star Rail | Zenless Zone Zero |
| ----------------- | -------------- | --------------- | ------- | ----------------- | ----------------- |
| 1 | 2 | 4 | 5 | 6 | 8 |
| Notices | Events | Info |
| ------- | ------ | ---- |
| 1 | 2 | 3 |`,
};
async function handler(ctx) {
try {
const { type, gids, language } = ctx.req.param();
const params = {
type,
gids,
language,
size: Number.parseInt(ctx.req.query('limit')) || 15,
};
const gameInfo = await getI18nGameInfo(gids, language, cache.tryGet);
const typeInfo = await getI18nType(language, cache.tryGet);
const list = await getEventList(params);
const items = await getPostContent(list, params);
return {
title: `HoYoLAB-${gameInfo.name}-${typeInfo[type].title}`,
link: `${LINK}/circles/${gids}/${OFFICIAL_PAGE_TYPE[gids]}/official?page_type=${OFFICIAL_PAGE_TYPE[gids]}&page_sort=${typeInfo[type].sort}`,
item: items,
image: gameInfo.icon,
icon: gameInfo.icon,
logo: gameInfo.icon,
};
} catch (error) {
logger.error(error);
}
}