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
@@ -1,159 +1,264 @@
1
- import { Route, ViewType } from '@/types';
1
+ import { type Data, type DataItem, type Route, ViewType } from '@/types';
2
+
2
3
  import cache from '@/utils/cache';
3
- import got from '@/utils/got';
4
- import { load } from 'cheerio';
5
- import timezone from '@/utils/timezone';
4
+ import ofetch from '@/utils/ofetch';
6
5
  import { parseDate } from '@/utils/parse-date';
6
+ import timezone from '@/utils/timezone';
7
7
 
8
- export const route: Route = {
9
- path: '/:id?',
10
- categories: ['finance', 'popular'],
11
- view: ViewType.Articles,
12
- example: '/stcn/yw',
13
- parameters: {
14
- id: {
15
- description: '栏目 id',
16
- options: [
17
- { value: 'kx', label: '快讯' },
18
- { value: 'yw', label: '要闻' },
19
- { value: 'gs', label: '股市' },
20
- { value: 'company', label: '公司' },
21
- { value: 'data', label: '数据' },
22
- { value: 'fund', label: '基金' },
23
- { value: 'finance', label: '金融' },
24
- { value: 'comment', label: '评论' },
25
- { value: 'cj', label: '产经' },
26
- { value: 'ct', label: '创投' },
27
- { value: 'kcb', label: '科创板' },
28
- { value: 'xsb', label: '新三板' },
29
- { value: 'tj', label: '投教' },
30
- { value: 'zk', label: 'ESG' },
31
- { value: 'gd', label: '滚动' },
32
- { value: 'gsyl', label: '股市一览' },
33
- { value: 'djjd', label: '独家解读' },
34
- { value: 'gsxw', label: '公司新闻' },
35
- { value: 'gsdt', label: '公司动态' },
36
- { value: 'djsj', label: '独家数据' },
37
- { value: 'kd', label: '看点数据' },
38
- { value: 'zj', label: '资金流向' },
39
- { value: 'sj_kcb', label: '科创板' },
40
- { value: 'hq', label: '行情总貌' },
41
- { value: 'zl', label: '专栏' },
42
- { value: 'author', label: '作者' },
43
- { value: 'cjhy', label: '行业' },
44
- { value: 'cjqc', label: '汽车' },
45
- { value: 'tjkt', label: '投教课堂' },
46
- { value: 'zczs', label: '政策知识' },
47
- { value: 'tjdt', label: '投教动态' },
48
- { value: 'zthd', label: '专题活动' },
49
- ],
50
- default: 'yw',
51
- },
52
- },
53
- features: {
54
- requireConfig: false,
55
- requirePuppeteer: false,
56
- antiCrawler: false,
57
- supportBT: false,
58
- supportPodcast: false,
59
- supportScihub: false,
60
- },
61
- name: '栏目',
62
- maintainers: ['nczitzk'],
63
- handler,
64
- description: `| 快讯 | 要闻 | 股市 | 公司 | 数据 |
65
- | ---- | ---- | ---- | ------- | ---- |
66
- | kx | yw | gs | company | data |
67
-
68
- | 基金 | 金融 | 评论 | 产经 | 创投 |
69
- | ---- | ------- | ------- | ---- | ---- |
70
- | fund | finance | comment | cj | ct |
71
-
72
- | 科创板 | 新三板 | 投教 | ESG | 滚动 |
73
- | ------ | ------ | ---- | --- | ---- |
74
- | kcb | xsb | tj | zk | gd |
75
-
76
- | 股市一览 | 独家解读 |
77
- | -------- | -------- |
78
- | gsyl | djjd |
8
+ import { type CheerioAPI, type Cheerio, type Element, load } from 'cheerio';
9
+ import { type Context } from 'hono';
79
10
 
80
- | 公司新闻 | 公司动态 |
81
- | -------- | -------- |
82
- | gsxw | gsdt |
11
+ export const handler = async (ctx: Context): Promise<Data> => {
12
+ const { id = 'yw' } = ctx.req.param();
13
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
83
14
 
84
- | 独家数据 | 看点数据 | 资金流向 | 科创板 | 行情总貌 |
85
- | -------- | -------- | -------- | ------- | -------- |
86
- | djsj | kd | zj | sj\_kcb | hq |
15
+ const baseUrl: string = 'https://www.stcn.com';
16
+ const targetUrl: string = new URL(`article/list/${id}.html`, baseUrl).href;
87
17
 
88
- | 专栏 | 作者 |
89
- | ---- | ------ |
90
- | zl | author |
18
+ const response = await ofetch(targetUrl);
19
+ const $: CheerioAPI = load(response);
20
+ const language = $('html').attr('lang') ?? 'zh-CN';
91
21
 
92
- | 行业 | 汽车 |
93
- | ---- | ---- |
94
- | cjhy | cjqc |
22
+ let items: DataItem[] = [];
95
23
 
96
- | 投教课堂 | 政策知识 | 投教动态 | 专题活动 |
97
- | -------- | -------- | -------- | -------- |
98
- | tjkt | zczs | tjdt | zthd |`,
99
- };
24
+ items = $('ul.infinite-list li')
25
+ .slice(0, limit)
26
+ .toArray()
27
+ .map((el): Element => {
28
+ const $el: Cheerio<Element> = $(el);
100
29
 
101
- async function handler(ctx) {
102
- const id = ctx.req.param('id') ?? 'yw';
30
+ const $aEl: Cheerio<Element> = $el.find('div.tt a');
103
31
 
104
- const rootUrl = 'https://www.stcn.com';
105
- const currentUrl = `${rootUrl}/article/list/${id}.html`;
106
- const apiUrl = `${rootUrl}/article/list.html?type=${id}`;
32
+ const title: string = $aEl.text();
33
+ const description: string = $el.find('div.text').html();
34
+ const pubDateStr: string | undefined = $el.find('div.info span').last().text().trim();
35
+ const linkUrl: string | undefined = $aEl.attr('href');
36
+ const categoryEls: Element[] = $el.find('div.tags span').toArray();
37
+ const categories: string[] = [...new Set(categoryEls.map((el) => $(el).text()).filter(Boolean))];
38
+ const authors: DataItem['author'] = $el.find('div.info span').first().text();
39
+ const image: string | undefined = $el.find('div.side a img').attr('src');
40
+ const upDatedStr: string | undefined = pubDateStr;
107
41
 
108
- const response = await got({
109
- method: 'get',
110
- url: apiUrl,
111
- });
42
+ const processedItem: DataItem = {
43
+ title,
44
+ description,
45
+ pubDate: pubDateStr ? timezone(parseDate(pubDateStr, ['HH:mm', 'MM-DD HH:mm', 'YYYY-MM-DD HH:mm']), +8) : undefined,
46
+ link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
47
+ category: categories,
48
+ author: authors,
49
+ content: {
50
+ html: description,
51
+ text: description,
52
+ },
53
+ image,
54
+ banner: image,
55
+ updated: upDatedStr ? timezone(parseDate(upDatedStr, ['HH:mm', 'MM-DD HH:mm', 'YYYY-MM-DD HH:mm']), +8) : undefined,
56
+ language,
57
+ };
112
58
 
113
- const $ = load(response.data);
59
+ return processedItem;
60
+ });
114
61
 
115
- let items = $('.t, .tt, .title')
116
- .find('a')
117
- .toArray()
118
- .map((item) => {
119
- item = $(item);
62
+ items = (
63
+ await Promise.all(
64
+ items.map((item) => {
65
+ if (!item.link) {
66
+ return item;
67
+ }
120
68
 
121
- const link = item.attr('href');
69
+ return cache.tryGet(item.link, async (): Promise<DataItem> => {
70
+ const detailResponse = await ofetch(item.link);
71
+ const $$: CheerioAPI = load(detailResponse);
122
72
 
123
- return {
124
- title: item.text().replaceAll(/(^【|】$)/g, ''),
125
- link: link.startsWith('http') ? link : `${rootUrl}${link}`,
126
- };
127
- });
73
+ const title: string = $$('div.detail-title').text();
74
+ const description: string = $$('div.detail-content').html() ?? '';
75
+ const pubDateStr: string | undefined = $$('div.detail-info span').last().text().trim();
76
+ const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? [];
77
+ const authors: DataItem['author'] = $$('div.detail-info span').first().text().split(/:/).pop();
78
+ const upDatedStr: string | undefined = pubDateStr;
128
79
 
129
- items = await Promise.all(
130
- items.map((item) =>
131
- cache.tryGet(item.link, async () => {
132
- if (/\.html$/.test(item.link)) {
133
- const detailResponse = await got({
134
- method: 'get',
135
- url: item.link,
136
- });
137
-
138
- const content = load(detailResponse.data);
139
-
140
- item.title = content('.detail-title').text();
141
- item.author = content('.detail-info span').first().text().split(':').pop();
142
- item.pubDate = timezone(parseDate(content('.detail-info span').last().text()), +8);
143
- item.category = content('.detail-content-tags div')
144
- .toArray()
145
- .map((t) => content(t).text());
146
- item.description = content('.detail-content').html();
147
- }
80
+ const processedItem: DataItem = {
81
+ title,
82
+ description,
83
+ pubDate: pubDateStr ? timezone(parseDate(pubDateStr), +8) : item.pubDate,
84
+ category: categories,
85
+ author: authors,
86
+ content: {
87
+ html: description,
88
+ text: description,
89
+ },
90
+ updated: upDatedStr ? timezone(parseDate(upDatedStr), +8) : item.updated,
91
+ language,
92
+ };
148
93
 
149
- return item;
94
+ return {
95
+ ...item,
96
+ ...processedItem,
97
+ };
98
+ });
150
99
  })
151
100
  )
152
- );
101
+ ).filter((_): _ is DataItem => true);
153
102
 
154
103
  return {
155
- title: `证券时报网 - ${$('.breadcrumb a').last().text()}`,
156
- link: currentUrl,
104
+ title: $('title').text(),
105
+ description: $('meta[name="description"]').attr('content'),
106
+ link: targetUrl,
157
107
  item: items,
108
+ allowEmpty: true,
109
+ image: $('img.stcn-logo').attr('src'),
110
+ author: $('meta[name="keywords"]').attr('content')?.split(/,/)[0],
111
+ language,
112
+ id: targetUrl,
158
113
  };
159
- }
114
+ };
115
+
116
+ export const route: Route = {
117
+ path: '/article/list/:id?',
118
+ name: '列表',
119
+ url: 'www.stcn.com',
120
+ maintainers: ['nczitzk'],
121
+ handler,
122
+ example: '/stcn/article/list/yw',
123
+ parameters: {
124
+ category: {
125
+ description: '分类,默认为 `yw`,即要闻,可在对应分类页 URL 中找到',
126
+ options: [
127
+ {
128
+ label: '要闻',
129
+ value: 'yw',
130
+ },
131
+ {
132
+ label: '股市',
133
+ value: 'gs',
134
+ },
135
+ {
136
+ label: '公司',
137
+ value: 'company',
138
+ },
139
+ {
140
+ label: '基金',
141
+ value: 'fund',
142
+ },
143
+ {
144
+ label: '金融',
145
+ value: 'finance',
146
+ },
147
+ {
148
+ label: '评论',
149
+ value: 'comment',
150
+ },
151
+ {
152
+ label: '产经',
153
+ value: 'cj',
154
+ },
155
+ {
156
+ label: '科创板',
157
+ value: 'kcb',
158
+ },
159
+ {
160
+ label: '新三板',
161
+ value: 'xsb',
162
+ },
163
+ {
164
+ label: 'ESG',
165
+ value: 'zk',
166
+ },
167
+ {
168
+ label: '滚动',
169
+ value: 'gd',
170
+ },
171
+ ],
172
+ },
173
+ },
174
+ description: `:::tip
175
+ 若订阅 [要闻](https://www.stcn.com/article/list/yw.html),网址为 \`https://www.stcn.com/article/list/yw.html\`,请截取 \`https://www.stcn.com/article/list/\` 到末尾 \`.html\` 的部分 \`yw\` 作为 \`id\` 参数填入,此时目标路由为 [\`/stcn/article/list/yw\`](https://rsshub.app/stcn/article/list/yw)。
176
+
177
+ :::
178
+
179
+ | 要闻 | 股市 | 公司 | 基金 | 金融 | 评论 |
180
+ | ---- | ---- | ------- | ---- | ------- | ------- |
181
+ | yw | gs | company | fund | finance | comment |
182
+
183
+ | 产经 | 科创板 | 新三板 | ESG | 滚动 |
184
+ | ---- | ------ | ------ | --- | ---- |
185
+ | cj | kcb | xsb | zk | gd |
186
+ `,
187
+ categories: ['finance'],
188
+ features: {
189
+ requireConfig: false,
190
+ requirePuppeteer: false,
191
+ antiCrawler: false,
192
+ supportRadar: true,
193
+ supportBT: false,
194
+ supportPodcast: false,
195
+ supportScihub: false,
196
+ },
197
+ radar: [
198
+ {
199
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/:id'],
200
+ target: (params, url) => {
201
+ const urlObj: URL = new URL(url);
202
+ const id: string | undefined = urlObj.searchParams.get('type') ?? params.id;
203
+
204
+ return `/stcn/article/list${id ? `/${id}` : ''}`;
205
+ },
206
+ },
207
+ {
208
+ title: '要闻',
209
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/yw.html'],
210
+ target: '/article/list/yw',
211
+ },
212
+ {
213
+ title: '股市',
214
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/gs.html'],
215
+ target: '/article/list/gs',
216
+ },
217
+ {
218
+ title: '公司',
219
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/company.html'],
220
+ target: '/article/list/company',
221
+ },
222
+ {
223
+ title: '基金',
224
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/fund.html'],
225
+ target: '/article/list/fund',
226
+ },
227
+ {
228
+ title: '金融',
229
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/finance.html'],
230
+ target: '/article/list/finance',
231
+ },
232
+ {
233
+ title: '评论',
234
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/comment.html'],
235
+ target: '/article/list/comment',
236
+ },
237
+ {
238
+ title: '产经',
239
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/cj.html'],
240
+ target: '/article/list/cj',
241
+ },
242
+ {
243
+ title: '科创板',
244
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/kcb.html'],
245
+ target: '/article/list/kcb',
246
+ },
247
+ {
248
+ title: '新三板',
249
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/xsb.html'],
250
+ target: '/article/list/xsb',
251
+ },
252
+ {
253
+ title: 'ESG',
254
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/zk.html'],
255
+ target: '/article/list/zk',
256
+ },
257
+ {
258
+ title: '滚动',
259
+ source: ['www.stcn.com/article/list.html', 'www.stcn.com/article/list/gd.html'],
260
+ target: '/article/list/gd',
261
+ },
262
+ ],
263
+ view: ViewType.Articles,
264
+ };
@@ -0,0 +1,144 @@
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, load } from 'cheerio';
9
+ import { type Context } from 'hono';
10
+
11
+ export const handler = async (ctx: Context): Promise<Data> => {
12
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
13
+
14
+ const baseUrl: string = 'https://www.stcn.com';
15
+ const targetUrl: string = new URL('article/list/kx.html', baseUrl).href;
16
+ const apiUrl: string = new URL('article/list.html', baseUrl).href;
17
+
18
+ const targetResponse = await ofetch(targetUrl);
19
+
20
+ const response = await ofetch(apiUrl, {
21
+ headers: {
22
+ 'x-requested-with': 'XMLHttpRequest',
23
+ },
24
+ query: {
25
+ type: 'kx',
26
+ },
27
+ });
28
+
29
+ const $: CheerioAPI = load(targetResponse);
30
+ const language = $('html').attr('lang') ?? 'zh-CN';
31
+
32
+ let items: DataItem[] = [];
33
+
34
+ items = response.data.slice(0, limit).map((item): DataItem => {
35
+ const title: string = item.title;
36
+ const description: string = item.content;
37
+ const pubDate: number | string = item.time;
38
+ const linkUrl: string | undefined = item.url;
39
+ const categories: string[] = item.tags ? item.tags.map((c) => c.name) : [];
40
+ const authors: DataItem['author'] = item.source;
41
+ const image: string | undefined = item.share?.image;
42
+ const updated: number | string = pubDate;
43
+
44
+ const processedItem: DataItem = {
45
+ title,
46
+ description,
47
+ pubDate: pubDate ? parseDate(pubDate, 'X') : undefined,
48
+ link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
49
+ category: categories,
50
+ author: authors,
51
+ content: {
52
+ html: description,
53
+ text: item.content ?? description,
54
+ },
55
+ image,
56
+ banner: image,
57
+ updated: updated ? parseDate(updated, 'X') : undefined,
58
+ language,
59
+ };
60
+
61
+ return processedItem;
62
+ });
63
+
64
+ items = (
65
+ await Promise.all(
66
+ items.map((item) => {
67
+ if (!item.link) {
68
+ return item;
69
+ }
70
+
71
+ return cache.tryGet(item.link, async (): Promise<DataItem> => {
72
+ const detailResponse = await ofetch(item.link);
73
+ const $$: CheerioAPI = load(detailResponse);
74
+
75
+ const title: string = $$('div.detail-title').text();
76
+ const description: string = $$('div.detail-content').html() ?? '';
77
+ const pubDateStr: string | undefined = $$('div.detail-info span').last().text().trim();
78
+ const categories: string[] = [...new Set([...(item.category as string[]), ...($$('meta[name="keywords"]').attr('content')?.split(/,/) ?? [])])];
79
+ const authors: DataItem['author'] = $$('div.detail-info span').first().text().split(/:/).pop();
80
+ const upDatedStr: string | undefined = pubDateStr;
81
+
82
+ const processedItem: DataItem = {
83
+ title,
84
+ description,
85
+ pubDate: pubDateStr ? timezone(parseDate(pubDateStr), +8) : item.pubDate,
86
+ category: categories,
87
+ author: authors,
88
+ content: {
89
+ html: description,
90
+ text: description,
91
+ },
92
+ updated: upDatedStr ? timezone(parseDate(upDatedStr), +8) : item.updated,
93
+ language,
94
+ };
95
+
96
+ return {
97
+ ...item,
98
+ ...processedItem,
99
+ };
100
+ });
101
+ })
102
+ )
103
+ ).filter((_): _ is DataItem => true);
104
+
105
+ return {
106
+ title: $('title').text(),
107
+ description: $('meta[name="description"]').attr('content'),
108
+ link: targetUrl,
109
+ item: items,
110
+ allowEmpty: true,
111
+ image: $('img.stcn-logo').attr('src'),
112
+ author: $('meta[name="keywords"]').attr('content')?.split(/,/)[0],
113
+ language,
114
+ id: targetUrl,
115
+ };
116
+ };
117
+
118
+ export const route: Route = {
119
+ path: '/article/list/kx',
120
+ name: '快讯',
121
+ url: 'www.stcn.com',
122
+ maintainers: ['nczitzk'],
123
+ handler,
124
+ example: '/stcn/article/list/kx',
125
+ parameters: undefined,
126
+ description: undefined,
127
+ categories: ['finance'],
128
+ features: {
129
+ requireConfig: false,
130
+ requirePuppeteer: false,
131
+ antiCrawler: false,
132
+ supportRadar: true,
133
+ supportBT: false,
134
+ supportPodcast: false,
135
+ supportScihub: false,
136
+ },
137
+ radar: [
138
+ {
139
+ source: ['www.stcn.com/article/list/kx.html'],
140
+ target: '/article/list/kx',
141
+ },
142
+ ],
143
+ view: ViewType.Articles,
144
+ };
@@ -2,6 +2,6 @@ import type { Namespace } from '@/types';
2
2
 
3
3
  export const namespace: Namespace = {
4
4
  name: '西南交通大学',
5
- url: 'ctt.swjtu.edu.cn',
5
+ url: 'www.swjtu.edu.cn',
6
6
  lang: 'zh-CN',
7
7
  };
@@ -5,13 +5,12 @@ import { parseDate } from '@/utils/parse-date';
5
5
  import ofetch from '@/utils/ofetch';
6
6
 
7
7
  const rootURL = 'https://scai.swjtu.edu.cn';
8
- const pageURL = `${rootURL}/web/page-module.html?mid=B730BEB095B31840`;
9
8
 
10
9
  export const route: Route = {
11
- path: '/scai/bks',
10
+ path: '/scai/:type',
12
11
  categories: ['university'],
13
12
  example: '/swjtu/scai/bks',
14
- parameters: {},
13
+ parameters: { type: '通知类型' },
15
14
  features: {
16
15
  requireConfig: false,
17
16
  requirePuppeteer: false,
@@ -26,11 +25,32 @@ export const route: Route = {
26
25
  },
27
26
  ],
28
27
  name: '计算机与人工智能学院',
29
- description: '本科生教育',
28
+ description: `
29
+ | 分区 | 参数 |
30
+ | ----------------- | ----------- |
31
+ | 本科生教育 | bks |
32
+ | 研究生教育 | yjs |
33
+ | 学生工作 | xsgz |
34
+ `,
30
35
  maintainers: ['AzureG03', 'SuperJeason'],
31
36
  handler,
32
37
  };
33
38
 
39
+ const partition = {
40
+ bks: {
41
+ title: '本科生教育',
42
+ url: `${rootURL}/web/page-module.html?mid=B730BEB095B31840`,
43
+ },
44
+ yjs: {
45
+ title: '研究生教育',
46
+ url: `${rootURL}/web/page-module.html?mid=6A69B0E32021446B`,
47
+ },
48
+ xsgz: {
49
+ title: '学生工作',
50
+ url: `${rootURL}/web/page-module.html?mid=F3D3909EB1861B5D`,
51
+ },
52
+ };
53
+
34
54
  const getItem = (item, cache) => {
35
55
  const title = item.find('a').text();
36
56
  const link = `${rootURL}${item.find('a').attr('href').slice(2)}`;
@@ -39,22 +59,14 @@ const getItem = (item, cache) => {
39
59
  const res = await ofetch(link);
40
60
  const $ = load(res);
41
61
 
42
- // 尝试获取日期
43
62
  let dateText = $('div.news-info span:nth-of-type(2)').text();
63
+ // 转教务通知时的时间获取方法
44
64
  if (!dateText) {
45
65
  dateText = $('div.news-top-bar span:nth-of-type(1)').text();
46
66
  }
47
- if (!dateText) {
48
- // console.error('Date not found for', link);
49
- return null; // 或者返回一个默认值
50
- }
51
-
52
- const dateMatch = dateText.match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/);
53
- if (!dateMatch) {
54
- // console.error('Invalid date format for', link);
55
- return null; // 或者返回一个默认值
56
- }
57
- const pubDate = parseDate(dateMatch[0]);
67
+ // 'date' may be undefined. and 'parseDate' will return current time.
68
+ const date = dateText.match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)?.[0];
69
+ const pubDate = parseDate(date);
58
70
  const description = $('div.content-main').html();
59
71
 
60
72
  return {
@@ -66,8 +78,10 @@ const getItem = (item, cache) => {
66
78
  });
67
79
  };
68
80
 
69
- async function handler() {
70
- const res = await ofetch(pageURL);
81
+ async function handler(ctx) {
82
+ const { type } = ctx.req.param();
83
+ const url = partition[type].url;
84
+ const res = await ofetch(url);
71
85
 
72
86
  const $ = load(res);
73
87
  const $list = $('div.list-top-item, div.item-wrapper');
@@ -80,8 +94,8 @@ async function handler() {
80
94
  );
81
95
 
82
96
  return {
83
- title: '西南交大计算机学院-本科生教育',
84
- link: pageURL,
97
+ title: `西南交大计院-${partition[type].title}`,
98
+ link: url,
85
99
  item: items,
86
100
  allowEmpty: true,
87
101
  };