rsshub 1.0.0-master.f9ec483 → 1.0.0-master.fa1dcc5

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 (334) hide show
  1. package/lib/config.ts +42 -1
  2. package/lib/middleware/access-control.ts +3 -3
  3. package/lib/middleware/cache.ts +5 -3
  4. package/lib/middleware/template.test.ts +6 -0
  5. package/lib/middleware/template.tsx +3 -1
  6. package/lib/router.js +0 -6
  7. package/lib/routes/141jav/index.ts +1 -1
  8. package/lib/routes/141ppv/index.ts +1 -1
  9. package/lib/routes/36kr/hot-list.ts +1 -1
  10. package/lib/routes/36kr/index.ts +1 -1
  11. package/lib/routes/78dm/index.ts +429 -43
  12. package/lib/routes/78dm/namespace.ts +2 -0
  13. package/lib/routes/78dm/templates/description.art +17 -0
  14. package/lib/routes/abc/index.ts +10 -9
  15. package/lib/routes/aeon/category.ts +1 -1
  16. package/lib/routes/aeon/type.ts +1 -1
  17. package/lib/routes/afr/latest.ts +69 -0
  18. package/lib/routes/afr/namespace.ts +7 -0
  19. package/lib/routes/afr/navigation.ts +75 -0
  20. package/lib/routes/afr/query.ts +349 -0
  21. package/lib/routes/afr/utils.ts +80 -0
  22. package/lib/routes/aibase/discover.ts +1 -1
  23. package/lib/routes/aibase/news.ts +1 -1
  24. package/lib/routes/aibase/topic.ts +1 -1
  25. package/lib/routes/air-level/index.ts +49 -0
  26. package/lib/routes/air-level/levelrank.ts +65 -0
  27. package/lib/routes/air-level/namespace.ts +12 -0
  28. package/lib/routes/ajcass/namespace.ts +11 -0
  29. package/lib/routes/ajcass/shxyj.ts +73 -0
  30. package/lib/routes/alpinelinux/pkgs.ts +2 -2
  31. package/lib/routes/anime1/anime.ts +1 -1
  32. package/lib/routes/apnews/utils.ts +6 -2
  33. package/lib/routes/appleinsider/index.ts +1 -1
  34. package/lib/routes/aqara/region.ts +1 -1
  35. package/lib/routes/asus/bios.ts +0 -1
  36. package/lib/routes/baoyu/index.ts +57 -0
  37. package/lib/routes/baoyu/namespace.ts +8 -0
  38. package/lib/routes/bilibili/article.ts +37 -14
  39. package/lib/routes/bilibili/dynamic.ts +15 -7
  40. package/lib/routes/bilibili/followings-dynamic.ts +2 -2
  41. package/lib/routes/bilibili/manga-update.ts +3 -0
  42. package/lib/routes/bilibili/ranking.ts +188 -117
  43. package/lib/routes/bilibili/reply.ts +2 -0
  44. package/lib/routes/bilibili/templates/description.art +2 -0
  45. package/lib/routes/bing/daily-wallpaper.ts +5 -1
  46. package/lib/routes/bsky/posts.ts +0 -1
  47. package/lib/routes/bsky/templates/post.art +8 -1
  48. package/lib/routes/buaa/lib/space/newbook.ts +1 -1
  49. package/lib/routes/c114/namespace.ts +2 -0
  50. package/lib/routes/c114/roll.ts +71 -42
  51. package/lib/routes/cags/edu/index.ts +84 -0
  52. package/lib/routes/cags/namespace.ts +9 -0
  53. package/lib/routes/caixin/article.ts +1 -1
  54. package/lib/routes/caixin/blog.ts +2 -3
  55. package/lib/routes/caixin/category.ts +1 -1
  56. package/lib/routes/caixin/latest.ts +12 -16
  57. package/lib/routes/caixin/namespace.ts +1 -1
  58. package/lib/routes/caixin/utils-fulltext.ts +51 -0
  59. package/lib/routes/caixin/utils.ts +37 -37
  60. package/lib/routes/cara/likes.ts +1 -1
  61. package/lib/routes/cara/portfolio.ts +1 -1
  62. package/lib/routes/cara/timeline.ts +1 -1
  63. package/lib/routes/ccfa/index.ts +11 -11
  64. package/lib/routes/chub/characters.ts +1 -1
  65. package/lib/routes/cnbeta/namespace.ts +1 -1
  66. package/lib/routes/cngold/index.ts +195 -0
  67. package/lib/routes/cngold/namespace.ts +9 -0
  68. package/lib/routes/cnki/author.ts +120 -48
  69. package/lib/routes/cnki/utils.ts +4 -4
  70. package/lib/routes/coomer/artist.ts +1 -1
  71. package/lib/routes/coomer/namespace.ts +1 -1
  72. package/lib/routes/coomer/posts.ts +2 -2
  73. package/lib/routes/coomer/utils.ts +1 -1
  74. package/lib/routes/cpcaauto/index.ts +22 -22
  75. package/lib/routes/cs/zzkx.ts +1 -1
  76. package/lib/routes/csdn/blog.ts +17 -13
  77. package/lib/routes/cybersecurityventures/namespace.ts +7 -0
  78. package/lib/routes/cybersecurityventures/news.ts +121 -0
  79. package/lib/routes/cybersecurityventures/types.ts +17 -0
  80. package/lib/routes/daily/discussed.ts +22 -21
  81. package/lib/routes/daily/index.ts +14 -11
  82. package/lib/routes/daily/namespace.ts +1 -1
  83. package/lib/routes/daily/source.ts +186 -0
  84. package/lib/routes/daily/upvoted.ts +17 -17
  85. package/lib/routes/daily/user.ts +6 -23
  86. package/lib/routes/daily/utils.ts +27 -32
  87. package/lib/routes/dataguidance/index.ts +19 -30
  88. package/lib/routes/dcfever/news.ts +1 -1
  89. package/lib/routes/dcfever/reviews.ts +1 -1
  90. package/lib/routes/dcfever/trading-search.ts +1 -1
  91. package/lib/routes/dcfever/trading.ts +1 -1
  92. package/lib/routes/deeplearning/namespace.ts +3 -1
  93. package/lib/routes/deeplearning/templates/description.art +21 -0
  94. package/lib/routes/deeplearning/the-batch.ts +296 -0
  95. package/lib/routes/deepmind/blog.ts +1 -1
  96. package/lib/routes/digitalcameraworld/news.ts +1 -1
  97. package/lib/routes/discord/discord-api.ts +44 -2
  98. package/lib/routes/discord/search.ts +106 -0
  99. package/lib/routes/dn/news.ts +1 -1
  100. package/lib/routes/dockerhub/repositories.ts +42 -0
  101. package/lib/routes/dongqiudi/daily.ts +1 -1
  102. package/lib/routes/dw/namespace.ts +6 -0
  103. package/lib/routes/dw/news.ts +89 -0
  104. package/lib/routes/dw/rss.ts +62 -0
  105. package/lib/routes/dw/templates/description.art +23 -0
  106. package/lib/routes/dw/templates/liveblog.art +13 -0
  107. package/lib/routes/dw/templates/video.art +14 -0
  108. package/lib/routes/dw/utils.ts +172 -0
  109. package/lib/routes/ecnu/jwc.ts +5 -1
  110. package/lib/routes/espn/news.ts +1 -1
  111. package/lib/routes/fashionnetwork/namespace.ts +1 -1
  112. package/lib/routes/flyert/forum.ts +99 -0
  113. package/lib/routes/flyert/namespace.ts +1 -0
  114. package/lib/routes/flyert/templates/description.art +21 -0
  115. package/lib/routes/flyert/util.ts +188 -0
  116. package/lib/routes/follow/profile.ts +2 -2
  117. package/lib/routes/foresightnews/article.ts +1 -1
  118. package/lib/routes/foresightnews/column.ts +1 -1
  119. package/lib/routes/foresightnews/index.ts +3 -1
  120. package/lib/routes/foresightnews/news.ts +1 -1
  121. package/lib/routes/freecomputerbooks/index.ts +0 -10
  122. package/lib/routes/ftm/index.ts +1 -1
  123. package/lib/routes/furaffinity/art.ts +85 -0
  124. package/lib/routes/furaffinity/browse.ts +58 -0
  125. package/lib/routes/furaffinity/commissions.ts +55 -0
  126. package/lib/routes/furaffinity/home.ts +80 -0
  127. package/lib/routes/furaffinity/journal-comments.ts +64 -0
  128. package/lib/routes/furaffinity/journals.ts +56 -0
  129. package/lib/routes/furaffinity/namespace.ts +7 -0
  130. package/lib/routes/furaffinity/search.ts +72 -0
  131. package/lib/routes/furaffinity/shouts.ts +56 -0
  132. package/lib/routes/furaffinity/status.ts +58 -0
  133. package/lib/routes/furaffinity/submission-comments.ts +64 -0
  134. package/lib/{routes-deprecated/furaffinity/user.js → routes/furaffinity/user.ts} +53 -41
  135. package/lib/routes/furaffinity/watchers.ts +62 -0
  136. package/lib/routes/furaffinity/watching.ts +62 -0
  137. package/lib/routes/google/developers.ts +71 -0
  138. package/lib/routes/google/news.ts +26 -6
  139. package/lib/routes/gov/cbirc/index.ts +35 -35
  140. package/lib/routes/hamel/index.ts +81 -0
  141. package/lib/routes/hamel/namespace.ts +7 -0
  142. package/lib/routes/hellogithub/volume.ts +3 -0
  143. package/lib/routes/hket/index.ts +13 -1
  144. package/lib/routes/hostmonit/cloudflareyesv6.ts +1 -1
  145. package/lib/routes/hrbeu/sec/list.ts +80 -0
  146. package/lib/routes/hrbust/jwzx.ts +6 -0
  147. package/lib/routes/hunau/gfxy/index.ts +1 -1
  148. package/lib/routes/hunau/ied.ts +1 -1
  149. package/lib/routes/hunau/jwc.ts +1 -1
  150. package/lib/routes/hunau/xky/index.ts +1 -1
  151. package/lib/routes/hust/gs.ts +4 -3
  152. package/lib/routes/idolmaster/namespace.ts +7 -0
  153. package/lib/routes/idolmaster/news.ts +112 -0
  154. package/lib/routes/ieee/author.ts +95 -0
  155. package/lib/routes/ikea/cn/low-price.ts +2 -1
  156. package/lib/routes/imdb/chart.ts +1 -1
  157. package/lib/routes/inspirehep/author.ts +9 -1
  158. package/lib/routes/isct/namespace.ts +11 -0
  159. package/lib/routes/isct/news.ts +66 -0
  160. package/lib/routes/javtrailers/casts.ts +41 -0
  161. package/lib/routes/javtrailers/categories.ts +40 -0
  162. package/lib/routes/javtrailers/namespace.ts +7 -0
  163. package/lib/routes/javtrailers/studios.ts +39 -0
  164. package/lib/routes/javtrailers/templates/description.art +31 -0
  165. package/lib/routes/javtrailers/types.ts +59 -0
  166. package/lib/routes/javtrailers/utils.ts +48 -0
  167. package/lib/routes/jiemian/list.ts +1 -1
  168. package/lib/routes/jiuyangongshe/community.ts +1 -1
  169. package/lib/routes/jlu/phy/index.ts +55 -0
  170. package/lib/routes/joins/chinese.ts +13 -13
  171. package/lib/routes/kcna/news.ts +7 -5
  172. package/lib/routes/keep/user.ts +1 -1
  173. package/lib/routes/langchain/index.ts +74 -0
  174. package/lib/routes/langchain/namespace.ts +7 -0
  175. package/lib/routes/last-origin/namespace.ts +7 -0
  176. package/lib/routes/last-origin/news.ts +65 -0
  177. package/lib/routes/linkresearcher/index.ts +108 -46
  178. package/lib/routes/linkresearcher/namespace.ts +8 -2
  179. package/lib/routes/linkresearcher/templates/bilingual.art +7 -0
  180. package/lib/routes/linkresearcher/types.ts +103 -0
  181. package/lib/routes/liulinblog/itnews.ts +1 -1
  182. package/lib/routes/lofter/collection.ts +1 -1
  183. package/lib/routes/lofter/tag.ts +1 -1
  184. package/lib/routes/logrocket/index.ts +71 -0
  185. package/lib/routes/logrocket/namespace.ts +7 -0
  186. package/lib/routes/m-78/namespace.ts +7 -0
  187. package/lib/routes/m-78/news.ts +115 -0
  188. package/lib/routes/m-78/types.ts +13 -0
  189. package/lib/routes/mastodon/account-id.ts +1 -1
  190. package/lib/routes/mastodon/acct.ts +1 -1
  191. package/lib/routes/mastodon/timeline-local.ts +2 -2
  192. package/lib/routes/mastodon/timeline-remote.ts +2 -2
  193. package/lib/routes/mastodon/utils.ts +1 -1
  194. package/lib/routes/metacritic/index.ts +1 -1
  195. package/lib/routes/mi/utils.ts +1 -1
  196. package/lib/routes/mihoyo/bbs/official.ts +2 -1
  197. package/lib/routes/misskey/utils.ts +19 -1
  198. package/lib/routes/mittrchina/index.ts +2 -2
  199. package/lib/routes/natgeo/natgeo.ts +25 -24
  200. package/lib/routes/nbd/daily.ts +1 -1
  201. package/lib/routes/nielsberglund/index.ts +78 -0
  202. package/lib/routes/nielsberglund/namespace.ts +7 -0
  203. package/lib/routes/nytimes/rss.ts +57 -0
  204. package/lib/routes/openrice/chart.ts +68 -0
  205. package/lib/routes/openrice/templates/chart.art +9 -0
  206. package/lib/routes/openrice/voting.ts +84 -0
  207. package/lib/routes/oschina/news.ts +26 -7
  208. package/lib/routes/patreon/feed.ts +126 -0
  209. package/lib/routes/patreon/namespace.ts +7 -0
  210. package/lib/routes/patreon/templates/description.art +39 -0
  211. package/lib/routes/patreon/types.ts +387 -0
  212. package/lib/routes/pixiv/api/get-illust-detail.ts +22 -0
  213. package/lib/routes/pixiv/novel-api/content/nsfw.ts +73 -0
  214. package/lib/routes/pixiv/novel-api/content/sfw.ts +63 -0
  215. package/lib/routes/pixiv/novel-api/content/types.ts +254 -0
  216. package/lib/routes/pixiv/novel-api/content/utils.ts +133 -0
  217. package/lib/routes/pixiv/novel-api/series/nsfw.ts +84 -0
  218. package/lib/routes/pixiv/novel-api/series/sfw.ts +70 -0
  219. package/lib/routes/pixiv/novel-api/series/types.ts +94 -0
  220. package/lib/routes/pixiv/novel-api/user-novels/nsfw.ts +82 -0
  221. package/lib/routes/pixiv/novel-api/user-novels/sfw.ts +74 -0
  222. package/lib/routes/pixiv/novel-api/user-novels/types.ts +133 -0
  223. package/lib/routes/pixiv/novel-series.ts +52 -0
  224. package/lib/routes/pixiv/novels.ts +69 -44
  225. package/lib/routes/pixiv/utils.ts +3 -0
  226. package/lib/routes/plurk/anonymous.ts +1 -1
  227. package/lib/routes/plurk/hotlinks.ts +1 -1
  228. package/lib/routes/plurk/news.ts +1 -1
  229. package/lib/routes/plurk/search.ts +1 -1
  230. package/lib/routes/qstheory/index.ts +121 -0
  231. package/lib/routes/qstheory/magazine.ts +64 -0
  232. package/lib/routes/qstheory/namespace.ts +7 -0
  233. package/lib/routes/qstheory/utils.ts +25 -0
  234. package/lib/routes/ruc/ai.ts +1 -1
  235. package/lib/routes/scmp/coronavirus.ts +1 -1
  236. package/lib/routes/scu/jwc/tzgg.ts +75 -0
  237. package/lib/routes/scu/namespace.ts +7 -0
  238. package/lib/routes/scu/scupi/_utils.ts +62 -0
  239. package/lib/routes/scu/scupi/notice.ts +41 -0
  240. package/lib/routes/solidot/namespace.ts +1 -1
  241. package/lib/routes/stbu/jsjxy.ts +4 -1
  242. package/lib/routes/steam/search.ts +7 -0
  243. package/lib/routes/sycl/feeds.ts +1 -1
  244. package/lib/routes/syosetu/dev.ts +67 -0
  245. package/lib/routes/syosetu/index.ts +85 -0
  246. package/lib/routes/syosetu/namespace.ts +2 -2
  247. package/lib/routes/syosetu/ranking-isekai.ts +93 -0
  248. package/lib/routes/syosetu/ranking-r18.ts +192 -0
  249. package/lib/routes/syosetu/ranking.ts +285 -0
  250. package/lib/routes/syosetu/search.ts +161 -0
  251. package/lib/routes/syosetu/templates/description.art +73 -0
  252. package/lib/routes/syosetu/types/ranking-r18.ts +60 -0
  253. package/lib/routes/syosetu/types/ranking.ts +67 -0
  254. package/lib/routes/syosetu/types/search.ts +138 -0
  255. package/lib/routes/syosetu/utils.ts +50 -0
  256. package/lib/routes/telegram/channel.ts +31 -1
  257. package/lib/routes/telegram/scripts/get-telegram-session.mjs +16 -6
  258. package/lib/routes/telegram/stickerpack.ts +1 -1
  259. package/lib/routes/telegram/tglib/channel.ts +12 -9
  260. package/lib/routes/telegram/tglib/client.ts +13 -9
  261. package/lib/routes/test/index.ts +4 -0
  262. package/lib/routes/theblockbeats/index.ts +1 -1
  263. package/lib/routes/thegradient/index.ts +80 -0
  264. package/lib/routes/thegradient/namespace.ts +7 -0
  265. package/lib/routes/theinitium/app.ts +4 -0
  266. package/lib/routes/thepaper/channel.ts +1 -1
  267. package/lib/routes/thepaper/factpaper.ts +1 -1
  268. package/lib/routes/thepaper/featured.ts +1 -1
  269. package/lib/routes/thepaper/list.ts +1 -1
  270. package/lib/routes/thepaper/user.ts +174 -0
  271. package/lib/routes/tongji/sem/_utils.ts +65 -0
  272. package/lib/routes/tongji/sem/notice.ts +58 -0
  273. package/lib/routes/toutiao/a-bogus.ts +540 -0
  274. package/lib/routes/toutiao/namespace.ts +7 -0
  275. package/lib/routes/toutiao/templates/video.art +7 -0
  276. package/lib/routes/toutiao/types.ts +443 -0
  277. package/lib/routes/toutiao/user.ts +127 -0
  278. package/lib/routes/twitter/api/mobile-api/api.ts +8 -0
  279. package/lib/routes/twitter/api/web-api/utils.ts +21 -9
  280. package/lib/routes/twitter/namespace.ts +1 -0
  281. package/lib/routes/twitter/utils.ts +7 -1
  282. package/lib/routes/vertikal/latest.ts +73 -0
  283. package/lib/routes/vertikal/namespace.ts +7 -0
  284. package/lib/routes/vice/topic.ts +1 -1
  285. package/lib/routes/vocus/publication.ts +1 -1
  286. package/lib/routes/vocus/user.ts +1 -1
  287. package/lib/routes/wallstreetcn/calendar.ts +90 -0
  288. package/lib/routes/wallstreetcn/hot.ts +2 -2
  289. package/lib/routes/wallstreetcn/live.ts +1 -1
  290. package/lib/routes/wallstreetcn/news.ts +5 -3
  291. package/lib/routes/weibo/timeline.ts +3 -2
  292. package/lib/routes/wellcee/rent.ts +1 -1
  293. package/lib/routes/wufazhuce/namespace.ts +7 -0
  294. package/lib/routes/wufazhuce/one.ts +90 -0
  295. package/lib/routes/xiaoheihe/discount.ts +72 -28
  296. package/lib/routes/xiaohongshu/user.ts +40 -3
  297. package/lib/routes/xiaohongshu/util.ts +112 -105
  298. package/lib/routes/xsijishe/rank.ts +33 -7
  299. package/lib/routes/xueqiu/cookies.ts +13 -18
  300. package/lib/routes/yahoo/news/listid.ts +4 -1
  301. package/lib/routes/yahoo/news/provider-helper.ts +4 -1
  302. package/lib/routes/yahoo/news/provider.ts +4 -1
  303. package/lib/routes/yande/post.ts +13 -3
  304. package/lib/routes/youtube/user.ts +37 -25
  305. package/lib/routes/zhonglun/index.ts +5 -6
  306. package/lib/routes/zjut/cs/index.ts +1 -1
  307. package/lib/utils/got.ts +2 -1
  308. package/lib/utils/helpers.test.ts +13 -1
  309. package/lib/utils/helpers.ts +10 -0
  310. package/lib/utils/ofetch.ts +3 -0
  311. package/lib/utils/request-rewriter/fetch.test.ts +94 -0
  312. package/lib/utils/request-rewriter/fetch.ts +14 -1
  313. package/lib/views/error.tsx +1 -1
  314. package/package.json +40 -37
  315. package/lib/routes/78dm/templates/image.art +0 -1
  316. package/lib/routes/deeplearning/thebatch.ts +0 -74
  317. package/lib/routes/syosetu/chapter.ts +0 -87
  318. package/lib/routes/xiaohongshu/notes.ts +0 -39
  319. package/lib/routes-deprecated/furaffinity/browse.js +0 -44
  320. package/lib/routes-deprecated/furaffinity/commissions.js +0 -39
  321. package/lib/routes-deprecated/furaffinity/favorites.js +0 -56
  322. package/lib/routes-deprecated/furaffinity/gallery.js +0 -56
  323. package/lib/routes-deprecated/furaffinity/home.js +0 -72
  324. package/lib/routes-deprecated/furaffinity/journal-comments.js +0 -50
  325. package/lib/routes-deprecated/furaffinity/journals.js +0 -49
  326. package/lib/routes-deprecated/furaffinity/scraps.js +0 -56
  327. package/lib/routes-deprecated/furaffinity/search.js +0 -56
  328. package/lib/routes-deprecated/furaffinity/shouts.js +0 -40
  329. package/lib/routes-deprecated/furaffinity/status.js +0 -38
  330. package/lib/routes-deprecated/furaffinity/submission-comments.js +0 -50
  331. package/lib/routes-deprecated/furaffinity/watchers.js +0 -47
  332. package/lib/routes-deprecated/furaffinity/watching.js +0 -47
  333. package/lib/routes-deprecated/ieee/author.js +0 -30
  334. package/lib/routes-deprecated/qstheory/index.js +0 -122
@@ -1,7 +1,9 @@
1
1
  import type { Namespace } from '@/types';
2
2
 
3
3
  export const namespace: Namespace = {
4
- name: 'deeplearning.ai',
4
+ name: 'DeepLearning.AI',
5
5
  url: 'www.deeplearning.ai',
6
+ categories: ['programming'],
7
+ description: '',
6
8
  lang: 'en',
7
9
  };
@@ -0,0 +1,21 @@
1
+ {{ if images }}
2
+ {{ each images image }}
3
+ {{ if image?.src }}
4
+ <figure>
5
+ <img
6
+ {{ if image.alt }}
7
+ alt="{{ image.alt }}"
8
+ {{ /if }}
9
+ src="{{ image.src }}">
10
+ </figure>
11
+ {{ /if }}
12
+ {{ /each }}
13
+ {{ /if }}
14
+
15
+ {{ if intro }}
16
+ <blockquote>{{ intro }}</blockquote>
17
+ {{ /if }}
18
+
19
+ {{ if description }}
20
+ {{@ description }}
21
+ {{ /if }}
@@ -0,0 +1,296 @@
1
+ import { Route } from '@/types';
2
+ import { getCurrentPath } from '@/utils/helpers';
3
+ const __dirname = getCurrentPath(import.meta.url);
4
+
5
+ import cache from '@/utils/cache';
6
+ import ofetch from '@/utils/ofetch';
7
+ import { load } from 'cheerio';
8
+ import { parseDate } from '@/utils/parse-date';
9
+ import { art } from '@/utils/render';
10
+ import path from 'node:path';
11
+
12
+ export const handler = async (ctx) => {
13
+ const { tag } = ctx.req.param();
14
+ const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 1;
15
+
16
+ const rootUrl = 'https://www.deeplearning.ai';
17
+ const currentUrl = new URL(`the-batch${tag ? `/tag/${tag.replace(/^tag\//, '').replace(/\/$/, '')}` : ''}/`, rootUrl).href;
18
+
19
+ const response = await ofetch(currentUrl);
20
+
21
+ const $ = load(response);
22
+
23
+ const language = $('html').prop('lang');
24
+
25
+ const data = JSON.parse($('script#__NEXT_DATA__').text());
26
+
27
+ const nextBuildId = data.buildId;
28
+ const posts = data.props?.pageProps?.posts ?? [];
29
+
30
+ let items = posts.slice(0, limit).map((item) => {
31
+ const title = item.title;
32
+ const description = art(path.join(__dirname, 'templates/description.art'), {
33
+ images: item.feature_image
34
+ ? [
35
+ {
36
+ src: item.feature_image,
37
+ alt: item.feature_image_alt,
38
+ },
39
+ ]
40
+ : undefined,
41
+ intro: item.excerpt ?? item.custom_excerpt,
42
+ });
43
+ const image = item.feature_image;
44
+ const guid = `the-batch-${item.slug}`;
45
+
46
+ return {
47
+ title,
48
+ description,
49
+ pubDate: parseDate(item.published_at),
50
+ link: new URL(`_next/data/${nextBuildId}/the-batch/${item.slug}.json`, rootUrl).href,
51
+ category: item.tags.map((t) => t.name),
52
+ guid,
53
+ id: guid,
54
+ content: {
55
+ html: description,
56
+ text: item.excerpt ?? item.custom_excerpt,
57
+ },
58
+ image,
59
+ banner: image,
60
+ language,
61
+ };
62
+ });
63
+
64
+ items = await Promise.all(
65
+ items.map((item) =>
66
+ cache.tryGet(item.link, async () => {
67
+ const detailResponse = await ofetch(item.link);
68
+
69
+ const post = detailResponse.pageProps?.cmsData?.post ?? undefined;
70
+
71
+ if (!post) {
72
+ return item;
73
+ }
74
+
75
+ const $$ = load(post.html);
76
+
77
+ $$('a').each((_, ele) => {
78
+ if (ele.attribs.href?.includes('utm_campaign')) {
79
+ const url = new URL(ele.attribs.href);
80
+ url.searchParams.delete('utm_campaign');
81
+ url.searchParams.delete('utm_source');
82
+ url.searchParams.delete('utm_medium');
83
+ url.searchParams.delete('_hsenc');
84
+ ele.attribs.href = url.href;
85
+ }
86
+ });
87
+
88
+ const title = post.title;
89
+ const description = art(path.join(__dirname, 'templates/description.art'), {
90
+ images: post.feature_image
91
+ ? [
92
+ {
93
+ src: post.feature_image,
94
+ alt: post.feature_image_alt,
95
+ },
96
+ ]
97
+ : undefined,
98
+ intro: post.excerpt ?? post.custom_excerpt,
99
+ description: $$.html(),
100
+ });
101
+ const guid = `the-batch-${post.slug}`;
102
+ const image = post.feature_image;
103
+
104
+ item.title = title;
105
+ item.description = description;
106
+ item.pubDate = parseDate(post.published_at);
107
+ item.link = new URL(`the-batch/${post.slug}`, rootUrl).href;
108
+ item.category = post.tags.map((t) => t.name);
109
+ item.author = post.authors.map((a) => a.name).join('/');
110
+ item.guid = guid;
111
+ item.id = guid;
112
+ item.content = {
113
+ html: description,
114
+ text: post.excerpt ?? post.custom_excerpt,
115
+ };
116
+ item.image = image;
117
+ item.banner = image;
118
+ item.updated = parseDate(post.updated_at);
119
+ item.language = language;
120
+
121
+ return item;
122
+ })
123
+ )
124
+ );
125
+
126
+ const image = new URL($('meta[property="og:image"]').prop('content'), rootUrl).href;
127
+
128
+ return {
129
+ title: $('title').text(),
130
+ description: $('meta[property="og:description"]').prop('content'),
131
+ link: currentUrl,
132
+ item: items,
133
+ allowEmpty: true,
134
+ image,
135
+ author: $('meta[property="og:site_name"]').prop('content'),
136
+ language,
137
+ };
138
+ };
139
+
140
+ export const route: Route = {
141
+ path: '/the-batch/:tag{.+}?',
142
+ name: 'The Batch',
143
+ url: 'www.deeplearning.ai',
144
+ maintainers: ['nczitzk', 'juvenn', 'TonyRL'],
145
+ handler,
146
+ example: '/deeplearning/the-batch',
147
+ parameters: { tag: 'Tag, Weekly Issues by default' },
148
+ description: `::: tip
149
+ If you subscribe to [Data Points](https://www.deeplearning.ai/the-batch/tag/data-points/),where the URL is \`https://www.deeplearning.ai/the-batch/tag/data-points/\`, extract the part \`https://www.deeplearning.ai/the-batch/tag\` to the end, which is \`data-points\`, and use it as the parameter to fill in. Therefore, the route will be [\`/deeplearning/the-batch/data-points\`](https://rsshub.app/deeplearning/the-batch/data-points).
150
+
151
+ :::
152
+
153
+ | Tag | ID |
154
+ | ---------------------------------------------------------------------- | -------------------------------------------------------------------- |
155
+ | [Weekly Issues](https://www.deeplearning.ai/the-batch/) | [*null*](https://rsshub.app/deeplearning/the-batch) |
156
+ | [Andrew's Letters](https://www.deeplearning.ai/the-batch/tag/letters/) | [letters](https://rsshub.app/deeplearning/the-batch/letters) |
157
+ | [Data Points](https://www.deeplearning.ai/the-batch/tag/data-points/) | [data-points](https://rsshub.app/deeplearning/the-batch/data-points) |
158
+ | [ML Research](https://www.deeplearning.ai/the-batch/tag/research/) | [research](https://rsshub.app/deeplearning/the-batch/research) |
159
+ | [Business](https://www.deeplearning.ai/the-batch/tag/business/) | [business](https://rsshub.app/deeplearning/the-batch/business) |
160
+ | [Science](https://www.deeplearning.ai/the-batch/tag/science/) | [science](https://rsshub.app/deeplearning/the-batch/science) |
161
+ | [AI & Society](https://www.deeplearning.ai/the-batch/tag/ai-society/) | [ai-society](https://rsshub.app/deeplearning/the-batch/ai-society) |
162
+ | [Culture](https://www.deeplearning.ai/the-batch/tag/culture/) | [culture](https://rsshub.app/deeplearning/the-batch/culture) |
163
+ | [Hardware](https://www.deeplearning.ai/the-batch/tag/hardware/) | [hardware](https://rsshub.app/deeplearning/the-batch/hardware) |
164
+ | [AI Careers](https://www.deeplearning.ai/the-batch/tag/ai-careers/) | [ai-careers](https://rsshub.app/deeplearning/the-batch/ai-careers) |
165
+
166
+ #### [Letters from Andrew Ng](https://www.deeplearning.ai/the-batch/tag/letters/)
167
+
168
+ | Tag | ID |
169
+ | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
170
+ | [All](https://www.deeplearning.ai/the-batch/tag/letters/) | [letters](https://rsshub.app/deeplearning/the-batch/letters) |
171
+ | [Personal Insights](https://www.deeplearning.ai/the-batch/tag/personal-insights/) | [personal-insights](https://rsshub.app/deeplearning/the-batch/personal-insights) |
172
+ | [Technical Insights](https://www.deeplearning.ai/the-batch/tag/technical-insights/) | [technical-insights](https://rsshub.app/deeplearning/the-batch/technical-insights) |
173
+ | [Business Insights](https://www.deeplearning.ai/the-batch/tag/business-insights/) | [business-insights](https://rsshub.app/deeplearning/the-batch/business-insights) |
174
+ | [Tech & Society](https://www.deeplearning.ai/the-batch/tag/tech-society/) | [tech-society](https://rsshub.app/deeplearning/the-batch/tech-society) |
175
+ | [DeepLearning.AI News](https://www.deeplearning.ai/the-batch/tag/deeplearning-ai-news/) | [deeplearning-ai-news](https://rsshub.app/deeplearning/the-batch/deeplearning-ai-news) |
176
+ | [AI Careers](https://www.deeplearning.ai/the-batch/tag/ai-careers/) | [ai-careers](https://rsshub.app/deeplearning/the-batch/ai-careers) |
177
+ | [Just For Fun](https://www.deeplearning.ai/the-batch/tag/just-for-fun/) | [just-for-fun](https://rsshub.app/deeplearning/the-batch/just-for-fun) |
178
+ | [Learning & Education](https://www.deeplearning.ai/the-batch/tag/learning-education/) | [learning-education](https://rsshub.app/deeplearning/the-batch/learning-education) |
179
+ `,
180
+ categories: ['programming'],
181
+
182
+ features: {
183
+ requireConfig: false,
184
+ requirePuppeteer: false,
185
+ antiCrawler: false,
186
+ supportRadar: true,
187
+ supportBT: false,
188
+ supportPodcast: false,
189
+ supportScihub: false,
190
+ },
191
+ radar: [
192
+ {
193
+ source: ['www.deeplearning.ai/the-batch', 'www.deeplearning.ai/the-batch/tag/:tag/'],
194
+ target: (params) => {
195
+ const tag = params.tag;
196
+
197
+ return `/the-batch${tag ? `/${tag}` : ''}`;
198
+ },
199
+ },
200
+ {
201
+ title: 'Weekly Issues',
202
+ source: ['www.deeplearning.ai/the-batch/'],
203
+ target: '/the-batch',
204
+ },
205
+ {
206
+ title: "Andrew's Letters",
207
+ source: ['www.deeplearning.ai/the-batch/tag/letters/'],
208
+ target: '/the-batch/letters',
209
+ },
210
+ {
211
+ title: 'Data Points',
212
+ source: ['www.deeplearning.ai/the-batch/tag/data-points/'],
213
+ target: '/the-batch/data-points',
214
+ },
215
+ {
216
+ title: 'ML Research',
217
+ source: ['www.deeplearning.ai/the-batch/tag/research/'],
218
+ target: '/the-batch/research',
219
+ },
220
+ {
221
+ title: 'Business',
222
+ source: ['www.deeplearning.ai/the-batch/tag/business/'],
223
+ target: '/the-batch/business',
224
+ },
225
+ {
226
+ title: 'Science',
227
+ source: ['www.deeplearning.ai/the-batch/tag/science/'],
228
+ target: '/the-batch/science',
229
+ },
230
+ {
231
+ title: 'AI & Society',
232
+ source: ['www.deeplearning.ai/the-batch/tag/ai-society/'],
233
+ target: '/the-batch/ai-society',
234
+ },
235
+ {
236
+ title: 'Culture',
237
+ source: ['www.deeplearning.ai/the-batch/tag/culture/'],
238
+ target: '/the-batch/culture',
239
+ },
240
+ {
241
+ title: 'Hardware',
242
+ source: ['www.deeplearning.ai/the-batch/tag/hardware/'],
243
+ target: '/the-batch/hardware',
244
+ },
245
+ {
246
+ title: 'AI Careers',
247
+ source: ['www.deeplearning.ai/the-batch/tag/ai-careers/'],
248
+ target: '/the-batch/ai-careers',
249
+ },
250
+ {
251
+ title: 'Letters from Andrew Ng - All',
252
+ source: ['www.deeplearning.ai/the-batch/tag/letters/'],
253
+ target: '/the-batch/letters',
254
+ },
255
+ {
256
+ title: 'Letters from Andrew Ng - Personal Insights',
257
+ source: ['www.deeplearning.ai/the-batch/tag/personal-insights/'],
258
+ target: '/the-batch/personal-insights',
259
+ },
260
+ {
261
+ title: 'Letters from Andrew Ng - Technical Insights',
262
+ source: ['www.deeplearning.ai/the-batch/tag/technical-insights/'],
263
+ target: '/the-batch/technical-insights',
264
+ },
265
+ {
266
+ title: 'Letters from Andrew Ng - Business Insights',
267
+ source: ['www.deeplearning.ai/the-batch/tag/business-insights/'],
268
+ target: '/the-batch/business-insights',
269
+ },
270
+ {
271
+ title: 'Letters from Andrew Ng - Tech & Society',
272
+ source: ['www.deeplearning.ai/the-batch/tag/tech-society/'],
273
+ target: '/the-batch/tech-society',
274
+ },
275
+ {
276
+ title: 'Letters from Andrew Ng - DeepLearning.AI News',
277
+ source: ['www.deeplearning.ai/the-batch/tag/deeplearning-ai-news/'],
278
+ target: '/the-batch/deeplearning-ai-news',
279
+ },
280
+ {
281
+ title: 'Letters from Andrew Ng - AI Careers',
282
+ source: ['www.deeplearning.ai/the-batch/tag/ai-careers/'],
283
+ target: '/the-batch/ai-careers',
284
+ },
285
+ {
286
+ title: 'Letters from Andrew Ng - Just For Fun',
287
+ source: ['www.deeplearning.ai/the-batch/tag/just-for-fun/'],
288
+ target: '/the-batch/just-for-fun',
289
+ },
290
+ {
291
+ title: 'Letters from Andrew Ng - Learning & Education',
292
+ source: ['www.deeplearning.ai/the-batch/tag/learning-education/'],
293
+ target: '/the-batch/learning-education',
294
+ },
295
+ ],
296
+ };
@@ -6,7 +6,7 @@ import parser from '@/utils/rss-parser';
6
6
 
7
7
  export const route: Route = {
8
8
  path: '/blog',
9
- categories: ['new-media'],
9
+ categories: ['new-media', 'popular'],
10
10
  example: '/deepmind/blog',
11
11
  parameters: {},
12
12
  features: {
@@ -5,7 +5,7 @@ import { parseDate } from '@/utils/parse-date';
5
5
  const host = 'https://www.digitalcameraworld.com';
6
6
  export const route: Route = {
7
7
  path: '/news',
8
- categories: ['new-media'],
8
+ categories: ['new-media', 'popular'],
9
9
  example: '/digitalcameraworld/news',
10
10
  parameters: {},
11
11
  features: {
@@ -1,7 +1,9 @@
1
+ import { APIMessage } from 'discord-api-types/v10';
2
+ import { RESTGetAPIGuildResult, RESTGetAPIGuildChannelsResult, RESTGetAPIChannelResult, RESTGetAPIChannelMessagesQuery, RESTGetAPIChannelMessagesResult } from 'discord-api-types/rest/v10';
3
+
4
+ import { config } from '@/config';
1
5
  import cache from '@/utils/cache';
2
6
  import ofetch from '@/utils/ofetch';
3
- import { config } from '@/config';
4
- import { RESTGetAPIGuildResult, RESTGetAPIGuildChannelsResult, RESTGetAPIChannelResult, RESTGetAPIChannelMessagesQuery, RESTGetAPIChannelMessagesResult } from 'discord-api-types/rest/v10';
5
7
 
6
8
  export const baseUrl = 'https://discord.com';
7
9
  const apiUrl = `${baseUrl}/api/v10`;
@@ -48,3 +50,43 @@ export const getChannelMessages = (channelId, authorization, limit = 100) =>
48
50
  config.cache.routeExpire,
49
51
  false
50
52
  ) as Promise<RESTGetAPIChannelMessagesResult>;
53
+
54
+ interface SearchGuildMessagesResult {
55
+ analytics_id: string;
56
+ doing_deep_historical_index: boolean;
57
+ total_results: number;
58
+ messages: APIMessage[][];
59
+ }
60
+
61
+ export const VALID_HAS_TYPES = new Set(['link', 'embed', 'poll', 'file', 'video', 'image', 'sound', 'sticker', 'snapshot'] as const);
62
+
63
+ export type HasType = typeof VALID_HAS_TYPES extends Set<infer T> ? T : never;
64
+
65
+ export interface SearchGuildMessagesParams {
66
+ content?: string;
67
+ author_id?: string;
68
+ mentions?: string;
69
+ has?: HasType[];
70
+ max_id?: string;
71
+ min_id?: string;
72
+ channel_id?: string;
73
+ pinned?: boolean;
74
+ }
75
+
76
+ export const searchGuildMessages = (guildId: string, authorization: string, params: SearchGuildMessagesParams) =>
77
+ cache.tryGet(
78
+ `discord:guilds:${guildId}:search:${JSON.stringify(params)}`,
79
+ () => {
80
+ const queryParams = {
81
+ ...params,
82
+ has: params.has?.length ? params.has : undefined,
83
+ };
84
+
85
+ return ofetch(`${apiUrl}/guilds/${guildId}/messages/search`, {
86
+ headers: { authorization },
87
+ query: queryParams,
88
+ });
89
+ },
90
+ config.cache.routeExpire,
91
+ false
92
+ ) as Promise<SearchGuildMessagesResult>;
@@ -0,0 +1,106 @@
1
+ import path from 'node:path';
2
+
3
+ import { config } from '@/config';
4
+ import InvalidParameterError from '@/errors/types/invalid-parameter';
5
+ import { Route } from '@/types';
6
+ import { getCurrentPath } from '@/utils/helpers';
7
+ import { parseDate } from '@/utils/parse-date';
8
+ import { art } from '@/utils/render';
9
+ import ConfigNotFoundError from '@/errors/types/config-not-found';
10
+ import { queryToBoolean } from '@/utils/readable-social';
11
+
12
+ import { baseUrl, getGuild, searchGuildMessages, SearchGuildMessagesParams, HasType, VALID_HAS_TYPES } from './discord-api';
13
+
14
+ const __dirname = getCurrentPath(import.meta.url);
15
+
16
+ export const route: Route = {
17
+ path: '/search/:guildId/:routeParams',
18
+ categories: ['social-media'],
19
+ example: '/discord/search/302094807046684672/content=friendly&has=image,video',
20
+ parameters: {
21
+ guildId: 'Guild ID',
22
+ routeParams: 'Search parameters, support content, author_id, mentions, has, min_id, max_id, channel_id, pinned',
23
+ },
24
+ features: {
25
+ requireConfig: [
26
+ {
27
+ name: 'DISCORD_AUTHORIZATION',
28
+ description: 'Discord authorization header',
29
+ },
30
+ ],
31
+ requirePuppeteer: false,
32
+ antiCrawler: false,
33
+ supportBT: false,
34
+ supportPodcast: false,
35
+ supportScihub: false,
36
+ },
37
+ name: 'Guild Search',
38
+ maintainers: ['NekoAria'],
39
+ handler,
40
+ };
41
+
42
+ const parseSearchParams = (routeParams?: string): SearchGuildMessagesParams => {
43
+ const parsed = new URLSearchParams(routeParams);
44
+ const hasTypes = parsed.get('has')?.split(',').filter(Boolean);
45
+ const validHasTypes = hasTypes?.filter((type) => VALID_HAS_TYPES.has(type as HasType)) as HasType[];
46
+
47
+ const params = {
48
+ content: parsed.get('content') ?? undefined,
49
+ author_id: parsed.get('author_id') ?? undefined,
50
+ mentions: parsed.get('mentions') ?? undefined,
51
+ has: validHasTypes?.length ? validHasTypes : undefined,
52
+ min_id: parsed.get('min_id') ?? undefined,
53
+ max_id: parsed.get('max_id') ?? undefined,
54
+ channel_id: parsed.get('channel_id') ?? undefined,
55
+ pinned: parsed.has('pinned') ? queryToBoolean(parsed.get('pinned')) : undefined,
56
+ };
57
+
58
+ return Object.fromEntries(Object.entries(params).filter(([, value]) => value !== undefined));
59
+ };
60
+
61
+ async function handler(ctx) {
62
+ const { authorization } = config.discord || {};
63
+ if (!authorization) {
64
+ throw new ConfigNotFoundError('Discord RSS is disabled due to the lack of authorization config');
65
+ }
66
+
67
+ const { guildId } = ctx.req.param();
68
+ const searchParams = parseSearchParams(ctx.req.param('routeParams'));
69
+
70
+ if (!Object.keys(searchParams).length) {
71
+ throw new InvalidParameterError('At least one valid search parameter is required');
72
+ }
73
+
74
+ const [guildInfo, searchResult] = await Promise.all([getGuild(guildId, authorization), searchGuildMessages(guildId, authorization, searchParams)]);
75
+
76
+ if (!searchResult?.messages?.length) {
77
+ return {
78
+ title: `Search Results - ${guildInfo.name}`,
79
+ link: `${baseUrl}/channels/${guildId}`,
80
+ item: [],
81
+ allowEmpty: true,
82
+ };
83
+ }
84
+
85
+ const messages = searchResult.messages.flat().map((message) => ({
86
+ title: message.content.split('\n')[0] || '(no content)',
87
+ description: art(path.join(__dirname, 'templates/message.art'), { message, guildInfo }),
88
+ author: message.author.global_name ?? message.author.username,
89
+ pubDate: parseDate(message.timestamp),
90
+ updated: message.edited_timestamp ? parseDate(message.edited_timestamp) : undefined,
91
+ category: [`#${message.channel_id}`],
92
+ link: `${baseUrl}/channels/${guildId}/${message.channel_id}/${message.id}`,
93
+ }));
94
+
95
+ const searchDesc = Object.entries(searchParams)
96
+ .filter(([, value]) => value !== undefined)
97
+ .map(([key, value]) => `${key}:${Array.isArray(value) ? value.join(',') : value}`)
98
+ .join(' ');
99
+
100
+ return {
101
+ title: `Search "${searchDesc}" in ${guildInfo.name} - Discord`,
102
+ link: `${baseUrl}/channels/${guildId}`,
103
+ item: messages,
104
+ allowEmpty: true,
105
+ };
106
+ }
@@ -12,7 +12,7 @@ import path from 'node:path';
12
12
 
13
13
  export const route: Route = {
14
14
  path: '/:language/news/:category?',
15
- categories: ['new-media'],
15
+ categories: ['new-media', 'popular'],
16
16
  example: '/dn/en-us/news',
17
17
  parameters: { language: 'Language, see below', category: 'Category, see below, The Latest by default' },
18
18
  features: {
@@ -0,0 +1,42 @@
1
+ import { Route, ViewType } from '@/types';
2
+ import got from '@/utils/got';
3
+ import { parseDate } from '@/utils/parse-date';
4
+ import { Context } from 'hono';
5
+
6
+ export const route: Route = {
7
+ name: 'Owner Repositories',
8
+ description: 'List of repositories for an image owner',
9
+ maintainers: ['CaoMeiYouRen'],
10
+ path: '/repositories/:owner',
11
+ categories: ['program-update'],
12
+ view: ViewType.Notifications,
13
+ example: '/dockerhub/repositories/diygod',
14
+ parameters: { owner: 'Image owner' },
15
+ handler,
16
+ };
17
+
18
+ async function handler(ctx: Context) {
19
+ const owner = ctx.req.param('owner').toLowerCase();
20
+ const limit = Number.parseInt(ctx.req.query('limit') || '10');
21
+ const link = `https://hub.docker.com/r/${owner}`;
22
+ const url = `https://hub.docker.com/v2/repositories/${owner}`;
23
+ const response = await got(url, {
24
+ searchParams: {
25
+ page_size: limit,
26
+ },
27
+ });
28
+ const item = response.data.results.map((repo) => ({
29
+ title: repo.name,
30
+ description: `${repo.description}<br>status: ${repo.status_description}<br>stars: ${repo.star_count}<br>pulls: ${repo.pull_count}`,
31
+ link: `https://hub.docker.com/r/${owner}/${repo.name}`,
32
+ author: owner,
33
+ pubDate: parseDate(repo.last_updated),
34
+ guid: `${owner}/${repo.name}`,
35
+ }));
36
+ return {
37
+ title: `${owner} repositories`,
38
+ description: `List of repositories for ${owner}`,
39
+ link,
40
+ item,
41
+ };
42
+ }
@@ -18,5 +18,5 @@ export const route: Route = {
18
18
  };
19
19
 
20
20
  function handler(ctx) {
21
- ctx.redirect('/dongqiudi/special/48');
21
+ ctx.set('redirect', '/dongqiudi/special/48');
22
22
  }
@@ -0,0 +1,6 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'DW Deutsche Welle',
5
+ url: 'dw.com',
6
+ };
@@ -0,0 +1,89 @@
1
+ import { Route } from '@/types';
2
+ import { processItems } from './utils';
3
+ import got from '@/utils/got';
4
+ import cache from '@/utils/cache';
5
+ import { config } from '@/config';
6
+
7
+ export const route: Route = {
8
+ path: '/news/:lang?/:id?',
9
+ categories: ['traditional-media'],
10
+ example: '/dw/news',
11
+ parameters: {
12
+ lang: 'Language, see below, default to en',
13
+ id: 'Category ID, see below, default to the id of the Top Stories Page of the language chosen',
14
+ },
15
+ features: {
16
+ requirePuppeteer: false,
17
+ antiCrawler: false,
18
+ supportBT: false,
19
+ supportPodcast: false,
20
+ supportScihub: false,
21
+ requireConfig: false,
22
+ },
23
+ name: 'News',
24
+ maintainers: ['quiniapiezoelectricity'],
25
+ handler,
26
+ description: `
27
+ :::tip
28
+ Parameters can be obtained from the official website, for instance:
29
+ For the site https://www.dw.com/de/deutschland/s-12321 the language code would be \`de\` and the category ID would be \`s-1432\`.
30
+ :::
31
+ `,
32
+ radar: [
33
+ {
34
+ source: ['www.dw.com/:lang/:name/:id'],
35
+ target: '/news/:lang/:id',
36
+ },
37
+ ],
38
+ };
39
+
40
+ const defaultUrl = `https://www.dw.com/graph-api/en/content/navigation/9097`;
41
+ const typenames = new Set(['Article', 'Liveblog', 'Video']);
42
+
43
+ async function handler(ctx) {
44
+ const lang = ctx.req.param('lang') ?? 'en';
45
+ let id = ctx.req.param('id');
46
+
47
+ if (/^s-\d+$/.test(id)) {
48
+ id = id.match(/^s-(\d+)$/i)[1]; // convert s-1234 id to 1234
49
+ } else if (id === undefined) {
50
+ // Look up the id of the Top Stories Page of the selected language if id is not specified in the URL.
51
+ const navigation = await cache.tryGet(
52
+ 'dw:navigation',
53
+ async () => {
54
+ const res = await got(defaultUrl);
55
+ return res.data.data.content.topStoriesNavigations;
56
+ },
57
+ config.cache.routeExpire,
58
+ false
59
+ );
60
+ id = navigation
61
+ .map((item) => item.namedUrl.split('/'))
62
+ .find((item) => item[1] === lang)[3]
63
+ .match(/^s-(\d+)$/i)[1];
64
+ }
65
+
66
+ const response = await got(`https://www.dw.com/graph-api/${lang}/content/navigation/${id}`);
67
+ const feed = response.data.data.content;
68
+ cache.set('dw:navigation', feed.topStoriesNavigations, config.cache.routeExpire);
69
+
70
+ const list = feed.contentComposition.informationSpaces.flatMap((section) => Object.values(section).flatMap((component) => component[0]?.contents || [])).filter((item) => typenames.has(item.__typename) && item.id);
71
+ const items = await processItems(
72
+ list.map((item) => {
73
+ item.link = new URL(item.namedUrl, 'https://www.dw.com').href;
74
+ item.pubDate = item.contentDate;
75
+ item.description = item.teaser;
76
+ item.language = lang;
77
+ item.type = item.__typename.toLowerCase();
78
+ return item;
79
+ })
80
+ );
81
+
82
+ return {
83
+ title: `DW | ${feed.title}`,
84
+ link: feed.canonicalUrl,
85
+ description: feed.metaDescription,
86
+ language: feed.topStoriesNavigations.find((item) => item.namedUrl.startsWith(`/${lang}/`))?.localeLang ?? lang,
87
+ item: items,
88
+ };
89
+ }