rsshub 1.0.0-master.f6cb490 → 1.0.0-master.f6f0273

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.
Files changed (88) hide show
  1. package/lib/api/index.ts +1 -6
  2. package/lib/routes/2048/index.ts +24 -23
  3. package/lib/routes/anthropic/news.ts +27 -13
  4. package/lib/routes/asianfanfics/namespace.ts +7 -0
  5. package/lib/routes/asianfanfics/tag.ts +89 -0
  6. package/lib/routes/asianfanfics/text-search.ts +68 -0
  7. package/lib/routes/blockworks/index.ts +128 -0
  8. package/lib/routes/blockworks/namespace.ts +7 -0
  9. package/lib/routes/cmu/andypavlo/blog.ts +55 -0
  10. package/lib/routes/cmu/namespace.ts +7 -0
  11. package/lib/routes/coindesk/{index.ts → consensus-magazine.ts} +17 -21
  12. package/lib/routes/coindesk/namespace.ts +2 -1
  13. package/lib/routes/coindesk/news.ts +47 -0
  14. package/lib/routes/coindesk/utils.ts +26 -0
  15. package/lib/routes/cointelegraph/index.ts +106 -0
  16. package/lib/routes/cointelegraph/namespace.ts +7 -0
  17. package/lib/routes/collabo-cafe/category.ts +37 -0
  18. package/lib/routes/collabo-cafe/index.ts +35 -0
  19. package/lib/routes/collabo-cafe/namespace.ts +9 -0
  20. package/lib/routes/collabo-cafe/parser.ts +29 -0
  21. package/lib/routes/collabo-cafe/tag.ts +37 -0
  22. package/lib/routes/cryptoslate/index.ts +98 -0
  23. package/lib/routes/cryptoslate/namespace.ts +7 -0
  24. package/lib/routes/decrypt/index.ts +115 -0
  25. package/lib/routes/decrypt/namespace.ts +7 -0
  26. package/lib/routes/discuz/discuz.ts +7 -9
  27. package/lib/routes/fangchan/list.ts +224 -0
  28. package/lib/routes/fangchan/namespace.ts +9 -0
  29. package/lib/routes/fangchan/templates/description.art +7 -0
  30. package/lib/routes/foreignaffairs/namespace.ts +7 -0
  31. package/lib/routes/foreignaffairs/rss.ts +55 -0
  32. package/lib/routes/forklog/index.ts +72 -0
  33. package/lib/routes/forklog/namespace.ts +7 -0
  34. package/lib/routes/gcores/categories.ts +129 -0
  35. package/lib/routes/gcores/collections.ts +129 -0
  36. package/lib/routes/gcores/topics.ts +63 -0
  37. package/lib/routes/gov/moa/gjs.ts +210 -0
  38. package/lib/routes/gov/tianjin/tjftz.ts +53 -0
  39. package/lib/routes/gov/tianjin/tjrcgzw.ts +51 -0
  40. package/lib/routes/grainoil/category.ts +207 -0
  41. package/lib/routes/grainoil/namespace.ts +9 -0
  42. package/lib/routes/huxiu/util.ts +11 -9
  43. package/lib/routes/ifanr/category.ts +7 -2
  44. package/lib/routes/ifanr/digest.ts +1 -1
  45. package/lib/routes/ifanr/index.ts +1 -1
  46. package/lib/routes/instructables/projects.ts +20 -15
  47. package/lib/routes/juejin/collections.ts +1 -1
  48. package/lib/routes/komiic/comic.ts +88 -0
  49. package/lib/routes/komiic/namespace.ts +7 -0
  50. package/lib/routes/leagueoflegends/namespace.ts +8 -0
  51. package/lib/routes/leagueoflegends/patch-notes.ts +76 -0
  52. package/lib/routes/likeshop/index.ts +43 -0
  53. package/lib/routes/likeshop/namespace.ts +7 -0
  54. package/lib/routes/ltaaa/article.ts +180 -0
  55. package/lib/routes/ltaaa/namespace.ts +9 -0
  56. package/lib/routes/ltaaa/templates/description.art +7 -0
  57. package/lib/routes/mashiro/index.ts +1 -0
  58. package/lib/routes/nhentai/util.ts +4 -1
  59. package/lib/routes/pinterest/user.ts +9 -0
  60. package/lib/routes/sohu/mp.ts +3 -2
  61. package/lib/routes/spotify/show.ts +1 -1
  62. package/lib/routes/stcn/index.ts +241 -136
  63. package/lib/routes/stcn/kx.ts +144 -0
  64. package/lib/routes/swjtu/namespace.ts +1 -1
  65. package/lib/routes/swjtu/{scai/bks.ts → scai.ts} +34 -20
  66. package/lib/routes/swjtu/sports.ts +77 -0
  67. package/lib/routes/theblock/index.ts +142 -0
  68. package/lib/routes/theblock/namespace.ts +7 -0
  69. package/lib/routes/theverge/index.ts +73 -62
  70. package/lib/routes/theverge/templates/header.art +19 -0
  71. package/lib/routes/threads/index.ts +73 -54
  72. package/lib/routes/threads/utils.ts +60 -78
  73. package/lib/routes/tmtpost/column.ts +298 -0
  74. package/lib/routes/tmtpost/new.ts +4 -199
  75. package/lib/routes/tmtpost/util.ts +207 -0
  76. package/lib/routes/toranoana/namespace.ts +7 -0
  77. package/lib/routes/toranoana/news.ts +110 -0
  78. package/lib/routes/wainao/templates/description.art +9 -0
  79. package/lib/routes/wainao/topics.ts +214 -0
  80. package/lib/routes/xiaoyuzhou/podcast.ts +27 -27
  81. package/lib/routes/xjtu/yz.ts +74 -0
  82. package/lib/routes/youmemark/index.ts +6 -6
  83. package/lib/routes/zaobao/util.ts +11 -3
  84. package/lib/routes/zhihu/answers.ts +26 -54
  85. package/package.json +36 -35
  86. package/lib/routes/gcores/category.ts +0 -171
  87. package/lib/routes/gcores/collection.ts +0 -161
  88. package/lib/routes-deprecated/ltaaa/index.js +0 -69
@@ -0,0 +1,63 @@
1
+ import { type Data, type Route, ViewType } from '@/types';
2
+
3
+ import { getCurrentPath } from '@/utils/helpers';
4
+ import { type Context } from 'hono';
5
+
6
+ import { baseUrl, processItems } from './util';
7
+
8
+ export const __dirname = getCurrentPath(import.meta.url);
9
+
10
+ export const handler = async (ctx: Context): Promise<Data> => {
11
+ const { id } = ctx.req.param();
12
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
13
+
14
+ const targetUrl: string = new URL(`topics/${id ?? 'home'}`, baseUrl).href;
15
+ const apiUrl: string = new URL(`gapi/v1/${id ? `topics/${id}/recommend` : 'talk-original-recommendations'}`, baseUrl).href;
16
+
17
+ const query = {
18
+ 'page[limit]': limit,
19
+ include: 'talk,talk.topic,talk.user',
20
+ 'talk-include': 'topic,user',
21
+ };
22
+
23
+ return await processItems(limit, query, apiUrl, targetUrl);
24
+ };
25
+
26
+ export const route: Route = {
27
+ path: ['/topics/:id/recommend', '/topics/recommend'],
28
+ name: '机组推荐',
29
+ url: 'www.gcores.com',
30
+ maintainers: ['nczitzk'],
31
+ handler,
32
+ example: '/gcores/topics/recommend',
33
+ parameters: {
34
+ id: {
35
+ description: '小组 ID,默认为空,即全部,可在对应小组页 URL 中找到',
36
+ },
37
+ },
38
+ description: `:::tip
39
+ 若订阅 [我的年度总结](https://www.gcores.com/topics/581),网址为 \`https://www.gcores.com/topics/581\`,请截取 \`https://www.gcores.com/topics/\` 到末尾的部分 \`581\` 作为 \`id\` 参数填入,此时目标路由为 [\`/gcores/topics/581/recommend\`](https://rsshub.app/gcores/topics/581/recommend)。
40
+ :::
41
+ `,
42
+ categories: ['game'],
43
+ features: {
44
+ requireConfig: false,
45
+ requirePuppeteer: false,
46
+ antiCrawler: false,
47
+ supportRadar: true,
48
+ supportBT: false,
49
+ supportPodcast: false,
50
+ supportScihub: false,
51
+ },
52
+ radar: [
53
+ {
54
+ source: ['www.gcores.com/topics/home'],
55
+ target: '/gcores/topics/recommend',
56
+ },
57
+ {
58
+ source: ['www.gcores.com/topics/:id'],
59
+ target: '/gcores/topics/:id/recommend',
60
+ },
61
+ ],
62
+ view: ViewType.SocialMedia,
63
+ };
@@ -0,0 +1,210 @@
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
+ import timezone from '@/utils/timezone';
7
+
8
+ import { type CheerioAPI, type Cheerio, type Element, load } from 'cheerio';
9
+ import { type Context } from 'hono';
10
+
11
+ export const handler = async (ctx: Context): Promise<Data> => {
12
+ const { category = 'gzdt' } = ctx.req.param();
13
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
14
+
15
+ const baseUrl: string = 'http://www.gjs.moa.gov.cn';
16
+ const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href;
17
+
18
+ const response = await ofetch(targetUrl);
19
+ const $: CheerioAPI = load(response);
20
+ const language = $('html').attr('lang') ?? 'zh-CN';
21
+
22
+ let items: DataItem[] = [];
23
+
24
+ items = $('ul#div li')
25
+ .slice(0, limit)
26
+ .toArray()
27
+ .map((el): Element => {
28
+ const $el: Cheerio<Element> = $(el);
29
+
30
+ const aEl: Cheerio<Element> = $el.find('a');
31
+
32
+ const title: string = aEl.attr('title') ?? $el.find('span.sj_gztzle').text();
33
+ const pubDateStr: string | undefined = $el.find('span.sj_gztzri').text();
34
+ const linkUrl: string | undefined = aEl.attr('href');
35
+ const upDatedStr: string | undefined = pubDateStr;
36
+
37
+ const processedItem: DataItem = {
38
+ title,
39
+ pubDate: parseDate(pubDateStr),
40
+ link: linkUrl ? new URL(linkUrl, targetUrl).href : undefined,
41
+ updated: parseDate(upDatedStr),
42
+ language,
43
+ };
44
+
45
+ return processedItem;
46
+ });
47
+
48
+ items = (
49
+ await Promise.all(
50
+ items.map((item) => {
51
+ if (!item.link) {
52
+ return item;
53
+ }
54
+
55
+ return cache.tryGet(item.link, async (): Promise<DataItem> => {
56
+ const detailResponse = await ofetch(item.link);
57
+ const $$: CheerioAPI = load(detailResponse);
58
+
59
+ const title: string = $$('meta[name="ArticleTitle"]').attr('content') ?? '';
60
+ const description: string = $$('div.TRS_Editor').html() ?? '';
61
+ const pubDateStr: string | undefined = $$('meta[name="PubDate"]').attr('content');
62
+ const linkUrl: string | undefined = $$('meta[name="Url"]').attr('content');
63
+ const categoryEls: Element[] = $$('meta[name="ColumnName"], meta[name="ContentSource"], meta[name="Keywords"]').toArray();
64
+ const categories: string[] = [...new Set(categoryEls.map((el) => $$(el).attr('content') as string).filter(Boolean))];
65
+ const authors: DataItem['author'] = $$('meta[name="Author"]').attr('content');
66
+ const upDatedStr: string | undefined = pubDateStr;
67
+
68
+ let processedItem: DataItem = {
69
+ title,
70
+ description,
71
+ pubDate: pubDateStr ? timezone(parseDate(pubDateStr), +8) : item.pubDate,
72
+ link: linkUrl ?? item.link,
73
+ category: categories,
74
+ author: authors,
75
+ content: {
76
+ html: description,
77
+ text: description,
78
+ },
79
+ updated: upDatedStr ? timezone(parseDate(upDatedStr), +8) : item.updated,
80
+ language,
81
+ };
82
+
83
+ const $enclosureEl: Cheerio<Element> = $$('div.sj_fujianxia_right ul li a').first();
84
+ const enclosureUrl: string | undefined = $enclosureEl.attr('href');
85
+
86
+ if (enclosureUrl) {
87
+ const enclosureType: string = `application/${enclosureUrl.split('.').pop()}`;
88
+ const enclosureTitle: string = $enclosureEl.text();
89
+
90
+ processedItem = {
91
+ ...processedItem,
92
+ enclosure_url: new URL(enclosureUrl, item.link).href,
93
+ enclosure_type: enclosureType,
94
+ enclosure_title: enclosureTitle || title,
95
+ };
96
+ }
97
+
98
+ return {
99
+ ...item,
100
+ ...processedItem,
101
+ };
102
+ });
103
+ })
104
+ )
105
+ ).filter((_): _ is DataItem => true);
106
+
107
+ const author: string = '中华人民共和国农业农村部国际合作司';
108
+ const description: string = $('meta[name="ColumnName"]').attr('content') ?? '';
109
+
110
+ return {
111
+ title: `${author} - ${description}`,
112
+ description,
113
+ link: targetUrl,
114
+ item: items,
115
+ allowEmpty: true,
116
+ image: new URL('images/logo-china.png', baseUrl).href,
117
+ author,
118
+ language,
119
+ };
120
+ };
121
+
122
+ export const route: Route = {
123
+ path: '/moa/gjs/:category{.+}?',
124
+ name: '中华人民共和国农业农村部国际合作司',
125
+ url: 'www.gjs.moa.gov.cn',
126
+ maintainers: ['nczitzk'],
127
+ handler,
128
+ example: '/gov/moa/gjs/gzdt',
129
+ parameters: {
130
+ category: {
131
+ description: '分类,默认为 `gzdt`,即工作动态,可在对应分类页 URL 中找到',
132
+ options: [
133
+ {
134
+ label: '工作动态',
135
+ value: 'gzdt',
136
+ },
137
+ {
138
+ label: '通知公告',
139
+ value: 'tzgg',
140
+ },
141
+ {
142
+ label: '“一带一路”合作和农业走出去',
143
+ value: 'ydylhzhhnyzcq',
144
+ },
145
+ {
146
+ label: '农业国际贸易监测与展望',
147
+ value: 'ncpmy',
148
+ },
149
+ {
150
+ label: '多双边合作',
151
+ value: 'dsbhz',
152
+ },
153
+ ],
154
+ },
155
+ },
156
+ description: `:::tip
157
+ 若订阅 [中华人民共和国农业农村部国际合作司工作动态](https://www.gjs.moa.gov.cn/gzdt/),网址为 \`https://www.gjs.moa.gov.cn/gzdt/\`,请截取 \`https://www.gjs.moa.gov.cn/\` 到末尾 \`/\` 的部分 \`gzdt\` 作为 \`category\` 参数填入,此时目标路由为 [\`/gov/moa/gjs/gzdt\`](https://rsshub.app/gov/moa/gjs/gzdt)。
158
+ :::
159
+
160
+ | [工作动态](http://www.gjs.moa.gov.cn/gzdt/) | [通知公告](http://www.gjs.moa.gov.cn/tzgg/) | [“一带一路”合作和农业走出去](http://www.gjs.moa.gov.cn/ydylhzhhnyzcq/) | [农业国际贸易监测与展望](http://www.gjs.moa.gov.cn/ncpmy/) | [多双边合作](http://www.gjs.moa.gov.cn/dsbhz/) |
161
+ | ------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------- |
162
+ | [gzdt](https://rsshub.app/gov/moa/gjs/gzdt) | [tzgg](https://rsshub.app/gov/moa/gjs/tzgg) | [ydylhzhhnyzcq](https://rsshub.app/gov/moa/gjs/ydylhzhhnyzcq) | [ncpmy](https://rsshub.app/gov/moa/gjs/ncpmy) | [dsbhz](https://rsshub.app/gov/moa/gjs/dsbhz) |
163
+ `,
164
+ categories: ['government'],
165
+ features: {
166
+ requireConfig: false,
167
+ requirePuppeteer: false,
168
+ antiCrawler: false,
169
+ supportRadar: true,
170
+ supportBT: false,
171
+ supportPodcast: false,
172
+ supportScihub: false,
173
+ },
174
+ radar: [
175
+ {
176
+ source: ['www.gjs.moa.gov.cn/:category{.+}?'],
177
+ target: (params) => {
178
+ const category: string = params.category;
179
+
180
+ return `/gov/moa/gjs${category ? `/${category}` : ''}`;
181
+ },
182
+ },
183
+ {
184
+ title: '工作动态',
185
+ source: ['www.gjs.moa.gov.cn/gzdt/'],
186
+ target: '/moa/gjs/gzdt',
187
+ },
188
+ {
189
+ title: '通知公告',
190
+ source: ['www.gjs.moa.gov.cn/tzgg/'],
191
+ target: '/moa/gjs/tzgg',
192
+ },
193
+ {
194
+ title: '“一带一路”合作和农业走出去',
195
+ source: ['www.gjs.moa.gov.cn/ydylhzhhnyzcq/'],
196
+ target: '/moa/gjs/ydylhzhhnyzcq',
197
+ },
198
+ {
199
+ title: '农业国际贸易监测与展望',
200
+ source: ['www.gjs.moa.gov.cn/ncpmy/'],
201
+ target: '/moa/gjs/ncpmy',
202
+ },
203
+ {
204
+ title: '多双边合作',
205
+ source: ['www.gjs.moa.gov.cn/dsbhz/'],
206
+ target: '/moa/gjs/dsbhz',
207
+ },
208
+ ],
209
+ view: ViewType.Articles,
210
+ };
@@ -0,0 +1,53 @@
1
+ import { Route } from '@/types';
2
+ import got from '@/utils/got';
3
+ import { load } from 'cheerio';
4
+ import { parseDate } from '@/utils/parse-date';
5
+
6
+ export const route: Route = {
7
+ path: '/tianjin/tjftz-notice/:channelId',
8
+ categories: ['government'],
9
+ example: '/gov/tianjin/tjftz-notice/6302',
10
+ parameters: {
11
+ channelId: '公告分类id、详细信息点击源网站https://www.tjftz.gov.cn/请求中寻找',
12
+ },
13
+ radar: [
14
+ {
15
+ source: ['tjftz.gov.cn/channels/:channelId.html'],
16
+ target: '/tianjin/tjftz-notice/:channelId',
17
+ },
18
+ ],
19
+ name: '天津港保税区-公告',
20
+ url: 'tjftz.gov.cn',
21
+ maintainers: ['HaoyuLee'],
22
+ description: `
23
+ | 公告类别 | channelId |
24
+ | ------------ | -- |
25
+ | 首页>新闻>保税区要闻>区域聚焦 | 6302 |
26
+ `,
27
+ async handler(ctx) {
28
+ const { channelId = '6302' } = ctx.req.param();
29
+ const url = `https://www.tjftz.gov.cn/channels/${channelId}.html`;
30
+ const { data: response } = await got(url);
31
+ const noticeCate = load(response)('.location').text().trim();
32
+ const item = load(response)('#sec_right>ul li>.layui-row')
33
+ .toArray()
34
+ .map((el) => {
35
+ const $ = load(el);
36
+ return {
37
+ title: `天津保税区:${$('a').attr('title')}`,
38
+ link: `https://www.tjftz.gov.cn${$('a').attr('href')}`,
39
+ pubDate: parseDate($('span').text().trim()),
40
+ author: '天津保税区',
41
+ description: `
42
+ <h4>${noticeCate}</h4>
43
+ <a href="https://www.tjftz.gov.cn${$('a').attr('href')}">${$('a').attr('title')}</a>
44
+ `,
45
+ };
46
+ });
47
+ return {
48
+ title: '天津港保税区-公告',
49
+ link: url,
50
+ item,
51
+ };
52
+ },
53
+ };
@@ -0,0 +1,51 @@
1
+ import { Route } from '@/types';
2
+ import got from '@/utils/got';
3
+ import { load } from 'cheerio';
4
+ import { parseDate } from '@/utils/parse-date';
5
+ export const route: Route = {
6
+ path: '/tianjin/tjrcgzw-notice/:cate/:subCate',
7
+ categories: ['government'],
8
+ example: '/gov/tianjin/tjrcgzw-notice/rczc/sjrczc/',
9
+ parameters: {
10
+ channelId: '公告分类id、详细信息点击源网站https://hrss.tj.gov.cn/ztzl/ztzl1/tjrcgzw/请求中寻找',
11
+ },
12
+ radar: [
13
+ {
14
+ source: ['hrss.tj.gov.cn/ztzl/ztzl1/tjrcgzw/'],
15
+ target: '/tianjin/tjrcgzw-notice/:cate/:subCate',
16
+ },
17
+ ],
18
+ name: '天津人才工作网-公告',
19
+ url: 'hrss.tj.gov.cn/ztzl/ztzl1/tjrcgzw/',
20
+ maintainers: ['HaoyuLee'],
21
+ async handler(ctx) {
22
+ const { cate, subCate } = ctx.req.param();
23
+ const url = `https://hrss.tj.gov.cn/ztzl/ztzl1/tjrcgzw/${cate}/${subCate}/`;
24
+ const { data: response } = await got(url);
25
+ const noticeCate = load(response)('.routeBlockAuto').text().trim();
26
+ const item = load(response)('ul.listUlBox01>li')
27
+ .toArray()
28
+ .map((el) => {
29
+ const $ = load(el);
30
+ const title = $('a').text().trim();
31
+ const href = $('a').attr('href') || '';
32
+ const date = $('span').text().trim();
33
+ const link = href!.includes('http') ? href : new URL(href, url).href;
34
+ return {
35
+ title: `天津人才工作网:${title}`,
36
+ link,
37
+ pubDate: parseDate(date),
38
+ author: '天津人才工作网',
39
+ description: `
40
+ <h4>${noticeCate}</h4>
41
+ <a href="${link}">${title}</a>
42
+ `,
43
+ };
44
+ });
45
+ return {
46
+ title: '天津人才工作网-公告',
47
+ link: url,
48
+ item,
49
+ };
50
+ },
51
+ };
@@ -0,0 +1,207 @@
1
+ import { type Data, type DataItem, type Route, ViewType } from '@/types';
2
+
3
+ import ofetch from '@/utils/ofetch';
4
+ import { parseDate } from '@/utils/parse-date';
5
+ import timezone from '@/utils/timezone';
6
+
7
+ import { type CheerioAPI, type Cheerio, type Element, load } from 'cheerio';
8
+ import { type Context } from 'hono';
9
+
10
+ export const handler = async (ctx: Context): Promise<Data> => {
11
+ const { category, id } = ctx.req.param();
12
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
13
+
14
+ const baseUrl: string = 'http://load.grainoil.com.cn';
15
+ const targetUrl: string = new URL(`${category}/${id}.jspx`, baseUrl).href;
16
+
17
+ const response = await ofetch(targetUrl);
18
+ const $: CheerioAPI = load(response);
19
+ const language = $('html').attr('lang') ?? 'zh-CN';
20
+
21
+ let items: DataItem[] = [];
22
+
23
+ items = $('div.m_listpagebox ol li a')
24
+ .slice(0, limit)
25
+ .toArray()
26
+ .map((el): Element => {
27
+ const $el: Cheerio<Element> = $(el);
28
+
29
+ const title: string = $el.find('b').text();
30
+ const pubDateStr: string | undefined = $el.find('span').text().trim();
31
+ const linkUrl: string | undefined = $el.attr('href');
32
+ const upDatedStr: string | undefined = pubDateStr;
33
+
34
+ const processedItem: DataItem = {
35
+ title,
36
+ pubDate: pubDateStr ? timezone(parseDate(pubDateStr), +8) : undefined,
37
+ link: linkUrl,
38
+ updated: upDatedStr ? timezone(parseDate(upDatedStr), +8) : undefined,
39
+ language,
40
+ };
41
+
42
+ return processedItem;
43
+ });
44
+
45
+ items = (
46
+ await Promise.all(
47
+ items.map((item) => {
48
+ if (!item.link) {
49
+ return item;
50
+ }
51
+
52
+ return cache.tryGet(item.link, async (): Promise<DataItem> => {
53
+ const detailResponse = await ofetch(item.link);
54
+ const $$: CheerioAPI = load(detailResponse);
55
+
56
+ const title: string = $$('div.m_tit h2').text();
57
+ const description: string = $$('div.TRS_Editor').html() ?? '';
58
+ const authors: DataItem['author'] = $$('div.m_tit h2 a').first().text();
59
+
60
+ const processedItem: DataItem = {
61
+ title,
62
+ description,
63
+ author: authors,
64
+ content: {
65
+ html: description,
66
+ text: description,
67
+ },
68
+ language,
69
+ };
70
+
71
+ return {
72
+ ...item,
73
+ ...processedItem,
74
+ };
75
+ });
76
+ })
77
+ )
78
+ ).filter((_): _ is DataItem => true);
79
+
80
+ return {
81
+ title: $('title').text(),
82
+ description: $('meta[http-equiv="description"]').attr('content'),
83
+ link: targetUrl,
84
+ item: items,
85
+ allowEmpty: true,
86
+ image: $('div.top_logo img').attr('src') ? new URL($('div.top_logo img').attr('src') as string, baseUrl).href : undefined,
87
+ author: $('meta[http-equiv="keywords"]').attr('content'),
88
+ language,
89
+ id: targetUrl,
90
+ };
91
+ };
92
+
93
+ export const route: Route = {
94
+ path: '/:category/:id',
95
+ name: '分类',
96
+ url: 'load.grainoil.com.cn',
97
+ maintainers: ['nczitzk'],
98
+ handler,
99
+ example: '/grainoil/newsListHome/3',
100
+ parameters: {
101
+ category: {
102
+ description: '分类,默认为 `newsListHome`,可在对应分类页 URL 中找到',
103
+ options: [
104
+ {
105
+ label: 'newsListHome',
106
+ value: 'newsListHome',
107
+ },
108
+ {
109
+ label: 'newsListChannel',
110
+ value: 'newsListChannel',
111
+ },
112
+ ],
113
+ },
114
+ id: {
115
+ description: '分类 ID,可在对应分类页 URL 中找到',
116
+ },
117
+ },
118
+ description: `:::tip
119
+ 若订阅 [政务信息](http://load.grainoil.com.cn/newsListHome/1430.jspx),网址为 \`http://load.grainoil.com.cn/newsListHome/1430.jspx\`,请截取 \`https://load.grainoil.com.cn/\` 到末尾 \`.jspx\` 的部分 \`newsListHome/1430\` 作为 \`category\` 和 \`id\`参数填入,此时目标路由为 [\`/grainoil/newsListHome/1430\`](https://rsshub.app/grainoil/newsListHome/1430)。
120
+
121
+ :::
122
+
123
+ <details>
124
+ <summary>更多分类</summary>
125
+
126
+ | 分类 | ID |
127
+ | -------- | ------------------ |
128
+ | 政务信息 | newsListHome/1430 |
129
+ | 要闻动态 | newsListHome/3 |
130
+ | 产业经济 | newsListHome/1469 |
131
+ | 产业信息 | newsListHome/1471 |
132
+ | 爱粮节粮 | newsListHome/1470 |
133
+ | 政策法规 | newsListChannel/18 |
134
+ | 生产气象 | newsListChannel/19 |
135
+ | 统计资料 | newsListChannel/20 |
136
+ | 综合信息 | newsListChannel/21 |
137
+
138
+ </details>
139
+ `,
140
+ categories: ['new-media'],
141
+ features: {
142
+ requireConfig: false,
143
+ requirePuppeteer: false,
144
+ antiCrawler: false,
145
+ supportRadar: true,
146
+ supportBT: false,
147
+ supportPodcast: false,
148
+ supportScihub: false,
149
+ },
150
+ radar: [
151
+ {
152
+ source: ['load.grainoil.com.cn/:category/:id'],
153
+ target: (params) => {
154
+ const category: string = params.category;
155
+ const id: string = params.id;
156
+
157
+ return `/grainoil/${category}/${id}`;
158
+ },
159
+ },
160
+ {
161
+ title: '政务信息',
162
+ source: ['load.grainoil.com.cn/newsListHome/1430.jspx'],
163
+ target: '/newsListHome/1430',
164
+ },
165
+ {
166
+ title: '要闻动态',
167
+ source: ['load.grainoil.com.cn/newsListHome/3.jspx'],
168
+ target: '/newsListHome/3',
169
+ },
170
+ {
171
+ title: '产业经济',
172
+ source: ['load.grainoil.com.cn/newsListHome/1469.jspx'],
173
+ target: '/newsListHome/1469',
174
+ },
175
+ {
176
+ title: '产业信息',
177
+ source: ['load.grainoil.com.cn/newsListHome/1471.jspx'],
178
+ target: '/newsListHome/1471',
179
+ },
180
+ {
181
+ title: '爱粮节粮',
182
+ source: ['load.grainoil.com.cn/newsListHome/1470.jspx'],
183
+ target: '/newsListHome/1470',
184
+ },
185
+ {
186
+ title: '政策法规',
187
+ source: ['load.grainoil.com.cn/newsListChannel/18.jspx'],
188
+ target: '/newsListChannel/18',
189
+ },
190
+ {
191
+ title: '生产气象',
192
+ source: ['load.grainoil.com.cn/newsListChannel/19.jspx'],
193
+ target: '/newsListChannel/19',
194
+ },
195
+ {
196
+ title: '统计资料',
197
+ source: ['load.grainoil.com.cn/newsListChannel/20.jspx'],
198
+ target: '/newsListChannel/20',
199
+ },
200
+ {
201
+ title: '综合信息',
202
+ source: ['load.grainoil.com.cn/newsListChannel/21.jspx'],
203
+ target: '/newsListChannel/21',
204
+ },
205
+ ],
206
+ view: ViewType.Articles,
207
+ };
@@ -0,0 +1,9 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: '国家粮油信息中心',
5
+ url: 'load.grainoil.com.cn',
6
+ categories: ['new-media'],
7
+ description: '中国粮食信息网',
8
+ lang: 'zh-CN',
9
+ };
@@ -30,15 +30,17 @@ const cleanUpHTML = (data) => {
30
30
  $('em.vote__bar, div.vote__btn, div.vote__time').remove();
31
31
  $('p img').each((_, e) => {
32
32
  e = $(e);
33
- e.parent().replaceWith(
34
- art(path.join(__dirname, 'templates/description.art'), {
35
- image: {
36
- src: (e.prop('src') ?? e.prop('_src')).split(/\?/)[0],
37
- width: e.prop('data-w'),
38
- height: e.prop('data-h'),
39
- },
40
- })
41
- );
33
+ if ((e.prop('src') ?? e.prop('_src')) !== undefined) {
34
+ e.parent().replaceWith(
35
+ art(path.join(__dirname, 'templates/description.art'), {
36
+ image: {
37
+ src: (e.prop('src') ?? e.prop('_src')).split(/\?/)[0],
38
+ width: e.prop('data-w'),
39
+ height: e.prop('data-h'),
40
+ },
41
+ })
42
+ );
43
+ }
42
44
  });
43
45
  $('p, span').each((_, e) => {
44
46
  e = $(e);
@@ -11,9 +11,14 @@ const PATH_LIST = {
11
11
 
12
12
  export const route: Route = {
13
13
  path: '/category/:name',
14
- categories: ['new-media'],
14
+ categories: ['new-media', 'popular'],
15
15
  example: '/ifanr/category/早报',
16
- parameters: { name: '分类名称' },
16
+ parameters: {
17
+ name: {
18
+ description: '分类名称',
19
+ options: Object.keys(PATH_LIST).map((name) => ({ value: name, label: name })),
20
+ },
21
+ },
17
22
  features: {
18
23
  requireConfig: false,
19
24
  requirePuppeteer: false,
@@ -84,7 +84,7 @@ export const route: Route = {
84
84
  example: '/ifanr/digest',
85
85
  parameters: undefined,
86
86
  description: undefined,
87
- categories: ['new-media'],
87
+ categories: ['new-media', 'popular'],
88
88
  features: {
89
89
  requireConfig: false,
90
90
  requirePuppeteer: false,
@@ -5,7 +5,7 @@ import { parseDate } from '@/utils/parse-date';
5
5
 
6
6
  export const route: Route = {
7
7
  path: '/index',
8
- categories: ['new-media'],
8
+ categories: ['new-media', 'popular'],
9
9
  view: ViewType.Articles,
10
10
  example: '/ifanr/index',
11
11
  parameters: {},