rsshub 1.0.0-master.ff487f3 → 1.0.0-master.ff76aa7

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 (532) hide show
  1. package/README.md +13 -48
  2. package/lib/api/radar/rules/all.ts +3 -6
  3. package/lib/api/radar/rules/one.ts +3 -6
  4. package/lib/config.ts +50 -2
  5. package/lib/errors/index.test.ts +1 -1
  6. package/lib/errors/index.tsx +1 -1
  7. package/lib/index.ts +1 -1
  8. package/lib/middleware/anti-hotlink.test.ts +191 -6
  9. package/lib/middleware/anti-hotlink.ts +29 -1
  10. package/lib/middleware/logger.ts +2 -2
  11. package/lib/middleware/parameter.test.ts +22 -5
  12. package/lib/middleware/parameter.ts +47 -17
  13. package/lib/middleware/template.tsx +10 -2
  14. package/lib/registry.test.ts +8 -0
  15. package/lib/registry.ts +2 -0
  16. package/lib/router.js +1 -9
  17. package/lib/routes/4ksj/forum.ts +54 -20
  18. package/lib/routes/500px/tribe-set.ts +3 -2
  19. package/lib/routes/51cto/recommend.ts +45 -7
  20. package/lib/routes/5eplay/index.ts +1 -51
  21. package/lib/routes/5eplay/utils.ts +5 -3
  22. package/lib/routes/a9vg/index.ts +214 -0
  23. package/lib/routes/a9vg/namespace.ts +1 -0
  24. package/lib/routes/a9vg/templates/description.art +17 -0
  25. package/lib/routes/abc/index.ts +2 -2
  26. package/lib/routes/academia/namespace.ts +1 -1
  27. package/lib/routes/agri/index.ts +307 -0
  28. package/lib/routes/agri/namespace.ts +8 -0
  29. package/lib/routes/agri/templates/description.art +21 -0
  30. package/lib/routes/aibase/discover.ts +388 -0
  31. package/lib/routes/aibase/namespace.ts +8 -0
  32. package/lib/routes/aibase/news.ts +118 -0
  33. package/lib/routes/aibase/templates/description.art +100 -0
  34. package/lib/routes/aibase/topic.ts +614 -0
  35. package/lib/routes/aibase/util.ts +114 -0
  36. package/lib/routes/anime1/anime.ts +63 -0
  37. package/lib/routes/anime1/namespace.ts +6 -0
  38. package/lib/routes/anime1/search.ts +57 -0
  39. package/lib/routes/apnews/api.ts +77 -0
  40. package/lib/routes/apnews/rss.ts +2 -2
  41. package/lib/routes/apnews/sitemap.ts +99 -0
  42. package/lib/routes/apnews/topics.ts +2 -2
  43. package/lib/routes/apnews/utils.ts +2 -1
  44. package/lib/routes/apple/podcast.ts +20 -16
  45. package/lib/routes/asus/bios.ts +88 -21
  46. package/lib/routes/asus/templates/bios.art +13 -6
  47. package/lib/routes/bangumi/templates/tv/subject.art +6 -0
  48. package/lib/routes/bangumi/tv/other/followrank.ts +19 -22
  49. package/lib/routes/bangumi/tv/user/collections.ts +172 -0
  50. package/lib/routes/behance/user.ts +14 -3
  51. package/lib/routes/beijingprice/index.ts +12 -12
  52. package/lib/routes/bestblogs/feeds.ts +107 -0
  53. package/lib/routes/bestblogs/namespace.ts +6 -0
  54. package/lib/routes/bilibili/api-interface.d.ts +1 -0
  55. package/lib/routes/bilibili/bangumi.ts +3 -2
  56. package/lib/routes/bilibili/bilibili-recommend.ts +42 -0
  57. package/lib/routes/bilibili/cache.ts +41 -9
  58. package/lib/routes/bilibili/dynamic.ts +103 -105
  59. package/lib/routes/bilibili/followings-dynamic.ts +3 -1
  60. package/lib/routes/bilibili/followings-video.ts +9 -3
  61. package/lib/routes/bilibili/readlist.ts +3 -2
  62. package/lib/routes/bilibili/utils.ts +90 -3
  63. package/lib/routes/bilibili/video.ts +8 -9
  64. package/lib/routes/bilibili/vsearch.ts +1 -1
  65. package/lib/routes/binance/announcement.ts +168 -0
  66. package/lib/routes/binance/types.ts +26 -0
  67. package/lib/routes/bitget/announcement.ts +200 -0
  68. package/lib/routes/bitget/namespace.ts +6 -0
  69. package/lib/routes/bitget/type.ts +26 -0
  70. package/lib/routes/bjnews/cat.ts +9 -1
  71. package/lib/routes/bjp/apod.ts +4 -2
  72. package/lib/routes/buaa/jiaowu.ts +1 -1
  73. package/lib/routes/buaa/lib/space/newbook.ts +171 -0
  74. package/lib/routes/buaa/lib/space/templates/newbook.art +44 -0
  75. package/lib/routes/bupt/jwc.ts +130 -0
  76. package/lib/routes/caixin/latest.ts +3 -2
  77. package/lib/routes/cbpanet/index.ts +380 -0
  78. package/lib/routes/cbpanet/namespace.ts +8 -0
  79. package/lib/routes/ccfa/index.ts +216 -0
  80. package/lib/routes/ccfa/namespace.ts +8 -0
  81. package/lib/routes/ccfa/templates/description.art +7 -0
  82. package/lib/routes/cctv/category.ts +16 -12
  83. package/lib/routes/cctv/xwlb.ts +32 -10
  84. package/lib/routes/cdu/jwgg.ts +79 -0
  85. package/lib/routes/cdu/namespace.ts +6 -0
  86. package/lib/routes/ceph/blog.ts +72 -0
  87. package/lib/routes/ceph/namespace.ts +7 -0
  88. package/lib/routes/changba/user.ts +6 -5
  89. package/lib/routes/chikubi/category.ts +41 -0
  90. package/lib/routes/chikubi/index.ts +66 -0
  91. package/lib/routes/chikubi/namespace.ts +15 -0
  92. package/lib/routes/chikubi/navigation.ts +66 -0
  93. package/lib/routes/chikubi/nipple-video-category.ts +49 -0
  94. package/lib/routes/chikubi/nipple-video-maker.ts +49 -0
  95. package/lib/routes/chikubi/search.ts +39 -0
  96. package/lib/routes/chikubi/tag.ts +41 -0
  97. package/lib/routes/chikubi/utils.ts +135 -0
  98. package/lib/routes/chinanews/index.ts +1 -1
  99. package/lib/routes/chinaventure/index.ts +1 -1
  100. package/lib/routes/chlinlearn/daily-blog.ts +7 -0
  101. package/lib/routes/cisia/index.ts +264 -0
  102. package/lib/routes/cisia/namespace.ts +8 -0
  103. package/lib/routes/cjlu/namespace.ts +10 -0
  104. package/lib/routes/cjlu/yjsy/index.ts +107 -0
  105. package/lib/routes/cls/subject.ts +153 -0
  106. package/lib/routes/cls/templates/description.art +21 -0
  107. package/lib/routes/cohere/index.ts +54 -0
  108. package/lib/routes/cohere/namespace.ts +6 -0
  109. package/lib/routes/damai/activity.ts +3 -2
  110. package/lib/routes/dcfever/trading.ts +3 -5
  111. package/lib/routes/dcfever/utils.ts +13 -4
  112. package/lib/routes/dealstreetasia/home.ts +72 -0
  113. package/lib/routes/dealstreetasia/namespace.ts +6 -0
  114. package/lib/routes/dealstreetasia/section.ts +57 -0
  115. package/lib/routes/deepin/thread.ts +102 -0
  116. package/lib/routes/deeplearning/thebatch.ts +30 -18
  117. package/lib/routes/devolverdigital/blog.ts +59 -42
  118. package/lib/routes/devtrium/namespace.ts +6 -0
  119. package/lib/routes/devtrium/posts.ts +55 -0
  120. package/lib/routes/discourse/official.ts +55 -0
  121. package/lib/routes/dlsite/campaign.ts +1 -2
  122. package/lib/routes/dlsite/new.ts +1 -2
  123. package/lib/routes/dlsite/{index.ts → z-index/index.ts} +1 -1
  124. package/lib/routes/dockerhub/build.ts +1 -1
  125. package/lib/routes/dockerhub/utils.ts +4 -3
  126. package/lib/routes/douban/movie/coming.ts +68 -0
  127. package/lib/routes/douban/templates/movie_coming.art +27 -0
  128. package/lib/routes/douyin/types.ts +795 -0
  129. package/lib/routes/douyin/user.ts +51 -23
  130. package/lib/routes/douyin/utils.ts +1 -87
  131. package/lib/routes/dykszx/namespace.ts +8 -0
  132. package/lib/routes/dykszx/news.ts +88 -0
  133. package/lib/routes/eagle/blog.ts +13 -2
  134. package/lib/routes/eastmoney/report/index.ts +63 -15
  135. package/lib/routes/eastmoney/templates/stock_description.art +36 -0
  136. package/lib/routes/eastmoney/utils.ts +35 -0
  137. package/lib/routes/easynomad/joblist.ts +56 -0
  138. package/lib/routes/easynomad/namespace.ts +6 -0
  139. package/lib/routes/economist/espresso.ts +3 -2
  140. package/lib/routes/economist/full.ts +3 -2
  141. package/lib/routes/epicgames/index.ts +1 -1
  142. package/lib/routes/espn/namespace.ts +6 -0
  143. package/lib/routes/espn/news.ts +120 -0
  144. package/lib/routes/espn/templates/media.art +19 -0
  145. package/lib/routes/famitsu/category.ts +1 -1
  146. package/lib/routes/fanbox/index.ts +1 -1
  147. package/lib/routes/fanbox/types.ts +1 -5
  148. package/lib/routes/fediverse/timeline.ts +2 -2
  149. package/lib/routes/flashcat/blog.ts +61 -0
  150. package/lib/routes/flashcat/namespace.ts +6 -0
  151. package/lib/routes/follow/namespace.ts +6 -0
  152. package/lib/routes/follow/profile.ts +83 -0
  153. package/lib/routes/follow/types.ts +78 -0
  154. package/lib/routes/ftm/index.ts +60 -0
  155. package/lib/routes/ftm/namespace.ts +6 -0
  156. package/lib/routes/galxe/index.ts +94 -0
  157. package/lib/routes/galxe/namespace.ts +9 -0
  158. package/lib/routes/gamer/ani/anime.ts +10 -3
  159. package/lib/routes/gamer/ani/new-anime.ts +4 -3
  160. package/lib/routes/gamer/gnn-index.ts +31 -11
  161. package/lib/routes/gamer/hot.ts +4 -3
  162. package/lib/routes/gamersky/ent.ts +57 -0
  163. package/lib/routes/gamersky/namespace.ts +10 -0
  164. package/lib/routes/gamersky/news.ts +91 -0
  165. package/lib/routes/gamersky/review.ts +80 -0
  166. package/lib/routes/gamersky/utils.ts +94 -0
  167. package/lib/routes/gdut/oa-news.ts +12 -17
  168. package/lib/routes/geekpark/index.ts +219 -0
  169. package/lib/routes/geekpark/namespace.ts +8 -0
  170. package/lib/routes/geekpark/templates/description.art +21 -0
  171. package/lib/routes/gisreportsonline/index.ts +60 -0
  172. package/lib/routes/gisreportsonline/namespace.ts +6 -0
  173. package/lib/routes/github/activity.ts +2 -1
  174. package/lib/routes/github/advisor.ts +97 -0
  175. package/lib/routes/github/comments.ts +32 -30
  176. package/lib/routes/github/discussions.ts +194 -0
  177. package/lib/routes/github/issue.ts +1 -1
  178. package/lib/routes/github/pulls.ts +1 -1
  179. package/lib/routes/gmcmonline/chinacustoms.ts +130 -0
  180. package/lib/routes/gmcmonline/namespace.ts +8 -0
  181. package/lib/routes/go/mhlw/pdf.ts +87 -0
  182. package/lib/routes/go/namespace.ts +8 -0
  183. package/lib/routes/go/niid/idwr-dl.ts +104 -0
  184. package/lib/routes/gov/cbirc/index.ts +371 -0
  185. package/lib/routes/gov/chongqing/sydwgkzp.ts +4 -4
  186. package/lib/routes/gov/cn/namespace.ts +8 -0
  187. package/lib/routes/gov/{news → cn/news}/index.ts +11 -7
  188. package/lib/routes/gov/csrc/csrc.ts +541 -0
  189. package/lib/routes/gov/lswz/index.ts +289 -0
  190. package/lib/routes/gov/moa/moa.ts +3 -3
  191. package/lib/routes/gov/moa/szcpxx.ts +115 -0
  192. package/lib/routes/gov/moa/zdscxx.ts +71 -21
  193. package/lib/routes/gov/ndrc/fggz.ts +637 -2
  194. package/lib/routes/gov/ndrc/namespace.ts +8 -0
  195. package/lib/routes/gov/ndrc/xwdt.ts +28 -8
  196. package/lib/routes/gov/ndrc/zfxxgk.ts +106 -0
  197. package/lib/routes/gov/zj/search.ts +17 -12
  198. package/lib/routes/gq/news.ts +3 -2
  199. package/lib/routes/gs/developer/blog.ts +58 -0
  200. package/lib/routes/gs/namespace.ts +9 -0
  201. package/lib/routes/guancha/member.ts +1 -1
  202. package/lib/routes/hackernews/index.ts +16 -5
  203. package/lib/routes/healthz.ts +8 -0
  204. package/lib/routes/hellogithub/index.ts +17 -67
  205. package/lib/routes/hellogithub/report.ts +1 -1
  206. package/lib/routes/hex-rays/index.ts +23 -29
  207. package/lib/routes/hfut/hf/notice.ts +43 -0
  208. package/lib/routes/hfut/hf/utils.ts +80 -0
  209. package/lib/routes/hfut/namespace.ts +6 -0
  210. package/lib/routes/hfut/xc/notice.ts +43 -0
  211. package/lib/routes/hfut/xc/utils.ts +73 -0
  212. package/lib/routes/hket/index.ts +80 -76
  213. package/lib/routes/hko/earthquake.ts +56 -0
  214. package/lib/routes/hko/namespace.ts +8 -0
  215. package/lib/routes/hko/weather.ts +56 -0
  216. package/lib/routes/hottoys/index.ts +63 -0
  217. package/lib/routes/hottoys/namespace.ts +6 -0
  218. package/lib/routes/hpoi/all.ts +17 -6
  219. package/lib/routes/hpoi/character.ts +18 -6
  220. package/lib/routes/hpoi/info.ts +11 -6
  221. package/lib/routes/hpoi/user.ts +16 -4
  222. package/lib/routes/hpoi/utils.ts +36 -3
  223. package/lib/routes/hpoi/work.ts +18 -6
  224. package/lib/routes/huggingface/blog-zh.ts +1 -1
  225. package/lib/routes/hust/gs.ts +281 -0
  226. package/lib/routes/hust/mse.ts +575 -0
  227. package/lib/routes/hust/namespace.ts +1 -1
  228. package/lib/routes/i-cable/namespace.ts +6 -0
  229. package/lib/routes/i-cable/news.ts +77 -0
  230. package/lib/routes/i-cable/templates/description.art +8 -0
  231. package/lib/routes/imhcg/blog.ts +47 -0
  232. package/lib/routes/imhcg/namespace.ts +7 -0
  233. package/lib/routes/imop/namespace.ts +10 -0
  234. package/lib/routes/imop/tianshu.ts +53 -0
  235. package/lib/routes/infzm/hot.ts +39 -0
  236. package/lib/routes/infzm/index.ts +3 -30
  237. package/lib/routes/infzm/utils.ts +34 -0
  238. package/lib/routes/ipsw.dev/index.ts +64 -0
  239. package/lib/routes/ipsw.dev/namespace.ts +7 -0
  240. package/lib/routes/ipsw.dev/templates/description.art +20 -0
  241. package/lib/routes/iwara/subscriptions.ts +1 -1
  242. package/lib/routes/javtiful/actress.ts +37 -0
  243. package/lib/routes/javtiful/channel.ts +37 -0
  244. package/lib/routes/javtiful/namespace.ts +6 -0
  245. package/lib/routes/javtiful/templates/description.art +7 -0
  246. package/lib/routes/javtiful/utils.ts +18 -0
  247. package/lib/routes/jianshu/collection.ts +3 -2
  248. package/lib/routes/jianshu/home.ts +3 -2
  249. package/lib/routes/jianshu/user.ts +3 -2
  250. package/lib/routes/jike/topic.ts +10 -3
  251. package/lib/routes/jike/user.ts +3 -2
  252. package/lib/routes/jjwxc/book.ts +18 -14
  253. package/lib/routes/joins/chinese.ts +218 -0
  254. package/lib/routes/joins/namespace.ts +8 -0
  255. package/lib/routes/joins/templates/description.art +17 -0
  256. package/lib/routes/kakuyomu/namespace.ts +6 -0
  257. package/lib/routes/kakuyomu/types.ts +6 -0
  258. package/lib/routes/kakuyomu/works.ts +73 -0
  259. package/lib/routes/kaopu/news.ts +3 -3
  260. package/lib/routes/kisskiss/blog.ts +74 -0
  261. package/lib/routes/kisskiss/namespace.ts +6 -0
  262. package/lib/routes/konachan/namespace.ts +7 -0
  263. package/lib/routes/konachan/post.ts +84 -0
  264. package/lib/routes/lemmy/index.ts +30 -3
  265. package/lib/routes/lemmy/namespace.ts +1 -0
  266. package/lib/routes/link3/events.ts +86 -0
  267. package/lib/routes/link3/namespace.ts +6 -0
  268. package/lib/routes/link3/profile.ts +143 -0
  269. package/lib/routes/lmu/jobs.ts +96 -0
  270. package/lib/routes/lmu/namespace.ts +17 -0
  271. package/lib/routes/lmu/templates/jobPosting.art +11 -0
  272. package/lib/routes/lofter/collection.ts +87 -0
  273. package/lib/routes/lofter/user.ts +1 -1
  274. package/lib/routes/lorientlejour/index.ts +186 -0
  275. package/lib/routes/lorientlejour/namespace.ts +7 -0
  276. package/lib/routes/lorientlejour/templates/description.art +18 -0
  277. package/lib/routes/lovelive-anime/namespace.ts +1 -1
  278. package/lib/routes/lovelive-anime/news.ts +56 -26
  279. package/lib/routes/lovelive-anime/schedules.ts +18 -6
  280. package/lib/routes/lovelive-anime/templates/description.art +1 -1
  281. package/lib/routes/lovelive-anime/templates/scheduleDesc.art +0 -1
  282. package/lib/routes/lovelive-anime/topics.ts +1 -1
  283. package/lib/routes/luma/index.ts +92 -0
  284. package/lib/routes/luma/namespace.ts +6 -0
  285. package/lib/routes/malaysiakini/index.ts +179 -0
  286. package/lib/routes/malaysiakini/namespace.ts +11 -0
  287. package/lib/routes/manhuagui/comic.ts +1 -1
  288. package/lib/routes/mastodon/account-id.ts +26 -3
  289. package/lib/routes/mastodon/acct.ts +15 -4
  290. package/lib/routes/mastodon/timeline-local.ts +14 -3
  291. package/lib/routes/mastodon/timeline-remote.ts +14 -3
  292. package/lib/routes/meishichina/index.ts +1997 -0
  293. package/lib/routes/meishichina/namespace.ts +8 -0
  294. package/lib/routes/mi/crowdfunding.ts +43 -22
  295. package/lib/routes/mi/templates/crowdfunding.art +28 -0
  296. package/lib/routes/mi/types.ts +40 -0
  297. package/lib/routes/mi/utils.ts +80 -0
  298. package/lib/routes/misskey/featured-notes.ts +3 -2
  299. package/lib/routes/misskey/user-timeline.ts +3 -2
  300. package/lib/routes/misskey/utils.ts +1 -0
  301. package/lib/routes/natgeo/dailyphoto.ts +4 -3
  302. package/lib/routes/natgeo/dailyselection.ts +17 -2
  303. package/lib/routes/ncku/csie.ts +83 -0
  304. package/lib/routes/ncku/namespace.ts +6 -0
  305. package/lib/routes/ncku/phys.ts +95 -0
  306. package/lib/routes/news/namespace.ts +1 -1
  307. package/lib/routes/newyorker/news.ts +29 -21
  308. package/lib/routes/nhk/news.ts +32 -15
  309. package/lib/routes/nicovideo/namespace.ts +7 -0
  310. package/lib/routes/nicovideo/templates/video.art +7 -0
  311. package/lib/routes/nicovideo/types.ts +54 -0
  312. package/lib/routes/nicovideo/utils.ts +37 -0
  313. package/lib/routes/nicovideo/video.ts +47 -0
  314. package/lib/routes/nikkei/cn/index.ts +12 -1
  315. package/lib/routes/nytimes/book.ts +25 -28
  316. package/lib/routes/nytimes/daily-briefing-chinese.ts +4 -4
  317. package/lib/routes/nytimes/index.ts +16 -9
  318. package/lib/routes/nytimes/namespace.ts +1 -1
  319. package/lib/routes/obsidian/namespace.ts +6 -0
  320. package/lib/routes/obsidian/publish.ts +83 -0
  321. package/lib/routes/obsidian/utils.ts +8 -0
  322. package/lib/routes/oct0pu5/namespace.ts +10 -0
  323. package/lib/routes/oct0pu5/rss.ts +48 -0
  324. package/lib/routes/oncc/templates/article.art +1 -1
  325. package/lib/routes/papers/index.ts +7 -1
  326. package/lib/routes/parliament.uk/commonslibrary.ts +55 -0
  327. package/lib/routes/parliament.uk/lordslibrary.ts +55 -0
  328. package/lib/routes/parliament.uk/namespace.ts +6 -0
  329. package/lib/routes/pconline/focus.ts +143 -0
  330. package/lib/routes/pconline/namespace.ts +10 -0
  331. package/lib/routes/people/index.ts +6 -2
  332. package/lib/routes/picnob/user.ts +11 -3
  333. package/lib/routes/picuki/profile.ts +50 -39
  334. package/lib/routes/pixiv/ranking.ts +1 -0
  335. package/lib/routes/pixivision/index.ts +84 -0
  336. package/lib/routes/pixivision/namespace.ts +6 -0
  337. package/lib/routes/pixivision/utils.ts +86 -0
  338. package/lib/routes/pku/bbs/hot.ts +1 -1
  339. package/lib/routes/pornhub/category.ts +3 -2
  340. package/lib/routes/pornhub/model.ts +6 -7
  341. package/lib/routes/pornhub/pornstar.ts +67 -19
  342. package/lib/routes/pornhub/search.ts +3 -2
  343. package/lib/routes/pornhub/users.ts +3 -5
  344. package/lib/routes/qiche365/namespace.ts +5 -0
  345. package/lib/routes/qiche365/recall.ts +53 -0
  346. package/lib/routes/qidian/namespace.ts +1 -1
  347. package/lib/routes/questn/community.ts +76 -0
  348. package/lib/routes/questn/events.ts +101 -0
  349. package/lib/routes/questn/namespace.ts +6 -0
  350. package/lib/routes/questn/util.ts +16 -0
  351. package/lib/routes/rebase/geekdaily.ts +35 -0
  352. package/lib/routes/rebase/namespace.ts +6 -0
  353. package/lib/routes/resonac/namespace.ts +6 -0
  354. package/lib/routes/resonac/products.ts +85 -0
  355. package/lib/routes/reuters/common.ts +54 -1
  356. package/lib/routes/rss3/index.ts +22 -13
  357. package/lib/routes/rss3/interfaces/metadata.ts +67 -0
  358. package/lib/routes/rsshub/transform/html.ts +114 -75
  359. package/lib/routes/samd/news.ts +1 -1
  360. package/lib/routes/sciencenet/user.ts +3 -1
  361. package/lib/routes/scmp/utils.ts +2 -2
  362. package/lib/routes/scut/gzic/media.ts +71 -0
  363. package/lib/routes/scut/gzic/news.ts +65 -0
  364. package/lib/routes/scut/gzic/notice.ts +88 -0
  365. package/lib/routes/sdu/{cs.ts → cs/index.ts} +46 -12
  366. package/lib/routes/sdu/cs/yjsgz.ts +85 -0
  367. package/lib/routes/sdu/gjsw.ts +80 -0
  368. package/lib/routes/sdu/namespace.ts +2 -2
  369. package/lib/routes/sdu/qd/xszxqd.ts +98 -0
  370. package/lib/routes/sdu/qd/xyb.ts +82 -0
  371. package/lib/routes/sdu/ygb.ts +90 -0
  372. package/lib/routes/showstart/search.ts +33 -1
  373. package/lib/routes/sis001/author.ts +52 -0
  374. package/lib/routes/sis001/common.ts +38 -0
  375. package/lib/routes/sis001/forum.ts +2 -29
  376. package/lib/routes/sjtu/jwc.ts +4 -1
  377. package/lib/routes/sjtu/namespace.ts +1 -1
  378. package/lib/routes/sjtu/seiee/index.ts +97 -0
  379. package/lib/routes/skeb/following-creators.ts +52 -0
  380. package/lib/routes/skeb/following-works.ts +52 -0
  381. package/lib/routes/skeb/friend-works.ts +52 -0
  382. package/lib/routes/skeb/index.ts +131 -0
  383. package/lib/routes/skeb/namespace.ts +6 -0
  384. package/lib/routes/skeb/search.ts +77 -0
  385. package/lib/routes/skeb/templates/creator.art +8 -0
  386. package/lib/routes/skeb/templates/work.art +10 -0
  387. package/lib/routes/skeb/utils.ts +155 -0
  388. package/lib/routes/skeb/works.ts +88 -0
  389. package/lib/routes/skebetter/illust.ts +83 -0
  390. package/lib/routes/skebetter/index.ts +83 -0
  391. package/lib/routes/skebetter/manga.ts +65 -0
  392. package/lib/routes/skebetter/namespace.ts +6 -0
  393. package/lib/routes/skebetter/utils.ts +72 -0
  394. package/lib/routes/smzdm/product.ts +85 -0
  395. package/lib/routes/solidot/main.ts +28 -12
  396. package/lib/routes/soundon/namespace.ts +6 -0
  397. package/lib/routes/soundon/podcast.ts +80 -0
  398. package/lib/routes/soundon/types.ts +67 -0
  399. package/lib/routes/spankbang/namespace.ts +6 -0
  400. package/lib/routes/spankbang/new-videos.ts +90 -0
  401. package/lib/routes/spankbang/templates/video.art +7 -0
  402. package/lib/routes/sse/sselawsrules.ts +370 -0
  403. package/lib/routes/sspai/index.ts +2 -2
  404. package/lib/routes/straitstimes/index.ts +135 -0
  405. package/lib/routes/straitstimes/namespace.ts +7 -0
  406. package/lib/routes/straitstimes/templates/description.art +27 -0
  407. package/lib/routes/sustainabilitymag/articles.ts +2 -0
  408. package/lib/routes/sustainabilitymag/namespace.ts +1 -1
  409. package/lib/routes/swjtu/scai/bks.ts +76 -0
  410. package/lib/routes/szftedu/dongtai.ts +62 -0
  411. package/lib/routes/szftedu/gonggao.ts +62 -0
  412. package/lib/routes/szftedu/namespace.ts +6 -0
  413. package/lib/routes/szse/rule.ts +315 -42
  414. package/lib/routes/taoguba/blog.ts +1 -1
  415. package/lib/routes/taoguba/index.ts +1 -1
  416. package/lib/routes/telegram/blog.ts +3 -2
  417. package/lib/routes/telegram/channel.ts +34 -3
  418. package/lib/routes/telegram/stickerpack.ts +3 -2
  419. package/lib/routes/tencent/news/author.ts +2 -1
  420. package/lib/routes/test/index.ts +50 -43
  421. package/lib/routes/the/index.ts +8 -4
  422. package/lib/routes/theblockbeats/index.ts +10 -5
  423. package/lib/routes/theinitium/app.ts +137 -0
  424. package/lib/routes/theinitium/templates/description.art +17 -0
  425. package/lib/routes/threads/index.ts +26 -20
  426. package/lib/routes/tju/cic/index.ts +2 -2
  427. package/lib/routes/tju/news/index.ts +2 -2
  428. package/lib/routes/tju/oaa/index.ts +2 -2
  429. package/lib/routes/towardsdatascience/latest.ts +67 -0
  430. package/lib/routes/towardsdatascience/namespace.ts +6 -0
  431. package/lib/routes/ttv/index.ts +1 -1
  432. package/lib/routes/tvb/news.ts +2 -1
  433. package/lib/routes/twitch/live.ts +3 -2
  434. package/lib/routes/twitch/video.ts +15 -6
  435. package/lib/routes/twitter/api/index.ts +2 -0
  436. package/lib/routes/twitter/api/mobile-api/login.ts +6 -1
  437. package/lib/routes/twitter/api/web-api/api.ts +34 -10
  438. package/lib/routes/twitter/api/web-api/constants.ts +18 -15
  439. package/lib/routes/twitter/api/web-api/login.ts +75 -0
  440. package/lib/routes/twitter/api/web-api/utils.ts +147 -52
  441. package/lib/routes/twitter/home-latest.ts +60 -0
  442. package/lib/routes/twitter/list.ts +5 -2
  443. package/lib/routes/twitter/media.ts +13 -4
  444. package/lib/routes/twitter/tweet.ts +1 -0
  445. package/lib/routes/twitter/user.ts +15 -6
  446. package/lib/routes/uber/blog.ts +1 -1
  447. package/lib/routes/udn/breaking-news.ts +2 -2
  448. package/lib/routes/uestc/bbs.ts +97 -0
  449. package/lib/routes/uestc/gr.ts +65 -37
  450. package/lib/routes/uestc/jwc.ts +49 -28
  451. package/lib/routes/uestc/namespace.ts +1 -1
  452. package/lib/routes/ustc/scms.ts +97 -0
  453. package/lib/routes/v2ex/topics.ts +1 -0
  454. package/lib/routes/vimeo/category.ts +4 -3
  455. package/lib/routes/vimeo/usr-videos.ts +3 -2
  456. package/lib/routes/voronoiapp/author.ts +41 -0
  457. package/lib/routes/voronoiapp/common.ts +247 -0
  458. package/lib/routes/voronoiapp/editors-pick.ts +28 -0
  459. package/lib/routes/voronoiapp/home.ts +29 -0
  460. package/lib/routes/voronoiapp/latest.ts +28 -0
  461. package/lib/routes/voronoiapp/namespace.ts +6 -0
  462. package/lib/routes/voronoiapp/popular.ts +49 -0
  463. package/lib/routes/voronoiapp/search.ts +33 -0
  464. package/lib/routes/voronoiapp/types.d.ts +78 -0
  465. package/lib/routes/wchscu/namespace.ts +6 -0
  466. package/lib/routes/wchscu/recruit.ts +73 -0
  467. package/lib/routes/wechat/ershcimi.ts +4 -2
  468. package/lib/routes/weibo/namespace.ts +1 -0
  469. package/lib/routes/weibo/oasis/user.ts +1 -1
  470. package/lib/routes/weibo/super-index.ts +23 -11
  471. package/lib/routes/weibo/user.ts +22 -3
  472. package/lib/routes/weibo/utils.ts +0 -4
  473. package/lib/routes/whu/cs.ts +11 -3
  474. package/lib/routes/whu/gs/index.ts +2 -1
  475. package/lib/routes/whu/news.ts +27 -2
  476. package/lib/routes/withgoogle/explorables.ts +54 -0
  477. package/lib/routes/withgoogle/namespace.ts +6 -0
  478. package/lib/routes/xbookcn/blog.ts +66 -0
  479. package/lib/routes/xbookcn/namespace.ts +6 -0
  480. package/lib/routes/xiaohongshu/notes.ts +10 -2
  481. package/lib/routes/xiaohongshu/user.ts +30 -6
  482. package/lib/routes/xiaohongshu/util.ts +3 -1
  483. package/lib/routes/xiaoyuzhou/podcast.ts +35 -7
  484. package/lib/routes/xidian/cs.ts +143 -0
  485. package/lib/routes/xidian/gr.ts +289 -0
  486. package/lib/routes/xidian/jwc.ts +1 -0
  487. package/lib/routes/xidian/namespace.ts +1 -1
  488. package/lib/routes/xueqiu/cookies.ts +17 -2
  489. package/lib/routes/xueqiu/favorite.ts +16 -2
  490. package/lib/routes/xueqiu/stock-info.ts +6 -12
  491. package/lib/routes/yamap/articles.ts +62 -0
  492. package/lib/routes/yamap/namespace.ts +6 -0
  493. package/lib/routes/yande/namespace.ts +1 -1
  494. package/lib/routes/yande/post.ts +7 -7
  495. package/lib/routes/yicai/carousel.ts +48 -0
  496. package/lib/routes/yicai/utils.ts +23 -17
  497. package/lib/routes/yilinzazhi/index.ts +58 -0
  498. package/lib/routes/yilinzazhi/latest.ts +103 -0
  499. package/lib/routes/yilinzazhi/namespace.ts +8 -0
  500. package/lib/routes/youtube/playlist.ts +3 -2
  501. package/lib/routes/yystv/docs.ts +28 -19
  502. package/lib/routes/zaobao/util.ts +41 -43
  503. package/lib/routes/zhihu/activities.ts +2 -1
  504. package/lib/routes/zhihu/answers.ts +5 -1
  505. package/lib/routes/zhihu/execlib/x-zse-96-v3.ts +2 -0
  506. package/lib/routes/zhihu/timeline.ts +6 -1
  507. package/lib/routes/zhihu/utils.ts +15 -7
  508. package/lib/routes/zhihu/zhuanlan.ts +8 -7
  509. package/lib/routes/zsxq/types.ts +9 -0
  510. package/lib/routes/zsxq/utils.ts +4 -3
  511. package/lib/setup.test.ts +1 -1
  512. package/lib/types.ts +6 -0
  513. package/lib/utils/cache/redis.ts +1 -1
  514. package/lib/utils/camelcase-keys.spec.ts +127 -0
  515. package/lib/utils/camelcase-keys.ts +27 -0
  516. package/lib/utils/common-utils.ts +1 -0
  517. package/lib/utils/otel/metric.ts +3 -2
  518. package/lib/utils/request-rewriter/fetch.ts +1 -1
  519. package/lib/views/index.tsx +3 -6
  520. package/lib/views/json.ts +1 -1
  521. package/lib/views/rss.tsx +7 -0
  522. package/package.json +55 -55
  523. package/lib/routes/a9vg/a9vg.ts +0 -45
  524. package/lib/routes/sse/lawandrules.ts +0 -68
  525. package/lib/routes-deprecated/anime1/anime.js +0 -25
  526. package/lib/routes-deprecated/anime1/search.js +0 -25
  527. package/lib/routes-deprecated/dykszx/news.js +0 -60
  528. package/lib/routes-deprecated/gamersky/ent.js +0 -89
  529. package/lib/routes-deprecated/gamersky/news.js +0 -34
  530. package/lib/routes-deprecated/geekpark/breakingnews.js +0 -25
  531. package/lib/routes-deprecated/hko/weather.js +0 -44
  532. package/lib/routes-deprecated/konachan/post-popular-recent.js +0 -67
@@ -1,47 +1,51 @@
1
1
  const baseUrl = 'https://x.com/i/api';
2
2
 
3
3
  const graphQLEndpointsPlain = [
4
- '/graphql/eS7LO5Jy3xgmd3dbL044EA/UserTweets',
5
- '/graphql/k5XapwcSikNsEsILW5FvgA/UserByScreenName',
6
- '/graphql/k3YiLNE_MAy5J-NANLERdg/HomeTimeline',
7
- '/graphql/3GeIaLmNhTm1YsUmxR57tg/UserTweetsAndReplies',
8
- '/graphql/TOU4gQw8wXIqpSzA4TYKgg/UserMedia',
9
- '/graphql/B8I_QCljDBVfin21TTWMqA/Likes',
10
- '/graphql/tD8zKvQzwY3kdx5yz6YmOw/UserByRestId',
11
- '/graphql/flaR-PUMshxFWZWPNpq4zA/SearchTimeline',
12
- '/graphql/TOTgqavWmxywKv5IbMMK1w/ListLatestTweetsTimeline',
13
- '/graphql/zJvfJs3gSbrVhC0MKjt_OQ/TweetDetail',
4
+ '/graphql/E3opETHurmVJflFsUBVuUQ/UserTweets',
5
+ '/graphql/Yka-W8dz7RaEuQNkroPkYw/UserByScreenName',
6
+ '/graphql/HJFjzBgCs16TqxewQOeLNg/HomeTimeline',
7
+ '/graphql/DiTkXJgLqBBxCs7zaYsbtA/HomeLatestTimeline',
8
+ '/graphql/bt4TKuFz4T7Ckk-VvQVSow/UserTweetsAndReplies',
9
+ '/graphql/dexO_2tohK86JDudXXG3Yw/UserMedia',
10
+ '/graphql/Qw77dDjp9xCpUY-AXwt-yQ/UserByRestId',
11
+ '/graphql/UN1i3zUiCWa-6r-Uaho4fw/SearchTimeline',
12
+ '/graphql/Pa45JvqZuKcW1plybfgBlQ/ListLatestTweetsTimeline',
13
+ '/graphql/QuBlQ6SxNAQCt6-kBiCXCQ/TweetDetail',
14
14
  ];
15
15
 
16
16
  const gqlMap = Object.fromEntries(graphQLEndpointsPlain.map((endpoint) => [endpoint.split('/')[3].replace(/V2$|Query$|QueryV2$/, ''), endpoint]));
17
17
 
18
18
  const gqlFeatureUser = {
19
- hidden_profile_likes_enabled: true,
20
19
  hidden_profile_subscriptions_enabled: true,
20
+ rweb_tipjar_consumption_enabled: true,
21
21
  responsive_web_graphql_exclude_directive_enabled: true,
22
22
  verified_phone_label_enabled: false,
23
23
  subscriptions_verification_info_is_identity_verified_enabled: true,
24
24
  subscriptions_verification_info_verified_since_enabled: true,
25
25
  highlights_tweets_tab_ui_enabled: true,
26
26
  responsive_web_twitter_article_notes_tab_enabled: true,
27
+ subscriptions_feature_can_gift_premium: true,
27
28
  creator_subscriptions_tweet_preview_api_enabled: true,
28
29
  responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
29
30
  responsive_web_graphql_timeline_navigation_enabled: true,
30
31
  };
31
32
  const gqlFeatureFeed = {
33
+ rweb_tipjar_consumption_enabled: true,
32
34
  responsive_web_graphql_exclude_directive_enabled: true,
33
35
  verified_phone_label_enabled: false,
34
36
  creator_subscriptions_tweet_preview_api_enabled: true,
35
37
  responsive_web_graphql_timeline_navigation_enabled: true,
36
38
  responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
39
+ communities_web_enable_tweet_community_results_fetch: true,
37
40
  c9s_tweet_anatomy_moderator_badge_enabled: true,
38
- tweetypie_unmention_optimization_enabled: true,
41
+ articles_preview_enabled: true,
39
42
  responsive_web_edit_tweet_api_enabled: true,
40
43
  graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
41
44
  view_counts_everywhere_api_enabled: true,
42
45
  longform_notetweets_consumption_enabled: true,
43
46
  responsive_web_twitter_article_tweet_consumption_enabled: true,
44
47
  tweet_awards_web_tipping_enabled: false,
48
+ creator_subscriptions_quote_tweet_preview_enabled: false,
45
49
  freedom_of_speech_not_reach_fetch_enabled: true,
46
50
  standardized_nudges_misinfo: true,
47
51
  tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
@@ -60,8 +64,7 @@ const TweetDetailFeatures = {
60
64
  responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
61
65
  communities_web_enable_tweet_community_results_fetch: true,
62
66
  c9s_tweet_anatomy_moderator_badge_enabled: true,
63
- articles_preview_enabled: false,
64
- tweetypie_unmention_optimization_enabled: true,
67
+ articles_preview_enabled: true,
65
68
  responsive_web_edit_tweet_api_enabled: true,
66
69
  graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
67
70
  view_counts_everywhere_api_enabled: true,
@@ -72,7 +75,6 @@ const TweetDetailFeatures = {
72
75
  freedom_of_speech_not_reach_fetch_enabled: true,
73
76
  standardized_nudges_misinfo: true,
74
77
  tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
75
- tweet_with_visibility_results_prefer_gql_media_interstitial_enabled: true,
76
78
  rweb_video_timestamps_enabled: true,
77
79
  longform_notetweets_rich_text_read_enabled: true,
78
80
  longform_notetweets_inline_media_enabled: true,
@@ -87,6 +89,7 @@ const gqlFeatures = {
87
89
  SearchTimeline: gqlFeatureFeed,
88
90
  ListLatestTweetsTimeline: gqlFeatureFeed,
89
91
  HomeTimeline: gqlFeatureFeed,
92
+ HomeLatestTimeline: TweetDetailFeatures,
90
93
  TweetDetail: TweetDetailFeatures,
91
94
  Likes: gqlFeatureFeed,
92
95
  };
@@ -0,0 +1,75 @@
1
+ import { authenticator } from 'otplib';
2
+ import logger from '@/utils/logger';
3
+ import cache from '@/utils/cache';
4
+ import { RateLimiterMemory, RateLimiterRedis, RateLimiterQueue } from 'rate-limiter-flexible';
5
+ import puppeteer from '@/utils/puppeteer';
6
+ import { CookieJar } from 'tough-cookie';
7
+
8
+ const loginLimiter = cache.clients.redisClient
9
+ ? new RateLimiterRedis({
10
+ points: 1,
11
+ duration: 20,
12
+ execEvenly: true,
13
+ storeClient: cache.clients.redisClient,
14
+ })
15
+ : new RateLimiterMemory({
16
+ points: 1,
17
+ duration: 20,
18
+ execEvenly: true,
19
+ });
20
+
21
+ const loginLimiterQueue = new RateLimiterQueue(loginLimiter);
22
+
23
+ async function login({ username, password, authenticationSecret }) {
24
+ if (!username || !password) {
25
+ return;
26
+ }
27
+ try {
28
+ await loginLimiterQueue.removeTokens(1);
29
+
30
+ const cookieJar = new CookieJar();
31
+ const browser = await puppeteer({
32
+ stealth: true,
33
+ });
34
+ const page = await browser.newPage();
35
+ await page.goto('https://x.com/i/flow/login');
36
+ await page.waitForSelector('input[autocomplete="username"]');
37
+ await page.type('input[autocomplete="username"]', username);
38
+ const buttons = await page.$$('button');
39
+ await buttons[3]?.click();
40
+ await page.waitForSelector('input[autocomplete="current-password"]');
41
+ await page.type('input[autocomplete="current-password"]', password);
42
+ (await page.waitForSelector('button[data-testid="LoginForm_Login_Button"]'))?.click();
43
+ if (authenticationSecret) {
44
+ await page.waitForSelector('input[inputmode="numeric"]');
45
+ const token = authenticator.generate(authenticationSecret);
46
+ await page.type('input[inputmode="numeric"]', token);
47
+ (await page.waitForSelector('button[data-testid="ocfEnterTextNextButton"]'))?.click();
48
+ }
49
+ const waitForRequest = new Promise<string>((resolve) => {
50
+ page.on('response', async (response) => {
51
+ if (response.url().includes('/HomeTimeline')) {
52
+ const data = await response.json();
53
+ const message = data?.data?.home?.home_timeline_urt?.instructions?.[0]?.entries?.[0]?.entryId;
54
+ if (message === 'messageprompt-suspended-prompt') {
55
+ logger.error(`twitter debug: twitter username ${username} login failed: messageprompt-suspended-prompt`);
56
+ resolve('');
57
+ }
58
+ const cookies = await page.cookies();
59
+ for (const cookie of cookies) {
60
+ cookieJar.setCookieSync(`${cookie.name}=${cookie.value}`, 'https://x.com');
61
+ }
62
+ logger.debug(`twitter debug: twitter username ${username} login success`);
63
+ resolve(JSON.stringify(cookieJar.serializeSync()));
64
+ }
65
+ });
66
+ });
67
+ const cookieString = await waitForRequest;
68
+ await browser.close();
69
+ return cookieString;
70
+ } catch (error) {
71
+ logger.error(`twitter debug: twitter username ${username} login failed:`, error);
72
+ }
73
+ }
74
+
75
+ export default login;
@@ -1,35 +1,18 @@
1
1
  import ConfigNotFoundError from '@/errors/types/config-not-found';
2
2
  import { baseUrl, gqlFeatures, bearerToken, gqlMap } from './constants';
3
3
  import { config } from '@/config';
4
- import got from '@/utils/got';
5
4
  import queryString from 'query-string';
6
5
  import { Cookie, CookieJar } from 'tough-cookie';
7
6
  import { CookieAgent, CookieClient } from 'http-cookie-agent/undici';
8
7
  import { ProxyAgent } from 'undici';
9
8
  import cache from '@/utils/cache';
10
9
  import logger from '@/utils/logger';
11
- import { RateLimiterMemory, RateLimiterRedis, RateLimiterQueue } from 'rate-limiter-flexible';
12
10
  import ofetch from '@/utils/ofetch';
13
11
  import proxy from '@/utils/proxy';
12
+ import login from './login';
14
13
 
15
- const dispatchers = {};
16
14
  let authTokenIndex = 0;
17
15
 
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
16
  const token2Cookie = (token) =>
34
17
  cache.tryGet(`twitter:cookie:${token}`, async () => {
35
18
  const jar = new CookieJar();
@@ -41,9 +24,19 @@ const token2Cookie = (token) =>
41
24
  uri: proxy.proxyUri,
42
25
  })
43
26
  : new CookieAgent({ cookies: { jar } });
44
- await got('https://x.com', {
45
- dispatcher: agent,
46
- });
27
+ if (token) {
28
+ await ofetch('https://x.com', {
29
+ dispatcher: agent,
30
+ });
31
+ } else {
32
+ const data = await ofetch('https://x.com/narendramodi?mx=2', {
33
+ dispatcher: agent,
34
+ });
35
+ const gt = data.match(/document\.cookie="gt=(\d+)/)?.[1];
36
+ if (gt) {
37
+ jar.setCookieSync(`gt=${gt}`, 'https://x.com');
38
+ }
39
+ }
47
40
  return JSON.stringify(jar.serializeSync());
48
41
  } catch {
49
42
  // ignore
@@ -51,15 +44,60 @@ const token2Cookie = (token) =>
51
44
  }
52
45
  });
53
46
 
54
- export const twitterGot = async (url, params) => {
55
- if (!config.twitter.authToken) {
56
- throw new ConfigNotFoundError('Twitter cookie is not configured');
47
+ const lockPrefix = 'twitter:lock-token1:';
48
+
49
+ const getAuth = async (retry: number) => {
50
+ if (config.twitter.authToken && retry > 0) {
51
+ const index = authTokenIndex++ % config.twitter.authToken.length;
52
+ const token = config.twitter.authToken[index];
53
+ const lock = await cache.get(`${lockPrefix}${token}`, false);
54
+ if (lock) {
55
+ await new Promise((resolve) => setTimeout(resolve, Math.random() * 500 + 500));
56
+ return await getAuth(retry - 1);
57
+ } else {
58
+ logger.debug(`twitter debug: lock twitter cookie for token ${token}`);
59
+ await cache.set(`${lockPrefix}${token}`, '1', 20);
60
+ return {
61
+ token,
62
+ username: config.twitter.username?.[index],
63
+ password: config.twitter.password?.[index],
64
+ authenticationSecret: config.twitter.authenticationSecret?.[index],
65
+ };
66
+ }
67
+ }
68
+ };
69
+
70
+ export const twitterGot = async (
71
+ url,
72
+ params,
73
+ options?: {
74
+ allowNoAuth?: boolean;
57
75
  }
58
- await loginLimiterQueue.removeTokens(1);
59
- const token = config.twitter.authToken[authTokenIndex++ % config.twitter.authToken.length];
60
- let cookie = await token2Cookie(token);
76
+ ) => {
77
+ const auth = await getAuth(30);
78
+
79
+ if (!auth && !options?.allowNoAuth) {
80
+ throw new ConfigNotFoundError('No valid Twitter token found');
81
+ }
82
+
83
+ const requestUrl = `${url}?${queryString.stringify(params)}`;
84
+
85
+ let cookie: string | Record<string, any> | null | undefined = await token2Cookie(auth?.token);
86
+ if (!cookie && auth) {
87
+ cookie = await login({
88
+ username: auth.username,
89
+ password: auth.password,
90
+ authenticationSecret: auth.authenticationSecret,
91
+ });
92
+ }
93
+ let dispatchers:
94
+ | {
95
+ jar: CookieJar;
96
+ agent: CookieAgent | ProxyAgent;
97
+ }
98
+ | undefined;
61
99
  if (cookie) {
62
- logger.debug(`Got twitter cookie for token ${token}`);
100
+ logger.debug(`twitter debug: got twitter cookie for token ${auth?.token}`);
63
101
  if (typeof cookie === 'string') {
64
102
  cookie = JSON.parse(cookie);
65
103
  }
@@ -70,22 +108,28 @@ export const twitterGot = async (url, params) => {
70
108
  uri: proxy.proxyUri,
71
109
  })
72
110
  : new CookieAgent({ cookies: { jar } });
73
- dispatchers[token] = {
111
+ if (proxy.proxyUri) {
112
+ logger.debug(`twitter debug: Proxying request: ${requestUrl}`);
113
+ }
114
+ dispatchers = {
74
115
  jar,
75
116
  agent,
76
117
  };
77
- } else {
78
- throw new ConfigNotFoundError(`Twitter cookie for token ${token} is not valid`);
118
+ } else if (auth) {
119
+ throw new ConfigNotFoundError(`Twitter cookie for token ${auth?.token} is not valid`);
79
120
  }
80
- const jsonCookie = Object.fromEntries(
81
- dispatchers[token].jar
82
- .getCookieStringSync(url)
83
- .split(';')
84
- .map((c) => Cookie.parse(c)?.toJSON())
85
- .map((c) => [c?.key, c?.value])
86
- );
121
+ const jsonCookie = dispatchers
122
+ ? Object.fromEntries(
123
+ dispatchers.jar
124
+ .getCookieStringSync(url)
125
+ .split(';')
126
+ .map((c) => Cookie.parse(c)?.toJSON())
127
+ .map((c) => [c?.key, c?.value])
128
+ )
129
+ : {};
87
130
 
88
- const response = await ofetch.raw(`${url}?${queryString.stringify(params)}`, {
131
+ const response = await ofetch.raw(requestUrl, {
132
+ retry: 0,
89
133
  headers: {
90
134
  authority: 'x.com',
91
135
  accept: '*/*',
@@ -95,24 +139,75 @@ export const twitterGot = async (url, params) => {
95
139
  'content-type': 'application/json',
96
140
  dnt: '1',
97
141
  pragma: 'no-cache',
98
- referer: 'https://x.com/narendramodi',
142
+ referer: 'https://x.com/',
99
143
  'x-twitter-active-user': 'yes',
100
- 'x-twitter-auth-type': 'OAuth2Session',
101
144
  'x-twitter-client-language': 'en',
102
145
  'x-csrf-token': jsonCookie.ct0,
146
+ ...(auth?.token
147
+ ? {
148
+ 'x-twitter-auth-type': 'OAuth2Session',
149
+ }
150
+ : {
151
+ 'x-guest-token': jsonCookie.gt,
152
+ }),
103
153
  },
104
- dispatcher: dispatchers[token].agent,
154
+ dispatcher: dispatchers?.agent,
105
155
  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);
156
+ const remaining = response.headers.get('x-rate-limit-remaining');
157
+ const remainingInt = Number.parseInt(remaining || '0');
158
+ const reset = response.headers.get('x-rate-limit-reset');
159
+ logger.debug(`twitter debug: twitter rate limit remaining for token ${auth?.token} is ${remaining} and reset at ${reset}`);
160
+ if (auth) {
161
+ if (remaining && remainingInt < 2 && reset) {
162
+ const resetTime = new Date(Number.parseInt(reset) * 1000);
163
+ const delay = (resetTime.getTime() - Date.now()) / 1000;
164
+ logger.debug(`twitter debug: twitter rate limit exceeded for token ${auth.token} with status ${response.status}, will unlock after ${delay}s`);
165
+ await cache.set(`${lockPrefix}${auth.token}`, '1', Math.ceil(delay));
166
+ } else if (response.status === 429 || JSON.stringify(response._data?.data) === '{"user":{}}') {
167
+ logger.debug(`twitter debug: twitter rate limit exceeded for token ${auth.token} with status ${response.status}`);
168
+ await cache.set(`${lockPrefix}${auth.token}`, '1', 2000);
169
+ } else if (response.status === 403 || response.status === 401) {
170
+ const newCookie = await login({
171
+ username: auth.username,
172
+ password: auth.password,
173
+ authenticationSecret: auth.authenticationSecret,
174
+ });
175
+ if (newCookie) {
176
+ logger.debug(`twitter debug: reset twitter cookie for token ${auth.token}, ${newCookie}`);
177
+ await cache.set(`twitter:cookie:${auth.token}`, newCookie, config.cache.contentExpire);
178
+ logger.debug(`twitter debug: unlock twitter cookie for token ${auth.token} with error1`);
179
+ await cache.set(`${lockPrefix}${auth.token}`, '', 1);
180
+ } else {
181
+ const tokenIndex = config.twitter.authToken?.indexOf(auth.token);
182
+ if (tokenIndex !== undefined && tokenIndex !== -1) {
183
+ config.twitter.authToken?.splice(tokenIndex, 1);
184
+ }
185
+ if (auth.username) {
186
+ const usernameIndex = config.twitter.username?.indexOf(auth.username);
187
+ if (usernameIndex !== undefined && usernameIndex !== -1) {
188
+ config.twitter.username?.splice(usernameIndex, 1);
189
+ }
190
+ }
191
+ if (auth.password) {
192
+ const passwordIndex = config.twitter.password?.indexOf(auth.password);
193
+ if (passwordIndex !== undefined && passwordIndex !== -1) {
194
+ config.twitter.password?.splice(passwordIndex, 1);
195
+ }
196
+ }
197
+ logger.debug(`twitter debug: delete twitter cookie for token ${auth.token} with status ${response.status}, remaining tokens: ${config.twitter.authToken?.length}`);
198
+ await cache.set(`${lockPrefix}${auth.token}`, '1', 86400);
199
+ }
200
+ } else {
201
+ logger.debug(`twitter debug: unlock twitter cookie with success for token ${auth.token}`);
202
+ await cache.set(`${lockPrefix}${auth.token}`, '', 1);
203
+ }
109
204
  }
110
205
  },
111
206
  });
112
207
 
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);
208
+ if (auth?.token) {
209
+ logger.debug(`twitter debug: update twitter cookie for token ${auth.token}`);
210
+ await cache.set(`twitter:cookie:${auth.token}`, JSON.stringify(dispatchers?.jar.serializeSync()), config.cache.contentExpire);
116
211
  }
117
212
 
118
213
  return response._data;
@@ -138,13 +233,13 @@ export const paginationTweets = async (endpoint: string, userId: number | undefi
138
233
  instructions = data.user.result.timeline_v2.timeline.instructions;
139
234
  } else {
140
235
  // 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)}`);
236
+ logger.debug(`twitter debug: instructions not found in data: ${JSON.stringify(data)}`);
142
237
  }
143
238
  }
144
239
 
145
- const entries1 = instructions.find((i) => i.type === 'TimelineAddToModule')?.moduleItems; // Media
146
- const entries2 = instructions.find((i) => i.type === 'TimelineAddEntries').entries;
147
- return entries1 || entries2;
240
+ const entries1 = instructions?.find((i) => i.type === 'TimelineAddToModule')?.moduleItems; // Media
241
+ const entries2 = instructions?.find((i) => i.type === 'TimelineAddEntries').entries;
242
+ return entries1 || entries2 || [];
148
243
  };
149
244
 
150
245
  export function gatherLegacyFromData(entries: any[], filterNested?: string[], userId?: number | string) {
@@ -0,0 +1,60 @@
1
+ import { Route } from '@/types';
2
+ import utils from './utils';
3
+ import api from './api';
4
+
5
+ export const route: Route = {
6
+ path: '/home_latest/:routeParams?',
7
+ categories: ['social-media'],
8
+ example: '/twitter/home_latest',
9
+ features: {
10
+ requireConfig: [
11
+ {
12
+ name: 'TWITTER_USERNAME',
13
+ description: 'Please see above for details.',
14
+ },
15
+ {
16
+ name: 'TWITTER_PASSWORD',
17
+ description: 'Please see above for details.',
18
+ },
19
+ {
20
+ name: 'TWITTER_AUTH_TOKEN',
21
+ description: 'Please see above for details.',
22
+ },
23
+ ],
24
+ requirePuppeteer: false,
25
+ antiCrawler: false,
26
+ supportBT: false,
27
+ supportPodcast: false,
28
+ supportScihub: false,
29
+ },
30
+ name: 'Home latest timeline',
31
+ maintainers: ['DIYgod', 'CaoMeiYouRen'],
32
+ handler,
33
+ radar: [
34
+ {
35
+ source: ['x.com/home'],
36
+ target: '/home_latest',
37
+ },
38
+ ],
39
+ };
40
+
41
+ async function handler(ctx) {
42
+ // For compatibility
43
+ const { count, include_rts } = utils.parseRouteParams(ctx.req.param('routeParams'));
44
+ const params = count ? { count } : {};
45
+
46
+ await api.init();
47
+ let data = await api.getHomeLatestTimeline('', params);
48
+ if (!include_rts) {
49
+ data = utils.excludeRetweet(data);
50
+ }
51
+
52
+ return {
53
+ title: `Twitter following timeline`,
54
+ link: `https://x.com/home`,
55
+ // description: userInfo?.description,
56
+ item: utils.ProcessFeed(ctx, {
57
+ data,
58
+ }),
59
+ };
60
+ }
@@ -41,11 +41,14 @@ export const route: Route = {
41
41
 
42
42
  async function handler(ctx) {
43
43
  const id = ctx.req.param('id');
44
- const { count } = utils.parseRouteParams(ctx.req.param('routeParams'));
44
+ const { count, include_rts } = utils.parseRouteParams(ctx.req.param('routeParams'));
45
45
  const params = count ? { count } : {};
46
46
 
47
47
  await api.init();
48
- const data = await api.getList(id, params);
48
+ let data = await api.getList(id, params);
49
+ if (!include_rts) {
50
+ data = utils.excludeRetweet(data);
51
+ }
49
52
 
50
53
  return {
51
54
  title: `Twitter List - ${id}`,
@@ -1,6 +1,7 @@
1
1
  import { Route, ViewType } from '@/types';
2
2
  import api from './api';
3
3
  import utils from './utils';
4
+ import logger from '@/utils/logger';
4
5
 
5
6
  export const route: Route = {
6
7
  path: '/media/:id/:routeParams?',
@@ -47,7 +48,12 @@ async function handler(ctx) {
47
48
 
48
49
  await api.init();
49
50
  const userInfo = await api.getUser(id);
50
- const data = await api.getUserMedia(id, params);
51
+ let data;
52
+ try {
53
+ data = await api.getUserMedia(id, params);
54
+ } catch (error) {
55
+ logger.error(error);
56
+ }
51
57
  const profileImageUrl = userInfo?.profile_image_url || userInfo?.profile_image_url_https;
52
58
 
53
59
  return {
@@ -55,8 +61,11 @@ async function handler(ctx) {
55
61
  link: `https://x.com/${userInfo?.screen_name}/media`,
56
62
  image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
57
63
  description: userInfo?.description,
58
- item: utils.ProcessFeed(ctx, {
59
- data,
60
- }),
64
+ item:
65
+ data &&
66
+ utils.ProcessFeed(ctx, {
67
+ data,
68
+ }),
69
+ allowEmpty: true,
61
70
  };
62
71
  }
@@ -2,6 +2,7 @@ import { Route } from '@/types';
2
2
  import api from './api';
3
3
  import utils from './utils';
4
4
  import { fallback, queryToBoolean } from '@/utils/readable-social';
5
+ import { config } from '@/config';
5
6
 
6
7
  export const route: Route = {
7
8
  path: '/tweet/:id/status/:status/:original?',
@@ -1,6 +1,7 @@
1
1
  import { Route, ViewType } from '@/types';
2
2
  import utils from './utils';
3
3
  import api from './api';
4
+ import logger from '@/utils/logger';
4
5
 
5
6
  export const route: Route = {
6
7
  path: '/user/:id/:routeParams?',
@@ -58,9 +59,14 @@ async function handler(ctx) {
58
59
 
59
60
  await api.init();
60
61
  const userInfo = await api.getUser(id);
61
- let data = await (exclude_replies ? api.getUserTweets(id, params) : api.getUserTweetsAndReplies(id, params));
62
- if (!include_rts) {
63
- data = utils.excludeRetweet(data);
62
+ let data;
63
+ try {
64
+ data = await (exclude_replies ? api.getUserTweets(id, params) : api.getUserTweetsAndReplies(id, params));
65
+ if (!include_rts) {
66
+ data = utils.excludeRetweet(data);
67
+ }
68
+ } catch (error) {
69
+ logger.error(error);
64
70
  }
65
71
 
66
72
  const profileImageUrl = userInfo?.profile_image_url || userInfo?.profile_image_url_https;
@@ -70,8 +76,11 @@ async function handler(ctx) {
70
76
  link: `https://x.com/${userInfo?.screen_name}`,
71
77
  image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
72
78
  description: userInfo?.description,
73
- item: utils.ProcessFeed(ctx, {
74
- data,
75
- }),
79
+ item:
80
+ data &&
81
+ utils.ProcessFeed(ctx, {
82
+ data,
83
+ }),
84
+ allowEmpty: true,
76
85
  };
77
86
  }
@@ -21,7 +21,7 @@ export const route: Route = {
21
21
  },
22
22
  radar: [
23
23
  {
24
- source: ['www.uber.com/blog/pittsburgh/engineering'],
24
+ source: ['www.uber.com/:language/blog/engineering', 'www.uber.com/:language/blog'],
25
25
  target: '/blog',
26
26
  },
27
27
  ],
@@ -87,11 +87,11 @@ async function handler(ctx) {
87
87
 
88
88
  if (data.publisher.name === '轉角國際 udn Global') {
89
89
  // 轉角24小時
90
- description += $('.story_body_content')
90
+ description = $('.story_body_content')
91
91
  .html()
92
92
  .split(/<!--\d+?-->/g)
93
93
  .slice(1, -1)
94
- .join(',');
94
+ .join('');
95
95
  }
96
96
 
97
97
  return {