rsshub 1.0.0-master.ff3a9ce → 1.0.0-master.ff3ea5a

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.

Potentially problematic release.


This version of rsshub might be problematic. Click here for more details.

Files changed (388) hide show
  1. package/lib/config.ts +38 -0
  2. package/lib/errors/index.tsx +3 -2
  3. package/lib/middleware/debug.ts +6 -4
  4. package/lib/middleware/header.ts +4 -2
  5. package/lib/registry.test.ts +24 -1
  6. package/lib/registry.ts +40 -11
  7. package/lib/router.js +1 -1
  8. package/lib/routes/121/namespace.ts +9 -0
  9. package/lib/routes/121/templates/description.art +17 -0
  10. package/lib/routes/121/weather-live.ts +104 -0
  11. package/lib/routes/141jav/index.ts +3 -0
  12. package/lib/routes/141ppv/index.ts +3 -0
  13. package/lib/routes/163/news/rank.ts +1 -1
  14. package/lib/routes/18comic/album.ts +59 -64
  15. package/lib/routes/18comic/index.ts +1 -0
  16. package/lib/routes/18comic/search.ts +53 -4
  17. package/lib/routes/18comic/utils.ts +70 -1
  18. package/lib/routes/2048/index.ts +1 -0
  19. package/lib/routes/30secondsofcode/utils.ts +0 -1
  20. package/lib/routes/6park/index.ts +1 -1
  21. package/lib/routes/7mmtv/index.ts +1 -0
  22. package/lib/routes/91porn/index.ts +1 -0
  23. package/lib/routes/95mm/tab.ts +1 -0
  24. package/lib/routes/agefans/detail.ts +1 -1
  25. package/lib/routes/aliyun/database-month.ts +1 -1
  26. package/lib/routes/anthropic/research.ts +112 -0
  27. package/lib/routes/apnews/topics.ts +2 -1
  28. package/lib/routes/apnews/utils.ts +1 -1
  29. package/lib/routes/apple/design.ts +54 -0
  30. package/lib/routes/apple/namespace.ts +1 -1
  31. package/lib/routes/aqara/post.ts +1 -1
  32. package/lib/routes/augmentcode/blog.ts +161 -0
  33. package/lib/routes/augmentcode/namespace.ts +9 -0
  34. package/lib/routes/augmentcode/templates/description.art +17 -0
  35. package/lib/routes/azul/namespace.ts +9 -0
  36. package/lib/routes/azul/packages.ts +105 -0
  37. package/lib/routes/banshujiang/index.ts +763 -0
  38. package/lib/routes/banshujiang/namespace.ts +9 -0
  39. package/lib/routes/banshujiang/templates/description.art +17 -0
  40. package/lib/routes/bfl/announcements.ts +1 -1
  41. package/lib/routes/bilibili/cache.ts +78 -0
  42. package/lib/routes/bilibili/danmaku.ts +1 -1
  43. package/lib/routes/bilibili/dynamic.ts +8 -2
  44. package/lib/routes/bilibili/video.ts +31 -73
  45. package/lib/routes/biquge/index.ts +1 -1
  46. package/lib/routes/bsky/keyword.ts +15 -10
  47. package/lib/routes/capitalmind/insights.ts +40 -0
  48. package/lib/routes/capitalmind/namespace.ts +8 -0
  49. package/lib/routes/capitalmind/podcasts.ts +41 -0
  50. package/lib/routes/capitalmind/utils.ts +122 -0
  51. package/lib/routes/cartoonmad/comic.ts +1 -1
  52. package/lib/routes/chikubi/index.ts +1 -0
  53. package/lib/routes/chikubi/tag.ts +1 -0
  54. package/lib/routes/chocolatey/namespace.ts +9 -0
  55. package/lib/routes/chocolatey/packages.ts +122 -0
  56. package/lib/routes/cmde/index.ts +1 -1
  57. package/lib/routes/cockroachlabs/blog.ts +113 -0
  58. package/lib/routes/cockroachlabs/namespace.ts +7 -0
  59. package/lib/routes/cool18/index.ts +4 -1
  60. package/lib/routes/coomer/index.ts +12 -6
  61. package/lib/routes/coomer/namespace.ts +1 -1
  62. package/lib/routes/copymanga/comic.ts +7 -9
  63. package/lib/routes/css-tricks/articles.ts +48 -0
  64. package/lib/routes/css-tricks/collections.ts +62 -0
  65. package/lib/routes/css-tricks/namespace.ts +8 -0
  66. package/lib/routes/css-tricks/popular.ts +39 -0
  67. package/lib/routes/css-tricks/utils.ts +103 -0
  68. package/lib/routes/cursor/changelog.ts +26 -17
  69. package/lib/routes/cursor/namespace.ts +1 -1
  70. package/lib/routes/cw/utils.ts +6 -13
  71. package/lib/routes/dealstreetasia/home.ts +1 -1
  72. package/lib/routes/dedao/index.ts +3 -3
  73. package/lib/routes/deepl/blog.ts +422 -0
  74. package/lib/routes/deepl/namespace.ts +9 -0
  75. package/lib/routes/deepl/templates/description.art +21 -0
  76. package/lib/routes/deeplearning/the-batch.ts +1 -1
  77. package/lib/routes/dev.to/guides.ts +100 -0
  78. package/lib/routes/dev.to/namespace.ts +8 -0
  79. package/lib/routes/dev.to/top.ts +105 -0
  80. package/lib/routes/dgut/jwb.ts +73 -0
  81. package/lib/routes/dgut/namespace.ts +9 -0
  82. package/lib/routes/digitalpolicyalert/activity-tracker.ts +135 -0
  83. package/lib/routes/digitalpolicyalert/namespace.ts +9 -0
  84. package/lib/routes/dnaindia/common.ts +26 -8
  85. package/lib/routes/douban/other/list.ts +3 -3
  86. package/lib/routes/e-hentai/index.ts +4 -1
  87. package/lib/routes/ehentai/tag.ts +1 -0
  88. package/lib/routes/englishhome/index.ts +55 -0
  89. package/lib/routes/englishhome/namespace.ts +6 -0
  90. package/lib/routes/epicgames/index.ts +2 -2
  91. package/lib/routes/espn/news.ts +1 -1
  92. package/lib/routes/expats/czech-news.ts +272 -0
  93. package/lib/routes/expats/namespace.ts +9 -0
  94. package/lib/routes/fanbox/index.ts +1 -0
  95. package/lib/routes/fanbox/utils.ts +1 -1
  96. package/lib/routes/fansly/tag.ts +1 -0
  97. package/lib/routes/fantia/search.ts +1 -1
  98. package/lib/routes/fantube/creator.ts +69 -0
  99. package/lib/routes/fantube/namespace.ts +7 -0
  100. package/lib/routes/fantube/templates/post.art +17 -0
  101. package/lib/routes/fantube/types.ts +100 -0
  102. package/lib/routes/fantube/utils.ts +268 -0
  103. package/lib/routes/follow/profile.ts +1 -1
  104. package/lib/routes/followin/news.ts +1 -1
  105. package/lib/routes/foresightnews/util.ts +1 -1
  106. package/lib/routes/freexcomic/book.ts +4 -1
  107. package/lib/routes/gamer/hot.ts +5 -5
  108. package/lib/routes/gaoyu/blog.ts +145 -0
  109. package/lib/routes/gaoyu/namespace.ts +9 -0
  110. package/lib/routes/gaoyu/templates/description.art +7 -0
  111. package/lib/routes/geocaching/blogs.ts +2 -2
  112. package/lib/routes/github/namespace.ts +1 -1
  113. package/lib/routes/github/private-feed.ts +229 -0
  114. package/lib/routes/github/star.ts +1 -1
  115. package/lib/routes/google/extension.ts +9 -10
  116. package/lib/routes/gov/beijing/bphc/index.ts +1 -1
  117. package/lib/routes/gov/ccdi/index.ts +1 -1
  118. package/lib/routes/gov/chongqing/sydwgkzp.ts +22 -6
  119. package/lib/routes/gov/customs/list.ts +1 -1
  120. package/lib/routes/gov/general/general.ts +1 -1
  121. package/lib/routes/gov/hangzhou/zwfw.ts +1 -1
  122. package/lib/routes/gov/pbc/goutongjiaoliu.ts +1 -1
  123. package/lib/routes/gov/shenzhen/szlh/index.ts +77 -0
  124. package/lib/routes/gov/shenzhen/szlh/namespace.ts +8 -0
  125. package/lib/routes/grist/utils.ts +5 -4
  126. package/lib/routes/hanime1/search.ts +4 -2
  127. package/lib/routes/hellogithub/report.ts +1 -1
  128. package/lib/routes/hit/hitgs.ts +240 -54
  129. package/lib/routes/hit/templates/description.art +7 -0
  130. package/lib/routes/hlju/namespace.ts +8 -0
  131. package/lib/routes/hlju/news.ts +141 -0
  132. package/lib/routes/hostmonit/cloudflareyes.ts +1 -1
  133. package/lib/routes/hotukdeals/index.ts +1 -1
  134. package/lib/routes/hyperdash/namespace.ts +7 -0
  135. package/lib/routes/hyperdash/templates/description.art +34 -0
  136. package/lib/routes/hyperdash/top-traders.ts +84 -0
  137. package/lib/routes/hyperdash/utils.ts +49 -0
  138. package/lib/routes/hypergryph/arknights/announce.ts +1 -1
  139. package/lib/routes/ielts/index.ts +1 -1
  140. package/lib/routes/infoq/topic.ts +16 -13
  141. package/lib/routes/instagram/web-api/index.ts +17 -13
  142. package/lib/routes/instagram/web-api/utils.ts +46 -63
  143. package/lib/routes/jamesclear/book-summaries.ts +40 -0
  144. package/lib/routes/jamesclear/great-speeches.ts +40 -0
  145. package/lib/routes/jamesclear/namespace.ts +8 -0
  146. package/lib/routes/jamesclear/quotes.ts +40 -0
  147. package/lib/routes/jamesclear/three-two-one.ts +41 -0
  148. package/lib/routes/jamesclear/utils.ts +22 -0
  149. package/lib/routes/japanpost/utils.ts +2 -2
  150. package/lib/routes/javbus/index.ts +3 -0
  151. package/lib/routes/javdb/tags.ts +1 -0
  152. package/lib/routes/javlibrary/star.ts +1 -0
  153. package/lib/routes/javtiful/actress.ts +3 -0
  154. package/lib/routes/javtiful/channel.ts +3 -0
  155. package/lib/routes/javtrailers/casts.ts +3 -0
  156. package/lib/routes/jetbrains/comments.ts +99 -0
  157. package/lib/routes/jetbrains/namespace.ts +8 -0
  158. package/lib/routes/jimmyspa/books.ts +1 -1
  159. package/lib/routes/jimmyspa/news.ts +1 -1
  160. package/lib/routes/jpxgmn/tab.ts +3 -0
  161. package/lib/routes/kakuyomu/works.ts +3 -0
  162. package/lib/routes/kaopu/news.ts +1 -1
  163. package/lib/routes/kemono/const.ts +1 -1
  164. package/lib/routes/kemono/index.ts +12 -11
  165. package/lib/routes/kemono/namespace.ts +1 -1
  166. package/lib/routes/kiro/blog.ts +131 -0
  167. package/lib/routes/kiro/changelog.ts +128 -0
  168. package/lib/routes/kiro/namespace.ts +9 -0
  169. package/lib/routes/konachan/post.ts +3 -0
  170. package/lib/routes/kovidgoyal/kitty/changelog.ts +83 -0
  171. package/lib/routes/kovidgoyal/namespace.ts +7 -0
  172. package/lib/routes/kunchengblog/essay.ts +1 -1
  173. package/lib/routes/landiannews/category.ts +1 -1
  174. package/lib/routes/landiannews/index.ts +1 -1
  175. package/lib/routes/landiannews/tag.ts +1 -1
  176. package/lib/routes/line/utils.ts +1 -1
  177. package/lib/routes/lineageos/changes.ts +79 -0
  178. package/lib/routes/lineageos/namespace.ts +9 -0
  179. package/lib/routes/linkedin/cn/renderer.ts +1 -1
  180. package/lib/routes/liquipedia/cs-matches.ts +42 -14
  181. package/lib/routes/literotica/new.ts +1 -0
  182. package/lib/routes/lofter/tag.ts +27 -3
  183. package/lib/routes/makerworld/contest.ts +51 -0
  184. package/lib/routes/makerworld/namespace.ts +7 -0
  185. package/lib/routes/makerworld/trending.ts +45 -0
  186. package/lib/routes/makerworld/user-upload.ts +54 -0
  187. package/lib/routes/makerworld/utils.ts +18 -0
  188. package/lib/routes/manhuagui/comic.ts +1 -1
  189. package/lib/routes/manus/blog.ts +77 -0
  190. package/lib/routes/manus/namespace.ts +7 -0
  191. package/lib/routes/mastodon/tag.ts +48 -0
  192. package/lib/routes/mathpix/blog.ts +155 -0
  193. package/lib/routes/mathpix/namespace.ts +9 -0
  194. package/lib/routes/mathpix/templates/description.art +21 -0
  195. package/lib/routes/mckinsey/cn/category-map.ts +9 -15
  196. package/lib/routes/mckinsey/cn/index.ts +77 -40
  197. package/lib/routes/melonbooks/search.ts +1 -0
  198. package/lib/routes/metacritic/index.ts +2 -2
  199. package/lib/routes/meteoblue/namespace.ts +8 -0
  200. package/lib/routes/meteoblue/weathernews.ts +75 -0
  201. package/lib/routes/meteor/index.ts +1 -1
  202. package/lib/routes/microsoft/addon.ts +7 -9
  203. package/lib/routes/mihoyo/zzz/news.ts +106 -0
  204. package/lib/routes/missav/new.ts +2 -1
  205. package/lib/routes/mit/hanlab.ts +61 -0
  206. package/lib/routes/mit/namespace.ts +6 -0
  207. package/lib/routes/mittrchina/index.ts +3 -3
  208. package/lib/routes/mixcloud/config.ts +129 -0
  209. package/lib/routes/mixcloud/index.ts +163 -107
  210. package/lib/routes/mixcloud/user-playlist.ts +31 -0
  211. package/lib/routes/mixi2/community.ts +72 -0
  212. package/lib/routes/mixi2/discovery.ts +52 -0
  213. package/lib/routes/mixi2/home.ts +51 -0
  214. package/lib/routes/mixi2/namespace.ts +8 -0
  215. package/lib/routes/mixi2/user.ts +77 -0
  216. package/lib/routes/mixi2/utils.ts +56 -0
  217. package/lib/routes/modelscope/learn.ts +94 -0
  218. package/lib/routes/moodysmismicrosite/report.ts +1 -1
  219. package/lib/routes/musify/index.ts +130 -0
  220. package/lib/routes/musify/namespace.ts +9 -0
  221. package/lib/routes/musikguru/namespace.ts +9 -0
  222. package/lib/routes/musikguru/news.ts +146 -0
  223. package/lib/routes/musikguru/templates/description.art +21 -0
  224. package/lib/routes/myfans/post.ts +1 -0
  225. package/lib/routes/nankai/cc-notice.ts +114 -0
  226. package/lib/routes/nankai/jwc.ts +131 -0
  227. package/lib/routes/nankai/namespace.ts +7 -0
  228. package/lib/routes/nankai/notice.ts +84 -0
  229. package/lib/routes/nankai/yzb.ts +97 -0
  230. package/lib/routes/neu/yz.ts +172 -0
  231. package/lib/routes/newslaundry/explainer.ts +30 -0
  232. package/lib/routes/newslaundry/namespace.ts +8 -0
  233. package/lib/routes/newslaundry/nl-cheatsheet.ts +30 -0
  234. package/lib/routes/newslaundry/nl-collaborations.ts +30 -0
  235. package/lib/routes/newslaundry/podcast.ts +61 -0
  236. package/lib/routes/newslaundry/reports.ts +30 -0
  237. package/lib/routes/newslaundry/shot.ts +30 -0
  238. package/lib/routes/newslaundry/subscriber-only.ts +30 -0
  239. package/lib/routes/newslaundry/templates/description.art +28 -0
  240. package/lib/routes/newslaundry/utils.ts +108 -0
  241. package/lib/routes/newswav/latest.ts +100 -0
  242. package/lib/routes/newswav/namespace.ts +7 -0
  243. package/lib/routes/nhentai/index.ts +1 -0
  244. package/lib/routes/nifd/research.ts +16 -0
  245. package/lib/routes/nio/namespace.ts +9 -0
  246. package/lib/routes/nio/nioradio.ts +75 -0
  247. package/lib/routes/njust/cs.ts +58 -0
  248. package/lib/routes/njust/utils.ts +1 -1
  249. package/lib/routes/ntdm/video.ts +1 -1
  250. package/lib/routes/nuaa/utils/pypasswaf.ts +1 -1
  251. package/lib/routes/oilchem/index.ts +1 -1
  252. package/lib/routes/oncc/money18.ts +1 -1
  253. package/lib/routes/openai/cookbook.ts +1 -1
  254. package/lib/routes/paulgraham/article.ts +2 -2
  255. package/lib/routes/picuki/profile.ts +4 -0
  256. package/lib/routes/pixiv/bookmarks.ts +1 -1
  257. package/lib/routes/pixiv/novel-api/series/sfw.ts +1 -1
  258. package/lib/routes/pixiv/novels.ts +2 -2
  259. package/lib/routes/pixiv/user.ts +1 -1
  260. package/lib/routes/playno1/av.ts +1 -0
  261. package/lib/routes/pornhub/model.ts +3 -7
  262. package/lib/routes/pornhub/pornstar.ts +2 -7
  263. package/lib/routes/pornhub/users.ts +2 -7
  264. package/lib/routes/pornhub/utils.ts +12 -1
  265. package/lib/routes/qingting/podcast.ts +2 -1
  266. package/lib/routes/qipamaijia/index.ts +1 -0
  267. package/lib/routes/questmobile/report.ts +1 -1
  268. package/lib/routes/railway/index.ts +73 -0
  269. package/lib/routes/railway/namespace.ts +8 -0
  270. package/lib/routes/reuters/common.ts +0 -3
  271. package/lib/routes/rockthejvm/articles.ts +167 -0
  272. package/lib/routes/rockthejvm/namespace.ts +9 -0
  273. package/lib/routes/rockthejvm/templates/description.art +7 -0
  274. package/lib/routes/samrdprc/namespace.ts +7 -0
  275. package/lib/routes/samrdprc/news.ts +79 -0
  276. package/lib/routes/sankei/namespace.ts +7 -0
  277. package/lib/routes/sankei/news.ts +68 -0
  278. package/lib/routes/sankei/topics.ts +71 -0
  279. package/lib/routes/sciencenet/user.ts +1 -1
  280. package/lib/routes/scoop/apps.ts +188 -0
  281. package/lib/routes/scoop/namespace.ts +9 -0
  282. package/lib/routes/scoop/templates/description.art +56 -0
  283. package/lib/routes/scpta/namespace.ts +8 -0
  284. package/lib/routes/scpta/news.ts +101 -0
  285. package/lib/routes/seekingalpha/index.ts +1 -1
  286. package/lib/routes/sehuatang/index.ts +29 -31
  287. package/lib/routes/setn/index.ts +11 -2
  288. package/lib/routes/seu/cyber/index.ts +78 -0
  289. package/lib/routes/sicau/jiaowu.ts +42 -34
  290. package/lib/routes/sis001/forum.ts +1 -0
  291. package/lib/routes/sjtu/seiee/icisee.ts +67 -0
  292. package/lib/routes/sketis/isabelle-dev/blog/index.ts +1 -1
  293. package/lib/routes/smartlink/index.ts +13 -3
  294. package/lib/routes/sohu/mp.ts +17 -6
  295. package/lib/routes/sotwe/namespace.ts +1 -0
  296. package/lib/routes/sotwe/user.ts +98 -0
  297. package/lib/routes/spankbang/new-videos.ts +1 -0
  298. package/lib/routes/spglobal/ratings.ts +1 -1
  299. package/lib/routes/stanford/blog.ts +77 -0
  300. package/lib/routes/stanford/namespace.ts +7 -0
  301. package/lib/routes/syosetu/index.ts +1 -1
  302. package/lib/routes/t66y/index.ts +1 -0
  303. package/lib/routes/taobao/mysql.ts +157 -0
  304. package/lib/routes/taobao/namespace.ts +2 -2
  305. package/lib/routes/techcrunch/category.ts +48 -0
  306. package/lib/routes/telegram/channel.ts +2 -2
  307. package/lib/routes/tesla/cx.ts +1 -1
  308. package/lib/routes/test/index.ts +1 -1
  309. package/lib/routes/themoviedb/episodes.ts +1 -1
  310. package/lib/routes/thewirehindi/category.ts +78 -0
  311. package/lib/routes/thewirehindi/index.ts +43 -0
  312. package/lib/routes/thewirehindi/namespace.ts +7 -0
  313. package/lib/routes/thewirehindi/templates/description.art +9 -0
  314. package/lib/routes/thewirehindi/utils.ts +33 -0
  315. package/lib/routes/threads/utils.ts +2 -2
  316. package/lib/routes/tidb/blog.ts +314 -0
  317. package/lib/routes/tidb/namespace.ts +9 -0
  318. package/lib/routes/tumblr/namespace.ts +17 -0
  319. package/lib/routes/tumblr/posts.ts +74 -0
  320. package/lib/routes/tumblr/utils.ts +110 -0
  321. package/lib/routes/tver/namespace.ts +7 -0
  322. package/lib/routes/tver/series.ts +98 -0
  323. package/lib/routes/twitter/api/web-api/login.ts +1 -3
  324. package/lib/routes/twitter/api/web-api/utils.ts +23 -1
  325. package/lib/routes/twitter/utils.ts +21 -21
  326. package/lib/routes/udn/breaking-news.ts +2 -2
  327. package/lib/routes/uestc/auto.ts +1 -1
  328. package/lib/routes/uestc/cqe.ts +1 -1
  329. package/lib/routes/uestc/scse.ts +1 -1
  330. package/lib/routes/uestc/sice.ts +1 -1
  331. package/lib/routes/uestc/sise.ts +1 -1
  332. package/lib/routes/upc/jwc.ts +32 -46
  333. package/lib/routes/uptimerobot/rss.ts +1 -1
  334. package/lib/routes/visionias/daily-news-summary.ts +65 -0
  335. package/lib/routes/visionias/monthly-magazine.ts +68 -0
  336. package/lib/routes/visionias/news-today.ts +3 -13
  337. package/lib/routes/visionias/utils.ts +5 -5
  338. package/lib/routes/visionias/weekly-focus.ts +2 -2
  339. package/lib/routes/wdfxw/bookfree.ts +528 -0
  340. package/lib/routes/wdfxw/namespace.ts +8 -0
  341. package/lib/routes/wdfxw/templates/description.art +13 -0
  342. package/lib/routes/whu/swrh.ts +1 -1
  343. package/lib/routes/windsurf/blog.ts +118 -0
  344. package/lib/routes/windsurf/changelog.ts +100 -0
  345. package/lib/routes/windsurf/namespace.ts +9 -0
  346. package/lib/routes/windsurf/templates/description.art +21 -0
  347. package/lib/routes/x6d/index.ts +1 -1
  348. package/lib/routes/xbookcn/blog.ts +1 -0
  349. package/lib/routes/xiaohongshu/util.ts +1 -3
  350. package/lib/routes/xiaoyuzhou/pickup.ts +1 -1
  351. package/lib/routes/ximalaya/album.ts +32 -70
  352. package/lib/routes/ximalaya/types.ts +48 -0
  353. package/lib/routes/ximalaya/utils.ts +9 -7
  354. package/lib/routes/xmanhua/index.ts +1 -0
  355. package/lib/routes/xueqiu/cookies.ts +1 -1
  356. package/lib/routes/xueqiu/user.ts +1 -1
  357. package/lib/routes/xwenming/index.ts +184 -0
  358. package/lib/routes/xwenming/namespace.ts +9 -0
  359. package/lib/routes/yahoo/news/utils.ts +1 -1
  360. package/lib/routes/yande/post.ts +3 -0
  361. package/lib/routes/ymgal/game.ts +1 -0
  362. package/lib/routes/youtube/api/google.ts +27 -0
  363. package/lib/routes/youtube/api/youtubei.ts +70 -20
  364. package/lib/routes/youtube/community.ts +3 -1
  365. package/lib/routes/youtube/utils.ts +16 -14
  366. package/lib/routes/zaobao/util.ts +8 -5
  367. package/lib/routes/zhihu/activities.ts +3 -0
  368. package/lib/routes/zhihu/execlib/x-zse-96-v3.ts +5 -5
  369. package/lib/routes/zhizhuan100/namespace.ts +7 -0
  370. package/lib/routes/zhizhuan100/report.ts +79 -0
  371. package/lib/routes/zimuxia/portfolio.ts +1 -1
  372. package/lib/routes/zju/sis/index.ts +161 -0
  373. package/lib/server.ts +5 -0
  374. package/lib/types.ts +58 -54
  375. package/lib/utils/helpers.ts +1 -1
  376. package/lib/utils/logger.ts +1 -1
  377. package/lib/utils/proxy/index.ts +102 -18
  378. package/lib/utils/proxy/multi-proxy.ts +139 -0
  379. package/lib/utils/proxy/unify-proxy.ts +3 -1
  380. package/lib/utils/puppeteer-utils.test.ts +1 -1
  381. package/lib/utils/puppeteer.test.ts +14 -27
  382. package/lib/utils/puppeteer.ts +51 -37
  383. package/lib/utils/readable-social.test.ts +65 -0
  384. package/lib/utils/readable-social.ts +15 -1
  385. package/lib/utils/request-rewriter/fetch.ts +42 -4
  386. package/package.json +54 -56
  387. package/lib/routes/mixcloud/queries.ts +0 -2274
  388. package/lib/routes-deprecated/dev.to/top.js +0 -40
@@ -0,0 +1,161 @@
1
+ import { DataItem, Route } from '@/types';
2
+ import cache from '@/utils/cache';
3
+ import got from '@/utils/got';
4
+ import { load } from 'cheerio';
5
+ import { parseDate } from '@/utils/parse-date';
6
+
7
+ const base = 'http://www.sis.zju.edu.cn/sischinese/';
8
+
9
+ /**
10
+ * Map of category types to their corresponding IDs and titles
11
+ * Used to generate URLs and titles for different news categories
12
+ */
13
+ const categoryMap = new Map([
14
+ [0, { id: '12614/list.htm', title: '浙江大学外国语学院-重要公告' }],
15
+ [1, { id: '12616/list.htm', title: '浙江大学外国语学院-最新通知' }],
16
+ [2, { id: '12617/list.htm', title: '浙江大学外国语学院-教育教学' }],
17
+ [3, { id: '12618/list.htm', title: '浙江大学外国语学院-科学研究' }],
18
+ [4, { id: '12619/list.htm', title: '浙江大学外国语学院-新闻动态' }],
19
+ [5, { id: '12620/list.htm', title: '浙江大学外国语学院-联系我们' }],
20
+ [6, { id: '12554/list.htm', title: '浙江大学外国语学院-党政管理' }],
21
+ [7, { id: '12563/list.htm', title: '浙江大学外国语学院-组织人事' }],
22
+ [8, { id: '12572/list.htm', title: '浙江大学外国语学院-科学研究' }],
23
+ [9, { id: '12577/list.htm', title: '浙江大学外国语学院-本科教育' }],
24
+ [10, { id: '12541/list.htm', title: '浙江大学外国语学院-研究生教育' }],
25
+ [11, { id: '12542/list.htm', title: '浙江大学外国语学院-学生思政' }],
26
+ [12, { id: 'xyll/list.htm', title: '浙江大学外国语学院-校友联络' }],
27
+ [13, { id: '12609/list.htm', title: '浙江大学外国语学院-对外交流' }],
28
+ ]);
29
+
30
+ /**
31
+ * Fetches and parses news items from a specific category page
32
+ * @param categoryId - The category ID to fetch news from
33
+ * @returns Promise<DataItem[]> - Array of news items with basic information
34
+ */
35
+ async function fetchNewsItemsByCategory(categoryId: string): Promise<DataItem[]> {
36
+ const response = await got({
37
+ method: 'get',
38
+ url: `${base}${categoryId}`,
39
+ });
40
+
41
+ const $ = load(response.data);
42
+ const newsItems = $('.news_list').find('li');
43
+
44
+ return newsItems.toArray().map((item) => {
45
+ const element = $(item);
46
+ const href = element.find('a').attr('href');
47
+ let title = element.find('a').attr('title');
48
+ if (!title) {
49
+ // If the title is not found, try to extract it from the link text
50
+ title = element.find('a').text().trim();
51
+ }
52
+
53
+ return {
54
+ title,
55
+ pubDate: parseDate(element.find('.news_meta').text()),
56
+ link: href ? new URL(href, base).href : undefined,
57
+ };
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Enriches a news item with detailed content by fetching its full page
63
+ * @param item - The basic news item to enrich
64
+ * @param refererUrl - The referer URL to use when fetching the item details
65
+ * @returns Promise<DataItem> - The enriched news item with description, author, and full title
66
+ */
67
+ async function enrichNewsItemWithDetails(item: DataItem, refererUrl: string): Promise<DataItem> {
68
+ // If the item doesn't have a link, return the item as-is
69
+ if (!item.link) {
70
+ return item;
71
+ }
72
+
73
+ return await cache.tryGet(item.link, async () => {
74
+ try {
75
+ // Some news items may link to pages that require ZJU private network access
76
+ // or university account authentication. We'll handle these gracefully.
77
+ const response = await got({
78
+ method: 'get',
79
+ url: item.link,
80
+ headers: {
81
+ Referer: refererUrl,
82
+ },
83
+ });
84
+
85
+ const $ = load(response.data);
86
+
87
+ // Extract and set the full article content as description
88
+ const description = $('.wp_articlecontent').html();
89
+ if (description) {
90
+ item.description = description;
91
+ }
92
+
93
+ // Extract and clean the author information
94
+ let author = $('.arti_metas').find('.arti_publisher').text();
95
+ // Remove the '发布者:' (Publisher:) prefix and trim whitespace
96
+ author = author.replace('发布者:', '').trim();
97
+ if (author) {
98
+ item.author = author;
99
+ }
100
+
101
+ return item;
102
+ } catch {
103
+ // If there's an error accessing the page (network issues, authentication required, etc.),
104
+ // return the item with only the basic information we already have
105
+ return item;
106
+ }
107
+ });
108
+ }
109
+
110
+ export const route: Route = {
111
+ path: '/sis/:type',
112
+ categories: ['university'],
113
+ example: '/zju/sis/0',
114
+ parameters: { type: '分类,见下表' },
115
+ features: {
116
+ requireConfig: false,
117
+ requirePuppeteer: false,
118
+ antiCrawler: false,
119
+ supportBT: false,
120
+ supportPodcast: false,
121
+ supportScihub: false,
122
+ },
123
+ name: '外国语学院',
124
+ description: `| 重要公告 | 最新通知 | 教育教学 | 科学研究 | 新闻动态 | 联系我们 | 党政管理 | 组织人事 | 科学研究 | 本科教育 | 研究生教育 | 学生思政 | 校友联络 | 对外交流 |
125
+ | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
126
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
127
+ `,
128
+ maintainers: ['Alex222222222222'],
129
+ handler: handleSisRequest,
130
+ url: 'www.sis.zju.edu.cn',
131
+ };
132
+
133
+ /**
134
+ * Main handler function for processing SIS (School of International Studies) news requests
135
+ * @param ctx - The request context containing route parameters
136
+ * @returns Promise with RSS feed data including title, link, and news items
137
+ */
138
+ async function handleSisRequest(ctx: { req: { param: (arg0: string) => string } }) {
139
+ const requestedType = Number.parseInt(ctx.req.param('type'));
140
+ const categoryInfo = categoryMap.get(requestedType);
141
+
142
+ // Validate the requested category type
143
+ if (!categoryInfo) {
144
+ const validTypes = [...categoryMap.keys()].join(', ');
145
+ throw new Error(`Invalid type: ${requestedType}. Valid types are: ${validTypes}`);
146
+ }
147
+
148
+ const categoryUrl = `${base}${categoryInfo.id}`;
149
+
150
+ // Fetch news items from all relevant categories
151
+ const allNewsItems = await fetchNewsItemsByCategory(categoryInfo.id);
152
+
153
+ // Enrich each news item with detailed content
154
+ const enrichedItems = await Promise.all(allNewsItems.map((item) => enrichNewsItemWithDetails(item, categoryUrl)));
155
+
156
+ return {
157
+ title: categoryInfo.title,
158
+ link: categoryUrl,
159
+ item: enrichedItems,
160
+ };
161
+ }
package/lib/server.ts ADDED
@@ -0,0 +1,5 @@
1
+ // This file is for compatibility with Vercel's deployment.
2
+
3
+ import '@/utils/request-rewriter';
4
+
5
+ export default (await import('./app-bootstrap')).default;
package/lib/types.ts CHANGED
@@ -2,7 +2,7 @@ import type { Context } from 'hono';
2
2
 
3
3
  // Make sure it's synchronise with scripts/workflow/data.ts
4
4
  // and lib/routes/rsshub/routes.ts
5
- type Category =
5
+ export type Category =
6
6
  | 'popular'
7
7
  | 'social-media'
8
8
  | 'new-media'
@@ -98,71 +98,89 @@ export type Data = {
98
98
  ttl?: number;
99
99
  };
100
100
 
101
- type Language =
101
+ export type Language =
102
102
  | 'af'
103
- | 'sq'
104
- | 'eu'
105
- | 'be'
106
- | 'bg'
107
- | 'ca'
108
- | 'zh-CN'
109
- | 'zh-TW'
110
- | 'zh-HK'
111
- | 'hr'
112
- | 'cs'
113
103
  | 'ar-DZ'
114
- | 'ar-SA'
115
- | 'ar-MA'
116
104
  | 'ar-IQ'
117
105
  | 'ar-KW'
106
+ | 'ar-MA'
107
+ | 'ar-SA'
118
108
  | 'ar-TN'
109
+ | 'be'
110
+ | 'bg'
111
+ | 'ca'
112
+ | 'cs'
119
113
  | 'da'
120
- | 'nl'
121
- | 'nl-be'
122
- | 'nl-nl'
114
+ | 'de'
115
+ | 'de-at'
116
+ | 'de-ch'
117
+ | 'de-de'
118
+ | 'de-li'
119
+ | 'de-lu'
120
+ | 'el'
123
121
  | 'en'
124
122
  | 'en-au'
125
123
  | 'en-bz'
126
124
  | 'en-ca'
125
+ | 'en-gb'
127
126
  | 'en-ie'
128
127
  | 'en-jm'
129
128
  | 'en-nz'
130
129
  | 'en-ph'
131
- | 'en-za'
132
130
  | 'en-tt'
133
- | 'en-gb'
134
131
  | 'en-us'
132
+ | 'en-za'
135
133
  | 'en-zw'
134
+ | 'es'
135
+ | 'es-ar'
136
+ | 'es-bo'
137
+ | 'es-cl'
138
+ | 'es-co'
139
+ | 'es-cr'
140
+ | 'es-do'
141
+ | 'es-ec'
142
+ | 'es-es'
143
+ | 'es-gt'
144
+ | 'es-hn'
145
+ | 'es-mx'
146
+ | 'es-ni'
147
+ | 'es-pa'
148
+ | 'es-pe'
149
+ | 'es-pr'
150
+ | 'es-py'
151
+ | 'es-sv'
152
+ | 'es-uy'
153
+ | 'es-ve'
136
154
  | 'et'
137
- | 'fo'
155
+ | 'eu'
138
156
  | 'fi'
157
+ | 'fo'
139
158
  | 'fr'
140
159
  | 'fr-be'
141
160
  | 'fr-ca'
161
+ | 'fr-ch'
142
162
  | 'fr-fr'
143
163
  | 'fr-lu'
144
164
  | 'fr-mc'
145
- | 'fr-ch'
146
- | 'gl'
165
+ | 'ga'
147
166
  | 'gd'
148
- | 'de'
149
- | 'de-at'
150
- | 'de-de'
151
- | 'de-li'
152
- | 'de-lu'
153
- | 'de-ch'
154
- | 'el'
167
+ | 'gl'
155
168
  | 'haw'
169
+ | 'hi'
170
+ | 'hr'
156
171
  | 'hu'
157
- | 'is'
158
172
  | 'in'
159
- | 'ga'
173
+ | 'is'
160
174
  | 'it'
161
- | 'it-it'
162
175
  | 'it-ch'
176
+ | 'it-it'
163
177
  | 'ja'
164
178
  | 'ko'
165
179
  | 'mk'
180
+ | 'ne'
181
+ | 'nl'
182
+ | 'nl-be'
183
+ | 'nl-nl'
166
184
  | 'no'
167
185
  | 'pl'
168
186
  | 'pt'
@@ -174,35 +192,18 @@ type Language =
174
192
  | 'ru'
175
193
  | 'ru-mo'
176
194
  | 'ru-ru'
177
- | 'sr'
178
195
  | 'sk'
179
196
  | 'sl'
180
- | 'es'
181
- | 'es-ar'
182
- | 'es-bo'
183
- | 'es-cl'
184
- | 'es-co'
185
- | 'es-cr'
186
- | 'es-do'
187
- | 'es-ec'
188
- | 'es-sv'
189
- | 'es-gt'
190
- | 'es-hn'
191
- | 'es-mx'
192
- | 'es-ni'
193
- | 'es-pa'
194
- | 'es-py'
195
- | 'es-pe'
196
- | 'es-pr'
197
- | 'es-es'
198
- | 'es-uy'
199
- | 'es-ve'
197
+ | 'sq'
198
+ | 'sr'
200
199
  | 'sv'
201
200
  | 'sv-fi'
202
201
  | 'sv-se'
203
202
  | 'tr'
204
203
  | 'uk'
205
- | 'ne'
204
+ | 'zh-CN'
205
+ | 'zh-HK'
206
+ | 'zh-TW'
206
207
  | 'other';
207
208
 
208
209
  // namespace
@@ -346,6 +347,9 @@ interface RouteItem {
346
347
 
347
348
  /** Set to `true` if the feed supports Sci-Hub */
348
349
  supportScihub?: boolean;
350
+
351
+ /** Set to `true` if this feed is not safe for work */
352
+ nsfw?: boolean;
349
353
  };
350
354
 
351
355
  /**
@@ -50,7 +50,7 @@ export function parseDuration(timeStr: string) {
50
50
  const clean = timeStr.trim().replaceAll(/[^\d:]/g, '');
51
51
  return clean
52
52
  .split(':')
53
- .reverse()
53
+ .toReversed()
54
54
  .reduce((total, part, idx) => {
55
55
  const n = Number(part);
56
56
  if (Number.isNaN(n)) {
@@ -3,7 +3,7 @@ import winston from 'winston';
3
3
  import { config } from '@/config';
4
4
 
5
5
  let transports: (typeof winston.transports.File)[] = [];
6
- if (!config.noLogfiles) {
6
+ if (!config.noLogfiles && !process.env.VERCEL) {
7
7
  transports = [
8
8
  new winston.transports.File({
9
9
  filename: path.resolve('logs/error.log'),
@@ -3,20 +3,72 @@ import { PacProxyAgent } from 'pac-proxy-agent';
3
3
  import { HttpsProxyAgent } from 'https-proxy-agent';
4
4
  import { SocksProxyAgent } from 'socks-proxy-agent';
5
5
  import { ProxyAgent } from 'undici';
6
+ import logger from '@/utils/logger';
6
7
 
7
8
  const proxyIsPAC = config.pacUri || config.pacScript;
8
9
 
9
10
  import pacProxy from './pac-proxy';
10
11
  import unifyProxy from './unify-proxy';
12
+ import createMultiProxy, { type MultiProxyResult, type ProxyState } from './multi-proxy';
13
+
14
+ interface ProxyExport {
15
+ agent: PacProxyAgent<string> | HttpsProxyAgent<string> | SocksProxyAgent | null;
16
+ dispatcher: ProxyAgent | null;
17
+ proxyUri?: string;
18
+ proxyObj: Record<string, any>;
19
+ proxyUrlHandler?: URL | null;
20
+ multiProxy?: MultiProxyResult;
21
+ getCurrentProxy: () => ProxyState | null;
22
+ markProxyFailed: (proxyUri: string) => void;
23
+ getAgentForProxy: (proxyState: ProxyState) => any;
24
+ getDispatcherForProxy: (proxyState: ProxyState) => ProxyAgent | null;
25
+ }
11
26
 
12
27
  let proxyUri: string | undefined;
13
- let proxyObj: Record<string, any> | undefined;
28
+ let proxyObj: Record<string, any> = {};
14
29
  let proxyUrlHandler: URL | null = null;
30
+ let multiProxy: MultiProxyResult | undefined;
31
+
32
+ const createAgentForProxy = (uri: string, proxyObj: Record<string, any>): any => {
33
+ if (uri.startsWith('http')) {
34
+ return new HttpsProxyAgent(uri, {
35
+ headers: {
36
+ 'proxy-authorization': proxyObj?.auth ? `Basic ${proxyObj.auth}` : undefined,
37
+ },
38
+ });
39
+ } else if (uri.startsWith('socks')) {
40
+ return new SocksProxyAgent(uri);
41
+ }
42
+ return null;
43
+ };
44
+
45
+ const createDispatcherForProxy = (uri: string, proxyObj: Record<string, any>): ProxyAgent | null => {
46
+ if (uri.startsWith('http')) {
47
+ return new ProxyAgent({
48
+ uri,
49
+ token: proxyObj?.auth ? `Basic ${proxyObj.auth}` : undefined,
50
+ requestTls: {
51
+ rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0',
52
+ },
53
+ });
54
+ }
55
+ return null;
56
+ };
57
+
15
58
  if (proxyIsPAC) {
16
59
  const proxy = pacProxy(config.pacUri, config.pacScript, config.proxy);
17
60
  proxyUri = proxy.proxyUri;
18
61
  proxyObj = proxy.proxyObj;
19
62
  proxyUrlHandler = proxy.proxyUrlHandler;
63
+ } else if (config.proxyUris && config.proxyUris.length > 0) {
64
+ multiProxy = createMultiProxy(config.proxyUris, config.proxy);
65
+ proxyObj = multiProxy.proxyObj;
66
+ const currentProxy = multiProxy.getNextProxy();
67
+ if (currentProxy) {
68
+ proxyUri = currentProxy.uri;
69
+ proxyUrlHandler = currentProxy.urlHandler;
70
+ }
71
+ logger.info(`Multi-proxy initialized with ${config.proxyUris.length} proxies`);
20
72
  } else {
21
73
  const proxy = unifyProxy(config.proxyUri, config.proxy);
22
74
  proxyUri = proxy.proxyUri;
@@ -26,31 +78,63 @@ if (proxyIsPAC) {
26
78
 
27
79
  let agent: PacProxyAgent<string> | HttpsProxyAgent<string> | SocksProxyAgent | null = null;
28
80
  let dispatcher: ProxyAgent | null = null;
29
- if (proxyIsPAC) {
81
+
82
+ if (proxyIsPAC && proxyUri) {
30
83
  agent = new PacProxyAgent(`pac+${proxyUri}`);
31
84
  } else if (proxyUri) {
32
- if (proxyUri.startsWith('http')) {
33
- agent = new HttpsProxyAgent(proxyUri, {
34
- headers: {
35
- 'proxy-authorization': config.proxy?.auth ? `Basic ${config.proxy?.auth}` : undefined,
36
- },
37
- });
38
- dispatcher = new ProxyAgent({
85
+ agent = createAgentForProxy(proxyUri, proxyObj);
86
+ dispatcher = createDispatcherForProxy(proxyUri, proxyObj);
87
+ }
88
+
89
+ const getCurrentProxy = (): ProxyState | null => {
90
+ if (multiProxy) {
91
+ return multiProxy.getNextProxy();
92
+ }
93
+ if (proxyUri) {
94
+ return {
39
95
  uri: proxyUri,
40
- token: config.proxy?.auth ? `Basic ${config.proxy?.auth}` : undefined,
41
- requestTls: {
42
- rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0',
43
- },
44
- });
45
- } else if (proxyUri.startsWith('socks')) {
46
- agent = new SocksProxyAgent(proxyUri);
96
+ isActive: true,
97
+ failureCount: 0,
98
+ urlHandler: proxyUrlHandler,
99
+ };
47
100
  }
48
- }
101
+ return null;
102
+ };
49
103
 
50
- export default {
104
+ const markProxyFailed = (failedProxyUri: string) => {
105
+ if (multiProxy) {
106
+ multiProxy.markProxyFailed(failedProxyUri);
107
+ const nextProxy = multiProxy.getNextProxy();
108
+ if (nextProxy) {
109
+ proxyUri = nextProxy.uri;
110
+ proxyUrlHandler = nextProxy.urlHandler || null;
111
+ agent = createAgentForProxy(nextProxy.uri, proxyObj);
112
+ dispatcher = createDispatcherForProxy(nextProxy.uri, proxyObj);
113
+ logger.info(`Switched to proxy: ${nextProxy.uri}`);
114
+ } else {
115
+ logger.warn('No available proxies remaining');
116
+ agent = null;
117
+ dispatcher = null;
118
+ proxyUri = undefined;
119
+ }
120
+ }
121
+ };
122
+
123
+ const getAgentForProxy = (proxyState: ProxyState) => createAgentForProxy(proxyState.uri, proxyObj);
124
+
125
+ const getDispatcherForProxy = (proxyState: ProxyState) => createDispatcherForProxy(proxyState.uri, proxyObj);
126
+
127
+ const proxyExport: ProxyExport = {
51
128
  agent,
52
129
  dispatcher,
53
130
  proxyUri,
54
131
  proxyObj,
55
132
  proxyUrlHandler,
133
+ multiProxy,
134
+ getCurrentProxy,
135
+ markProxyFailed,
136
+ getAgentForProxy,
137
+ getDispatcherForProxy,
56
138
  };
139
+
140
+ export default proxyExport;
@@ -0,0 +1,139 @@
1
+ import { type Config } from '@/config';
2
+ import logger from '@/utils/logger';
3
+ import unifyProxy from './unify-proxy';
4
+
5
+ export interface ProxyState {
6
+ uri: string;
7
+ isActive: boolean;
8
+ failureCount: number;
9
+ lastFailureTime?: number;
10
+ agent?: any;
11
+ dispatcher?: any;
12
+ urlHandler?: URL | null;
13
+ }
14
+
15
+ export interface MultiProxyResult {
16
+ currentProxy?: ProxyState | null;
17
+ allProxies: ProxyState[];
18
+ proxyObj: Config['proxy'];
19
+ getNextProxy: () => ProxyState | null;
20
+ markProxyFailed: (proxyUri: string) => void;
21
+ resetProxy: (proxyUri: string) => void;
22
+ }
23
+
24
+ const createMultiProxy = (proxyUris: string[], proxyObj: Config['proxy']): MultiProxyResult => {
25
+ const proxies: ProxyState[] = [];
26
+ let currentProxyIndex = 0;
27
+
28
+ for (const uri of proxyUris) {
29
+ const unifiedProxy = unifyProxy(uri, proxyObj);
30
+ if (unifiedProxy.proxyUri) {
31
+ proxies.push({
32
+ uri: unifiedProxy.proxyUri,
33
+ isActive: true,
34
+ failureCount: 0,
35
+ urlHandler: unifiedProxy.proxyUrlHandler,
36
+ });
37
+ }
38
+ }
39
+
40
+ if (proxies.length === 0) {
41
+ logger.warn('No valid proxies found in the provided list');
42
+ return {
43
+ allProxies: [],
44
+ proxyObj: proxyObj || {},
45
+ getNextProxy: () => null,
46
+ markProxyFailed: () => {},
47
+ resetProxy: () => {},
48
+ };
49
+ }
50
+
51
+ const healthCheckInterval = proxyObj?.healthCheckInterval || 60000;
52
+ const maxFailures = 3;
53
+
54
+ const healthCheck = () => {
55
+ const now = Date.now();
56
+ for (const proxy of proxies) {
57
+ if (!proxy.isActive && proxy.lastFailureTime && now - proxy.lastFailureTime > healthCheckInterval) {
58
+ proxy.isActive = true;
59
+ proxy.failureCount = 0;
60
+ delete proxy.lastFailureTime;
61
+ logger.info(`Proxy ${proxy.uri} marked as active again after health check`);
62
+ }
63
+ }
64
+ };
65
+
66
+ setInterval(healthCheck, healthCheckInterval);
67
+
68
+ const getNextProxy = (): ProxyState | null => {
69
+ const activeProxies = proxies.filter((p) => p.isActive);
70
+ if (activeProxies.length === 0) {
71
+ logger.warn('No active proxies available');
72
+ return null;
73
+ }
74
+
75
+ let nextProxy = activeProxies[currentProxyIndex % activeProxies.length];
76
+ let attempts = 0;
77
+
78
+ while (!nextProxy.isActive && attempts < activeProxies.length) {
79
+ currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length;
80
+ nextProxy = activeProxies[currentProxyIndex];
81
+ attempts++;
82
+ }
83
+
84
+ if (!nextProxy.isActive) {
85
+ return null;
86
+ }
87
+
88
+ return nextProxy;
89
+ };
90
+
91
+ const markProxyFailed = (proxyUri: string) => {
92
+ const proxy = proxies.find((p) => p.uri === proxyUri);
93
+ if (proxy) {
94
+ proxy.failureCount++;
95
+ proxy.lastFailureTime = Date.now();
96
+ if (proxy.failureCount >= maxFailures) {
97
+ proxy.isActive = false;
98
+ logger.warn(`Proxy ${proxyUri} marked as inactive after ${maxFailures} failures`);
99
+ } else {
100
+ logger.warn(`Proxy ${proxyUri} failed (${proxy.failureCount}/${maxFailures})`);
101
+ }
102
+
103
+ const activeProxies = proxies.filter((p) => p.isActive);
104
+ if (activeProxies.length > 0) {
105
+ currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length;
106
+ const nextProxy = getNextProxy();
107
+ if (nextProxy) {
108
+ logger.info(`Switching to proxy: ${nextProxy.uri}`);
109
+ }
110
+ }
111
+ }
112
+ };
113
+
114
+ const resetProxy = (proxyUri: string) => {
115
+ const proxy = proxies.find((p) => p.uri === proxyUri);
116
+ if (proxy) {
117
+ proxy.isActive = true;
118
+ proxy.failureCount = 0;
119
+ delete proxy.lastFailureTime;
120
+ logger.info(`Proxy ${proxyUri} manually reset`);
121
+ }
122
+ };
123
+
124
+ const currentProxy = getNextProxy();
125
+ if (currentProxy) {
126
+ logger.info(`Initial proxy selected: ${currentProxy.uri}`);
127
+ }
128
+
129
+ return {
130
+ currentProxy,
131
+ allProxies: proxies,
132
+ proxyObj: proxyObj || {},
133
+ getNextProxy,
134
+ markProxyFailed,
135
+ resetProxy,
136
+ };
137
+ };
138
+
139
+ export default createMultiProxy;
@@ -4,7 +4,7 @@ import logger from '@/utils/logger';
4
4
  const defaultProtocol = 'http';
5
5
  const possibleProtocol = ['http', 'https', 'socks', 'socks4', 'socks4a', 'socks5', 'socks5h'];
6
6
 
7
- const unifyProxy = (proxyUri: Config['proxyUri'], proxyObj: Config['proxy']) => {
7
+ const unifyProxy = (proxyUri: Config['proxyUri'] | string, proxyObj: Config['proxy']) => {
8
8
  proxyObj = proxyObj || {};
9
9
  const [oriProxyUri, oriProxyObj] = [proxyUri, proxyObj];
10
10
  proxyObj = { ...proxyObj };
@@ -110,4 +110,6 @@ const unifyProxy = (proxyUri: Config['proxyUri'], proxyObj: Config['proxy']) =>
110
110
  return { proxyUri, proxyObj, proxyUrlHandler };
111
111
  };
112
112
 
113
+ export const unifyProxies = (proxyUris: string[], proxyObj: Config['proxy']) => proxyUris.map((uri) => unifyProxy(uri, proxyObj)).filter((result) => result.proxyUri);
114
+
113
115
  export default unifyProxy;
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it, vi, afterEach } from 'vitest';
2
2
  import { parseCookieArray, constructCookieArray, setCookies, getCookies } from '@/utils/puppeteer-utils';
3
3
  import puppeteer from '@/utils/puppeteer';
4
- import type { Browser } from 'puppeteer';
4
+ import type { Browser } from 'rebrowser-puppeteer';
5
5
 
6
6
  let browser: Browser | null = null;
7
7