rsshub 1.0.0-master.f72af1b → 1.0.0-master.f7347d9

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 (222) hide show
  1. package/lib/config.ts +18 -0
  2. package/lib/middleware/template.tsx +10 -2
  3. package/lib/routes/163/music/playlist.ts +2 -1
  4. package/lib/routes/5eplay/index.ts +1 -51
  5. package/lib/routes/a9vg/index.ts +214 -0
  6. package/lib/routes/a9vg/namespace.ts +1 -0
  7. package/lib/routes/a9vg/templates/description.art +17 -0
  8. package/lib/routes/aibase/discover.ts +388 -0
  9. package/lib/routes/aibase/namespace.ts +8 -0
  10. package/lib/routes/aibase/news.ts +118 -0
  11. package/lib/routes/aibase/templates/description.art +100 -0
  12. package/lib/routes/aibase/topic.ts +614 -0
  13. package/lib/routes/aibase/util.ts +114 -0
  14. package/lib/routes/apnews/api.ts +18 -3
  15. package/lib/routes/apnews/rss.ts +1 -1
  16. package/lib/routes/apnews/topics.ts +4 -3
  17. package/lib/routes/apple/podcast.ts +19 -15
  18. package/lib/routes/bangumi/templates/tv/subject.art +6 -0
  19. package/lib/routes/bangumi/tv/other/followrank.ts +19 -22
  20. package/lib/routes/bangumi/tv/user/collections.ts +172 -0
  21. package/lib/routes/bilibili/cache.ts +16 -8
  22. package/lib/routes/bilibili/utils.ts +85 -3
  23. package/lib/routes/bilibili/video.ts +7 -9
  24. package/lib/routes/bilibili/vsearch.ts +1 -1
  25. package/lib/routes/bjnews/cat.ts +9 -1
  26. package/lib/routes/cbpanet/index.ts +380 -0
  27. package/lib/routes/cbpanet/namespace.ts +8 -0
  28. package/lib/routes/ceph/blog.ts +72 -0
  29. package/lib/routes/ceph/namespace.ts +7 -0
  30. package/lib/routes/chinanews/index.ts +1 -1
  31. package/lib/routes/chinaventure/index.ts +1 -1
  32. package/lib/routes/cisia/index.ts +264 -0
  33. package/lib/routes/cisia/namespace.ts +8 -0
  34. package/lib/routes/cjlu/namespace.ts +10 -0
  35. package/lib/routes/cjlu/yjsy/index.ts +107 -0
  36. package/lib/routes/cnki/journals.ts +31 -2
  37. package/lib/routes/cohere/index.ts +54 -0
  38. package/lib/routes/cohere/namespace.ts +6 -0
  39. package/lib/routes/coolapk/dyh.ts +7 -1
  40. package/lib/routes/coolapk/hot.ts +7 -1
  41. package/lib/routes/coolapk/huati.ts +7 -1
  42. package/lib/routes/coolapk/namespace.ts +6 -0
  43. package/lib/routes/coolapk/toutiao.ts +7 -1
  44. package/lib/routes/coolapk/tuwen.ts +10 -5
  45. package/lib/routes/coolapk/user-dynamic.ts +7 -1
  46. package/lib/routes/damai/activity.ts +3 -2
  47. package/lib/routes/dcfever/trading.ts +3 -5
  48. package/lib/routes/dcfever/utils.ts +13 -4
  49. package/lib/routes/dealstreetasia/home.ts +72 -0
  50. package/lib/routes/dealstreetasia/namespace.ts +6 -0
  51. package/lib/routes/dealstreetasia/section.ts +57 -0
  52. package/lib/routes/dlsite/campaign.ts +1 -2
  53. package/lib/routes/dlsite/new.ts +1 -2
  54. package/lib/routes/dlsite/{index.ts → z-index/index.ts} +1 -1
  55. package/lib/routes/douban/other/topic.ts +10 -4
  56. package/lib/routes/douyin/types.ts +795 -0
  57. package/lib/routes/douyin/user.ts +51 -23
  58. package/lib/routes/douyin/utils.ts +1 -87
  59. package/lib/routes/dribbble/keyword.ts +1 -1
  60. package/lib/routes/dribbble/popular.ts +1 -1
  61. package/lib/routes/dribbble/user.ts +1 -1
  62. package/lib/routes/dribbble/utils.ts +16 -18
  63. package/lib/routes/famitsu/category.ts +1 -1
  64. package/lib/routes/fanbox/index.ts +1 -1
  65. package/lib/routes/fanbox/types.ts +1 -5
  66. package/lib/routes/fediverse/timeline.ts +21 -3
  67. package/lib/routes/follow/profile.ts +4 -2
  68. package/lib/routes/follow/types.ts +11 -1
  69. package/lib/routes/github/advisor.ts +97 -0
  70. package/lib/routes/github/discussions.ts +194 -0
  71. package/lib/routes/github/issue.ts +1 -1
  72. package/lib/routes/github/pulls.ts +1 -1
  73. package/lib/routes/gmcmonline/chinacustoms.ts +130 -0
  74. package/lib/routes/gmcmonline/namespace.ts +8 -0
  75. package/lib/routes/gov/csrc/csrc.ts +541 -0
  76. package/lib/routes/gov/customs/list.ts +5 -2
  77. package/lib/routes/gov/customs/namespace.ts +7 -0
  78. package/lib/routes/gov/jgjcndrc/index.ts +95 -66
  79. package/lib/routes/gov/moa/moa.ts +3 -3
  80. package/lib/routes/gov/moa/szcpxx.ts +115 -0
  81. package/lib/routes/gov/moa/zdscxx.ts +71 -21
  82. package/lib/routes/gov/ndrc/zfxxgk.ts +106 -0
  83. package/lib/routes/gov/pudong/zwgk.ts +65 -0
  84. package/lib/routes/gov/zj/search.ts +17 -12
  85. package/lib/routes/guancha/member.ts +1 -1
  86. package/lib/routes/hellogithub/index.ts +17 -67
  87. package/lib/routes/hellogithub/report.ts +1 -1
  88. package/lib/routes/hex-rays/index.ts +23 -29
  89. package/lib/routes/hfut/hf/notice.ts +43 -0
  90. package/lib/routes/hfut/hf/utils.ts +80 -0
  91. package/lib/routes/hfut/namespace.ts +6 -0
  92. package/lib/routes/hfut/xc/notice.ts +43 -0
  93. package/lib/routes/hfut/xc/utils.ts +73 -0
  94. package/lib/routes/hko/earthquake.ts +56 -0
  95. package/lib/routes/hko/namespace.ts +8 -0
  96. package/lib/routes/hko/weather.ts +56 -0
  97. package/lib/routes/huggingface/blog-zh.ts +1 -1
  98. package/lib/routes/hust/mse.ts +575 -0
  99. package/lib/routes/i-cable/namespace.ts +6 -0
  100. package/lib/routes/i-cable/news.ts +77 -0
  101. package/lib/routes/i-cable/templates/description.art +8 -0
  102. package/lib/routes/infoq/presentations.ts +203 -0
  103. package/lib/routes/infoq/templates/description.art +41 -0
  104. package/lib/routes/ipsw.dev/index.ts +64 -0
  105. package/lib/routes/ipsw.dev/namespace.ts +7 -0
  106. package/lib/routes/ipsw.dev/templates/description.art +20 -0
  107. package/lib/routes/ixigua/user-video.ts +18 -7
  108. package/lib/routes/javtiful/actress.ts +37 -0
  109. package/lib/routes/javtiful/channel.ts +37 -0
  110. package/lib/routes/javtiful/namespace.ts +6 -0
  111. package/lib/routes/javtiful/templates/description.art +7 -0
  112. package/lib/routes/javtiful/utils.ts +18 -0
  113. package/lib/routes/kisskiss/blog.ts +74 -0
  114. package/lib/routes/kisskiss/namespace.ts +6 -0
  115. package/lib/routes/ktown4u/artist-brandlist.ts +61 -0
  116. package/lib/routes/ktown4u/namespace.ts +6 -0
  117. package/lib/routes/lorientlejour/index.ts +186 -0
  118. package/lib/routes/lorientlejour/namespace.ts +7 -0
  119. package/lib/routes/lorientlejour/templates/description.art +18 -0
  120. package/lib/routes/lovelive-anime/namespace.ts +1 -1
  121. package/lib/routes/lovelive-anime/news.ts +56 -26
  122. package/lib/routes/lovelive-anime/schedules.ts +18 -6
  123. package/lib/routes/lovelive-anime/templates/description.art +1 -1
  124. package/lib/routes/lovelive-anime/templates/scheduleDesc.art +0 -1
  125. package/lib/routes/lovelive-anime/topics.ts +1 -1
  126. package/lib/routes/manhuagui/comic.ts +1 -1
  127. package/lib/routes/mi/crowdfunding.ts +43 -22
  128. package/lib/routes/mi/templates/crowdfunding.art +28 -0
  129. package/lib/routes/mi/types.ts +40 -0
  130. package/lib/routes/mi/utils.ts +80 -0
  131. package/lib/routes/misskey/utils.ts +1 -0
  132. package/lib/routes/misskon/namespace.ts +6 -0
  133. package/lib/routes/misskon/posts.ts +33 -0
  134. package/lib/routes/misskon/tag.ts +38 -0
  135. package/lib/routes/misskon/top.ts +67 -0
  136. package/lib/routes/misskon/utils.ts +41 -0
  137. package/lib/routes/natgeo/dailyphoto.ts +1 -1
  138. package/lib/routes/natgeo/dailyselection.ts +1 -1
  139. package/lib/routes/ncku/namespace.ts +1 -1
  140. package/lib/routes/ncku/phys.ts +95 -0
  141. package/lib/routes/news/namespace.ts +1 -1
  142. package/lib/routes/nikkei/cn/index.ts +12 -1
  143. package/lib/routes/nudt/yjszs.ts +33 -13
  144. package/lib/routes/nytimes/book.ts +11 -11
  145. package/lib/routes/nytimes/daily-briefing-chinese.ts +4 -4
  146. package/lib/routes/nytimes/namespace.ts +1 -1
  147. package/lib/routes/oncc/templates/article.art +1 -1
  148. package/lib/routes/papers/index.ts +7 -1
  149. package/lib/routes/parliament.uk/commonslibrary.ts +55 -0
  150. package/lib/routes/parliament.uk/lordslibrary.ts +55 -0
  151. package/lib/routes/parliament.uk/namespace.ts +6 -0
  152. package/lib/routes/picuki/profile.ts +50 -39
  153. package/lib/routes/pornhub/model.ts +3 -5
  154. package/lib/routes/pornhub/pornstar.ts +3 -5
  155. package/lib/routes/pornhub/users.ts +3 -5
  156. package/lib/routes/resonac/namespace.ts +6 -0
  157. package/lib/routes/resonac/products.ts +85 -0
  158. package/lib/routes/rsshub/transform/html.ts +114 -75
  159. package/lib/routes/rsshub/transform/json.ts +14 -8
  160. package/lib/routes/sciencenet/user.ts +3 -1
  161. package/lib/routes/scmp/utils.ts +2 -2
  162. package/lib/routes/skeb/following-creators.ts +52 -0
  163. package/lib/routes/skeb/following-works.ts +52 -0
  164. package/lib/routes/skeb/friend-works.ts +52 -0
  165. package/lib/routes/skeb/index.ts +131 -0
  166. package/lib/routes/skeb/namespace.ts +6 -0
  167. package/lib/routes/skeb/search.ts +77 -0
  168. package/lib/routes/skeb/templates/creator.art +8 -0
  169. package/lib/routes/skeb/templates/work.art +10 -0
  170. package/lib/routes/skeb/utils.ts +155 -0
  171. package/lib/routes/skeb/works.ts +88 -0
  172. package/lib/routes/skebetter/illust.ts +83 -0
  173. package/lib/routes/skebetter/index.ts +83 -0
  174. package/lib/routes/skebetter/manga.ts +65 -0
  175. package/lib/routes/skebetter/namespace.ts +6 -0
  176. package/lib/routes/skebetter/utils.ts +72 -0
  177. package/lib/routes/spankbang/namespace.ts +6 -0
  178. package/lib/routes/spankbang/new-videos.ts +90 -0
  179. package/lib/routes/spankbang/templates/video.art +7 -0
  180. package/lib/routes/straitstimes/index.ts +135 -0
  181. package/lib/routes/straitstimes/namespace.ts +7 -0
  182. package/lib/routes/straitstimes/templates/description.art +27 -0
  183. package/lib/routes/szftedu/dongtai.ts +62 -0
  184. package/lib/routes/szftedu/gonggao.ts +62 -0
  185. package/lib/routes/szftedu/namespace.ts +6 -0
  186. package/lib/routes/the/index.ts +1 -1
  187. package/lib/routes/tkww/index.ts +83 -0
  188. package/lib/routes/tkww/namespace.ts +6 -0
  189. package/lib/routes/ttv/index.ts +1 -1
  190. package/lib/routes/tvb/news.ts +2 -1
  191. package/lib/routes/twitter/api/mobile-api/login.ts +5 -0
  192. package/lib/routes/twitter/api/web-api/api.ts +16 -10
  193. package/lib/routes/twitter/api/web-api/constants.ts +1 -1
  194. package/lib/routes/twitter/api/web-api/utils.ts +94 -56
  195. package/lib/routes/twitter/list.ts +4 -12
  196. package/lib/routes/twitter/media.ts +13 -4
  197. package/lib/routes/twitter/user.ts +15 -6
  198. package/lib/routes/udn/breaking-news.ts +2 -2
  199. package/lib/routes/uestc/gr.ts +65 -37
  200. package/lib/routes/uestc/jwc.ts +49 -28
  201. package/lib/routes/wechat/ershcimi.ts +4 -2
  202. package/lib/routes/weibo/user.ts +10 -2
  203. package/lib/routes/xaut/index.ts +13 -20
  204. package/lib/routes/xaut/namespace.ts +1 -1
  205. package/lib/routes/xbookcn/blog.ts +66 -0
  206. package/lib/routes/xbookcn/namespace.ts +6 -0
  207. package/lib/routes/xiaoyuzhou/podcast.ts +35 -7
  208. package/lib/routes/xueqiu/cookies.ts +5 -3
  209. package/lib/routes/xueqiu/stock-info.ts +6 -12
  210. package/lib/routes/yande/namespace.ts +1 -1
  211. package/lib/routes/yande/post.ts +6 -6
  212. package/lib/routes/zaobao/util.ts +41 -43
  213. package/lib/routes/zhihu/activities.ts +2 -1
  214. package/lib/routes/zhihu/timeline.ts +6 -1
  215. package/lib/routes/zhihu/utils.ts +1 -1
  216. package/lib/routes/zjut/cs/index.ts +105 -0
  217. package/lib/routes/zjut/jwc/index.ts +117 -0
  218. package/lib/utils/cache/redis.ts +1 -1
  219. package/package.json +35 -36
  220. package/lib/routes/a9vg/a9vg.ts +0 -45
  221. package/lib/routes/gov/ndrc/zfxxgk/articles.ts +0 -73
  222. package/lib/routes-deprecated/hko/weather.js +0 -44
@@ -4,8 +4,11 @@ import { parseDate } from '@/utils/parse-date';
4
4
  import { art } from '@/utils/render';
5
5
  import { config } from '@/config';
6
6
  import { fallback, queryToBoolean } from '@/utils/readable-social';
7
- import { templates, resolveUrl, proxyVideo, getOriginAvatar, universalGet } from './utils';
7
+ import { templates, resolveUrl, proxyVideo, getOriginAvatar } from './utils';
8
8
  import InvalidParameterError from '@/errors/types/invalid-parameter';
9
+ import puppeteer from '@/utils/puppeteer';
10
+ import logger from '@/utils/logger';
11
+ import { PostData } from './types';
9
12
 
10
13
  export const route: Route = {
11
14
  path: '/user/:uid/:routeParams?',
@@ -43,29 +46,56 @@ async function handler(ctx) {
43
46
 
44
47
  const pageUrl = `https://www.douyin.com/user/${uid}`;
45
48
 
46
- const pageData = await cache.tryGet(
49
+ const pageData = (await cache.tryGet(
47
50
  `douyin:user:${uid}`,
48
51
  async () => {
49
- const renderData = await universalGet(pageUrl, 'user');
50
- const dataKey = Object.keys(renderData).find((key) => renderData[key].user && renderData[key].post);
51
- return renderData[dataKey];
52
+ let postData;
53
+ const browser = await puppeteer();
54
+ const page = await browser.newPage();
55
+ await page.setRequestInterception(true);
56
+
57
+ page.on('request', (request) => {
58
+ request.resourceType() === 'document' || request.resourceType() === 'script' || request.resourceType() === 'xhr' ? request.continue() : request.abort();
59
+ });
60
+ page.on('response', async (response) => {
61
+ const request = response.request();
62
+ if (request.url().includes('/web/aweme/post') && !postData) {
63
+ postData = await response.json();
64
+ }
65
+ });
66
+
67
+ logger.http(`Requesting ${pageUrl}`);
68
+ await page.goto(pageUrl, {
69
+ waitUntil: 'networkidle2',
70
+ });
71
+
72
+ browser.close();
73
+
74
+ if (!postData) {
75
+ throw new Error('Empty post data. The request may be filtered by WAF.');
76
+ }
77
+
78
+ return postData;
52
79
  },
53
80
  config.cache.routeExpire,
54
81
  false
55
- );
56
- const userInfo = pageData.user.user;
82
+ )) as PostData;
83
+
84
+ if (!pageData.aweme_list?.length) {
85
+ throw new Error('Empty post data. The request may be filtered by WAF.');
86
+ }
87
+ const userInfo = pageData.aweme_list[0].author;
57
88
  const userNickName = userInfo.nickname;
58
- const userDescription = userInfo.desc;
59
- const userAvatar = getOriginAvatar(userInfo.avatar300Url || userInfo.avatarUrl);
89
+ // const userDescription = userInfo.desc;
90
+ const userAvatar = getOriginAvatar(userInfo.avatar_thumb.url_list.at(-1));
60
91
 
61
- const posts = pageData.post.data;
62
- const items = posts.map((post) => {
92
+ const items = pageData.aweme_list.map((post) => {
63
93
  // parse video
64
- let videoList = post.video && post.video.bitRateList && post.video.bitRateList.map((item) => resolveUrl(item.playApi));
94
+ let videoList = post.video?.bit_rate?.map((item) => resolveUrl(item.play_addr.url_list.at(-1)));
65
95
  if (relay) {
66
96
  videoList = videoList.map((item) => proxyVideo(item, relay));
67
97
  }
68
- let duration = post.video && post.video.duration;
98
+ let duration = post.video?.duration;
69
99
  duration = duration && duration / 1000;
70
100
  let img;
71
101
  // if (!embed) {
@@ -73,30 +103,28 @@ async function handler(ctx) {
73
103
  // }
74
104
  img =
75
105
  img ||
76
- (post.video &&
77
- ((post.video.coverUrlList && post.video.coverUrlList.at(-1)) || // HD
78
- post.video.originCover || // LD
79
- post.video.cover)); // even more LD
106
+ post.video?.cover?.url_list.at(-1) || // HD
107
+ post.video?.origin_cover?.url_list.at(-1); // LD
80
108
  img = img && resolveUrl(img);
81
109
 
82
110
  // render description
83
- const desc = post.desc && post.desc.replaceAll('\n', '<br>');
111
+ const desc = post.desc?.replaceAll('\n', '<br>');
84
112
  let media = art(embed && videoList ? templates.embed : templates.cover, { img, videoList, duration });
85
113
  media = embed && videoList && iframe ? art(templates.iframe, { content: media }) : media; // warp in iframe
86
114
  const description = art(templates.desc, { desc, media });
87
115
 
88
116
  return {
89
- title: post.desc,
117
+ title: post.desc.split('\n')[0],
90
118
  description,
91
- link: `https://www.douyin.com/video/${post.awemeId}`,
92
- pubDate: parseDate(post.createTime * 1000),
93
- category: post.textExtra.map((extra) => extra.hashtagName),
119
+ link: `https://www.douyin.com/video/${post.aweme_id}`,
120
+ pubDate: parseDate(post.create_time * 1000),
121
+ category: post.video_tag.map((t) => t.tag_name),
94
122
  };
95
123
  });
96
124
 
97
125
  return {
98
126
  title: userNickName,
99
- description: userDescription,
127
+ // description: userDescription,
100
128
  image: userAvatar,
101
129
  link: pageUrl,
102
130
  item: items,
@@ -1,13 +1,7 @@
1
1
  import { getCurrentPath } from '@/utils/helpers';
2
2
  const __dirname = getCurrentPath(import.meta.url);
3
3
 
4
- import cache from '@/utils/cache';
5
4
  import path from 'node:path';
6
- import got from '@/utils/got';
7
- import { load } from 'cheerio';
8
- import logger from '@/utils/logger';
9
- import { config } from '@/config';
10
- import puppeteer from '@/utils/puppeteer';
11
5
 
12
6
  const templates = {
13
7
  desc: path.join(__dirname, 'templates/desc.art'),
@@ -51,84 +45,4 @@ const getOriginAvatar = (url) =>
51
45
  .replace(/^(.*\.douyinpic\.com\/).*(\/aweme-avatar\/)([^?]*)(\?.*)?$/, '$1origin$2$3')
52
46
  .replaceAll(/~\w+_\d+x\d+/g, '');
53
47
 
54
- const extractRenderData = (html) => {
55
- if (!html) {
56
- throw new Error('Empty html. The request may be filtered by WAF.');
57
- }
58
-
59
- const $ = load(html);
60
- const renderData = JSON.parse(decodeURIComponent($('script#RENDER_DATA').html()));
61
- if (!renderData) {
62
- const title = $('title').text();
63
- if (title.includes('验证')) {
64
- throw new Error(title);
65
- } else if ($('#captcha_container').length > 0) {
66
- throw new Error('Captcha required.');
67
- }
68
- throw new Error('Failed to get render data.');
69
- } else if (renderData.isSpider) {
70
- throw new Error('The request was considered to be from a spider.');
71
- } else {
72
- return renderData;
73
- }
74
- };
75
-
76
- const gotGet = async (url, ua) => {
77
- const response = await got(url, {
78
- headers: {
79
- 'User-Agent': ua, // magic!
80
- },
81
- });
82
- return extractRenderData(response.data);
83
- };
84
-
85
- const puppeteerGet = async (pageUrl) => {
86
- const browser = await puppeteer();
87
- const page = await browser.newPage();
88
- await page.setRequestInterception(true);
89
- page.on('request', (request) => {
90
- request.resourceType() === 'document' ? request.continue() : request.abort();
91
- });
92
- await page.goto(pageUrl, {
93
- waitUntil: 'domcontentloaded',
94
- });
95
- const html = await page.evaluate(() => document.documentElement.innerHTML);
96
- await browser.close();
97
- return extractRenderData(html);
98
- };
99
-
100
- const universalGet = async (url, route) => {
101
- const cacheKey = `douyin:utils:universalGet:gotFirst:${route}`;
102
- const gotFirst = await cache.tryGet(
103
- cacheKey,
104
- () => '1',
105
- config.cache.contentExpire,
106
- false // no refresh now
107
- );
108
-
109
- const ua = route === 'live' ? 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' : 'curl/7.86.0';
110
-
111
- let data;
112
- if (gotFirst === '1') {
113
- try {
114
- data = await gotGet(url, ua);
115
- cache.set(cacheKey, '1', config.cache.contentExpire); // refresh if success
116
- } catch (error) {
117
- logger.warn('Failed to get data with got, trying puppeteer: ' + error.message);
118
- data = await puppeteerGet(url);
119
- cache.set(cacheKey, '0', config.cache.contentExpire); // only set if success
120
- }
121
- } else {
122
- try {
123
- data = await puppeteerGet(url);
124
- cache.set(cacheKey, '0', config.cache.contentExpire); // refresh if success
125
- } catch (error) {
126
- logger.warn('Failed to get data with puppeteer, trying got: ' + error.message);
127
- data = await gotGet(url, ua);
128
- cache.set(cacheKey, '1', config.cache.contentExpire); // only set if success
129
- }
130
- }
131
- return data;
132
- };
133
-
134
- export { templates, resolveUrl, proxyVideo, getOriginAvatar, universalGet };
48
+ export { templates, resolveUrl, proxyVideo, getOriginAvatar };
@@ -25,5 +25,5 @@ async function handler(ctx) {
25
25
 
26
26
  const title = `Dribbble - keyword ${keyword}`;
27
27
 
28
- return await utils.getData(ctx, url, title);
28
+ return await utils.getData(url, title);
29
29
  }
@@ -32,5 +32,5 @@ async function handler(ctx) {
32
32
 
33
33
  const title = 'Dribbble - Popular Shots';
34
34
 
35
- return await utils.getData(ctx, url, title);
35
+ return await utils.getData(url, title);
36
36
  }
@@ -30,5 +30,5 @@ async function handler(ctx) {
30
30
 
31
31
  const title = `Dribbble - user ${name}`;
32
32
 
33
- return await utils.getData(ctx, url, title);
33
+ return await utils.getData(url, title);
34
34
  }
@@ -2,22 +2,24 @@ import { getCurrentPath } from '@/utils/helpers';
2
2
  const __dirname = getCurrentPath(import.meta.url);
3
3
 
4
4
  import cache from '@/utils/cache';
5
- import got from '@/utils/got';
5
+ import ofetch from '@/utils/ofetch';
6
6
  import { load } from 'cheerio';
7
7
  import { parseDate } from '@/utils/parse-date';
8
8
  import { art } from '@/utils/render';
9
9
  import path from 'node:path';
10
10
 
11
+ const host = 'https://dribbble.com';
12
+
11
13
  // Refactored function to load a link asynchronously
12
14
  async function loadContent(link) {
13
15
  // Make a GET request to the specified and retrieve the response
14
- const response = await got(link);
15
- const $ = load(response.data);
16
+ const response = await ofetch(link);
17
+ const $ = load(response);
16
18
 
17
19
  const shotData = JSON.parse(
18
20
  $('script')
19
21
  .text()
20
- .match(/shotData:\s({.+?}),\n/)[1]
22
+ .match(/shotData:\s({.+?}),\n/)?.[1] ?? '{}'
21
23
  );
22
24
 
23
25
  // Join multiple shots together by selecting elements with class 'media-shot' or 'main-shot' or 'block-media-wrapper'
@@ -91,9 +93,7 @@ async function loadContent(link) {
91
93
 
92
94
  // Refactored code with comments for clarity
93
95
 
94
- function ProcessFeed(list, caches) {
95
- const host = 'https://dribbble.com';
96
-
96
+ function ProcessFeed(list) {
97
97
  // Use Promise.all to process all items in the list asynchronously
98
98
  return Promise.all(
99
99
  list.map((item) => {
@@ -101,18 +101,20 @@ function ProcessFeed(list, caches) {
101
101
 
102
102
  // The link of item is "/signup/new" when access "https://dribbble.com/search/something"
103
103
  // So get url by id
104
- const itemId = $(item).attr('id').replace('screenshot-', '');
104
+ const itemId = $(item).data('thumbnail-id');
105
105
 
106
106
  // Construct the full item URL using the host and the item ID
107
- const itemUrl = new URL(`/shots/${itemId}`, host).href;
107
+ const guid = new URL(`/shots/${itemId}`, host).href;
108
+ const itemUrl = new URL($(item).find('.shot-thumbnail-link').attr('href')!, host).href;
108
109
 
109
110
  // Return a Promise that resolves to an object combining the single item data and the additional data
110
- return caches.tryGet(itemUrl, async () => {
111
+ return cache.tryGet(guid, async () => {
111
112
  const { description, pubDate, author, category } = await loadContent(itemUrl);
112
113
 
113
114
  return {
114
115
  title: $('.shot-title').text(),
115
116
  link: itemUrl,
117
+ guid,
116
118
  description,
117
119
  pubDate,
118
120
  author,
@@ -126,25 +128,21 @@ function ProcessFeed(list, caches) {
126
128
  /**
127
129
  * Retrieves data from a given URL and processes it.
128
130
  *
129
- * @param {Object} ctx - The context object.
130
131
  * @param {string} url - The URL to retrieve data from.
131
132
  * @param {string} title - The title of the data.
132
133
  * @return {Object} - An object containing the retrieved data and metadata.
133
134
  */
134
- const getData = async (ctx, url, title) => {
135
+ const getData = async (url, title) => {
135
136
  // Make a GET request to the specified URL
136
- const response = await got(url);
137
-
138
- // Extract the response data
139
- const data = response.data;
137
+ const response = await ofetch(url);
140
138
 
141
139
  // Load the response data into a cheerio object
142
- const $ = load(data);
140
+ const $ = load(response);
143
141
  // Get all the list items under the 'ol.dribbbles.group' element
144
142
  const list = $('ol.dribbbles.group > li').toArray();
145
143
 
146
144
  // Process the list items using the ProcessFeed function
147
- const result = await ProcessFeed(list, cache);
145
+ const result = await ProcessFeed(list);
148
146
 
149
147
  // Return an object containing the retrieved data and metadata
150
148
  return {
@@ -106,7 +106,7 @@ async function handler(ctx) {
106
106
  },
107
107
  });
108
108
 
109
- const list = (data.pageProps.categoryArticleData as CategoryArticle[])
109
+ const list = (data.pageProps.categoryArticleDataForPc as CategoryArticle[])
110
110
  .filter((item) => !item.advertiserName)
111
111
  .map((item) => {
112
112
  const publicationDate = item.publishedAt?.slice(0, 7).replace('-', '');
@@ -50,7 +50,7 @@ async function handler(ctx: Context): Promise<Data> {
50
50
  }
51
51
 
52
52
  const postListResponse = (await ofetch(`https://api.fanbox.cc/post.listCreator?creatorId=${creator}&limit=20`, { headers: getHeaders() })) as PostListResponse;
53
- const items = await Promise.all(postListResponse.body.items.map((i) => parseItem(i)));
53
+ const items = await Promise.all(postListResponse.body.map((i) => parseItem(i)));
54
54
 
55
55
  return {
56
56
  title,
@@ -25,10 +25,7 @@ export interface UserInfoResponse {
25
25
  }
26
26
 
27
27
  export interface PostListResponse {
28
- body: {
29
- items: PostItem[];
30
- nextUrl: string | null;
31
- };
28
+ body: PostItem[];
32
29
  }
33
30
 
34
31
  export interface PostDetailResponse {
@@ -88,7 +85,6 @@ interface BasicPost {
88
85
  userId: string;
89
86
  };
90
87
  }[];
91
- nextUrl: string | null;
92
88
  };
93
89
  coverImageUrl: string | null;
94
90
  creatorId: string;
@@ -5,6 +5,7 @@ import { parseDate } from '@/utils/parse-date';
5
5
  import ofetch from '@/utils/ofetch';
6
6
  import { config } from '@/config';
7
7
  import ConfigNotFoundError from '@/errors/types/config-not-found';
8
+ import parser from '@/utils/rss-parser';
8
9
 
9
10
  export const route: Route = {
10
11
  path: '/timeline/:account',
@@ -21,7 +22,7 @@ export const route: Route = {
21
22
  supportScihub: false,
22
23
  },
23
24
  name: 'Timeline',
24
- maintainers: ['DIYgod'],
25
+ maintainers: ['DIYgod', 'pseudoyu'],
25
26
  handler,
26
27
  };
27
28
 
@@ -51,12 +52,29 @@ async function handler(ctx) {
51
52
  Accept: 'application/jrd+json',
52
53
  },
53
54
  });
54
-
55
55
  const jsonLink = acc.links.find((link) => link.rel === 'self' && activityPubTypes.has(link.type))?.href;
56
56
  const link = acc.links.find((link) => link.rel === 'http://webfinger.net/rel/profile-page')?.href;
57
+ const officialFeed = await parser.parseURL(`${link}.rss`);
58
+
59
+ if (officialFeed) {
60
+ return {
61
+ title: `${officialFeed.title} (Fediverse@${account})`,
62
+ description: officialFeed.description,
63
+ image: officialFeed.image?.url,
64
+ link: officialFeed.link,
65
+ item: officialFeed.items.map((item) => ({
66
+ title: item.title,
67
+ description: item.content,
68
+ link: item.link,
69
+ pubDate: item.pubDate ? parseDate(item.pubDate) : null,
70
+ guid: item.guid,
71
+ })),
72
+ };
73
+ }
57
74
 
58
75
  const self = await ofetch(jsonLink, requestOptions);
59
76
 
77
+ // If RSS feed is not available, fallback to original method
60
78
  const outbox = await ofetch(self.outbox, requestOptions);
61
79
  const firstOutbox = await ofetch(outbox.first, requestOptions);
62
80
 
@@ -88,7 +106,7 @@ async function handler(ctx) {
88
106
  image: self.icon?.url || self.image?.url,
89
107
  link,
90
108
  item: resolvedItems.map((item) => ({
91
- title: item.object.content,
109
+ title: item.object.content.replaceAll(/<[^<]*>/g, ''),
92
110
  description: `${item.object.content}\n${item.object.attachment?.map((attachment) => `<img src="${attachment.url}" width="${attachment.width}" height="${attachment.height}" />`).join('\n') || ''}`,
93
111
  link: item.object.url,
94
112
  pubDate: parseDate(item.published),
@@ -1,7 +1,7 @@
1
1
  import { ViewType, type Data, type Route } from '@/types';
2
2
  import type { Context } from 'hono';
3
3
  import ofetch from '@/utils/ofetch';
4
- import type { FollowResponse, ListSubscription, Profile, Subscription } from './types';
4
+ import type { FollowResponse, InboxSubscription, ListSubscription, Profile, Subscription } from './types';
5
5
  import { parse } from 'tldts';
6
6
 
7
7
  export const route: Route = {
@@ -28,6 +28,8 @@ export const route: Route = {
28
28
 
29
29
  const isList = (subscription: Subscription): subscription is ListSubscription => 'lists' in subscription;
30
30
 
31
+ const isInbox = (subscription: Subscription): subscription is InboxSubscription => 'inboxId' in subscription;
32
+
31
33
  async function handler(ctx: Context): Promise<Data> {
32
34
  const uid = ctx.req.param('uid');
33
35
  const host = 'https://api.follow.is';
@@ -37,7 +39,7 @@ async function handler(ctx: Context): Promise<Data> {
37
39
 
38
40
  return {
39
41
  title: `${profile.data.name}'s subscriptions`,
40
- item: subscriptions.data.map((subscription) => {
42
+ item: (<Exclude<Subscription, InboxSubscription>[]>subscriptions.data.filter((i) => !isInbox(i))).map((subscription) => {
41
43
  if (isList(subscription)) {
42
44
  return {
43
45
  title: subscription.lists.title,
@@ -3,7 +3,7 @@ export interface FollowResponse<T> {
3
3
  data: T;
4
4
  }
5
5
 
6
- export type Subscription = FeedSubscription | ListSubscription;
6
+ export type Subscription = FeedSubscription | ListSubscription | InboxSubscription;
7
7
 
8
8
  export interface Profile {
9
9
  id: string;
@@ -66,3 +66,13 @@ export interface ListSubscription extends BaseSubscription {
66
66
  view: number;
67
67
  };
68
68
  }
69
+
70
+ export interface InboxSubscription extends BaseSubscription {
71
+ inboxes: {
72
+ type: 'inbox';
73
+ id: string;
74
+ secret: string;
75
+ title: string;
76
+ };
77
+ inboxId: string;
78
+ }
@@ -0,0 +1,97 @@
1
+ import { Route } from '@/types';
2
+ import got from '@/utils/got';
3
+ import { load } from 'cheerio';
4
+ import { parseDate } from '@/utils/parse-date';
5
+ import ofetch from '@/utils/ofetch';
6
+ import cache from '@/utils/cache';
7
+
8
+ export const route: Route = {
9
+ path: '/advisor/data/:type?/:category?',
10
+ categories: ['programming'],
11
+ example: '/github/advisor/data/reviewed/composer',
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: ['github.com/advisories', 'github.com'],
23
+ },
24
+ ],
25
+ name: 'Github Advisory Database RSS',
26
+ maintainers: ['sd0ric4'],
27
+ handler,
28
+ description: `
29
+ | Type | Description | Explanation |
30
+ | --- | --- | --- |
31
+ | reviewed | Reviewed | 已审核 |
32
+ | unreviewed | Unreviewed | 未审核 |
33
+
34
+ | Category | Description | Explanation |
35
+ | --- | --- | --- |
36
+ | composer | Composer | PHP 依赖管理工具 |
37
+ | go | Go | Go 语言包管理工具 |
38
+ | maven | Maven | Java 项目管理工具 |
39
+ | npm | NPM | Node.js 包管理工具 |
40
+ | nuget | NuGet | .NET 包管理工具 |
41
+ | pip | Pip | Python 包管理工具 |
42
+ | pub | Pub | Dart 包管理工具 |
43
+ | rubygems | RubyGems | Ruby 包管理工具 |
44
+ | rust | Rust | Rust 包管理工具 |
45
+ | erlang | Erlang | Erlang 包管理工具 |
46
+ | actions | Actions | GitHub Actions |
47
+ | swift | Swift | Swift 包管理工具 |`,
48
+ };
49
+
50
+ async function handler(ctx) {
51
+ const { category, type } = ctx.req.param();
52
+
53
+ const apiRootUrl = 'https://github.com/advisories';
54
+ const apiUrl = `${apiRootUrl}?query=type%3A${type}+ecosystem%3A${category}`;
55
+ const currentUrl = `https://github.com/advisories`;
56
+
57
+ const response = await got({
58
+ method: 'get',
59
+ url: apiUrl,
60
+ });
61
+ const $ = load(response.data);
62
+
63
+ const list = $('div.Box-row')
64
+ .toArray()
65
+ .map((item) => {
66
+ item = $(item);
67
+ const a = item.find('a.Link--primary');
68
+ const b = item.find('relative-time').attr('datetime');
69
+ const title = a.text() || 'No title';
70
+ const link = a.attr('href') || '#';
71
+ const pubDate = parseDate(b || '');
72
+
73
+ return {
74
+ title,
75
+ link: `https://github.com${link}`,
76
+ pubDate,
77
+ description: '',
78
+ };
79
+ });
80
+ const items = await Promise.all(
81
+ list.map((item) =>
82
+ cache.tryGet(item.link, async () => {
83
+ const response = await ofetch(item.link);
84
+ const $ = load(response);
85
+
86
+ item.description = $('.comment-body').first().html() || '';
87
+
88
+ return item;
89
+ })
90
+ )
91
+ );
92
+ return {
93
+ title: `GitHub Advisory Database RSS - ${category} - ${type}`,
94
+ link: currentUrl,
95
+ item: items,
96
+ };
97
+ }