Merge remote-tracking branch 'origin/master' into feature/vitest

This commit is contained in:
DIYgod
2024-03-04 01:17:26 +08:00
35 changed files with 386 additions and 56 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
import { MiddlewareHandler } from 'hono';
import logger from '@/utils/logger';
import { config } from '@/config';
import Sentry from '@sentry/node';
import * as Sentry from '@sentry/node';
import { getRouteNameFromPath } from '@/utils/helpers';
if (config.sentry.dsn) {
+1 -1
View File
@@ -155,7 +155,7 @@ router.get('/mafengwo/ziyouxing/:code', lazyloadRouteHandler('./routes/mafengwo/
router.get('/novel/uukanshu/:uid', lazyloadRouteHandler('./routes/novel/uukanshu'));
// 中国气象网
router.get('/weatheralarm/:province?', lazyloadRouteHandler('./routes/weatheralarm'));
// router.get('/weatheralarm/:province?', lazyloadRouteHandler('./routes/weatheralarm'));
// Gitlab
router.get('/gitlab/explore/:type/:host?', lazyloadRouteHandler('./routes/gitlab/explore'));
@@ -1,26 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const province = ctx.params.province || '&';
const alarmInfoURL = `http://www.nmc.cn/rest/findAlarm?pageNo=1&pageSize=20&signaltype=&signallevel=&province=${province}`;
const response = await got({
method: 'get',
url: alarmInfoURL,
});
const data = response.data.data;
const list = data.page.list;
ctx.state.data = {
title: '中央气象台全国气象预警',
link: `http://www.nmc.cn/publish/alarm.html`,
item: list.map((item) => {
const title = item.title;
const url = item.url;
return {
title,
link: `http://www.nmc.cn${url}`,
pubDate: item.issuetime,
};
}),
};
};
+1 -1
View File
@@ -75,7 +75,7 @@ module.exports = {
const qw = searchParams.get('qw');
const kw = searchParams.get('kw');
if (qw) {
const route = `/baidu/tieba/user/${qw}`;
const route = `/baidu/tieba/search/${qw}`;
return kw ? `${route}/kw=${kw}` : route;
}
},
+1 -1
View File
@@ -16,7 +16,7 @@ export default async (ctx) => {
// PC端:https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}
// 移动端接口:https://tieba.baidu.com/mo/q/m?kw=${encodeURIComponent(kw)}&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=1&pn=0&is_ajax=1
const params = { kw: encodeURIComponent(kw) };
ctx._matchedRoute.includes('good') && (params.tab = 'good');
ctx.req.path.includes('good') && (params.tab = 'good');
cid && (params.cid = cid);
const { data } = await got(`https://tieba.baidu.com/f`, {
headers: {
+1 -1
View File
@@ -34,7 +34,7 @@ async function getPost(id, lz = 0, pn = 7e6) {
export default async (ctx) => {
const id = ctx.req.param('id');
const lz = ctx._matchedRoute.includes('lz') ? 1 : 0;
const lz = ctx.req.path.includes('lz') ? 1 : 0;
const html = await getPost(id, lz);
const $ = load(html);
const title = $('.core_title_txt').attr('title');
+35
View File
@@ -0,0 +1,35 @@
import got from '@/utils/got';
const cache = require('./cache');
const utils = require('./utils');
import { parseDate } from '@/utils/parse-date';
module.exports = async (ctx) => {
const uid = ctx.req.param('uid');
const disableEmbed = ctx.req.param('disableEmbed');
const name = await cache.getUsernameFromUID(ctx, uid);
const response = await got({
url: `https://api.bilibili.com/x/space/like/video?vmid=${uid}`,
headers: {
Referer: `https://space.bilibili.com/${uid}/`,
},
});
const { data, code, message } = response.data;
if (code) {
throw new Error(message ?? code);
}
ctx.set('data', {
title: `${name} 的 bilibili 点赞视频`,
link: `https://space.bilibili.com/${uid}`,
description: `${name} 的 bilibili 点赞视频`,
item: data.list.map((item) => ({
title: item.title,
description: `${item.desc}${disableEmbed ? '' : `<br><br>${utils.iframe(item.aid)}`}<br><img src='${item.pic}'>`,
pubDate: parseDate(item.pubdate * 1000),
link: item.pubdate > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`,
author: item.owner.name,
})),
});
};
+1
View File
@@ -31,6 +31,7 @@ module.exports = {
'/user/fav/:uid/:disableEmbed?': ['DIYgod'],
'/user/followers/:uid/:loginUid': ['Qixingchen'],
'/user/followings/:uid/:loginUid': ['Qixingchen'],
'/user/like/:uid/:disableEmbed?': ['ygguorun'],
'/user/video/:uid/:disableEmbed?': ['DIYgod'],
'/video/danmaku/:bvid/:pid?': ['Qixingchen'],
'/video/page/:bvid/:disableEmbed?': ['sxzz'],
+6
View File
@@ -102,6 +102,12 @@ module.exports = {
source: '/:uid',
target: '/bilibili/user/coin/:uid',
},
{
title: 'UP 主点赞视频',
docs: 'https://docs.rsshub.app/routes/social-media#bilibili',
source: '/:uid',
target: '/bilibili/user/like/:uid',
},
{
title: 'UP 主粉丝',
docs: 'https://docs.rsshub.app/routes/social-media#bilibili',
+1
View File
@@ -31,6 +31,7 @@ export default (router) => {
router.get('/user/fav/:uid/:disableEmbed?', './user-fav');
router.get('/user/followers/:uid/:loginUid', './followers');
router.get('/user/followings/:uid/:loginUid', './followings');
router.get('/user/like/:uid/:disableEmbed?', './like');
router.get('/user/video/:uid/:disableEmbed?', './video');
router.get('/user/video-all/:uid/:disableEmbed?', './video-all');
router.get('/video/danmaku/:bvid/:pid?', './danmaku');
+13 -7
View File
@@ -20,13 +20,19 @@ export default async (ctx) => {
});
const notifications = response.data;
const items = notifications.map((item) => ({
title: item.subject.title,
description: item.subject.title,
pubDate: parseDate(item.updated_at), // item.updated_at follows ISO 8601.
guid: item.id,
link: item.subject.url,
}));
const items = notifications.map((item) => {
let originUrl = item.subject.url ? item.subject.url.replace('https://api.github.com/repos/', 'https://github.com/') : 'https://github.com/notifications';
if (originUrl.includes('/releases/')) {
originUrl = originUrl.replace(/\/releases\/\d+$/, '/releases');
}
return {
title: item.subject.title,
description: item.subject.title,
pubDate: parseDate(item.updated_at), // item.updated_at follows ISO 8601.
guid: item.id,
link: originUrl,
};
});
ctx.set('data', {
title: 'Github Notifications',
+1 -1
View File
@@ -12,7 +12,7 @@ export default async (ctx) => {
const url = `http://${site}/api/v1/timelines/public?local=true&only_media=${only_media}`;
const response = await got.get(url);
const response = await got.get(url, { headers: utils.apiHeaders() });
const list = response.data;
ctx.set('data', {
+1 -1
View File
@@ -12,7 +12,7 @@ export default async (ctx) => {
const url = `http://${site}/api/v1/timelines/public?remote=true&only_media=${only_media}`;
const response = await got.get(url);
const response = await got.get(url, { headers: utils.apiHeaders() });
const list = response.data;
ctx.set('data', {
+10 -3
View File
@@ -6,6 +6,12 @@ import { config } from '@/config';
const allowSiteList = ['mastodon.social', 'pawoo.net'];
const apiHeaders = (site) => {
const { accessToken, apiHost } = config.mastodon;
// avoid sending API token to other sites
return accessToken && site === apiHost ? { Authorization: `Bearer ${accessToken}` } : {};
};
const mediaParse = (media_attachments) =>
media_attachments
.map((item) => {
@@ -60,6 +66,7 @@ async function getAccountStatuses(site, account_id, only_media) {
const statuses_response = await got({
method: 'get',
url: statuses_url,
headers: apiHeaders(site),
});
const data = statuses_response.data;
@@ -71,6 +78,7 @@ async function getAccountStatuses(site, account_id, only_media) {
const account_response = await got({
method: 'get',
url: account_url,
headers: apiHeaders(site),
});
account_data = account_response.data;
}
@@ -96,9 +104,7 @@ async function getAccountIdByAcct(acct) {
const search_response = await got({
method: 'get',
url: search_url,
headers: {
...(mastodonConfig.accessToken ? { Authorization: `Bearer ${mastodonConfig.accessToken}` } : {}),
},
headers: apiHeaders(site),
searchParams: {
q: acct,
type: 'accounts',
@@ -124,6 +130,7 @@ async function getAccountIdByAcct(acct) {
}
module.exports = {
apiHeaders,
parseStatuses,
getAccountStatuses,
getAccountIdByAcct,
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
'/read/:type?': ['nczitzk'],
};
+66
View File
@@ -0,0 +1,66 @@
module.exports = {
'nlc.cn': {
_name: '中国国家图书馆',
read: [
{
title: '读者云平台',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: (_, url) => {
url = new URL(url);
const type = url.searchParams.get('type');
return `/nlc/read/${type ? `/${type}` : ''}`;
},
},
{
title: '电子图书',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/电子图书',
},
{
title: '电子期刊',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/电子期刊',
},
{
title: '电子论文',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/电子论文',
},
{
title: '电子报纸',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/电子报纸',
},
{
title: '音视频',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/音视频',
},
{
title: '标准专利',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/标准专利',
},
{
title: '工具书',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/工具书',
},
{
title: '少儿资源',
docs: 'https://docs.rsshub.app/routes/other#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai',
source: ['/outRes/outResList'],
target: '/nlc/read/少儿资源',
},
],
},
};
+71
View File
@@ -0,0 +1,71 @@
import got from '@/utils/got';
import { load } from 'cheerio';
import { art } from '@/utils/render';
import * as path from 'node:path';
export default async (ctx) => {
const { type = '电子图书' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15;
const author = '中国国家图书馆';
const rootUrl = 'http://read.nlc.cn';
const currentUrl = new URL(`/outRes/outResList?type=${type}`, rootUrl).href;
const { data: response } = await got(currentUrl);
const $ = load(response);
const items = $('ul.YMH2019_New_GRZX_list7 li a.aa')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
const title = item.find('span').first().text();
return {
title,
link: item.prop('onclick').match(/openOutRes\('1','(.*?)','1',/)[1],
description: art(path.join(__dirname, 'templates/description.art'), {
images: item
.find('div.pic img')
.toArray()
.map((i) => {
i = $(i);
return {
src: i.prop('src'),
alt: title,
};
}),
intro: item.find('div.txt').prop('title'),
}),
author,
category: [type],
guid: `nlc-read#${
item
.prev()
.prop('onclick')
.match(/\('(\d+)'\)/)[1]
}`,
};
});
const image = new URL('static/style/css/images/YMH_home_main_logo.png', rootUrl).href;
const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
ctx.set('data', {
item: items,
title: `${$('title').text()} - ${type}`,
link: currentUrl,
description: type,
language: 'zh',
image,
icon,
logo: icon,
subtitle: type,
author,
allowEmpty: true,
});
};
+3
View File
@@ -0,0 +1,3 @@
export default (router) => {
router.get('/read/:type?', './read');
};
+17
View File
@@ -0,0 +1,17 @@
{{ if images }}
{{ each images image }}
{{ if image?.src }}
<figure>
<img
{{ if image.alt }}
alt="{{ image.alt }}"
{{ /if }}
src="{{ image.src }}">
</figure>
{{ /if }}
{{ /each }}
{{ /if }}
{{ if intro }}
<p>{{ intro }}</p>
{{ /if }}
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
'/weatheralarm/:province?': ['ylc395'],
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
'nmc.cn': {
_name: '中央气象台',
'.': [
{
title: '全国气象预警',
docs: 'https://docs.rsshub.app/routes/forecast#zhong-yang-qi-xiang-tai',
source: ['/publish/alarm.html', '/'],
target: '/nmc/weatheralarm',
},
],
},
};
+3
View File
@@ -0,0 +1,3 @@
export default (router) => {
router.get('/weatheralarm/:province?', './weatheralarm');
};
+49
View File
@@ -0,0 +1,49 @@
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import * as cheerio from 'cheerio';
export default async (ctx) => {
const { province } = ctx.req.param();
const alarmInfoURL = `http://www.nmc.cn/rest/findAlarm`;
const { data: response } = await got(alarmInfoURL, {
searchParams: {
pageNo: 1,
pageSize: 20,
signaltype: '',
signallevel: '',
province,
},
});
const list = response.data.page.list.map((item) => ({
title: item.title,
link: `http://www.nmc.cn${item.url}`,
pubDate: timezone(parseDate(item.issuetime), 8),
}));
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = cheerio.load(response);
item.description =
$('#icon').html() +
$('#alarmtext')
.toArray()
.map((i) => $(i).html())
.join('');
return item;
})
)
);
ctx.set('data', {
title: '中央气象台全国气象预警',
link: 'http://www.nmc.cn/publish/alarm.html',
item: items,
});
};
+5 -7
View File
@@ -7,7 +7,7 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
export default async (ctx) => {
const name = ctx.req.url.split('package/')[1];
const name = ctx.req.param('name');
const packageDownloadLastMonthAPI = `https://api.npmjs.org/downloads/point/last-month/${name}`; // 按月统计
const packageDownloadLastWeekAPI = `https://api.npmjs.org/downloads/point/last-week/${name}`; // 按周统计
const packageDownloadLastDayAPI = `https://api.npmjs.org/downloads/point/last-day/${name}`; // 按天统计
@@ -19,14 +19,12 @@ export default async (ctx) => {
const packageVersionRes = await got(packageVersionAPI).json();
const packageVersion = packageVersionRes.time;
const packageVersionList = [];
for (const key in packageVersion) {
packageVersionList.push({
const packageVersionList = Object.keys(packageVersion)
.map((key) => ({
version: key,
time: packageVersion[key],
});
}
packageVersionList.reverse();
}))
.toReversed();
ctx.set('data', {
title: `${name} - npm`,
+2 -1
View File
@@ -1,3 +1,4 @@
export default (router) => {
router.get('package/*', './package');
// https://github.com/dword-design/package-name-regex/blob/master/src/index.js
router.get('package/:name{(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*}', './package');
};
+3 -3
View File
@@ -32,10 +32,10 @@ export default async (ctx) => {
});
const $ = load(detailResponse.data);
const rcState = $('script:contains("window._rcState_ = JSON.parse")')
const rcState = $('script:contains("window._rcState_ = ")')
.text()
.match(/JSON\.parse\((".*")\);/)[1];
const rcStateJson = JSON.parse(JSON.parse(rcState));
.match(/window\._rcState_ = (.*);/)[1];
const rcStateJson = JSON.parse(rcState);
const news = Object.values(rcStateJson.pagesV2.pages)[0];
item.description = news.data.newsStory.body.html.replaceAll('\\n', '<br>');
+1 -1
View File
@@ -1,6 +1,6 @@
// @ts-nocheck
import cache from '@/utils/cache';
const querystring = require('query-string');
import querystring from 'querystring';
import got from '@/utils/got';
const weiboUtils = require('./utils');
import { config } from '@/config';
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
'/': ['cnkmmk'],
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
'zhujiceping.com': {
_name: '国外主机测评',
'.': [
{
title: '最新发布',
docs: 'https://docs.rsshub.app/routes/blog#guo-wai-zhu-ji-ce-ping',
source: ['/'],
target: '/zhujiceping',
},
],
},
};
+3
View File
@@ -0,0 +1,3 @@
export default (router) => {
router.get('/', './rss');
};
+32
View File
@@ -0,0 +1,32 @@
import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
import { load } from 'cheerio';
export default async (ctx) => {
const url = 'https://www.zhujiceping.com/';
const response = await got({ method: 'get', url });
const $ = load(response.data);
const list = $('article.excerpt')
.map((i, e) => {
const element = $(e);
const title = element.find('h2 > a').attr('title');
const link = element.find('h2 > a').attr('href');
const description = element.find('p.note').text();
const dateraw = element.find('time').text();
return {
title,
description,
link,
pubDate: parseDate(dateraw, 'YYYY-MM-DD'),
};
})
.get();
ctx.set('data', {
title: '国外主机测评',
link: url,
item: list,
});
};
+6
View File
@@ -334,6 +334,12 @@
| news | blog | articles | about-influence | customers |
</Route>
## 国外主机测评 {#guo-wai-zhu-ji-ce-ping}
### 最新发布 {#guo-wai-zhu-ji-ce-ping-zui-xin-fa-bu}
<Route author="cnkmmk" example="/zhujiceping" path="/zhujiceping" />
## 虎皮椒 {#hu-pi-jiao}
### 文章 {#hu-pi-jiao-wen-zhang}
+1 -1
View File
@@ -316,6 +316,6 @@
### 全国气象预警 {#zhong-yang-qi-xiang-tai-quan-guo-qi-xiang-yu-jing}
<Route author="ylc395" example="/weatheralarm/广东省" path="/weatheralarm/:province?" paramsDesc={['省份']}>
<Route author="ylc395" example="/nmc/weatheralarm/广东省" path="/nmc/weatheralarm/:province?" paramsDesc={['省份']}>
可通过全局过滤参数订阅您感兴趣的地区.
</Route>
+12
View File
@@ -995,6 +995,18 @@ Specify options (in the format of query string) in parameter `routeParams` param
| usd | gbp | hkd | chf | sek | dkk | nok | jpy | cad | aud | sgd | eur | mop | thb | nzd | krw |
</Route>
## 中国国家图书馆 {#zhong-guo-guo-jia-tu-shu-guan}
### 读者云平台 {#zhong-guo-guo-jia-tu-shu-guan-du-zhe-yun-ping-tai}
<Route author="nczitzk" example="/nlc/read/电子图书" path="/nlc/read/:type?" paramsDesc={['分类,见下表,默认为电子图书']} radar="1">
| [电子图书](http://read.nlc.cn/outRes/outResList?type=电子图书) | [电子期刊](http://read.nlc.cn/outRes/outResList?type=电子期刊) | [电子论文](http://read.nlc.cn/outRes/outResList?type=电子论文) | [电子报纸](http://read.nlc.cn/outRes/outResList?type=电子报纸) | [音视频](http://read.nlc.cn/outRes/outResList?type=音视频) |
| -------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------- |
| [标准专利](http://read.nlc.cn/outRes/outResList?type=标准专利) | [工具书](http://read.nlc.cn/outRes/outResList?type=工具书) | [少儿资源](http://read.nlc.cn/outRes/outResList?type=少儿资源) |
| -------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------- |
</Route>
## 中国互联网联合辟谣平台 {#zhong-guo-hu-lian-wang-lian-he-pi-yao-ping-tai}
### 今日辟谣 {#zhong-guo-hu-lian-wang-lian-he-pi-yao-ping-tai-jin-ri-pi-yao}
+4
View File
@@ -67,6 +67,10 @@
<Route author="DIYgod" example="/bilibili/user/coin/208259" path="/bilibili/user/coin/:uid/:disableEmbed?" paramsDesc={['用户 id, 可在 UP 主主页中找到', '默认为开启内嵌视频, 任意值为关闭']} radar="1" />
### UP 主点赞视频 {#bilibili-up-zhu-dian-zan-shi-pin}
<Route author="ygguorun" example="/bilibili/user/like/208259" path="/bilibili/user/like/:uid/:disableEmbed?" paramsDesc={['用户 id, 可在 UP 主主页中找到', '默认为开启内嵌视频, 任意值为关闭']} radar="1" />
### UP 主粉丝 {#bilibili-up-zhu-fen-si}
<Route author="Qixingchen" example="/bilibili/user/followers/2267573/3" path="/bilibili/user/followers/:uid/:loginUid" paramsDesc={['用户 id, 可在 UP 主主页中找到','用于登入的用户id,需要配置对应的 Cookie 值']} radar="1" configRequired="1">