rsshub 1.0.0-master.f9003f9 → 1.0.0-master.f917bdc

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 (532) hide show
  1. package/README.md +1 -0
  2. package/lib/api/category/one.ts +54 -0
  3. package/lib/api/index.ts +10 -4
  4. package/lib/app.tsx +2 -0
  5. package/lib/config.ts +43 -17
  6. package/lib/errors/index.test.ts +9 -5
  7. package/lib/errors/index.tsx +11 -4
  8. package/lib/index.ts +5 -2
  9. package/lib/middleware/anti-hotlink.test.ts +6 -6
  10. package/lib/middleware/cache.ts +21 -6
  11. package/lib/middleware/logger.ts +5 -1
  12. package/lib/middleware/template.tsx +15 -11
  13. package/lib/middleware/trace.ts +25 -0
  14. package/lib/registry.test.ts +8 -0
  15. package/lib/registry.ts +10 -2
  16. package/lib/router.js +0 -9
  17. package/lib/routes/141jav/index.ts +25 -21
  18. package/lib/routes/141ppv/index.ts +24 -20
  19. package/lib/routes/2048/index.ts +13 -10
  20. package/lib/routes/3dmgame/news-center.ts +1 -1
  21. package/lib/routes/51read/article.ts +82 -0
  22. package/lib/routes/51read/namespace.ts +6 -0
  23. package/lib/routes/69shu/article.ts +8 -8
  24. package/lib/routes/69shu/namespace.ts +1 -1
  25. package/lib/routes/academia/namespace.ts +6 -0
  26. package/lib/routes/academia/topics.ts +49 -0
  27. package/lib/routes/alistapart/utils.ts +2 -2
  28. package/lib/routes/aljazeera/index.ts +2 -2
  29. package/lib/routes/alpinelinux/namespace.ts +11 -0
  30. package/lib/routes/alpinelinux/pkgs.ts +104 -0
  31. package/lib/routes/apnews/rss.ts +10 -4
  32. package/lib/routes/apnews/topics.ts +11 -5
  33. package/lib/routes/apnews/utils.ts +36 -12
  34. package/lib/routes/apple/apps.ts +30 -8
  35. package/lib/routes/asmr-200/index.ts +66 -0
  36. package/lib/routes/asmr-200/namespace.ts +6 -0
  37. package/lib/routes/asmr-200/templates/work.art +7 -0
  38. package/lib/routes/asmr-200/type.ts +96 -0
  39. package/lib/routes/autocentre/index.ts +29 -0
  40. package/lib/routes/autocentre/namespace.ts +7 -0
  41. package/lib/routes/bbc/index.ts +3 -1
  42. package/lib/routes/bc3ts/list.ts +72 -0
  43. package/lib/routes/bc3ts/namespace.ts +7 -0
  44. package/lib/routes/bc3ts/templates/media.art +10 -0
  45. package/lib/routes/bc3ts/types.ts +118 -0
  46. package/lib/routes/beijingprice/index.ts +193 -0
  47. package/lib/routes/beijingprice/namespace.ts +8 -0
  48. package/lib/routes/bilibili/api-interface.d.ts +1 -0
  49. package/lib/routes/bilibili/dynamic.ts +35 -18
  50. package/lib/routes/bilibili/namespace.ts +1 -1
  51. package/lib/routes/bilibili/ranking.ts +99 -8
  52. package/lib/routes/bilibili/video.ts +3 -5
  53. package/lib/routes/bing/daily-wallpaper.ts +57 -18
  54. package/lib/routes/bjnews/cat.ts +43 -0
  55. package/lib/routes/bjnews/column.ts +43 -0
  56. package/lib/routes/bjnews/namespace.ts +6 -0
  57. package/lib/routes/bjnews/utils.ts +19 -0
  58. package/lib/routes/bjx/fd.ts +63 -0
  59. package/lib/routes/bjx/namespace.ts +1 -1
  60. package/lib/routes/bluearchive/namespace.ts +7 -0
  61. package/lib/routes/bluearchive/news.ts +88 -0
  62. package/lib/routes/bt0/mv.ts +65 -0
  63. package/lib/routes/bt0/namespace.ts +9 -0
  64. package/lib/routes/bt0/tlist.ts +67 -0
  65. package/lib/routes/bt0/util.ts +51 -0
  66. package/lib/routes/buaa/jiaowu.ts +124 -0
  67. package/lib/routes/buaa/lib/space/newbook.ts +171 -0
  68. package/lib/routes/buaa/lib/space/templates/newbook.art +44 -0
  69. package/lib/routes/buaa/news/index.ts +15 -13
  70. package/lib/routes/buaa/sme.ts +5 -3
  71. package/lib/routes/bugzilla/bug.ts +59 -0
  72. package/lib/routes/bugzilla/namespace.ts +11 -0
  73. package/lib/routes/cara/constant.ts +5 -0
  74. package/lib/routes/cara/likes.ts +56 -0
  75. package/lib/routes/cara/namespace.ts +6 -0
  76. package/lib/routes/cara/portfolio.ts +40 -0
  77. package/lib/routes/cara/templates/post.art +6 -0
  78. package/lib/routes/cara/timeline.ts +56 -0
  79. package/lib/routes/cara/types.ts +45 -0
  80. package/lib/routes/cara/utils.ts +62 -0
  81. package/lib/routes/cbaigui/utils.ts +1 -1
  82. package/lib/routes/cccfna/index.ts +82 -0
  83. package/lib/routes/cccfna/namespace.ts +6 -0
  84. package/lib/routes/cffex/announcement.ts +73 -0
  85. package/lib/routes/cffex/namespace.ts +6 -0
  86. package/lib/routes/cfr/index.ts +58 -0
  87. package/lib/routes/cfr/namespace.ts +6 -0
  88. package/lib/routes/cfr/types.ts +30 -0
  89. package/lib/routes/cfr/utils.ts +284 -0
  90. package/lib/routes/chaping/newsflash.ts +2 -2
  91. package/lib/routes/chlinlearn/daily-blog.ts +48 -0
  92. package/lib/routes/chlinlearn/namespcae.ts +6 -0
  93. package/lib/routes/cnbc/rss.ts +1 -1
  94. package/lib/routes/codeforces/contests.ts +2 -2
  95. package/lib/routes/codeforces/recent-actions.ts +2 -2
  96. package/lib/routes/copymanga/comic.ts +3 -1
  97. package/lib/routes/dangdang/namespace.ts +6 -0
  98. package/lib/routes/dangdang/notice.ts +69 -0
  99. package/lib/routes/dataguidance/index.ts +66 -0
  100. package/lib/routes/dataguidance/namespace.ts +7 -0
  101. package/lib/routes/dblp/publication.ts +4 -6
  102. package/lib/routes/deadbydaylight/index.ts +69 -0
  103. package/lib/routes/deadbydaylight/namespace.ts +12 -0
  104. package/lib/routes/devolverdigital/blog.ts +59 -42
  105. package/lib/routes/dewu/declaration.ts +86 -0
  106. package/lib/routes/dewu/namespace.ts +6 -0
  107. package/lib/routes/dhu/jiaowu/news.ts +53 -24
  108. package/lib/routes/dhu/namespace.ts +1 -1
  109. package/lib/routes/dhu/xxgk/news.ts +48 -20
  110. package/lib/routes/digitalcameraworld/news.ts +1 -1
  111. package/lib/routes/discord/channel.ts +10 -10
  112. package/lib/routes/discord/discord-api.ts +27 -32
  113. package/lib/routes/discord/templates/message.art +15 -0
  114. package/lib/routes/discourse/notifications.ts +4 -2
  115. package/lib/routes/dlnews/utils.ts +2 -2
  116. package/lib/routes/dockerhub/build.ts +11 -6
  117. package/lib/routes/domp4/detail.ts +1 -1
  118. package/lib/routes/domp4/latest-movie-bt.ts +71 -0
  119. package/lib/routes/dongqiudi/result.ts +1 -1
  120. package/lib/routes/douban/other/group.ts +24 -3
  121. package/lib/routes/duckdb/namespace.ts +6 -0
  122. package/lib/routes/duckdb/news.ts +65 -0
  123. package/lib/routes/dushu/fuzhou/index.ts +10 -11
  124. package/lib/routes/epicgames/index.ts +14 -4
  125. package/lib/routes/famitsu/category.ts +110 -43
  126. package/lib/routes/famitsu/templates/description.art +7 -0
  127. package/lib/routes/famitsu/types.ts +117 -0
  128. package/lib/routes/fanbox/namespace.ts +1 -1
  129. package/lib/routes/fanqienovel/namespace.ts +7 -0
  130. package/lib/routes/fanqienovel/page.ts +100 -0
  131. package/lib/routes/fansly/post.ts +1 -2
  132. package/lib/routes/fansly/tag.ts +1 -2
  133. package/lib/routes/fansly/utils.ts +5 -4
  134. package/lib/routes/fashionnetwork/index.ts +192 -0
  135. package/lib/routes/fashionnetwork/namespace.ts +8 -0
  136. package/lib/routes/fashionnetwork/templates/description.art +17 -0
  137. package/lib/routes/fediverse/namespace.ts +6 -0
  138. package/lib/routes/fediverse/timeline.ts +98 -0
  139. package/lib/routes/finviz/news.ts +4 -4
  140. package/lib/routes/foresightnews/util.ts +1 -1
  141. package/lib/routes/fortunechina/index.ts +24 -14
  142. package/lib/routes/freebuf/index.ts +3 -3
  143. package/lib/routes/ftchinese/channel.ts +3 -3
  144. package/lib/routes/gdut/oa-news.ts +12 -17
  145. package/lib/routes/getitfree/util.ts +1 -1
  146. package/lib/routes/github/activity.ts +55 -0
  147. package/lib/routes/github/issue.ts +26 -3
  148. package/lib/routes/github/search.ts +20 -18
  149. package/lib/routes/github/star.ts +4 -3
  150. package/lib/routes/github/trending.ts +27 -5
  151. package/lib/routes/google/citations.ts +2 -2
  152. package/lib/routes/google/research.ts +45 -0
  153. package/lib/routes/google/scholar.ts +2 -2
  154. package/lib/routes/gov/beijing/bphc/index.ts +3 -3
  155. package/lib/routes/gov/immiau/news.ts +2 -2
  156. package/lib/routes/gov/sh/fgw/index.ts +155 -0
  157. package/lib/routes/gov/sh/fgw/templates/description.art +17 -0
  158. package/lib/routes/gov/sh/namespace.ts +8 -0
  159. package/lib/routes/gov/{shanghai → sh}/rsj/ksxm.ts +2 -2
  160. package/lib/routes/gov/{shanghai → sh}/wgj/wgj.ts +3 -3
  161. package/lib/routes/gov/{shanghai → sh}/wsjkw/yqtb/index.ts +2 -2
  162. package/lib/routes/gov/{shanghai → sh}/yjj/index.ts +3 -3
  163. package/lib/routes/gov/stats/index.ts +8 -8
  164. package/lib/routes/gov/zhengce/index.ts +3 -6
  165. package/lib/routes/gov/zj/ningbogzw-notice.ts +50 -0
  166. package/lib/routes/gov/zj/ningborsjnotice.ts +50 -0
  167. package/lib/routes/gq/news.ts +1 -1
  168. package/lib/routes/grist/utils.ts +2 -2
  169. package/lib/routes/guokr/utils.ts +1 -1
  170. package/lib/routes/healthz.ts +8 -0
  171. package/lib/routes/hitcon/zeroday.ts +1 -1
  172. package/lib/routes/hket/index.ts +80 -76
  173. package/lib/routes/hrbeu/job/calendar.ts +8 -6
  174. package/lib/routes/huxiu/util.ts +2 -2
  175. package/lib/routes/hypergryph/arknights/arktca.ts +124 -0
  176. package/lib/routes/ieee/journal.ts +50 -67
  177. package/lib/routes/ieee/templates/description.art +1 -1
  178. package/lib/routes/imdb/chart.ts +61 -0
  179. package/lib/routes/imdb/namespace.ts +6 -0
  180. package/lib/routes/imdb/templates/chart.art +15 -0
  181. package/lib/routes/imdb/types.ts +103 -0
  182. package/lib/routes/imop/namespace.ts +10 -0
  183. package/lib/routes/imop/tianshu.ts +53 -0
  184. package/lib/routes/informedainews/docs.ts +78 -0
  185. package/lib/routes/informedainews/namespace.ts +18 -0
  186. package/lib/routes/inspirehep/author.ts +46 -0
  187. package/lib/routes/inspirehep/literature.ts +42 -0
  188. package/lib/routes/inspirehep/namespace.ts +7 -0
  189. package/lib/routes/inspirehep/types.ts +210 -0
  190. package/lib/routes/inspirehep/utils.ts +15 -0
  191. package/lib/routes/instagram/private-api/index.ts +28 -6
  192. package/lib/routes/investor/index.ts +213 -0
  193. package/lib/routes/investor/namespace.ts +8 -0
  194. package/lib/routes/iplaysoft/index.ts +68 -0
  195. package/lib/routes/iplaysoft/namespace.ts +6 -0
  196. package/lib/routes/j-test/namespace.ts +6 -0
  197. package/lib/routes/j-test/news.ts +64 -0
  198. package/lib/routes/japanpost/namespace.ts +7 -1
  199. package/lib/routes/japanpost/router.ts +40 -0
  200. package/lib/routes/japanpost/track.ts +1 -23
  201. package/lib/routes/javbus/index.ts +8 -1
  202. package/lib/routes/javdb/actors.ts +6 -2
  203. package/lib/routes/javdb/index.ts +4 -3
  204. package/lib/routes/javdb/series.ts +1 -1
  205. package/lib/routes/javdb/utils.ts +6 -0
  206. package/lib/routes/javdb/videocodes.ts +56 -0
  207. package/lib/routes/jike/topic.ts +1 -1
  208. package/lib/routes/jike/user.ts +1 -1
  209. package/lib/routes/jingzhengu/namespace.ts +6 -0
  210. package/lib/routes/jingzhengu/news.ts +77 -0
  211. package/lib/routes/jingzhengu/types.ts +39 -0
  212. package/lib/routes/jingzhengu/utils.ts +35 -0
  213. package/lib/routes/jinritemai/docs.ts +74 -0
  214. package/lib/routes/jinritemai/namespace.ts +6 -0
  215. package/lib/routes/jiuyangongshe/community.ts +147 -0
  216. package/lib/routes/jiuyangongshe/namespace.ts +7 -0
  217. package/lib/routes/jiuyangongshe/templates/community-description.art +4 -0
  218. package/lib/routes/jjwxc/book.ts +4 -3
  219. package/lib/routes/jlu/ccst/xwzx/index.ts +63 -0
  220. package/lib/routes/jlu/jwc.ts +55 -0
  221. package/lib/routes/jlu/namespace.ts +6 -0
  222. package/lib/routes/juejin/dynamic.ts +114 -0
  223. package/lib/routes/kadokawa/blog.ts +134 -0
  224. package/lib/routes/kadokawa/namespace.ts +8 -0
  225. package/lib/routes/kadokawa/templates/description.art +21 -0
  226. package/lib/routes/kaopu/news.ts +3 -3
  227. package/lib/routes/kelownacapnews/namespace.ts +6 -0
  228. package/lib/routes/kelownacapnews/news.ts +93 -0
  229. package/lib/routes/kemono/index.ts +21 -0
  230. package/lib/routes/keylol/index.ts +56 -11
  231. package/lib/routes/kurogames/namespace.ts +7 -0
  232. package/lib/routes/kurogames/wutheringwaves/news.ts +59 -0
  233. package/lib/routes/kyodonews/index.ts +1 -1
  234. package/lib/routes/leetcode/articles.ts +19 -21
  235. package/lib/routes/line/today.ts +1 -1
  236. package/lib/routes/linkedin/cn/renderer.ts +1 -1
  237. package/lib/routes/lmu/jobs.ts +96 -0
  238. package/lib/routes/lmu/namespace.ts +17 -0
  239. package/lib/routes/lmu/templates/jobPosting.art +11 -0
  240. package/lib/routes/lofter/user.ts +2 -1
  241. package/lib/routes/logclub/index.ts +1 -2
  242. package/lib/routes/luogu/user-blog.ts +6 -6
  243. package/lib/routes/luolei/index.ts +1 -2
  244. package/lib/routes/maccms/index.ts +72 -0
  245. package/lib/routes/maccms/namespace.ts +9 -0
  246. package/lib/routes/maccms/templates/vod.art +12 -0
  247. package/lib/routes/maccms/type.ts +102 -0
  248. package/lib/routes/mangadex/namespace.ts +1 -1
  249. package/lib/routes/manyvids/namespace.ts +7 -0
  250. package/lib/routes/manyvids/templates/video.art +3 -0
  251. package/lib/routes/manyvids/types.ts +89 -0
  252. package/lib/routes/manyvids/video.ts +51 -0
  253. package/lib/routes/matters/author.ts +63 -0
  254. package/lib/routes/matters/latest.ts +74 -0
  255. package/lib/routes/matters/namespace.ts +7 -0
  256. package/lib/routes/matters/tags.ts +85 -0
  257. package/lib/routes/matters/utils.ts +30 -0
  258. package/lib/routes/mcmod/index.ts +105 -0
  259. package/lib/routes/mcmod/namespace.ts +6 -0
  260. package/lib/routes/mcmod/templates/mod.art +13 -0
  261. package/lib/routes/meituan/namespace.ts +6 -0
  262. package/lib/routes/meituan/tech.ts +67 -0
  263. package/lib/routes/metacritic/index.ts +10 -0
  264. package/lib/routes/metrics.ts +12 -0
  265. package/lib/routes/microsoft/mcr.ts +63 -0
  266. package/lib/routes/microsoft/namespace.ts +2 -2
  267. package/lib/routes/mihoyo/bbs/img-ranking.ts +4 -4
  268. package/lib/routes/mihoyo/bbs/static-data.ts +7 -0
  269. package/lib/routes/minecraft/version.ts +1 -1
  270. package/lib/routes/mirrormedia/category.ts +101 -0
  271. package/lib/routes/mirrormedia/index.ts +43 -0
  272. package/lib/routes/mirrormedia/namespace.ts +6 -0
  273. package/lib/routes/mirrormedia/utils.ts +19 -0
  274. package/lib/routes/misskey/user-timeline.ts +42 -0
  275. package/lib/routes/misskey/utils.ts +41 -1
  276. package/lib/routes/modb/topic.ts +5 -6
  277. package/lib/routes/my-formosa/index.ts +78 -0
  278. package/lib/routes/my-formosa/namespace.ts +6 -0
  279. package/lib/routes/myfans/namespace.ts +6 -0
  280. package/lib/routes/myfans/post.ts +64 -0
  281. package/lib/routes/myfans/templates/post.art +8 -0
  282. package/lib/routes/myfans/types.ts +104 -0
  283. package/lib/routes/myfans/utils.ts +39 -0
  284. package/lib/routes/nasa/apod.ts +4 -3
  285. package/lib/routes/nasa/namespace.ts +1 -1
  286. package/lib/routes/nationalgeographic/latest-stories.ts +1 -1
  287. package/lib/routes/nationalgeographic/templates/stories.art +1 -1
  288. package/lib/routes/neea/index.ts +47 -19
  289. package/lib/routes/neea/jlpt.ts +12 -15
  290. package/lib/routes/neea/namespace.ts +2 -2
  291. package/lib/routes/newrank/utils.ts +1 -1
  292. package/lib/routes/newzmz/util.ts +1 -1
  293. package/lib/routes/nextjs/blog.ts +63 -0
  294. package/lib/routes/nextjs/namespace.ts +6 -0
  295. package/lib/routes/nga/forum.ts +3 -2
  296. package/lib/routes/nikkei/asia/index.ts +1 -10
  297. package/lib/routes/nikkei/cn/index.ts +39 -3
  298. package/lib/routes/nikkei/index.ts +5 -4
  299. package/lib/routes/nikkei/namespace.ts +1 -1
  300. package/lib/routes/nikkei/news.ts +2 -2
  301. package/lib/routes/nmc/weatheralarm.ts +1 -0
  302. package/lib/routes/notion/release.ts +81 -0
  303. package/lib/routes/npm/package.ts +15 -8
  304. package/lib/routes/nyaa/main.ts +2 -3
  305. package/lib/routes/nyaa/namespace.ts +10 -0
  306. package/lib/routes/ollama/blog.ts +39 -0
  307. package/lib/routes/ollama/models.ts +7 -4
  308. package/lib/routes/oschina/news.ts +6 -2
  309. package/lib/routes/people/index.ts +4 -2
  310. package/lib/routes/picnob/namespace.ts +1 -4
  311. package/lib/routes/picnob/user.ts +3 -2
  312. package/lib/routes/picuki/namespace.ts +1 -4
  313. package/lib/routes/picuki/profile.ts +11 -7
  314. package/lib/routes/pixiv/ranking.ts +73 -10
  315. package/lib/routes/pixiv/search.ts +58 -7
  316. package/lib/routes/pixiv/user.ts +3 -1
  317. package/lib/routes/pixiv/utils.ts +3 -3
  318. package/lib/routes/podwise/collections.ts +46 -0
  319. package/lib/routes/podwise/episodes.ts +119 -0
  320. package/lib/routes/podwise/namespace.ts +9 -0
  321. package/lib/routes/ps/monthly-games.ts +3 -2
  322. package/lib/routes/psyche/namespace.ts +6 -0
  323. package/lib/routes/psyche/templates/essay.art +3 -0
  324. package/lib/routes/psyche/templates/video.art +10 -0
  325. package/lib/routes/psyche/topic.ts +46 -0
  326. package/lib/routes/psyche/type.ts +53 -0
  327. package/lib/routes/psyche/utils.ts +107 -0
  328. package/lib/routes/pubscholar/explore.ts +62 -0
  329. package/lib/routes/pubscholar/namespace.ts +7 -0
  330. package/lib/routes/pubscholar/types.ts +41 -0
  331. package/lib/routes/pubscholar/utils.ts +44 -0
  332. package/lib/routes/pwc/sustainability.ts +53 -49
  333. package/lib/routes/qidian/chapter.ts +4 -3
  334. package/lib/routes/raycast/changelog.ts +54 -0
  335. package/lib/routes/raycast/namespace.ts +7 -0
  336. package/lib/routes/react/blog.ts +57 -0
  337. package/lib/routes/react/namespace.ts +6 -0
  338. package/lib/routes/readwise/list.ts +152 -0
  339. package/lib/routes/readwise/namespace.ts +6 -0
  340. package/lib/routes/reuters/common.ts +3 -2
  341. package/lib/routes/reuters/namespace.ts +1 -1
  342. package/lib/routes/rss3/index.ts +148 -0
  343. package/lib/routes/rss3/namespace.ts +7 -0
  344. package/lib/routes/rsshub/routes.ts +36 -14
  345. package/lib/routes/ruc/ai.ts +87 -0
  346. package/lib/routes/ruc/namespace.ts +5 -2
  347. package/lib/routes/samd/namespace.ts +6 -0
  348. package/lib/routes/samd/news.ts +74 -0
  349. package/lib/routes/sara/index.ts +1 -1
  350. package/lib/routes/sciencedirect/cf-email.ts +0 -1
  351. package/lib/routes/sehuatang/index.ts +33 -14
  352. package/lib/routes/sehuatang/user.ts +1 -1
  353. package/lib/routes/shiep/config.ts +4 -10
  354. package/lib/routes/shiep/index.ts +9 -9
  355. package/lib/routes/showstart/artist.ts +5 -4
  356. package/lib/routes/showstart/brand.ts +5 -4
  357. package/lib/routes/showstart/event.ts +8 -7
  358. package/lib/routes/showstart/search.ts +10 -8
  359. package/lib/routes/showstart/service.ts +81 -53
  360. package/lib/routes/showstart/site.ts +41 -0
  361. package/lib/routes/showstart/utils.ts +9 -8
  362. package/lib/routes/sicau/jiaowu.ts +77 -0
  363. package/lib/routes/sicau/jk.ts +210 -0
  364. package/lib/routes/sicau/namespace.ts +1 -1
  365. package/lib/routes/sjtu/jwc.ts +4 -1
  366. package/lib/routes/sjtu/yzb/zkxx.ts +36 -16
  367. package/lib/routes/smzdm/keyword.ts +3 -2
  368. package/lib/routes/smzdm/ranking.ts +180 -46
  369. package/lib/routes/spotify/artist.ts +16 -18
  370. package/lib/routes/spotify/artists-top.ts +7 -8
  371. package/lib/routes/spotify/playlist.ts +10 -10
  372. package/lib/routes/spotify/saved.ts +7 -8
  373. package/lib/routes/spotify/show.ts +8 -9
  374. package/lib/routes/spotify/tracks-top.ts +7 -8
  375. package/lib/routes/spotify/utils.ts +22 -22
  376. package/lib/routes/springer/journal.ts +10 -12
  377. package/lib/routes/sspai/author.ts +8 -2
  378. package/lib/routes/sspai/column.ts +1 -1
  379. package/lib/routes/sspai/index.ts +10 -4
  380. package/lib/routes/sspai/matrix.ts +8 -2
  381. package/lib/routes/sspai/prime-community.ts +83 -0
  382. package/lib/routes/sspai/series-update.ts +1 -1
  383. package/lib/routes/sspai/tag.ts +8 -2
  384. package/lib/routes/sspai/topic.ts +8 -2
  385. package/lib/routes/sspai/topics.ts +1 -1
  386. package/lib/routes/steam/sharefile-changelog.ts +65 -0
  387. package/lib/routes/steam/templates/workshop-search-description.art +5 -0
  388. package/lib/routes/steam/workshop-search.ts +109 -0
  389. package/lib/routes/stockedge/utils.ts +9 -11
  390. package/lib/routes/stream-capital/namespace.ts +6 -0
  391. package/lib/routes/stream-capital/search.ts +79 -0
  392. package/lib/routes/stream-capital/types.ts +44 -0
  393. package/lib/routes/stream-capital/utils.ts +18 -0
  394. package/lib/routes/sustainabilitymag/articles.ts +144 -0
  395. package/lib/routes/sustainabilitymag/namespace.ts +6 -0
  396. package/lib/routes/t66y/index.ts +29 -7
  397. package/lib/routes/techcrunch/news.ts +20 -29
  398. package/lib/routes/techcrunch/templates/description.art +7 -0
  399. package/lib/routes/telecompaper/news.ts +1 -1
  400. package/lib/routes/telegram/channel.ts +37 -31
  401. package/lib/routes/tencent/cloud/developer/column.ts +81 -0
  402. package/lib/routes/tencent/namespace.ts +2 -2
  403. package/lib/routes/tesla/cx.ts +1 -1
  404. package/lib/routes/test/index.ts +5 -1
  405. package/lib/routes/test/namespace.ts +1 -1
  406. package/lib/routes/the/index.ts +210 -0
  407. package/lib/routes/the/namespace.ts +8 -0
  408. package/lib/routes/the/templates/description.art +27 -0
  409. package/lib/routes/the/util.ts +307 -0
  410. package/lib/routes/theinitium/channel.ts +1 -1
  411. package/lib/routes/theinitium/follow.ts +1 -6
  412. package/lib/routes/theinitium/utils.ts +2 -18
  413. package/lib/routes/thoughtworks/index.ts +71 -0
  414. package/lib/routes/thoughtworks/namespace.ts +6 -0
  415. package/lib/routes/trending/all-trending.ts +2 -2
  416. package/lib/routes/tsdm39/bd.ts +101 -0
  417. package/lib/routes/tsdm39/namespace.ts +7 -0
  418. package/lib/routes/tvb/news.ts +1 -0
  419. package/lib/routes/twitter/api/index.ts +1 -1
  420. package/lib/routes/twitter/api/mobile-api/api.ts +9 -6
  421. package/lib/routes/twitter/api/mobile-api/login.ts +161 -138
  422. package/lib/routes/twitter/api/mobile-api/token.ts +16 -22
  423. package/lib/routes/twitter/api/web-api/api.ts +1 -0
  424. package/lib/routes/twitter/api/web-api/utils.ts +89 -17
  425. package/lib/routes/twitter/home.ts +2 -2
  426. package/lib/routes/twitter/keyword.ts +3 -2
  427. package/lib/routes/twitter/likes.ts +1 -1
  428. package/lib/routes/twitter/list.ts +1 -1
  429. package/lib/routes/twitter/media.ts +4 -3
  430. package/lib/routes/twitter/namespace.ts +2 -2
  431. package/lib/routes/twitter/user.ts +5 -4
  432. package/lib/routes/twitter/utils.ts +14 -5
  433. package/lib/routes/twreporter/category.ts +67 -22
  434. package/lib/routes/twreporter/fetch-article.ts +11 -5
  435. package/lib/routes/twreporter/newest.ts +2 -2
  436. package/lib/routes/typst/universe.ts +34 -8
  437. package/lib/routes/udn/breaking-news.ts +9 -0
  438. package/lib/routes/utgd/category.ts +18 -59
  439. package/lib/routes/utgd/timeline.ts +9 -45
  440. package/lib/routes/utgd/topic.ts +9 -51
  441. package/lib/routes/utgd/utils.ts +41 -0
  442. package/lib/routes/v2ex/tab.ts +3 -2
  443. package/lib/routes/v2ex/topics.ts +19 -3
  444. package/lib/routes/v2ex/xna.ts +55 -0
  445. package/lib/routes/vice/namespace.ts +6 -0
  446. package/lib/routes/vice/templates/article.art +19 -0
  447. package/lib/routes/vice/topic.ts +103 -0
  448. package/lib/routes/watasuke/blog.ts +1 -1
  449. package/lib/routes/weibo/friends.ts +1 -0
  450. package/lib/routes/weibo/group.ts +1 -0
  451. package/lib/routes/weibo/keyword.ts +3 -2
  452. package/lib/routes/weibo/namespace.ts +1 -1
  453. package/lib/routes/weibo/search/hot.ts +12 -7
  454. package/lib/routes/weibo/user-bookmarks.ts +191 -0
  455. package/lib/routes/weibo/user.ts +11 -8
  456. package/lib/routes/weibo/utils.ts +32 -2
  457. package/lib/routes/wfu/news.ts +1 -1
  458. package/lib/routes/wired/namespace.ts +7 -0
  459. package/lib/routes/wired/tag.ts +97 -0
  460. package/lib/routes/wired/types.ts +81 -0
  461. package/lib/routes/wordpress/util.ts +1 -1
  462. package/lib/routes/xiaoyuzhou/podcast.ts +2 -1
  463. package/lib/routes/ximalaya/album.ts +27 -19
  464. package/lib/routes/ximalaya/types.ts +68 -0
  465. package/lib/routes/ximalaya/utils.ts +38 -1
  466. package/lib/routes/xinhuanet/app.ts +109 -0
  467. package/lib/routes/xinhuanet/namespace.ts +8 -0
  468. package/lib/routes/xsijishe/forum.ts +23 -3
  469. package/lib/routes/xsijishe/rank.ts +23 -3
  470. package/lib/routes/yahoo/news/index.ts +149 -0
  471. package/lib/routes/yahoo/news/listid.ts +67 -0
  472. package/lib/routes/yahoo/news/{tw/provider-helper.ts → provider-helper.ts} +9 -4
  473. package/lib/routes/yahoo/news/{tw/provider.ts → provider.ts} +21 -6
  474. package/lib/routes/yahoo/news/{tw/utils.ts → utils.ts} +24 -15
  475. package/lib/routes/yicai/carousel.ts +48 -0
  476. package/lib/routes/yicai/utils.ts +23 -17
  477. package/lib/routes/ymgal/article.ts +1 -1
  478. package/lib/routes/youtube/channel.ts +1 -0
  479. package/lib/routes/youtube/custom.ts +2 -0
  480. package/lib/routes/youtube/live.ts +1 -0
  481. package/lib/routes/youtube/playlist.ts +1 -0
  482. package/lib/routes/youtube/subscriptions.ts +2 -0
  483. package/lib/routes/youtube/user.ts +16 -4
  484. package/lib/routes/zaobao/interactive.ts +2 -11
  485. package/lib/routes/zaobao/{index.ts → other.ts} +2 -10
  486. package/lib/routes/zaobao/realtime.ts +0 -8
  487. package/lib/routes/zaobao/util.ts +14 -10
  488. package/lib/routes/zcool/discover.ts +2 -1
  489. package/lib/routes/zhihu/activities.ts +2 -1
  490. package/lib/routes/zhihu/execlib/x-zse-96-v3.ts +2 -0
  491. package/lib/routes/zhihu/hot.ts +56 -7
  492. package/lib/routes/zhihu/namespace.ts +3 -0
  493. package/lib/routes/zhihu/posts.ts +20 -15
  494. package/lib/routes/zhihu/types.ts +104 -0
  495. package/lib/routes/zhihu/utils.ts +25 -4
  496. package/lib/routes/zhihu/zhuanlan.ts +8 -2
  497. package/lib/routes/zhubai/top20.ts +2 -2
  498. package/lib/routes/zhuwang/index.ts +1 -1
  499. package/lib/routes/zjgtjy/index.ts +3 -9
  500. package/lib/routes.test.ts +11 -5
  501. package/lib/types.ts +34 -2
  502. package/lib/utils/cache/index.ts +8 -0
  503. package/lib/utils/ofetch.ts +15 -1
  504. package/lib/utils/otel/index.ts +2 -0
  505. package/lib/utils/otel/metric.ts +66 -0
  506. package/lib/utils/otel/trace.ts +28 -0
  507. package/lib/utils/request-rewriter/fetch.ts +21 -2
  508. package/lib/utils/request-rewriter.test.ts +14 -0
  509. package/lib/views/atom.tsx +2 -2
  510. package/lib/views/error.tsx +4 -4
  511. package/lib/views/index.tsx +5 -4
  512. package/lib/views/json.ts +1 -1
  513. package/lib/views/layout.tsx +1 -1
  514. package/lib/views/rss.tsx +3 -2
  515. package/lib/views/rss3.test.ts +99 -0
  516. package/package.json +69 -56
  517. package/lib/routes/ieee/earlyaccess.ts +0 -100
  518. package/lib/routes/ieee/recent.ts +0 -107
  519. package/lib/routes/yahoo/news/tw/index.ts +0 -80
  520. package/lib/routes/yahoo/news/us/index.ts +0 -55
  521. package/lib/routes/zhihu/hotlist.ts +0 -72
  522. package/lib/routes-deprecated/bjnews/news.js +0 -44
  523. package/lib/routes-deprecated/iplay/home.js +0 -26
  524. package/lib/routes-deprecated/iplay/utils.js +0 -57
  525. package/lib/routes-deprecated/matters/author.js +0 -49
  526. package/lib/routes-deprecated/matters/latest.js +0 -69
  527. package/lib/routes-deprecated/matters/tags.js +0 -56
  528. /package/lib/routes/gov/{shanghai → sh}/rsj/templates/ksxm.art +0 -0
  529. /package/lib/routes/gov/{shanghai → sh}/wgj/templates/wgj.art +0 -0
  530. /package/lib/routes/hypergryph/{announce.ts → arknights/announce.ts} +0 -0
  531. /package/lib/routes/hypergryph/{japan.ts → arknights/japan.ts} +0 -0
  532. /package/lib/routes/hypergryph/{news.ts → arknights/news.ts} +0 -0
@@ -4,16 +4,13 @@ import { bearerToken, guestActivateUrl } from './constants';
4
4
  import got from '@/utils/got';
5
5
  import ofetch from '@/utils/ofetch';
6
6
  import crypto from 'crypto';
7
- import { config } from '@/config';
8
7
  import { v5 as uuidv5 } from 'uuid';
9
8
  import { authenticator } from 'otplib';
10
9
  import logger from '@/utils/logger';
11
10
  import cache from '@/utils/cache';
11
+ import { RateLimiterMemory, RateLimiterRedis, RateLimiterQueue } from 'rate-limiter-flexible';
12
12
 
13
13
  const NAMESPACE = 'd41d092b-b007-48f7-9129-e9538d2d8fe9';
14
- const username = config.twitter.username;
15
- const password = config.twitter.password;
16
- const authenticationSecret = config.twitter.authenticationSecret;
17
14
 
18
15
  let authentication = null;
19
16
 
@@ -29,157 +26,183 @@ const headers = {
29
26
  Authorization: bearerToken,
30
27
  };
31
28
 
32
- async function login() {
33
- return await cache.tryGet(
29
+ const loginLimiter = cache.clients.redisClient
30
+ ? new RateLimiterRedis({
31
+ points: 1,
32
+ duration: 20,
33
+ execEvenly: true,
34
+ storeClient: cache.clients.redisClient,
35
+ })
36
+ : new RateLimiterMemory({
37
+ points: 1,
38
+ duration: 20,
39
+ execEvenly: true,
40
+ });
41
+
42
+ const loginLimiterQueue = new RateLimiterQueue(loginLimiter);
43
+
44
+ async function login({ username, password, authenticationSecret }) {
45
+ return (await cache.tryGet(
34
46
  `twitter:authentication:${username}`,
35
47
  async () => {
36
- logger.debug('Twitter login start.');
37
- const android_id = uuidv5(username, NAMESPACE);
38
- headers['X-Twitter-Client-DeviceID'] = android_id;
39
-
40
- const ct0 = crypto.randomUUID().replaceAll('-', '');
41
- const guestToken = await got(guestActivateUrl, {
42
- headers: {
43
- authorization: bearerToken,
44
- 'x-csrf-token': ct0,
45
- cookie: 'ct0=' + ct0,
46
- },
47
- method: 'POST',
48
- });
49
- logger.debug('Twitter login 1 finished: guest token.');
50
-
51
- headers['x-guest-token'] = guestToken.data.guest_token;
52
-
53
- const task1 = await ofetch.raw(
54
- 'https://api.x.com/1.1/onboarding/task.json?' +
55
- new URLSearchParams({
56
- flow_name: 'login',
57
- api_version: '1',
58
- known_device_token: '',
59
- sim_country_code: 'us',
60
- }).toString(),
61
- {
48
+ try {
49
+ await loginLimiterQueue.removeTokens(1);
50
+
51
+ logger.debug('Twitter login start.');
52
+ const android_id = uuidv5(username, NAMESPACE);
53
+ headers['X-Twitter-Client-DeviceID'] = android_id;
54
+
55
+ const ct0 = crypto.randomUUID().replaceAll('-', '');
56
+ const guestToken = await got(guestActivateUrl, {
57
+ headers: {
58
+ authorization: bearerToken,
59
+ 'x-csrf-token': ct0,
60
+ cookie: 'ct0=' + ct0,
61
+ },
62
62
  method: 'POST',
63
- headers,
64
- body: {
65
- flow_token: null,
66
- input_flow_data: {
67
- country_code: null,
68
- flow_context: {
69
- referrer_context: {
70
- referral_details: 'utm_source=google-play&utm_medium=organic',
71
- referrer_url: '',
72
- },
73
- start_location: {
74
- location: 'deeplink',
63
+ });
64
+ logger.debug('Twitter login 1 finished: guest token.');
65
+
66
+ headers['x-guest-token'] = guestToken.data.guest_token;
67
+
68
+ const task1 = await ofetch.raw(
69
+ 'https://api.x.com/1.1/onboarding/task.json?' +
70
+ new URLSearchParams({
71
+ flow_name: 'login',
72
+ api_version: '1',
73
+ known_device_token: '',
74
+ sim_country_code: 'us',
75
+ }).toString(),
76
+ {
77
+ method: 'POST',
78
+ headers,
79
+ body: {
80
+ flow_token: null,
81
+ input_flow_data: {
82
+ country_code: null,
83
+ flow_context: {
84
+ referrer_context: {
85
+ referral_details: 'utm_source=google-play&utm_medium=organic',
86
+ referrer_url: '',
87
+ },
88
+ start_location: {
89
+ location: 'deeplink',
90
+ },
75
91
  },
92
+ requested_variant: null,
93
+ target_user_id: 0,
76
94
  },
77
- requested_variant: null,
78
- target_user_id: 0,
79
95
  },
80
- },
81
- }
82
- );
83
- logger.debug('Twitter login 2 finished: login flow.');
84
-
85
- headers.att = task1.headers.get('att');
86
-
87
- const task2 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
88
- headers,
89
- json: {
90
- flow_token: task1._data.flow_token,
91
- subtask_inputs: [
92
- {
93
- enter_text: {
94
- suggestion_id: null,
95
- text: username,
96
- link: 'next_link',
96
+ }
97
+ );
98
+ logger.debug('Twitter login 2 finished: login flow.');
99
+
100
+ headers.att = task1.headers.get('att');
101
+
102
+ const task2 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
103
+ headers,
104
+ json: {
105
+ flow_token: task1._data.flow_token,
106
+ subtask_inputs: [
107
+ {
108
+ enter_text: {
109
+ suggestion_id: null,
110
+ text: username,
111
+ link: 'next_link',
112
+ },
113
+ subtask_id: 'LoginEnterUserIdentifier',
97
114
  },
98
- subtask_id: 'LoginEnterUserIdentifier',
99
- },
100
- ],
101
- },
102
- });
103
- logger.debug('Twitter login 3 finished: LoginEnterUserIdentifier.');
104
-
105
- const task3 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
106
- headers,
107
- json: {
108
- flow_token: task2.data.flow_token,
109
- subtask_inputs: [
110
- {
111
- enter_password: {
112
- password,
113
- link: 'next_link',
115
+ ],
116
+ },
117
+ });
118
+ logger.debug('Twitter login 3 finished: LoginEnterUserIdentifier.');
119
+
120
+ const task3 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
121
+ headers,
122
+ json: {
123
+ flow_token: task2.data.flow_token,
124
+ subtask_inputs: [
125
+ {
126
+ enter_password: {
127
+ password,
128
+ link: 'next_link',
129
+ },
130
+ subtask_id: 'LoginEnterPassword',
114
131
  },
115
- subtask_id: 'LoginEnterPassword',
116
- },
117
- ],
118
- },
119
- });
120
- logger.debug('Twitter login 4 finished: LoginEnterPassword.');
121
-
122
- const task4 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
123
- headers,
124
- json: {
125
- flow_token: task3.data.flow_token,
126
- subtask_inputs: [
127
- {
128
- check_logged_in_account: {
129
- link: 'AccountDuplicationCheck_false',
132
+ ],
133
+ },
134
+ });
135
+ logger.debug('Twitter login 4 finished: LoginEnterPassword.');
136
+
137
+ const task4 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
138
+ headers,
139
+ json: {
140
+ flow_token: task3.data.flow_token,
141
+ subtask_inputs: [
142
+ {
143
+ check_logged_in_account: {
144
+ link: 'AccountDuplicationCheck_false',
145
+ },
146
+ subtask_id: 'AccountDuplicationCheck',
130
147
  },
131
- subtask_id: 'AccountDuplicationCheck',
132
- },
133
- ],
134
- },
135
- });
136
- logger.debug('Twitter login 5 finished: AccountDuplicationCheck.');
137
-
138
- for await (const subtask of task4.data?.subtasks || []) {
139
- if (subtask.open_account) {
140
- authentication = subtask.open_account;
141
- break;
142
- } else if (subtask.subtask_id === 'LoginTwoFactorAuthChallenge') {
143
- const token = authenticator.generate(authenticationSecret);
144
-
145
- const task5 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
146
- headers,
147
- json: {
148
- flow_token: task4.data.flow_token,
149
- subtask_inputs: [
150
- {
151
- enter_text: {
152
- suggestion_id: null,
153
- text: token,
154
- link: 'next_link',
148
+ ],
149
+ },
150
+ });
151
+ logger.debug('Twitter login 5 finished: AccountDuplicationCheck.');
152
+
153
+ for await (const subtask of task4.data?.subtasks || []) {
154
+ if (subtask.open_account) {
155
+ authentication = subtask.open_account;
156
+ break;
157
+ } else if (subtask.subtask_id === 'LoginTwoFactorAuthChallenge') {
158
+ const token = authenticator.generate(authenticationSecret);
159
+
160
+ const task5 = await got.post('https://api.x.com/1.1/onboarding/task.json', {
161
+ headers,
162
+ json: {
163
+ flow_token: task4.data.flow_token,
164
+ subtask_inputs: [
165
+ {
166
+ enter_text: {
167
+ suggestion_id: null,
168
+ text: token,
169
+ link: 'next_link',
170
+ },
171
+ subtask_id: 'LoginTwoFactorAuthChallenge',
155
172
  },
156
- subtask_id: 'LoginTwoFactorAuthChallenge',
157
- },
158
- ],
159
- },
160
- });
161
- logger.debug('Twitter login 6 finished: LoginTwoFactorAuthChallenge.');
162
-
163
- for (const subtask of task5.data?.subtasks || []) {
164
- if (subtask.open_account) {
165
- authentication = subtask.open_account;
166
- break;
173
+ ],
174
+ },
175
+ });
176
+ logger.debug('Twitter login 6 finished: LoginTwoFactorAuthChallenge.');
177
+
178
+ for (const subtask of task5.data?.subtasks || []) {
179
+ if (subtask.open_account) {
180
+ authentication = subtask.open_account;
181
+ break;
182
+ }
167
183
  }
184
+ break;
185
+ } else {
186
+ logger.error(`Twitter login 6 failed: unknown subtask: ${subtask.subtask_id}`);
168
187
  }
169
- break;
170
188
  }
171
- }
172
- if (authentication) {
173
- logger.debug('Twitter login success.');
174
- } else {
175
- logger.error(`Twitter login failed. ${JSON.stringify(task4.data?.subtasks, null, 2)}`);
176
- }
189
+ if (authentication) {
190
+ logger.debug('Twitter login success.');
191
+ } else {
192
+ logger.error(`Twitter login failed. ${JSON.stringify(task4.data?.subtasks, null, 2)}`);
193
+ }
177
194
 
178
- return authentication;
195
+ return authentication;
196
+ } catch (error) {
197
+ logger.error(`Twitter username ${username} login failed:`, error);
198
+ }
179
199
  },
180
200
  60 * 60 * 24 * 30, // 30 days
181
201
  false
182
- );
202
+ )) as {
203
+ oauth_token: string;
204
+ oauth_token_secret: string;
205
+ } | null;
183
206
  }
184
207
 
185
208
  export default login;
@@ -3,35 +3,29 @@ import login from './login';
3
3
  import ConfigNotFoundError from '@/errors/types/config-not-found';
4
4
 
5
5
  let tokenIndex = 0;
6
- let authentication = null;
7
- let first = true;
8
6
 
9
- async function initToken() {
10
- if (config.twitter.username && config.twitter.password && !authentication && first) {
11
- authentication = await login();
12
- first = false;
13
- }
14
- }
15
-
16
- function getToken() {
7
+ async function getToken() {
17
8
  let token;
18
9
  if (config.twitter.username && config.twitter.password) {
19
- if (authentication) {
10
+ const index = tokenIndex++ % config.twitter.username.length;
11
+ const username = config.twitter.username[index];
12
+ const password = config.twitter.password[index];
13
+ const authenticationSecret = config.twitter.authenticationSecret?.[index];
14
+ if (username && password) {
15
+ const authentication = await login({
16
+ username,
17
+ password,
18
+ authenticationSecret,
19
+ });
20
+ if (!authentication) {
21
+ throw new ConfigNotFoundError(`Invalid twitter configs: ${username}`);
22
+ }
20
23
  token = {
21
24
  key: authentication.oauth_token,
22
25
  secret: authentication.oauth_token_secret,
26
+ cacheKey: `twitter:authentication:${username}`,
23
27
  };
24
28
  }
25
- } else if (config.twitter.oauthTokens?.length && config.twitter.oauthTokenSecrets.length && config.twitter.oauthTokens.length === config.twitter.oauthTokenSecrets.length) {
26
- token = {
27
- key: config.twitter.oauthTokens[tokenIndex],
28
- secret: config.twitter.oauthTokenSecrets[tokenIndex],
29
- };
30
-
31
- tokenIndex++;
32
- if (tokenIndex >= config.twitter.oauthTokens.length) {
33
- tokenIndex = 0;
34
- }
35
29
  } else {
36
30
  throw new ConfigNotFoundError('Invalid twitter configs');
37
31
  }
@@ -39,4 +33,4 @@ function getToken() {
39
33
  return token;
40
34
  }
41
35
 
42
- export { initToken, getToken };
36
+ export { getToken };
@@ -34,6 +34,7 @@ const cacheTryGet = async (_id, params, func) => {
34
34
  const userData: any = await getUserData(_id);
35
35
  const id = (userData.data?.user || userData.data?.user_result)?.result?.rest_id;
36
36
  if (id === undefined) {
37
+ cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire);
37
38
  throw new InvalidParameterError('User not found');
38
39
  }
39
40
  const funcName = func.name;
@@ -3,25 +3,89 @@ import { baseUrl, gqlFeatures, bearerToken, gqlMap } from './constants';
3
3
  import { config } from '@/config';
4
4
  import got from '@/utils/got';
5
5
  import queryString from 'query-string';
6
- import { Cookie } from 'tough-cookie';
6
+ import { Cookie, CookieJar } from 'tough-cookie';
7
+ import { CookieAgent, CookieClient } from 'http-cookie-agent/undici';
8
+ import { ProxyAgent } from 'undici';
9
+ import cache from '@/utils/cache';
10
+ import logger from '@/utils/logger';
11
+ import { RateLimiterMemory, RateLimiterRedis, RateLimiterQueue } from 'rate-limiter-flexible';
12
+ import ofetch from '@/utils/ofetch';
13
+ import proxy from '@/utils/proxy';
14
+
15
+ const dispatchers = {};
16
+ let authTokenIndex = 0;
17
+
18
+ const loginLimiter = cache.clients.redisClient
19
+ ? new RateLimiterRedis({
20
+ points: 1,
21
+ duration: 5,
22
+ execEvenly: true,
23
+ storeClient: cache.clients.redisClient,
24
+ })
25
+ : new RateLimiterMemory({
26
+ points: 1,
27
+ duration: 5,
28
+ execEvenly: true,
29
+ });
30
+
31
+ const loginLimiterQueue = new RateLimiterQueue(loginLimiter);
32
+
33
+ const token2Cookie = (token) =>
34
+ cache.tryGet(`twitter:cookie:${token}`, async () => {
35
+ const jar = new CookieJar();
36
+ jar.setCookieSync(`auth_token=${token}`, 'https://x.com');
37
+ try {
38
+ const agent = proxy.proxyUri
39
+ ? new ProxyAgent({
40
+ factory: (origin, opts) => new CookieClient(origin as string, { ...opts, cookies: { jar } }),
41
+ uri: proxy.proxyUri,
42
+ })
43
+ : new CookieAgent({ cookies: { jar } });
44
+ await got('https://x.com', {
45
+ dispatcher: agent,
46
+ });
47
+ return JSON.stringify(jar.serializeSync());
48
+ } catch {
49
+ // ignore
50
+ return '';
51
+ }
52
+ });
7
53
 
8
54
  export const twitterGot = async (url, params) => {
9
- if (!config.twitter.cookie) {
55
+ if (!config.twitter.authToken) {
10
56
  throw new ConfigNotFoundError('Twitter cookie is not configured');
11
57
  }
58
+ await loginLimiterQueue.removeTokens(1);
59
+ const token = config.twitter.authToken[authTokenIndex++ % config.twitter.authToken.length];
60
+ let cookie = await token2Cookie(token);
61
+ if (cookie) {
62
+ logger.debug(`Got twitter cookie for token ${token}`);
63
+ if (typeof cookie === 'string') {
64
+ cookie = JSON.parse(cookie);
65
+ }
66
+ const jar = CookieJar.deserializeSync(cookie as any);
67
+ const agent = proxy.proxyUri
68
+ ? new ProxyAgent({
69
+ factory: (origin, opts) => new CookieClient(origin as string, { ...opts, cookies: { jar } }),
70
+ uri: proxy.proxyUri,
71
+ })
72
+ : new CookieAgent({ cookies: { jar } });
73
+ dispatchers[token] = {
74
+ jar,
75
+ agent,
76
+ };
77
+ } else {
78
+ throw new ConfigNotFoundError(`Twitter cookie for token ${token} is not valid`);
79
+ }
12
80
  const jsonCookie = Object.fromEntries(
13
- config.twitter.cookie
81
+ dispatchers[token].jar
82
+ .getCookieStringSync(url)
14
83
  .split(';')
15
84
  .map((c) => Cookie.parse(c)?.toJSON())
16
85
  .map((c) => [c?.key, c?.value])
17
86
  );
18
- if (!jsonCookie || !jsonCookie.auth_token || !jsonCookie.ct0) {
19
- throw new ConfigNotFoundError('Twitter cookie is not valid');
20
- }
21
87
 
22
- const requestData = {
23
- url: `${url}?${queryString.stringify(params)}`,
24
- method: 'GET',
88
+ const response = await ofetch.raw(`${url}?${queryString.stringify(params)}`, {
25
89
  headers: {
26
90
  authority: 'x.com',
27
91
  accept: '*/*',
@@ -29,22 +93,29 @@ export const twitterGot = async (url, params) => {
29
93
  authorization: bearerToken,
30
94
  'cache-control': 'no-cache',
31
95
  'content-type': 'application/json',
32
- cookie: config.twitter.cookie,
33
96
  dnt: '1',
34
97
  pragma: 'no-cache',
35
98
  referer: 'https://x.com/narendramodi',
36
- 'x-csrf-token': jsonCookie.ct0,
37
99
  'x-twitter-active-user': 'yes',
38
100
  'x-twitter-auth-type': 'OAuth2Session',
39
101
  'x-twitter-client-language': 'en',
102
+ 'x-csrf-token': jsonCookie.ct0,
103
+ },
104
+ dispatcher: dispatchers[token].agent,
105
+ onResponse: async ({ response }) => {
106
+ if (response.status === 403) {
107
+ logger.debug(`Delete twitter cookie for token ${token}`);
108
+ await cache.set(`twitter:cookie:${token}`, '', config.cache.contentExpire);
109
+ }
40
110
  },
41
- };
42
-
43
- const response = await got(requestData.url, {
44
- headers: requestData.headers,
45
111
  });
46
112
 
47
- return response.data;
113
+ if (token) {
114
+ logger.debug(`Reset twitter cookie for token ${token}`);
115
+ await cache.set(`twitter:cookie:${token}`, JSON.stringify(dispatchers[token].jar.serializeSync()), config.cache.contentExpire);
116
+ }
117
+
118
+ return response._data;
48
119
  };
49
120
 
50
121
  export const paginationTweets = async (endpoint: string, userId: number | undefined, variables: Record<string, any>, path?: string[]) => {
@@ -66,7 +137,8 @@ export const paginationTweets = async (endpoint: string, userId: number | undefi
66
137
  if (data?.user?.result?.timeline_v2?.timeline?.instructions) {
67
138
  instructions = data.user.result.timeline_v2.timeline.instructions;
68
139
  } else {
69
- throw new Error('Because Twitter Premium has features that hide your likes, this RSS link is not available for Twitter Premium accounts.');
140
+ // throw new Error('Because Twitter Premium has features that hide your likes, this RSS link is not available for Twitter Premium accounts.');
141
+ logger.debug(`Instructions not found in data: ${JSON.stringify(data)}`);
70
142
  }
71
143
  }
72
144
 
@@ -17,7 +17,7 @@ export const route: Route = {
17
17
  description: 'Please see above for details.',
18
18
  },
19
19
  {
20
- name: 'TWITTER_COOKIE',
20
+ name: 'TWITTER_AUTH_TOKEN',
21
21
  description: 'Please see above for details.',
22
22
  },
23
23
  ],
@@ -28,7 +28,7 @@ export const route: Route = {
28
28
  supportScihub: false,
29
29
  },
30
30
  name: 'Home timeline',
31
- maintainers: ['DIYgod'],
31
+ maintainers: ['DIYgod', 'CaoMeiYouRen'],
32
32
  handler,
33
33
  radar: [
34
34
  {
@@ -1,10 +1,11 @@
1
- import { Route } from '@/types';
1
+ import { Route, ViewType } from '@/types';
2
2
  import api from './api';
3
3
  import utils from './utils';
4
4
 
5
5
  export const route: Route = {
6
6
  path: '/keyword/:keyword/:routeParams?',
7
7
  categories: ['social-media', 'popular'],
8
+ view: ViewType.SocialMedia,
8
9
  example: '/twitter/keyword/RSSHub',
9
10
  parameters: { keyword: 'keyword', routeParams: 'extra parameters, see the table above' },
10
11
  features: {
@@ -18,7 +19,7 @@ export const route: Route = {
18
19
  description: 'Please see above for details.',
19
20
  },
20
21
  {
21
- name: 'TWITTER_COOKIE',
22
+ name: 'TWITTER_AUTH_TOKEN',
22
23
  description: 'Please see above for details.',
23
24
  },
24
25
  ],
@@ -10,7 +10,7 @@ export const route: Route = {
10
10
  features: {
11
11
  requireConfig: [
12
12
  {
13
- name: 'TWITTER_COOKIE',
13
+ name: 'TWITTER_AUTH_TOKEN',
14
14
  description: 'Please see above for details.',
15
15
  },
16
16
  ],
@@ -18,7 +18,7 @@ export const route: Route = {
18
18
  description: 'Please see above for details.',
19
19
  },
20
20
  {
21
- name: 'TWITTER_COOKIE',
21
+ name: 'TWITTER_AUTH_TOKEN',
22
22
  description: 'Please see above for details.',
23
23
  },
24
24
  ],
@@ -1,11 +1,12 @@
1
- import { Route } from '@/types';
1
+ import { Route, ViewType } from '@/types';
2
2
  import api from './api';
3
3
  import utils from './utils';
4
4
 
5
5
  export const route: Route = {
6
6
  path: '/media/:id/:routeParams?',
7
7
  categories: ['social-media', 'popular'],
8
- example: '/twitter/media/DIYgod',
8
+ view: ViewType.Pictures,
9
+ example: '/twitter/media/_RSSHub',
9
10
  parameters: { id: 'username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`', routeParams: 'extra parameters, see the table above.' },
10
11
  features: {
11
12
  requireConfig: [
@@ -18,7 +19,7 @@ export const route: Route = {
18
19
  description: 'Please see above for details.',
19
20
  },
20
21
  {
21
- name: 'TWITTER_COOKIE',
22
+ name: 'TWITTER_AUTH_TOKEN',
22
23
  description: 'Please see above for details.',
23
24
  },
24
25
  ],
@@ -40,8 +40,8 @@ generates
40
40
 
41
41
  Currently supports two authentication methods:
42
42
 
43
- - Using \`TWITTER_COOKIE\` (recommended): Configure the cookies of logged-in Twitter Web, at least including the fields auth_token and ct0. RSSHub will use this information to directly access Twitter's web API to obtain data.
43
+ - Using \`TWITTER_AUTH_TOKEN\` (recommended): Configure a comma-separated list of \`auth_token\` cookies of logged-in Twitter Web. RSSHub will use this information to directly access Twitter's web API to obtain data.
44
44
 
45
- - Using \`TWITTER_USERNAME\` \`TWITTER_PASSWORD\` and \`TWITTER_AUTHENTICATION_SECRET\`: Configure the Twitter username and password. RSSHub will use this information to log in to Twitter and obtain data using the mobile API. Please note that if you have not logged in with the current IP address before, it is easy to trigger Twitter's risk control mechanism.
45
+ - Using \`TWITTER_USERNAME\` \`TWITTER_PASSWORD\` and \`TWITTER_AUTHENTICATION_SECRET\`: Configure a comma-separated list of Twitter username and password. RSSHub will use this information to log in to Twitter and obtain data using the mobile API. Please note that if you have not logged in with the current IP address before, it is easy to trigger Twitter's risk control mechanism.
46
46
  `,
47
47
  };