rsshub 1.0.0-master.f9b85f5 → 1.0.0-master.f9c381a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config.ts +10 -0
- package/lib/middleware/cache.ts +4 -0
- package/lib/middleware/parameter.ts +1 -1
- package/lib/registry.ts +6 -2
- package/lib/routes/51cto/utils.ts +1 -1
- package/lib/routes/abc/index.ts +1 -1
- package/lib/routes/acgvinyl/namespace.ts +6 -0
- package/lib/routes/acgvinyl/news.ts +86 -0
- package/lib/routes/ally/rail.ts +1 -1
- package/lib/routes/anthropic/news.ts +13 -11
- package/lib/routes/apnews/mobile-api.ts +1 -1
- package/lib/routes/apnews/sitemap.ts +1 -1
- package/lib/routes/apple/apps.ts +2 -2
- package/lib/routes/apple/podcast.ts +58 -25
- package/lib/routes/bangumi.tv/group/reply.ts +1 -1
- package/lib/routes/bilibili/cache.ts +18 -2
- package/lib/routes/bilibili/dynamic.ts +31 -7
- package/lib/routes/bilibili/page.ts +1 -1
- package/lib/routes/bilibili/ranking.ts +23 -17
- package/lib/routes/bilibili/wasm-exec.ts +1 -1
- package/lib/routes/bilibili/weekly-recommend.ts +22 -6
- package/lib/routes/bjp/apod.ts +1 -1
- package/lib/routes/buaa/jiaowu.ts +1 -1
- package/lib/routes/bullionvault/gold-news.ts +3 -3
- package/lib/routes/cast/index.ts +1 -1
- package/lib/routes/coolapk/utils.ts +5 -4
- package/lib/routes/coolbuy/index.ts +106 -0
- package/lib/routes/{xinhuanet → coolbuy}/namespace.ts +3 -3
- package/lib/routes/coolbuy/templates/description.art +48 -0
- package/lib/routes/coolidge/film-guide.ts +60 -0
- package/lib/routes/coolidge/namespace.ts +7 -0
- package/lib/routes/coolidge/news.ts +65 -0
- package/lib/routes/coolidge/templates/description.art +4 -0
- package/lib/routes/copymanga/comic.ts +1 -1
- package/lib/routes/cpta/handler.ts +1 -1
- package/lib/routes/creative-comic/book.ts +1 -1
- package/lib/routes/daum/potplayer.ts +1 -1
- package/lib/routes/dockerhub/utils.ts +1 -1
- package/lib/routes/dora-world/article.ts +2 -2
- package/lib/routes/ehentai/ehapi.ts +3 -3
- package/lib/routes/eventbrite/events.ts +152 -0
- package/lib/routes/eventbrite/namespace.ts +7 -0
- package/lib/routes/github/activity.ts +1 -1
- package/lib/routes/google/jules.ts +63 -0
- package/lib/routes/gov/mem/namespace.ts +7 -0
- package/lib/routes/gov/mem/zfxxgkpt.ts +96 -0
- package/lib/routes/gov/mot/index.ts +158 -53
- package/lib/routes/hameln/chapter.ts +1 -1
- package/lib/routes/hpoi/banner-item.ts +28 -11
- package/lib/routes/hpoi/info.ts +1 -1
- package/lib/routes/huggingface/daily-papers.ts +1 -1
- package/lib/routes/hupu/index.ts +158 -74
- package/lib/routes/hupu/types.ts +163 -0
- package/lib/routes/hust/gs.ts +1 -1
- package/lib/routes/hust/mse.ts +1 -1
- package/lib/routes/huxiu/util.ts +2 -2
- package/lib/routes/infoq/presentations.ts +1 -1
- package/lib/routes/instagram/common-utils.ts +3 -3
- package/lib/routes/itch/devlog.ts +7 -3
- package/lib/routes/javbus/index.ts +1 -1
- package/lib/routes/javlibrary/utils.ts +1 -1
- package/lib/routes/jbma/namespace.ts +17 -0
- package/lib/routes/jbma/report.ts +473 -0
- package/lib/routes/jetbrains/comments.ts +1 -1
- package/lib/routes/jingzhengu/utils.ts +1 -1
- package/lib/routes/juejin/aicoding.ts +102 -0
- package/lib/routes/juejin/utils.ts +36 -51
- package/lib/routes/kakuyomu/works.ts +1 -1
- package/lib/routes/kemono/index.ts +2 -2
- package/lib/routes/komiic/comic.ts +1 -1
- package/lib/routes/koyso/index.ts +338 -0
- package/lib/routes/koyso/namespace.ts +9 -0
- package/lib/routes/koyso/templates/description.art +13 -0
- package/lib/routes/letterboxd/index.ts +65 -0
- package/lib/routes/letterboxd/namespace.ts +8 -0
- package/lib/routes/maccms/index.ts +1 -1
- package/lib/routes/mercari/util.ts +1 -1
- package/lib/routes/mingpao/index.ts +1 -1
- package/lib/routes/nankai/ai-notice.ts +142 -0
- package/lib/routes/nankai/graduate-notice.ts +162 -0
- package/lib/routes/natgeo/natgeo.ts +1 -0
- package/lib/routes/nhk/news-web-easy.ts +1 -1
- package/lib/routes/nicovideo/mylist.ts +39 -0
- package/lib/routes/nicovideo/types.ts +27 -0
- package/lib/routes/nicovideo/utils.ts +27 -1
- package/lib/routes/nikkei/cn/index.ts +1 -4
- package/lib/routes/now/news.ts +1 -1
- package/lib/routes/nytimes/index.ts +1 -1
- package/lib/routes/pixiv/novel-api/user-novels/sfw.ts +1 -1
- package/lib/routes/pixivision/utils.ts +1 -1
- package/lib/routes/pku/hr.ts +1 -1
- package/lib/routes/pku/scc/recruit.ts +1 -1
- package/lib/routes/producthunt/templates/description.art +2 -2
- package/lib/routes/producthunt/today.ts +17 -8
- package/lib/routes/ps/trophy.ts +1 -1
- package/lib/routes/pubscholar/utils.ts +14 -1
- package/lib/routes/qweather/3days.ts +14 -14
- package/lib/routes/qweather/now.ts +12 -10
- package/lib/routes/qweather/util.tsx +89 -0
- package/lib/routes/qwenlm/blog.ts +75 -0
- package/lib/routes/qwenlm/namespace.ts +6 -0
- package/lib/routes/radio-canada/latest.ts +30 -17
- package/lib/routes/ruc/ai.ts +1 -1
- package/lib/routes/ruc/hr.ts +1 -1
- package/lib/routes/samrdprc/index.ts +241 -0
- package/lib/routes/samrdprc/namespace.ts +1 -1
- package/lib/routes/sdo/ff14risingstones/api.ts +78 -0
- package/lib/routes/sdo/ff14risingstones/constant.ts +338 -0
- package/lib/routes/sdo/ff14risingstones/posts.ts +80 -0
- package/lib/routes/sdo/ff14risingstones/strats.ts +75 -0
- package/lib/routes/sdo/ff14risingstones/templates/duties-party.art +41 -0
- package/lib/routes/sdo/ff14risingstones/templates/fc-party.art +26 -0
- package/lib/routes/sdo/ff14risingstones/templates/novice-network-party.art +9 -0
- package/lib/routes/sdo/ff14risingstones/templates/rp-party.art +15 -0
- package/lib/routes/sdo/ff14risingstones/timeline.ts +31 -0
- package/lib/routes/sdo/ff14risingstones/types/dynamic.ts +50 -0
- package/lib/routes/sdo/ff14risingstones/types/index.ts +3 -0
- package/lib/routes/sdo/ff14risingstones/types/other.ts +57 -0
- package/lib/routes/sdo/ff14risingstones/types/party.ts +111 -0
- package/lib/routes/sdo/ff14risingstones/user-dynamics.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-posts.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-resently.ts +38 -0
- package/lib/routes/sdo/ff14risingstones/user-strats.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/utils.ts +215 -0
- package/lib/routes/sdo/namespace.ts +7 -0
- package/lib/routes/showstart/utils.ts +1 -1
- package/lib/routes/sohu/mp.ts +1 -1
- package/lib/routes/sotwe/user.ts +1 -1
- package/lib/routes/surfshark/blog.ts +273 -77
- package/lib/routes/surfshark/templates/description.art +16 -4
- package/lib/routes/sustainabilitymag/articles.ts +1 -1
- package/lib/routes/syosetu/dev.ts +1 -1
- package/lib/routes/syosetu/ranking-isekai.ts +1 -1
- package/lib/routes/szse/disclosure/listed-notice.ts +44 -6
- package/lib/routes/telegram/channel-media.ts +249 -0
- package/lib/routes/telegram/channel.ts +5 -4
- package/lib/routes/telegram/stories.ts +130 -0
- package/lib/routes/telegram/tglib/channel.ts +136 -118
- package/lib/routes/telegram/tglib/client.ts +37 -139
- package/lib/routes/tesla/cx.ts +1 -1
- package/lib/routes/theverge/index.ts +20 -6
- package/lib/routes/threads/utils.ts +7 -3
- package/lib/routes/tidb/blog.ts +1 -1
- package/lib/routes/toutiao/user.ts +2 -2
- package/lib/routes/twitter/api/mobile-api/api.ts +1 -1
- package/lib/routes/txrjy/fornumtopic.ts +2 -2
- package/lib/routes/typst/universe.ts +1 -1
- package/lib/routes/uber/blog.ts +87 -46
- package/lib/routes/weibo/utils.ts +17 -9
- package/lib/routes/xiaohongshu/user.ts +1 -1
- package/lib/routes/xjtu/ee-jzxx.ts +1 -1
- package/lib/routes/yahoo/news/utils.ts +1 -1
- package/lib/routes/ymgal/article.ts +1 -1
- package/lib/routes/yoasobi-music/media.ts +1 -1
- package/lib/routes/youtube/api/youtubei.ts +1 -1
- package/lib/routes/youtube/community.ts +4 -4
- package/lib/routes/zaker/utils.ts +1 -1
- package/lib/routes/zaobao/util.tsx +1 -1
- package/lib/server.ts +1 -1
- package/lib/types.ts +1 -1
- package/lib/utils/puppeteer-utils.test.ts +2 -2
- package/lib/views/index.tsx +4 -4
- package/package.json +40 -40
- package/lib/routes/qweather/templates/3days.art +0 -22
- package/lib/routes/qweather/templates/now.art +0 -16
- package/lib/routes/xinhuanet/app.ts +0 -109
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { Route } from '@/types';
|
|
2
|
-
|
|
3
2
|
import cache from '@/utils/cache';
|
|
4
3
|
import got from '@/utils/got';
|
|
5
|
-
import { art } from '@/utils/render';
|
|
6
|
-
import path from 'node:path';
|
|
7
4
|
import { config } from '@/config';
|
|
8
5
|
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
|
9
|
-
|
|
10
|
-
const WEATHER_API = 'https://devapi.qweather.com/v7/weather/3d';
|
|
11
|
-
const AIR_QUALITY_API = 'https://devapi.qweather.com/v7/air/5d';
|
|
12
|
-
const CIRY_LOOKUP_API = 'https://geoapi.qweather.com/v2/city/lookup';
|
|
6
|
+
import { render3DaysDescription, type WeatherForecastItem } from './util';
|
|
13
7
|
const author = 'QWeather';
|
|
14
8
|
|
|
15
9
|
export const route: Route = {
|
|
@@ -21,7 +15,11 @@ export const route: Route = {
|
|
|
21
15
|
requireConfig: [
|
|
22
16
|
{
|
|
23
17
|
name: 'HEFENG_KEY',
|
|
24
|
-
description: '',
|
|
18
|
+
description: 'QWeather API KEY',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'HEFENG_API_HOST',
|
|
22
|
+
description: 'This is required after 2026/01/01: https://blog.qweather.com/announce/public-api-domain-change-to-api-host/',
|
|
25
23
|
},
|
|
26
24
|
],
|
|
27
25
|
requirePuppeteer: false,
|
|
@@ -37,10 +35,14 @@ export const route: Route = {
|
|
|
37
35
|
};
|
|
38
36
|
|
|
39
37
|
async function handler(ctx) {
|
|
40
|
-
if (!config.hefeng.key) {
|
|
38
|
+
if (!config.hefeng.key || !config.hefeng.apiHost) {
|
|
41
39
|
throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>');
|
|
42
40
|
}
|
|
43
41
|
|
|
42
|
+
const WEATHER_API = `https://${config.hefeng.apiHost}/v7/weather/3d`;
|
|
43
|
+
const AIR_QUALITY_API = `https://${config.hefeng.apiHost}/v7/air/5d`;
|
|
44
|
+
const CIRY_LOOKUP_API = `https://${config.hefeng.apiHost}/geo/v2/city/lookup`;
|
|
45
|
+
|
|
44
46
|
const id = await cache.tryGet('qweather:' + ctx.req.param('location') + ':id', async () => {
|
|
45
47
|
const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
|
|
46
48
|
return response.data.location[0].id;
|
|
@@ -67,7 +69,7 @@ async function handler(ctx) {
|
|
|
67
69
|
const combined = {
|
|
68
70
|
updateTime: weatherData.updateTime,
|
|
69
71
|
fxLink: weatherData.fxLink,
|
|
70
|
-
daily: weatherData.daily.map((weatherItem) => {
|
|
72
|
+
daily: weatherData.daily.map((weatherItem: WeatherForecastItem) => {
|
|
71
73
|
const dailyAirQuality = airQualityData.daily.find((airQualityItem) => airQualityItem.fxDate === weatherItem.fxDate);
|
|
72
74
|
if (dailyAirQuality) {
|
|
73
75
|
return {
|
|
@@ -81,11 +83,9 @@ async function handler(ctx) {
|
|
|
81
83
|
return weatherItem;
|
|
82
84
|
}),
|
|
83
85
|
};
|
|
84
|
-
const items = combined.daily.map((item) => ({
|
|
86
|
+
const items = combined.daily.map((item: WeatherForecastItem) => ({
|
|
85
87
|
title: `${item.fxDate}: ${item.textDay === item.textNight ? item.textDay : item.textDay + '转' + item.textNight} ${item.tempMin}~${item.tempMax}℃`,
|
|
86
|
-
description:
|
|
87
|
-
item,
|
|
88
|
-
}),
|
|
88
|
+
description: render3DaysDescription(item),
|
|
89
89
|
pubDate: combined.updateTime,
|
|
90
90
|
guid: '位置:' + ctx.req.param('location') + '--日期:' + item.fxDate,
|
|
91
91
|
link: combined.fxLink,
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { Route } from '@/types';
|
|
2
|
-
|
|
3
2
|
import cache from '@/utils/cache';
|
|
4
3
|
import got from '@/utils/got';
|
|
5
|
-
import { art } from '@/utils/render';
|
|
6
|
-
import path from 'node:path';
|
|
7
4
|
import { parseDate } from '@/utils/parse-date';
|
|
8
5
|
import { config } from '@/config';
|
|
9
6
|
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
|
10
|
-
|
|
11
|
-
const rootUrl = 'https://devapi.qweather.com/v7/weather/now?';
|
|
7
|
+
import { renderNowDescription, type NowItem } from './util';
|
|
12
8
|
|
|
13
9
|
export const route: Route = {
|
|
14
10
|
path: '/now/:location',
|
|
@@ -21,6 +17,10 @@ export const route: Route = {
|
|
|
21
17
|
name: 'HEFENG_KEY',
|
|
22
18
|
description: '访问 `https://www.qweather.com/` 注册开发 API Key。',
|
|
23
19
|
},
|
|
20
|
+
{
|
|
21
|
+
name: 'HEFENG_API_HOST',
|
|
22
|
+
description: 'This is required after 2026/01/01: https://blog.qweather.com/announce/public-api-domain-change-to-api-host/',
|
|
23
|
+
},
|
|
24
24
|
],
|
|
25
25
|
requirePuppeteer: false,
|
|
26
26
|
antiCrawler: false,
|
|
@@ -34,16 +34,18 @@ export const route: Route = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
async function handler(ctx) {
|
|
37
|
-
if (!config.hefeng.key) {
|
|
37
|
+
if (!config.hefeng.key || !config.hefeng.apiHost) {
|
|
38
38
|
throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>');
|
|
39
39
|
}
|
|
40
|
+
const NOW_WEATHER_API = `https://${config.hefeng.apiHost}/v7/weather/now`;
|
|
41
|
+
const CIRY_LOOKUP_API = `https://${config.hefeng.apiHost}/geo/v2/city/lookup`;
|
|
40
42
|
|
|
41
43
|
const id = await cache.tryGet('qweather:' + ctx.req.param('location') + ':id', async () => {
|
|
42
|
-
const response = await got(
|
|
44
|
+
const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
|
|
43
45
|
const data = response.data.location.map((loc) => loc);
|
|
44
46
|
return data[0].id;
|
|
45
47
|
});
|
|
46
|
-
const requestUrl =
|
|
48
|
+
const requestUrl = `${NOW_WEATHER_API}?key=${config.hefeng.key}&location=${id}`;
|
|
47
49
|
const responseData = await cache.tryGet(
|
|
48
50
|
'qweather:' + ctx.req.param('location') + ':now',
|
|
49
51
|
async () => {
|
|
@@ -63,9 +65,9 @@ async function handler(ctx) {
|
|
|
63
65
|
return {
|
|
64
66
|
title: ctx.req.param('location') + '实时天气',
|
|
65
67
|
description: ctx.req.param('location') + '实时天气状况',
|
|
66
|
-
item: data.map((item) => ({
|
|
68
|
+
item: data.map((item: NowItem) => ({
|
|
67
69
|
title: '观测时间:' + time_show,
|
|
68
|
-
description:
|
|
70
|
+
description: renderNowDescription(item),
|
|
69
71
|
pubDate: timeObj,
|
|
70
72
|
guid: '位置:' + ctx.req.param('location') + '--时间:' + time_show,
|
|
71
73
|
link: responseData.fxLink,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { renderToString } from 'hono/jsx/dom/server';
|
|
2
|
+
|
|
3
|
+
interface WeatherForecastItem {
|
|
4
|
+
fxDate: string;
|
|
5
|
+
textDay: string;
|
|
6
|
+
textNight: string;
|
|
7
|
+
tempMin: number;
|
|
8
|
+
tempMax: number;
|
|
9
|
+
humidity: number;
|
|
10
|
+
aqi: number;
|
|
11
|
+
aqiCategory: string;
|
|
12
|
+
pressure: number;
|
|
13
|
+
uvIndex: number;
|
|
14
|
+
windDirDay: string;
|
|
15
|
+
windScaleDay: number;
|
|
16
|
+
windSpeedDay: number;
|
|
17
|
+
windDirNight: string;
|
|
18
|
+
windScaleNight: number;
|
|
19
|
+
windSpeedNight: number;
|
|
20
|
+
vis: number;
|
|
21
|
+
sunrise: string;
|
|
22
|
+
sunset: string;
|
|
23
|
+
moonPhase: string;
|
|
24
|
+
moonset: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface NowItem {
|
|
28
|
+
text: string;
|
|
29
|
+
temp: number;
|
|
30
|
+
feelsLike: number;
|
|
31
|
+
windDir: string;
|
|
32
|
+
windScale: number;
|
|
33
|
+
windSpeed: number;
|
|
34
|
+
humidity: number;
|
|
35
|
+
pressure: number;
|
|
36
|
+
precip: number;
|
|
37
|
+
vis: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const render3DaysDescription = (item: WeatherForecastItem) => renderToString(<WeatherForecast item={item} />);
|
|
41
|
+
const renderNowDescription = (item: NowItem) => renderToString(<Now item={item} />);
|
|
42
|
+
|
|
43
|
+
const WeatherForecast = ({ item }: { item: WeatherForecastItem }) => (
|
|
44
|
+
<p>
|
|
45
|
+
白天:{item.textDay}——夜间:{item.textNight}
|
|
46
|
+
<br />
|
|
47
|
+
气温:{item.tempMin}℃~{item.tempMax}℃
|
|
48
|
+
<br />
|
|
49
|
+
相对湿度:{item.humidity}%
|
|
50
|
+
<br />
|
|
51
|
+
空气质量指数:{item.aqi} ({item.aqiCategory})
|
|
52
|
+
<br />
|
|
53
|
+
大气压强:{item.pressure}百帕
|
|
54
|
+
<br />
|
|
55
|
+
紫外线强度:{item.uvIndex}
|
|
56
|
+
<br />
|
|
57
|
+
白天风向:{item.windDirDay} 风力:{item.windScaleDay}级 风速:{item.windSpeedDay}公里/小时
|
|
58
|
+
<br />
|
|
59
|
+
夜间风向:{item.windDirNight} 风力:{item.windScaleNight}级 风速:{item.windSpeedNight}公里/小时
|
|
60
|
+
<br />
|
|
61
|
+
能见度:{item.vis}公里
|
|
62
|
+
<br />
|
|
63
|
+
日出:{item.sunrise} 日落: {item.sunset}
|
|
64
|
+
<br />
|
|
65
|
+
月相:{item.moonPhase} 月出:{item.sunrise} 月落:{item.moonset}
|
|
66
|
+
</p>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const Now = ({ item }: { item: NowItem }) => (
|
|
70
|
+
<p>
|
|
71
|
+
天气:{item.text}
|
|
72
|
+
<br />
|
|
73
|
+
气温:{item.temp}℃
|
|
74
|
+
<br />
|
|
75
|
+
体感温度:{item.feelsLike}℃
|
|
76
|
+
<br />
|
|
77
|
+
风向:{item.windDir}
|
|
78
|
+
<br />
|
|
79
|
+
风力:{item.windScale}级 风速:{item.windSpeed}km/h
|
|
80
|
+
<br />
|
|
81
|
+
湿度:{item.humidity}% 大气压强:{item.pressure}hPa
|
|
82
|
+
<br />
|
|
83
|
+
本小时降水量:{item.precip}mm
|
|
84
|
+
<br />
|
|
85
|
+
能见度:{item.vis}km
|
|
86
|
+
</p>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
export { render3DaysDescription, renderNowDescription, type WeatherForecastItem, type NowItem };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Route } from '@/types';
|
|
2
|
+
import ofetch from '@/utils/ofetch';
|
|
3
|
+
import { load } from 'cheerio';
|
|
4
|
+
import cache from '@/utils/cache';
|
|
5
|
+
import { parseDate } from '@/utils/parse-date';
|
|
6
|
+
|
|
7
|
+
export const route: Route = {
|
|
8
|
+
path: '/blog/:lang?',
|
|
9
|
+
categories: ['blog'],
|
|
10
|
+
example: '/qwenlm/blog/zh',
|
|
11
|
+
parameters: { lang: 'Blog language' },
|
|
12
|
+
features: {
|
|
13
|
+
requireConfig: false,
|
|
14
|
+
requirePuppeteer: false,
|
|
15
|
+
antiCrawler: false,
|
|
16
|
+
supportBT: false,
|
|
17
|
+
supportPodcast: false,
|
|
18
|
+
supportScihub: false,
|
|
19
|
+
},
|
|
20
|
+
radar: [
|
|
21
|
+
{
|
|
22
|
+
source: ['qwenlm.github.io/blog/', 'qwenlm.github.io/:lang/blog/'],
|
|
23
|
+
target: '/qwenlm/blog/:lang',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
name: 'Blog',
|
|
27
|
+
maintainers: ['Kjasn'],
|
|
28
|
+
handler: async (ctx) => {
|
|
29
|
+
const { lang } = ctx.req.param();
|
|
30
|
+
|
|
31
|
+
const blogUrl = lang ? `https://qwenlm.github.io/${lang}/blog` : 'https://qwenlm.github.io/blog';
|
|
32
|
+
|
|
33
|
+
const response = await ofetch(blogUrl);
|
|
34
|
+
const $ = load(response);
|
|
35
|
+
|
|
36
|
+
// get blog list
|
|
37
|
+
const list = $('article.post-entry')
|
|
38
|
+
.toArray()
|
|
39
|
+
.map((item) => {
|
|
40
|
+
item = $(item);
|
|
41
|
+
|
|
42
|
+
const dateString = item
|
|
43
|
+
.find('.entry-footer span')
|
|
44
|
+
.attr('title')
|
|
45
|
+
.trim()
|
|
46
|
+
.replace(/\+0800$/, '');
|
|
47
|
+
// const pubDate = timezone(parseDate(dateString, 'YYYY-MM-DD HH:mm:ss ZZ'), +8);
|
|
48
|
+
const pubDate = parseDate(dateString);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
title: item.find('header.entry-header h2').text().trim(),
|
|
52
|
+
link: item.find('.entry-link').attr('href'),
|
|
53
|
+
pubDate,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// get blog content
|
|
58
|
+
const items = await Promise.all(
|
|
59
|
+
list.map((item) =>
|
|
60
|
+
cache.tryGet(item.link, async () => {
|
|
61
|
+
const response = await ofetch(item.link);
|
|
62
|
+
const $ = load(response);
|
|
63
|
+
item.description = $('main').html();
|
|
64
|
+
return item;
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
title: 'Qwen Blog',
|
|
71
|
+
link: blogUrl,
|
|
72
|
+
item: items,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Route } from '@/types';
|
|
2
2
|
import cache from '@/utils/cache';
|
|
3
|
-
import
|
|
3
|
+
import ofetch from '@/utils/ofetch';
|
|
4
4
|
import { parseDate } from '@/utils/parse-date';
|
|
5
5
|
import { load } from 'cheerio';
|
|
6
6
|
|
|
@@ -37,12 +37,9 @@ async function handler(ctx) {
|
|
|
37
37
|
const apiRootUrl = 'https://services.radio-canada.ca';
|
|
38
38
|
const currentUrl = `${apiRootUrl}/neuro/sphere/v1/rci/${language}/continuous-feed?pageSize=50`;
|
|
39
39
|
|
|
40
|
-
const response = await
|
|
41
|
-
method: 'get',
|
|
42
|
-
url: currentUrl,
|
|
43
|
-
});
|
|
40
|
+
const response = await ofetch(currentUrl);
|
|
44
41
|
|
|
45
|
-
const list = response.data.
|
|
42
|
+
const list = response.data.lineup.items.map((item) => ({
|
|
46
43
|
title: item.title,
|
|
47
44
|
category: item.kicker,
|
|
48
45
|
link: `${rootUrl}${item.url}`,
|
|
@@ -52,18 +49,15 @@ async function handler(ctx) {
|
|
|
52
49
|
const items = await Promise.all(
|
|
53
50
|
list.map((item) =>
|
|
54
51
|
cache.tryGet(item.link, async () => {
|
|
55
|
-
const detailResponse = await
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
52
|
+
const detailResponse = await ofetch(item.link);
|
|
53
|
+
|
|
54
|
+
const $ = load(detailResponse);
|
|
59
55
|
|
|
60
|
-
const $ = load(detailResponse.data);
|
|
61
56
|
const rcState = $('script:contains("window._rcState_ = ")')
|
|
62
57
|
.text()
|
|
63
|
-
.match(/window\._rcState_ = (.*);/)[1];
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
item.description = news.data.newsStory.body.html.replaceAll(String.raw`\n`, '<br>');
|
|
58
|
+
.match(/window\._rcState_ = (.*);/)?.[1];
|
|
59
|
+
|
|
60
|
+
item.description = rcState ? parseDescriptionFromState(rcState) : ($(`div[data-testid="newsStoryMedia"]`).html() ?? '') + ($('article > main').html() ?? '');
|
|
67
61
|
|
|
68
62
|
return item;
|
|
69
63
|
})
|
|
@@ -71,8 +65,27 @@ async function handler(ctx) {
|
|
|
71
65
|
);
|
|
72
66
|
|
|
73
67
|
return {
|
|
74
|
-
title: response.
|
|
75
|
-
link: response.
|
|
68
|
+
title: response.meta.title,
|
|
69
|
+
link: response.metric.metrikContent.omniture.url,
|
|
76
70
|
item: items,
|
|
77
71
|
};
|
|
78
72
|
}
|
|
73
|
+
|
|
74
|
+
const parseDescriptionFromState = (rcState) => {
|
|
75
|
+
const rcStateJson = JSON.parse(rcState);
|
|
76
|
+
const news = Object.values(rcStateJson?.pages?.pages ?? {})[0] as any;
|
|
77
|
+
|
|
78
|
+
const headerImg = news?.data?.newsStory?.headerMultimediaItem?.picture;
|
|
79
|
+
const headerImgUrl = headerImg?.pattern ? headerImg?.pattern.replace('/q_auto,w_{width}', '').replace('{ratio}', '16x9') : '';
|
|
80
|
+
const header = `<figure><picture><img src="${headerImgUrl}" alt="${headerImg?.alt ?? ''}"></picture><figcaption>${headerImg?.legend ?? ''}</figcaption></figure>`;
|
|
81
|
+
const primer = news?.data?.newsStory?.primer?.replaceAll(String.raw`\n`, '') ?? '';
|
|
82
|
+
const body = news?.data?.newsStory?.body?.html?.replaceAll(String.raw`\n`, '') ?? '';
|
|
83
|
+
let bodyWithImg = body;
|
|
84
|
+
for (const [index, attachment] of (news?.data?.newsStory?.body?.attachments ?? []).entries()) {
|
|
85
|
+
const placeholder = `<!--body:attachment:${index}-->`;
|
|
86
|
+
const picture = attachment?.picture;
|
|
87
|
+
const imageUrl = picture?.pattern ? picture?.pattern.replace('/q_auto,w_{width}', '').replace('{ratio}', attachment?.dimensionRatio ?? '16x9') : '';
|
|
88
|
+
bodyWithImg = bodyWithImg.replace(placeholder, `<figure><picture><img src="${imageUrl}" alt="${picture?.alt ?? ''}"></picture><figcaption>${picture?.legend ?? ''}</figcaption></figure>`);
|
|
89
|
+
}
|
|
90
|
+
return header + primer + bodyWithImg;
|
|
91
|
+
};
|
package/lib/routes/ruc/ai.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const route: Route = {
|
|
|
26
26
|
name: '高瓴人工智能学院',
|
|
27
27
|
maintainers: ['yinhanyan'],
|
|
28
28
|
handler: async (ctx) => {
|
|
29
|
-
const category = ctx.req.param('category')?.
|
|
29
|
+
const category = ctx.req.param('category')?.replaceAll('-', '/') ?? 'newslist/notice';
|
|
30
30
|
const baseURL = `http://ai.ruc.edu.cn/${category}/`;
|
|
31
31
|
const indexUrl = baseURL + 'index.htm';
|
|
32
32
|
const response = await ofetch(indexUrl);
|
package/lib/routes/ruc/hr.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const route: Route = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
async function handler(ctx) {
|
|
37
|
-
const category = ctx.req.param('category')?.
|
|
37
|
+
const category = ctx.req.param('category')?.replaceAll('-', '/') ?? 'tzgg';
|
|
38
38
|
|
|
39
39
|
const rootUrl = 'http://hr.ruc.edu.cn';
|
|
40
40
|
const currentUrl = `${rootUrl}/${category}/index.htm`;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { type Data, type DataItem, type Route, ViewType } from '@/types';
|
|
2
|
+
|
|
3
|
+
import cache from '@/utils/cache';
|
|
4
|
+
import ofetch from '@/utils/ofetch';
|
|
5
|
+
import { parseDate } from '@/utils/parse-date';
|
|
6
|
+
|
|
7
|
+
import { type CheerioAPI, type Cheerio, load } from 'cheerio';
|
|
8
|
+
import type { Element } from 'domhandler';
|
|
9
|
+
import { type Context } from 'hono';
|
|
10
|
+
|
|
11
|
+
export const handler = async (ctx: Context): Promise<Data> => {
|
|
12
|
+
const { id = 'xwdt/gzdt' } = ctx.req.param();
|
|
13
|
+
const limit: number = Number.parseInt(ctx.req.query('limit') ?? '17', 10);
|
|
14
|
+
|
|
15
|
+
const baseUrl: string = 'https://www.samrdprc.org.cn';
|
|
16
|
+
const targetUrl: string = new URL(id.endsWith('/') ? id : `${id}/`, baseUrl).href;
|
|
17
|
+
|
|
18
|
+
const response = await ofetch(targetUrl);
|
|
19
|
+
const $: CheerioAPI = load(response);
|
|
20
|
+
const language = $('html').attr('lang') ?? 'zh';
|
|
21
|
+
|
|
22
|
+
let items: DataItem[] = [];
|
|
23
|
+
|
|
24
|
+
items = $('div.boxl_ul ul li')
|
|
25
|
+
.slice(0, limit)
|
|
26
|
+
.toArray()
|
|
27
|
+
.map((el): Element => {
|
|
28
|
+
const $el: Cheerio<Element> = $(el);
|
|
29
|
+
const $aEl: Cheerio<Element> = $el.find('a').first();
|
|
30
|
+
|
|
31
|
+
const title: string = $aEl.text();
|
|
32
|
+
const pubDateStr: string | undefined = $el.find('span').text();
|
|
33
|
+
const linkUrl: string | undefined = $aEl.attr('href');
|
|
34
|
+
const upDatedStr: string | undefined = pubDateStr;
|
|
35
|
+
|
|
36
|
+
const processedItem: DataItem = {
|
|
37
|
+
title,
|
|
38
|
+
pubDate: pubDateStr ? parseDate(pubDateStr) : undefined,
|
|
39
|
+
link: linkUrl ? new URL(linkUrl, targetUrl).href : undefined,
|
|
40
|
+
updated: upDatedStr ? parseDate(upDatedStr) : undefined,
|
|
41
|
+
language,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return processedItem;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
items = await Promise.all(
|
|
48
|
+
items.map((item) => {
|
|
49
|
+
if (!item.link) {
|
|
50
|
+
return item;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return cache.tryGet(item.link, async (): Promise<DataItem> => {
|
|
54
|
+
const detailResponse = await ofetch(item.link);
|
|
55
|
+
const $$: CheerioAPI = load(detailResponse);
|
|
56
|
+
|
|
57
|
+
const title: string = $$('div.show_tit').text();
|
|
58
|
+
const description: string | undefined = $$('div.TRS_Editor div.TRS_Editor').html() ?? undefined;
|
|
59
|
+
const pubDateStr: string | undefined = $$('div.show_tit2').text().split(/:/).pop()?.trim();
|
|
60
|
+
const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? [];
|
|
61
|
+
const upDatedStr: string | undefined = pubDateStr;
|
|
62
|
+
|
|
63
|
+
const processedItem: DataItem = {
|
|
64
|
+
title,
|
|
65
|
+
description,
|
|
66
|
+
pubDate: pubDateStr ? parseDate(pubDateStr) : item.pubDate,
|
|
67
|
+
category: categories,
|
|
68
|
+
content: {
|
|
69
|
+
html: description,
|
|
70
|
+
text: description,
|
|
71
|
+
},
|
|
72
|
+
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
|
|
73
|
+
language,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
...item,
|
|
78
|
+
...processedItem,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
title: $('title').text(),
|
|
86
|
+
description: $('meta[name="description"]').attr('content'),
|
|
87
|
+
link: targetUrl,
|
|
88
|
+
item: items,
|
|
89
|
+
allowEmpty: true,
|
|
90
|
+
image: new URL('images/logo_DPRC.png', baseUrl).href,
|
|
91
|
+
author: $('meta[name="keyword"]').attr('content')?.split(/,/)[0],
|
|
92
|
+
language,
|
|
93
|
+
id: $('meta[property="og:url"]').attr('content'),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const route: Route = {
|
|
98
|
+
path: '/:id{.+}?',
|
|
99
|
+
name: '栏目',
|
|
100
|
+
url: 'www.samrdprc.org.cn',
|
|
101
|
+
maintainers: ['nczitzk'],
|
|
102
|
+
handler,
|
|
103
|
+
example: '/samrdprc/xwdt/gzdt',
|
|
104
|
+
parameters: {
|
|
105
|
+
id: {
|
|
106
|
+
description: '栏目 id,默认为 `xwdt/gzdt`,即国内新闻,可在对应分类页 URL 中找到',
|
|
107
|
+
options: [
|
|
108
|
+
{
|
|
109
|
+
label: '新闻动态',
|
|
110
|
+
value: 'xwdt/gzdt',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
label: '网站公告',
|
|
114
|
+
value: 'wzgg',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: '汽车召回',
|
|
118
|
+
value: 'qczh',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
label: '消费品召回',
|
|
122
|
+
value: 'xfpzh',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
label: '技术报告',
|
|
126
|
+
value: 'yjgz/jsyj',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
label: 'SAC/TC463',
|
|
130
|
+
value: 'yjgz/sactc',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: '研究动态',
|
|
134
|
+
value: 'yjgz/yjfx',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
label: '安全教育',
|
|
138
|
+
value: 'aqjy',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: '国内法规',
|
|
142
|
+
value: 'flfg/gnfg',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
description: `:::tip
|
|
148
|
+
订阅 [网站公告](https://www.samrdprc.org.cn/wzgg/),其源网址为 \`https://www.samrdprc.org.cn/wzgg/\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/samrdprc/wzgg\`](https://rsshub.app/samrdprc/wzgg)。
|
|
149
|
+
:::
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary>更多分类</summary>
|
|
153
|
+
|
|
154
|
+
#### 网站首页
|
|
155
|
+
|
|
156
|
+
| [新闻动态](https://www.samrdprc.org.cn/xwdt/gzdt/) | [网站公告](https://www.samrdprc.org.cn/wzgg/) | [汽车召回](https://www.samrdprc.org.cn/qczh/) | [消费品召回](https://www.samrdprc.org.cn/xfpzh/) |
|
|
157
|
+
| -------------------------------------------------- | --------------------------------------------- | --------------------------------------------- | ------------------------------------------------ |
|
|
158
|
+
| [xwdt/gzdt](https://rsshub.app/samrdprc/xwdt/gzdt) | [wzgg](https://rsshub.app/samrdprc/wzgg) | [qczh](https://rsshub.app/samrdprc/qczh) | [xfpzh](https://rsshub.app/samrdprc/xfpzh) |
|
|
159
|
+
|
|
160
|
+
#### 科学研究
|
|
161
|
+
|
|
162
|
+
| [技术报告](https://www.samrdprc.org.cn/yjgz/jsyj/) | [SAC/TC463](https://www.samrdprc.org.cn/yjgz/sactc/) | [研究动态](https://www.samrdprc.org.cn/yjgz/yjfx/) |
|
|
163
|
+
| -------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- |
|
|
164
|
+
| [yjgz/jsyj](https://rsshub.app/samrdprc/yjgz/jsyj) | [yjgz/sactc](https://rsshub.app/samrdprc/yjgz/sactc) | [yjgz/yjfx](https://rsshub.app/samrdprc/yjgz/yjfx) |
|
|
165
|
+
|
|
166
|
+
#### 安全教育
|
|
167
|
+
|
|
168
|
+
| [安全教育](https://www.samrdprc.org.cn/aqjy/) |
|
|
169
|
+
| --------------------------------------------- |
|
|
170
|
+
| [aqjy](https://rsshub.app/samrdprc/aqjy) |
|
|
171
|
+
|
|
172
|
+
#### 法律法规
|
|
173
|
+
|
|
174
|
+
| [国内法规](https://www.samrdprc.org.cn/flfg/gnfg/) |
|
|
175
|
+
| -------------------------------------------------- |
|
|
176
|
+
| [flfg/gnfg](https://rsshub.app/samrdprc/flfg/gnfg) |
|
|
177
|
+
</details>
|
|
178
|
+
`,
|
|
179
|
+
categories: ['government'],
|
|
180
|
+
features: {
|
|
181
|
+
requireConfig: false,
|
|
182
|
+
requirePuppeteer: false,
|
|
183
|
+
antiCrawler: false,
|
|
184
|
+
supportRadar: true,
|
|
185
|
+
supportBT: false,
|
|
186
|
+
supportPodcast: false,
|
|
187
|
+
supportScihub: false,
|
|
188
|
+
},
|
|
189
|
+
radar: [
|
|
190
|
+
{
|
|
191
|
+
source: ['www.samrdprc.org.cn/:id'],
|
|
192
|
+
target: '/:id',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: '网站首页 - 新闻动态',
|
|
196
|
+
source: ['www.samrdprc.org.cn/xwdt/gzdt/'],
|
|
197
|
+
target: '/xwdt/gzdt',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
title: '网站首页 - 网站公告',
|
|
201
|
+
source: ['www.samrdprc.org.cn/wzgg/'],
|
|
202
|
+
target: '/wzgg',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
title: '网站首页 - 汽车召回',
|
|
206
|
+
source: ['www.samrdprc.org.cn/qczh/'],
|
|
207
|
+
target: '/qczh',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
title: '网站首页 - 消费品召回',
|
|
211
|
+
source: ['www.samrdprc.org.cn/xfpzh/'],
|
|
212
|
+
target: '/xfpzh',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
title: '科学研究 - 技术报告',
|
|
216
|
+
source: ['www.samrdprc.org.cn/yjgz/jsyj/'],
|
|
217
|
+
target: '/yjgz/jsyj',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
title: '科学研究 - SAC/TC463',
|
|
221
|
+
source: ['www.samrdprc.org.cn/yjgz/sactc/'],
|
|
222
|
+
target: '/yjgz/sactc',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
title: '科学研究 - 研究动态',
|
|
226
|
+
source: ['www.samrdprc.org.cn/yjgz/yjfx/'],
|
|
227
|
+
target: '/yjgz/yjfx',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
title: '安全教育 - 安全教育',
|
|
231
|
+
source: ['www.samrdprc.org.cn/aqjy/'],
|
|
232
|
+
target: '/aqjy',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
title: '法律法规 - 国内法规',
|
|
236
|
+
source: ['www.samrdprc.org.cn/flfg/gnfg/'],
|
|
237
|
+
target: '/flfg/gnfg',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
view: ViewType.Articles,
|
|
241
|
+
};
|