From 9646761c4bfe922e55b1b6c6dfedfc589fea0c86 Mon Sep 17 00:00:00 2001 From: "pull[bot]" <39814207+pull[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 02:56:28 +0800 Subject: [PATCH] [pull] master from diygod:master (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: Update InstanceList.tsx (#14244) Add instance hosted by Kai. * feat(route): recover kuwaitlocal agirls qianp taiwannews jiaoliudao (#14247) * fix: recover kuwaitlocal * fix: recover agirls * fix: recover qianp * fix: recover taiwannews * fix: recover hket * fix: recover jiaoliudao * fix: qianp * fix: deepscan issue * feat(route): zhihu xhu posts (#14246) * feat: recover shuiguopai (#14248) * chore(deps-dev): bump @types/react from 18.2.47 to 18.2.48 in /website (#14255) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.47 to 18.2.48. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump pinyin-pro from 3.19.0 to 3.19.2 in /website (#14256) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.19.0 to 3.19.2. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/commits/3.19.2) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump supertest from 6.3.3 to 6.3.4 (#14250) * chore(deps-dev): bump supertest from 6.3.3 to 6.3.4 Bumps [supertest](https://github.com/ladjs/supertest) from 6.3.3 to 6.3.4. - [Release notes](https://github.com/ladjs/supertest/releases) - [Commits](https://github.com/ladjs/supertest/compare/v6.3.3...v6.3.4) --- updated-dependencies: - dependency-name: supertest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint-plugin-yml from 1.11.0 to 1.12.0 (#14251) * chore(deps-dev): bump eslint-plugin-yml from 1.11.0 to 1.12.0 Bumps [eslint-plugin-yml](https://github.com/ota-meshi/eslint-plugin-yml) from 1.11.0 to 1.12.0. - [Release notes](https://github.com/ota-meshi/eslint-plugin-yml/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-yml/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-yml/compare/v1.11.0...v1.12.0) --- updated-dependencies: - dependency-name: eslint-plugin-yml dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 3.2.1 to 3.2.2 (#14252) * chore(deps-dev): bump prettier from 3.2.1 to 3.2.2 Bumps [prettier](https://github.com/prettier/prettier) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.2.1...3.2.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @tonyrl/rand-user-agent from 2.0.45 to 2.0.46 (#14253) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.45 to 2.0.46 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.45 to 2.0.46. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.45...v2.0.46) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump nock from 13.4.0 to 13.5.0 (#14254) * chore(deps-dev): bump nock from 13.4.0 to 13.5.0 Bumps [nock](https://github.com/nock/nock) from 13.4.0 to 13.5.0. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.4.0...v13.5.0) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: Add deploy to Sealos (#14259) * fix(route): coolapk publish time (#14261) * chore(deps-dev): bump nodemon from 3.0.2 to 3.0.3 (#14264) * chore(deps-dev): bump nodemon from 3.0.2 to 3.0.3 Bumps [nodemon](https://github.com/remy/nodemon) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/remy/nodemon/releases) - [Commits](https://github.com/remy/nodemon/compare/v3.0.2...v3.0.3) --- updated-dependencies: - dependency-name: nodemon dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump imapflow from 1.0.147 to 1.0.148 (#14265) * chore(deps): bump imapflow from 1.0.147 to 1.0.148 Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.0.147 to 1.0.148. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.0.147...v1.0.148) --- updated-dependencies: - dependency-name: imapflow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump googleapis from 130.0.0 to 131.0.0 (#14266) * chore(deps): bump googleapis from 130.0.0 to 131.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 130.0.0 to 131.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/release-please-config.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v130.0.0...googleapis-v131.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(docs): fix some working notOperational routes (#14270) * chore(deps-dev): bump prettier from 3.2.2 to 3.2.4 (#14273) * chore(deps-dev): bump prettier from 3.2.2 to 3.2.4 Bumps [prettier](https://github.com/prettier/prettier) from 3.2.2 to 3.2.4. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.2.2...3.2.4) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * style: auto format * style(eslint): add eslint-unicorn (#14257) * style: add eslint-unicorn * style: fix unicorn/no-useless-spread * style: fix unicorn/no-useless-promise-resolve-reject * style: fix unicorn/no-for-loop * fix: codeql bad HTML filtering regexp * fix: codeql incomplete replace * fix: unicorn/no-abusive-eslint-disable * style: fix unicorn/no-new-array * style: fix unicorn/no-typeof-undefined * style: fix unicorn/no-zero-fractions * style: fix unicorn/no-empty-file * style: fix unicorn/prefer-date-now * revert: auto fix unicorn/prefer-switch on lib/v2/kuaidi100/utils.js * style: fix unicorn/prefer-array-find * style: fix unicorn/prefer-array-flat * style: fix unicorn/prefer-array-flat-map * style: fix unicorn/prefer-at * style: fix unicorn/prefer-string-starts-ends-with * style: fix unicorn/prefer-includes * fix: codeql URL substring sanitization * style: fix unicorn/prefer-optional-catch-binding * style: fix unicorn/catch-error-name * style: fix unicorn/escape-case * style: fix unicorn/prefer-native-coercion-functions * style: fix unicorn/prefer-regexp-test * style: fix unicorn/require-array-join-separator * style: fix unicorn/prefer-math-trunc * style: fix unicorn/prefer-negative-index * style: fix unicorn/prefer-dom-node-dataset * style: fix unicorn/prefer-dom-node-text-content * style: fix unicorn/prefer-query-selector * style: fix unicorn/no-array-for-each * style: fix unicorn/no-negated-condition * style: fix unicorn/prefer-add-event-listener * style: fix unicorn/import-style * style: fix prefer-regex-literals * style: disable unicorn/no-useless-switch-case * style: disable unicorn/text-encoding-identifier-case * style: fix unicorn/prefer-set-has * style: fix unicorn/prefer-spread * revert: auto fix on lib/routes/universities/ynnu/edu/base64.js * style: fix unicorn/no-useless-undefined * style: fix unicorn/no-array-push-push * style: fix unicorn/no-useless-undefined again * style: fix unicorn/no-lonely-if * style: fix unicorn/prefer-reflect-apply * style: fix unicorn/switch-case-braces * style: fix unicorn/prefer-switch * style: fix unicorn/prefer-array-some * fix: deepscan UNUSED_VAR_ASSIGN * style: fix unicorn/prefer-ternary * fix: follow-up of unicorn/prefer-ternary * revert: auto fix of unicorn/prefer-string-slice for substring() * style: disable unicorn/prefer-string-slice fix: auto fix slice over deprecated substr * style: fix unicorn/throw-new-error * style: fix unicorn/filename-case * test: fix dateParser renaming * style: fix unicorn/better-regex * style: fix unicorn/prefer-string-replace-all * fix(deps): add sanitize-html * style: fix no-prototype-builtins * style: fix unicorn/consistent-destructuring * style: fix unicorn/consistent-function-scoping * style: fix unicorn/prefer-regexp-test * style: fix unicorn/prefer-logical-operator-over-ternary * style: fix unicorn/no-array-callback-reference * style: add prefer-object-has-own * style: warn unicorn/no-empty-file * style: fix unicorn/prefer-number-properties * style: fix no-useless-undefined again * style: fix unicorn/numeric-separators-style * style: disable unicorn/no-array-callback-reference false postive with cheerio * style: auto format * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.5.3 to 1.5.4 (#14274) * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.5.3 to 1.5.4 Bumps [@stylistic/eslint-plugin-js](https://github.com/eslint-stylistic/eslint-stylistic/tree/HEAD/packages/eslint-plugin-js) from 1.5.3 to 1.5.4. - [Release notes](https://github.com/eslint-stylistic/eslint-stylistic/releases) - [Commits](https://github.com/eslint-stylistic/eslint-stylistic/commits/v1.5.4/packages/eslint-plugin-js) --- updated-dependencies: - dependency-name: "@stylistic/eslint-plugin-js" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: Add Thai Department Of Lands e-LandsAnnouncement website (#14245) * feat: Add Thai DOL website * feat: Add timezone, Radar for Thai DOL e-LandsAnnoucements * Update lib/v2/dol/maintainer.js * Update website/docs/routes/government.mdx * Update lib/v2/dol/radar.js --------- Co-authored-by: rrachasak * style: auto format * chore(deps-dev): bump eslint-plugin-yml from 1.12.0 to 1.12.2 (#14263) * chore(deps-dev): bump eslint-plugin-yml from 1.12.0 to 1.12.2 Bumps [eslint-plugin-yml](https://github.com/ota-meshi/eslint-plugin-yml) from 1.12.0 to 1.12.2. - [Release notes](https://github.com/ota-meshi/eslint-plugin-yml/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-yml/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-yml/compare/v1.12.0...v1.12.2) --- updated-dependencies: - dependency-name: eslint-plugin-yml dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): broken item URL in Google Scholar Author Citations (#14277) * fix broken url in item fields * add author information in item field * add query parameter usage * fine-tune channel title&description * add maintainer * shorten the itemUrl definition --------- * style(eslint): add `default-case`: 1 `default-case-last`: 2 `unicorn/prefer-spread`: 2->1 `unicorn/prefer-switch`: 2->1 * chore(deps): bump pinyin-pro from 3.19.2 to 3.19.3 in /website (#14278) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.19.2 to 3.19.3. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/compare/3.19.2...3.19.3) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add TradingView Pine Script™ Release notes (#14279) * style: auto format * feat(route): New route: MiYouShe - User Posts; Optimize the routing of MiYouShe | 新增路由:米游社 - 用户帖子;优化米游社路由 (#14268) * fix(route): 修复 米游社 公告栏 template 错误 * feat(route): 新增 bing 搜索 * docs: Update other.mdx * docs: fix docs * feat(route): 新增 百度搜索 * fix(route): 修复 pubDate 解析错误 * fix(route): 优化 百度搜索的缓存,减轻反爬问题 * feat(route): 新增 360 搜索 * feat(route): 迁移 搜狗特色LOGO 到 v2 规范;添加 搜狗搜索 * fix(route): 百度搜索增加图片 * feat(route): 新增 Google Search * fix(route): 修复 百度搜索相关问题 * fix(route): 修复 Google 相关问题 * fix(route): 修复 360 搜索 * fix(route): 修复 搜狗搜索 * fix(route): 修复 await 问题 * fix: 移除 google sites * fix(route): 修复 缓存和过滤逻辑问题 * fix(route): 修复 360 搜索缺少 cookie 的问题 * fix(route): 修复 360 搜索 cookie 的问题 * feat(route): 移除 so.com 路由 * fix: merge conflict * feat(route): 新增路由:米游社 - 用户帖子;优化米游社路由 * fix(route): 修复 米游社用户帖子路由 * feat(route): 新增 米游社 用户关注路由 * fix(route): 修复 米游社帖子可能缺失封面的 bug;增加 点赞和评论数 * fix(route): 修复 米游社路由的规范和 bug --------- * fix: twitter changed the fields * fix: 修复界面标题重复 (#14283) * fix(jiemian): 标题重复 * Update lib/v2/jiemian/lists.js --------- * feat(secrss): full text support (#14284) * feat(secrss): full text support * fix(secrss): pubDate author category * fix(route): tencent gifs (#14286) * feat(route): add 中国期货市场监控中心 (#14287) * feat(route): add 中国期货市场监控中心 * Update lib/v2/cfmmc/radar.js --------- * style: auto format * feat(route): bjedu (#14288) * chore(deps): bump twitter-api-v2 from 1.15.2 to 1.16.0 (#14292) * chore(deps): bump twitter-api-v2 from 1.15.2 to 1.16.0 Bumps [twitter-api-v2](https://github.com/plhery/node-twitter-api-v2) from 1.15.2 to 1.16.0. - [Release notes](https://github.com/plhery/node-twitter-api-v2/releases) - [Changelog](https://github.com/PLhery/node-twitter-api-v2/blob/master/changelog.md) - [Commits](https://github.com/plhery/node-twitter-api-v2/compare/1.15.2...1.16.0) --- updated-dependencies: - dependency-name: twitter-api-v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump dotenv from 16.3.1 to 16.3.2 (#14291) * chore(deps): bump dotenv from 16.3.1 to 16.3.2 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.3.1 to 16.3.2. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.3.1...v16.3.2) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.93.0 to 7.94.1 (#14293) * chore(deps): bump @sentry/node from 7.93.0 to 7.94.1 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.93.0 to 7.94.1. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.93.0...7.94.1) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route/twitter/web-api): excludeReplies=1 (#14290) Fixes #14285 The postprocessing that removes replies accidentally removed all tweets except for the first one after the recent Twitter update. Signed-off-by: Rongrong * fix(route): 金色财经 (#14272) * fix(route): 金色财经 * style: lint * docs: move to finance --------- * fix(route/twitter): readability and RT link (#14289) 1. Trim link pointing to the tweet itself (usually appears when the tweet is truncated). 2. Only insert
when both showTimestampInDescription and readable are enabled. 3. Make RT links point to the RT itself, instead of the original tweet. Signed-off-by: Rongrong * feat(route): laimanhua (#14297) * fix: codeql incomplete multi-character sanitization (#14298) * build(docker): don't copy docs * feat(route): scmp topics (#14299) * feat(route): scmp topics * docs: remove /en from docs url * fix: router order * fix: http verb * fix: recover cntheory (#14300) * fix: recover cntheory * fix: replaceAll * feat(route): keepass (#14301) * fix(route): huanqiu (#14304) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.46 to 2.0.47 (#14309) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.46 to 2.0.47 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.46 to 2.0.47. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.46...v2.0.47) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump string-width from 7.0.0 to 7.1.0 (#14308) * chore(deps-dev): bump string-width from 7.0.0 to 7.1.0 Bumps [string-width](https://github.com/sindresorhus/string-width) from 7.0.0 to 7.1.0. - [Release notes](https://github.com/sindresorhus/string-width/releases) - [Commits](https://github.com/sindresorhus/string-width/compare/v7.0.0...v7.1.0) --- updated-dependencies: - dependency-name: string-width dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump chrono-node from 2.7.4 to 2.7.5 (#14307) * chore(deps): bump chrono-node from 2.7.4 to 2.7.5 Bumps [chrono-node](https://github.com/wanasit/chrono) from 2.7.4 to 2.7.5. - [Release notes](https://github.com/wanasit/chrono/releases) - [Commits](https://github.com/wanasit/chrono/compare/v2.7.4...v2.7.5) --- updated-dependencies: - dependency-name: chrono-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump jsdom from 23.2.0 to 24.0.0 (#14306) * chore(deps): bump jsdom from 23.2.0 to 24.0.0 Bumps [jsdom](https://github.com/jsdom/jsdom) from 23.2.0 to 24.0.0. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/23.2.0...24.0.0) --- updated-dependencies: - dependency-name: jsdom dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/crypto-js from 4.2.1 to 4.2.2 (#14305) * chore(deps-dev): bump @types/crypto-js from 4.2.1 to 4.2.2 Bumps [@types/crypto-js](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/crypto-js) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/crypto-js) --- updated-dependencies: - dependency-name: "@types/crypto-js" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): mrm (#14311) * feat(route): mrm * fix: maintainer * feat(route): add 川流严选 (#14303) * feat(route): add 川流严选 * fix: add null check * fix: add null check * style: auto format * fix(route/tencent): Circumvent video type link. (#14302) * fix(route/tencent): Circumvent video type link. * . * chore: decrease `js_wait` in docs.rsshub.app.json * fix: use sanitize-html (#14312) * fix: use sanitize-html * test: add brief test * chore(deps): bump dotenv from 16.3.2 to 16.4.0 (#14316) * chore(deps): bump dotenv from 16.3.2 to 16.4.0 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.3.2 to 16.4.0. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.3.2...v16.4.0) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.94.1 to 7.95.0 (#14317) * chore(deps): bump @sentry/node from 7.94.1 to 7.95.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.94.1 to 7.95.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.94.1...7.95.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump dotenv from 16.4.0 to 16.4.1 (#14320) * chore(deps): bump dotenv from 16.4.0 to 16.4.1 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.4.0 to 16.4.1. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.4.0...v16.4.1) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 21.7.0 to 21.9.0 (#14321) * chore(deps): bump puppeteer from 21.7.0 to 21.9.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.7.0 to 21.9.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.7.0...puppeteer-v21.9.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: update instance list * fix(route): Foresight News column (#14322) * chore(deps): bump peter-evans/dockerhub-description from 3 to 4 (#14323) Bumps [peter-evans/dockerhub-description](https://github.com/peter-evans/dockerhub-description) from 3 to 4. - [Release notes](https://github.com/peter-evans/dockerhub-description/releases) - [Commits](https://github.com/peter-evans/dockerhub-description/compare/v3...v4) --- updated-dependencies: - dependency-name: peter-evans/dockerhub-description dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.95.0 to 7.98.0 (#14325) * chore(deps): bump @sentry/node from 7.95.0 to 7.98.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.95.0 to 7.98.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.95.0...7.98.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump lru-cache from 10.1.0 to 10.2.0 (#14324) * chore(deps): bump lru-cache from 10.1.0 to 10.2.0 Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 10.1.0 to 10.2.0. - [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-lru-cache/compare/v10.1.0...v10.2.0) --- updated-dependencies: - dependency-name: lru-cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump husky from 8.0.3 to 9.0.5 (#14326) * chore(deps-dev): bump husky from 8.0.3 to 9.0.5 Bumps [husky](https://github.com/typicode/husky) from 8.0.3 to 9.0.5. - [Release notes](https://github.com/typicode/husky/releases) - [Commits](https://github.com/typicode/husky/compare/v8.0.3...v9.0.5) --- updated-dependencies: - dependency-name: husky dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install * chore: migrate to husky v9 https://github.com/typicode/husky/releases/tag/v9.0.1 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the docusaurus group in /website with 7 updates (#14328) Bumps the docusaurus group in /website with 7 updates: | Package | From | To | | --- | --- | --- | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.1.0` | `3.1.1` | | [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) | `3.1.0` | `3.1.1` | | [@docusaurus/plugin-pwa](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-pwa) | `3.1.0` | `3.1.1` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.1.0` | `3.1.1` | | [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) | `3.1.0` | `3.1.1` | | [@docusaurus/tsconfig](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-tsconfig) | `3.1.0` | `3.1.1` | | [@docusaurus/types](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-types) | `3.1.0` | `3.1.1` | Updates `@docusaurus/core` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus) Updates `@docusaurus/plugin-client-redirects` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-plugin-client-redirects) Updates `@docusaurus/plugin-pwa` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-plugin-pwa) Updates `@docusaurus/preset-classic` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-preset-classic) Updates `@docusaurus/module-type-aliases` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-module-type-aliases) Updates `@docusaurus/tsconfig` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-tsconfig) Updates `@docusaurus/types` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.1.1/packages/docusaurus-types) --- updated-dependencies: - dependency-name: "@docusaurus/core" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/plugin-client-redirects" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/plugin-pwa" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/module-type-aliases" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/tsconfig" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: docusaurus - dependency-name: "@docusaurus/types" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: docusaurus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @dipakparmar/docusaurus-plugin-umami in /website (#14329) Bumps [@dipakparmar/docusaurus-plugin-umami](https://github.com/dipakparmar/docusaurus-plugin-umami) from 2.1.2 to 2.1.3. - [Release notes](https://github.com/dipakparmar/docusaurus-plugin-umami/releases) - [Commits](https://github.com/dipakparmar/docusaurus-plugin-umami/compare/v2.1.2...v2.1.3) --- updated-dependencies: - dependency-name: "@dipakparmar/docusaurus-plugin-umami" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump husky from 9.0.5 to 9.0.6 (#14327) * chore(deps-dev): bump husky from 9.0.5 to 9.0.6 Bumps [husky](https://github.com/typicode/husky) from 9.0.5 to 9.0.6. - [Release notes](https://github.com/typicode/husky/releases) - [Commits](https://github.com/typicode/husky/compare/v9.0.5...v9.0.6) --- updated-dependencies: - dependency-name: husky dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route/twitter): reply URL; threads when excludeReplies=1 (#14330) Without `legacy.id_str`, reply URLs would be constructed with conversion IDs, resulting in: - wrong URLs: pointing to the first tweet in the conversion (thread) - multiple items with the same guid: each tweet in a conversion (thread) had the same URL and thus guid The bug was caused by the widely used temporary fix (legacy.id_str || legacy.conversation_id_str) after Twitter had upgraded their API. Thus, it is quite easy to fix it: Refill legacy.id_str using the rest_id that resides in the container of legacy. With it fixed, take a step further to include threads when excludeReplies=1 (simulating the behavior of Twitter Web/App). Signed-off-by: Rongrong * fix(route): Firefox release notes (#14331) * fix javlibrary genre route because some spans may not have class attr in individual pages (#14334) * fix(route): guanhai (#14337) * feat: support twitter username and password login * feat: add TWITTER_AUTHENTICATION_SECRET * docs: add twitter config tips * chore(deps): bump @tonyrl/rand-user-agent from 2.0.47 to 2.0.48 (#14342) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.47 to 2.0.48 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.47 to 2.0.48. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.47...v2.0.48) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump husky from 9.0.6 to 9.0.7 (#14343) * chore(deps-dev): bump husky from 9.0.6 to 9.0.7 Bumps [husky](https://github.com/typicode/husky) from 9.0.6 to 9.0.7. - [Release notes](https://github.com/typicode/husky/releases) - [Commits](https://github.com/typicode/husky/compare/v9.0.6...v9.0.7) --- updated-dependencies: - dependency-name: husky dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump nock from 13.5.0 to 13.5.1 (#14344) * chore(deps-dev): bump nock from 13.5.0 to 13.5.1 Bumps [nock](https://github.com/nock/nock) from 13.5.0 to 13.5.1. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.5.0...v13.5.1) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 21.9.0 to 21.10.0 (#14341) * chore(deps): bump puppeteer from 21.9.0 to 21.10.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.9.0 to 21.10.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.9.0...puppeteer-v21.10.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add 中国汽车工业协会统计信息网 (#14339) * style: auto format * feat(modrinth): add project versions route (#14340) * feat(modrinth): add project versions route * docs(modrinth): add project versions * fix(modrinth): query string wrong * feat(modrinth): add radar rule * fix(modrinth): using `searchParams` from `got` & single line type annotation * style: auto format * feat: Add Thai Parliament's Public hearing (according to Thai constitution section 77) (#14260) * feat: Add Thai DOL website * feat: Add timezone, Radar for Thai DOL e-LandsAnnoucements * Update lib/v2/dol/maintainer.js Co-authored-by: Tony * Update website/docs/routes/government.mdx Co-authored-by: Tony * Update lib/v2/dol/radar.js Co-authored-by: Tony * Feat: Init Thai Parliament section77 * Switch got to Puppetteer, add radar, doc * Fix DeepScan: Expression 'parseInt(maxPageElem.attr('id'))' always passes null check. Consider using 'isNaN()' instead if invalid number checking was intended. * Change NaN handling to satisfy DeepScan * Fix ESLint issues * Fix: Reject PuppeTeer, embrace got with ToughJar. Thk @TonyRL * Correct header style * perf: Remove fetching pagination. Only first page. * chore: Move original text out of header * Update lib/v2/parliament/radar.js --------- Co-authored-by: rrachasak * chore(deps): bump @sentry/node from 7.98.0 to 7.99.0 (#14349) * chore(deps): bump @sentry/node from 7.98.0 to 7.99.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.98.0 to 7.99.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.98.0...7.99.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(modrinth): correct the radar rules for working with (#14351) * fix(modrinth): match index of projects (#14352) * feat: twitter login cache and logs * feat: twitter login error logs * fix: twitter login error logs * feat(route): add 中国汽车工业协会 (#14356) * style: auto format * chore(deps): bump codecov/codecov-action from 3 to 4 (#14358) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump imapflow from 1.0.148 to 1.0.149 (#14360) * chore(deps): bump imapflow from 1.0.148 to 1.0.149 Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.0.148 to 1.0.149. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.0.148...v1.0.149) --- updated-dependencies: - dependency-name: imapflow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump lint-staged from 15.2.0 to 15.2.1 (#14359) * chore(deps-dev): bump lint-staged from 15.2.0 to 15.2.1 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 15.2.0 to 15.2.1. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/okonet/lint-staged/compare/v15.2.0...v15.2.1) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: replace weibo a href img (#14354) * docs(modrinth): add query params & fix the route path (#14362) * feat(route): 优化微博博主路由 (#14365) * chore(deps-dev): bump @types/react from 18.2.48 to 18.2.51 in /website (#14369) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.48 to 18.2.51. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump pinyin-pro from 3.19.3 to 3.19.4 in /website (#14370) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.19.3 to 3.19.4. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/compare/3.19.3...3.19.4) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mailparser from 3.6.6 to 3.6.7 (#14371) * chore(deps): bump mailparser from 3.6.6 to 3.6.7 Bumps [mailparser](https://github.com/nodemailer/mailparser) from 3.6.6 to 3.6.7. - [Release notes](https://github.com/nodemailer/mailparser/releases) - [Changelog](https://github.com/nodemailer/mailparser/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/mailparser/compare/v3.6.6...v3.6.7) --- updated-dependencies: - dependency-name: mailparser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump husky from 9.0.7 to 9.0.10 (#14372) * chore(deps-dev): bump husky from 9.0.7 to 9.0.10 Bumps [husky](https://github.com/typicode/husky) from 9.0.7 to 9.0.10. - [Release notes](https://github.com/typicode/husky/releases) - [Commits](https://github.com/typicode/husky/compare/v9.0.7...v9.0.10) --- updated-dependencies: - dependency-name: husky dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @vercel/nft from 0.26.2 to 0.26.3 (#14373) * chore(deps-dev): bump @vercel/nft from 0.26.2 to 0.26.3 Bumps [@vercel/nft](https://github.com/vercel/nft) from 0.26.2 to 0.26.3. - [Release notes](https://github.com/vercel/nft/releases) - [Commits](https://github.com/vercel/nft/compare/0.26.2...0.26.3) --- updated-dependencies: - dependency-name: "@vercel/nft" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * style: auto format * feat(route): add 我不是盐神 (#14348) * Create common.js * Create maintainer.js * Create router.js * Create radar.js * Update common.js A space is add after ','. * Update common.js A space is add after ','. * Update common.js * Update radar.js * Update new-media.mdx * Update maintainer.js * Update radar.js * Update radar.js * Update radar.js * style: auto format * chore(deps): bump imapflow from 1.0.149 to 1.0.150 (#14381) * chore(deps): bump imapflow from 1.0.149 to 1.0.150 Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.0.149 to 1.0.150. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.0.149...v1.0.150) --- updated-dependencies: - dependency-name: imapflow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(modrinth): using path param instead of query string since the que… (#14367) * fix(modrinth): using path param instead of query string since the query won't affect cached result * fix(modrinth): use `URLSearchParams` instead of querystring * docs(modrinth): typo * fix(modrinth): avoid empty array query when no value --------- * chore(deps): bump puppeteer from 21.10.0 to 21.11.0 (#14382) * chore(deps): bump puppeteer from 21.10.0 to 21.11.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.10.0 to 21.11.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.10.0...puppeteer-v21.11.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): 新华网新华社新闻 (#14390) * feat: make url clickable (#14353) * feat(route): Optimize the title of Caixin Weekly | 优化《财新周刊》标题 (#14396) * 优化标题 * feat(route): Optimize the title of Caixin Weekly * style: auto format * feat(route): Weibo Routing Add User Latest Follow Timeline | 微博路由 添加 用户最新关注时间线 (#14385) * feat(route): 微博路由 添加 用户最新关注时间线 * fix(route): 优化 微博最新关注时间线 的标题显示 --------- * style: auto format * fix(route): freewechat link (#14397) * chore: bump docs scraper js_wait * fix(route): 艾瑞周度市场观察 (#14400) * chore(deps-dev): bump @types/react from 18.2.51 to 18.2.54 in /website (#14401) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.51 to 18.2.54. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump pinyin-pro from 3.19.4 to 3.19.5 in /website (#14402) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.19.4 to 3.19.5. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/commits) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @tonyrl/rand-user-agent from 2.0.48 to 2.0.49 (#14404) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.48 to 2.0.49 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.48 to 2.0.49. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.48...v2.0.49) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump lint-staged from 15.2.1 to 15.2.2 (#14407) * chore(deps-dev): bump lint-staged from 15.2.1 to 15.2.2 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 15.2.1 to 15.2.2. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/okonet/lint-staged/compare/v15.2.1...v15.2.2) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump googleapis from 131.0.0 to 132.0.0 (#14405) * chore(deps): bump googleapis from 131.0.0 to 132.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 131.0.0 to 132.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/release-please-config.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v131.0.0...googleapis-v132.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.5.4 to 1.6.0 (#14406) * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.5.4 to 1.6.0 Bumps [@stylistic/eslint-plugin-js](https://github.com/eslint-stylistic/eslint-stylistic/tree/HEAD/packages/eslint-plugin-js) from 1.5.4 to 1.6.0. - [Release notes](https://github.com/eslint-stylistic/eslint-stylistic/releases) - [Commits](https://github.com/eslint-stylistic/eslint-stylistic/commits/v1.6.0/packages/eslint-plugin-js) --- updated-dependencies: - dependency-name: "@stylistic/eslint-plugin-js" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 3.2.4 to 3.2.5 (#14408) * chore(deps-dev): bump prettier from 3.2.4 to 3.2.5 Bumps [prettier](https://github.com/prettier/prettier) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.2.4...3.2.5) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * style: auto format * chore(deps-dev): bump @types/react from 18.2.54 to 18.2.55 in /website (#14419) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.54 to 18.2.55. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: update the "routes" example in the pull request template to make it less confusing (#14415) * Fix: a typo in the pull request template * Enhance: when parsing routes from PR body, remove all comments. * feat(route): add new route "konghq/blog-posts" (#14414) * Feature: add new route honghq/blog-posts * Update comment of route konghq/blog-posts * Update lib/v2/konghq/blog-posts.js Co-authored-by: Tony * Update lib/v2/konghq/blog-posts.js Co-authored-by: Tony * Add radar.js for konghq route. --------- * chore(deps-dev): bump @types/imapflow from 1.0.17 to 1.0.18 (#14417) * chore(deps-dev): bump @types/imapflow from 1.0.17 to 1.0.18 Bumps [@types/imapflow](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/imapflow) from 1.0.17 to 1.0.18. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/imapflow) --- updated-dependencies: - dependency-name: "@types/imapflow" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.99.0 to 7.100.0 (#14418) * chore(deps): bump @sentry/node from 7.99.0 to 7.100.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.99.0 to 7.100.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.99.0...7.100.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint-plugin-unicorn from 50.0.1 to 51.0.1 (#14416) * chore(deps-dev): bump eslint-plugin-unicorn from 50.0.1 to 51.0.1 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 50.0.1 to 51.0.1. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v50.0.1...v51.0.1) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 21.11.0 to 22.0.0 (#14403) * chore(deps): bump puppeteer from 21.11.0 to 22.0.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.11.0 to 22.0.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.11.0...puppeteer-v22.0.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route/thepaper): Avoid destructuring string when parsing tags (#14383) * Update utils.js * Update lib/v2/thepaper/utils.js Co-authored-by: Tony --------- * feat(route): tass (#14426) * chore(deps-dev): bump @types/react-dom in /website (#14428) Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.2.18 to 18.2.19. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.100.0 to 7.100.1 (#14427) * chore(deps): bump @sentry/node from 7.100.0 to 7.100.1 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.100.0 to 7.100.1. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.100.1/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.100.0...7.100.1) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): bsky (#14429) * feat: New Route Miyoushe - User Follow Dynamics | 新增路由 米游社 - 用户关注动态 (#14425) * feat(route): 增加 米游社 - 用户关注动态 路由 * fix(route): 修复 路由、文档的风格问题 * docs: fix envs --------- * style: auto format * feat(route): zhihu top topic (#14431) * feat(route): 中证网中证快讯 (#14424) * feat(route): 中证网中证快讯 * update lib/v2/cs/zzkx.js Co-authored-by: Tony * update lib/v2/cs/zzkx.js Co-authored-by: Tony * fix: remove unused import statement --------- * chore(deps): bump pnpm/action-setup from 2 to 3 (#14432) Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 2 to 3. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/v2...v3) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): Fix the issue of missing post cover in the official routing of Miyoushe; Optimize the data processing of posts | 修复 米游社官方路由中帖子封面缺失的问题;优化帖子的数据处理 (#14436) * feat(route): 增加 米游社 - 用户关注动态 路由 * fix(route): 修复 路由、文档的风格问题 * docs: fix envs * fix(route): 修复 米游社官方路由中帖子封面缺失的问题;优化帖子的数据处理 --------- * style: auto format * feat(route): chinamoney (#14437) * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.6.0 to 1.6.1 (#14433) * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.6.0 to 1.6.1 Bumps [@stylistic/eslint-plugin-js](https://github.com/eslint-stylistic/eslint-stylistic/tree/HEAD/packages/eslint-plugin-js) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/eslint-stylistic/eslint-stylistic/releases) - [Commits](https://github.com/eslint-stylistic/eslint-stylistic/commits/v1.6.1/packages/eslint-plugin-js) --- updated-dependencies: - dependency-name: "@stylistic/eslint-plugin-js" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump googleapis from 132.0.0 to 133.0.0 (#14434) * chore(deps): bump googleapis from 132.0.0 to 133.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 132.0.0 to 133.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/release-please-config.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v132.0.0...googleapis-v133.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add World Meteorological Centre Beijing Publish (#14441) * fix(route/huxiu): Prevent URL parsing error. (#14445) * Update util.js * Update util.js * feat(route): add 中证网 (#14446) * chore(deps): bump @mdx-js/react from 3.0.0 to 3.0.1 in /website (#14452) Bumps [@mdx-js/react](https://github.com/mdx-js/mdx/tree/HEAD/packages/react) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/mdx-js/mdx/releases) - [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md) - [Commits](https://github.com/mdx-js/mdx/commits/3.0.1/packages/react) --- updated-dependencies: - dependency-name: "@mdx-js/react" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @tonyrl/rand-user-agent from 2.0.49 to 2.0.50 (#14448) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.49 to 2.0.50 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.49 to 2.0.50. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.49...v2.0.50) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump https-proxy-agent from 7.0.2 to 7.0.3 (#14449) * chore(deps): bump https-proxy-agent from 7.0.2 to 7.0.3 Bumps [https-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/https-proxy-agent) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/TooTallNate/proxy-agents/releases) - [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/https-proxy-agent/CHANGELOG.md) - [Commits](https://github.com/TooTallNate/proxy-agents/commits/https-proxy-agent@7.0.3/packages/https-proxy-agent) --- updated-dependencies: - dependency-name: https-proxy-agent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump dotenv from 16.4.1 to 16.4.3 (#14450) * chore(deps): bump dotenv from 16.4.1 to 16.4.3 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.4.1 to 16.4.3. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.4.1...v16.4.3) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump imapflow from 1.0.150 to 1.0.152 (#14451) * chore(deps): bump imapflow from 1.0.150 to 1.0.152 Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.0.150 to 1.0.152. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.0.150...v1.0.152) --- updated-dependencies: - dependency-name: imapflow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.100.1 to 7.101.0 (#14454) * chore(deps): bump @sentry/node from 7.100.1 to 7.101.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.100.1 to 7.101.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.100.1...7.101.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump dotenv from 16.4.3 to 16.4.4 (#14455) * chore(deps): bump dotenv from 16.4.3 to 16.4.4 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.4.3 to 16.4.4. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.4.3...v16.4.4) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: bump deps (#14456) * feat(route): add 中证网中证视频 (#14457) * feat(route): fansly (#14458) * chore(deps-dev): bump @vercel/nft from 0.26.3 to 0.26.4 (#14460) * chore(deps-dev): bump @vercel/nft from 0.26.3 to 0.26.4 Bumps [@vercel/nft](https://github.com/vercel/nft) from 0.26.3 to 0.26.4. - [Release notes](https://github.com/vercel/nft/releases) - [Commits](https://github.com/vercel/nft/compare/0.26.3...0.26.4) --- updated-dependencies: - dependency-name: "@vercel/nft" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump husky from 9.0.10 to 9.0.11 (#14459) * chore(deps-dev): bump husky from 9.0.10 to 9.0.11 Bumps [husky](https://github.com/typicode/husky) from 9.0.10 to 9.0.11. - [Release notes](https://github.com/typicode/husky/releases) - [Commits](https://github.com/typicode/husky/compare/v9.0.10...v9.0.11) --- updated-dependencies: - dependency-name: husky dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): throw better errors (#14461) * fix(route): 4games full text (#14466) * fix: 4games full text * docs: add docs * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.6.1 to 1.6.2 (#14468) * chore(deps-dev): bump @stylistic/eslint-plugin-js from 1.6.1 to 1.6.2 Bumps [@stylistic/eslint-plugin-js](https://github.com/eslint-stylistic/eslint-stylistic/tree/HEAD/packages/eslint-plugin-js) from 1.6.1 to 1.6.2. - [Release notes](https://github.com/eslint-stylistic/eslint-stylistic/releases) - [Commits](https://github.com/eslint-stylistic/eslint-stylistic/commits/v1.6.2/packages/eslint-plugin-js) --- updated-dependencies: - dependency-name: "@stylistic/eslint-plugin-js" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump https-proxy-agent from 7.0.3 to 7.0.4 (#14469) * chore(deps): bump https-proxy-agent from 7.0.3 to 7.0.4 Bumps [https-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/https-proxy-agent) from 7.0.3 to 7.0.4. - [Release notes](https://github.com/TooTallNate/proxy-agents/releases) - [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/https-proxy-agent/CHANGELOG.md) - [Commits](https://github.com/TooTallNate/proxy-agents/commits/https-proxy-agent@7.0.4/packages/https-proxy-agent) --- updated-dependencies: - dependency-name: https-proxy-agent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.101.0 to 7.101.1 (#14470) * chore(deps): bump @sentry/node from 7.101.0 to 7.101.1 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.101.0 to 7.101.1. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.101.1/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.0...7.101.1) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): Let CAS-IGDB router accepts path argument (#14465) * let IGDB router accepts path argument * add support for "dqyd" column * add example of dqyd column to docs * Update lib/v2/cas/radar.js --------- * feat(route): add 不良林 (#14479) * 增加几个自己用的路由 * 创建 maintainer.js * 更新 maintainer.js * 更新 freeadaycom.js * 增加 不良林 * 更新 radar.js * style: auto format * 重新调整路径 几次PR都不通过,参照一个通过的调整了路径 * 根据要求进行了修改 * 更新 blog.mdx * 更新 blog.mdx --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * feat(route): shoac (#14481) * feat: add PwC Strategy& Sustainability route (#14467) * build: add skeleton codes * build: change to puppeteer for content scraping * docs: add documentation * feat: add radar config * Apply suggestions from code review --------- * fix: 豆瓣榜单与集合支持分页处理 (#14483) * docs: fix spotify envs docs * feat(route): Support sortby upvotes in Huggingface Daily Papers (#14484) * Support sortby upvotes in Huggingface Daily Papers * Revert "Support sortby upvotes in Huggingface Daily Papers" This reverts commit 6e43f8e8a260d44b0b35f38a1af7d38eef2ffc3c. * Only sort by upvotes --------- * feat(route): add 电脑玩物 (#14486) * 增加电脑玩物 * style: auto format * Update radar.js --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * feat(route): add Transcript Forest (#14482) * feat(route): add Transcript Forest * fix: add transcript text * fix: lofter tag 绕过登录需求 (#14453) * Fix lofter tag * Apply suggestions from code review Co-authored-by: Tony * fix: import jsdom, correct URL of key 'link' * fix: post encoded formdata --------- * feat(route): indienova (#14493) * feat(route): indienova * refactor: migrate to v2 * fix: Update metacritic API root URL (#14497) * fix: encode tag name in lofter request's form (#14494) * fix(route): instagram null caption (#14498) * feat(route): add 免費資源網路社群 (#14495) * 增加 免費資源網路社群 * style: auto format * 更新 rss.js 使用 wordpress 的 api 来获取数据 * 更新 rss.js * 更新 rss.js * 更新 rss.js --------- Co-authored-by: pull[bot] <39814207+pull[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/react from 18.2.55 to 18.2.56 in /website (#14503) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.55 to 18.2.56. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump nock from 13.5.1 to 13.5.3 (#14501) * chore(deps-dev): bump nock from 13.5.1 to 13.5.3 Bumps [nock](https://github.com/nock/nock) from 13.5.1 to 13.5.3. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.5.1...v13.5.3) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @tonyrl/rand-user-agent from 2.0.50 to 2.0.51 (#14502) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.50 to 2.0.51 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.50 to 2.0.51. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.50...v2.0.51) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add 晋江文学城作者最新作品 (#14499) * feat(route): add 晋江文学城作者最新作品 * fix: add null check * fix typo * chore(deps): bump dotenv from 16.4.4 to 16.4.5 (#14510) * chore(deps): bump dotenv from 16.4.4 to 16.4.5 Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.4.4 to 16.4.5. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.4.4...v16.4.5) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/react from 18.2.56 to 18.2.57 in /website (#14512) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.56 to 18.2.57. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.101.1 to 7.102.0 (#14511) * chore(deps): bump @sentry/node from 7.101.1 to 7.102.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.101.1 to 7.102.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.102.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.101.1...7.102.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 22.0.0 to 22.1.0 (#14500) * chore(deps): bump puppeteer from 22.0.0 to 22.1.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 22.0.0 to 22.1.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v22.0.0...puppeteer-v22.1.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): hitwh (#14519) * feat(route): Add qdu houqin (#14515) * feat(route): Add qdu houqin * feat(route): qdu houqin - more accurate time * fix: alphabetical order * fix: unnecessary condition --------- * chore(deps): bump sanitize-html from 2.11.0 to 2.12.0 (#14521) * chore(deps): bump sanitize-html from 2.11.0 to 2.12.0 Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.11.0 to 2.12.0. - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/commits) --- updated-dependencies: - dependency-name: sanitize-html dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route/cna): Rectify link. (#14524) * feat(route): add 明月中文网 (#14520) * feat(route): add 明月中文网 * fix: remove 404 items * feat(route): add 吉首大学相关通知公告 (#14514) * feat(route): add 吉首大学相关通知公告 * feat(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * fix: 修复文档问题 * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * feat(route): add 吉首大学相关通知公告 * feat(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * Update lib/v2/jsu/cxzx.js Co-authored-by: Tony * Update lib/v2/jsu/radar.js Co-authored-by: Tony * Update lib/v2/jsu/utils/index.js Co-authored-by: Tony * Update lib/v2/jsu/radar.js Co-authored-by: Tony * Update lib/v2/jsu/radar.js Co-authored-by: Tony * Update lib/v2/jsu/radar.js Co-authored-by: Tony * Update lib/v2/jsu/radar.js Co-authored-by: Tony * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * fix: 修复教务处返回的标题和描述为”计算机学院“ * style: 参照其他PR中Maintainer给出的建议,对radar.js 和 maintainer.js以字典序排序。 * refactor: 将整个RSS项对象作为缓存而不是单仅正文作为缓存,同时对应的修改了正文获取工具类中的内容,去除了固定的UserAgent; * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * fix: 修复教务处返回的标题和描述为”计算机学院“ * style: 参照其他PR中Maintainer给出的建议,对radar.js 和 maintainer.js以字典序排序。 * refactor: 将整个RSS项对象作为缓存而不是单仅正文作为缓存,同时对应的修改了正文获取工具类中的内容,去除了固定的UserAgent; * Update website/docs/routes/university.mdx Co-authored-by: Tony * Update website/docs/routes/university.mdx Co-authored-by: Tony * Update lib/v2/jsu/math.js Co-authored-by: Tony * Update lib/v2/jsu/universityindex.js Co-authored-by: Tony * feat(route): add 吉首大学相关通知公告 * fea(route): add 吉首大学计算机科学与工程学院、吉首大学数统学院、吉首大学主站、创新中心通知公告、教务处教务动态、教务通知 * fix: 修复教务处返回的标题和描述为”计算机学院“ * style: 参照其他PR中Maintainer给出的建议,对radar.js 和 maintainer.js以字典序排序。 * refactor: 将整个RSS项对象作为缓存而不是单仅正文作为缓存,同时对应的修改了正文获取工具类中的内容,去除了固定的UserAgent; * Update lib/v2/jsu/rjxy.js * Update lib/v2/jsu/math.js * Update lib/v2/jsu/jwc.js --------- Co-authored-by: WenjiaChen * style: auto format * feat(route): add HiFeng'Blog (#14518) * 增加 免費資源網路社群 * style: auto format * 更新 rss.js 使用 wordpress 的 api 来获取数据 * 更新 rss.js * 更新 rss.js * 更新 rss.js * 增加 HiFeng'Blog * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 更新 rss.js * 1 * 2 * 3 * 4 * 更新 rss.js * 1 * 更新 router.js * 更新 rss1.js * 改router文件 * 更新 rss.js * 修改 hicairo * 更新 radar.js * 更新 rss.js * 更新 rss.js * Update lib/v2/hicairo/radar.js Co-authored-by: Tony --------- Co-authored-by: pull[bot] <39814207+pull[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto format * fix(route/ft): Fix author name. (#14529) * style: auto format * fix(route/pts): Modify item parsing method. (#14526) * fix(route/pts): Modify item parsing method. * . * chore(deps): bump sanitize-html from 2.12.0 to 2.12.1 (#14530) * chore(deps): bump sanitize-html from 2.12.0 to 2.12.1 Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.12.0 to 2.12.1. - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/commits/2.12.1) --- updated-dependencies: - dependency-name: sanitize-html dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump nodemon from 3.0.3 to 3.1.0 (#14532) * chore(deps-dev): bump nodemon from 3.0.3 to 3.1.0 Bumps [nodemon](https://github.com/remy/nodemon) from 3.0.3 to 3.1.0. - [Release notes](https://github.com/remy/nodemon/releases) - [Commits](https://github.com/remy/nodemon/compare/v3.0.3...v3.1.0) --- updated-dependencies: - dependency-name: nodemon dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.102.0 to 7.102.1 (#14533) * chore(deps): bump @sentry/node from 7.102.0 to 7.102.1 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.102.0 to 7.102.1. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.102.1/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.102.0...7.102.1) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 22.1.0 to 22.2.0 (#14531) * chore(deps): bump puppeteer from 22.1.0 to 22.2.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 22.1.0 to 22.2.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v22.1.0...puppeteer-v22.2.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(router/u9a9): return cache if need (#14537) * feat(route): bilibili/user/article (#14522) * bilibili/user/article fix #14231 * Update cache.js delete UA * using INITIAL_STATE data * resolve code review * Update lib/v2/bilibili/cache.js --------- * fix(route/remnote): fix author name (#14534) * feat(route): cib/whpj (#14539) * feat(route): cib/whpj 兴业银行外汇牌价 * fix lint & security issue * fix code review * fix(route): Kemono Posts published date (#14541) * fix: 更改 B站嵌入方式来防止误点会导致跳转 B站 (#14517) * style: auto format * chore(deps): bump pinyin-pro from 3.19.5 to 3.19.6 in /website (#14544) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.19.5 to 3.19.6. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/commits) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/react from 18.2.57 to 18.2.58 in /website (#14545) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.57 to 18.2.58. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint and @types/eslint (#14542) * chore(deps-dev): bump eslint and @types/eslint Bumps [eslint](https://github.com/eslint/eslint) and [@types/eslint](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/eslint). These dependencies needed to be updated together. Updates `eslint` from 8.56.0 to 8.57.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/v8.57.0/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.56.0...v8.57.0) Updates `@types/eslint` from 8.56.2 to 8.56.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/eslint) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: "@types/eslint" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/koa from 2.14.0 to 2.15.0 (#14543) * chore(deps-dev): bump @types/koa from 2.14.0 to 2.15.0 Bumps [@types/koa](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/koa) from 2.14.0 to 2.15.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/koa) --- updated-dependencies: - dependency-name: "@types/koa" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add howtoforge (#14538) * add howtoforge * Update study.mdx * 更新 rss.js * 更新 radar.js * 更新 radar.js * style: auto format * fix(route): bilibili/watchlater (#14547) * feat(route): Add routes to SISU news. (#14546) * feat(route): Add route to SISU news ```routes /shisu/news ``` ## New RSS Route Checklist / 新 RSS 路由检查表 - [x] New Route / 新的路由 - [x] Follows [v2 Script Standard](https://docs.rsshub.app/joinus/advanced/script-standard) / 跟随 [v2 路由规范](https://docs.rsshub.app/zh/joinus/advanced/script-standard) - [ ] Documentation / 文档说明 - [x] Full text / 全文获取 - [x] Use cache / 使用缓存 - [ ] Anti-bot or rate limit / 反爬/频率限制 - [ ] If yes, do your code reflect this sign? / 如果有, 是否有对应的措施? - [x] [Date and time](https://docs.rsshub.app/joinus/advanced/pub-date) / [日期和时间](https://docs.rsshub.app/zh/joinus/advanced/pub-date) - [x] Parsed / 可以解析 - [x] Correct time zone / 时区正确 - [ ] New package added / 添加了新的包 - [ ] `Puppeteer` * Update university.mdx * Update news.js * Update news.js Modified the url... * fix(route): Add more options to SISU news. ```route /shisu/news /shisu/news/notice /shisu/news/campus ``` * fix: some small problems. * feat(route): Add more options to SISU news. ```routes /shisu/news /shisu/news/notice /shisu/news/campus ``` * fix(routes): modified to the correct route. * Forgot to format the document. * fix(route): made category mandatory * Update news.js * style: auto format * feat(route): add 荒岛 (#14550) * add lala * 更新 rss.js * style: auto format * feat(route): add 四月网 (#14560) * feat(route): add unraid community apps (#14564) * add unraid community apps feed * unraid ca support limit query parameter * feat(router/u3c3): support preview (#14548) * feat(router/u3c3): support preview * fix(router/u3c3): modify route maintainer * chore(deps-dev): bump @types/react from 18.2.58 to 18.2.59 in /website (#14567) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.58 to 18.2.59. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @tonyrl/rand-user-agent from 2.0.51 to 2.0.52 (#14568) * chore(deps): bump @tonyrl/rand-user-agent from 2.0.51 to 2.0.52 Bumps [@tonyrl/rand-user-agent](https://github.com/TonyRL/rand-user-agent) from 2.0.51 to 2.0.52. - [Release notes](https://github.com/TonyRL/rand-user-agent/releases) - [Commits](https://github.com/TonyRL/rand-user-agent/compare/v2.0.51...v2.0.52) --- updated-dependencies: - dependency-name: "@tonyrl/rand-user-agent" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump nock from 13.5.3 to 13.5.4 (#14569) * chore(deps-dev): bump nock from 13.5.3 to 13.5.4 Bumps [nock](https://github.com/nock/nock) from 13.5.3 to 13.5.4. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.5.3...v13.5.4) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 22.2.0 to 22.3.0 (#14570) * chore(deps): bump puppeteer from 22.2.0 to 22.3.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 22.2.0 to 22.3.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v22.2.0...puppeteer-v22.3.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): 2048/:type (#14572) * chore: remove `!` from heading slut * style: auto format * feat(route/pcr/news): Fetch full text (#14559) * feat(route/pcr/news): Fetch full text * fix(route/pcr/news): add missing language code * fix(route/pcr/news) Use cache.tryGet() * refactor: migrate to v2 --------- * chore(deps-dev): bump @types/react from 18.2.59 to 18.2.60 in /website (#14575) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.59 to 18.2.60. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.102.1 to 7.103.0 (#14577) * chore(deps): bump @sentry/node from 7.102.1 to 7.103.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.102.1 to 7.103.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.103.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.102.1...7.103.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/eslint from 8.56.3 to 8.56.4 (#14578) * chore(deps-dev): bump @types/eslint from 8.56.3 to 8.56.4 Bumps [@types/eslint](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/eslint) from 8.56.3 to 8.56.4. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/eslint) --- updated-dependencies: - dependency-name: "@types/eslint" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add 赵容部落 (#14580) * style: auto format * feat(route): add TV Tropes Featured (#14574) * feat(route): add TV Tropes Featured * fix typo --------- * feat(route): add sfacg (#14579) * feat(route): add sfacg * refactor code * feat: add radar.js * update description * add feed desc & img * update radar.js * update http url replacement * fix: radar * fix: maintainer --------- --------- Signed-off-by: dependabot[bot] Signed-off-by: Rongrong Co-authored-by: 陈楷 <69948498+mu-bibai@users.noreply.github.com> Co-authored-by: Tony Co-authored-by: JimenezLi <75196426+JimenezLi@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carson Yang Co-authored-by: xizeyoupan <44920131+xizeyoupan@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Rachasak Ragkamnerd Co-authored-by: rrachasak Co-authored-by: Kun CHEN Co-authored-by: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Co-authored-by: CaoMeiYouRen <40430746+CaoMeiYouRen@users.noreply.github.com> Co-authored-by: DIYgod Co-authored-by: lidashuang Co-authored-by: SunBK201 Co-authored-by: Rongrong Co-authored-by: Andvari <31068367+dzx-dzx@users.noreply.github.com> Co-authored-by: KTachibanaM Co-authored-by: SettingDust Co-authored-by: tmr <32825326+ttttmr@users.noreply.github.com> Co-authored-by: ruoshui9527 <104556597+ruoshui9527@users.noreply.github.com> Co-authored-by: Kawauso Co-authored-by: piglei Co-authored-by: CrackTC Co-authored-by: panyq357 <50689054+panyq357@users.noreply.github.com> Co-authored-by: cnkmmk <22782790+cnkmmk@users.noreply.github.com> Co-authored-by: minty_frankie <77310871+mintyfrankie@users.noreply.github.com> Co-authored-by: Kai Yang Co-authored-by: Elsa Granger Co-authored-by: LucunJi <36262513+LucunJi@users.noreply.github.com> Co-authored-by: moppman Co-authored-by: pull[bot] <39814207+pull[bot]@users.noreply.github.com> Co-authored-by: abc1763613206 Co-authored-by: Ben Chen <1070127864@qq.com> Co-authored-by: WenjiaChen Co-authored-by: storytellerF <34095089+storytellerF@users.noreply.github.com> Co-authored-by: qixingchen <4182240+Qixingchen@users.noreply.github.com> Co-authored-by: Makerlife <64350662+amakerlife@users.noreply.github.com> Co-authored-by: Apricity Co-authored-by: GalaxvSpaceship Co-authored-by: moemoe <90035294+hudatumoe@users.noreply.github.com> Co-authored-by: randompasser <33059426+randompasser@users.noreply.github.com> Co-authored-by: Keo --- .devcontainer/devcontainer.json | 5 +- .dockerignore | 6 +- .eslintrc.json | 126 +- .github/PULL_REQUEST_TEMPLATE.md | 12 +- .github/dependabot.yml | 47 +- .github/labeler.yml | 18 +- .github/workflows/build-assets.yml | 17 +- .github/workflows/codeql.yml | 6 +- .github/workflows/comment-on-issue.yml | 7 +- .github/workflows/docker-release.yml | 6 +- ...oy-route-test.yml => docker-test-cont.yml} | 19 +- .github/workflows/docker-test.yml | 4 +- .github/workflows/docs-search-index.yml | 7 +- .github/workflows/format.yml | 6 +- .github/workflows/issue-command.yml | 19 +- .github/workflows/{pr-lint.yml => lint.yml} | 76 +- .github/workflows/npm-publish.yml | 9 +- .github/workflows/semgrep.yml | 37 +- .github/workflows/stale.yml | 6 +- .github/workflows/test.yml | 36 +- .github/workflows/yarn-lock-changes.yml | 22 - .gitignore | 2 + .gitpod.yml | 10 +- .husky/pre-commit | 3 - .markdownlint.jsonc | 3 +- .nvmrc | 2 +- CODE_OF_CONDUCT.md | 6 +- Dockerfile | 14 +- README.md | 16 +- assets/radar-rules.js | 102 +- docker-compose.yml | 3 +- lib/{api_router.js => api-router.js} | 5 +- lib/app.js | 10 +- lib/config.js | 73 +- lib/{core_router.js => core-router.js} | 0 lib/index.js | 4 +- lib/maintainer.js | 27 +- lib/middleware/access-control.js | 43 +- lib/middleware/anti-hotlink.js | 28 +- lib/middleware/cache/index.js | 25 +- lib/middleware/cache/redis.js | 12 +- lib/middleware/header.js | 2 +- lib/middleware/load-on-demand.js | 6 +- lib/middleware/onerror.js | 40 +- lib/middleware/parameter.js | 134 +- lib/middleware/template.js | 104 +- lib/middleware/utf8.js | 2 +- ...rotected_router.js => protected-router.js} | 0 lib/radar-rules.js | 373 +- lib/radar.js | 46 +- lib/router.js | 982 +-- lib/routes/005tv/zx.js | 2 +- lib/routes/10000link/news.js | 4 +- lib/routes/12379/index.js | 2 +- lib/routes/199it/category.js | 2 +- lib/routes/199it/tag.js | 2 +- lib/routes/199it/utils.js | 2 +- lib/routes/1draw/index.js | 44 - lib/routes/21caijing/channel.js | 6 +- lib/routes/3ycy/home.js | 58 - lib/routes/4gamers/category.js | 20 - lib/routes/4gamers/tag.js | 20 - lib/routes/4gamers/topic.js | 20 - lib/routes/51voa/channel.js | 158 - lib/routes/60s-science/transcript.js | 6 +- lib/routes/755/user.js | 6 +- lib/routes/8btc/author.js | 49 - lib/routes/8btc/news/flash.js | 41 - lib/routes/91ddcc/stage.js | 48 - lib/routes/91ddcc/user.js | 45 - lib/routes/93/index.js | 50 - lib/routes/99percentinvisible/transcript.js | 4 +- lib/routes/abc/documentId.js | 7272 ----------------- lib/routes/abc/id.js | 7272 ----------------- lib/routes/abc/index.js | 74 - lib/routes/acm/amturingaward.js | 4 +- lib/routes/acwifi/index.js | 4 +- lib/routes/adquan/index.js | 7 +- lib/routes/aiyanxishe/home.js | 46 +- lib/routes/aliyun-kernel/index.js | 60 - lib/routes/allnow/column.js | 7 - lib/routes/allnow/index.js | 5 - lib/routes/allnow/tag.js | 7 - lib/routes/allnow/user.js | 7 - lib/routes/allnow/utils.js | 67 - lib/routes/alter-cn/news.js | 44 - lib/routes/amazfitwatchfaces/search.js | 2 +- lib/routes/amazon/ku.js | 57 - lib/routes/amd/graphicsdrivers.js | 6 +- lib/routes/andyt/index.js | 36 - .../anigamer/{new_anime.js => new-anime.js} | 0 lib/routes/animen/news.js | 84 - lib/routes/anitama/channel.js | 64 - lib/routes/anki/changes.js | 39 - lib/routes/aom/journal.js | 107 - lib/routes/aozora/newbook.js | 18 +- lib/routes/appsales/index.js | 4 +- lib/routes/aptonic/action.js | 38 - lib/routes/archdaily/home.js | 4 +- lib/routes/asahi/area.js | 57 - lib/routes/asahi/index.js | 8 +- lib/routes/atfd/index.js | 46 - lib/routes/autotrader/index.js | 4 +- lib/routes/avgle/videos.js | 8 +- lib/routes/axis-studios/work.js | 117 +- lib/routes/babehub/index.js | 30 - lib/routes/babehub/search.js | 15 - lib/routes/babehub/utils.js | 71 - lib/routes/babykingdom/index.js | 38 +- .../{creation_index.js => creation-index.js} | 8 +- lib/routes/bahamut/utils.js | 14 +- lib/routes/baidu/daily.js | 55 - lib/routes/baidu/doodles.js | 27 - lib/routes/bandisoft/index.js | 2 +- lib/routes/banyuetan/index.js | 4 +- lib/routes/bell-labs/events-news.js | 70 - lib/routes/benedictevans/recent.js | 4 +- lib/routes/bibgame/category.js | 2 +- lib/routes/bihu/activaties.js | 76 - lib/routes/biobio/index.js | 40 - lib/routes/biobio/others.js | 45 - lib/routes/bioon/latest.js | 51 - lib/routes/bishijie/kuaixun.js | 21 - lib/routes/bjnews/epaper.js | 80 - lib/routes/bjnews/news.js | 4 +- lib/routes/blogs/diygod/animal-crossing.js | 39 - lib/routes/blogs/diygod/gk.js | 33 - lib/routes/blogs/hedwig.js | 6 +- lib/routes/blogs/jianning.js | 45 - .../{jingwei_link.js => jingwei-link.js} | 0 lib/routes/blogs/paulgraham.js | 46 - lib/routes/blogs/wangyin.js | 4 +- lib/routes/blogs/wordpress.js | 10 +- lib/routes/blow-studio/work.js | 4 +- lib/routes/blur-studio/index.js | 14 +- lib/routes/bof/home.js | 42 - lib/routes/bookscomtw/newbooks.js | 37 - lib/routes/booksource/index.js | 22 - lib/routes/booth-pm/shop.js | 6 +- lib/routes/boston/index.js | 73 - lib/routes/buaq/index.js | 31 - lib/routes/ccg/index.js | 2 +- lib/routes/cell/cell/index.js | 4 +- lib/routes/cell/cover.js | 2 +- lib/routes/cfan/news.js | 4 +- lib/routes/cfd/gbp_div.js | 28 - lib/routes/cgtn/most.js | 2 +- lib/routes/cgtn/opinions.js | 2 +- lib/routes/cgtn/pick.js | 45 - lib/routes/cgtn/top.js | 45 - lib/routes/changku/index.js | 39 - lib/routes/chaoli/index.js | 2 +- lib/routes/chicagotribune/index.js | 4 +- lib/routes/chinadialogue/column.js | 58 - lib/routes/chinadialogue/topics.js | 55 - lib/routes/chinalaborwatch/reports.js | 53 - lib/routes/chinatimes/index.js | 115 - lib/routes/chiphell/forum.js | 8 +- lib/routes/chocolatey/software.js | 2 +- lib/routes/chouti/top.js | 2 +- lib/routes/chsi/zszcgd.js | 2 +- lib/routes/chuiniu/column.js | 89 - lib/routes/chuiniu/column_list.js | 36 - lib/routes/ciweimao/chapter.js | 34 - lib/routes/cktest/app.js | 34 - lib/routes/cktest/policy.js | 43 - lib/routes/cmes/news.js | 64 - lib/routes/cnu/discovery.js | 12 +- lib/routes/cnu/selected.js | 10 +- lib/routes/cnu/utils.js | 8 +- lib/routes/codeceo/category.js | 51 - lib/routes/codeceo/home.js | 49 - lib/routes/commonapp/blog.js | 42 - lib/routes/coronavirus/caixin.js | 34 - lib/routes/coronavirus/dxy-data.js | 86 - lib/routes/coronavirus/dxy.js | 27 - lib/routes/coronavirus/mogov-2019ncov.js | 49 - lib/routes/coronavirus/nhc.js | 30 - lib/routes/coronavirus/scmp.js | 32 - lib/routes/coronavirus/sg-moh.js | 40 - lib/routes/coronavirus/yahoo-japan.js | 77 - lib/routes/cpta/notice.js | 41 - lib/routes/cpython/index.js | 6 +- lib/routes/craigslist/search.js | 51 - lib/routes/cria/news.js | 57 - lib/routes/cs/news.js | 69 - lib/routes/csc/notice.js | 8 +- lib/routes/curseforge/files.js | 36 - lib/routes/curseforge/generalfiles.js | 42 - lib/routes/cve/search.js | 26 - lib/routes/d1bz/novel.js | 72 - lib/routes/d2/daily.js | 49 - lib/routes/damai/activity.js | 28 - lib/routes/daxiaamu/home.js | 4 +- lib/routes/dayone/blog.js | 56 - lib/routes/ddrk/index.js | 2 +- lib/routes/ddrk/list.js | 4 +- lib/routes/deepl/blog.js | 2 +- lib/routes/deeplearningai/thebatch.js | 29 - lib/routes/dengekionline/new.js | 133 - lib/routes/dev.to/top.js | 4 +- lib/routes/dgtle/index.js | 4 +- lib/routes/dgtle/keyword.js | 3 +- lib/routes/dgtle/trade.js | 4 +- .../dgtle/{whale_rank.js => whale-rank.js} | 0 lib/routes/dhl/shipment-tracking.js | 56 - lib/routes/dianping/user.js | 98 - lib/routes/dida365/habit-checkins.js | 15 +- lib/routes/digic-pictures/index.js | 72 - lib/routes/digitaling/article.js | 2 +- lib/routes/digitaling/index.js | 2 +- lib/routes/digitaling/project.js | 2 +- lib/routes/dilbert/strip.js | 54 - lib/routes/dilidili/fanju.js | 78 - lib/routes/discuss/index.js | 16 +- lib/routes/disqus/posts.js | 12 +- lib/routes/dockone/weekly.js | 60 - lib/routes/donews/index.js | 59 - lib/routes/donews/utils.js | 19 - lib/routes/dongmanmanhua/comic.js | 4 +- lib/routes/dsb/area.js | 33 - lib/routes/duozhi/index.js | 9 +- lib/routes/dxy/vaccine.js | 109 - lib/routes/dysfz/index.js | 20 - lib/routes/edrawsoft/mindmap.js | 10 +- lib/routes/eeo/index.js | 6 +- lib/routes/egsea/flash.js | 40 - lib/routes/eleme/open-be/announce.js | 21 - lib/routes/eleme/open/announce.js | 37 +- lib/routes/elife/index.js | 67 - lib/routes/elitebabes/index.js | 30 - lib/routes/elitebabes/search.js | 15 - lib/routes/elitebabes/utils.js | 88 - lib/routes/elitebabes/videos.js | 26 - lib/routes/emi-nitta/home.js | 9 +- lib/routes/enclavebooks/category.js | 9 +- lib/routes/enclavebooks/collection.js | 2 +- lib/routes/enclavebooks/user.js | 2 +- lib/routes/engadget/home.js | 10 +- lib/routes/esquirehk/tag.js | 71 - lib/routes/etherscan/transactions.js | 2 +- lib/routes/everything/changes.js | 6 +- lib/routes/facebook/page.js | 128 - lib/routes/fanbox/conv.js | 12 +- lib/routes/fanbox/main.js | 6 +- lib/routes/fanfou/favorites.js | 2 +- .../{home_timeline.js => home-timeline.js} | 2 +- ...{public_timeline.js => public-timeline.js} | 2 +- .../{user_timeline.js => user-timeline.js} | 2 +- lib/routes/fashionnetwork/headline.js | 2 +- lib/routes/fashionnetwork/news.js | 8 +- lib/routes/feed-the-beast/modpack.js | 31 - lib/routes/feixuew/index.js | 63 - lib/routes/fir/update.js | 31 - lib/routes/firefox/release.js | 30 - lib/routes/fitchratings/site.js | 33 - lib/routes/fjnews/fznews.js | 55 - lib/routes/fjnews/jjnews.js | 43 - lib/routes/fontstory/tw.js | 48 - lib/routes/fulinian/index.js | 50 - lib/routes/furaffinity/home.js | 33 +- ...ournal_comments.js => journal-comments.js} | 0 lib/routes/furaffinity/status.js | 9 +- ...ion_comments.js => submission-comments.js} | 0 lib/routes/furaffinity/user.js | 40 +- lib/routes/gab/explore.js | 31 - lib/routes/galaxylab/index.js | 2 +- lib/routes/galgame/hhgal.js | 70 - lib/routes/galgame/sayhuahuo.js | 22 - lib/routes/galgame/zdfx.js | 30 - lib/routes/gamegrape/index.js | 73 - lib/routes/gamersky/ent.js | 4 +- lib/routes/gaoqing/utils.js | 2 +- lib/routes/gaoqingla/latest.js | 37 - lib/routes/gbcc/trust.js | 28 - lib/routes/geektime/news.js | 54 - lib/routes/getitfree/category.js | 59 - lib/routes/getitfree/search.js | 25 - lib/routes/getitfree/utils.js | 47 - lib/routes/girlimg/album.js | 57 - lib/routes/gitchat/newest.js | 23 - lib/routes/gitea/blog.js | 70 - lib/routes/gitlab/explore.js | 4 +- lib/routes/gitlab/release.js | 2 +- lib/routes/gitlab/tag.js | 2 +- lib/routes/go.jp/mofa/main.js | 15 +- lib/routes/gocomics/index.js | 2 +- lib/routes/gov/beijing/eea.js | 8 +- lib/routes/gov/beijing/mhc.js | 2 +- lib/routes/gov/chongqing/ljxq/zwgk.js | 2 +- .../nanjing/{getContent.js => get-content.js} | 4 +- lib/routes/gov/city/nanjing/index.js | 2 +- lib/routes/gov/cnca/hydt.js | 4 +- lib/routes/gov/cnca/jgdt.js | 4 +- lib/routes/gov/cnca/zxtz.js | 8 +- lib/routes/gov/cppcc/index.js | 4 +- lib/routes/gov/guangdong/edu.js | 2 +- lib/routes/gov/guangdong/eea.js | 2 +- lib/routes/gov/harbin/kjj.js | 4 +- lib/routes/gov/hubei/hbsia.js | 2 +- lib/routes/gov/hunan/notice.js | 4 +- lib/routes/gov/moa/sjzxfb.js | 20 - lib/routes/gov/mohrss/sbjm.js | 4 +- lib/routes/gov/mohurd/policy.js | 2 +- lib/routes/gov/ngd/index.js | 4 +- lib/routes/gov/nppa/channels.js | 4 +- lib/routes/gov/nppa/contents.js | 2 +- .../jiangsu/{getContent.js => get-content.js} | 4 +- lib/routes/gov/province/jiangsu/index.js | 2 +- lib/routes/gov/sapprft/7026.js | 4 +- lib/routes/gov/shanghai/sthj.js | 2 +- lib/routes/gov/suzhou/doc.js | 39 - lib/routes/gov/suzhou/news.js | 142 - lib/routes/gov/suzhou/utils.js | 20 - lib/routes/gov/taiwan/mnd.js | 4 +- lib/routes/gov/veterans/china.js | 2 +- lib/routes/gov/veterans/hebei.js | 10 +- lib/routes/gov/wuhan/kjj.js | 2 +- lib/routes/gov/wuhan/wehdz.js | 2 +- lib/routes/gov/xinwen/tujie.js | 2 +- lib/routes/gracg/user.js | 65 - lib/routes/gradcafe/result.js | 37 - lib/routes/growincity/news.js | 49 - lib/routes/grubstreet/utils.js | 78 - lib/routes/guanggoo/index.js | 2 +- lib/routes/guardian/guardian.js | 4 +- lib/routes/guat/news.js | 2 +- lib/routes/guet/news.js | 10 +- lib/routes/guiltfree/onsale.js | 48 - lib/routes/gvm/index.js | 94 - lib/routes/gwern/category.js | 4 +- lib/routes/hackerone/hacktivity.js | 6 +- lib/routes/hackerone/search.js | 6 +- lib/routes/haimaoba/comics.js | 57 - lib/routes/haohaozhu/discover.js | 2 +- lib/routes/haohaozhu/whole-house.js | 10 +- lib/routes/hdx/explore.js | 4 +- lib/routes/hentai-cosplays/hentai-cosplays.js | 11 - lib/routes/hentai-cosplays/utils.js | 4 +- lib/routes/hexo/fluid.js | 2 +- lib/routes/hexo/next.js | 2 +- lib/routes/hexo/yilia.js | 2 +- lib/routes/hkcd/pdf.js | 30 - lib/routes/hkcnews/news.js | 63 - lib/routes/hkej/index.js | 2 +- lib/routes/hkgolden/index.js | 110 - lib/routes/hopper/index.js | 82 - lib/routes/hpoi/index.js | 47 - lib/routes/hpoi/user.js | 37 - lib/routes/huawei/xinsheng/index.js | 61 - lib/routes/hugo/releases.js | 53 - lib/routes/huya/live.js | 2 +- lib/routes/icourse163/newest.js | 2 +- lib/routes/idownloadblog/index.js | 2 +- lib/routes/iea/index.js | 53 - lib/routes/ifanr/index.js | 8 +- lib/routes/ifnews/column.js | 8 +- lib/routes/iie/blog.js | 47 - lib/routes/im2maker/index.js | 2 +- lib/routes/imaijia/category.js | 63 - lib/routes/imuseum/index.js | 49 - lib/routes/index.js | 34 +- lib/routes/indienova/article.js | 53 - lib/routes/instapaper/person.js | 28 - ...mical_events.js => astronomical-events.js} | 0 lib/routes/iplay/utils.js | 8 +- lib/routes/ipsw/index.js | 57 +- lib/routes/itjuzi/invest.js | 32 - lib/routes/itjuzi/merge.js | 32 - lib/routes/itslide/new.js | 40 - lib/routes/iyiou/index.js | 41 - lib/routes/iyouport/index.js | 35 - lib/routes/iyouport/utils.js | 22 - lib/routes/jdlingyu/index.js | 45 - lib/routes/jianshu/trending.js | 28 - lib/routes/jiazhen108/index.js | 47 - lib/routes/jijitang/article.js | 19 - lib/routes/jijitang/publication.js | 18 - lib/routes/jingdong/zhongchou.js | 78 - lib/routes/jinritoutiao/keyword.js | 27 - lib/routes/jinse/catalogue.js | 20 - lib/routes/jinse/lives.js | 20 - lib/routes/jinse/timeline.js | 21 - lib/routes/jpmorganchase/research.js | 4 +- lib/routes/jskou/index.js | 21 - lib/routes/juesheng/index.js | 49 - lib/routes/justrun/index.js | 44 - lib/routes/jx3/news.js | 8 +- lib/routes/kaggle/competitions.js | 51 - lib/routes/kaggle/discussion.js | 72 - lib/routes/kaggle/user.js | 19 - lib/routes/kaiyan/index.js | 4 +- lib/routes/kirara/news.js | 30 - ...pular_recent.js => post-popular-recent.js} | 0 lib/routes/konami/pesmobile.js | 31 - lib/routes/kongfz/shop.js | 2 +- lib/routes/krankenkassen/index.js | 54 - lib/routes/ku/index.js | 31 - lib/routes/kuai/id.js | 4 +- lib/routes/kuai/index.js | 4 +- lib/routes/kzfeed/topic.js | 122 - lib/routes/lastfm/loved.js | 4 +- lib/routes/lastfm/recent.js | 4 +- lib/routes/lastfm/top.js | 4 +- lib/routes/law/dh.js | 4 +- lib/routes/law/gf.js | 4 +- lib/routes/law/hq.js | 4 +- lib/routes/law/hw.js | 4 +- lib/routes/law/jctd.js | 4 +- lib/routes/law/jh.js | 4 +- lib/routes/law/jtc.js | 4 +- lib/routes/law/ts.js | 4 +- lib/routes/law/zl.js | 4 +- lib/routes/leboncoin/ad.js | 136 - lib/routes/leetcode/check-cn.js | 98 - lib/routes/leetcode/check-us.js | 32 - lib/routes/leetcode/utils.js | 54 - lib/routes/letterboxd/utils.js | 12 +- lib/routes/liequtv/room.js | 36 - lib/routes/lifetimes/index.js | 18 +- lib/routes/lingyi/index.js | 64 - lib/routes/linkedkeeper/index.js | 37 - lib/routes/liquipedia/dota2_matches.js | 50 - lib/routes/liwushuo/index.js | 28 - lib/routes/liyuan-forums/threads.js | 24 +- lib/routes/lol/newsindex.js | 42 +- lib/routes/lolapp/article.js | 4 +- lib/routes/loveheaven/update.js | 45 - lib/routes/lowendtalk/discussion.js | 43 - lib/routes/macau-bolsas/index.js | 47 - lib/routes/macked/app.js | 25 - lib/routes/macwk/soft.js | 32 - lib/routes/mafengwo/note.js | 2 +- lib/routes/magireco/announcements.js | 2 +- .../{event_banner.js => event-banner.js} | 2 +- lib/routes/makeuseof/index.js | 59 - lib/routes/manhuadui/manhua.js | 36 - lib/routes/manong-weekly/issues.js | 33 - lib/routes/manxiaosi/book.js | 2 +- .../{hotComplete.js => hot-complete.js} | 10 +- lib/routes/matataki/site/posts/author.js | 17 - lib/routes/matataki/site/posts/favorite.js | 42 - .../matataki/site/posts/scoreranking.js | 15 - lib/routes/matataki/site/posts/tag.js | 16 - lib/routes/matataki/site/posts/timeranking.js | 15 - lib/routes/matataki/site/posts/token.js | 18 - lib/routes/matataki/utils/matataki-utils.js | 125 - lib/routes/maxnews/dota2.js | 45 - lib/routes/mcdonalds/news.js | 6 +- lib/routes/mediadigest/category.js | 10 +- lib/routes/meihua/article.js | 2 +- lib/routes/meihua/shots.js | 2 +- lib/routes/meituan/open/announce.js | 4 +- lib/routes/meituan/tech/home.js | 45 - lib/routes/mercari/index.js | 67 - lib/routes/method-studios/index.js | 10 +- lib/routes/metred/fuli.js | 42 - lib/routes/mhw/news.js | 9 +- lib/routes/mhw/update.js | 7 +- lib/routes/mi/board.js | 36 - lib/routes/mi/crowdfunding.js | 6 +- lib/routes/mi/miui/index.js | 2 +- lib/routes/micmicidol/article.js | 2 +- lib/routes/microsoft-store/updates.js | 2 +- lib/routes/mihoyo/bh2.js | 2 +- lib/routes/mihoyo/bh3.js | 2 +- lib/routes/mind42/index.js | 7 - lib/routes/mind42/search.js | 5 - lib/routes/mind42/tag.js | 5 - lib/routes/mind42/utils.js | 47 - lib/routes/mingjian/index.js | 46 - lib/routes/miniapp/article.js | 57 - lib/routes/miniapp/store/newest.js | 36 - .../{get_entries.js => get-entries.js} | 16 +- .../miniflux/{get_feeds.js => get-feeds.js} | 8 +- lib/routes/missevan/drama.js | 2 +- lib/routes/missevan/latest.js | 6 +- lib/routes/mitbbs/index.js | 54 - lib/routes/mitre/publications.js | 27 - lib/routes/mittrchina/index.js | 45 - lib/routes/mlhang/latest.js | 23 - lib/routes/mlog-club/projects.js | 33 - lib/routes/mlog-club/topics.js | 37 - lib/routes/mobdata/report.js | 30 - lib/routes/modian/zhongchou.js | 6 +- lib/routes/moxingfans/index.js | 4 +- lib/routes/moxingnet/index.js | 61 - lib/routes/mp4ba/index.js | 133 - lib/routes/mqube/latest.js | 21 - lib/routes/mqube/tag.js | 23 - lib/routes/mqube/top.js | 21 - lib/routes/mqube/user.js | 25 - lib/routes/muchong/journal.js | 2 +- lib/routes/mzitu/category.js | 26 - lib/routes/mzitu/home.js | 32 - lib/routes/mzitu/post.js | 41 - lib/routes/mzitu/tag.js | 25 - lib/routes/mzitu/tags.js | 23 - lib/routes/mzitu/util.js | 51 - lib/routes/namoc/announcement.js | 64 - lib/routes/namoc/exhibition.js | 67 - lib/routes/namoc/media.js | 67 - lib/routes/namoc/news.js | 67 - lib/routes/namoc/specials.js | 42 - lib/routes/nba/app_news.js | 40 - lib/routes/netflix/newsroom.js | 4 +- lib/routes/network360/jobs.js | 24 - lib/routes/newsmth/account.js | 39 - lib/routes/newsmth/section.js | 99 - lib/routes/nfmovies/index.js | 58 - lib/routes/ningmeng/song.js | 77 - lib/routes/nobelprize/index.js | 2 +- lib/routes/noi/index.js | 2 +- lib/routes/noi/province-news.js | 2 +- lib/routes/noi/rg-news.js | 2 +- lib/routes/noi/winners-list.js | 2 +- lib/routes/northhouse/index.js | 54 - lib/routes/nosec/index.js | 6 +- lib/routes/novel/axdzs.js | 67 - lib/routes/novel/biquge.js | 2 +- lib/routes/novel/biqugeinfo.js | 11 +- lib/routes/novel/booksky.js | 44 - lib/routes/novel/dcrsw.js | 58 - lib/routes/novel/ptwxz.js | 76 - lib/routes/novel/uukanshu.js | 4 +- lib/routes/novel/wenxuemi.js | 63 - lib/routes/novel/zhaishuyuan.js | 84 - lib/routes/npc/index.js | 42 - lib/routes/obsidian/announcements.js | 22 - lib/routes/one/index.js | 4 +- lib/routes/or/index.js | 54 - lib/routes/ow/patch.js | 23 - lib/routes/owspace/read.js | 16 +- lib/routes/p-articles/contributors.js | 2 +- lib/routes/p-articles/section.js | 2 +- lib/routes/partnershiponai/resources.js | 49 - lib/routes/patchwork.kernel.org/comments.js | 2 +- lib/routes/pcr/news-cn.js | 2 +- lib/routes/pcr/news-tw.js | 8 +- lib/routes/pcr/news.js | 31 - lib/routes/pediy/topic.js | 10 +- lib/routes/pediy/utils.js | 10 +- lib/routes/pgyer/app.js | 91 - lib/routes/piaohua/hot.js | 37 - lib/routes/piapro/public.js | 6 +- lib/routes/piapro/user.js | 2 +- lib/routes/piapro/utils.js | 2 +- lib/routes/pintu360/index.js | 54 - lib/routes/plainlaw/archives.js | 42 - lib/routes/pmcaff/feed.js | 4 +- lib/routes/pmcaff/list.js | 4 +- lib/routes/pmcaff/user.js | 56 - lib/routes/pocket/trending.js | 31 - lib/routes/polar/blog.js | 47 - lib/routes/popiask/questions.js | 41 - lib/routes/popiask/tapechat_questions.js | 43 - lib/routes/popyard/index.js | 93 - lib/routes/pork-price/index.js | 4 +- lib/routes/potplayer/update.js | 6 +- lib/routes/processon/popular.js | 51 - lib/routes/project-zero-issues/index.js | 4 +- lib/routes/ps/list.js | 34 - lib/routes/ps/product.js | 133 - lib/routes/ps/ps4updates.js | 60 - lib/routes/ptpress/book.js | 2 +- lib/routes/putonghua/hangzhou.js | 45 - lib/routes/qlwb/city.js | 54 - lib/routes/qlwb/news.js | 37 - lib/routes/qstheory/index.js | 2 +- lib/routes/qtfyfl/category.js | 91 - lib/routes/quantamagazine/archive.js | 24 +- lib/routes/questmobile/report.js | 121 - lib/routes/qutoutiao/category.js | 8 +- lib/routes/react/react-native-weekly.js | 2 +- lib/routes/remote-work/index.js | 2 +- lib/routes/rescuetime/release-notes.js | 4 +- lib/routes/rs05/rs05.js | 35 - lib/routes/rthk-news/index.js | 4 +- lib/routes/s-hentai/index.js | 64 - lib/routes/sagawa/index.js | 67 - lib/routes/sankakucomplex/post.js | 50 - lib/routes/sans/summit_archive.js | 24 - lib/routes/sbs/chinese.js | 70 - lib/routes/scala-blog/scala-blog.js | 29 - lib/routes/scboy/thread.js | 2 +- lib/routes/sckjt/news.js | 35 - lib/routes/scoresaber/user.js | 44 - .../{release_notes.js => release-notes.js} | 0 lib/routes/sexinsex/index.js | 12 +- lib/routes/sf/sffq-announce.js | 22 - lib/routes/shanbay/checkin.js | 6 +- lib/routes/shanbay/footprints.js | 4 +- lib/routes/shengwugu/index.js | 41 - lib/routes/shinybbs/index.js | 60 - lib/routes/shinybbs/latest.js | 57 - lib/routes/shinybbs/p.js | 38 - lib/routes/showroom/room.js | 31 - lib/routes/shuax/project.js | 37 - lib/routes/shuhui/comics.js | 95 - lib/routes/simonsfoundation/articles.js | 11 +- lib/routes/simpread/changelog.js | 8 +- lib/routes/sixthtone/news.js | 52 - lib/routes/sketch/updates.js | 6 +- lib/routes/socialbeta/home.js | 4 +- lib/routes/socialbeta/hunt.js | 29 - lib/routes/socialclub/events.js | 4 +- lib/routes/soomal/topics.js | 130 - lib/routes/soul/hot.js | 34 +- lib/routes/soundcloud/utils.js | 2 +- lib/routes/steam/search.js | 2 +- lib/routes/stork/keyword.js | 58 - lib/routes/t66y/index.js | 47 +- lib/routes/t66y/post.js | 22 +- lib/routes/tahui/rptlist.js | 48 - lib/routes/tam/forecast.js | 53 - lib/routes/tanwu/products.js | 27 - lib/routes/technologyreview/topic.js | 4 +- lib/routes/tencent/bigdata/index.js | 53 - lib/routes/tencent/bugly/changelog.js | 49 - lib/routes/tencent/egame/room.js | 52 - lib/routes/tencent/gameinstitute/community.js | 60 - lib/routes/tencent/guyu/channel.js | 64 - lib/routes/tencent/tucaoqq/post.js | 4 +- lib/routes/tencent/video/playlist.js | 46 - .../tencent/wechat/miniprogram/devtools.js | 9 +- .../tencent/wechat/miniprogram/framework.js | 6 +- .../tencent/wechat/miniprogram/plugins.js | 54 - .../tencent/wechat/miniprogram/wxcloud.js | 8 +- lib/routes/thrillist/index.js | 67 - lib/routes/tianya/comments.js | 41 - lib/routes/tianya/index.js | 32 - lib/routes/tianya/user.js | 34 - lib/routes/tingdiantz/95598.js | 7 +- lib/routes/titsguru/model.js | 2 +- lib/routes/titsguru/util.js | 4 +- lib/routes/tongli/news.js | 2 +- lib/routes/totalcommander/whatsnew.js | 2 +- lib/routes/touhougarakuta/index.js | 2 +- lib/routes/touhougarakuta/json2html.js | 8 +- lib/routes/tprtc/cqzr.js | 4 +- lib/routes/tprtc/news.js | 4 +- lib/routes/tprtc/qyzc.js | 4 +- lib/routes/tssstatus/index.js | 35 - lib/routes/tuicool/mags.js | 41 - lib/routes/ui-cn/article.js | 11 +- lib/routes/uisdc/news.js | 34 - lib/routes/uisdc/talk.js | 58 - lib/routes/uisdc/topic.js | 55 - lib/routes/uisdc/zt.js | 65 - lib/routes/uniqlo/stylingbook.js | 24 - lib/routes/unit-image/films.js | 4 +- lib/routes/universities/ahau/cs_news/utils.js | 2 +- lib/routes/universities/ahau/jwc/utils.js | 2 +- lib/routes/universities/ahau/main/utils.js | 2 +- lib/routes/universities/ahmu/news.js | 2 +- lib/routes/universities/ahut/cstzgg.js | 2 +- lib/routes/universities/ahut/jwc.js | 2 +- lib/routes/universities/ahut/news.js | 2 +- lib/routes/universities/bjtu/gs/index.js | 6 +- lib/routes/universities/buaa/news/index.js | 66 - lib/routes/universities/buaa/utils.js | 57 - lib/routes/universities/bupt/funbox.js | 6 +- lib/routes/universities/bupt/grs.js | 6 +- lib/routes/universities/bupt/news.js | 8 +- lib/routes/universities/bupt/portal.js | 8 +- lib/routes/universities/bupt/utils.js | 7 +- lib/routes/universities/bwu/utils.js | 2 +- lib/routes/universities/cpu/home.js | 2 +- lib/routes/universities/cpu/jwc.js | 2 +- lib/routes/universities/cpu/yjsy.js | 2 +- .../universities/cqu/jwc/announcement.js | 2 +- lib/routes/universities/cqu/net/info.js | 2 +- lib/routes/universities/cqu/news/tz.js | 2 +- lib/routes/universities/cqu/news/utils.js | 4 +- lib/routes/universities/cqu/sci/info.js | 7 +- lib/routes/universities/cqu/youth/info.js | 7 +- lib/routes/universities/cug/gcxy/index.js | 4 +- lib/routes/universities/cug/news.js | 8 +- lib/routes/universities/cuit/cxxww.js | 4 +- lib/routes/universities/dlmu/news.js | 6 +- lib/routes/universities/dlu/jiaowu/news.js | 9 +- lib/routes/universities/fudan/cce.js | 6 +- .../fzu/{news_min.js => news-min.js} | 0 lib/routes/universities/gdou/jwc/utils.js | 4 +- lib/routes/universities/gzmtu/jwc/utils.js | 2 +- lib/routes/universities/gzmtu/tsg/utils.js | 2 +- .../hhu/{libNews.js => lib-news.js} | 12 +- .../hhu/{libNewsc.js => lib-newsc.js} | 32 +- .../universities/hnust/graduate/index.js | 2 +- lib/routes/universities/jnu/yw/index.js | 4 +- lib/routes/universities/kmust/job/careers.js | 2 +- lib/routes/universities/kmust/job/jobfairs.js | 2 +- lib/routes/universities/kmust/jwc.js | 2 +- lib/routes/universities/lit/jwc.js | 2 +- lib/routes/universities/lit/tw.js | 2 +- lib/routes/universities/lit/xwzx.js | 4 +- lib/routes/universities/lntu/jwnews.js | 4 +- lib/routes/universities/lyu/news/utils.js | 2 +- .../universities/mit/graduateadmissions.js | 6 +- lib/routes/universities/nciae/news.js | 6 +- lib/routes/universities/nciae/tzgg.js | 6 +- lib/routes/universities/nciae/xsxx.js | 6 +- lib/routes/universities/njfu/jwc.js | 9 +- lib/routes/universities/njtech/jwc.js | 6 +- lib/routes/universities/nuc/index.js | 8 +- lib/routes/universities/nudt/yjszs.js | 7 +- .../universities/shanghaitech/activity.js | 2 +- .../shanghaitech/sist/activity.js | 6 +- lib/routes/universities/shou/www.js | 2 +- lib/routes/universities/slu/utils.js | 4 +- lib/routes/universities/swufe/seie/index.js | 6 +- lib/routes/universities/swust/cs.js | 41 +- lib/routes/universities/swust/helper.js | 2 +- .../swust/{jwc_news.js => jwc-news.js} | 2 +- .../swust/{jwc_notice.js => jwc-notice.js} | 2 +- lib/routes/universities/thu/index.js | 79 +- lib/routes/universities/usst/jwc.js | 6 +- lib/routes/universities/utdallas/isso.js | 6 +- lib/routes/universities/ynnu/edu/base64.js | 9 +- lib/routes/universities/ynnu/edu/news.js | 6 +- lib/routes/universities/ynu/utils.js | 16 +- lib/routes/universities/yzu/home.js | 8 +- lib/routes/universities/yzu/yjszs.js | 8 +- lib/routes/universities/zjgsu/tzgg/utils.js | 2 +- lib/routes/universities/zjut/design.js | 6 +- lib/routes/universities/zjut/index.js | 2 +- .../universities/zucc/cssearch/index.js | 2 +- lib/routes/universities/zucc/news/index.js | 8 +- lib/routes/universities/zzu/news.js | 2 +- lib/routes/universities/zzu/soft/news.js | 2 +- .../{argument_audio.js => argument-audio.js} | 15 +- lib/routes/uwants/index.js | 14 +- lib/routes/vgtime/keyword.js | 46 - lib/routes/vgtime/news.js | 52 - lib/routes/vgtime/release.js | 20 - lib/routes/voa/day-photos.js | 54 - lib/routes/voa/index.js | 2 +- lib/routes/vol/lastupdate.js | 6 +- lib/routes/vuevideo/user.js | 60 - lib/routes/vulture/utils.js | 2 +- lib/routes/wanwansub/index.js | 57 - lib/routes/wanwansub/info.js | 44 - lib/routes/watchface/update.js | 8 +- lib/routes/webtoons/comic.js | 4 +- lib/routes/webtoons/naver.js | 2 +- lib/routes/weexcn/index.js | 4 +- lib/routes/weforum/report.js | 56 - lib/routes/wegene/column.js | 7 +- lib/routes/weidian/goods.js | 2 +- lib/routes/weseepro/circle.js | 38 - lib/routes/weseepro/newest-direct.js | 69 - lib/routes/weseepro/newest.js | 69 - lib/routes/westore/new.js | 40 - lib/routes/whalegogo/home.js | 43 - lib/routes/whalegogo/portal.js | 57 - lib/routes/whb/zhuzhan.js | 14 +- lib/routes/wikihow/category.js | 34 - lib/routes/wikihow/index.js | 32 - lib/routes/wineyun/index.js | 36 - lib/routes/wired/tag.js | 31 - lib/routes/wolley/host.js | 39 - lib/routes/wolley/index.js | 47 - lib/routes/wolley/user.js | 38 - lib/routes/wto/dispute-settlement.js | 2 +- lib/routes/wukong/user.js | 93 - lib/routes/x-mol/news.js | 48 - lib/routes/x-mol/paper.js | 78 - lib/routes/x-mol/utils.js | 16 - lib/routes/xiachufang/utils.js | 11 +- lib/routes/xiaoheihe/discount.js | 4 +- lib/routes/xiaoheihe/news.js | 6 +- lib/routes/xiaoheihe/user.js | 2 +- lib/routes/xici/index.js | 33 - lib/routes/xinquji/internal.js | 15 - lib/routes/xinquji/today.js | 15 - lib/routes/xposed/module.js | 36 - lib/routes/xuangubao/subject.js | 7 +- .../{course_info.js => course-info.js} | 0 .../{course_list.js => course-list.js} | 6 +- lib/routes/yahoo-jp-tv/index.js | 21 - ...pular_recent.js => post-popular-recent.js} | 0 lib/routes/yicas/blog.js | 52 - lib/routes/yidoutang/index.js | 33 - lib/routes/yinxiang/card.js | 8 +- lib/routes/yinxiang/category.js | 8 +- lib/routes/yinxiang/note.js | 8 +- lib/routes/yinxiang/personal.js | 8 +- lib/routes/yinxiang/tag.js | 8 +- lib/routes/youdao/latest.js | 2 +- lib/routes/youdao/xueba.js | 4 +- lib/routes/yuzu-emu/entry.js | 2 +- lib/routes/zcfy/hot.js | 34 - lib/routes/zcfy/index.js | 34 - lib/routes/zfrontier/board_postlist.js | 36 - lib/routes/zfrontier/postlist.js | 33 - lib/routes/zhanqi/room.js | 37 - lib/routes/zhilian/index.js | 78 - lib/routes/zhishifenzi/depth.js | 4 +- lib/routes/zhishifenzi/innovation.js | 12 +- lib/routes/zhuixinfan/list.js | 6 +- lib/routes/zhutix/latest.js | 41 - lib/routes/zimuku/index.js | 2 +- lib/routes/zimuxia/portfolio.js | 39 - lib/routes/zimuzu/resource.js | 19 - lib/routes/zimuzu/top.js | 27 - lib/routes/ziroom/room.js | 55 - lib/routes/zreading/home.js | 4 +- lib/routes/zsnews/index.js | 4 +- lib/routes/zzz/index.js | 59 - lib/utils/cf-email.js | 2 +- lib/utils/common-config.js | 6 +- lib/utils/common-utils.js | 10 +- lib/utils/{dateParser.js => date-parser.js} | 31 +- lib/utils/date.js | 76 +- lib/utils/got.js | 7 +- lib/utils/logger.js | 6 +- lib/utils/pac-proxy.js | 75 + lib/utils/parse-date.js | 34 +- lib/utils/puppeteer.js | 28 +- lib/utils/rand-user-agent.js | 3 +- lib/utils/readable-social.js | 6 +- lib/utils/render.js | 2 + lib/utils/request-wrapper.js | 53 +- lib/utils/unify-proxy.js | 30 +- lib/utils/valid-host.js | 2 +- lib/utils/wechat-mp.js | 20 +- lib/v2/12306/index.js | 8 +- lib/v2/12306/zxdt.js | 17 +- lib/v2/141jav/radar.js | 4 +- lib/v2/141ppv/index.js | 5 +- lib/v2/141ppv/radar.js | 4 +- lib/v2/163/exclusive.js | 2 +- lib/v2/163/music/artist-songs.js | 2 +- lib/v2/163/music/djradio.js | 2 +- lib/v2/163/music/playlist.js | 2 +- lib/v2/163/music/userplayrecords.js | 2 +- lib/v2/163/news/rank.js | 10 +- lib/v2/163/news/special.js | 8 +- lib/v2/163/open/vip.js | 2 +- lib/v2/163/renjian.js | 4 +- lib/v2/163/today.js | 2 +- lib/v2/18comic/index.js | 2 +- lib/v2/18comic/search.js | 2 +- lib/v2/18comic/utils.js | 6 +- lib/v2/19lou/index.js | 2 +- lib/v2/1lou/index.js | 53 + lib/v2/1lou/maintainer.js | 3 + lib/v2/1lou/radar.js | 13 + lib/v2/1lou/router.js | 3 + lib/v2/1point3acres/blog.js | 4 +- lib/v2/1point3acres/category.js | 2 +- lib/v2/1point3acres/maintainer.js | 4 +- lib/v2/1point3acres/offer.js | 10 +- lib/v2/1point3acres/section.js | 4 +- lib/v2/1point3acres/thread.js | 2 +- lib/v2/1point3acres/utils.js | 4 +- lib/v2/2047/index.js | 55 - lib/v2/2047/maintainer.js | 3 - lib/v2/2047/radar.js | 13 - lib/v2/2047/router.js | 3 - lib/v2/2048/index.js | 38 +- lib/v2/2cycd/radar.js | 2 +- lib/v2/35photo/actual.js | 7 - lib/v2/35photo/author.js | 9 - lib/v2/35photo/genre.js | 10 - lib/v2/35photo/interesting.js | 7 - lib/v2/35photo/maintainer.js | 8 - lib/v2/35photo/map.js | 7 - lib/v2/35photo/new.js | 7 - lib/v2/35photo/radar.js | 43 - lib/v2/35photo/router.js | 8 - lib/v2/35photo/utils.js | 57 - lib/v2/36kr/hot-list.js | 4 +- lib/v2/36kr/index.js | 8 +- lib/v2/36kr/utils.js | 8 +- lib/v2/3dmgame/game.js | 7 +- .../{news_center.js => news-center.js} | 2 +- lib/v2/3dmgame/router.js | 2 +- lib/v2/4gamers/category.js | 31 + lib/v2/4gamers/maintainer.js | 6 + lib/v2/4gamers/radar.js | 31 + lib/v2/4gamers/router.js | 6 + lib/v2/4gamers/tag.js | 23 + lib/v2/4gamers/templates/description.art | 3 + lib/v2/4gamers/templates/image.art | 3 + lib/v2/4gamers/topic.js | 22 + lib/v2/4gamers/utils.js | 73 + lib/v2/4ksj/forum.js | 25 +- lib/v2/500px/router.js | 2 +- lib/v2/500px/{tribeSet.js => tribe-set.js} | 2 +- lib/v2/500px/user.js | 2 +- lib/v2/500px/utils.js | 2 +- lib/v2/50forum/zhuanjia.js | 2 +- lib/v2/56kog/class.js | 10 + lib/v2/56kog/maintainer.js | 4 + lib/v2/56kog/radar.js | 85 + lib/v2/56kog/router.js | 4 + lib/v2/56kog/templates/description.art | 32 + lib/v2/56kog/top.js | 10 + lib/v2/56kog/util.js | 111 + lib/v2/591/list.js | 4 +- lib/v2/5eplay/utils.js | 11 +- lib/v2/6park/index.js | 6 +- lib/v2/6park/news.js | 12 +- .../{latestMovies.js => latest-movies.js} | 0 .../{latestTVSeries.js => latest-tvseries.js} | 0 lib/v2/6v123/router.js | 4 +- lib/v2/6v123/utils.js | 6 +- lib/v2/78dm/index.js | 4 +- lib/v2/7mmtv/radar.js | 4 +- lib/v2/81/81rc/index.js | 2 +- lib/v2/8264/list.js | 4 +- lib/v2/8kcos/article.js | 2 +- lib/v2/8kcos/cat.js | 2 +- lib/v2/8kcos/latest.js | 2 +- lib/v2/8kcos/tag.js | 2 +- lib/v2/91porn/utils.js | 4 +- lib/v2/95mm/utils.js | 5 +- lib/v2/9to5/subsite.js | 2 +- lib/v2/9to5/utils.js | 4 +- lib/v2/abc/index.js | 161 + lib/v2/{ezone => abc}/maintainer.js | 0 lib/v2/abc/radar.js | 13 + lib/v2/abc/router.js | 3 + lib/v2/abc/templates/description.art | 21 + lib/v2/abskoop/index.js | 2 +- lib/v2/abskoop/nsfw.js | 2 +- lib/v2/acfun/article.js | 14 +- lib/v2/acpaa/index.js | 57 + lib/v2/acpaa/maintainer.js | 3 + lib/v2/acpaa/radar.js | 19 + lib/v2/acpaa/router.js | 3 + lib/v2/aeon/utils.js | 4 +- lib/v2/agirls/index.js | 24 +- lib/v2/agirls/radar.js | 6 +- lib/v2/agirls/router.js | 4 +- .../agirls/{topic_list.js => topic-list.js} | 15 +- lib/v2/agirls/topic.js | 19 +- lib/v2/agirls/utils.js | 10 +- lib/v2/agora0/index.js | 2 +- lib/v2/ahjzu/news.js | 2 +- lib/v2/aicaijing/index.js | 2 +- lib/v2/aijishu/utils.js | 6 +- lib/v2/ainvest/article.js | 2 +- lib/v2/ainvest/news.js | 2 +- lib/v2/ainvest/radar.js | 4 +- lib/v2/ainvest/utils.js | 4 +- lib/v2/aip/journal-pupp.js | 2 +- lib/v2/aisixiang/column.js | 4 +- lib/v2/aisixiang/thinktank.js | 4 +- lib/v2/aisixiang/toplist.js | 2 +- lib/v2/aisixiang/utils.js | 4 +- lib/v2/aisixiang/zhuanti.js | 2 +- lib/v2/aliresearch/information.js | 2 +- lib/v2/alistapart/radar.js | 4 +- .../{database_month.js => database-month.js} | 0 lib/v2/aliyun/notice.js | 2 +- lib/v2/aliyun/router.js | 2 +- lib/v2/aliyundrive/files.js | 59 - lib/v2/aliyundrive/maintainer.js | 3 - lib/v2/aliyundrive/radar.js | 13 - lib/v2/aliyundrive/router.js | 3 - lib/v2/aljazeera/index.js | 6 +- lib/v2/allrecode/index.js | 65 - lib/v2/allrecode/maintainer.js | 5 - lib/v2/allrecode/news.js | 35 - lib/v2/allrecode/radar.js | 25 - lib/v2/allrecode/router.js | 4 - lib/v2/ally/rail.js | 4 +- lib/v2/amazon/kindle-software-updates.js | 2 +- lib/v2/amazon/maintainer.js | 2 +- lib/v2/annualreviews/index.js | 2 +- lib/v2/anquanke/vul.js | 6 +- lib/v2/apkpure/versions.js | 6 +- lib/v2/apnews/topics.js | 5 +- lib/v2/app-center/release.js | 2 +- lib/v2/apple/apps.js | 105 +- ...{exchange_repair.js => exchange-repair.js} | 0 lib/v2/apple/maintainer.js | 2 +- lib/v2/apple/radar.js | 6 +- lib/v2/apple/router.js | 4 +- lib/v2/appledaily/index.js | 65 - lib/v2/appledaily/maintainer.js | 3 - lib/v2/appledaily/radar.js | 157 - lib/v2/appleinsider/index.js | 2 +- lib/v2/appstore/gofans.js | 34 - lib/v2/appstore/maintainer.js | 1 - lib/v2/appstore/radar.js | 11 - lib/v2/appstore/router.js | 1 - lib/v2/aqara/community.js | 2 +- lib/v2/aqara/news.js | 4 +- lib/v2/aqara/post.js | 4 +- lib/v2/arcteryx/maintainer.js | 6 +- lib/v2/arcteryx/new-arrivals.js | 5 +- lib/v2/arcteryx/outlet.js | 5 +- lib/v2/arcteryx/regear-new-arrivals.js | 6 +- lib/v2/arcteryx/utils.js | 6 +- lib/v2/artstation/maintainer.js | 3 + lib/v2/artstation/radar.js | 13 + lib/v2/artstation/router.js | 3 + lib/v2/artstation/templates/description.art | 17 + lib/v2/artstation/user.js | 98 + lib/v2/ash-maurya/index.js | 54 - lib/v2/ash-maurya/maintainer.js | 3 - lib/v2/ash-maurya/radar.js | 13 - lib/v2/ash-maurya/router.js | 3 - lib/v2/asiantolick/index.js | 100 +- lib/v2/asiantolick/maintainer.js | 8 +- lib/v2/asiantolick/radar.js | 46 +- lib/v2/asiantolick/router.js | 4 +- lib/v2/asiantolick/templates/description.art | 12 +- lib/v2/atcoder/contest.js | 2 +- lib/v2/audiobar/latest.js | 53 - lib/v2/audiobar/maintainer.js | 3 - lib/v2/audiobar/radar.js | 13 - lib/v2/auto-stats/index.js | 65 + .../maintainer.js | 0 lib/v2/auto-stats/radar.js | 35 + lib/v2/auto-stats/router.js | 3 + lib/v2/baai/comments.js | 5 +- lib/v2/baai/events.js | 7 +- lib/v2/baai/hub.js | 11 +- lib/v2/backlinko/blog.js | 44 + lib/v2/backlinko/maintainer.js | 3 + lib/v2/backlinko/radar.js | 13 + .../router.js | 1 - lib/v2/baidu/maintainer.js | 1 + lib/v2/baidu/radar.js | 13 +- lib/v2/baidu/router.js | 1 + lib/v2/baidu/search.js | 54 + lib/v2/baidu/templates/description.art | 6 + lib/v2/baidu/tieba/post.js | 2 +- lib/v2/baijing/index.js | 54 - lib/v2/baijing/maintainer.js | 3 - lib/v2/baijing/radar.js | 22 - lib/v2/baijing/router.js | 3 - lib/v2/bangumi/maintainer.js | 2 + lib/v2/bangumi/moe/index.js | 2 +- lib/v2/bangumi/online/online.js | 2 +- lib/v2/bangumi/radar.js | 12 + lib/v2/bangumi/router.js | 2 + lib/v2/bangumi/tv/calendar/today.js | 10 +- lib/v2/bangumi/tv/group/reply.js | 2 +- lib/v2/bangumi/tv/other/followrank.js | 42 + lib/v2/bangumi/tv/person/index.js | 2 +- lib/v2/bangumi/tv/subject/comments.js | 11 +- lib/v2/bangumi/tv/subject/ep.js | 4 +- lib/v2/bangumi/tv/subject/index.js | 2 +- lib/v2/bangumi/tv/user/wish.js | 38 + lib/v2/bast/index.js | 2 +- lib/v2/bbc/index.js | 22 +- lib/v2/bbc/utils.js | 2 +- lib/v2/bbcnewslabs/news.js | 3 +- lib/v2/bdys/index.js | 4 +- lib/v2/behance/radar.js | 9 +- lib/v2/bellroy/maintainer.js | 2 +- lib/v2/bendibao/news.js | 6 +- lib/v2/bilibili/bangumi.js | 24 +- lib/v2/bilibili/blackboard.js | 31 - lib/v2/bilibili/cache.js | 149 +- lib/v2/bilibili/channel.js | 31 - lib/v2/bilibili/coin.js | 4 +- lib/v2/bilibili/danmaku.js | 17 +- lib/v2/bilibili/dynamic.js | 86 +- lib/v2/bilibili/fav.js | 6 +- lib/v2/bilibili/followers.js | 4 +- ...wings_article.js => followings-article.js} | 4 +- ...wings_dynamic.js => followings-dynamic.js} | 86 +- ...ollowings_video.js => followings-video.js} | 6 +- lib/v2/bilibili/followings.js | 6 +- .../bilibili/{hotSearch.js => hot-search.js} | 4 +- lib/v2/bilibili/{linkNews.js => link-news.js} | 2 +- lib/v2/bilibili/{liveArea.js => live-area.js} | 0 lib/v2/bilibili/{liveRoom.js => live-room.js} | 2 +- .../{liveSearch.js => live-search.js} | 4 +- lib/v2/bilibili/maintainer.js | 8 +- lib/v2/bilibili/{mallIP.js => mall-ip.js} | 0 lib/v2/bilibili/{mallNew.js => mall-new.js} | 4 +- ...anga_followings.js => manga-followings.js} | 4 +- .../{manga_update.js => manga-update.js} | 0 lib/v2/bilibili/online.js | 25 - lib/v2/bilibili/page.js | 4 +- lib/v2/bilibili/partion-ranking.js | 14 +- lib/v2/bilibili/partion.js | 4 +- lib/v2/bilibili/platform.js | 43 + lib/v2/bilibili/popular.js | 2 +- lib/v2/bilibili/radar.js | 16 +- lib/v2/bilibili/ranking.js | 8 +- lib/v2/bilibili/readlist.js | 2 +- lib/v2/bilibili/router.js | 39 +- lib/v2/bilibili/topic.js | 50 - .../{user_bangumi.js => user-bangumi.js} | 4 +- .../{userChannel.js => user-channel.js} | 4 +- .../{userCollection.js => user-collection.js} | 4 +- lib/v2/bilibili/{userFav.js => user-fav.js} | 4 +- lib/v2/bilibili/utils.js | 113 +- lib/v2/bilibili/video-all.js | 19 +- lib/v2/bilibili/video.js | 21 +- lib/v2/bilibili/vsearch.js | 8 +- lib/v2/bilibili/watchlater.js | 8 +- ...eekly_recommend.js => weekly-recommend.js} | 2 +- .../index.js => v2/bing/daily-wallpaper.js} | 9 +- lib/v2/bing/maintainer.js | 4 + lib/v2/bing/radar.js | 22 + lib/v2/bing/router.js | 4 + lib/v2/bing/search.js | 28 + lib/v2/bioone/featured.js | 4 +- lib/v2/bioone/journal.js | 2 +- lib/v2/biquge/index.js | 10 +- lib/v2/bitmovin/blog.js | 2 +- lib/v2/bjfu/it/utils.js | 2 +- lib/v2/bjfu/news/utils.js | 2 +- lib/v2/bjsk/keti.js | 2 +- lib/v2/bjwxdxh/index.js | 2 +- lib/v2/bjx/huanbao.js | 4 +- lib/v2/blizzard/news.js | 2 +- lib/v2/blockbeats/index.js | 43 - lib/v2/blockbeats/router.js | 3 - lib/v2/bloomberg/utils.js | 27 +- lib/v2/bnu/dwxgb.js | 41 +- lib/v2/boc/radar.js | 2 +- lib/v2/boc/whpj.js | 2 +- lib/v2/booru/maintainer.js | 3 + lib/v2/booru/mmda.js | 47 + lib/v2/booru/radar.js | 12 + lib/v2/booru/router.js | 3 + lib/v2/bossdesign/index.js | 41 + lib/v2/bossdesign/maintainer.js | 3 + lib/v2/bossdesign/radar.js | 13 + lib/v2/{dbmv => bossdesign}/router.js | 0 lib/v2/brave/latest.js | 6 +- lib/v2/brooklynmuseum/radar.js | 2 +- lib/v2/bse/index.js | 11 +- lib/v2/bsky/keyword.js | 2 +- lib/v2/bsky/maintainer.js | 1 + lib/v2/bsky/posts.js | 42 + lib/v2/bsky/radar.js | 6 + lib/v2/bsky/router.js | 1 + lib/v2/bsky/templates/post.art | 15 + lib/v2/bsky/utils.js | 52 + lib/v2/btzj/index.js | 4 +- lib/v2/buaa/maintainer.js | 4 + lib/v2/buaa/news/index.js | 46 + lib/v2/buaa/radar.js | 21 + lib/v2/buaa/router.js | 4 + lib/v2/buaa/sme.js | 59 + lib/v2/bulianglin/bulianglin.js | 32 + lib/v2/{good => bulianglin}/maintainer.js | 2 +- lib/v2/bulianglin/radar.js | 13 + lib/v2/bulianglin/router.js | 3 + lib/v2/byteclicks/index.js | 2 +- lib/v2/byteclicks/tag.js | 2 +- lib/v2/bytes/bytes.js | 2 +- lib/v2/bytes/maintainer.js | 1 - lib/v2/bytes/radar.js | 2 +- lib/v2/c114/roll.js | 2 +- lib/v2/caam/index.js | 64 + lib/v2/{lianxh => caam}/maintainer.js | 0 lib/v2/caam/radar.js | 2677 ++++++ lib/v2/caam/router.js | 3 + lib/v2/caijing/roll.js | 2 +- lib/v2/caixin/blog.js | 113 +- lib/v2/caixin/category.js | 6 +- lib/v2/caixin/router.js | 2 +- lib/v2/caixin/utils.js | 45 +- lib/v2/caixin/weekly.js | 35 +- lib/v2/caixinglobal/latest.js | 64 + lib/v2/caixinglobal/maintainer.js | 3 + lib/v2/caixinglobal/radar.js | 13 + lib/v2/{audiobar => caixinglobal}/router.js | 2 +- lib/v2/camchina/index.js | 2 +- lib/v2/cankaoxiaoxi/index.js | 2 +- lib/v2/cas/genetics/index.js | 63 + lib/v2/cas/genetics/xshd.js | 29 - lib/v2/cas/is/index.js | 2 +- lib/v2/cas/maintainer.js | 2 +- lib/v2/cas/mesalab/kb.js | 2 +- lib/v2/cas/radar.js | 8 +- lib/v2/cas/router.js | 2 +- lib/v2/cau/yjs.js | 2 +- lib/v2/caus/index.js | 2 +- lib/v2/cbaigui/index.js | 4 +- lib/v2/cbc/topics.js | 4 +- lib/v2/ccf/ccfcv/index.js | 2 +- lib/v2/cctv/jx.js | 2 +- lib/v2/cctv/lm.js | 2 +- lib/v2/cctv/maintainer.js | 1 - lib/v2/cctv/radar.js | 6 - lib/v2/cctv/router.js | 1 - lib/v2/cctv/special.js | 110 - lib/v2/cctv/utils/mzzlbg.js | 2 +- lib/v2/cde/utils.js | 2 +- lib/v2/cdi/index.js | 2 +- .../{projectList.js => project-list.js} | 0 lib/v2/cdzjryb/router.js | 2 +- lib/v2/cfachina/analygarden.js | 60 + lib/v2/cfachina/maintainer.js | 3 + lib/v2/cfachina/radar.js | 13 + lib/v2/cfachina/router.js | 3 + lib/v2/cfmmc/index.js | 72 + lib/v2/cfmmc/maintainer.js | 3 + lib/v2/cfmmc/radar.js | 17 + lib/v2/cfmmc/router.js | 3 + lib/v2/changba/user.js | 4 +- lib/v2/chiculture/topic.js | 8 +- lib/v2/china/finance/finance.js | 61 + lib/v2/china/maintainer.js | 1 + lib/v2/china/news/military/news.js | 11 +- lib/v2/china/radar.js | 9 + lib/v2/china/router.js | 1 + lib/v2/chinacef/experts.js | 38 - lib/v2/chinacef/hot.js | 38 - lib/v2/chinacef/index.js | 39 - lib/v2/chinacef/maintainer.js | 5 - lib/v2/chinacef/radar.js | 25 - lib/v2/chinacef/router.js | 5 - lib/v2/chinacef/templates/description.art | 1 - lib/v2/chinacef/utils.js | 55 - lib/v2/chinafactcheck/index.js | 41 +- .../chinafactcheck/templates/description.art | 1 - lib/v2/chinafactcheck/utils.js | 32 +- lib/v2/chinaisa/index.js | 83 + lib/v2/chinaisa/maintainer.js | 3 + lib/v2/chinaisa/radar.js | 18 + lib/v2/chinaisa/router.js | 3 + lib/v2/chinamoney/channels.js | 57 + lib/v2/chinamoney/maintainer.js | 3 + lib/v2/chinamoney/notice.js | 61 + lib/v2/chinamoney/radar.js | 11 + lib/v2/chinamoney/router.js | 3 + lib/v2/chinanews/index.js | 2 +- lib/v2/chinaventure/index.js | 2 +- lib/v2/chinawriter/index.js | 12 +- lib/v2/chsi/hotnews.js | 14 +- lib/v2/chsi/kydt.js | 7 +- lib/v2/chsi/kyzx.js | 6 +- lib/v2/chuanliu/maintainer.js | 3 + lib/v2/chuanliu/nice.js | 76 + lib/v2/chuanliu/radar.js | 13 + lib/v2/chuanliu/router.js | 3 + lib/v2/chuanliu/templates/description.art | 23 + lib/v2/cib/maintainer.js | 3 + lib/v2/cib/radar.js | 13 + lib/v2/cib/router.js | 3 + lib/v2/cib/whpj.js | 85 + lib/v2/civitai/discussions.js | 4 +- lib/v2/clash/maintainer.js | 3 - lib/v2/clash/premium.js | 23 - lib/v2/clash/radar.js | 13 - lib/v2/clash/router.js | 3 - lib/v2/cls/depth.js | 19 +- lib/v2/cls/hot.js | 2 +- lib/v2/cls/telegraph.js | 2 +- lib/v2/cma/channel.js | 87 + lib/v2/cma/maintainer.js | 3 + lib/v2/cma/radar.js | 18 + lib/v2/cma/router.js | 3 + lib/v2/cma/templates/description.art | 13 + lib/v2/cmpxchg8b/articles.js | 43 + lib/v2/cmpxchg8b/maintainer.js | 3 + lib/v2/cmpxchg8b/radar.js | 13 + lib/v2/cmpxchg8b/router.js | 3 + lib/v2/cna/index.js | 3 +- lib/v2/cna/maintainer.js | 1 + lib/v2/cna/radar.js | 6 + lib/v2/cna/router.js | 1 + lib/v2/cna/web/index.js | 56 + lib/v2/cnbc/rss.js | 2 +- lib/v2/cnbeta/type.js | 24 +- lib/v2/cnbeta/utils.js | 5 +- lib/v2/cneb/yjxw.js | 6 +- lib/v2/cneb/yjxx.js | 2 +- lib/v2/cnjxol/index.js | 6 +- lib/v2/cnljxh/index.js | 71 + lib/v2/cnljxh/maintainer.js | 7 + lib/v2/cnljxh/radar.js | 194 + lib/v2/cnljxh/router.js | 3 + lib/v2/cntheory/paper.js | 4 +- lib/v2/cntheory/templates/description.art | 2 +- lib/v2/cntv/column.js | 2 +- lib/v2/codeforces/contests.js | 4 +- .../{recent_actions.js => recent-actions.js} | 0 lib/v2/codeforces/router.js | 2 +- lib/v2/comicskingdom/index.js | 2 +- lib/v2/consumer/maintainer.js | 1 + lib/v2/consumer/radar.js | 6 + lib/v2/consumer/router.js | 1 + lib/v2/consumer/shopping-guide.js | 66 + lib/v2/cool18/index.js | 4 +- lib/v2/coolapk/dyh.js | 4 +- lib/v2/coolapk/hot.js | 2 +- lib/v2/coolapk/huati.js | 4 +- lib/v2/coolapk/router.js | 2 +- .../{userDynamic.js => user-dynamic.js} | 6 +- lib/v2/coolapk/utils.js | 14 +- lib/v2/coomer/utils.js | 2 +- lib/v2/copymanga/comic.js | 11 +- lib/v2/cpcey/index.js | 4 +- lib/v2/cqwu/index.js | 19 +- lib/v2/cqwu/radar.js | 4 +- lib/v2/crac/index.js | 2 +- lib/v2/creative-comic/book.js | 74 +- lib/v2/creative-comic/utils.js | 3 +- lib/v2/cs/index.js | 83 + lib/v2/cs/maintainer.js | 5 + lib/v2/cs/radar.js | 229 + lib/v2/cs/router.js | 13 + lib/v2/cs/video.js | 70 + lib/v2/csc/maintainer.js | 3 - lib/v2/csc/notice.js | 99 - lib/v2/csc/radar.js | 31 - lib/v2/csc/router.js | 3 - lib/v2/cscse/maintainer.js | 3 - lib/v2/cscse/radar.js | 13 - lib/v2/cscse/router.js | 3 - lib/v2/cscse/tzgg.js | 54 - lib/v2/cssn/iolaw.js | 2 +- lib/v2/cste/index.js | 2 +- lib/v2/cste/radar.js | 2 +- lib/v2/csu/utils.js | 2 +- lib/v2/cts/news.js | 2 +- lib/v2/curiouscat/radar.js | 2 +- lib/v2/curius/templates/description.art | 2 +- lib/v2/cw/utils.js | 6 +- lib/v2/cyzone/author.js | 6 +- lib/v2/cyzone/index.js | 6 +- lib/v2/cyzone/label.js | 6 +- lib/v2/cyzone/maintainer.js | 2 +- lib/v2/cyzone/util.js | 10 +- lib/v2/dahecube/index.js | 2 +- lib/v2/dahecube/radar.js | 46 +- lib/v2/daily/radar.js | 6 +- lib/v2/damai/activity.js | 44 + lib/v2/damai/maintainer.js | 3 + lib/v2/damai/radar.js | 13 + lib/v2/damai/router.js | 3 + lib/v2/damai/templates/activity.art | 5 + lib/v2/dayanzai/index.js | 32 +- lib/v2/dblp/publication.js | 7 +- lib/v2/dbmv/index.js | 64 - lib/v2/dbmv/maintainer.js | 3 - lib/v2/dbmv/radar.js | 13 - lib/v2/dcard/section.js | 2 +- lib/v2/dcard/utils.js | 10 +- lib/v2/dcfever/news.js | 2 +- lib/v2/dcfever/utils.js | 9 +- lib/v2/deadline/maintainer.js | 1 - lib/v2/deadline/posts.js | 4 +- lib/v2/deadline/radar.js | 2 +- lib/v2/dedao/knowledge.js | 2 +- lib/v2/dedao/list.js | 4 +- lib/v2/dedao/user.js | 4 +- lib/v2/deeplearning/maintainer.js | 3 + lib/v2/deeplearning/radar.js | 13 + lib/v2/deeplearning/router.js | 3 + lib/v2/deeplearning/thebatch.js | 36 + lib/v2/dgjyw/index.js | 2 +- lib/v2/dgjyw/router.js | 2 +- lib/v2/dhu/jiaowu/news.js | 6 +- lib/v2/dhu/xxgk/news.js | 4 +- lib/v2/diandong/ddh.js | 51 - lib/v2/diandong/maintainer.js | 1 - lib/v2/diandong/radar.js | 6 - lib/v2/diandong/router.js | 1 - lib/v2/diershoubing/news.js | 2 +- lib/v2/discord/channel.js | 2 +- lib/v2/discord/radar.js | 2 +- lib/v2/discourse/notifications.js | 4 +- lib/v2/discourse/utils.js | 2 +- lib/v2/discuz/discuz.js | 16 +- lib/v2/discuz/radar.js | 1 + lib/v2/disinformationindex/blog.js | 48 - lib/v2/disinformationindex/maintainer.js | 4 - lib/v2/disinformationindex/radar.js | 19 - lib/v2/disinformationindex/research.js | 45 - lib/v2/dlnews/category.js | 37 +- lib/v2/dlsite/campaign.js | 2 +- lib/v2/dlsite/ci-en/article.js | 4 +- lib/v2/dlsite/new.js | 4 +- lib/v2/dlsite/utils.js | 6 +- lib/v2/dn/news.js | 2 +- lib/v2/dnaindia/radar.js | 4 +- lib/v2/dockerhub/build.js | 2 +- lib/v2/dockerhub/radar.js | 4 +- lib/v2/dockerhub/tag.js | 2 +- lib/v2/dol/announce.js | 92 + lib/v2/dol/maintainer.js | 3 + lib/v2/dol/radar.js | 33 + lib/v2/dol/router.js | 3 + lib/v2/domp4/detail.js | 14 +- lib/v2/domp4/utils.js | 8 +- .../{player_news.js => player-news.js} | 0 lib/v2/dongqiudi/result.js | 2 +- lib/v2/dongqiudi/router.js | 6 +- lib/v2/dongqiudi/special.js | 34 +- .../dongqiudi/{team_news.js => team-news.js} | 0 lib/v2/dongqiudi/{top_news.js => top-news.js} | 2 +- lib/v2/dongqiudi/utils.js | 30 +- lib/v2/dorohedoro/news.js | 4 +- lib/v2/douban/channel/topic.js | 2 +- lib/v2/douban/maintainer.js | 6 +- lib/v2/douban/other/celebrity.js | 2 +- lib/v2/douban/other/classification.js | 22 +- lib/v2/douban/other/discussion.js | 58 + lib/v2/douban/other/doulist.js | 16 +- .../{explore_column.js => explore-column.js} | 2 +- lib/v2/douban/other/explore.js | 2 +- lib/v2/douban/other/group.js | 2 +- .../other/{latest_book.js => latest-book.js} | 0 .../{latest_music.js => latest-music.js} | 0 lib/v2/douban/other/list.js | 79 +- lib/v2/douban/other/playing.js | 26 +- lib/v2/douban/other/recommended.js | 70 + lib/v2/douban/other/replied.js | 4 +- lib/v2/douban/other/topic.js | 4 +- .../other/{weekly_best.js => weekly-best.js} | 0 lib/v2/douban/people/status.js | 102 +- lib/v2/douban/people/wish.js | 8 +- lib/v2/douban/radar.js | 10 +- lib/v2/douban/router.js | 12 +- lib/v2/douyin/hashtag.js | 8 +- lib/v2/douyin/live.js | 6 +- lib/v2/douyin/user.js | 8 +- lib/v2/douyin/utils.js | 22 +- lib/v2/dribbble/utils.js | 6 +- lib/v2/duozhuayu/search.js | 8 +- lib/v2/dushu/fuzhou/index.js | 2 +- lib/v2/dut/defaults.js | 4 + lib/v2/dut/index.js | 40 +- lib/v2/dut/maintainer.js | 15 +- lib/v2/dut/radar.js | 90 +- lib/v2/dut/router.js | 2 +- lib/v2/dut/shortcuts.js | 5 + lib/v2/dx2025/radar.js | 8 +- lib/v2/dxy/profile/thread.js | 12 +- lib/v2/dxy/special.js | 12 +- lib/v2/dxy/utils.js | 2 +- lib/v2/e-hentai/index.js | 2 +- lib/v2/eagle/blog.js | 6 +- lib/v2/eagle/changelog.js | 6 +- lib/v2/earthquake/ceic.js | 8 +- lib/v2/earthquake/index.js | 6 +- lib/v2/eastday/24.js | 8 +- lib/v2/eastday/find.js | 49 - lib/v2/eastday/maintainer.js | 1 - lib/v2/eastday/radar.js | 6 - lib/v2/eastday/router.js | 1 - lib/v2/eastmoney/report/index.js | 2 +- lib/v2/eastmoney/search/index.js | 2 +- lib/v2/economist/download.js | 30 - lib/v2/economist/espresso.js | 4 +- lib/v2/economist/full.js | 6 +- lib/v2/economist/global-business-review.js | 20 +- lib/v2/economist/gre-vocabulary.js | 19 - lib/v2/economist/maintainer.js | 2 - lib/v2/economist/radar.js | 8 - lib/v2/economist/router.js | 2 - lib/v2/ecust/e/news.js | 4 +- lib/v2/ecust/jwc/notice.js | 2 +- lib/v2/eet-china/maintainer.js | 4 - lib/v2/eet-china/mp/index.js | 10 - lib/v2/eet-china/mp/tags.js | 10 - lib/v2/eet-china/mp/util.js | 67 - lib/v2/eet-china/radar.js | 24 - lib/v2/eet-china/router.js | 4 - lib/v2/egsea/flash.js | 30 + lib/v2/egsea/maintainer.js | 3 + lib/v2/egsea/radar.js | 13 + lib/v2/egsea/router.js | 3 + lib/v2/ehentai/ehapi.js | 36 +- lib/v2/ehentai/favorites.js | 28 +- lib/v2/ehentai/search.js | 24 +- lib/v2/ehentai/tag.js | 24 +- lib/v2/ekantipur/issue.js | 64 + lib/v2/ekantipur/maintainer.js | 3 + lib/v2/ekantipur/radar.js | 13 + lib/v2/ekantipur/router.js | 3 + lib/v2/eleduck/posts.js | 4 +- lib/v2/embassy/index.js | 2 +- .../{supportedList.js => supported-list.js} | 0 lib/v2/ems/apple.js | 42 - lib/v2/ems/maintainer.js | 4 - lib/v2/ems/news.js | 21 - lib/v2/ems/radar.js | 19 - lib/v2/ems/router.js | 4 - lib/v2/ems/templates/apple.art | 6 - lib/v2/epicgames/index.js | 27 +- lib/v2/epicgames/templates/description.art | 1 + lib/v2/eprice/rss.js | 10 +- lib/v2/eventernote/actors.js | 4 +- lib/v2/ezone/index.js | 70 - lib/v2/ezone/radar.js | 13 - lib/v2/fansly/maintainer.js | 4 + lib/v2/fansly/post.js | 29 + lib/v2/fansly/radar.js | 19 + lib/v2/fansly/router.js | 4 + lib/v2/fansly/tag.js | 30 + lib/v2/fansly/templates/media.art | 8 + lib/v2/fansly/templates/poll.art | 4 + lib/v2/fansly/templates/tip-goal.art | 2 + lib/v2/fansly/utils.js | 176 + lib/v2/fantia/search.js | 17 +- lib/v2/fantia/user.js | 8 +- lib/v2/farmatters/index.js | 85 + lib/v2/farmatters/maintainer.js | 7 + lib/v2/farmatters/radar.js | 36 + lib/v2/farmatters/router.js | 6 + lib/v2/farmatters/templates/description.art | 9 + lib/v2/fastbull/express-news.js | 2 +- lib/v2/fastbull/news.js | 2 +- lib/v2/fda/cdrh.js | 6 +- .../ff14/{ff14_global.js => ff14-global.js} | 6 +- lib/v2/ff14/{ff14_zh.js => ff14-zh.js} | 0 lib/v2/ff14/router.js | 8 +- lib/v2/fffdm/manhua/manhua.js | 7 +- lib/v2/filmdeepfocus/index.js | 58 - lib/v2/filmdeepfocus/radar.js | 13 - lib/v2/filmdeepfocus/router.js | 3 - .../{mostViewed.js => most-viewed.js} | 0 lib/v2/finology/radar.js | 8 +- lib/v2/finology/router.js | 2 +- lib/v2/finviz/news.js | 8 +- lib/{routes => v2}/firefox/addons.js | 4 +- lib/v2/firefox/breaches.js | 38 + lib/v2/firefox/index.js | 74 - lib/v2/firefox/maintainer.js | 2 + lib/v2/firefox/radar.js | 17 + lib/v2/firefox/release.js | 36 + lib/v2/firefox/router.js | 4 +- lib/v2/fisher-spb/news.js | 3 +- lib/v2/fisher-spb/radar.js | 2 +- lib/v2/fjksbm/index.js | 4 +- lib/v2/flyert/utils.js | 5 +- lib/v2/followin/index.js | 6 +- lib/v2/followin/radar.js | 2 +- lib/v2/followin/tag.js | 4 +- lib/v2/followin/utils.js | 14 +- lib/v2/foresightnews/article.js | 2 +- lib/v2/foresightnews/column.js | 2 +- lib/v2/foresightnews/index.js | 2 +- lib/v2/foresightnews/maintainer.js | 1 - lib/v2/foresightnews/news.js | 2 +- lib/v2/foresightnews/util.js | 7 +- lib/v2/fortnite/news.js | 2 +- lib/v2/fortunechina/index.js | 2 +- lib/v2/fosshub/radar.js | 2 +- lib/v2/{pmthinking => free}/maintainer.js | 2 +- lib/v2/free/radar.js | 13 + lib/v2/{secnews => free}/router.js | 2 +- lib/v2/free/rss.js | 19 + lib/v2/freecomputerbooks/radar.js | 4 +- lib/v2/freewechat/profile.js | 6 +- lib/v2/ft/myft.js | 12 +- lib/v2/ft/radar.js | 2 +- lib/v2/ft/utils.js | 2 +- lib/v2/fuliba/latest.js | 25 + lib/v2/fuliba/maintainer.js | 3 + lib/v2/fuliba/radar.js | 15 + lib/v2/{meituclub => fuliba}/router.js | 2 +- lib/v2/furstar/archive.js | 2 +- lib/v2/furstar/artists.js | 2 +- lib/v2/furstar/index.js | 2 +- lib/v2/futunn/main.js | 4 +- lib/v2/fx-markets/channel.js | 2 +- lib/v2/fxiaoke/crm.js | 63 + lib/v2/fxiaoke/maintainer.js | 3 + lib/v2/fxiaoke/radar.js | 37 + lib/v2/fxiaoke/router.js | 3 + lib/v2/gamebase/news.js | 6 +- lib/v2/gamer/{gnn_index.js => gnn-index.js} | 22 +- lib/v2/gamer/router.js | 2 +- lib/v2/gamersecret/index.js | 2 +- lib/v2/gamme/category.js | 6 +- lib/v2/gamme/tag.js | 8 +- lib/v2/gaze/maintainer.js | 3 - lib/v2/gaze/radar.js | 13 - lib/v2/gaze/router.js | 3 - lib/v2/gaze/templates/update.art | 4 - lib/v2/gaze/update.js | 27 - lib/v2/gcores/category.js | 37 +- lib/v2/gcores/collection.js | 28 +- lib/v2/gcores/radio.js | 19 +- lib/v2/gdsrx/index.js | 72 + lib/v2/gdsrx/maintainer.js | 3 + lib/v2/gdsrx/radar.js | 61 + lib/v2/gdsrx/router.js | 3 + lib/v2/gdut/news.js | 5 +- lib/v2/gdut/{oa_news.js => oa-news.js} | 82 +- lib/v2/gdut/router.js | 2 +- lib/v2/getitfree/index.js | 76 +- lib/v2/getitfree/maintainer.js | 4 +- lib/v2/getitfree/radar.js | 21 +- lib/v2/getitfree/router.js | 12 +- lib/v2/getitfree/util.js | 326 + lib/v2/gf-cn/news.js | 2 +- lib/v2/gitee/repos/commits.js | 2 +- lib/v2/gitee/repos/events.js | 2 +- lib/v2/gitee/repos/releases.js | 2 +- lib/v2/gitee/users/events.js | 2 +- lib/v2/github/comments.js | 34 +- lib/v2/github/contributors.js | 65 +- lib/v2/github/follower.js | 2 +- lib/v2/github/issue.js | 2 +- lib/v2/github/notifications.js | 10 +- lib/v2/github/pulls.js | 2 +- lib/v2/github/radar.js | 24 +- lib/v2/github/router.js | 2 +- lib/v2/github/star.js | 2 +- .../{starred_repos.js => starred-repos.js} | 2 +- lib/v2/github/topic.js | 36 +- lib/v2/github/trending.js | 2 +- lib/v2/gitpod/blog.js | 2 +- lib/v2/globallawreview/index.js | 2 +- lib/v2/gocn/jobs.js | 23 +- lib/v2/gocn/maintainer.js | 4 +- lib/v2/gocn/news.js | 31 + lib/v2/gocn/radar.js | 10 +- lib/v2/gocn/router.js | 4 +- lib/v2/gocn/topics.js | 22 +- lib/v2/gocn/util.js | 17 - lib/v2/gocn/utils.js | 38 + lib/v2/gofans/index.js | 41 + lib/v2/gofans/maintainer.js | 3 + lib/v2/gofans/radar.js | 13 + lib/v2/gofans/router.js | 3 + lib/v2/gofans/templates/description.art | 7 + lib/v2/gogoanimehd/maintainer.js | 3 + lib/v2/gogoanimehd/radar.js | 13 + lib/v2/gogoanimehd/recent-releases.js | 37 + lib/v2/gogoanimehd/router.js | 3 + lib/v2/good/index.js | 53 - lib/v2/good/radar.js | 13 - lib/v2/good/router.js | 3 - lib/v2/google/album.js | 2 +- lib/v2/google/alerts.js | 33 + lib/v2/google/citations.js | 9 +- lib/v2/google/fonts.js | 2 +- lib/v2/google/maintainer.js | 4 +- lib/v2/google/radar.js | 20 +- lib/v2/google/router.js | 4 +- lib/v2/google/scholar.js | 2 +- lib/v2/google/search.js | 62 + lib/v2/google/sites.js | 75 - lib/v2/google/sitesRecentChanges.js | 51 - lib/v2/google/templates/description.art | 6 + lib/v2/gov/beijing/bjedu/gh.js | 48 + lib/v2/gov/beijing/bphc/index.js | 2 +- lib/v2/gov/beijing/jw/tzgg.js | 2 +- lib/v2/gov/beijing/kw/index.js | 6 +- lib/v2/gov/caac/cjwt.js | 56 + lib/v2/gov/caac/templates/description.art | 25 + lib/v2/gov/cac/index.js | 4 +- lib/v2/gov/ccdi/index.js | 2 +- lib/v2/gov/ccdi/utils.js | 10 +- lib/v2/gov/chinamine-safety/util.js | 77 + lib/v2/gov/chinamine-safety/xw.js | 37 + lib/v2/gov/chinamine-safety/zfxxgk.js | 36 + lib/v2/gov/chinatax/latest.js | 8 +- lib/v2/gov/chongqing/gzw.js | 65 + lib/v2/gov/chongqing/sydwgkzp.js | 47 + lib/v2/gov/cmse/index.js | 4 +- lib/v2/gov/cnnic/index.js | 4 +- lib/v2/gov/forestry/gjlckjdjt.js | 96 + lib/v2/gov/forestry/templates/description.art | 15 + lib/v2/gov/general/general.js | 52 +- lib/v2/gov/huizhou/zwgk/index.js | 2 +- lib/v2/gov/jgjcndrc/index.js | 59 + ..._exam_notice.js => medical-exam-notice.js} | 0 lib/v2/gov/maintainer.js | 32 +- lib/v2/gov/maoming/maoming.js | 76 +- lib/v2/gov/mee/ywdt.js | 4 +- lib/v2/gov/mem/sgcc.js | 64 + lib/v2/gov/mfa/wjdt.js | 2 +- lib/v2/gov/miit/wjfb.js | 4 +- lib/v2/gov/miit/wjgs.js | 2 +- lib/v2/gov/miit/yjzj.js | 4 +- lib/v2/gov/miit/zcjd.js | 2 +- lib/v2/gov/miit/zcwj.js | 4 +- lib/{routes => v2}/gov/moa/moa.js | 14 +- lib/v2/gov/moa/zdscxx.js | 102 + lib/v2/gov/moe/moe.js | 50 +- lib/v2/gov/mof/bond.js | 51 + lib/v2/gov/mofcom/article.js | 14 +- lib/v2/gov/moj/lfyjzj.js | 53 + lib/v2/gov/mot/index.js | 66 + lib/v2/gov/ndrc/fggz.js | 62 + lib/{routes => v2}/gov/ndrc/xwdt.js | 4 +- lib/v2/gov/nea/ghs.js | 58 + lib/v2/gov/news/index.js | 96 +- lib/v2/gov/nifdc/index.js | 73 +- lib/v2/gov/nopss/index.js | 2 +- lib/v2/gov/npc/index.js | 47 + lib/v2/gov/nrta/dsj.js | 2 +- lib/v2/gov/nrta/news.js | 4 +- lib/v2/gov/nsfc/index.js | 6 +- lib/v2/gov/pbc/gzlw.js | 2 +- ...eAnnouncement.js => trade-announcement.js} | 0 lib/v2/gov/radar.js | 1378 +++- lib/v2/gov/router.js | 39 +- lib/v2/gov/safe/business.js | 8 + lib/v2/gov/safe/complaint.js | 8 + lib/v2/gov/safe/templates/message.art | 25 + lib/v2/gov/safe/util.js | 87 + lib/v2/gov/samr/templates/description.art | 26 + lib/v2/gov/samr/xgzlyhd.js | 100 + lib/v2/gov/shanghai/wgj/wgj.js | 2 +- lib/v2/gov/shanghai/yjj/index.js | 4 +- lib/v2/gov/shenzhen/hrss/szksy/index.js | 2 +- lib/v2/gov/shenzhen/xxgk/zfxxgj.js | 2 +- lib/v2/gov/shenzhen/zjj/index.js | 2 +- lib/v2/gov/shenzhen/zzb/index.js | 2 +- lib/v2/gov/sichuan/deyang/govpublicinfo.js | 2 +- lib/v2/gov/stats/index.js | 4 +- lib/v2/gov/suzhou/doc.js | 46 + lib/v2/gov/suzhou/fg.js | 62 + lib/v2/gov/suzhou/news.js | 116 + lib/v2/gov/taiyuan/rsj.js | 2 +- lib/v2/gov/xuzhou/hrss.js | 2 +- lib/v2/gov/zhejiang/gwy.js | 84 + lib/v2/gov/zhengce/govall.js | 4 +- lib/v2/gov/zhengce/index.js | 99 + lib/v2/gov/zhengce/wenjian.js | 2 +- lib/v2/gov/zhengce/zuixin.js | 39 - lib/v2/gq/maintainer.js | 3 - lib/v2/gq/radar.js | 13 - lib/v2/gq/router.js | 3 - lib/v2/gq/templates/embed-article.art | 1 - lib/v2/gq/templates/embed-product.art | 4 - lib/v2/gq/templates/img.art | 6 - lib/v2/gq/templates/tw.art | 3 - lib/v2/gq/templates/videoObject.art | 6 - lib/v2/gq/templates/youtube.art | 3 - lib/v2/gq/tw/index.js | 165 - lib/v2/greasyfork/maintainer.js | 1 + lib/v2/greasyfork/router.js | 1 + lib/v2/greasyfork/scripts.js | 45 +- lib/v2/grist/featured.js | 2 +- lib/v2/grist/radar.js | 8 +- lib/{routes => v2}/grubstreet/index.js | 0 lib/v2/grubstreet/maintainer.js | 3 + lib/v2/{yaohuo => grubstreet}/radar.js | 10 +- lib/v2/{xwlb => grubstreet}/router.js | 1 - lib/v2/grubstreet/utils.js | 84 + lib/v2/guancha/index.js | 24 +- lib/v2/guancha/member.js | 8 +- lib/v2/guancha/personalpage.js | 8 +- lib/v2/guandian/index.js | 37 - lib/v2/guandian/maintainer.js | 3 - lib/v2/guandian/radar.js | 13 - lib/v2/guandian/router.js | 3 - lib/v2/guangdiu/index.js | 9 +- lib/v2/guangdiu/maintainer.js | 1 + lib/v2/guangdiu/radar.js | 10 +- lib/v2/guangdiu/router.js | 1 + lib/v2/guangdiu/search.js | 40 + lib/v2/guangzhoumetro/news.js | 2 +- lib/v2/guanhai/index.js | 33 +- lib/v2/guanhai/maintainer.js | 1 - lib/v2/guggenheim/exhibitions.js | 29 - lib/v2/guggenheim/maintainer.js | 3 - lib/v2/guggenheim/radar.js | 11 - lib/v2/guggenheim/router.js | 3 - lib/v2/gumroad/index.js | 2 +- lib/v2/guokr/channel.js | 4 +- lib/v2/gxmzu/lib.js | 70 + lib/v2/gxmzu/maintainer.js | 1 + lib/v2/gxmzu/radar.js | 8 + lib/v2/gxmzu/router.js | 1 + lib/v2/gxmzu/utils/index.js | 8 +- lib/v2/gz-cmc/index.js | 137 - lib/v2/gz-cmc/maintainer.js | 3 - lib/v2/gz-cmc/radar.js | 12 - lib/v2/gz-cmc/router.js | 3 - lib/v2/gz-cmc/templates/description.art | 3 - lib/v2/gzh360/_README | 1 - lib/v2/gzh360/category.js | 8 - lib/v2/gzh360/gzh.js | 18 - lib/v2/gzh360/maintainer.js | 4 - lib/v2/gzh360/radar.js | 19 - lib/v2/gzh360/router.js | 4 - lib/v2/gzh360/universal.js | 53 - lib/v2/gzh360/utils.js | 62 - lib/v2/hackernews/index.js | 2 +- lib/v2/hacking8/search.js | 2 +- lib/v2/hackyournews/index.js | 50 + lib/v2/{yaohuo => hackyournews}/maintainer.js | 2 +- lib/v2/hackyournews/radar.js | 13 + lib/v2/{pmthinking => hackyournews}/router.js | 2 +- lib/v2/hakkatv/type.js | 55 +- lib/v2/hameln/chapter.js | 2 +- .../harvard/health/blog.js | 15 +- lib/v2/harvard/maintainer.js | 3 + lib/v2/harvard/radar.js | 13 + lib/v2/harvard/router.js | 3 + lib/v2/hdu/cs/notice.js | 2 +- lib/v2/hdu/cs/pg.js | 2 +- lib/v2/hebtv/maintainer.js | 3 + lib/v2/hebtv/nong-bo-shi-zai-xing-dong.js | 109 + lib/v2/hebtv/radar.js | 13 + lib/v2/hebtv/router.js | 3 + lib/v2/hebtv/templates/description.art | 24 + lib/v2/hellobtc/kepu.js | 2 +- lib/v2/hellobtc/maintainer.js | 1 - lib/v2/hellobtc/news.js | 2 +- lib/v2/hellobtc/radar.js | 6 - lib/v2/hellobtc/router.js | 1 - lib/v2/hellobtc/topic.js | 43 - lib/v2/hellogithub/index.js | 2 +- lib/v2/{watchout => hicairo}/maintainer.js | 2 +- lib/v2/hicairo/radar.js | 13 + lib/v2/hicairo/router.js | 3 + lib/v2/hicairo/rss.js | 32 + lib/v2/hinatazaka46/news.js | 2 +- lib/v2/hit/jwc.js | 8 +- lib/v2/hit/today.js | 8 +- lib/v2/hitwh/today.js | 22 +- lib/v2/hizu/index.js | 2 +- lib/v2/hjedd/article.js | 27 - lib/v2/hjedd/hot.js | 14 - lib/v2/hjedd/latest.js | 14 - lib/v2/hjedd/maintainer.js | 10 - lib/v2/hjedd/news.js | 14 - lib/v2/hjedd/notice.js | 14 - lib/v2/hjedd/original.js | 14 - lib/v2/hjedd/radar.js | 55 - lib/v2/hjedd/router.js | 10 - lib/v2/hjedd/templates/attachment.art | 7 - lib/v2/hjedd/top.js | 14 - lib/v2/hjedd/utils.js | 111 - lib/v2/hk01/utils.js | 2 +- lib/v2/hket/index.js | 145 +- lib/v2/hket/router.js | 2 +- lib/v2/hket/templates/description.art | 1 - lib/v2/hket/templates/image.art | 5 +- lib/v2/hnrb/index.js | 4 +- lib/v2/hongkong/chp.js | 6 +- lib/v2/hostmonit/cloudflareyes.js | 91 + lib/v2/hostmonit/maintainer.js | 3 + lib/v2/hostmonit/radar.js | 17 + lib/v2/hostmonit/router.js | 8 + lib/v2/hostmonit/templates/description.art | 28 + lib/v2/hostmonit/templates/title.art | 1 + lib/v2/hotchina/index.js | 55 - lib/v2/hotchina/maintainer.js | 5 - lib/v2/hotchina/radar.js | 25 - lib/v2/hotchina/router.js | 3 - lib/v2/houxu/events.js | 2 +- lib/v2/houxu/index.js | 2 +- lib/v2/howtoforge/maintainer.js | 3 + lib/v2/howtoforge/radar.js | 13 + lib/v2/howtoforge/router.js | 3 + lib/v2/howtoforge/rss.js | 32 + lib/v2/hoyolab/constant.js | 37 + lib/v2/hoyolab/maintainer.js | 3 + lib/v2/hoyolab/news.js | 96 + lib/v2/hoyolab/radar.js | 22 + lib/v2/hoyolab/router.js | 3 + lib/v2/hoyolab/templates/post.art | 7 + lib/v2/hoyolab/utils.js | 32 + lib/v2/hpoi/all.js | 3 + lib/v2/hpoi/banner-item.js | 26 + lib/v2/hpoi/character.js | 3 + lib/{routes => v2}/hpoi/info.js | 0 lib/v2/hpoi/maintainer.js | 8 + lib/v2/hpoi/radar.js | 37 + lib/v2/hpoi/router.js | 8 + lib/v2/hpoi/user.js | 43 + lib/v2/hpoi/utils.js | 49 + lib/v2/hpoi/work.js | 3 + lib/v2/hrbeu/gx/card.js | 9 +- lib/v2/hrbeu/gx/list.js | 9 +- lib/v2/hrbeu/uae/news.js | 4 +- lib/v2/hrbeu/yjsy/list.js | 2 +- lib/v2/hrbust/utils.js | 13 +- lib/v2/huangz/index.js | 29 - lib/v2/huangz/maintainer.js | 3 - lib/v2/huangz/radar.js | 13 - lib/v2/huangz/router.js | 3 - lib/v2/huanqiu/index.js | 26 +- lib/v2/huggingface/blog-zh.js | 34 + lib/v2/huggingface/daily-papers.js | 17 +- lib/v2/huggingface/maintainer.js | 1 + lib/v2/huggingface/radar.js | 6 + lib/v2/huggingface/router.js | 1 + .../{categoryTitle.js => category-title.js} | 0 lib/v2/hunau/utils/common.js | 6 +- .../utils/{indexPage.js => index-page.js} | 2 +- .../utils/{newsContent.js => news-content.js} | 6 +- lib/v2/hupu/all.js | 2 +- lib/v2/hupu/bbs.js | 2 +- lib/v2/hupu/index.js | 2 +- lib/v2/huxiu/article.js | 33 - lib/v2/huxiu/author.js | 36 - lib/v2/huxiu/brief-column.js | 29 + lib/v2/huxiu/briefColumn.js | 41 - lib/v2/huxiu/channel.js | 28 + lib/v2/huxiu/club.js | 30 + lib/v2/huxiu/collection.js | 40 +- lib/v2/huxiu/maintainer.js | 13 +- lib/v2/huxiu/member.js | 27 + lib/v2/huxiu/moment.js | 36 +- lib/v2/huxiu/radar.js | 40 +- lib/v2/huxiu/router.js | 11 +- lib/v2/huxiu/search.js | 37 +- lib/v2/huxiu/tag.js | 38 +- lib/v2/huxiu/templates/brief.art | 22 - lib/v2/huxiu/templates/description.art | 46 + lib/v2/huxiu/templates/img.art | 3 - lib/v2/huxiu/templates/moment.art | 16 - lib/v2/huxiu/templates/video.art | 7 - lib/v2/huxiu/util.js | 456 ++ lib/v2/huxiu/utils.js | 154 - lib/v2/hyqss/index.js | 89 - lib/v2/hyqss/maintainer.js | 4 - lib/v2/hyqss/radar.js | 22 - lib/v2/hyqss/router.js | 3 - lib/v2/i-cable/category.js | 74 - lib/v2/i-cable/maintainer.js | 3 - lib/v2/i-cable/radar.js | 13 - lib/v2/i-cable/router.js | 3 - lib/v2/i-cable/templates/desc.art | 8 - lib/v2/ianspriggs/index.js | 90 + lib/v2/ianspriggs/maintainer.js | 3 + lib/v2/ianspriggs/radar.js | 17 + lib/v2/ianspriggs/router.js | 3 + lib/v2/ianspriggs/templates/description.art | 13 + lib/v2/ibc/maintainer.js | 4 - lib/v2/ibc/maitta.js | 32 - lib/v2/ibc/radar.js | 19 - lib/v2/ibc/radio.js | 73 - lib/v2/ibc/router.js | 4 - lib/v2/ibc/templates/description.art | 6 - lib/v2/icbc/radar.js | 2 +- lib/v2/icbc/whpj.js | 2 +- lib/v2/ieee/earlyaccess.js | 4 +- lib/v2/ieee/journal.js | 4 +- lib/v2/ieee/recent.js | 4 +- lib/v2/ielts/index.js | 2 +- lib/v2/ifeng/feng.js | 16 +- lib/v2/ifeng/news.js | 6 +- lib/v2/ifeng/utils.js | 6 +- lib/v2/ifi-audio/maintainer.js | 2 +- lib/v2/iguoguo/index.js | 8 +- .../cn/{family_offers.js => family-offers.js} | 8 +- lib/v2/ikea/cn/{low_price.js => low-price.js} | 2 +- lib/v2/ikea/cn/new.js | 4 +- lib/v2/ikea/cn/utils.js | 2 +- lib/v2/ikea/gb/offer.js | 12 +- lib/v2/ikea/router.js | 4 +- lib/v2/imagemagick/changelog.js | 2 +- lib/v2/imiker/jinghua.js | 87 + lib/v2/imiker/maintainer.js | 3 + lib/v2/imiker/radar.js | 13 + lib/v2/imiker/router.js | 3 + lib/v2/imiker/templates/description.art | 32 + lib/v2/independent/maintainer.js | 3 - lib/v2/independent/ps5-stock-uk.js | 27 - lib/v2/independent/radar.js | 13 - lib/v2/independent/router.js | 3 - lib/v2/indiansinkuwait/radar.js | 2 +- lib/v2/indienova/article.js | 24 + lib/v2/indienova/column.js | 20 + lib/v2/indienova/gamedb.js | 55 + lib/v2/indienova/maintainer.js | 6 + lib/v2/indienova/radar.js | 37 + lib/v2/indienova/router.js | 6 + lib/v2/indienova/usergames.js | 42 + lib/v2/indienova/utils.js | 43 + lib/v2/infoq/topic.js | 2 +- lib/v2/infoq/utils.js | 2 +- lib/v2/inoreader/maintainer.js | 2 +- lib/v2/instagram/common-utils.js | 17 +- lib/v2/instagram/private-api/index.js | 18 +- lib/v2/instagram/private-api/utils.js | 4 +- lib/v2/instagram/radar.js | 4 +- lib/v2/instagram/templates/images.art | 11 +- lib/v2/instagram/templates/video.art | 5 +- lib/v2/instagram/web-api/index.js | 44 +- lib/v2/instagram/web-api/utils.js | 176 +- lib/v2/instructables/projects.js | 2 +- lib/v2/iqilu/maintainer.js | 3 + lib/v2/iqilu/program.js | 98 + lib/v2/iqilu/radar.js | 107 + lib/v2/iqilu/router.js | 3 + lib/v2/iqilu/templates/description.art | 24 + lib/v2/iqiyi/album.js | 2 +- lib/v2/iqiyi/video.js | 2 +- lib/v2/iqnew/latest.js | 2 +- lib/v2/iqnew/radar.js | 2 +- lib/v2/iresearch/report.js | 2 +- lib/v2/iresearch/weekly.js | 6 +- lib/v2/islander/maintainer.js | 4 - lib/v2/islander/radar.js | 19 - lib/v2/islander/router.js | 4 - lib/v2/islander/search.js | 33 - lib/v2/islander/top30event.js | 28 - lib/v2/itch/devlog.js | 2 +- lib/v2/itch/router.js | 2 +- lib/v2/ithome/index.js | 2 +- lib/v2/ithome/ranking.js | 2 +- lib/v2/ithome/tag.js | 2 +- lib/v2/iwara/index.js | 2 +- lib/v2/iwara/subscriptions.js | 11 +- lib/v2/ixigua/router.js | 2 +- lib/v2/ixigua/{userVideo.js => user-video.js} | 5 +- lib/v2/jandan/section.js | 2 +- lib/v2/japanpost/radar.js | 2 +- lib/v2/japanpost/track.js | 6 +- lib/v2/japanpost/utils.js | 6 +- lib/v2/javbus/index.js | 32 +- lib/v2/javdb/utils.js | 6 +- lib/v2/javlibrary/maintainer.js | 1 + lib/v2/javlibrary/maker.js | 10 + lib/v2/javlibrary/router.js | 1 + lib/v2/javlibrary/utils.js | 13 +- lib/v2/jewishmuseum/radar.js | 2 +- lib/{routes => v2}/jianshu/collection.js | 0 lib/{routes => v2}/jianshu/home.js | 0 lib/v2/jianshu/maintainer.js | 5 + lib/v2/jianshu/radar.js | 25 + lib/v2/jianshu/router.js | 5 + lib/{routes => v2}/jianshu/user.js | 0 lib/{routes => v2}/jianshu/utils.js | 44 +- lib/v2/jiaoliudao/index.js | 27 +- lib/v2/jiemian/list.js | 60 - lib/v2/jiemian/lists.js | 100 + lib/v2/jiemian/maintainer.js | 7 +- lib/v2/jiemian/radar.js | 628 +- lib/v2/jiemian/router.js | 9 +- lib/v2/jiemian/templates/description.art | 39 + lib/v2/jike/router.js | 2 +- lib/v2/jike/{topicText.js => topic-text.js} | 2 +- lib/v2/jike/topic.js | 9 +- lib/v2/jike/user.js | 40 +- lib/v2/jike/utils.js | 8 +- lib/v2/jinse/catalogue.js | 101 + lib/v2/jinse/lives.js | 87 + lib/v2/jinse/maintainer.js | 5 + lib/v2/jinse/radar.js | 187 + lib/v2/jinse/router.js | 5 + lib/v2/jinse/templates/description.art | 34 + lib/v2/jinse/timeline.js | 96 + lib/v2/jisilu/index.js | 4 +- lib/v2/jjwxc/author.js | 68 + lib/v2/jjwxc/book.js | 106 + lib/v2/jjwxc/maintainer.js | 4 + lib/v2/jjwxc/radar.js | 29 + lib/v2/jjwxc/router.js | 4 + lib/v2/jjwxc/templates/author.art | 28 + lib/v2/jjwxc/templates/book.art | 44 + lib/v2/jornada/index.js | 10 +- lib/v2/jou/utils/index.js | 4 +- lib/v2/jsu/cxzx.js | 75 + lib/v2/jsu/jwc.js | 59 + lib/v2/jsu/maintainer.js | 7 + lib/v2/jsu/math.js | 41 + lib/v2/jsu/radar.js | 36 + lib/v2/jsu/rjxy.js | 43 + lib/v2/jsu/router.js | 8 + lib/v2/jsu/universityindex.js | 48 + lib/v2/jsu/utils/index.js | 52 + lib/v2/juejin/favorites.js | 6 +- lib/v2/juejin/maintainer.js | 2 - lib/v2/juejin/news.js | 25 - lib/v2/juejin/pins.js | 2 +- lib/v2/juejin/radar.js | 14 +- lib/v2/juejin/router.js | 2 - lib/v2/juejin/shares.js | 26 - lib/v2/juejin/utils.js | 2 +- lib/v2/jump/discount.js | 2 +- lib/v2/kakuyomu/episode.js | 54 - lib/v2/kakuyomu/maintainer.js | 3 - lib/v2/kakuyomu/radar.js | 13 - lib/v2/kakuyomu/router.js | 1 - lib/v2/kamen-rider-official/news.js | 16 +- lib/v2/kantarworldpanel/index.js | 2 +- lib/v2/kcna/news.js | 5 +- lib/v2/kcna/utils.js | 4 +- lib/v2/ke/results.js | 4 +- lib/v2/keep/user.js | 10 +- lib/v2/keepass/maintainer.js | 3 + lib/v2/keepass/news.js | 46 + lib/v2/keepass/radar.js | 13 + lib/v2/keepass/router.js | 3 + lib/v2/kemono/index.js | 38 +- lib/v2/kemono/radar.js | 2 +- lib/v2/kepu/live.js | 89 + lib/v2/kepu/maintainer.js | 3 + lib/v2/kepu/radar.js | 13 + lib/v2/kepu/router.js | 3 + lib/v2/kepu/templates/description.art | 33 + lib/v2/keylol/index.js | 10 +- lib/v2/knowmedia/index.js | 38 - lib/v2/knowmedia/maintainer.js | 3 - lib/v2/knowmedia/radar.js | 31 - lib/v2/knowmedia/router.js | 3 - lib/v2/knowmedia/templates/desc.art | 3 - lib/v2/konghq/blog-posts.js | 52 + lib/v2/konghq/maintainer.js | 3 + lib/v2/konghq/radar.js | 13 + lib/v2/konghq/router.js | 3 + lib/v2/kuaidi100/index.js | 19 +- lib/v2/kuaidi100/router.js | 2 +- ...ported_company.js => supported-company.js} | 0 lib/v2/kuaidi100/utils.js | 49 +- lib/v2/kunchengblog/essay.js | 10 +- lib/v2/kuwaitlocal/index.js | 21 +- lib/v2/kuwaitlocal/radar.js | 4 +- lib/v2/kyodonews/index.js | 6 +- lib/v2/laimanhua/index.js | 41 + lib/v2/laimanhua/maintainer.js | 3 + lib/v2/laimanhua/radar.js | 13 + lib/v2/{lativ => laimanhua}/router.js | 2 +- lib/v2/lala/maintainer.js | 3 + lib/v2/lala/radar.js | 13 + lib/v2/lala/router.js | 3 + lib/v2/lala/rss.js | 32 + lib/v2/lanqiao/questions.js | 32 +- lib/v2/laohu8/personal.js | 2 +- lib/v2/latepost/index.js | 27 +- lib/v2/lativ/index.js | 42 - lib/v2/lativ/maintainer.js | 3 - lib/v2/lativ/radar.js | 13 - lib/v2/lativ/templates/detail.art | 9 - lib/v2/layer3/maintainer.js | 3 - lib/v2/layer3/quests.js | 37 - lib/v2/layer3/radar.js | 13 - lib/v2/layer3/router.js | 3 - lib/v2/layer3/templates/description.art | 24 - lib/v2/layoffs/index.js | 10 +- lib/v2/layoffs/radar.js | 2 +- lib/v2/leetcode/articles.js | 24 +- lib/v2/leetcode/dailyquestion-solution-cn.js | 11 +- lib/v2/leetcode/dailyquestion-solution-en.js | 19 +- lib/v2/leetcode/maintainer.js | 2 +- lib/v2/leetcode/radar.js | 20 - lib/v2/leiphone/utils.js | 4 +- lib/v2/lever/index.js | 28 - lib/v2/lever/maintainer.js | 3 - lib/v2/lever/radar.js | 13 - lib/v2/lever/router.js | 3 - lib/v2/lfsyd/{old_home.js => old-home.js} | 0 lib/v2/lfsyd/router.js | 2 +- lib/v2/lfsyd/utils.js | 4 +- lib/v2/lianxh/index.js | 61 - lib/v2/lianxh/radar.js | 13 - lib/v2/lianxh/router.js | 3 - lib/v2/lifeweek/channel.js | 25 + lib/v2/lifeweek/maintainer.js | 4 + lib/v2/lifeweek/radar.js | 19 + lib/v2/lifeweek/router.js | 4 + lib/v2/lifeweek/tag.js | 24 + lib/v2/lifeweek/utils.js | 18 + lib/v2/lightnovel/light-novel.js | 62 + lib/v2/lightnovel/maintainer.js | 3 + lib/v2/lightnovel/radar.js | 13 + lib/v2/lightnovel/router.js | 3 + lib/v2/line/publisher.js | 2 +- lib/v2/line/radar.js | 2 +- lib/v2/linkedin/cn/renderer.js | 78 +- lib/v2/linkedin/cn/utils.js | 4 +- lib/v2/linkedin/radar.js | 2 +- lib/v2/linkresearcher/index.js | 10 +- lib/v2/liquipedia/cs-matches.js | 46 + lib/v2/liquipedia/dota2-matches.js | 47 + lib/v2/liquipedia/maintainer.js | 4 + lib/v2/liquipedia/radar.js | 19 + lib/v2/liquipedia/router.js | 4 + lib/v2/literotica/new.js | 2 +- lib/v2/liulinblog/index.js | 6 +- lib/v2/liulinblog/radar.js | 12 +- lib/v2/liveuamap/index.js | 41 + lib/v2/liveuamap/maintainer.js | 3 + lib/v2/liveuamap/radar.js | 13 + lib/v2/liveuamap/router.js | 3 + lib/v2/lkong/query.js | 6 +- lib/v2/lkong/thread.js | 2 +- lib/v2/lofter/tag.js | 103 +- lib/v2/lofter/user.js | 6 +- lib/v2/logclub/index.js | 133 + lib/v2/logclub/maintainer.js | 9 + lib/v2/logclub/radar.js | 139 + lib/v2/logclub/report.js | 98 + lib/v2/logclub/router.js | 5 + lib/v2/logclub/templates/description.art | 33 + lib/v2/logonews/index.js | 4 +- lib/v2/logonews/router.js | 2 +- lib/v2/lovelive-anime/topics.js | 7 +- lib/v2/lsnu/radar.js | 2 +- lib/v2/luogu/contest.js | 2 +- lib/v2/luogu/daily.js | 15 +- lib/v2/luogu/router.js | 4 +- lib/v2/luogu/{userBlog.js => user-blog.js} | 0 lib/v2/luogu/{userFeed.js => user-feed.js} | 0 lib/v2/lvv2/news.js | 29 +- lib/v2/lvv2/radar.js | 32 +- lib/v2/lvv2/top.js | 27 +- lib/v2/lxixsxa/discography.js | 8 +- lib/v2/lxixsxa/information.js | 4 +- .../jsonp-helper.js} | 6 +- lib/v2/lxixsxa/radar.js | 8 +- lib/v2/m4/index.js | 82 + lib/v2/{wp-china => m4}/maintainer.js | 1 + lib/v2/m4/radar.js | 87 + lib/v2/m4/router.js | 3 + lib/v2/m4/templates/description.art | 21 + lib/v2/macfilos/blog.js | 2 +- lib/v2/macmenubar/recently.js | 2 +- lib/v2/magazinelib/latest-magazine.js | 8 +- lib/v2/magazinelib/maintainer.js | 2 +- lib/v2/mail/imap.js | 16 +- lib/v2/mangadex/index.js | 2 +- lib/v2/manhuagui/comic.js | 10 +- lib/v2/manhuagui/subscribe.js | 2 +- .../mastodon/{account_id.js => account-id.js} | 0 lib/v2/mastodon/router.js | 6 +- .../{timeline_local.js => timeline-local.js} | 0 ...{timeline_remote.js => timeline-remote.js} | 0 lib/v2/mastodon/utils.js | 64 +- lib/v2/mcachicago/exhibitions.js | 17 - lib/v2/mcachicago/maintainer.js | 3 - lib/v2/mcachicago/radar.js | 11 - lib/v2/mcachicago/router.js | 3 - .../cn/{categoryMap.js => category-map.js} | 0 lib/v2/mckinsey/cn/index.js | 4 +- lib/v2/mckinsey/radar.js | 2 +- lib/v2/mclaren/index.js | 96 - lib/v2/mclaren/maintainer.js | 3 - lib/v2/mclaren/radar.js | 29 - lib/v2/mclaren/router.js | 3 - lib/v2/mclaren/templates/desc.art | 6 - lib/v2/mdpi/journal.js | 2 +- lib/v2/medieval-china/maintainer.js | 3 + lib/v2/medieval-china/post.js | 44 + lib/v2/medieval-china/radar.js | 13 + lib/v2/medieval-china/router.js | 3 + lib/v2/medium/following.js | 4 +- lib/v2/medium/for-you.js | 4 +- lib/v2/medium/list.js | 4 +- lib/v2/medium/parse-article.js | 8 +- lib/v2/medium/tag.js | 4 +- lib/v2/medsci/index.js | 2 +- lib/v2/meituclub/latest.js | 41 - lib/v2/meituclub/maintainer.js | 3 - lib/v2/meituclub/radar.js | 13 - lib/v2/meituclub/templates/description.art | 1 - lib/v2/metacritic/index.js | 121 + lib/v2/metacritic/maintainer.js | 5 + lib/v2/metacritic/radar.js | 43 + lib/{routes => v2}/metacritic/release.js | 0 lib/v2/metacritic/router.js | 3 + lib/v2/metacritic/templates/description.art | 19 + lib/v2/metacritic/util.js | 42 + lib/v2/meteor/index.js | 8 +- lib/v2/meteor/utils.js | 10 +- lib/v2/metmuseum/exhibitions.js | 2 +- lib/v2/metmuseum/radar.js | 2 +- lib/v2/mihoyo/bbs/cache.js | 40 + lib/v2/mihoyo/bbs/follow-list.js | 50 + lib/v2/mihoyo/bbs/img-ranking.js | 29 +- lib/v2/mihoyo/bbs/official.js | 23 +- lib/v2/mihoyo/bbs/timeline.js | 41 + lib/v2/mihoyo/bbs/user-post.js | 32 + lib/v2/mihoyo/bbs/utils.js | 34 + lib/v2/mihoyo/maintainer.js | 3 + lib/v2/mihoyo/radar.js | 93 +- lib/v2/mihoyo/router.js | 3 + lib/v2/mihoyo/sr/news.js | 12 +- lib/v2/mihoyo/ys/news.js | 4 +- lib/v2/mingpao/index.js | 6 +- lib/v2/miris/blog.js | 19 - lib/v2/miris/maintainer.js | 3 - lib/v2/miris/radar.js | 11 - lib/v2/mirror/index.js | 2 +- lib/v2/missav/maintainer.js | 3 + lib/v2/missav/new.js | 36 + lib/v2/missav/radar.js | 13 + lib/v2/missav/router.js | 3 + lib/v2/missav/templates/preview.art | 3 + lib/v2/mittrchina/index.js | 99 + lib/v2/mittrchina/maintainer.js | 3 + lib/v2/mittrchina/radar.js | 31 + lib/v2/mittrchina/router.js | 3 + lib/v2/mittrchina/templates/movie.art | 5 + lib/v2/mixcloud/index.js | 6 +- lib/v2/mobilism/forums.js | 75 - lib/v2/mobilism/maintainer.js | 4 - lib/v2/mobilism/portal.js | 54 - lib/v2/mobilism/radar.js | 23 - lib/v2/mobilism/router.js | 4 - lib/v2/modb/maintainer.js | 3 + lib/v2/modb/radar.js | 13 + lib/v2/modb/router.js | 3 + lib/v2/modb/topic.js | 59 + lib/v2/modelscope/community.js | 2 +- lib/v2/modelscope/datasets.js | 4 +- lib/v2/modelscope/models.js | 4 +- lib/v2/modelscope/studios.js | 2 +- lib/v2/modrinth/api.d.ts | 101 + lib/v2/modrinth/maintainer.js | 3 + lib/v2/modrinth/radar.js | 13 + lib/v2/modrinth/router.js | 3 + lib/v2/modrinth/templates/version.art | 8 + lib/v2/modrinth/versions.js | 75 + lib/v2/mpaypass/main.js | 14 +- lib/v2/mrm/index.js | 42 + lib/v2/mrm/maintainer.js | 3 + lib/v2/mrm/radar.js | 13 + lib/v2/{ezone => mrm}/router.js | 2 +- lib/v2/mtime/maintainer.js | 3 - lib/v2/mtime/news.js | 52 - lib/v2/mtime/radar.js | 13 - lib/v2/mydrivers/cid.js | 9 +- lib/v2/mydrivers/index.js | 6 +- lib/v2/mydrivers/rank.js | 4 +- lib/v2/mydrivers/util.js | 12 +- lib/v2/myfigurecollection/activity.js | 2 +- lib/v2/myfigurecollection/index.js | 10 +- lib/v2/mygopen/index.js | 2 +- lib/v2/mymusicsheet/maintainer.js | 3 + lib/v2/mymusicsheet/radar.js | 13 + lib/v2/mymusicsheet/router.js | 3 + lib/v2/mymusicsheet/templates/description.art | 41 + lib/v2/mymusicsheet/usersheets.js | 120 + lib/v2/nasa/apod-cn.js | 2 +- lib/v2/nasa/apod-ncku.js | 2 +- lib/v2/nasa/apod.js | 2 +- lib/v2/natgeo/dailyphoto.js | 2 +- lib/v2/natgeo/dailyselection.js | 2 +- lib/v2/natgeo/natgeo.js | 2 +- lib/v2/nationalgeographic/latest-stories.js | 8 +- lib/v2/nature/cover.js | 74 +- lib/v2/nature/highlight.js | 4 +- lib/v2/nature/news-and-comment.js | 4 +- lib/v2/nature/news.js | 4 +- lib/v2/nature/research.js | 4 +- lib/v2/nature/siteindex.js | 12 +- lib/v2/nature/utils.js | 137 +- lib/v2/nautil/radar.js | 2 +- lib/v2/nautil/topics.js | 2 +- lib/v2/nbd/article.js | 50 - lib/v2/nbd/index.js | 5 +- lib/v2/nbd/maintainer.js | 2 +- lib/v2/nbd/radar.js | 6 +- lib/v2/nbd/router.js | 2 +- lib/v2/nber/radar.js | 4 +- lib/v2/ncc-cma/cmdp.js | 81 + lib/v2/ncc-cma/maintainer.js | 3 + lib/v2/ncc-cma/radar.js | 229 + lib/v2/ncc-cma/router.js | 3 + lib/v2/ncc-cma/templates/description.art | 9 + lib/v2/ncepu/master/masterinfo.js | 7 +- lib/v2/ncu/jwc.js | 33 + lib/v2/ncu/maintainer.js | 3 + lib/v2/ncu/radar.js | 13 + lib/v2/ncu/router.js | 3 + lib/v2/ndss-symposium/ndss.js | 8 +- lib/v2/neatdownloadmanager/download.js | 10 +- lib/v2/neea/index.js | 2 +- lib/v2/nenu/sohac.js | 2 +- lib/v2/nenu/yjsy.js | 4 +- lib/v2/netflav/index.js | 39 + lib/v2/netflav/maintainer.js | 3 + lib/v2/netflav/radar.js | 13 + lib/v2/netflav/router.js | 3 + lib/v2/netflav/templates/description.art | 8 + lib/v2/neu/bmie.js | 2 +- lib/v2/newmuseum/radar.js | 2 +- lib/v2/newrank/douyin.js | 2 +- lib/v2/newrank/utils.js | 10 +- lib/v2/newrank/wechat.js | 6 +- lib/v2/news/maintainer.js | 2 +- lib/v2/news/radar.js | 4 +- lib/v2/news/router.js | 3 +- lib/v2/news/templates/description.art | 21 + lib/v2/news/whxw.js | 68 - lib/v2/news/xhsxw.js | 81 + lib/v2/newsmarket/index.js | 2 +- lib/v2/newzmz/index.js | 6 +- lib/v2/newzmz/util.js | 17 +- lib/v2/nga/forum.js | 6 +- lib/v2/nga/post.js | 45 +- lib/v2/nhentai/other.js | 8 +- lib/v2/nhentai/search.js | 2 +- lib/v2/nhentai/util.js | 10 +- .../{news_web_easy.js => news-web-easy.js} | 0 lib/v2/nhk/news.js | 2 +- lib/v2/nhk/router.js | 2 +- lib/v2/niaogebiji/index.js | 2 +- lib/v2/niaogebiji/maintainer.js | 1 - lib/v2/niaogebiji/radar.js | 6 +- lib/v2/niaogebiji/today.js | 2 +- lib/v2/nikkei/asia/index.js | 4 +- lib/v2/nikkei/cn/index.js | 12 +- lib/v2/nikkei/news.js | 15 +- lib/v2/nintendo/{eshop_cn.js => eshop-cn.js} | 0 lib/v2/nintendo/{eshop_hk.js => eshop-hk.js} | 2 +- lib/v2/nintendo/{eshop_jp.js => eshop-jp.js} | 0 lib/v2/nintendo/{eshop_us.js => eshop-us.js} | 0 .../nintendo/{news_china.js => news-china.js} | 0 lib/v2/nintendo/router.js | 10 +- lib/v2/nintendo/system-update.js | 2 +- lib/v2/nintendo/utils.js | 4 +- lib/v2/njglyy/utils/index.js | 4 +- lib/v2/njit/jwc.js | 34 +- lib/v2/njit/tzgg.js | 22 +- lib/v2/nju/admission.js | 16 +- lib/v2/nju/dafls.js | 20 +- lib/v2/nju/hosptial.js | 16 +- lib/v2/nju/hqjt.js | 20 +- lib/v2/nju/itsc.js | 28 +- lib/v2/nju/jjc.js | 22 +- lib/v2/nju/radar.js | 8 +- lib/v2/nju/zbb.js | 26 +- lib/v2/nju/zcc.js | 16 +- lib/v2/njucm/utils/index.js | 4 +- lib/v2/njupt/jwc.js | 6 +- lib/v2/njupt/radar.js | 4 +- lib/v2/njust/cwc.js | 2 +- lib/v2/njust/dgxg.js | 2 +- lib/v2/njust/eo.js | 2 +- lib/v2/njust/eoe.js | 2 +- lib/v2/njust/jwc.js | 2 +- lib/v2/njust/radar.js | 6 +- lib/v2/njxzc/utils/index.js | 4 +- lib/v2/nmbxd1/forum.js | 58 - lib/v2/nmbxd1/maintainer.js | 3 - lib/v2/nmbxd1/radar.js | 13 - lib/v2/nmbxd1/router.js | 3 - lib/v2/nmbxd1/templates/description.art | 65 - lib/v2/nmtv/column.js | 2 +- lib/v2/nmtv/radar.js | 2 +- lib/v2/nodejs/blog.js | 2 +- lib/v2/nogizaka46/news.js | 2 +- lib/v2/notefolio/search.js | 17 +- lib/v2/notion/database.js | 2 +- lib/v2/now/news.js | 9 +- lib/v2/nowcoder/discuss.js | 2 +- lib/v2/nowcoder/jobcenter.js | 4 +- lib/v2/nowcoder/recommend.js | 4 +- lib/v2/nowcoder/schedule.js | 12 +- lib/v2/npm/router.js | 2 +- lib/v2/npr/full.js | 12 +- lib/v2/npr/radar.js | 4 +- lib/v2/nua/dc.js | 2 +- lib/v2/nua/lib.js | 2 +- lib/v2/nuaa/college/cae.js | 2 +- lib/v2/nuaa/college/cs.js | 4 +- lib/v2/nuaa/jwc/jwc.js | 2 +- lib/v2/nuaa/yjsy/yjsy.js | 2 +- lib/v2/nuist/bulletin.js | 2 +- lib/v2/nuist/cas.js | 4 +- lib/v2/nuist/xgc.js | 4 +- lib/v2/nyaa/main.js | 7 +- lib/v2/nytimes/author.js | 43 - ...g_chinese.js => daily-briefing-chinese.js} | 6 +- lib/v2/nytimes/index.js | 39 +- lib/v2/nytimes/radar.js | 6 - lib/v2/nytimes/router.js | 3 +- ...arithmeticIndex.js => arithmetic-index.js} | 12 +- lib/v2/oceanengine/router.js | 2 +- lib/v2/odaily/activity.js | 2 +- lib/v2/odaily/router.js | 2 +- .../odaily/{search_news.js => search-news.js} | 0 lib/v2/odaily/user.js | 2 +- lib/v2/oeeee/app/channel.js | 2 +- lib/v2/oeeee/utils.js | 2 +- lib/v2/oncc/money18.js | 6 +- lib/v2/onehu/common.js | 28 + lib/v2/onehu/maintainer.js | 3 + lib/v2/onehu/radar.js | 11 + lib/v2/onehu/router.js | 3 + lib/v2/onet/maintainer.js | 3 + lib/v2/onet/news.js | 56 + lib/v2/onet/radar.js | 13 + lib/v2/{mtime => onet}/router.js | 2 +- lib/v2/onet/templates/article.art | 5 + lib/v2/onet/templates/image.art | 9 + lib/v2/onet/utils.js | 51 + lib/v2/oo-software/radar.js | 2 +- lib/v2/openai/chatgpt.js | 10 +- lib/v2/openai/radar.js | 6 +- lib/v2/orcid/index.js | 14 +- ..._sec_page_data.js => get-sec-page-data.js} | 0 lib/v2/oreno3d/main.js | 6 +- lib/v2/oreno3d/radar.js | 2 +- lib/v2/oschina/news.js | 1 + lib/v2/oschina/topic.js | 2 +- lib/v2/oschina/user.js | 2 +- lib/v2/oshwhub/explore.js | 37 +- lib/v2/osu/beatmaps/packs.js | 35 + lib/v2/osu/maintainer.js | 3 + lib/v2/osu/radar.js | 13 + lib/v2/osu/router.js | 3 + lib/v2/otobanana/cast.js | 29 + lib/v2/otobanana/livestream.js | 29 + lib/v2/otobanana/maintainer.js | 5 + lib/v2/otobanana/radar.js | 25 + lib/v2/otobanana/router.js | 5 + lib/v2/otobanana/templates/description.art | 11 + lib/v2/otobanana/timeline.js | 29 + lib/v2/otobanana/utils.js | 67 + lib/v2/ouc/it-tx.js | 44 + lib/v2/ouc/it.js | 6 +- lib/v2/ouc/maintainer.js | 3 +- lib/v2/ouc/radar.js | 6 + lib/v2/ouc/router.js | 1 + lib/v2/outagereport/index.js | 8 +- lib/v2/panewslab/news.js | 2 +- lib/v2/papers/index.js | 76 + lib/v2/papers/maintainer.js | 3 + lib/v2/papers/radar.js | 17 + lib/v2/papers/router.js | 3 + lib/v2/papers/templates/description.art | 15 + lib/v2/parliament/maintainer.js | 3 + lib/v2/parliament/radar.js | 22 + lib/v2/parliament/router.js | 3 + lib/v2/parliament/section77.js | 161 + lib/v2/patagonia/maintainer.js | 2 +- lib/v2/patagonia/radar.js | 6 +- lib/v2/paulgraham/article.js | 62 + lib/v2/paulgraham/maintainer.js | 3 + lib/v2/paulgraham/radar.js | 13 + lib/v2/paulgraham/router.js | 5 + lib/v2/penguin-random-house/utils.js | 4 +- lib/v2/people/index.js | 45 +- lib/v2/people/router.js | 3 +- lib/v2/people/xjpjh.js | 10 +- lib/v2/phoronix/index.js | 225 +- lib/v2/phoronix/maintainer.js | 2 +- lib/v2/phoronix/radar.js | 6 +- lib/v2/phoronix/router.js | 2 +- lib/v2/pianyuan/search.js | 2 +- lib/v2/pianyuan/utils.js | 10 +- lib/v2/picnob/maintainer.js | 2 +- lib/v2/picnob/radar.js | 2 +- lib/v2/picnob/templates/desc.art | 6 +- lib/v2/picnob/user.js | 111 +- lib/v2/picnob/utils.js | 20 + lib/v2/picuki/profile.js | 15 +- lib/v2/pikabu/radar.js | 6 +- lib/v2/pikabu/utils.js | 2 +- lib/v2/pixabay/radar.js | 2 +- lib/v2/pixabay/search.js | 2 +- .../api/{getBookmarks.js => get-bookmarks.js} | 0 ...IllustFollows.js => get-illust-follows.js} | 0 .../api/{getIllusts.js => get-illusts.js} | 0 .../api/{getRanking.js => get-ranking.js} | 4 +- .../{getUserDetail.js => get-user-detail.js} | 0 .../api/{searchIllust.js => search-illust.js} | 0 ...ularIllust.js => search-popular-illust.js} | 0 lib/v2/pixiv/bookmarks.js | 8 +- lib/v2/pixiv/illustfollow.js | 6 +- lib/v2/pixiv/novels.js | 6 +- lib/v2/pixiv/pixiv-got.js | 10 +- lib/v2/pixiv/ranking.js | 9 +- lib/v2/pixiv/search.js | 15 +- lib/v2/pixiv/token.js | 4 +- lib/v2/pixiv/user.js | 9 +- lib/v2/pixiv/utils.js | 4 +- lib/v2/pkmer/maintainer.js | 3 + lib/v2/pkmer/radar.js | 13 + lib/v2/pkmer/recent.js | 36 + lib/v2/pkmer/router.js | 3 + lib/v2/pku/bbs/hot.js | 2 +- lib/v2/pku/cls/announcement.js | 42 + lib/v2/pku/eecs.js | 2 +- lib/v2/pku/maintainer.js | 1 + lib/v2/pku/radar.js | 6 + lib/v2/pku/router.js | 3 +- lib/v2/pku/ss/{pg_admin.js => pg-admin.js} | 0 lib/v2/playpcesor/maintainer.js | 3 + lib/v2/playpcesor/radar.js | 13 + lib/v2/playpcesor/router.js | 3 + lib/v2/playpcesor/rss.js | 32 + lib/v2/plurk/top.js | 6 +- lib/v2/plurk/user.js | 4 +- lib/v2/plurk/utils.js | 2 +- lib/v2/pmthinking/index.js | 61 - lib/v2/pmthinking/radar.js | 13 - lib/v2/pmthinking/templates/description.art | 4 - lib/v2/pnas/index.js | 4 +- lib/v2/polkadot/home.js | 34 - lib/v2/polkadot/maintainer.js | 3 - lib/v2/polkadot/radar.js | 13 - lib/v2/polkadot/router.js | 3 - lib/v2/polkaworld/home.js | 24 - lib/v2/polkaworld/maintainer.js | 3 - lib/v2/polkaworld/radar.js | 13 - lib/v2/polkaworld/router.js | 3 - .../{category_url.js => category-url.js} | 2 +- lib/v2/pornhub/category.js | 4 +- lib/v2/pornhub/model.js | 2 +- lib/v2/pornhub/pornstar.js | 2 +- lib/v2/pornhub/router.js | 2 +- lib/v2/pornhub/users.js | 2 +- lib/v2/pornhub/utils.js | 11 +- lib/v2/prestige-av/maintainer.js | 3 - lib/v2/prestige-av/radar.js | 18 - lib/v2/prestige-av/router.js | 3 - lib/v2/prestige-av/series.js | 42 - lib/v2/priconne-redive/maintainer.js | 3 + lib/v2/priconne-redive/news.js | 54 + lib/v2/priconne-redive/radar.js | 13 + lib/v2/priconne-redive/router.js | 3 + lib/v2/producthunt/today.js | 2 +- lib/v2/ps/maintainer.js | 4 + lib/v2/ps/monthly-games.js | 31 + lib/v2/ps/radar.js | 17 + lib/v2/ps/router.js | 4 + lib/v2/ps/templates/monthly-games.art | 1 + lib/{routes => v2}/ps/trophy.js | 8 +- lib/v2/pts/index.js | 12 +- lib/v2/pts/live.js | 2 +- lib/v2/pubmed/trending.js | 2 +- lib/v2/pumc/mdadmission.js | 2 +- lib/v2/putty/changes.js | 2 +- lib/v2/pwc/maintainer.js | 3 + lib/v2/pwc/radar.js | 13 + lib/v2/pwc/router.js | 3 + lib/v2/pwc/sustainability.js | 58 + lib/v2/qbitai/category.js | 27 + lib/v2/qbitai/maintainer.js | 4 + lib/v2/qbitai/radar.js | 19 + lib/v2/qbitai/router.js | 4 + lib/v2/qbitai/tag.js | 27 + lib/v2/qdaily/index.js | 87 - lib/v2/qdaily/maintainer.js | 3 - lib/v2/qdaily/radar.js | 25 - lib/v2/qdaily/router.js | 3 - lib/v2/qdu/houqin.js | 63 + lib/v2/qdu/jwc.js | 22 +- lib/v2/qdu/maintainer.js | 1 + lib/v2/qdu/radar.js | 8 + lib/v2/qdu/router.js | 1 + lib/v2/qianp/news.js | 13 +- lib/v2/qianp/radar.js | 2 +- lib/v2/qianp/utils.js | 19 + lib/v2/qidian/forum.js | 3 +- lib/v2/qidiantu/index.js | 87 - lib/v2/qidiantu/maintainer.js | 4 - lib/v2/qidiantu/radar.js | 19 - lib/v2/qidiantu/router.js | 3 - lib/v2/qingting/podcast.js | 2 +- lib/v2/qlu/notice.js | 13 +- lib/v2/qoo-app/news.js | 10 +- lib/v2/qoo-app/notes/note.js | 2 +- lib/v2/qoo-app/router.js | 2 +- .../user/{appComment.js => app-comment.js} | 0 lib/v2/qq/ac/utils.js | 2 +- lib/v2/qq/kg/cache.js | 2 +- lib/v2/qq/live.js | 36 - lib/v2/qq/maintainer.js | 1 - lib/v2/qq/radar.js | 8 - lib/v2/qq/router.js | 1 - lib/v2/qqorw/index.js | 6 +- lib/v2/qqorw/maintainer.js | 2 +- lib/v2/questmobile/maintainer.js | 3 + lib/v2/questmobile/radar.js | 18 + lib/v2/questmobile/report.js | 127 + lib/v2/questmobile/router.js | 3 + lib/v2/questmobile/templates/description.art | 17 + lib/v2/quicker/qa.js | 4 +- lib/v2/quicker/share.js | 4 +- lib/v2/quicker/user.js | 4 +- lib/v2/qweather/3days.js | 67 +- lib/v2/qweather/maintainer.js | 2 +- lib/v2/qweather/templates/3days.art | 2 + lib/v2/radio-canada/latest.js | 9 +- lib/v2/radio/album.js | 6 +- lib/v2/radio/zhibo.js | 2 +- lib/v2/radiofrance/geopolitique.js | 33 - lib/v2/radiofrance/maintainer.js | 3 - lib/v2/radiofrance/radar.js | 13 - lib/v2/radiofrance/router.js | 3 - lib/v2/radiofrance/templates/article.art | 6 - lib/v2/rarehistoricalphotos/radar.js | 2 +- lib/v2/rattibha/radar.js | 2 +- lib/v2/rawkuma/manga.js | 4 +- lib/v2/reactnewsletter/maintainer.js | 1 - lib/v2/reactnewsletter/radar.js | 2 +- lib/v2/readhub/daily.js | 43 + lib/v2/readhub/index.js | 128 +- lib/v2/readhub/maintainer.js | 3 +- lib/v2/readhub/radar.js | 14 +- lib/v2/readhub/router.js | 6 +- lib/v2/readhub/templates/description.art | 40 + lib/v2/readhub/util.js | 68 + lib/v2/remnote/changelog.js | 1 - lib/v2/remnote/maintainer.js | 2 +- lib/v2/reuters/common.js | 140 +- lib/v2/reuters/maintainer.js | 2 - lib/v2/reuters/migration_prompt.js | 3 - lib/v2/reuters/router.js | 2 - lib/v2/rfa/index.js | 4 +- lib/v2/rfi/maintainer.js | 2 +- lib/v2/rfi/news.js | 57 +- lib/v2/rfi/radar.js | 10 +- lib/v2/rfi/router.js | 2 +- lib/v2/right/forum.js | 2 +- lib/v2/routledge/book-series.js | 78 + lib/v2/routledge/maintainer.js | 3 + lib/v2/routledge/radar.js | 13 + lib/v2/routledge/router.js | 3 + .../templates/description.art} | 2 + lib/v2/rsc/journal.js | 2 +- lib/v2/rsshub/maintainer.js | 1 - lib/v2/rsshub/radar.js | 6 - lib/v2/rsshub/router.js | 1 - lib/v2/rsshub/sponsors.js | 47 - lib/v2/rsshub/transform/html.js | 33 +- lib/v2/rsshub/transform/json.js | 4 +- lib/v2/rsshub/transform/sitemap.js | 46 +- lib/v2/ruancan/utils.js | 2 +- lib/v2/ruc/hr.js | 2 +- lib/v2/runtrail/maintainer.js | 1 - lib/v2/runtrail/posts.js | 4 +- lib/v2/sakurazaka46/news.js | 2 +- lib/v2/saraba1st/digest.js | 16 +- lib/v2/saraba1st/thread.js | 10 +- lib/v2/sass/gs/index.js | 13 +- lib/v2/scau/maintainer.js | 1 + lib/v2/scau/radar.js | 8 + lib/v2/scau/router.js | 1 + lib/v2/scau/yjs.js | 4 +- lib/v2/scau/yjsy.js | 27 + lib/v2/science/utils.js | 8 +- lib/v2/sciencenet/blog.js | 2 +- lib/v2/sciencenet/user.js | 2 +- lib/v2/scmp/index.js | 145 +- lib/v2/scmp/maintainer.js | 2 + lib/v2/scmp/radar.js | 8 +- lib/v2/scmp/router.js | 2 + lib/v2/scmp/topic.js | 66 + lib/v2/scmp/utils.js | 92 + ... physics-school-announcements-and-news.js} | 0 lib/v2/scnu/router.js | 6 +- lib/v2/sctv/programme.js | 6 +- lib/v2/scut/jwc/news.js | 14 +- lib/v2/scut/jwc/notice.js | 6 +- lib/v2/scut/jwc/school.js | 6 +- lib/v2/scut/router.js | 2 +- .../seie/{news_center.js => news-ccenter.js} | 2 +- lib/v2/sdu/cmse.js | 2 +- lib/v2/sdu/cs.js | 2 +- lib/v2/sdu/epe.js | 2 +- lib/v2/sdu/extractor/sdrj.js | 2 +- lib/v2/sdu/extractor/view.js | 2 +- lib/v2/sdu/extractor/wh/jwc.js | 2 +- lib/v2/sdu/extractor/wh/news.js | 2 +- lib/v2/sdu/mech.js | 2 +- lib/v2/sdu/sc.js | 4 +- lib/v2/sdu/wh/jwc.js | 2 +- lib/v2/sec-wiki/weekly.js | 2 +- lib/v2/secnews/index.js | 27 - lib/v2/secnews/maintainer.js | 3 - lib/v2/secnews/radar.js | 13 - lib/v2/secrss/author.js | 25 +- lib/v2/secrss/category.js | 27 +- lib/v2/secrss/maintainer.js | 4 +- lib/v2/seekingalpha/radar.js | 2 +- lib/v2/segmentfault/blogs.js | 29 +- lib/v2/segmentfault/channel.js | 48 +- lib/v2/segmentfault/user.js | 38 +- lib/v2/segmentfault/utils.js | 32 + lib/v2/sehuatang/index.js | 26 +- lib/v2/sehuatang/user.js | 29 +- lib/v2/sensortower/blog.js | 4 +- lib/v2/setn/index.js | 10 +- lib/v2/seu/cse/index.js | 2 +- lib/v2/seu/yzb/index.js | 2 +- lib/v2/sfacg/maintainer.js | 3 + lib/v2/sfacg/novel-chapter.js | 51 + lib/v2/sfacg/radar.js | 13 + lib/v2/sfacg/router.js | 3 + lib/v2/shiep/config.js | 22 +- lib/v2/shiep/index.js | 50 +- lib/v2/shiep/radar.js | 12 +- lib/v2/shisu/maintainer.js | 3 + lib/v2/shisu/news.js | 64 + lib/v2/shisu/radar.js | 13 + lib/v2/shisu/router.js | 3 + lib/v2/shmeea/index.js | 61 +- lib/v2/shmeea/maintainer.js | 2 +- lib/v2/shmeea/radar.js | 13 +- lib/v2/shmeea/router.js | 2 +- lib/v2/shoac/maintainer.js | 3 + lib/v2/shoac/radar.js | 13 + lib/v2/shoac/recent-show.js | 72 + lib/v2/shoac/router.js | 3 + lib/v2/shoac/templates/detail.art | 25 + lib/v2/showstart/artist.js | 15 + lib/v2/showstart/brand.js | 15 + lib/v2/showstart/const.js | 4 + lib/v2/showstart/event.js | 18 + lib/v2/showstart/maintainer.js | 6 + lib/v2/showstart/radar.js | 40 + lib/v2/showstart/router.js | 6 + lib/v2/showstart/search.js | 51 + lib/v2/showstart/service.js | 167 + lib/v2/showstart/utils.js | 92 + lib/v2/shuiguopai/index.js | 40 +- lib/v2/shuiguopai/templates/description.art | 2 +- lib/v2/sigsac/ccs.js | 4 +- lib/v2/sina/finance/stock/usstock.js | 2 +- lib/v2/sina/utils.js | 14 +- lib/v2/sinchew/index.js | 4 +- lib/v2/sjtu/tongqu/activity.js | 4 +- lib/v2/slowmist/slowmist.js | 28 +- lib/v2/smashingmagazine/category.js | 7 +- lib/v2/smashingmagazine/radar.js | 4 +- .../{haowen_fenlei.js => haowen-fenlei.js} | 9 +- lib/v2/smzdm/router.js | 2 +- lib/v2/snowpeak/maintainer.js | 2 +- lib/{routes => v2}/sogou/doodles.js | 0 lib/v2/sogou/maintainer.js | 4 + lib/v2/sogou/radar.js | 16 + lib/v2/sogou/router.js | 4 + lib/v2/sogou/search.js | 54 + lib/v2/sogou/templates/description.art | 6 + lib/v2/sohu/mp.js | 79 +- lib/v2/sohu/templates/video.art | 7 + lib/v2/solidot/_article.js | 16 +- lib/v2/solidot/main.js | 6 +- lib/v2/sony/maintainer.js | 2 +- lib/v2/sourceforge/index.js | 33 + lib/v2/sourceforge/maintainer.js | 3 + lib/v2/sourceforge/radar.js | 12 + lib/v2/sourceforge/router.js | 3 + lib/v2/spotify/saved.js | 2 +- lib/v2/spotify/top.js | 4 +- lib/v2/spotify/utils.js | 4 +- lib/v2/sqmc/radar.js | 2 +- lib/v2/sse/convert.js | 2 +- lib/v2/sse/disclosure.js | 9 +- lib/v2/sse/inquire.js | 2 +- lib/v2/sse/lawandrules.js | 2 +- lib/v2/sse/radar.js | 2 +- lib/v2/sse/renewal.js | 2 +- lib/v2/sspai/author.js | 4 +- lib/v2/sspai/router.js | 4 +- .../{seriesUpdate.js => series-update.js} | 0 ...ortcutsGallery.js => shortcuts-gallery.js} | 2 +- lib/v2/sspu/jwc.js | 44 + lib/v2/sspu/maintainer.js | 4 + lib/v2/sspu/pe.js | 65 + lib/v2/sspu/radar.js | 21 + lib/v2/sspu/router.js | 4 + lib/v2/stcn/index.js | 4 +- lib/v2/stockedge/radar.js | 2 +- lib/v2/stratechery/radar.js | 4 +- lib/v2/studygolang/utils.js | 4 +- lib/v2/subhd/index.js | 2 +- lib/v2/supchina/index.js | 6 +- lib/v2/supchina/podcasts.js | 2 +- lib/v2/surfshark/blog.js | 91 + lib/v2/surfshark/maintainer.js | 3 + lib/v2/surfshark/radar.js | 90 + lib/v2/surfshark/router.js | 3 + lib/v2/surfshark/templates/description.art | 5 + lib/v2/swissinfo/index.js | 2 +- lib/v2/swpu/utils.js | 2 +- lib/v2/syosetu/chapter.js | 2 +- lib/v2/sysu/cse.js | 16 +- lib/v2/sysu/ygafz.js | 2 +- lib/v2/szse/inquire.js | 2 +- lib/v2/szse/notice.js | 28 +- lib/v2/szse/projectdynamic.js | 2 +- lib/v2/szu/yz/index.js | 2 +- lib/v2/tableau/maintainer.js | 3 + lib/v2/tableau/radar.js | 11 + lib/v2/tableau/router.js | 3 + lib/v2/tableau/viz-of-the-day.js | 26 + lib/v2/taiwannews/hot.js | 7 +- lib/v2/tangshufang/index.js | 2 +- lib/v2/taoguba/blog.js | 2 +- lib/v2/taoguba/index.js | 2 +- lib/v2/tass/maintainer.js | 3 + lib/v2/tass/news.js | 58 + lib/v2/tass/radar.js | 13 + lib/v2/tass/router.js | 3 + lib/v2/techcrunch/maintainer.js | 2 +- lib/v2/techpowerup/maintainer.js | 1 - lib/v2/techpowerup/radar.js | 4 +- lib/v2/telecompaper/news.js | 2 +- lib/v2/telecompaper/search.js | 4 +- lib/v2/telegram/channel.js | 53 +- lib/v2/telegram/maintainer.js | 6 +- lib/v2/telegram/stickerpack.js | 2 +- lib/v2/tencent/cloud/column.js | 51 - lib/v2/tencent/maintainer.js | 1 - lib/v2/tencent/news/author.js | 52 +- lib/v2/tencent/pvp/newsindex.js | 6 +- lib/v2/tencent/qq/sdk/changelog.js | 2 +- lib/v2/tencent/radar.js | 42 +- lib/v2/tencent/router.js | 1 - lib/v2/tencent/templates/news/image.art | 4 + lib/v2/tesla/cx.js | 119 + lib/v2/tesla/maintainer.js | 1 + lib/v2/tesla/price/get-price.js | 2 +- lib/v2/tesla/radar.js | 14 +- lib/v2/tesla/router.js | 1 + lib/v2/tesla/templates/description.art | 140 + lib/v2/test/index.js | 465 +- lib/v2/tfc-taiwan/maintainer.js | 1 - lib/v2/tfc-taiwan/utils.js | 4 +- lib/v2/theatlantic/maintainer.js | 2 +- lib/v2/theatlantic/utils.js | 4 +- lib/v2/theblockbeats/index.js | 53 + .../maintainer.js | 0 lib/v2/{blockbeats => theblockbeats}/radar.js | 8 +- .../{appledaily => theblockbeats}/router.js | 2 +- lib/v2/thecatcity/index.js | 6 +- lib/v2/thecatcity/radar.js | 2 +- .../thecatcity/{termsMap.js => terms-map.js} | 0 lib/v2/thehindu/radar.js | 2 +- lib/v2/theinitium/full.js | 4 +- lib/v2/thenewslens/index.js | 2 +- lib/v2/thenewslens/radar.js | 6 - lib/v2/thepaper/factpaper.js | 2 +- lib/v2/thepaper/featured.js | 2 +- lib/v2/thepaper/radar.js | 4 +- lib/v2/thepaper/utils.js | 32 +- lib/v2/theverge/index.js | 8 +- lib/v2/thoughtco/index.js | 95 + lib/v2/thoughtco/maintainer.js | 3 + lib/v2/thoughtco/radar.js | 155 + lib/v2/thoughtco/router.js | 3 + lib/v2/thoughtco/templates/description.art | 16 + lib/v2/threads/index.js | 170 +- lib/v2/threads/radar.js | 2 +- lib/v2/threads/utils.js | 147 + lib/v2/thwiki/index.js | 4 +- lib/v2/thwiki/radar.js | 2 +- lib/v2/tiktok/maintainer.js | 2 +- lib/v2/tiktok/radar.js | 2 +- lib/v2/tingshuitz/wuhan.js | 2 +- lib/v2/tingtingfm/program.js | 10 +- lib/v2/tju/cic/index.js | 10 +- lib/v2/tju/news/index.js | 16 +- lib/v2/tju/oaa/index.js | 4 +- lib/v2/tju/yzb/index.js | 10 +- lib/v2/tokeninsight/blog.js | 4 +- lib/v2/tokeninsight/bulletin.js | 2 +- lib/v2/tokeninsight/report.js | 4 +- lib/v2/toodaylab/index.js | 10 +- lib/v2/tophub/list.js | 40 + lib/v2/tophub/maintainer.js | 1 + lib/v2/tophub/radar.js | 6 + lib/v2/tophub/router.js | 1 + lib/v2/tophub/templates/rank.art | 22 + lib/v2/topys/index.js | 2 +- lib/v2/tradingview/blog.js | 87 +- lib/v2/tradingview/desktop.js | 61 + lib/v2/tradingview/maintainer.js | 3 +- lib/v2/tradingview/pine.js | 54 + lib/v2/tradingview/radar.js | 100 +- lib/v2/tradingview/router.js | 4 +- lib/v2/tradingview/templates/description.art | 15 +- lib/v2/transcriptforest/index.js | 116 + lib/v2/transcriptforest/maintainer.js | 3 + lib/v2/transcriptforest/radar.js | 307 + lib/v2/transcriptforest/router.js | 3 + .../templates/description.art | 23 + .../{allTrending.js => all-trending.js} | 0 lib/v2/trending/router.js | 2 +- lib/v2/trendingpapers/maintainer.js | 3 + lib/v2/trendingpapers/papers.js | 41 + lib/v2/trendingpapers/radar.js | 11 + lib/v2/trendingpapers/router.js | 3 + lib/v2/tvb/news.js | 2 +- lib/v2/tvtropes/featured.js | 73 + lib/v2/tvtropes/maintainer.js | 3 + lib/v2/tvtropes/radar.js | 19 + lib/v2/tvtropes/router.js | 3 + lib/v2/tvtropes/templates/description.art | 23 + lib/v2/twitch/live.js | 88 + lib/v2/twitch/maintainer.js | 5 + lib/v2/twitch/radar.js | 37 + lib/v2/twitch/router.js | 5 + lib/v2/twitch/schedule.js | 76 + lib/v2/twitch/video.js | 71 + ...lback_common.js => api-fallback-common.js} | 4 +- lib/v2/twitter/collection.js | 6 +- lib/v2/twitter/followings.js | 2 +- lib/v2/twitter/keyword.js | 4 +- lib/v2/twitter/likes.js | 2 +- lib/v2/twitter/list.js | 6 +- lib/v2/twitter/media.js | 6 +- lib/v2/twitter/trends.js | 2 +- lib/v2/twitter/user.js | 4 +- lib/v2/twitter/utils.js | 105 +- lib/v2/twitter/web-api/constants.js | 154 +- lib/v2/twitter/web-api/login.js | 183 + lib/v2/twitter/web-api/media.js | 2 + lib/v2/twitter/web-api/search.js | 2 + lib/v2/twitter/web-api/token.js | 44 + lib/v2/twitter/web-api/tweet.js | 2 + lib/v2/twitter/web-api/twitter-api.js | 227 +- lib/v2/twitter/web-api/twitter-got.js | 113 - lib/v2/twitter/web-api/user.js | 9 +- lib/v2/twreporter/category.js | 40 - .../{fetch_article.js => fetch-article.js} | 0 lib/v2/twreporter/maintainer.js | 2 - lib/v2/twreporter/newest.js | 2 +- lib/v2/twreporter/photography.js | 46 - lib/v2/twreporter/radar.js | 12 - lib/v2/twreporter/router.js | 2 - lib/v2/txrjy/fornumtopic.js | 4 +- lib/v2/txrjy/radar.js | 2 +- lib/v2/u3c3/index.js | 28 +- lib/v2/u3c3/maintainer.js | 3 +- lib/v2/u3c3/router.js | 3 +- lib/v2/u9a9/index.js | 28 +- lib/v2/uber/blog.js | 2 +- lib/v2/uchicago/current.js | 4 +- lib/v2/udn/breaking-news.js | 8 +- lib/v2/udn/global/index.js | 20 +- lib/v2/uibe/hr.js | 2 +- lib/v2/uniqlo/maintainer.js | 3 + lib/v2/uniqlo/new.js | 62 + lib/v2/uniqlo/radar.js | 12 + lib/v2/uniqlo/router.js | 3 + lib/v2/unraid/community-apps.js | 35 + lib/v2/unraid/maintainer.js | 3 + lib/v2/unraid/radar.js | 13 + lib/v2/unraid/router.js | 3 + lib/v2/unusualwhales/radar.js | 2 +- lib/v2/uptimerobot/rss.js | 2 +- lib/v2/uraaka-joshi/uraaka-joshi-user.js | 4 +- lib/v2/uraaka-joshi/uraaka-joshi.js | 4 +- lib/v2/urbandictionary/radar.js | 2 +- lib/v2/usenix/usenix.js | 39 +- lib/v2/ustb/tj/news.js | 2 +- lib/v2/ustb/yjsy/news.js | 33 +- lib/v2/ustb/yzxc/tzgg.js | 2 +- lib/v2/ustc/eeis.js | 4 +- lib/v2/ustc/gs.js | 4 +- lib/v2/ustc/job.js | 2 +- lib/v2/ustc/sist.js | 4 +- lib/v2/usts/jwch.js | 19 +- lib/v2/utgd/category.js | 3 +- lib/v2/utgd/templates/description.art | 6 +- lib/v2/utgd/timeline.js | 3 +- lib/v2/utgd/topic.js | 5 +- lib/v2/v1tx/maintainer.js | 1 - lib/v2/v2ex/tab.js | 2 +- lib/v2/vcb-s/category.js | 2 +- lib/v2/vcb-s/index.js | 2 +- lib/v2/verfghbw/press.js | 26 +- lib/v2/verfghbw/radar.js | 2 +- lib/v2/verse/articles.js | 42 - lib/v2/verse/maintainer.js | 3 - lib/v2/verse/radar.js | 13 - lib/v2/verse/router.js | 3 - lib/v2/vimeo/category.js | 2 +- lib/v2/vimeo/channel.js | 4 +- lib/v2/vimeo/radar.js | 2 +- lib/v2/vimeo/usr-videos.js | 8 +- lib/v2/vlive/index.js | 71 - lib/v2/vlive/maintainer.js | 3 - lib/v2/vlive/radar.js | 13 - lib/v2/vlive/router.js | 3 - lib/v2/vlive/templates/post.art | 8 - lib/v2/vlive/templates/video.art | 3 - lib/v2/vmware/flings.js | 59 - lib/v2/vmware/maintainer.js | 3 - lib/v2/vmware/radar.js | 13 - lib/v2/vmware/router.js | 3 - lib/v2/wallhaven/index.js | 4 +- lib/v2/wangqiutiyu/anchor.js | 42 - lib/v2/wangqiutiyu/maintainer.js | 3 - lib/v2/wangqiutiyu/radar.js | 13 - lib/v2/wangqiutiyu/router.js | 3 - lib/v2/watchout/index.js | 51 - lib/v2/watchout/radar.js | 13 - lib/v2/watchout/router.js | 3 - lib/v2/web3caff/index.js | 2 +- lib/v2/wechat/ce.js | 2 +- lib/v2/wechat/data258.js | 28 +- lib/v2/wechat/feeddd.js | 2 +- lib/v2/wechat/maintainer.js | 2 +- lib/v2/wechat/sogou.js | 4 +- lib/v2/wechat/tgchannel.js | 8 +- lib/v2/wechat/uread.js | 4 +- lib/v2/wechat/wxnmh.js | 8 +- lib/v2/weekendhk/maintainer.js | 1 - lib/v2/weibo/friends.js | 122 + lib/v2/weibo/group.js | 14 +- lib/v2/weibo/maintainer.js | 3 +- lib/v2/weibo/oasis/user.js | 7 +- lib/v2/weibo/radar.js | 10 +- lib/v2/weibo/router.js | 5 +- lib/v2/weibo/search/hot.js | 121 +- lib/v2/weibo/search/template/digest.art | 17 + .../weibo/{super_index.js => super-index.js} | 0 lib/v2/weibo/timeline.js | 14 +- lib/v2/weibo/user.js | 18 +- lib/v2/weibo/utils.js | 75 +- lib/v2/wenku8/chapter.js | 2 +- lib/v2/wenku8/volume.js | 4 +- lib/v2/wfu/news.js | 6 +- lib/v2/who/news-room.js | 2 +- lib/v2/whoscall/index.js | 55 - lib/v2/whoscall/maintainer.js | 5 - lib/v2/whoscall/radar.js | 25 - lib/v2/whoscall/router.js | 3 - lib/v2/whu/cs.js | 33 +- lib/v2/whu/gs/index.js | 4 +- lib/v2/whu/hyxt.js | 50 + lib/v2/whu/maintainer.js | 3 +- lib/v2/whu/news.js | 128 +- lib/v2/whu/radar.js | 256 +- lib/v2/whu/router.js | 3 +- lib/v2/whu/templates/description.art | 39 + lib/v2/whu/util.js | 145 + lib/v2/winstall/update.js | 4 +- lib/v2/wise/pair.js | 4 +- lib/v2/wmc-bj/maintainer.js | 3 + lib/v2/wmc-bj/publish.js | 58 + lib/v2/wmc-bj/radar.js | 17 + lib/v2/wmc-bj/router.js | 3 + lib/v2/wmc-bj/templates/description.art | 5 + lib/v2/wnacg/index.js | 12 +- lib/v2/worldjournal/index.js | 2 +- lib/v2/woshipm/bookmarks.js | 34 - lib/v2/woshipm/latest.js | 27 - lib/v2/woshipm/maintainer.js | 2 - lib/v2/woshipm/radar.js | 12 - lib/v2/woshipm/router.js | 4 +- .../{user_article.js => user-article.js} | 2 +- lib/v2/wp-china/news.js | 56 - lib/v2/wp-china/radar.js | 13 - lib/v2/wp-china/router.js | 3 - lib/v2/wsj/news.js | 2 +- lib/v2/wsj/utils.js | 4 +- lib/v2/wsyu/news.js | 8 +- lib/v2/wtu/index.js | 2 +- lib/v2/wxkol/maintainer.js | 3 - lib/v2/wxkol/radar.js | 13 - lib/v2/wxkol/router.js | 3 - lib/v2/wxkol/show.js | 81 - lib/v2/wyzxwk/article.js | 2 +- lib/v2/wzu/news.js | 2 +- lib/v2/x-mol/maintainer.js | 4 + lib/v2/x-mol/news.js | 63 + lib/v2/x-mol/paper.js | 73 + lib/v2/x-mol/radar.js | 25 + lib/v2/x-mol/router.js | 4 + .../templates/description.art | 5 +- lib/v2/x-mol/utils.js | 3 + lib/v2/x6d/index.js | 21 +- lib/v2/xaufe/jiaowu.js | 5 +- lib/v2/xaut/rsc.js | 2 +- lib/v2/xiaohongshu/user.js | 42 +- lib/v2/xiaohongshu/util.js | 82 +- lib/v2/xiaomiyoupin/utils.js | 2 +- lib/v2/xiaote/index.js | 4 +- lib/v2/xiaote/radar.js | 2 +- lib/v2/ximalaya/album.js | 12 +- lib/v2/ximalaya/radar.js | 2 +- lib/v2/ximalaya/utils.js | 2 +- lib/v2/xinpianchang/index.js | 4 +- lib/v2/xinpianchang/rank.js | 2 +- lib/v2/xinpianchang/util.js | 2 +- lib/v2/xjtu/dean.js | 4 +- lib/v2/xmanhua/index.js | 18 +- lib/v2/xmnn/epaper.js | 4 +- lib/v2/xmnn/maintainer.js | 1 + lib/v2/xmnn/news.js | 65 + lib/v2/xmnn/radar.js | 68 +- lib/v2/xmnn/router.js | 1 + lib/v2/xsijishe/forum.js | 7 +- lib/v2/xsijishe/maintainer.js | 1 + lib/v2/xsijishe/radar.js | 12 + lib/v2/xsijishe/rank.js | 60 + lib/v2/xsijishe/router.js | 1 + lib/v2/xueqiu/hots.js | 3 +- lib/v2/xueqiu/router.js | 6 +- lib/v2/xueqiu/snb.js | 2 +- .../{stock_comments.js => stock-comments.js} | 4 +- .../xueqiu/{stock_info.js => stock-info.js} | 9 +- lib/v2/xueqiu/today.js | 2 +- .../xueqiu/{user_stock.js => user-stock.js} | 0 lib/v2/xueqiu/user.js | 3 +- lib/v2/xwlb/index.js | 40 - lib/v2/xwlb/maintainer.js | 4 - lib/v2/xwlb/radar.js | 13 - lib/v2/xys/new.js | 6 +- lib/v2/xyzrank/index.js | 2 +- lib/v2/yahoo/maintainer.js | 2 + lib/v2/yahoo/news/tw/index.js | 24 + lib/v2/yahoo/news/tw/provider-helper.js | 22 + lib/v2/yahoo/news/tw/provider.js | 24 + lib/v2/yahoo/news/tw/utils.js | 129 + lib/v2/yahoo/news/{ => us}/index.js | 2 +- lib/v2/yahoo/radar.js | 30 +- lib/v2/yahoo/router.js | 5 +- lib/v2/yahoo/templates/youtube.art | 1 + lib/v2/yangtzeu/dongke.js | 2 +- lib/v2/yaohuo/index.js | 30 - lib/v2/yaohuo/router.js | 3 - lib/v2/ycwb/index.js | 4 +- lib/v2/yicai/dt.js | 110 + lib/v2/yicai/maintainer.js | 1 + lib/v2/yicai/radar.js | 164 + lib/v2/yicai/router.js | 1 + lib/v2/yicai/templates/description.art | 42 +- lib/v2/yicai/utils.js | 14 +- lib/v2/yoasobi-music/info.js | 6 +- .../jsonp-helper.js} | 6 +- lib/v2/yoasobi-music/live.js | 4 +- lib/v2/yoasobi-music/media.js | 4 +- lib/v2/yoasobi-music/radar.js | 8 +- lib/v2/yomujp/level.js | 101 + lib/v2/yomujp/maintainer.js | 3 + lib/v2/yomujp/radar.js | 13 + lib/v2/yomujp/router.js | 3 + lib/v2/youku/channel.js | 4 +- lib/v2/youtube/channel.js | 4 +- lib/v2/youtube/charts.js | 48 +- lib/v2/youtube/community.js | 2 +- lib/v2/youtube/custom.js | 2 +- lib/v2/youtube/live.js | 2 +- lib/v2/youtube/playlist.js | 2 +- lib/v2/youtube/radar.js | 2 +- lib/v2/youtube/subscriptions.js | 4 +- lib/v2/youtube/user.js | 2 +- lib/v2/youtube/utils.js | 12 +- lib/v2/youzhiyouxing/materials.js | 4 +- lib/v2/yunspe/maintainer.js | 3 - lib/v2/yunspe/newsflash.js | 42 - lib/v2/yunspe/radar.js | 13 - lib/v2/yunspe/router.js | 3 - lib/v2/yuque/book.js | 7 +- lib/v2/yuque/utils.js | 23 +- lib/v2/yxdzqb/index.js | 2 +- lib/v2/yxrb/home.js | 2 +- lib/v2/yyets/radar.js | 4 +- lib/v2/yystv/category.js | 2 +- lib/v2/zagg/maintainer.js | 2 +- lib/v2/zaobao/realtime.js | 41 +- lib/v2/zaobao/util.js | 33 +- lib/v2/zaobao/znews.js | 47 +- lib/v2/zcmu/jwc/index.js | 2 +- lib/v2/zcmu/yxy/index.js | 2 +- lib/v2/zcool/discover.js | 7 +- lib/v2/zcool/maintainer.js | 2 +- lib/v2/zcool/user.js | 2 +- lib/v2/zhibo8/more.js | 7 +- lib/v2/zhibo8/post.js | 2 +- lib/v2/zhihu/activities.js | 41 +- lib/v2/zhihu/answers.js | 2 +- lib/v2/zhihu/bookstore/newest.js | 2 +- lib/v2/zhihu/collection.js | 2 +- .../{daily_section.js => daily-section.js} | 2 +- lib/v2/zhihu/daily.js | 4 +- .../{x-zse-96_v3.js => x-zse-96-v3.js} | 23 +- lib/v2/zhihu/maintainer.js | 5 +- lib/v2/zhihu/pin/utils.js | 6 +- lib/v2/zhihu/question.js | 4 +- lib/v2/zhihu/radar.js | 27 + lib/v2/zhihu/router.js | 7 +- lib/v2/zhihu/timeline.js | 21 +- lib/v2/zhihu/topic.js | 143 +- lib/v2/zhihu/utils.js | 1 - lib/v2/zhihu/xhu/activities.js | 127 + lib/v2/zhihu/xhu/answers.js | 31 + lib/v2/zhihu/xhu/auth.js | 2 +- lib/v2/zhihu/xhu/collection.js | 32 +- lib/v2/zhihu/xhu/posts.js | 34 + lib/v2/zhihu/xhu/zhuanlan.js | 52 +- lib/v2/zhihu/zhuanlan.js | 54 +- lib/v2/zhitongcaijing/index.js | 6 +- lib/v2/zhiy/letter.js | 10 +- lib/v2/zhiy/post.js | 10 +- lib/v2/zhubai/index.js | 4 +- lib/v2/zhubai/radar.js | 3 +- lib/v2/zhubai/templates/description.art | 54 +- lib/v2/zhubai/top20.js | 55 +- lib/{routes => v2}/zimuxia/index.js | 24 +- lib/v2/zimuxia/maintainer.js | 4 + lib/v2/zimuxia/portfolio.js | 52 + lib/v2/zimuxia/radar.js | 19 + lib/v2/zimuxia/router.js | 4 + lib/v2/zjgtjy/index.js | 5 +- lib/v2/zjol/paper.js | 2 +- lib/v2/zju/career/index.js | 2 +- lib/v2/zju/cst/custom.js | 4 +- lib/v2/zju/cst/index.js | 30 +- lib/v2/zju/grs/index.js | 2 +- lib/v2/zju/list.js | 6 +- lib/v2/zju/physics/index.js | 2 +- lib/v2/zjuvag/blog.js | 33 + lib/v2/zjuvag/maintainer.js | 3 + lib/v2/zjuvag/radar.js | 11 + lib/v2/{miris => zjuvag}/router.js | 2 +- lib/v2/zodgame/forum.js | 10 +- lib/v2/zooTeam/blog.js | 32 - lib/v2/zooTeam/maintainer.js | 4 - lib/v2/zooTeam/radar.js | 21 - lib/v2/zooTeam/router.js | 4 - lib/v2/zooTeam/weekly.js | 34 - lib/v2/zotero/versions.js | 6 +- lib/v2/zrblog/maintainer.js | 3 + lib/v2/zrblog/radar.js | 13 + lib/v2/zrblog/router.js | 3 + lib/v2/zrblog/rss.js | 32 + lib/v2/zuel/notice.js | 2 +- lib/v2/zuvio/utils.js | 31 +- lib/v2/zuzhirenshi/index.js | 87 - lib/v2/zuzhirenshi/maintainer.js | 3 - lib/v2/zuzhirenshi/radar.js | 13 - lib/v2/zuzhirenshi/router.js | 3 - lib/v2/zyshow/router.js | 2 +- lib/views/error.art | 2 +- lib/views/rss.art | 2 +- lib/views/rss3-ums.js | 62 + lib/views/welcome.art | 2 +- package.json | 163 +- pnpm-lock.yaml | 4796 +++++++---- scripts/ansible/README.md | 2 + scripts/docker/minify-docker.js | 6 +- scripts/docs-scraper/docs.rsshub.app.json | 19 +- scripts/twitter-token/generate.js | 126 + scripts/twitter-token/generate.sh | 17 + scripts/workflow/build-maintainer.js | 6 +- .../workflow/test-issue/call-maintainer.js | 26 +- scripts/workflow/test-route/identify.js | 68 +- scripts/workflow/test-route/test.js | 29 +- test/config.js | 30 +- test/middleware/access-control.js | 31 +- test/middleware/cache.js | 19 +- test/middleware/debug.js | 2 +- test/middleware/onerror.js | 32 + test/middleware/parameter.js | 70 +- test/middleware/template.js | 2 +- test/pkg.js | 4 +- test/router.js | 10 +- test/utils/common-config.js | 1 + test/utils/common-utils.js | 2 +- test/utils/{dateParser.js => date-parser.js} | 2 +- test/utils/date.js | 2 +- test/utils/got.js | 2 +- test/utils/pac-proxy.js | 82 + test/utils/puppeteer-utils.js | 16 +- test/utils/rand-user-agent.js | 12 +- test/utils/request-wrapper.js | 118 +- test/utils/wechat-mp.js | 6 +- vercel.json | 1 + website/README.md | 10 +- website/{babel.config.js => babel.config.cjs} | 0 website/docs/.format/chineseFormat.js | 30 - website/docs/.format/file.js | 11 - website/docs/.format/file.mjs | 11 + .../docs/.format/{format.js => format.mjs} | 29 +- website/docs/.format/handle/heading.mjs | 81 + website/docs/.format/md/hierarchySlug.js | 126 - website/docs/.format/removeHeadingId.mjs | 26 + website/docs/.format/routeFormat.mjs | 49 + website/docs/.format/rsshub-heading-id.mjs | 83 + website/docs/.format/rsshub-no-dupe-attrs.mjs | 22 + website/docs/.format/rsshub-route-level.mjs | 23 + .../docs/.format/{slugId.js => slugId.mjs} | 4 +- .../{sortByHeading.js => sortByHeading.mjs} | 4 +- website/docs/README.md | 23 +- website/docs/api.md | 4 +- website/docs/faq.md | 4 +- website/docs/install/README.md | 666 +- website/docs/install/config.md | 644 ++ website/docs/instances.mdx | 15 + website/docs/joinus/advanced/advanced-feed.md | 2 +- .../docs/joinus/advanced/script-standard.md | 2 +- website/docs/joinus/advanced/use-cache.md | 2 +- website/docs/joinus/new-radar.md | 4 +- website/docs/joinus/new-rss/add-docs.md | 33 +- website/docs/joinus/new-rss/before-start.md | 2 +- website/docs/joinus/new-rss/start-code.md | 12 +- website/docs/joinus/new-rss/submit-route.md | 40 +- website/docs/joinus/quick-start.md | 8 +- website/docs/parameter.md | 44 +- website/docs/routes/{anime.md => anime.mdx} | 690 +- website/docs/routes/bbs.md | 1363 --- website/docs/routes/bbs.mdx | 1021 +++ website/docs/routes/blog.md | 540 -- website/docs/routes/blog.mdx | 493 ++ website/docs/routes/design.md | 411 - website/docs/routes/design.mdx | 355 + website/docs/routes/finance.md | 929 --- website/docs/routes/finance.mdx | 1002 +++ website/docs/routes/forecast.md | 244 - website/docs/routes/forecast.mdx | 321 + website/docs/routes/game.md | 1147 --- website/docs/routes/game.mdx | 1093 +++ website/docs/routes/government.md | 1752 ---- website/docs/routes/government.mdx | 2156 +++++ website/docs/routes/journal.md | 599 -- website/docs/routes/journal.mdx | 519 ++ website/docs/routes/live.md | 121 - website/docs/routes/live.mdx | 91 + website/docs/routes/multimedia.md | 2039 ----- website/docs/routes/multimedia.mdx | 1675 ++++ website/docs/routes/new-media.md | 5859 ------------- website/docs/routes/new-media.mdx | 5324 ++++++++++++ website/docs/routes/other.md | 1352 --- website/docs/routes/other.mdx | 1242 +++ website/docs/routes/picture.md | 638 -- website/docs/routes/picture.mdx | 539 ++ .../{program-update.md => program-update.mdx} | 597 +- website/docs/routes/programming.md | 1556 ---- website/docs/routes/programming.mdx | 1144 +++ website/docs/routes/reading.md | 592 -- website/docs/routes/reading.mdx | 414 + website/docs/routes/shopping.md | 626 -- website/docs/routes/shopping.mdx | 648 ++ .../{social-media.md => social-media.mdx} | 1561 ++-- website/docs/routes/study.md | 874 -- website/docs/routes/study.mdx | 697 ++ website/docs/routes/traditional-media.md | 2794 ------- website/docs/routes/traditional-media.mdx | 2380 ++++++ website/docs/routes/travel.md | 192 - website/docs/routes/travel.mdx | 125 + website/docs/routes/university.md | 4044 --------- website/docs/routes/university.mdx | 3697 +++++++++ website/docs/support/README.md | 2 +- website/docs/usage.md | 26 +- website/docusaurus.config.js | 243 - website/docusaurus.config.ts | 247 + website/i18n/en/code.json | 31 +- website/i18n/zh/code.json | 31 +- .../current/README.md | 23 +- .../current/api.md | 10 +- .../current/faq.md | 4 +- .../current/install/README.md | 678 +- .../current/install/config.md | 635 ++ .../current/instances.mdx | 15 + .../current/joinus/advanced/advanced-feed.md | 2 +- .../joinus/advanced/script-standard.md | 2 +- .../current/joinus/advanced/use-cache.md | 2 +- .../current/joinus/new-radar.md | 4 +- .../current/joinus/new-rss/add-docs.md | 31 +- .../current/joinus/new-rss/before-start.md | 2 +- .../current/joinus/new-rss/start-code.md | 11 +- .../current/joinus/new-rss/submit-route.md | 41 +- .../current/joinus/quick-start.md | 6 +- .../current/parameter.md | 59 +- .../current/support/README.md | 2 +- .../current/usage.md | 25 +- website/package.json | 44 +- website/pnpm-lock.yaml | 6926 ++++++++-------- website/{sidebars.js => sidebars.mjs} | 4 +- website/src/components/Badge.tsx | 2 - website/src/components/CarbonAds/index.tsx | 40 +- website/src/components/InstanceList.tsx | 107 + website/src/components/Route.tsx | 55 +- website/src/css/custom.css | 45 +- .../DocSidebarItem/{index.js => index.jsx} | 1 - .../{MDXComponents.js => MDXComponents.ts} | 4 + website/src/theme/SearchBar/index.jsx | 4 +- .../SearchBar/{variables.css => style.css} | 0 website/tsconfig.json | 5 +- 3461 files changed, 72970 insertions(+), 87068 deletions(-) rename .github/workflows/{pr-deploy-route-test.yml => docker-test-cont.yml} (89%) rename .github/workflows/{pr-lint.yml => lint.yml} (54%) delete mode 100644 .github/workflows/yarn-lock-changes.yml rename lib/{api_router.js => api-router.js} (88%) rename lib/{core_router.js => core-router.js} (100%) rename lib/{protected_router.js => protected-router.js} (100%) delete mode 100644 lib/routes/1draw/index.js delete mode 100644 lib/routes/3ycy/home.js delete mode 100644 lib/routes/4gamers/category.js delete mode 100644 lib/routes/4gamers/tag.js delete mode 100644 lib/routes/4gamers/topic.js delete mode 100644 lib/routes/51voa/channel.js delete mode 100644 lib/routes/8btc/author.js delete mode 100644 lib/routes/8btc/news/flash.js delete mode 100644 lib/routes/91ddcc/stage.js delete mode 100644 lib/routes/91ddcc/user.js delete mode 100644 lib/routes/93/index.js delete mode 100644 lib/routes/abc/documentId.js delete mode 100644 lib/routes/abc/id.js delete mode 100644 lib/routes/abc/index.js delete mode 100644 lib/routes/aliyun-kernel/index.js delete mode 100644 lib/routes/allnow/column.js delete mode 100644 lib/routes/allnow/index.js delete mode 100644 lib/routes/allnow/tag.js delete mode 100644 lib/routes/allnow/user.js delete mode 100644 lib/routes/allnow/utils.js delete mode 100644 lib/routes/alter-cn/news.js delete mode 100644 lib/routes/amazon/ku.js delete mode 100644 lib/routes/andyt/index.js rename lib/routes/anigamer/{new_anime.js => new-anime.js} (100%) delete mode 100644 lib/routes/animen/news.js delete mode 100644 lib/routes/anitama/channel.js delete mode 100644 lib/routes/anki/changes.js delete mode 100644 lib/routes/aom/journal.js delete mode 100644 lib/routes/aptonic/action.js delete mode 100644 lib/routes/asahi/area.js delete mode 100644 lib/routes/atfd/index.js delete mode 100644 lib/routes/babehub/index.js delete mode 100644 lib/routes/babehub/search.js delete mode 100644 lib/routes/babehub/utils.js rename lib/routes/bahamut/{creation_index.js => creation-index.js} (77%) delete mode 100644 lib/routes/baidu/daily.js delete mode 100644 lib/routes/baidu/doodles.js delete mode 100644 lib/routes/bell-labs/events-news.js delete mode 100644 lib/routes/bihu/activaties.js delete mode 100644 lib/routes/biobio/index.js delete mode 100644 lib/routes/biobio/others.js delete mode 100644 lib/routes/bioon/latest.js delete mode 100644 lib/routes/bishijie/kuaixun.js delete mode 100644 lib/routes/bjnews/epaper.js delete mode 100644 lib/routes/blogs/diygod/animal-crossing.js delete mode 100644 lib/routes/blogs/diygod/gk.js delete mode 100644 lib/routes/blogs/jianning.js rename lib/routes/blogs/{jingwei_link.js => jingwei-link.js} (100%) delete mode 100644 lib/routes/blogs/paulgraham.js delete mode 100644 lib/routes/bof/home.js delete mode 100644 lib/routes/bookscomtw/newbooks.js delete mode 100644 lib/routes/booksource/index.js delete mode 100644 lib/routes/boston/index.js delete mode 100644 lib/routes/buaq/index.js delete mode 100644 lib/routes/cfd/gbp_div.js delete mode 100644 lib/routes/cgtn/pick.js delete mode 100644 lib/routes/cgtn/top.js delete mode 100644 lib/routes/changku/index.js delete mode 100644 lib/routes/chinadialogue/column.js delete mode 100644 lib/routes/chinadialogue/topics.js delete mode 100644 lib/routes/chinalaborwatch/reports.js delete mode 100644 lib/routes/chinatimes/index.js delete mode 100644 lib/routes/chuiniu/column.js delete mode 100644 lib/routes/chuiniu/column_list.js delete mode 100644 lib/routes/ciweimao/chapter.js delete mode 100644 lib/routes/cktest/app.js delete mode 100644 lib/routes/cktest/policy.js delete mode 100644 lib/routes/cmes/news.js delete mode 100644 lib/routes/codeceo/category.js delete mode 100644 lib/routes/codeceo/home.js delete mode 100644 lib/routes/commonapp/blog.js delete mode 100644 lib/routes/coronavirus/caixin.js delete mode 100644 lib/routes/coronavirus/dxy-data.js delete mode 100644 lib/routes/coronavirus/dxy.js delete mode 100644 lib/routes/coronavirus/mogov-2019ncov.js delete mode 100644 lib/routes/coronavirus/nhc.js delete mode 100644 lib/routes/coronavirus/scmp.js delete mode 100644 lib/routes/coronavirus/sg-moh.js delete mode 100644 lib/routes/coronavirus/yahoo-japan.js delete mode 100644 lib/routes/cpta/notice.js delete mode 100644 lib/routes/craigslist/search.js delete mode 100644 lib/routes/cria/news.js delete mode 100644 lib/routes/cs/news.js delete mode 100644 lib/routes/curseforge/files.js delete mode 100644 lib/routes/curseforge/generalfiles.js delete mode 100644 lib/routes/cve/search.js delete mode 100644 lib/routes/d1bz/novel.js delete mode 100644 lib/routes/d2/daily.js delete mode 100644 lib/routes/damai/activity.js delete mode 100644 lib/routes/dayone/blog.js delete mode 100644 lib/routes/deeplearningai/thebatch.js delete mode 100644 lib/routes/dengekionline/new.js rename lib/routes/dgtle/{whale_rank.js => whale-rank.js} (100%) delete mode 100644 lib/routes/dhl/shipment-tracking.js delete mode 100644 lib/routes/dianping/user.js delete mode 100755 lib/routes/digic-pictures/index.js delete mode 100644 lib/routes/dilbert/strip.js delete mode 100644 lib/routes/dilidili/fanju.js delete mode 100644 lib/routes/dockone/weekly.js delete mode 100644 lib/routes/donews/index.js delete mode 100644 lib/routes/donews/utils.js delete mode 100644 lib/routes/dsb/area.js delete mode 100644 lib/routes/dxy/vaccine.js delete mode 100644 lib/routes/dysfz/index.js delete mode 100644 lib/routes/egsea/flash.js delete mode 100644 lib/routes/eleme/open-be/announce.js delete mode 100644 lib/routes/elife/index.js delete mode 100644 lib/routes/elitebabes/index.js delete mode 100644 lib/routes/elitebabes/search.js delete mode 100644 lib/routes/elitebabes/utils.js delete mode 100644 lib/routes/elitebabes/videos.js delete mode 100644 lib/routes/esquirehk/tag.js delete mode 100644 lib/routes/facebook/page.js rename lib/routes/fanfou/{home_timeline.js => home-timeline.js} (86%) rename lib/routes/fanfou/{public_timeline.js => public-timeline.js} (86%) rename lib/routes/fanfou/{user_timeline.js => user-timeline.js} (87%) delete mode 100644 lib/routes/feed-the-beast/modpack.js delete mode 100644 lib/routes/feixuew/index.js delete mode 100644 lib/routes/fir/update.js delete mode 100644 lib/routes/firefox/release.js delete mode 100644 lib/routes/fitchratings/site.js delete mode 100644 lib/routes/fjnews/fznews.js delete mode 100644 lib/routes/fjnews/jjnews.js delete mode 100644 lib/routes/fontstory/tw.js delete mode 100644 lib/routes/fulinian/index.js rename lib/routes/furaffinity/{journal_comments.js => journal-comments.js} (100%) rename lib/routes/furaffinity/{submission_comments.js => submission-comments.js} (100%) delete mode 100644 lib/routes/gab/explore.js delete mode 100644 lib/routes/galgame/hhgal.js delete mode 100644 lib/routes/galgame/sayhuahuo.js delete mode 100644 lib/routes/galgame/zdfx.js delete mode 100644 lib/routes/gamegrape/index.js delete mode 100644 lib/routes/gaoqingla/latest.js delete mode 100644 lib/routes/gbcc/trust.js delete mode 100644 lib/routes/geektime/news.js delete mode 100644 lib/routes/getitfree/category.js delete mode 100644 lib/routes/getitfree/search.js delete mode 100644 lib/routes/getitfree/utils.js delete mode 100644 lib/routes/girlimg/album.js delete mode 100644 lib/routes/gitchat/newest.js delete mode 100644 lib/routes/gitea/blog.js rename lib/routes/gov/city/nanjing/{getContent.js => get-content.js} (96%) delete mode 100644 lib/routes/gov/moa/sjzxfb.js rename lib/routes/gov/province/jiangsu/{getContent.js => get-content.js} (97%) delete mode 100644 lib/routes/gov/suzhou/doc.js delete mode 100644 lib/routes/gov/suzhou/news.js delete mode 100644 lib/routes/gov/suzhou/utils.js delete mode 100644 lib/routes/gracg/user.js delete mode 100644 lib/routes/gradcafe/result.js delete mode 100644 lib/routes/growincity/news.js delete mode 100644 lib/routes/grubstreet/utils.js delete mode 100644 lib/routes/guiltfree/onsale.js delete mode 100644 lib/routes/gvm/index.js delete mode 100644 lib/routes/haimaoba/comics.js delete mode 100644 lib/routes/hentai-cosplays/hentai-cosplays.js delete mode 100644 lib/routes/hkcd/pdf.js delete mode 100644 lib/routes/hkcnews/news.js delete mode 100644 lib/routes/hkgolden/index.js delete mode 100644 lib/routes/hopper/index.js delete mode 100644 lib/routes/hpoi/index.js delete mode 100644 lib/routes/hpoi/user.js delete mode 100644 lib/routes/huawei/xinsheng/index.js delete mode 100644 lib/routes/hugo/releases.js delete mode 100644 lib/routes/iea/index.js delete mode 100644 lib/routes/iie/blog.js delete mode 100644 lib/routes/imaijia/category.js delete mode 100644 lib/routes/imuseum/index.js delete mode 100644 lib/routes/indienova/article.js delete mode 100644 lib/routes/instapaper/person.js rename lib/routes/interesting-sky/{astronomical_events.js => astronomical-events.js} (100%) delete mode 100644 lib/routes/itjuzi/invest.js delete mode 100644 lib/routes/itjuzi/merge.js delete mode 100644 lib/routes/itslide/new.js delete mode 100644 lib/routes/iyiou/index.js delete mode 100644 lib/routes/iyouport/index.js delete mode 100644 lib/routes/iyouport/utils.js delete mode 100644 lib/routes/jdlingyu/index.js delete mode 100644 lib/routes/jianshu/trending.js delete mode 100644 lib/routes/jiazhen108/index.js delete mode 100644 lib/routes/jijitang/article.js delete mode 100644 lib/routes/jijitang/publication.js delete mode 100644 lib/routes/jingdong/zhongchou.js delete mode 100644 lib/routes/jinritoutiao/keyword.js delete mode 100644 lib/routes/jinse/catalogue.js delete mode 100644 lib/routes/jinse/lives.js delete mode 100644 lib/routes/jinse/timeline.js delete mode 100644 lib/routes/jskou/index.js delete mode 100644 lib/routes/juesheng/index.js delete mode 100644 lib/routes/justrun/index.js delete mode 100644 lib/routes/kaggle/competitions.js delete mode 100644 lib/routes/kaggle/discussion.js delete mode 100644 lib/routes/kaggle/user.js delete mode 100644 lib/routes/kirara/news.js rename lib/routes/konachan/{post_popular_recent.js => post-popular-recent.js} (100%) delete mode 100644 lib/routes/konami/pesmobile.js delete mode 100644 lib/routes/krankenkassen/index.js delete mode 100644 lib/routes/ku/index.js delete mode 100644 lib/routes/kzfeed/topic.js delete mode 100644 lib/routes/leboncoin/ad.js delete mode 100644 lib/routes/leetcode/check-cn.js delete mode 100644 lib/routes/leetcode/check-us.js delete mode 100644 lib/routes/leetcode/utils.js delete mode 100644 lib/routes/liequtv/room.js delete mode 100644 lib/routes/lingyi/index.js delete mode 100644 lib/routes/linkedkeeper/index.js delete mode 100644 lib/routes/liquipedia/dota2_matches.js delete mode 100644 lib/routes/liwushuo/index.js delete mode 100644 lib/routes/loveheaven/update.js delete mode 100644 lib/routes/lowendtalk/discussion.js delete mode 100644 lib/routes/macau-bolsas/index.js delete mode 100644 lib/routes/macked/app.js delete mode 100644 lib/routes/macwk/soft.js rename lib/routes/magireco/{event_banner.js => event-banner.js} (95%) delete mode 100644 lib/routes/makeuseof/index.js delete mode 100644 lib/routes/manhuadui/manhua.js delete mode 100644 lib/routes/manong-weekly/issues.js rename lib/routes/maoyan/{hotComplete.js => hot-complete.js} (91%) delete mode 100644 lib/routes/matataki/site/posts/author.js delete mode 100644 lib/routes/matataki/site/posts/favorite.js delete mode 100644 lib/routes/matataki/site/posts/scoreranking.js delete mode 100644 lib/routes/matataki/site/posts/tag.js delete mode 100644 lib/routes/matataki/site/posts/timeranking.js delete mode 100644 lib/routes/matataki/site/posts/token.js delete mode 100644 lib/routes/matataki/utils/matataki-utils.js delete mode 100644 lib/routes/maxnews/dota2.js delete mode 100644 lib/routes/meituan/tech/home.js delete mode 100644 lib/routes/mercari/index.js delete mode 100644 lib/routes/metred/fuli.js delete mode 100644 lib/routes/mi/board.js delete mode 100644 lib/routes/mind42/index.js delete mode 100644 lib/routes/mind42/search.js delete mode 100644 lib/routes/mind42/tag.js delete mode 100644 lib/routes/mind42/utils.js delete mode 100644 lib/routes/mingjian/index.js delete mode 100644 lib/routes/miniapp/article.js delete mode 100644 lib/routes/miniapp/store/newest.js rename lib/routes/miniflux/{get_entries.js => get-entries.js} (93%) rename lib/routes/miniflux/{get_feeds.js => get-feeds.js} (94%) delete mode 100644 lib/routes/mitbbs/index.js delete mode 100644 lib/routes/mitre/publications.js delete mode 100644 lib/routes/mittrchina/index.js delete mode 100644 lib/routes/mlhang/latest.js delete mode 100644 lib/routes/mlog-club/projects.js delete mode 100644 lib/routes/mlog-club/topics.js delete mode 100644 lib/routes/mobdata/report.js delete mode 100644 lib/routes/moxingnet/index.js delete mode 100644 lib/routes/mp4ba/index.js delete mode 100644 lib/routes/mqube/latest.js delete mode 100644 lib/routes/mqube/tag.js delete mode 100644 lib/routes/mqube/top.js delete mode 100644 lib/routes/mqube/user.js delete mode 100644 lib/routes/mzitu/category.js delete mode 100644 lib/routes/mzitu/home.js delete mode 100644 lib/routes/mzitu/post.js delete mode 100644 lib/routes/mzitu/tag.js delete mode 100644 lib/routes/mzitu/tags.js delete mode 100644 lib/routes/mzitu/util.js delete mode 100644 lib/routes/namoc/announcement.js delete mode 100644 lib/routes/namoc/exhibition.js delete mode 100644 lib/routes/namoc/media.js delete mode 100644 lib/routes/namoc/news.js delete mode 100644 lib/routes/namoc/specials.js delete mode 100644 lib/routes/nba/app_news.js delete mode 100644 lib/routes/network360/jobs.js delete mode 100644 lib/routes/newsmth/account.js delete mode 100644 lib/routes/newsmth/section.js delete mode 100644 lib/routes/nfmovies/index.js delete mode 100644 lib/routes/ningmeng/song.js delete mode 100644 lib/routes/northhouse/index.js delete mode 100644 lib/routes/novel/axdzs.js delete mode 100644 lib/routes/novel/booksky.js delete mode 100644 lib/routes/novel/dcrsw.js delete mode 100644 lib/routes/novel/ptwxz.js delete mode 100644 lib/routes/novel/wenxuemi.js delete mode 100644 lib/routes/novel/zhaishuyuan.js delete mode 100644 lib/routes/npc/index.js delete mode 100644 lib/routes/obsidian/announcements.js delete mode 100644 lib/routes/or/index.js delete mode 100644 lib/routes/ow/patch.js delete mode 100644 lib/routes/partnershiponai/resources.js delete mode 100644 lib/routes/pcr/news.js delete mode 100644 lib/routes/pgyer/app.js delete mode 100644 lib/routes/piaohua/hot.js delete mode 100644 lib/routes/pintu360/index.js delete mode 100644 lib/routes/plainlaw/archives.js delete mode 100644 lib/routes/pmcaff/user.js delete mode 100644 lib/routes/pocket/trending.js delete mode 100644 lib/routes/polar/blog.js delete mode 100644 lib/routes/popiask/questions.js delete mode 100644 lib/routes/popiask/tapechat_questions.js delete mode 100644 lib/routes/popyard/index.js delete mode 100644 lib/routes/processon/popular.js delete mode 100644 lib/routes/ps/list.js delete mode 100755 lib/routes/ps/product.js delete mode 100644 lib/routes/ps/ps4updates.js delete mode 100644 lib/routes/putonghua/hangzhou.js delete mode 100644 lib/routes/qlwb/city.js delete mode 100644 lib/routes/qlwb/news.js delete mode 100644 lib/routes/qtfyfl/category.js delete mode 100644 lib/routes/questmobile/report.js delete mode 100644 lib/routes/rs05/rs05.js delete mode 100644 lib/routes/s-hentai/index.js delete mode 100644 lib/routes/sagawa/index.js delete mode 100644 lib/routes/sankakucomplex/post.js delete mode 100644 lib/routes/sans/summit_archive.js delete mode 100644 lib/routes/sbs/chinese.js delete mode 100644 lib/routes/scala-blog/scala-blog.js delete mode 100644 lib/routes/sckjt/news.js delete mode 100644 lib/routes/scoresaber/user.js rename lib/routes/sesame/{release_notes.js => release-notes.js} (100%) delete mode 100644 lib/routes/sf/sffq-announce.js delete mode 100644 lib/routes/shengwugu/index.js delete mode 100644 lib/routes/shinybbs/index.js delete mode 100644 lib/routes/shinybbs/latest.js delete mode 100644 lib/routes/shinybbs/p.js delete mode 100644 lib/routes/showroom/room.js delete mode 100644 lib/routes/shuax/project.js delete mode 100644 lib/routes/shuhui/comics.js delete mode 100644 lib/routes/sixthtone/news.js delete mode 100644 lib/routes/socialbeta/hunt.js delete mode 100644 lib/routes/soomal/topics.js delete mode 100644 lib/routes/stork/keyword.js delete mode 100644 lib/routes/tahui/rptlist.js delete mode 100644 lib/routes/tam/forecast.js delete mode 100644 lib/routes/tanwu/products.js delete mode 100644 lib/routes/tencent/bigdata/index.js delete mode 100644 lib/routes/tencent/bugly/changelog.js delete mode 100644 lib/routes/tencent/egame/room.js delete mode 100644 lib/routes/tencent/gameinstitute/community.js delete mode 100644 lib/routes/tencent/guyu/channel.js delete mode 100644 lib/routes/tencent/video/playlist.js delete mode 100644 lib/routes/tencent/wechat/miniprogram/plugins.js delete mode 100644 lib/routes/thrillist/index.js delete mode 100644 lib/routes/tianya/comments.js delete mode 100644 lib/routes/tianya/index.js delete mode 100644 lib/routes/tianya/user.js delete mode 100644 lib/routes/tssstatus/index.js delete mode 100644 lib/routes/tuicool/mags.js delete mode 100644 lib/routes/uisdc/news.js delete mode 100644 lib/routes/uisdc/talk.js delete mode 100644 lib/routes/uisdc/topic.js delete mode 100644 lib/routes/uisdc/zt.js delete mode 100644 lib/routes/uniqlo/stylingbook.js delete mode 100644 lib/routes/universities/buaa/news/index.js delete mode 100644 lib/routes/universities/buaa/utils.js rename lib/routes/universities/fzu/{news_min.js => news-min.js} (100%) rename lib/routes/universities/hhu/{libNews.js => lib-news.js} (78%) rename lib/routes/universities/hhu/{libNewsc.js => lib-newsc.js} (72%) rename lib/routes/universities/swust/{jwc_news.js => jwc-news.js} (96%) rename lib/routes/universities/swust/{jwc_notice.js => jwc-notice.js} (96%) rename lib/routes/us/supremecourt/{argument_audio.js => argument-audio.js} (79%) delete mode 100644 lib/routes/vgtime/keyword.js delete mode 100644 lib/routes/vgtime/news.js delete mode 100644 lib/routes/vgtime/release.js delete mode 100644 lib/routes/voa/day-photos.js delete mode 100644 lib/routes/vuevideo/user.js delete mode 100644 lib/routes/wanwansub/index.js delete mode 100644 lib/routes/wanwansub/info.js delete mode 100644 lib/routes/weforum/report.js delete mode 100644 lib/routes/weseepro/circle.js delete mode 100644 lib/routes/weseepro/newest-direct.js delete mode 100644 lib/routes/weseepro/newest.js delete mode 100644 lib/routes/westore/new.js delete mode 100644 lib/routes/whalegogo/home.js delete mode 100644 lib/routes/whalegogo/portal.js delete mode 100644 lib/routes/wikihow/category.js delete mode 100644 lib/routes/wikihow/index.js delete mode 100644 lib/routes/wineyun/index.js delete mode 100644 lib/routes/wired/tag.js delete mode 100644 lib/routes/wolley/host.js delete mode 100644 lib/routes/wolley/index.js delete mode 100644 lib/routes/wolley/user.js delete mode 100644 lib/routes/wukong/user.js delete mode 100644 lib/routes/x-mol/news.js delete mode 100644 lib/routes/x-mol/paper.js delete mode 100644 lib/routes/x-mol/utils.js delete mode 100644 lib/routes/xici/index.js delete mode 100644 lib/routes/xinquji/internal.js delete mode 100644 lib/routes/xinquji/today.js delete mode 100644 lib/routes/xposed/module.js rename lib/routes/xuetangx/{course_info.js => course-info.js} (100%) rename lib/routes/xuetangx/{course_list.js => course-list.js} (92%) delete mode 100644 lib/routes/yahoo-jp-tv/index.js rename lib/routes/yande.re/{post_popular_recent.js => post-popular-recent.js} (100%) delete mode 100644 lib/routes/yicas/blog.js delete mode 100644 lib/routes/yidoutang/index.js delete mode 100644 lib/routes/zcfy/hot.js delete mode 100644 lib/routes/zcfy/index.js delete mode 100644 lib/routes/zfrontier/board_postlist.js delete mode 100644 lib/routes/zfrontier/postlist.js delete mode 100644 lib/routes/zhanqi/room.js delete mode 100644 lib/routes/zhilian/index.js delete mode 100644 lib/routes/zhutix/latest.js delete mode 100644 lib/routes/zimuxia/portfolio.js delete mode 100644 lib/routes/zimuzu/resource.js delete mode 100644 lib/routes/zimuzu/top.js delete mode 100644 lib/routes/ziroom/room.js delete mode 100644 lib/routes/zzz/index.js rename lib/utils/{dateParser.js => date-parser.js} (77%) create mode 100644 lib/utils/pac-proxy.js create mode 100644 lib/v2/1lou/index.js create mode 100644 lib/v2/1lou/maintainer.js create mode 100644 lib/v2/1lou/radar.js create mode 100644 lib/v2/1lou/router.js delete mode 100644 lib/v2/2047/index.js delete mode 100644 lib/v2/2047/maintainer.js delete mode 100644 lib/v2/2047/radar.js delete mode 100644 lib/v2/2047/router.js delete mode 100644 lib/v2/35photo/actual.js delete mode 100644 lib/v2/35photo/author.js delete mode 100644 lib/v2/35photo/genre.js delete mode 100644 lib/v2/35photo/interesting.js delete mode 100644 lib/v2/35photo/maintainer.js delete mode 100644 lib/v2/35photo/map.js delete mode 100644 lib/v2/35photo/new.js delete mode 100644 lib/v2/35photo/radar.js delete mode 100644 lib/v2/35photo/router.js delete mode 100644 lib/v2/35photo/utils.js rename lib/v2/3dmgame/{news_center.js => news-center.js} (95%) create mode 100644 lib/v2/4gamers/category.js create mode 100644 lib/v2/4gamers/maintainer.js create mode 100644 lib/v2/4gamers/radar.js create mode 100644 lib/v2/4gamers/router.js create mode 100644 lib/v2/4gamers/tag.js create mode 100644 lib/v2/4gamers/templates/description.art create mode 100644 lib/v2/4gamers/templates/image.art create mode 100644 lib/v2/4gamers/topic.js create mode 100644 lib/v2/4gamers/utils.js rename lib/v2/500px/{tribeSet.js => tribe-set.js} (94%) create mode 100644 lib/v2/56kog/class.js create mode 100644 lib/v2/56kog/maintainer.js create mode 100644 lib/v2/56kog/radar.js create mode 100644 lib/v2/56kog/router.js create mode 100644 lib/v2/56kog/templates/description.art create mode 100644 lib/v2/56kog/top.js create mode 100644 lib/v2/56kog/util.js rename lib/v2/6v123/{latestMovies.js => latest-movies.js} (100%) rename lib/v2/6v123/{latestTVSeries.js => latest-tvseries.js} (100%) create mode 100644 lib/v2/abc/index.js rename lib/v2/{ezone => abc}/maintainer.js (100%) create mode 100644 lib/v2/abc/radar.js create mode 100644 lib/v2/abc/router.js create mode 100644 lib/v2/abc/templates/description.art create mode 100644 lib/v2/acpaa/index.js create mode 100644 lib/v2/acpaa/maintainer.js create mode 100644 lib/v2/acpaa/radar.js create mode 100644 lib/v2/acpaa/router.js rename lib/v2/agirls/{topic_list.js => topic-list.js} (70%) rename lib/v2/aliyun/{database_month.js => database-month.js} (100%) delete mode 100644 lib/v2/aliyundrive/files.js delete mode 100644 lib/v2/aliyundrive/maintainer.js delete mode 100644 lib/v2/aliyundrive/radar.js delete mode 100644 lib/v2/aliyundrive/router.js delete mode 100644 lib/v2/allrecode/index.js delete mode 100644 lib/v2/allrecode/maintainer.js delete mode 100644 lib/v2/allrecode/news.js delete mode 100644 lib/v2/allrecode/radar.js delete mode 100644 lib/v2/allrecode/router.js rename lib/v2/apple/{exchange_repair.js => exchange-repair.js} (100%) delete mode 100644 lib/v2/appledaily/index.js delete mode 100644 lib/v2/appledaily/maintainer.js delete mode 100644 lib/v2/appledaily/radar.js delete mode 100644 lib/v2/appstore/gofans.js create mode 100644 lib/v2/artstation/maintainer.js create mode 100644 lib/v2/artstation/radar.js create mode 100644 lib/v2/artstation/router.js create mode 100644 lib/v2/artstation/templates/description.art create mode 100644 lib/v2/artstation/user.js delete mode 100644 lib/v2/ash-maurya/index.js delete mode 100644 lib/v2/ash-maurya/maintainer.js delete mode 100644 lib/v2/ash-maurya/radar.js delete mode 100644 lib/v2/ash-maurya/router.js delete mode 100644 lib/v2/audiobar/latest.js delete mode 100644 lib/v2/audiobar/maintainer.js delete mode 100644 lib/v2/audiobar/radar.js create mode 100644 lib/v2/auto-stats/index.js rename lib/v2/{filmdeepfocus => auto-stats}/maintainer.js (100%) create mode 100644 lib/v2/auto-stats/radar.js create mode 100644 lib/v2/auto-stats/router.js create mode 100644 lib/v2/backlinko/blog.js create mode 100644 lib/v2/backlinko/maintainer.js create mode 100644 lib/v2/backlinko/radar.js rename lib/v2/{disinformationindex => backlinko}/router.js (60%) create mode 100644 lib/v2/baidu/search.js create mode 100644 lib/v2/baidu/templates/description.art delete mode 100644 lib/v2/baijing/index.js delete mode 100644 lib/v2/baijing/maintainer.js delete mode 100644 lib/v2/baijing/radar.js delete mode 100644 lib/v2/baijing/router.js create mode 100644 lib/v2/bangumi/tv/other/followrank.js create mode 100644 lib/v2/bangumi/tv/user/wish.js delete mode 100644 lib/v2/bilibili/blackboard.js delete mode 100644 lib/v2/bilibili/channel.js rename lib/v2/bilibili/{followings_article.js => followings-article.js} (89%) rename lib/v2/bilibili/{followings_dynamic.js => followings-dynamic.js} (65%) rename lib/v2/bilibili/{followings_video.js => followings-video.js} (81%) rename lib/v2/bilibili/{hotSearch.js => hot-search.js} (86%) rename lib/v2/bilibili/{linkNews.js => link-news.js} (90%) rename lib/v2/bilibili/{liveArea.js => live-area.js} (100%) rename lib/v2/bilibili/{liveRoom.js => live-room.js} (96%) rename lib/v2/bilibili/{liveSearch.js => live-search.js} (94%) rename lib/v2/bilibili/{mallIP.js => mall-ip.js} (100%) rename lib/v2/bilibili/{mallNew.js => mall-new.js} (97%) rename lib/v2/bilibili/{manga_followings.js => manga-followings.js} (88%) rename lib/v2/bilibili/{manga_update.js => manga-update.js} (100%) delete mode 100644 lib/v2/bilibili/online.js create mode 100644 lib/v2/bilibili/platform.js delete mode 100644 lib/v2/bilibili/topic.js rename lib/v2/bilibili/{user_bangumi.js => user-bangumi.js} (83%) rename lib/v2/bilibili/{userChannel.js => user-channel.js} (95%) rename lib/v2/bilibili/{userCollection.js => user-collection.js} (94%) rename lib/v2/bilibili/{userFav.js => user-fav.js} (84%) rename lib/v2/bilibili/{weekly_recommend.js => weekly-recommend.js} (94%) rename lib/{routes/bing/index.js => v2/bing/daily-wallpaper.js} (78%) create mode 100644 lib/v2/bing/maintainer.js create mode 100644 lib/v2/bing/radar.js create mode 100644 lib/v2/bing/router.js create mode 100644 lib/v2/bing/search.js delete mode 100644 lib/v2/blockbeats/index.js delete mode 100644 lib/v2/blockbeats/router.js create mode 100644 lib/v2/booru/maintainer.js create mode 100644 lib/v2/booru/mmda.js create mode 100644 lib/v2/booru/radar.js create mode 100644 lib/v2/booru/router.js create mode 100644 lib/v2/bossdesign/index.js create mode 100644 lib/v2/bossdesign/maintainer.js create mode 100644 lib/v2/bossdesign/radar.js rename lib/v2/{dbmv => bossdesign}/router.js (100%) create mode 100644 lib/v2/bsky/posts.js create mode 100644 lib/v2/bsky/templates/post.art create mode 100644 lib/v2/bsky/utils.js create mode 100644 lib/v2/buaa/maintainer.js create mode 100644 lib/v2/buaa/news/index.js create mode 100644 lib/v2/buaa/radar.js create mode 100755 lib/v2/buaa/router.js create mode 100755 lib/v2/buaa/sme.js create mode 100644 lib/v2/bulianglin/bulianglin.js rename lib/v2/{good => bulianglin}/maintainer.js (50%) create mode 100644 lib/v2/bulianglin/radar.js create mode 100644 lib/v2/bulianglin/router.js create mode 100644 lib/v2/caam/index.js rename lib/v2/{lianxh => caam}/maintainer.js (100%) create mode 100644 lib/v2/caam/radar.js create mode 100644 lib/v2/caam/router.js create mode 100644 lib/v2/caixinglobal/latest.js create mode 100644 lib/v2/caixinglobal/maintainer.js create mode 100644 lib/v2/caixinglobal/radar.js rename lib/v2/{audiobar => caixinglobal}/router.js (57%) create mode 100644 lib/v2/cas/genetics/index.js delete mode 100644 lib/v2/cas/genetics/xshd.js delete mode 100644 lib/v2/cctv/special.js rename lib/v2/cdzjryb/{projectList.js => project-list.js} (100%) create mode 100644 lib/v2/cfachina/analygarden.js create mode 100644 lib/v2/cfachina/maintainer.js create mode 100644 lib/v2/cfachina/radar.js create mode 100644 lib/v2/cfachina/router.js create mode 100644 lib/v2/cfmmc/index.js create mode 100644 lib/v2/cfmmc/maintainer.js create mode 100644 lib/v2/cfmmc/radar.js create mode 100644 lib/v2/cfmmc/router.js create mode 100644 lib/v2/china/finance/finance.js delete mode 100644 lib/v2/chinacef/experts.js delete mode 100644 lib/v2/chinacef/hot.js delete mode 100644 lib/v2/chinacef/index.js delete mode 100644 lib/v2/chinacef/maintainer.js delete mode 100644 lib/v2/chinacef/radar.js delete mode 100644 lib/v2/chinacef/router.js delete mode 100644 lib/v2/chinacef/templates/description.art delete mode 100644 lib/v2/chinacef/utils.js delete mode 100644 lib/v2/chinafactcheck/templates/description.art create mode 100644 lib/v2/chinaisa/index.js create mode 100644 lib/v2/chinaisa/maintainer.js create mode 100644 lib/v2/chinaisa/radar.js create mode 100644 lib/v2/chinaisa/router.js create mode 100644 lib/v2/chinamoney/channels.js create mode 100644 lib/v2/chinamoney/maintainer.js create mode 100644 lib/v2/chinamoney/notice.js create mode 100644 lib/v2/chinamoney/radar.js create mode 100644 lib/v2/chinamoney/router.js create mode 100644 lib/v2/chuanliu/maintainer.js create mode 100644 lib/v2/chuanliu/nice.js create mode 100644 lib/v2/chuanliu/radar.js create mode 100644 lib/v2/chuanliu/router.js create mode 100644 lib/v2/chuanliu/templates/description.art create mode 100644 lib/v2/cib/maintainer.js create mode 100644 lib/v2/cib/radar.js create mode 100644 lib/v2/cib/router.js create mode 100644 lib/v2/cib/whpj.js delete mode 100644 lib/v2/clash/maintainer.js delete mode 100644 lib/v2/clash/premium.js delete mode 100644 lib/v2/clash/radar.js delete mode 100644 lib/v2/clash/router.js create mode 100644 lib/v2/cma/channel.js create mode 100644 lib/v2/cma/maintainer.js create mode 100644 lib/v2/cma/radar.js create mode 100644 lib/v2/cma/router.js create mode 100644 lib/v2/cma/templates/description.art create mode 100644 lib/v2/cmpxchg8b/articles.js create mode 100644 lib/v2/cmpxchg8b/maintainer.js create mode 100644 lib/v2/cmpxchg8b/radar.js create mode 100644 lib/v2/cmpxchg8b/router.js create mode 100644 lib/v2/cna/web/index.js create mode 100644 lib/v2/cnljxh/index.js create mode 100644 lib/v2/cnljxh/maintainer.js create mode 100644 lib/v2/cnljxh/radar.js create mode 100644 lib/v2/cnljxh/router.js rename lib/v2/codeforces/{recent_actions.js => recent-actions.js} (100%) create mode 100644 lib/v2/consumer/shopping-guide.js rename lib/v2/coolapk/{userDynamic.js => user-dynamic.js} (83%) create mode 100644 lib/v2/cs/index.js create mode 100644 lib/v2/cs/maintainer.js create mode 100644 lib/v2/cs/radar.js create mode 100644 lib/v2/cs/router.js create mode 100644 lib/v2/cs/video.js delete mode 100644 lib/v2/csc/maintainer.js delete mode 100644 lib/v2/csc/notice.js delete mode 100644 lib/v2/csc/radar.js delete mode 100644 lib/v2/csc/router.js delete mode 100644 lib/v2/cscse/maintainer.js delete mode 100644 lib/v2/cscse/radar.js delete mode 100644 lib/v2/cscse/router.js delete mode 100644 lib/v2/cscse/tzgg.js create mode 100644 lib/v2/damai/activity.js create mode 100644 lib/v2/damai/maintainer.js create mode 100644 lib/v2/damai/radar.js create mode 100644 lib/v2/damai/router.js create mode 100644 lib/v2/damai/templates/activity.art delete mode 100644 lib/v2/dbmv/index.js delete mode 100644 lib/v2/dbmv/maintainer.js delete mode 100644 lib/v2/dbmv/radar.js create mode 100644 lib/v2/deeplearning/maintainer.js create mode 100644 lib/v2/deeplearning/radar.js create mode 100644 lib/v2/deeplearning/router.js create mode 100644 lib/v2/deeplearning/thebatch.js delete mode 100644 lib/v2/diandong/ddh.js delete mode 100644 lib/v2/disinformationindex/blog.js delete mode 100644 lib/v2/disinformationindex/maintainer.js delete mode 100644 lib/v2/disinformationindex/radar.js delete mode 100644 lib/v2/disinformationindex/research.js create mode 100644 lib/v2/dol/announce.js create mode 100644 lib/v2/dol/maintainer.js create mode 100644 lib/v2/dol/radar.js create mode 100644 lib/v2/dol/router.js rename lib/v2/dongqiudi/{player_news.js => player-news.js} (100%) rename lib/v2/dongqiudi/{team_news.js => team-news.js} (100%) rename lib/v2/dongqiudi/{top_news.js => top-news.js} (91%) create mode 100644 lib/v2/douban/other/discussion.js rename lib/v2/douban/other/{explore_column.js => explore-column.js} (97%) rename lib/v2/douban/other/{latest_book.js => latest-book.js} (100%) rename lib/v2/douban/other/{latest_music.js => latest-music.js} (100%) create mode 100644 lib/v2/douban/other/recommended.js rename lib/v2/douban/other/{weekly_best.js => weekly-best.js} (100%) delete mode 100644 lib/v2/eastday/find.js delete mode 100644 lib/v2/economist/download.js delete mode 100644 lib/v2/economist/gre-vocabulary.js delete mode 100644 lib/v2/eet-china/maintainer.js delete mode 100644 lib/v2/eet-china/mp/index.js delete mode 100644 lib/v2/eet-china/mp/tags.js delete mode 100644 lib/v2/eet-china/mp/util.js delete mode 100644 lib/v2/eet-china/radar.js delete mode 100644 lib/v2/eet-china/router.js create mode 100644 lib/v2/egsea/flash.js create mode 100644 lib/v2/egsea/maintainer.js create mode 100644 lib/v2/egsea/radar.js create mode 100644 lib/v2/egsea/router.js create mode 100644 lib/v2/ekantipur/issue.js create mode 100644 lib/v2/ekantipur/maintainer.js create mode 100644 lib/v2/ekantipur/radar.js create mode 100644 lib/v2/ekantipur/router.js rename lib/v2/embassy/{supportedList.js => supported-list.js} (100%) delete mode 100644 lib/v2/ems/apple.js delete mode 100644 lib/v2/ems/maintainer.js delete mode 100644 lib/v2/ems/news.js delete mode 100644 lib/v2/ems/radar.js delete mode 100644 lib/v2/ems/router.js delete mode 100644 lib/v2/ems/templates/apple.art delete mode 100644 lib/v2/ezone/index.js delete mode 100644 lib/v2/ezone/radar.js create mode 100644 lib/v2/fansly/maintainer.js create mode 100644 lib/v2/fansly/post.js create mode 100644 lib/v2/fansly/radar.js create mode 100644 lib/v2/fansly/router.js create mode 100644 lib/v2/fansly/tag.js create mode 100644 lib/v2/fansly/templates/media.art create mode 100644 lib/v2/fansly/templates/poll.art create mode 100644 lib/v2/fansly/templates/tip-goal.art create mode 100644 lib/v2/fansly/utils.js create mode 100644 lib/v2/farmatters/index.js create mode 100644 lib/v2/farmatters/maintainer.js create mode 100644 lib/v2/farmatters/radar.js create mode 100644 lib/v2/farmatters/router.js create mode 100644 lib/v2/farmatters/templates/description.art rename lib/v2/ff14/{ff14_global.js => ff14-global.js} (88%) rename lib/v2/ff14/{ff14_zh.js => ff14-zh.js} (100%) delete mode 100644 lib/v2/filmdeepfocus/index.js delete mode 100644 lib/v2/filmdeepfocus/radar.js delete mode 100644 lib/v2/filmdeepfocus/router.js rename lib/v2/finology/{mostViewed.js => most-viewed.js} (100%) rename lib/{routes => v2}/firefox/addons.js (91%) create mode 100644 lib/v2/firefox/breaches.js delete mode 100644 lib/v2/firefox/index.js create mode 100644 lib/v2/firefox/release.js rename lib/v2/{pmthinking => free}/maintainer.js (50%) create mode 100644 lib/v2/free/radar.js rename lib/v2/{secnews => free}/router.js (50%) create mode 100644 lib/v2/free/rss.js create mode 100644 lib/v2/fuliba/latest.js create mode 100644 lib/v2/fuliba/maintainer.js create mode 100644 lib/v2/fuliba/radar.js rename lib/v2/{meituclub => fuliba}/router.js (57%) create mode 100644 lib/v2/fxiaoke/crm.js create mode 100644 lib/v2/fxiaoke/maintainer.js create mode 100644 lib/v2/fxiaoke/radar.js create mode 100644 lib/v2/fxiaoke/router.js rename lib/v2/gamer/{gnn_index.js => gnn-index.js} (90%) delete mode 100644 lib/v2/gaze/maintainer.js delete mode 100644 lib/v2/gaze/radar.js delete mode 100644 lib/v2/gaze/router.js delete mode 100644 lib/v2/gaze/templates/update.art delete mode 100644 lib/v2/gaze/update.js create mode 100644 lib/v2/gdsrx/index.js create mode 100644 lib/v2/gdsrx/maintainer.js create mode 100644 lib/v2/gdsrx/radar.js create mode 100644 lib/v2/gdsrx/router.js rename lib/v2/gdut/{oa_news.js => oa-news.js} (80%) create mode 100644 lib/v2/getitfree/util.js rename lib/v2/github/{starred_repos.js => starred-repos.js} (92%) create mode 100644 lib/v2/gocn/news.js delete mode 100644 lib/v2/gocn/util.js create mode 100644 lib/v2/gocn/utils.js create mode 100644 lib/v2/gofans/index.js create mode 100644 lib/v2/gofans/maintainer.js create mode 100644 lib/v2/gofans/radar.js create mode 100644 lib/v2/gofans/router.js create mode 100644 lib/v2/gofans/templates/description.art create mode 100644 lib/v2/gogoanimehd/maintainer.js create mode 100644 lib/v2/gogoanimehd/radar.js create mode 100644 lib/v2/gogoanimehd/recent-releases.js create mode 100644 lib/v2/gogoanimehd/router.js delete mode 100644 lib/v2/good/index.js delete mode 100644 lib/v2/good/radar.js delete mode 100644 lib/v2/good/router.js create mode 100644 lib/v2/google/alerts.js create mode 100644 lib/v2/google/search.js delete mode 100644 lib/v2/google/sites.js delete mode 100644 lib/v2/google/sitesRecentChanges.js create mode 100644 lib/v2/google/templates/description.art create mode 100644 lib/v2/gov/beijing/bjedu/gh.js create mode 100644 lib/v2/gov/caac/cjwt.js create mode 100644 lib/v2/gov/caac/templates/description.art create mode 100644 lib/v2/gov/chinamine-safety/util.js create mode 100644 lib/v2/gov/chinamine-safety/xw.js create mode 100644 lib/v2/gov/chinamine-safety/zfxxgk.js create mode 100644 lib/v2/gov/chongqing/gzw.js create mode 100644 lib/v2/gov/chongqing/sydwgkzp.js create mode 100644 lib/v2/gov/forestry/gjlckjdjt.js create mode 100644 lib/v2/gov/forestry/templates/description.art create mode 100644 lib/v2/gov/jgjcndrc/index.js rename lib/v2/gov/jinan/healthcommission/{medical_exam_notice.js => medical-exam-notice.js} (100%) create mode 100644 lib/v2/gov/mem/sgcc.js rename lib/{routes => v2}/gov/moa/moa.js (95%) create mode 100644 lib/v2/gov/moa/zdscxx.js create mode 100644 lib/v2/gov/mof/bond.js create mode 100644 lib/v2/gov/moj/lfyjzj.js create mode 100644 lib/v2/gov/mot/index.js create mode 100644 lib/v2/gov/ndrc/fggz.js rename lib/{routes => v2}/gov/ndrc/xwdt.js (90%) create mode 100644 lib/v2/gov/nea/ghs.js create mode 100644 lib/v2/gov/npc/index.js rename lib/v2/gov/pbc/{tradeAnnouncement.js => trade-announcement.js} (100%) create mode 100644 lib/v2/gov/safe/business.js create mode 100644 lib/v2/gov/safe/complaint.js create mode 100644 lib/v2/gov/safe/templates/message.art create mode 100644 lib/v2/gov/safe/util.js create mode 100644 lib/v2/gov/samr/templates/description.art create mode 100644 lib/v2/gov/samr/xgzlyhd.js create mode 100644 lib/v2/gov/suzhou/doc.js create mode 100644 lib/v2/gov/suzhou/fg.js create mode 100644 lib/v2/gov/suzhou/news.js create mode 100644 lib/v2/gov/zhejiang/gwy.js create mode 100644 lib/v2/gov/zhengce/index.js delete mode 100644 lib/v2/gov/zhengce/zuixin.js delete mode 100644 lib/v2/gq/maintainer.js delete mode 100644 lib/v2/gq/radar.js delete mode 100644 lib/v2/gq/router.js delete mode 100644 lib/v2/gq/templates/embed-article.art delete mode 100644 lib/v2/gq/templates/embed-product.art delete mode 100644 lib/v2/gq/templates/img.art delete mode 100644 lib/v2/gq/templates/tw.art delete mode 100644 lib/v2/gq/templates/videoObject.art delete mode 100644 lib/v2/gq/templates/youtube.art delete mode 100644 lib/v2/gq/tw/index.js rename lib/{routes => v2}/grubstreet/index.js (100%) create mode 100644 lib/v2/grubstreet/maintainer.js rename lib/v2/{yaohuo => grubstreet}/radar.js (56%) rename lib/v2/{xwlb => grubstreet}/router.js (65%) create mode 100644 lib/v2/grubstreet/utils.js delete mode 100644 lib/v2/guandian/index.js delete mode 100644 lib/v2/guandian/maintainer.js delete mode 100644 lib/v2/guandian/radar.js delete mode 100644 lib/v2/guandian/router.js create mode 100644 lib/v2/guangdiu/search.js delete mode 100644 lib/v2/guggenheim/exhibitions.js delete mode 100644 lib/v2/guggenheim/maintainer.js delete mode 100644 lib/v2/guggenheim/radar.js delete mode 100644 lib/v2/guggenheim/router.js create mode 100644 lib/v2/gxmzu/lib.js delete mode 100644 lib/v2/gz-cmc/index.js delete mode 100644 lib/v2/gz-cmc/maintainer.js delete mode 100644 lib/v2/gz-cmc/radar.js delete mode 100644 lib/v2/gz-cmc/router.js delete mode 100644 lib/v2/gz-cmc/templates/description.art delete mode 100644 lib/v2/gzh360/_README delete mode 100644 lib/v2/gzh360/category.js delete mode 100644 lib/v2/gzh360/gzh.js delete mode 100644 lib/v2/gzh360/maintainer.js delete mode 100644 lib/v2/gzh360/radar.js delete mode 100644 lib/v2/gzh360/router.js delete mode 100644 lib/v2/gzh360/universal.js delete mode 100644 lib/v2/gzh360/utils.js create mode 100644 lib/v2/hackyournews/index.js rename lib/v2/{yaohuo => hackyournews}/maintainer.js (50%) create mode 100644 lib/v2/hackyournews/radar.js rename lib/v2/{pmthinking => hackyournews}/router.js (54%) rename lib/{routes/universities => v2}/harvard/health/blog.js (72%) create mode 100644 lib/v2/harvard/maintainer.js create mode 100644 lib/v2/harvard/radar.js create mode 100644 lib/v2/harvard/router.js create mode 100644 lib/v2/hebtv/maintainer.js create mode 100644 lib/v2/hebtv/nong-bo-shi-zai-xing-dong.js create mode 100644 lib/v2/hebtv/radar.js create mode 100644 lib/v2/hebtv/router.js create mode 100644 lib/v2/hebtv/templates/description.art delete mode 100644 lib/v2/hellobtc/topic.js rename lib/v2/{watchout => hicairo}/maintainer.js (50%) create mode 100644 lib/v2/hicairo/radar.js create mode 100644 lib/v2/hicairo/router.js create mode 100644 lib/v2/hicairo/rss.js delete mode 100644 lib/v2/hjedd/article.js delete mode 100644 lib/v2/hjedd/hot.js delete mode 100644 lib/v2/hjedd/latest.js delete mode 100644 lib/v2/hjedd/maintainer.js delete mode 100644 lib/v2/hjedd/news.js delete mode 100644 lib/v2/hjedd/notice.js delete mode 100644 lib/v2/hjedd/original.js delete mode 100644 lib/v2/hjedd/radar.js delete mode 100644 lib/v2/hjedd/router.js delete mode 100644 lib/v2/hjedd/templates/attachment.art delete mode 100644 lib/v2/hjedd/top.js delete mode 100644 lib/v2/hjedd/utils.js delete mode 100644 lib/v2/hket/templates/description.art create mode 100644 lib/v2/hostmonit/cloudflareyes.js create mode 100644 lib/v2/hostmonit/maintainer.js create mode 100644 lib/v2/hostmonit/radar.js create mode 100644 lib/v2/hostmonit/router.js create mode 100644 lib/v2/hostmonit/templates/description.art create mode 100644 lib/v2/hostmonit/templates/title.art delete mode 100644 lib/v2/hotchina/index.js delete mode 100644 lib/v2/hotchina/maintainer.js delete mode 100644 lib/v2/hotchina/radar.js delete mode 100644 lib/v2/hotchina/router.js create mode 100644 lib/v2/howtoforge/maintainer.js create mode 100644 lib/v2/howtoforge/radar.js create mode 100644 lib/v2/howtoforge/router.js create mode 100644 lib/v2/howtoforge/rss.js create mode 100644 lib/v2/hoyolab/constant.js create mode 100644 lib/v2/hoyolab/maintainer.js create mode 100644 lib/v2/hoyolab/news.js create mode 100644 lib/v2/hoyolab/radar.js create mode 100644 lib/v2/hoyolab/router.js create mode 100644 lib/v2/hoyolab/templates/post.art create mode 100644 lib/v2/hoyolab/utils.js create mode 100644 lib/v2/hpoi/all.js create mode 100644 lib/v2/hpoi/banner-item.js create mode 100644 lib/v2/hpoi/character.js rename lib/{routes => v2}/hpoi/info.js (100%) create mode 100644 lib/v2/hpoi/maintainer.js create mode 100644 lib/v2/hpoi/radar.js create mode 100644 lib/v2/hpoi/router.js create mode 100644 lib/v2/hpoi/user.js create mode 100644 lib/v2/hpoi/utils.js create mode 100644 lib/v2/hpoi/work.js delete mode 100644 lib/v2/huangz/index.js delete mode 100644 lib/v2/huangz/maintainer.js delete mode 100644 lib/v2/huangz/radar.js delete mode 100644 lib/v2/huangz/router.js create mode 100644 lib/v2/huggingface/blog-zh.js rename lib/v2/hunau/utils/{categoryTitle.js => category-title.js} (100%) rename lib/v2/hunau/utils/{indexPage.js => index-page.js} (69%) rename lib/v2/hunau/utils/{newsContent.js => news-content.js} (91%) delete mode 100644 lib/v2/huxiu/article.js delete mode 100644 lib/v2/huxiu/author.js create mode 100644 lib/v2/huxiu/brief-column.js delete mode 100644 lib/v2/huxiu/briefColumn.js create mode 100644 lib/v2/huxiu/channel.js create mode 100644 lib/v2/huxiu/club.js create mode 100644 lib/v2/huxiu/member.js delete mode 100644 lib/v2/huxiu/templates/brief.art create mode 100644 lib/v2/huxiu/templates/description.art delete mode 100644 lib/v2/huxiu/templates/img.art delete mode 100644 lib/v2/huxiu/templates/moment.art delete mode 100644 lib/v2/huxiu/templates/video.art create mode 100644 lib/v2/huxiu/util.js delete mode 100644 lib/v2/huxiu/utils.js delete mode 100644 lib/v2/hyqss/index.js delete mode 100644 lib/v2/hyqss/maintainer.js delete mode 100644 lib/v2/hyqss/radar.js delete mode 100644 lib/v2/hyqss/router.js delete mode 100644 lib/v2/i-cable/category.js delete mode 100644 lib/v2/i-cable/maintainer.js delete mode 100644 lib/v2/i-cable/radar.js delete mode 100644 lib/v2/i-cable/router.js delete mode 100644 lib/v2/i-cable/templates/desc.art create mode 100644 lib/v2/ianspriggs/index.js create mode 100644 lib/v2/ianspriggs/maintainer.js create mode 100644 lib/v2/ianspriggs/radar.js create mode 100644 lib/v2/ianspriggs/router.js create mode 100644 lib/v2/ianspriggs/templates/description.art delete mode 100644 lib/v2/ibc/maintainer.js delete mode 100644 lib/v2/ibc/maitta.js delete mode 100644 lib/v2/ibc/radar.js delete mode 100644 lib/v2/ibc/radio.js delete mode 100644 lib/v2/ibc/router.js delete mode 100644 lib/v2/ibc/templates/description.art rename lib/v2/ikea/cn/{family_offers.js => family-offers.js} (93%) rename lib/v2/ikea/cn/{low_price.js => low-price.js} (89%) create mode 100644 lib/v2/imiker/jinghua.js create mode 100644 lib/v2/imiker/maintainer.js create mode 100644 lib/v2/imiker/radar.js create mode 100644 lib/v2/imiker/router.js create mode 100644 lib/v2/imiker/templates/description.art delete mode 100644 lib/v2/independent/maintainer.js delete mode 100644 lib/v2/independent/ps5-stock-uk.js delete mode 100644 lib/v2/independent/radar.js delete mode 100644 lib/v2/independent/router.js create mode 100644 lib/v2/indienova/article.js create mode 100644 lib/v2/indienova/column.js create mode 100644 lib/v2/indienova/gamedb.js create mode 100644 lib/v2/indienova/maintainer.js create mode 100644 lib/v2/indienova/radar.js create mode 100644 lib/v2/indienova/router.js create mode 100644 lib/v2/indienova/usergames.js create mode 100644 lib/v2/indienova/utils.js create mode 100644 lib/v2/iqilu/maintainer.js create mode 100644 lib/v2/iqilu/program.js create mode 100644 lib/v2/iqilu/radar.js create mode 100644 lib/v2/iqilu/router.js create mode 100644 lib/v2/iqilu/templates/description.art delete mode 100644 lib/v2/islander/maintainer.js delete mode 100644 lib/v2/islander/radar.js delete mode 100644 lib/v2/islander/router.js delete mode 100644 lib/v2/islander/search.js delete mode 100644 lib/v2/islander/top30event.js rename lib/v2/ixigua/{userVideo.js => user-video.js} (88%) create mode 100644 lib/v2/javlibrary/maker.js rename lib/{routes => v2}/jianshu/collection.js (100%) rename lib/{routes => v2}/jianshu/home.js (100%) create mode 100644 lib/v2/jianshu/maintainer.js create mode 100644 lib/v2/jianshu/radar.js create mode 100644 lib/v2/jianshu/router.js rename lib/{routes => v2}/jianshu/user.js (100%) rename lib/{routes => v2}/jianshu/utils.js (56%) delete mode 100644 lib/v2/jiemian/list.js create mode 100644 lib/v2/jiemian/lists.js create mode 100644 lib/v2/jiemian/templates/description.art rename lib/v2/jike/{topicText.js => topic-text.js} (90%) create mode 100644 lib/v2/jinse/catalogue.js create mode 100644 lib/v2/jinse/lives.js create mode 100644 lib/v2/jinse/maintainer.js create mode 100644 lib/v2/jinse/radar.js create mode 100644 lib/v2/jinse/router.js create mode 100644 lib/v2/jinse/templates/description.art create mode 100644 lib/v2/jinse/timeline.js create mode 100644 lib/v2/jjwxc/author.js create mode 100644 lib/v2/jjwxc/book.js create mode 100644 lib/v2/jjwxc/maintainer.js create mode 100644 lib/v2/jjwxc/radar.js create mode 100644 lib/v2/jjwxc/router.js create mode 100644 lib/v2/jjwxc/templates/author.art create mode 100644 lib/v2/jjwxc/templates/book.art create mode 100644 lib/v2/jsu/cxzx.js create mode 100644 lib/v2/jsu/jwc.js create mode 100644 lib/v2/jsu/maintainer.js create mode 100644 lib/v2/jsu/math.js create mode 100644 lib/v2/jsu/radar.js create mode 100644 lib/v2/jsu/rjxy.js create mode 100644 lib/v2/jsu/router.js create mode 100644 lib/v2/jsu/universityindex.js create mode 100644 lib/v2/jsu/utils/index.js delete mode 100644 lib/v2/juejin/news.js delete mode 100644 lib/v2/juejin/shares.js delete mode 100644 lib/v2/kakuyomu/episode.js delete mode 100644 lib/v2/kakuyomu/maintainer.js delete mode 100644 lib/v2/kakuyomu/radar.js delete mode 100644 lib/v2/kakuyomu/router.js create mode 100644 lib/v2/keepass/maintainer.js create mode 100644 lib/v2/keepass/news.js create mode 100644 lib/v2/keepass/radar.js create mode 100644 lib/v2/keepass/router.js create mode 100644 lib/v2/kepu/live.js create mode 100644 lib/v2/kepu/maintainer.js create mode 100644 lib/v2/kepu/radar.js create mode 100644 lib/v2/kepu/router.js create mode 100644 lib/v2/kepu/templates/description.art delete mode 100644 lib/v2/knowmedia/index.js delete mode 100644 lib/v2/knowmedia/maintainer.js delete mode 100644 lib/v2/knowmedia/radar.js delete mode 100644 lib/v2/knowmedia/router.js delete mode 100644 lib/v2/knowmedia/templates/desc.art create mode 100644 lib/v2/konghq/blog-posts.js create mode 100644 lib/v2/konghq/maintainer.js create mode 100644 lib/v2/konghq/radar.js create mode 100644 lib/v2/konghq/router.js rename lib/v2/kuaidi100/{supported_company.js => supported-company.js} (100%) create mode 100644 lib/v2/laimanhua/index.js create mode 100644 lib/v2/laimanhua/maintainer.js create mode 100644 lib/v2/laimanhua/radar.js rename lib/v2/{lativ => laimanhua}/router.js (55%) create mode 100644 lib/v2/lala/maintainer.js create mode 100644 lib/v2/lala/radar.js create mode 100644 lib/v2/lala/router.js create mode 100644 lib/v2/lala/rss.js delete mode 100644 lib/v2/lativ/index.js delete mode 100644 lib/v2/lativ/maintainer.js delete mode 100644 lib/v2/lativ/radar.js delete mode 100644 lib/v2/lativ/templates/detail.art delete mode 100644 lib/v2/layer3/maintainer.js delete mode 100644 lib/v2/layer3/quests.js delete mode 100644 lib/v2/layer3/radar.js delete mode 100644 lib/v2/layer3/router.js delete mode 100644 lib/v2/layer3/templates/description.art delete mode 100644 lib/v2/lever/index.js delete mode 100644 lib/v2/lever/maintainer.js delete mode 100644 lib/v2/lever/radar.js delete mode 100644 lib/v2/lever/router.js rename lib/v2/lfsyd/{old_home.js => old-home.js} (100%) delete mode 100644 lib/v2/lianxh/index.js delete mode 100644 lib/v2/lianxh/radar.js delete mode 100644 lib/v2/lianxh/router.js create mode 100644 lib/v2/lifeweek/channel.js create mode 100644 lib/v2/lifeweek/maintainer.js create mode 100644 lib/v2/lifeweek/radar.js create mode 100644 lib/v2/lifeweek/router.js create mode 100644 lib/v2/lifeweek/tag.js create mode 100644 lib/v2/lifeweek/utils.js create mode 100644 lib/v2/lightnovel/light-novel.js create mode 100644 lib/v2/lightnovel/maintainer.js create mode 100644 lib/v2/lightnovel/radar.js create mode 100644 lib/v2/lightnovel/router.js create mode 100644 lib/v2/liquipedia/cs-matches.js create mode 100644 lib/v2/liquipedia/dota2-matches.js create mode 100644 lib/v2/liquipedia/maintainer.js create mode 100644 lib/v2/liquipedia/radar.js create mode 100644 lib/v2/liquipedia/router.js create mode 100644 lib/v2/liveuamap/index.js create mode 100644 lib/v2/liveuamap/maintainer.js create mode 100644 lib/v2/liveuamap/radar.js create mode 100644 lib/v2/liveuamap/router.js create mode 100644 lib/v2/logclub/index.js create mode 100644 lib/v2/logclub/maintainer.js create mode 100644 lib/v2/logclub/radar.js create mode 100644 lib/v2/logclub/report.js create mode 100644 lib/v2/logclub/router.js create mode 100644 lib/v2/logclub/templates/description.art rename lib/v2/luogu/{userBlog.js => user-blog.js} (100%) rename lib/v2/luogu/{userFeed.js => user-feed.js} (100%) rename lib/v2/{yoasobi-music/jsonpHelper.js => lxixsxa/jsonp-helper.js} (81%) create mode 100644 lib/v2/m4/index.js rename lib/v2/{wp-china => m4}/maintainer.js (62%) create mode 100644 lib/v2/m4/radar.js create mode 100644 lib/v2/m4/router.js create mode 100644 lib/v2/m4/templates/description.art rename lib/v2/mastodon/{account_id.js => account-id.js} (100%) rename lib/v2/mastodon/{timeline_local.js => timeline-local.js} (100%) rename lib/v2/mastodon/{timeline_remote.js => timeline-remote.js} (100%) delete mode 100644 lib/v2/mcachicago/exhibitions.js delete mode 100644 lib/v2/mcachicago/maintainer.js delete mode 100644 lib/v2/mcachicago/radar.js delete mode 100644 lib/v2/mcachicago/router.js rename lib/v2/mckinsey/cn/{categoryMap.js => category-map.js} (100%) delete mode 100644 lib/v2/mclaren/index.js delete mode 100644 lib/v2/mclaren/maintainer.js delete mode 100644 lib/v2/mclaren/radar.js delete mode 100644 lib/v2/mclaren/router.js delete mode 100644 lib/v2/mclaren/templates/desc.art create mode 100644 lib/v2/medieval-china/maintainer.js create mode 100644 lib/v2/medieval-china/post.js create mode 100644 lib/v2/medieval-china/radar.js create mode 100644 lib/v2/medieval-china/router.js delete mode 100644 lib/v2/meituclub/latest.js delete mode 100644 lib/v2/meituclub/maintainer.js delete mode 100644 lib/v2/meituclub/radar.js delete mode 100644 lib/v2/meituclub/templates/description.art create mode 100644 lib/v2/metacritic/index.js create mode 100644 lib/v2/metacritic/maintainer.js create mode 100644 lib/v2/metacritic/radar.js rename lib/{routes => v2}/metacritic/release.js (100%) create mode 100644 lib/v2/metacritic/router.js create mode 100644 lib/v2/metacritic/templates/description.art create mode 100644 lib/v2/metacritic/util.js create mode 100644 lib/v2/mihoyo/bbs/cache.js create mode 100644 lib/v2/mihoyo/bbs/follow-list.js create mode 100644 lib/v2/mihoyo/bbs/timeline.js create mode 100644 lib/v2/mihoyo/bbs/user-post.js create mode 100644 lib/v2/mihoyo/bbs/utils.js delete mode 100644 lib/v2/miris/blog.js delete mode 100644 lib/v2/miris/maintainer.js delete mode 100644 lib/v2/miris/radar.js create mode 100644 lib/v2/missav/maintainer.js create mode 100644 lib/v2/missav/new.js create mode 100644 lib/v2/missav/radar.js create mode 100644 lib/v2/missav/router.js create mode 100644 lib/v2/missav/templates/preview.art create mode 100644 lib/v2/mittrchina/index.js create mode 100644 lib/v2/mittrchina/maintainer.js create mode 100644 lib/v2/mittrchina/radar.js create mode 100644 lib/v2/mittrchina/router.js create mode 100644 lib/v2/mittrchina/templates/movie.art delete mode 100644 lib/v2/mobilism/forums.js delete mode 100644 lib/v2/mobilism/maintainer.js delete mode 100644 lib/v2/mobilism/portal.js delete mode 100644 lib/v2/mobilism/radar.js delete mode 100644 lib/v2/mobilism/router.js create mode 100644 lib/v2/modb/maintainer.js create mode 100644 lib/v2/modb/radar.js create mode 100644 lib/v2/modb/router.js create mode 100644 lib/v2/modb/topic.js create mode 100644 lib/v2/modrinth/api.d.ts create mode 100644 lib/v2/modrinth/maintainer.js create mode 100644 lib/v2/modrinth/radar.js create mode 100644 lib/v2/modrinth/router.js create mode 100644 lib/v2/modrinth/templates/version.art create mode 100644 lib/v2/modrinth/versions.js create mode 100644 lib/v2/mrm/index.js create mode 100644 lib/v2/mrm/maintainer.js create mode 100644 lib/v2/mrm/radar.js rename lib/v2/{ezone => mrm}/router.js (59%) delete mode 100644 lib/v2/mtime/maintainer.js delete mode 100644 lib/v2/mtime/news.js delete mode 100644 lib/v2/mtime/radar.js create mode 100644 lib/v2/mymusicsheet/maintainer.js create mode 100644 lib/v2/mymusicsheet/radar.js create mode 100644 lib/v2/mymusicsheet/router.js create mode 100644 lib/v2/mymusicsheet/templates/description.art create mode 100644 lib/v2/mymusicsheet/usersheets.js delete mode 100644 lib/v2/nbd/article.js create mode 100644 lib/v2/ncc-cma/cmdp.js create mode 100644 lib/v2/ncc-cma/maintainer.js create mode 100644 lib/v2/ncc-cma/radar.js create mode 100644 lib/v2/ncc-cma/router.js create mode 100644 lib/v2/ncc-cma/templates/description.art create mode 100644 lib/v2/ncu/jwc.js create mode 100644 lib/v2/ncu/maintainer.js create mode 100644 lib/v2/ncu/radar.js create mode 100644 lib/v2/ncu/router.js create mode 100644 lib/v2/netflav/index.js create mode 100644 lib/v2/netflav/maintainer.js create mode 100644 lib/v2/netflav/radar.js create mode 100644 lib/v2/netflav/router.js create mode 100644 lib/v2/netflav/templates/description.art create mode 100644 lib/v2/news/templates/description.art delete mode 100644 lib/v2/news/whxw.js create mode 100644 lib/v2/news/xhsxw.js rename lib/v2/nhk/{news_web_easy.js => news-web-easy.js} (100%) rename lib/v2/nintendo/{eshop_cn.js => eshop-cn.js} (100%) rename lib/v2/nintendo/{eshop_hk.js => eshop-hk.js} (96%) rename lib/v2/nintendo/{eshop_jp.js => eshop-jp.js} (100%) rename lib/v2/nintendo/{eshop_us.js => eshop-us.js} (100%) rename lib/v2/nintendo/{news_china.js => news-china.js} (100%) delete mode 100644 lib/v2/nmbxd1/forum.js delete mode 100644 lib/v2/nmbxd1/maintainer.js delete mode 100644 lib/v2/nmbxd1/radar.js delete mode 100644 lib/v2/nmbxd1/router.js delete mode 100644 lib/v2/nmbxd1/templates/description.art delete mode 100644 lib/v2/nytimes/author.js rename lib/v2/nytimes/{daily_briefing_chinese.js => daily-briefing-chinese.js} (94%) rename lib/v2/oceanengine/{arithmeticIndex.js => arithmetic-index.js} (93%) rename lib/v2/odaily/{search_news.js => search-news.js} (100%) create mode 100644 lib/v2/onehu/common.js create mode 100644 lib/v2/onehu/maintainer.js create mode 100644 lib/v2/onehu/radar.js create mode 100644 lib/v2/onehu/router.js create mode 100644 lib/v2/onet/maintainer.js create mode 100644 lib/v2/onet/news.js create mode 100644 lib/v2/onet/radar.js rename lib/v2/{mtime => onet}/router.js (55%) create mode 100644 lib/v2/onet/templates/article.art create mode 100644 lib/v2/onet/templates/image.art create mode 100644 lib/v2/onet/utils.js rename lib/v2/oreno3d/{get_sec_page_data.js => get-sec-page-data.js} (100%) create mode 100644 lib/v2/osu/beatmaps/packs.js create mode 100644 lib/v2/osu/maintainer.js create mode 100644 lib/v2/osu/radar.js create mode 100644 lib/v2/osu/router.js create mode 100644 lib/v2/otobanana/cast.js create mode 100644 lib/v2/otobanana/livestream.js create mode 100644 lib/v2/otobanana/maintainer.js create mode 100644 lib/v2/otobanana/radar.js create mode 100644 lib/v2/otobanana/router.js create mode 100644 lib/v2/otobanana/templates/description.art create mode 100644 lib/v2/otobanana/timeline.js create mode 100644 lib/v2/otobanana/utils.js create mode 100644 lib/v2/ouc/it-tx.js create mode 100644 lib/v2/papers/index.js create mode 100644 lib/v2/papers/maintainer.js create mode 100644 lib/v2/papers/radar.js create mode 100644 lib/v2/papers/router.js create mode 100644 lib/v2/papers/templates/description.art create mode 100644 lib/v2/parliament/maintainer.js create mode 100644 lib/v2/parliament/radar.js create mode 100644 lib/v2/parliament/router.js create mode 100644 lib/v2/parliament/section77.js create mode 100644 lib/v2/paulgraham/article.js create mode 100644 lib/v2/paulgraham/maintainer.js create mode 100644 lib/v2/paulgraham/radar.js create mode 100644 lib/v2/paulgraham/router.js create mode 100644 lib/v2/picnob/utils.js rename lib/v2/pixiv/api/{getBookmarks.js => get-bookmarks.js} (100%) rename lib/v2/pixiv/api/{getIllustFollows.js => get-illust-follows.js} (100%) rename lib/v2/pixiv/api/{getIllusts.js => get-illusts.js} (100%) rename lib/v2/pixiv/api/{getRanking.js => get-ranking.js} (76%) rename lib/v2/pixiv/api/{getUserDetail.js => get-user-detail.js} (100%) rename lib/v2/pixiv/api/{searchIllust.js => search-illust.js} (100%) rename lib/v2/pixiv/api/{searchPopularIllust.js => search-popular-illust.js} (100%) create mode 100644 lib/v2/pkmer/maintainer.js create mode 100644 lib/v2/pkmer/radar.js create mode 100644 lib/v2/pkmer/recent.js create mode 100644 lib/v2/pkmer/router.js create mode 100644 lib/v2/pku/cls/announcement.js rename lib/v2/pku/ss/{pg_admin.js => pg-admin.js} (100%) create mode 100644 lib/v2/playpcesor/maintainer.js create mode 100644 lib/v2/playpcesor/radar.js create mode 100644 lib/v2/playpcesor/router.js create mode 100644 lib/v2/playpcesor/rss.js delete mode 100644 lib/v2/pmthinking/index.js delete mode 100644 lib/v2/pmthinking/radar.js delete mode 100644 lib/v2/pmthinking/templates/description.art delete mode 100644 lib/v2/polkadot/home.js delete mode 100644 lib/v2/polkadot/maintainer.js delete mode 100644 lib/v2/polkadot/radar.js delete mode 100644 lib/v2/polkadot/router.js delete mode 100644 lib/v2/polkaworld/home.js delete mode 100644 lib/v2/polkaworld/maintainer.js delete mode 100644 lib/v2/polkaworld/radar.js delete mode 100644 lib/v2/polkaworld/router.js rename lib/v2/pornhub/{category_url.js => category-url.js} (94%) delete mode 100644 lib/v2/prestige-av/maintainer.js delete mode 100644 lib/v2/prestige-av/radar.js delete mode 100644 lib/v2/prestige-av/router.js delete mode 100644 lib/v2/prestige-av/series.js create mode 100644 lib/v2/priconne-redive/maintainer.js create mode 100644 lib/v2/priconne-redive/news.js create mode 100644 lib/v2/priconne-redive/radar.js create mode 100644 lib/v2/priconne-redive/router.js create mode 100644 lib/v2/ps/maintainer.js create mode 100644 lib/v2/ps/monthly-games.js create mode 100644 lib/v2/ps/radar.js create mode 100644 lib/v2/ps/router.js create mode 100644 lib/v2/ps/templates/monthly-games.art rename lib/{routes => v2}/ps/trophy.js (96%) create mode 100644 lib/v2/pwc/maintainer.js create mode 100644 lib/v2/pwc/radar.js create mode 100644 lib/v2/pwc/router.js create mode 100644 lib/v2/pwc/sustainability.js create mode 100644 lib/v2/qbitai/category.js create mode 100644 lib/v2/qbitai/maintainer.js create mode 100644 lib/v2/qbitai/radar.js create mode 100644 lib/v2/qbitai/router.js create mode 100644 lib/v2/qbitai/tag.js delete mode 100644 lib/v2/qdaily/index.js delete mode 100644 lib/v2/qdaily/maintainer.js delete mode 100644 lib/v2/qdaily/radar.js delete mode 100644 lib/v2/qdaily/router.js create mode 100644 lib/v2/qdu/houqin.js create mode 100644 lib/v2/qianp/utils.js delete mode 100644 lib/v2/qidiantu/index.js delete mode 100644 lib/v2/qidiantu/maintainer.js delete mode 100644 lib/v2/qidiantu/radar.js delete mode 100644 lib/v2/qidiantu/router.js rename lib/v2/qoo-app/user/{appComment.js => app-comment.js} (100%) delete mode 100644 lib/v2/qq/live.js create mode 100644 lib/v2/questmobile/maintainer.js create mode 100644 lib/v2/questmobile/radar.js create mode 100644 lib/v2/questmobile/report.js create mode 100644 lib/v2/questmobile/router.js create mode 100644 lib/v2/questmobile/templates/description.art delete mode 100644 lib/v2/radiofrance/geopolitique.js delete mode 100644 lib/v2/radiofrance/maintainer.js delete mode 100644 lib/v2/radiofrance/radar.js delete mode 100644 lib/v2/radiofrance/router.js delete mode 100644 lib/v2/radiofrance/templates/article.art create mode 100644 lib/v2/readhub/daily.js create mode 100644 lib/v2/readhub/templates/description.art create mode 100644 lib/v2/readhub/util.js delete mode 100644 lib/v2/reuters/migration_prompt.js create mode 100644 lib/v2/routledge/book-series.js create mode 100644 lib/v2/routledge/maintainer.js create mode 100644 lib/v2/routledge/radar.js create mode 100644 lib/v2/routledge/router.js rename lib/v2/{qdaily/templates/article.art => routledge/templates/description.art} (70%) delete mode 100644 lib/v2/rsshub/sponsors.js create mode 100644 lib/v2/scau/yjsy.js create mode 100644 lib/v2/scmp/topic.js create mode 100644 lib/v2/scmp/utils.js rename lib/v2/scnu/{physics_school_announcements_and_news.js => physics-school-announcements-and-news.js} (100%) rename lib/v2/scut/seie/{news_center.js => news-ccenter.js} (95%) delete mode 100644 lib/v2/secnews/index.js delete mode 100644 lib/v2/secnews/maintainer.js delete mode 100644 lib/v2/secnews/radar.js create mode 100644 lib/v2/sfacg/maintainer.js create mode 100644 lib/v2/sfacg/novel-chapter.js create mode 100644 lib/v2/sfacg/radar.js create mode 100644 lib/v2/sfacg/router.js create mode 100644 lib/v2/shisu/maintainer.js create mode 100644 lib/v2/shisu/news.js create mode 100644 lib/v2/shisu/radar.js create mode 100644 lib/v2/shisu/router.js create mode 100644 lib/v2/shoac/maintainer.js create mode 100644 lib/v2/shoac/radar.js create mode 100644 lib/v2/shoac/recent-show.js create mode 100644 lib/v2/shoac/router.js create mode 100644 lib/v2/shoac/templates/detail.art create mode 100644 lib/v2/showstart/artist.js create mode 100644 lib/v2/showstart/brand.js create mode 100644 lib/v2/showstart/const.js create mode 100644 lib/v2/showstart/event.js create mode 100644 lib/v2/showstart/maintainer.js create mode 100644 lib/v2/showstart/radar.js create mode 100644 lib/v2/showstart/router.js create mode 100644 lib/v2/showstart/search.js create mode 100644 lib/v2/showstart/service.js create mode 100644 lib/v2/showstart/utils.js rename lib/v2/smzdm/{haowen_fenlei.js => haowen-fenlei.js} (88%) rename lib/{routes => v2}/sogou/doodles.js (100%) create mode 100644 lib/v2/sogou/maintainer.js create mode 100644 lib/v2/sogou/radar.js create mode 100644 lib/v2/sogou/router.js create mode 100644 lib/v2/sogou/search.js create mode 100644 lib/v2/sogou/templates/description.art create mode 100644 lib/v2/sohu/templates/video.art create mode 100644 lib/v2/sourceforge/index.js create mode 100644 lib/v2/sourceforge/maintainer.js create mode 100644 lib/v2/sourceforge/radar.js create mode 100644 lib/v2/sourceforge/router.js rename lib/v2/sspai/{seriesUpdate.js => series-update.js} (100%) rename lib/v2/sspai/{shortcutsGallery.js => shortcuts-gallery.js} (94%) create mode 100644 lib/v2/sspu/jwc.js create mode 100644 lib/v2/sspu/maintainer.js create mode 100644 lib/v2/sspu/pe.js create mode 100644 lib/v2/sspu/radar.js create mode 100644 lib/v2/sspu/router.js create mode 100644 lib/v2/surfshark/blog.js create mode 100644 lib/v2/surfshark/maintainer.js create mode 100644 lib/v2/surfshark/radar.js create mode 100644 lib/v2/surfshark/router.js create mode 100644 lib/v2/surfshark/templates/description.art create mode 100644 lib/v2/tableau/maintainer.js create mode 100644 lib/v2/tableau/radar.js create mode 100644 lib/v2/tableau/router.js create mode 100644 lib/v2/tableau/viz-of-the-day.js create mode 100644 lib/v2/tass/maintainer.js create mode 100644 lib/v2/tass/news.js create mode 100644 lib/v2/tass/radar.js create mode 100644 lib/v2/tass/router.js delete mode 100644 lib/v2/tencent/cloud/column.js create mode 100644 lib/v2/tencent/templates/news/image.art create mode 100644 lib/v2/tesla/cx.js create mode 100644 lib/v2/tesla/templates/description.art create mode 100644 lib/v2/theblockbeats/index.js rename lib/v2/{blockbeats => theblockbeats}/maintainer.js (100%) rename lib/v2/{blockbeats => theblockbeats}/radar.js (51%) rename lib/v2/{appledaily => theblockbeats}/router.js (58%) rename lib/v2/thecatcity/{termsMap.js => terms-map.js} (100%) create mode 100644 lib/v2/thoughtco/index.js create mode 100644 lib/v2/thoughtco/maintainer.js create mode 100644 lib/v2/thoughtco/radar.js create mode 100644 lib/v2/thoughtco/router.js create mode 100644 lib/v2/thoughtco/templates/description.art create mode 100644 lib/v2/threads/utils.js create mode 100644 lib/v2/tophub/list.js create mode 100644 lib/v2/tophub/templates/rank.art create mode 100644 lib/v2/tradingview/desktop.js create mode 100644 lib/v2/tradingview/pine.js create mode 100644 lib/v2/transcriptforest/index.js create mode 100644 lib/v2/transcriptforest/maintainer.js create mode 100644 lib/v2/transcriptforest/radar.js create mode 100644 lib/v2/transcriptforest/router.js create mode 100644 lib/v2/transcriptforest/templates/description.art rename lib/v2/trending/{allTrending.js => all-trending.js} (100%) create mode 100644 lib/v2/trendingpapers/maintainer.js create mode 100644 lib/v2/trendingpapers/papers.js create mode 100644 lib/v2/trendingpapers/radar.js create mode 100644 lib/v2/trendingpapers/router.js create mode 100644 lib/v2/tvtropes/featured.js create mode 100644 lib/v2/tvtropes/maintainer.js create mode 100644 lib/v2/tvtropes/radar.js create mode 100644 lib/v2/tvtropes/router.js create mode 100644 lib/v2/tvtropes/templates/description.art create mode 100644 lib/v2/twitch/live.js create mode 100644 lib/v2/twitch/maintainer.js create mode 100644 lib/v2/twitch/radar.js create mode 100644 lib/v2/twitch/router.js create mode 100644 lib/v2/twitch/schedule.js create mode 100644 lib/v2/twitch/video.js rename lib/v2/twitter/{api_fallback_common.js => api-fallback-common.js} (89%) create mode 100644 lib/v2/twitter/web-api/login.js create mode 100644 lib/v2/twitter/web-api/token.js delete mode 100644 lib/v2/twitter/web-api/twitter-got.js delete mode 100644 lib/v2/twreporter/category.js rename lib/v2/twreporter/{fetch_article.js => fetch-article.js} (100%) delete mode 100644 lib/v2/twreporter/photography.js create mode 100644 lib/v2/uniqlo/maintainer.js create mode 100644 lib/v2/uniqlo/new.js create mode 100644 lib/v2/uniqlo/radar.js create mode 100644 lib/v2/uniqlo/router.js create mode 100644 lib/v2/unraid/community-apps.js create mode 100644 lib/v2/unraid/maintainer.js create mode 100644 lib/v2/unraid/radar.js create mode 100644 lib/v2/unraid/router.js delete mode 100644 lib/v2/verse/articles.js delete mode 100644 lib/v2/verse/maintainer.js delete mode 100644 lib/v2/verse/radar.js delete mode 100644 lib/v2/verse/router.js delete mode 100644 lib/v2/vlive/index.js delete mode 100644 lib/v2/vlive/maintainer.js delete mode 100644 lib/v2/vlive/radar.js delete mode 100644 lib/v2/vlive/router.js delete mode 100644 lib/v2/vlive/templates/post.art delete mode 100644 lib/v2/vlive/templates/video.art delete mode 100644 lib/v2/vmware/flings.js delete mode 100644 lib/v2/vmware/maintainer.js delete mode 100644 lib/v2/vmware/radar.js delete mode 100644 lib/v2/vmware/router.js delete mode 100644 lib/v2/wangqiutiyu/anchor.js delete mode 100644 lib/v2/wangqiutiyu/maintainer.js delete mode 100644 lib/v2/wangqiutiyu/radar.js delete mode 100644 lib/v2/wangqiutiyu/router.js delete mode 100644 lib/v2/watchout/index.js delete mode 100644 lib/v2/watchout/radar.js delete mode 100644 lib/v2/watchout/router.js create mode 100644 lib/v2/weibo/friends.js create mode 100644 lib/v2/weibo/search/template/digest.art rename lib/v2/weibo/{super_index.js => super-index.js} (100%) delete mode 100644 lib/v2/whoscall/index.js delete mode 100644 lib/v2/whoscall/maintainer.js delete mode 100644 lib/v2/whoscall/radar.js delete mode 100644 lib/v2/whoscall/router.js create mode 100644 lib/v2/whu/hyxt.js create mode 100644 lib/v2/whu/templates/description.art create mode 100644 lib/v2/whu/util.js create mode 100644 lib/v2/wmc-bj/maintainer.js create mode 100644 lib/v2/wmc-bj/publish.js create mode 100644 lib/v2/wmc-bj/radar.js create mode 100644 lib/v2/wmc-bj/router.js create mode 100644 lib/v2/wmc-bj/templates/description.art delete mode 100644 lib/v2/woshipm/bookmarks.js delete mode 100644 lib/v2/woshipm/latest.js rename lib/v2/woshipm/{user_article.js => user-article.js} (94%) delete mode 100644 lib/v2/wp-china/news.js delete mode 100644 lib/v2/wp-china/radar.js delete mode 100644 lib/v2/wp-china/router.js delete mode 100644 lib/v2/wxkol/maintainer.js delete mode 100644 lib/v2/wxkol/radar.js delete mode 100644 lib/v2/wxkol/router.js delete mode 100644 lib/v2/wxkol/show.js create mode 100644 lib/v2/x-mol/maintainer.js create mode 100644 lib/v2/x-mol/news.js create mode 100644 lib/v2/x-mol/paper.js create mode 100644 lib/v2/x-mol/radar.js create mode 100644 lib/v2/x-mol/router.js rename lib/v2/{disinformationindex => x-mol}/templates/description.art (59%) create mode 100644 lib/v2/x-mol/utils.js create mode 100644 lib/v2/xmnn/news.js create mode 100644 lib/v2/xsijishe/rank.js rename lib/v2/xueqiu/{stock_comments.js => stock-comments.js} (88%) rename lib/v2/xueqiu/{stock_info.js => stock-info.js} (89%) rename lib/v2/xueqiu/{user_stock.js => user-stock.js} (100%) delete mode 100644 lib/v2/xwlb/index.js delete mode 100644 lib/v2/xwlb/maintainer.js delete mode 100644 lib/v2/xwlb/radar.js create mode 100644 lib/v2/yahoo/news/tw/index.js create mode 100644 lib/v2/yahoo/news/tw/provider-helper.js create mode 100644 lib/v2/yahoo/news/tw/provider.js create mode 100644 lib/v2/yahoo/news/tw/utils.js rename lib/v2/yahoo/news/{ => us}/index.js (97%) create mode 100644 lib/v2/yahoo/templates/youtube.art delete mode 100644 lib/v2/yaohuo/index.js delete mode 100644 lib/v2/yaohuo/router.js create mode 100644 lib/v2/yicai/dt.js rename lib/v2/{lxixsxa/jsonpHelper.js => yoasobi-music/jsonp-helper.js} (81%) create mode 100644 lib/v2/yomujp/level.js create mode 100644 lib/v2/yomujp/maintainer.js create mode 100644 lib/v2/yomujp/radar.js create mode 100644 lib/v2/yomujp/router.js delete mode 100644 lib/v2/yunspe/maintainer.js delete mode 100644 lib/v2/yunspe/newsflash.js delete mode 100644 lib/v2/yunspe/radar.js delete mode 100644 lib/v2/yunspe/router.js rename lib/v2/zhihu/{daily_section.js => daily-section.js} (94%) rename lib/v2/zhihu/execlib/{x-zse-96_v3.js => x-zse-96-v3.js} (80%) create mode 100644 lib/v2/zhihu/xhu/activities.js create mode 100644 lib/v2/zhihu/xhu/answers.js create mode 100644 lib/v2/zhihu/xhu/posts.js rename lib/{routes => v2}/zimuxia/index.js (69%) create mode 100644 lib/v2/zimuxia/maintainer.js create mode 100644 lib/v2/zimuxia/portfolio.js create mode 100644 lib/v2/zimuxia/radar.js create mode 100644 lib/v2/zimuxia/router.js create mode 100644 lib/v2/zjuvag/blog.js create mode 100644 lib/v2/zjuvag/maintainer.js create mode 100644 lib/v2/zjuvag/radar.js rename lib/v2/{miris => zjuvag}/router.js (55%) delete mode 100644 lib/v2/zooTeam/blog.js delete mode 100644 lib/v2/zooTeam/maintainer.js delete mode 100644 lib/v2/zooTeam/radar.js delete mode 100644 lib/v2/zooTeam/router.js delete mode 100644 lib/v2/zooTeam/weekly.js create mode 100644 lib/v2/zrblog/maintainer.js create mode 100644 lib/v2/zrblog/radar.js create mode 100644 lib/v2/zrblog/router.js create mode 100644 lib/v2/zrblog/rss.js delete mode 100644 lib/v2/zuzhirenshi/index.js delete mode 100644 lib/v2/zuzhirenshi/maintainer.js delete mode 100644 lib/v2/zuzhirenshi/radar.js delete mode 100644 lib/v2/zuzhirenshi/router.js create mode 100644 lib/views/rss3-ums.js create mode 100644 scripts/twitter-token/generate.js create mode 100644 scripts/twitter-token/generate.sh rename test/utils/{dateParser.js => date-parser.js} (97%) create mode 100644 test/utils/pac-proxy.js rename website/{babel.config.js => babel.config.cjs} (100%) delete mode 100644 website/docs/.format/chineseFormat.js delete mode 100644 website/docs/.format/file.js create mode 100644 website/docs/.format/file.mjs rename website/docs/.format/{format.js => format.mjs} (83%) create mode 100644 website/docs/.format/handle/heading.mjs delete mode 100644 website/docs/.format/md/hierarchySlug.js create mode 100644 website/docs/.format/removeHeadingId.mjs create mode 100644 website/docs/.format/routeFormat.mjs create mode 100644 website/docs/.format/rsshub-heading-id.mjs create mode 100644 website/docs/.format/rsshub-no-dupe-attrs.mjs create mode 100644 website/docs/.format/rsshub-route-level.mjs rename website/docs/.format/{slugId.js => slugId.mjs} (95%) rename website/docs/.format/{sortByHeading.js => sortByHeading.mjs} (96%) create mode 100644 website/docs/install/config.md create mode 100644 website/docs/instances.mdx rename website/docs/routes/{anime.md => anime.mdx} (54%) delete mode 100644 website/docs/routes/bbs.md create mode 100644 website/docs/routes/bbs.mdx delete mode 100644 website/docs/routes/blog.md create mode 100644 website/docs/routes/blog.mdx delete mode 100644 website/docs/routes/design.md create mode 100644 website/docs/routes/design.mdx delete mode 100644 website/docs/routes/finance.md create mode 100644 website/docs/routes/finance.mdx delete mode 100644 website/docs/routes/forecast.md create mode 100644 website/docs/routes/forecast.mdx delete mode 100644 website/docs/routes/game.md create mode 100644 website/docs/routes/game.mdx delete mode 100644 website/docs/routes/government.md create mode 100644 website/docs/routes/government.mdx delete mode 100644 website/docs/routes/journal.md create mode 100644 website/docs/routes/journal.mdx delete mode 100644 website/docs/routes/live.md create mode 100644 website/docs/routes/live.mdx delete mode 100644 website/docs/routes/multimedia.md create mode 100644 website/docs/routes/multimedia.mdx delete mode 100644 website/docs/routes/new-media.md create mode 100644 website/docs/routes/new-media.mdx delete mode 100644 website/docs/routes/other.md create mode 100644 website/docs/routes/other.mdx delete mode 100644 website/docs/routes/picture.md create mode 100644 website/docs/routes/picture.mdx rename website/docs/routes/{program-update.md => program-update.mdx} (54%) delete mode 100644 website/docs/routes/programming.md create mode 100644 website/docs/routes/programming.mdx delete mode 100644 website/docs/routes/reading.md create mode 100644 website/docs/routes/reading.mdx delete mode 100644 website/docs/routes/shopping.md create mode 100644 website/docs/routes/shopping.mdx rename website/docs/routes/{social-media.md => social-media.mdx} (55%) delete mode 100644 website/docs/routes/study.md create mode 100644 website/docs/routes/study.mdx delete mode 100644 website/docs/routes/traditional-media.md create mode 100644 website/docs/routes/traditional-media.mdx delete mode 100644 website/docs/routes/travel.md create mode 100644 website/docs/routes/travel.mdx delete mode 100644 website/docs/routes/university.md create mode 100644 website/docs/routes/university.mdx delete mode 100644 website/docusaurus.config.js create mode 100644 website/docusaurus.config.ts create mode 100644 website/i18n/zh/docusaurus-plugin-content-docs/current/install/config.md create mode 100644 website/i18n/zh/docusaurus-plugin-content-docs/current/instances.mdx rename website/{sidebars.js => sidebars.mjs} (98%) create mode 100644 website/src/components/InstanceList.tsx rename website/src/theme/DocSidebarItem/{index.js => index.jsx} (93%) rename website/src/theme/{MDXComponents.js => MDXComponents.ts} (75%) rename website/src/theme/SearchBar/{variables.css => style.css} (100%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b034619be6124d..485990eb789bc6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,8 +1,8 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/devcontainers/images/blob/v0.2.24/src/javascript-node/.devcontainer/devcontainer.json +// https://github.com/devcontainers/images/blob/v0.3.24/src/javascript-node/.devcontainer/devcontainer.json { "name": "Node.js", - "image": "mcr.microsoft.com/devcontainers/javascript-node:18-bullseye", + "image": "mcr.microsoft.com/devcontainers/javascript-node:20-bookworm", // Configure tool-specific properties. "customizations": { @@ -18,6 +18,7 @@ "deepscan.vscode-deepscan", "rangav.vscode-thunder-client", "SonarSource.sonarlint-vscode", + "unifiedjs.vscode-mdx", "ZihanLi.at-helper" ] } diff --git a/.dockerignore b/.dockerignore index 7abe42543ba1a3..607ef1d3c69206 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,9 +10,9 @@ Procfile app-minimal assets coverage -docs node_modules test +website # files .codecov.yml @@ -35,13 +35,13 @@ process.json package-lock.json vercel.json -#git but keep the git commit hash +# git but keep the git commit hash .git/logs .git/objects .git/index .git/info .git/hooks -#rsshub auxiliary files +# rsshub auxiliary files lib/radar-rules.js lib/v2/**/radar.js diff --git a/.eslintrc.json b/.eslintrc.json index 7a65cd5d6bcc7d..c37b0f81e2e073 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,44 +1,34 @@ { - "extends": ["eslint:recommended", "plugin:n/recommended", "plugin:prettier/recommended", "plugin:yml/recommended"], - "plugins": ["prettier"], + "extends": ["eslint:recommended", "plugin:n/recommended", "plugin:unicorn/recommended", "plugin:prettier/recommended", "plugin:yml/recommended"], + "plugins": ["prettier", "@stylistic/js", "unicorn"], "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "env": { "node": true, - "es6": true, + "es2024": true, "browser": true }, "rules": { // possible problems - "array-callback-return": 2, + "array-callback-return": ["error", { "allowImplicit": true }], "no-await-in-loop": 2, "no-control-regex": 0, "no-duplicate-imports": 2, - "no-prototype-builtins": 0, - "no-unsafe-negation": 2, - "require-atomic-updates": 0, // suggestions "arrow-body-style": 2, "block-scoped-var": 2, "curly": 2, "dot-notation": 2, "eqeqeq": 2, + "default-case": ["warn", { "commentPattern": "^no default$" }], + "default-case-last": 2, "no-console": 2, "no-eval": 2, "no-extend-native": 2, "no-extra-label": 2, - "no-global-assign": 2, - "no-implicit-coercion": [ - "error", - { - "boolean": false, - "number": false, - "string": false, - "disallowTemplateShorthand": true - } - ], + "no-implicit-coercion": ["error", { "boolean": false, "number": false, "string": false, "disallowTemplateShorthand": true }], "no-implicit-globals": 2, "no-labels": 2, "no-multi-str": 2, @@ -52,61 +42,83 @@ "object-shorthand": 2, "prefer-arrow-callback": 2, "prefer-const": 2, - "prefer-regex-literals": 1, + "prefer-object-has-own": 2, + "prefer-regex-literals": ["error", { "disallowRedundantWrapping": true }], "require-await": 2, - "spaced-comment": 2, - // layout & formatting - "arrow-parens": 2, - "arrow-spacing": 2, - "comma-spacing": 2, - "comma-style": 2, - "func-call-spacing": 2, - "keyword-spacing": 2, - "linebreak-style": 2, - "lines-around-comment": 2, - "no-multiple-empty-lines": 2, - "no-trailing-spaces": 2, - "rest-spread-spacing": 2, - "semi": 2, - "space-before-blocks": 2, - "space-in-parens": 2, - "space-infix-ops": 2, - "space-unary-ops": 2, // plugin specific - "n/no-extraneous-require": [ - "error", + "unicorn/consistent-destructuring": 1, + "unicorn/consistent-function-scoping": 1, + "unicorn/explicit-length-check": 0, + "unicorn/filename-case": ["error", { "case": "kebabCase", "ignore": [".*\\.(yaml|yml)$", "RequestInProgress\\.js$"] }], + "unicorn/new-for-builtins": 0, + "unicorn/no-array-callback-reference": 0, + "unicorn/no-array-reduce": 1, + "unicorn/no-await-expression-member": 1, + "unicorn/no-empty-file": 1, + "unicorn/no-hex-escape": 1, + "unicorn/no-null": 0, + "unicorn/no-object-as-default-parameter": 1, + "unicorn/no-process-exit": 0, + "unicorn/no-useless-switch-case": 0, + "unicorn/no-useless-undefined": ["error", { "checkArguments": false }], + "unicorn/numeric-separators-style": [ + "warn", { - "allowModules": ["puppeteer-extra-plugin-user-preferences", "puppeteer-extra-plugin-user-data-dir"] + "onlyIfContainsSeparator": false, + "number": { "minimumDigits": 7, "groupLength": 3 }, + "binary": { "minimumDigits": 9, "groupLength": 4 }, + "octal": { "minimumDigits": 9, "groupLength": 4 }, + "hexadecimal": { "minimumDigits": 5, "groupLength": 2 } } ], + "unicorn/prefer-code-point": 1, + "unicorn/prefer-logical-operator-over-ternary": 1, + "unicorn/prefer-module": 0, + "unicorn/prefer-node-protocol": 0, + "unicorn/prefer-number-properties": ["warn", { "checkInfinity": false }], + "unicorn/prefer-object-from-entries": 1, + "unicorn/prefer-regexp-test": 1, + "unicorn/prefer-spread": 1, + "unicorn/prefer-string-replace-all": 1, + "unicorn/prefer-string-slice": 0, + "unicorn/prefer-switch": ["warn", { "emptyDefaultCase": "do-nothing-comment" }], + "unicorn/prefer-top-level-await": 0, + "unicorn/prevent-abbreviations": 0, + "unicorn/switch-case-braces": ["error", "avoid"], + "unicorn/text-encoding-identifier-case": 0, + // previous eslint formatting rules + "@stylistic/js/arrow-parens": 2, + "@stylistic/js/arrow-spacing": 2, + "@stylistic/js/comma-spacing": 2, + "@stylistic/js/comma-style": 2, + "@stylistic/js/function-call-spacing": 2, + "@stylistic/js/keyword-spacing": 2, + "@stylistic/js/linebreak-style": 2, + "@stylistic/js/lines-around-comment": ["error", { "beforeBlockComment": false }], + "@stylistic/js/no-multiple-empty-lines": 2, + "@stylistic/js/no-trailing-spaces": 2, + "@stylistic/js/rest-spread-spacing": 2, + "@stylistic/js/semi": 2, + "@stylistic/js/space-before-blocks": 2, + "@stylistic/js/space-in-parens": 2, + "@stylistic/js/space-infix-ops": 2, + "@stylistic/js/space-unary-ops": 2, + "@stylistic/js/spaced-comment": 2, + // https://github.com/eslint-community/eslint-plugin-n + "n/no-extraneous-require": ["error", { "allowModules": ["puppeteer-extra-plugin-user-preferences", "puppeteer-extra-plugin-user-data-dir"] }], "n/no-deprecated-api": 1, "n/no-missing-require": 0, "n/no-process-exit": 0, - "n/no-unpublished-require": [ - "error", - { - "allowModules": ["tosource"] - } - ], + "n/no-unpublished-require": ["error", { "allowModules": ["tosource"] }], "prettier/prettier": 0, - "yml/quotes": [ - "error", - { - "prefer": "single" - } - ] + "yml/quotes": ["error", { "prefer": "single" }] }, "overrides": [ { "files": ["*.yaml", "*.yml"], "parser": "yaml-eslint-parser", "rules": { - "lines-around-comment": [ - "error", - { - "beforeBlockComment": false - } - ] + "lines-around-comment": ["error", { "beforeBlockComment": false }] } } ] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fb401f50abd56d..cc445043447603 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ ## Involved Issue / 该 PR 相关 Issue @@ -15,7 +15,7 @@ Fail to comply will result in your pull request being closed automatically. 请在 `routes` 区域填写以 / 开头的完整路由地址,否则你的 PR 将会被无条件关闭。 如果路由包含在文档中列出可以完全穷举的参数(例如分类),请依次全部列出。 -```route +```routes /some/route /some/other/route /dont/use/this/or/modify/it @@ -34,12 +34,10 @@ If your changes are not related to route, please fill in `routes` section with ` - [ ] New Route / 新的路由 - [ ] Follows [v2 Script Standard](https://docs.rsshub.app/joinus/advanced/script-standard) / 跟随 [v2 路由规范](https://docs.rsshub.app/zh/joinus/advanced/script-standard) - [ ] Documentation / 文档说明 - - [ ] EN / 英文文档 - - [ ] CN / 中文文档 - [ ] Full text / 全文获取 - [ ] Use cache / 使用缓存 -- [ ] Anti-bot or rate limit / 反爬/频率限制 - - [ ] If yes, do your code reflect this sign? / 如果有, 是否有对应的措施? +- [ ] Anti-bot or rate limit / 反爬/频率限制 + - [ ] If yes, do your code reflect this sign? / 如果有, 是否有对应的措施? - [ ] [Date and time](https://docs.rsshub.app/joinus/advanced/pub-date) / [日期和时间](https://docs.rsshub.app/zh/joinus/advanced/pub-date) - [ ] Parsed / 可以解析 - [ ] Correct time zone / 时区正确 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4bfc2c1d8571e0..25004b0423f8ea 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,7 +9,9 @@ updates: labels: - dependencies ignore: - # ESM only packages + - dependency-name: jsrsasign + versions: ['>=11.0.0'] # no longer includes KJUR.crypto.Cipher for RSA + # ESM only packages - dependency-name: fanfou-sdk versions: ['>=5.0.0'] - dependency-name: got @@ -20,22 +22,27 @@ updates: versions: ['>=8.0.0'] - dependency-name: rand-user-agent versions: ['>=2.0.1'] - - dependency-name: remark - versions: ['>=14.0.0'] - - dependency-name: remark-frontmatter - versions: ['>=4.0.0'] - - dependency-name: remark-gfm - versions: ['>=2.0.0'] - dependency-name: remark-parse versions: ['>=10.0.0'] - dependency-name: remark-preset-prettier versions: ['>=1.0.0'] - - dependency-name: remark-stringify - versions: ['>=10.0.0'] - - dependency-name: string-width - versions: ['>=5.0.0'] - dependency-name: unified versions: ['>=10.0.0'] + # remark-custom-heading-id is not updated to + # the latest mdast/mdxast which is released from + # the second half of 2023 + - dependency-name: remark + versions: ['>=15.0.0'] + - dependency-name: remark-frontmatter + versions: ['>=5.0.0'] + - dependency-name: remark-gfm + versions: ['>=4.0.0'] + - dependency-name: remark-mdx + versions: ['>=3.0.0'] + - dependency-name: unist-util-visit + versions: ['>=5.0.0'] + - dependency-name: unist-util-visit-parents + versions: ['>=6.0.0'] - package-ecosystem: npm directory: '/website' @@ -45,24 +52,8 @@ updates: open-pull-requests-limit: 10 labels: - dependencies - ignore: - # ESM only packages - - dependency-name: remark - versions: ['>=14.0.0'] - - dependency-name: remark-frontmatter - versions: ['>=4.0.0'] - - dependency-name: remark-gfm - versions: ['>=2.0.0'] - - dependency-name: remark-parse - versions: ['>=10.0.0'] - - dependency-name: remark-preset-prettier - versions: ['>=1.0.0'] - - dependency-name: remark-stringify - versions: ['>=10.0.0'] - - dependency-name: string-width - versions: ['>=5.0.0'] groups: - docs: + docusaurus: patterns: - '@docusaurus/*' diff --git a/.github/labeler.yml b/.github/labeler.yml index d9a23aa3d088ee..5c6fa9d19ff91e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,15 +1,17 @@ 'Route: v1': -- lib/router.js -- any: ['lib/routes/**/*.js', '!lib/routes/index.js'] +- changed-files: + - any-glob-to-any-file: ['lib/router.js'] + - all-globs-to-any-file: ['lib/routes/**/*.js', '!lib/routes/index.js'] 'Route: v2': -- 'lib/v2/**/*.js' +- changed-files: + - any-glob-to-any-file: ['lib/v2/**/*.js'] core enhancement: -- lib/routes/index.js -- any: ['lib/**', '!lib/radar-rules.js', '!lib/router.js', '!lib/routes/**', '!lib/v2/**'] +- changed-files: + - any-glob-to-any-file: ['lib/routes/index.js'] + - all-globs-to-any-file: ['lib/**', '!lib/radar-rules.js', '!/lib/config.js', '!lib/router.js', '!lib/routes/**', '!lib/v2/**'] dependencies: -- package.json -- pnpm-lock.yaml -- yarn.lock +- changed-files: + - any-glob-to-any-file: ['package.json', 'pnpm-lock.yaml', 'yarn.lock'] diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index d80d8bf661a8b3..c1c0d9a2d93f0e 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -1,27 +1,34 @@ -name: build assets +name: Build assets on: push: branches: - master paths: - - 'lib/**' - - 'scripts/workflow/*.js' + - 'assets/radar-rules.js' + - 'lib/**/maintainer.js' + - 'lib/**/radar-rules.js' + - 'lib/**/radar.js' + +permissions: + contents: read jobs: build: runs-on: ubuntu-latest name: Build assets timeout-minutes: 5 + permissions: + contents: write steps: - name: Checkout uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 8 - name: Use Node.js Active LTS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6ac7b5230ae772..41a75509b2c98c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 61b518ba884bf2..3fea22cb689061 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -9,19 +9,20 @@ jobs: name: Call maintainers runs-on: ubuntu-latest timeout-minutes: 5 + if: github.event.sender.login != 'issuehunt-oss[bot]' steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 # just need its cache + - uses: actions/setup-node@v4 # just need its cache with: node-version: lts/* cache: 'pnpm' - name: Install dependencies (pnpm) # needed since we need to parse markdown, so we also use got instead run: pnpm i - name: Generate feedback - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 8e25b730e701c3..9d88d0f6381fbd 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -1,4 +1,4 @@ -name: '[docker] CI for releases' +name: 'Docker Release' on: push: @@ -13,7 +13,7 @@ on: - '!lib/v2/test/**' - '!test/**' - 'Dockerfile' - workflow_dispatch: ~ + workflow_dispatch: {} jobs: check-env: @@ -136,7 +136,7 @@ jobs: - uses: actions/checkout@v4 - name: Docker Hub Description - uses: peter-evans/dockerhub-description@v3 + uses: peter-evans/dockerhub-description@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/pr-deploy-route-test.yml b/.github/workflows/docker-test-cont.yml similarity index 89% rename from .github/workflows/pr-deploy-route-test.yml rename to .github/workflows/docker-test-cont.yml index 900bb493206c48..d3a1a6fdff40a3 100644 --- a/.github/workflows/pr-deploy-route-test.yml +++ b/.github/workflows/docker-test-cont.yml @@ -1,4 +1,5 @@ name: PR - route test + on: workflow_run: workflows: [ PR - Docker build test ] # open, reopen, synchronized, edited included @@ -32,8 +33,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Fetch affected routes - id: fetchRoute - uses: actions/github-script@v6 + id: fetch-route + uses: actions/github-script@v7 env: PULL_REQUEST: ${{ steps.pr-data.outputs.data }} with: @@ -49,7 +50,7 @@ jobs: - name: Fetch Docker image if: (env.TEST_CONTINUE) - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: workflow: ${{ github.event.workflow_run.workflow_id }} run_id: ${{ github.event.workflow_run.id }} @@ -68,11 +69,11 @@ jobs: -p 1200:1200 \ rsshub:latest - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 # just need its cache + - uses: actions/setup-node@v4 # just need its cache if: (env.TEST_CONTINUE) with: node-version: lts/* @@ -84,20 +85,20 @@ jobs: - name: Generate feedback if: (env.TEST_CONTINUE) - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: TEST_BASEURL: http://localhost:1200 - TEST_ROUTES: ${{ steps.fetchRoute.outputs.result }} + TEST_ROUTES: ${{ steps.fetch-route.outputs.result }} PULL_REQUEST: ${{ steps.pr-data.outputs.data }} with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | const PR = JSON.parse(process.env.PULL_REQUEST) const link = process.env.TEST_BASEURL const routes = JSON.parse(process.env.TEST_ROUTES) const number = PR.number core.info(`${link}, ${routes}, ${number}`) - const got = require("got"); + const got = require("got") const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/test.js`) return script({ github, context, core, got }, link, routes, number) diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 7571ecb60f6007..ffb5ec6fc4ef4c 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -1,5 +1,3 @@ -# name: '[docker] CI for build tests' -# https://github.community/t/215358 name: PR - Docker build test on: @@ -74,7 +72,7 @@ jobs: run: docker save rsshub:latest | gzip -1cf > rsshub.tar.gz - name: Upload Docker image - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docker-image path: rsshub.tar.gz diff --git a/.github/workflows/docs-search-index.yml b/.github/workflows/docs-search-index.yml index 2cca71ac98c38f..7e5f0b3469b99b 100644 --- a/.github/workflows/docs-search-index.yml +++ b/.github/workflows/docs-search-index.yml @@ -8,17 +8,19 @@ on: - '.github/workflows/docs-search-index.yml' - 'scripts/docs-scraper/docs.rsshub.app.json' - 'website/**' - workflow_dispatch: ~ + workflow_dispatch: {} schedule: - cron: '44 1 * * 1' +permissions: + contents: read + concurrency: group: docs-search-index jobs: scrape-docs: runs-on: ubuntu-latest - timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v4 @@ -28,6 +30,7 @@ jobs: run: sleep 2m if: github.event_name == 'push' - name: Run docs-scraper + timeout-minutes: 30 env: HOST_URL: ${{ secrets.MEILISEARCH_HOST_URL }} API_KEY: ${{ secrets.MEILISEARCH_API_KEY }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index e82387e8486fb9..e31a9081985bd9 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -1,4 +1,4 @@ -name: format +name: Format on: push: @@ -18,10 +18,10 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 1206b0c514e451..ac519655d621d8 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -13,6 +13,9 @@ jobs: if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && github.event.comment.author_association == 'COLLABORATOR' runs-on: ubuntu-latest timeout-minutes: 5 + permissions: + contents: write + pull-requests: write steps: - name: Checkout the latest code uses: actions/checkout@v4 @@ -32,7 +35,7 @@ jobs: contents: read issues: write steps: - - uses: bdougie/take-action@v1.5 + - uses: bdougie/take-action@v1.6.1 with: token: ${{ secrets.GITHUB_TOKEN }} trigger: '/wip' @@ -50,12 +53,12 @@ jobs: uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: version: 8 - name: Use Node.js Active LTS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: lts/* cache: 'pnpm' @@ -64,8 +67,8 @@ jobs: run: pnpm i && pnpm rb - name: Fetch affected routes - id: fetchRoute - uses: actions/github-script@v6 + id: fetch-route + uses: actions/github-script@v7 env: EVENT: ${{ toJson(github.event) }} with: @@ -88,10 +91,10 @@ jobs: - name: Generate feedback if: env.TEST_CONTINUE - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: TEST_BASEURL: http://localhost:1200 - TEST_ROUTES: ${{ steps.fetchRoute.outputs.result }} + TEST_ROUTES: ${{ steps.fetch-route.outputs.result }} EVENT: ${{ toJson(github.event) }} with: script: | @@ -109,7 +112,7 @@ jobs: run: cat ${{ github.workspace }}/logs/combined.log - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: logs path: logs diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/lint.yml similarity index 54% rename from .github/workflows/pr-lint.yml rename to .github/workflows/lint.yml index 3a1a784a5900f0..6185df836a19aa 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/lint.yml @@ -1,26 +1,35 @@ name: Linter -on: [push, pull_request, pull_request_target] +# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request +# pull_request includes [opened, reopened, synchronize] events by default +# 'edited' is required for title-lint +on: + push: {} + pull_request: + types: [opened, reopened, synchronize, edited] + pull_request_target: + types: [opened, reopened, synchronize, edited] jobs: - eslint: - name: ESLint - if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 - with: - version: 8 - - uses: actions/setup-node@v3 - with: - node-version: lts/* - cache: 'pnpm' - - run: pnpm i - - name: Lint - run: pnpm run lint + # eslint: + # name: ESLint + # if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + # runs-on: ubuntu-latest + # timeout-minutes: 5 + # steps: + # - uses: actions/checkout@v4 + # - uses: pnpm/action-setup@v3 + # with: + # version: 8 + # - uses: actions/setup-node@v4 + # with: + # node-version: lts/* + # cache: 'pnpm' + # - run: pnpm i + # - name: Lint + # run: pnpm run lint +# https://github.com/actions/starter-workflows/blob/main/code-scanning/eslint.yml eslint-warning: name: Lint if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} @@ -31,10 +40,10 @@ jobs: security-events: write steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: lts/* cache: 'pnpm' @@ -45,16 +54,19 @@ jobs: --output-file eslint-results.sarif continue-on-error: true - name: Upload analysis results to GitHub - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: eslint-results.sarif wait-for-processing: true - titleLint: +# https://github.com/amannn/action-semantic-pull-request + title-lint: if: ${{ github.event_name == 'pull_request_target' && github.repository == 'DIYgod/RSSHub' }} name: Validate PR title runs-on: ubuntu-latest timeout-minutes: 5 + permissions: + pull-requests: read steps: - uses: amannn/action-semantic-pull-request@v5 env: @@ -72,24 +84,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - chatgpt-review: - name: ChatGPT PR reviewer - if: ${{ github.event_name == 'pull_request_target' && false }} - permissions: - contents: read - pull-requests: write - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: fluxninja/openai-pr-reviewer@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - with: - openai_base_url: ${{ secrets.OPENAI_API_ENDPOINT }} - openai_model_temperature: '1.0' - disable_release_notes: true diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 03e77142c59db5..ad0b5bdd49fa84 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,4 +1,4 @@ -name: publish +name: npm Publish on: push: @@ -25,12 +25,13 @@ jobs: HUSKY: 0 steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: lts/* + # pinned to 18 until https://github.com/compulim/version-from-git/issues/16 is fixed + node-version: 18 cache: 'pnpm' registry-url: 'https://registry.npmjs.org' - name: Install dependencies (pnpm) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index d67cc7ddccd77a..3cc83b81f96f15 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -1,26 +1,37 @@ +name: Semgrep + +# https://semgrep.dev/docs/semgrep-ci/sample-ci-configs/#sample-github-actions-configuration-file on: pull_request_target: branches: - - master - paths: - - .github/workflows/semgrep.yml + - master push: branches: - - master - paths: - - .github/workflows/semgrep.yml + - master schedule: - # random HH:MM to avoid a load spike on GitHub Actions at 00:00 - - cron: 21 20 * * * -name: Semgrep + # random HH:MM to avoid a load spike on GitHub Actions at 00:00 + - cron: 21 20 * * * + +permissions: + contents: read + jobs: semgrep: name: Scan runs-on: ubuntu-latest - env: - SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} container: image: returntocorp/semgrep + if: (github.triggering_actor != 'dependabot[bot]') + permissions: + contents: read + security-events: write steps: - - uses: actions/checkout@v4 - - run: semgrep ci + - uses: actions/checkout@v4 + - run: semgrep ci --sarif > semgrep.sarif + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + - name: Upload SARIF file for GitHub Advanced Security Dashboard + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep.sarif + if: always() diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 90701a832db464..c11a215094233a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,4 +1,5 @@ name: 'Close stale issues and PRs' + on: schedule: - cron: '31 23 * * *' @@ -11,7 +12,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: # Don't stale issues days-before-issue-stale: -1 @@ -21,3 +22,6 @@ jobs: This PR is stale because it has been opened for more than 3 weeks with no activity. Comment or this will be closed in 7 days. close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.' + exempt-issue-labels: 'dependencies,wait for upstream' + exempt-pr-labels: 'dependencies,wait for upstream' + any-of-issue-labels: 'more data required' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 63356b25073d1a..2ae64e5a42740e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test +name: Test on: push: @@ -13,7 +13,7 @@ on: - 'package.json' - 'pnpm-lock.yaml' - '.github/workflows/test.yml' - pull_request: ~ + pull_request: {} permissions: contents: read @@ -31,14 +31,14 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 18, 20 ] + node-version: [ 18, 20, 21 ] name: Jest on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -51,8 +51,8 @@ jobs: env: REDIS_URL: redis://localhost:${{ job.services.redis.ports[6379] }}/ - name: Upload coverage to Codecov - if: ${{ matrix.node-version == '18' }} - uses: codecov/codecov-action@v3 + if: ${{ matrix.node-version == '20' }} + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos as documented, but seems broken @@ -62,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 18, 20 ] + node-version: [ 18, 20, 21 ] chromium: - name: bundled Chromium dependency: '' @@ -76,10 +76,10 @@ jobs: name: Jest puppeteer on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }} steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -91,7 +91,7 @@ jobs: if: ${{ matrix.chromium.dependency != '' }} # 'chromium-browser' from Ubuntu APT repo is a dummy package. Its version (85.0.4183.83) means # nothing since it calls Snap (disgusting!) to install Chromium, which should be up-to-date. - # That's not really a problem since the Chromium-bundled Docker image is based on Debian bullseye, + # That's not really a problem since the Chromium-bundled Docker image is based on Debian bookworm, # which provides up-to-date native packages. run: | set -ex @@ -111,17 +111,17 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 18, 20 ] + node-version: [ 18, 20, 21 ] defaults: run: working-directory: website name: Build docs on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -137,14 +137,14 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 18, 20 ] + node-version: [ 18, 20, 21 ] name: Build radar and maintainer on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -153,7 +153,7 @@ jobs: run: npm run build:all automerge: - if: github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' + if: github.triggering_actor == 'dependabot[bot]' && github.event_name == 'pull_request' needs: [ jest, puppeteer, docs, all ] runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/yarn-lock-changes.yml b/.github/workflows/yarn-lock-changes.yml deleted file mode 100644 index 00b14898fe60af..00000000000000 --- a/.github/workflows/yarn-lock-changes.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Yarn Lock Changes -on: - pull_request: - paths: - - '.github/workflows/yarn-lock-changes.yml' - - 'yarn.lock' - - 'docs/yarn.lock' - -jobs: - yarn_lock_changes: - runs-on: ubuntu-latest - timeout-minutes: 5 - # Permission overwrite is required for Dependabot PRs, see https://github.com/marketplace/actions/yarn-lock-changes#-common-issues. - permissions: - pull-requests: write - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Yarn Lock Changes - uses: Simek/yarn-lock-changes@v0.11.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 0f1bcc4871414c..c75ce47880cb62 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ package-lock.json # pnpm-lock.yaml yarn.lock yarn-error.log + +scripts/twitter-token/accounts.* diff --git a/.gitpod.yml b/.gitpod.yml index 1b49b8f8bd39b6..039f632e3ccf25 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -34,13 +34,5 @@ vscode: - deepscan.vscode-deepscan - rangav.vscode-thunder-client - sonarsource.sonarlint-vscode + - unifiedjs.vscode-mdx # - ZihanLi.at-helper not available on Open VSX - -github: - prebuilds: - master: true - branches: true - pullRequests: false - pullRequestsFromForks: false - addCheck: false - addComment: false diff --git a/.husky/pre-commit b/.husky/pre-commit index d24fdfc601b9ff..2312dc587f6118 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx lint-staged diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc index e426ea78adf606..f8f413b279616b 100644 --- a/.markdownlint.jsonc +++ b/.markdownlint.jsonc @@ -1,5 +1,4 @@ { - "MD007": { "indent": 4 }, // ul-indent - Unordered list indentation "MD013": false, // line-length - Line length "MD014": false, // commands-show-output - Dollar signs used before commands without showing output "MD024": { "siblings_only": true }, // no-duplicate-heading/no-duplicate-header - Multiple headings with the same content @@ -7,5 +6,7 @@ "MD033": false, // no-inline-html - Inline HTML "MD036": false, // no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading "MD040": false, // fenced-code-language - Fenced code blocks should have a language specified + "MD041": false, // first-line-heading/first-line-h1 - First line in a file should be a top-level heading + "MD045": false, // no-alt-text - Images should have alternate text (alt text) "MD051": false // link-fragments - Link fragments should be valid } diff --git a/.nvmrc b/.nvmrc index a77793ecc5200b..9de2256827aef9 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/hydrogen +lts/iron diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c63af734ae66fa..e452526ecf3d55 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -39,7 +39,7 @@ This Code of Conduct applies within all community spaces, and also applies when ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at i@diygod.me. All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. @@ -74,11 +74,11 @@ Community leaders will follow these Community Impact Guidelines in determining t ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, -available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +available at . Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. +. Translations are available at . diff --git a/Dockerfile b/Dockerfile index 0a765325cf8540..a1086b22974dd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-bullseye AS dep-builder +FROM node:20-bookworm AS dep-builder # Here we use the non-slim image to provide build-time deps (compilers and python), thus no need to install later. # This effectively speeds up qemu-based cross-build. @@ -28,11 +28,11 @@ RUN \ # --------------------------------------------------------------------------------------------------------------------- -FROM debian:bullseye-slim AS dep-version-parser +FROM debian:bookworm-slim AS dep-version-parser # This stage is necessary to limit the cache miss scope. # With this stage, any modification to package.json won't break the build cache of the next two stages as long as the # version unchanged. -# node:18-bullseye-slim is based on debian:bullseye-slim so this stage would not cause any additional download. +# node:20-bookworm-slim is based on debian:bookworm-slim so this stage would not cause any additional download. WORKDIR /ver COPY ./package.json /app/ @@ -44,7 +44,7 @@ RUN \ # --------------------------------------------------------------------------------------------------------------------- -FROM node:18-bullseye-slim AS docker-minifier +FROM node:20-bookworm-slim AS docker-minifier # The stage is used to further reduce the image size by removing unused files. WORKDIR /minifier @@ -77,7 +77,7 @@ RUN \ # --------------------------------------------------------------------------------------------------------------------- -FROM node:18-bullseye-slim AS chromium-downloader +FROM node:20-bookworm-slim AS chromium-downloader # This stage is necessary to improve build concurrency and minimize the image size. # Yeah, downloading Chromium never needs those dependencies below. @@ -109,7 +109,7 @@ RUN \ # --------------------------------------------------------------------------------------------------------------------- -FROM node:18-bullseye-slim AS app +FROM node:20-bookworm-slim AS app LABEL org.opencontainers.image.authors="https://github.com/DIYgod/RSSHub" @@ -123,7 +123,7 @@ ARG TARGETPLATFORM ARG PUPPETEER_SKIP_DOWNLOAD=1 # https://pptr.dev/troubleshooting#chrome-headless-doesnt-launch-on-unix # https://github.com/puppeteer/puppeteer/issues/7822 -# https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html#noteworthy-obsolete-packages +# https://www.debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#noteworthy-obsolete-packages # The official recommended way to use Puppeteer on arm/arm64 is to install Chromium from the distribution repositories: # https://github.com/puppeteer/puppeteer/blob/07391bbf5feaf85c191e1aa8aa78138dce84008d/packages/puppeteer-core/src/node/BrowserFetcher.ts#L128-L131 RUN \ diff --git a/README.md b/README.md index 53ca3bfe23cab7..952edaa6659e08 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,16 @@ > 🍰 Everything is RSSible -[![telegram](https://img.shields.io/badge/chat-telegram-brightgreen.svg?logo=telegram&style=flat-square)](https://t.me/rsshub) -[![npm publish](https://img.shields.io/github/actions/workflow/status/DIYgod/RSSHub/npm-publish.yml?branch=master&label=npm%20publish&logo=npm&style=flat-square)](https://www.npmjs.com/package/rsshub) -[![docker publish](https://img.shields.io/github/actions/workflow/status/DIYgod/RSSHub/docker-release.yml?branch=master&label=docker%20publish&logo=docker&style=flat-square)](https://hub.docker.com/r/diygod/rsshub) +[![](https://img.shields.io/badge/dynamic/json?url=https://rsshub-analytics.diygod.workers.dev/&query=requests&color=F38020&label=requests&logo=cloudflare&style=flat-square&suffix=/month)](https://rsshub.app) +[![docker publish](https://img.shields.io/docker/pulls/diygod/rsshub?label=docker%20pulls&logo=docker&style=flat-square)](https://hub.docker.com/r/diygod/rsshub) +[![npm publish](https://img.shields.io/npm/dt/rsshub?label=npm%20downloads&logo=npm&style=flat-square)](https://www.npmjs.com/package/rsshub) [![test](https://img.shields.io/github/actions/workflow/status/DIYgod/RSSHub/test.yml?branch=master&label=test&logo=github&style=flat-square)](https://github.com/DIYgod/RSSHub/actions/workflows/test.yml?query=event%3Apush+branch%3Amaster) [![Test coverage](https://img.shields.io/codecov/c/github/DIYgod/RSSHub.svg?style=flat-square&logo=codecov)](https://app.codecov.io/gh/DIYgod/RSSHub/branch/master) [![CodeFactor](https://www.codefactor.io/repository/github/diygod/rsshub/badge)](https://www.codefactor.io/repository/github/diygod/rsshub) [![DeepScan grade](https://deepscan.io/api/teams/6244/projects/8135/branches/92448/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=6244&pid=8135&bid=92448) +[![Telegram group](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.swo.moe%2Fstats%2Ftelegram%2Frsshub&query=count&color=2CA5E0&label=Telegram%20Group&logo=telegram&cacheSeconds=3600&style=flat-square)](https://t.me/rsshub) [![Telegram channel](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.swo.moe%2Fstats%2Ftelegram%2FawesomeRSSHub&query=count&color=2CA5E0&label=Telegram%20Channel&logo=telegram&cacheSeconds=3600&style=flat-square)](https://t.me/awesomeRSSHub) + ## Introduction RSSHub is an open source, easy to use, and extensible RSS feed generator. It's capable of generating RSS feeds from pretty much everything. @@ -30,7 +32,7 @@ RSSHub can be used with browser extension [RSSHub Radar](https://github.com/DIYg ### Special Sponsors

- +       

[![](https://opencollective.com/static/images/become_sponsor.svg)](https://docs.rsshub.app/support/) @@ -43,12 +45,12 @@ Logo designer [sheldonrrr](https://dribbble.com/sheldonrrr) ### Backers -        +               ## Related Projects - [RSSHub Radar](https://github.com/DIYgod/RSSHub-Radar) | A browser extension that can help you quickly discover and subscribe to the RSS and RSSHub of current websites. -- [RSSBud](https://github.com/Cay-Zhang/RSSBud) ([TestFlight](https://testflight.apple.com/join/rjCVzzHP)) | RSSHub Radar for iOS platform, designed specifically for mobile ecosystem optimization. +- [RSSBud](https://github.com/Cay-Zhang/RSSBud) | RSSHub Radar for iOS platform, designed specifically for mobile ecosystem optimization. - [RSSAid](https://github.com/LeetaoGoooo/RSSAid) | RSSHub Radar for Android platform built with Flutter. - [DocSearch](https://github.com/Fatpandac/DocSearch) | Link RSSHub DocSearch into Raycast @@ -78,7 +80,7 @@ Recurring donors will be rewarded via express issue response, or even have your - Become a Sponser on [Patreon](https://www.patreon.com/DIYgod) - Become a Sponser on [Open Collective](https://opencollective.com/RSSHub) - Become a Sponser on [爱发电](https://afdian.net/@diygod) -- Contact us directly: i@diygod.me +- Contact us directly: ### One-time Donation diff --git a/assets/radar-rules.js b/assets/radar-rules.js index bb0034b05f788f..1b172452956688 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -51,22 +51,7 @@ { title: '经济信息工程学院 - 学院新闻', docs: 'https://docs.rsshub.app/routes/university#xi-nan-cai-jing-da-xue', source: '/index/xyxw.htm', target: '/swufe/seie/xyxw' }, ], }, - 'ishuhui.com': { _name: '鼠绘漫画', www: [{ title: '鼠绘漫画', docs: 'https://docs.rsshub.app/routes/anime#shu-hui-man-hua', source: '/comics/anime/:id', target: '/shuhui/comics/:id' }] }, 'www.chicagotribune.com': { _name: 'Chicago Tribune', www: [{ title: 'Chicago Tribune', docs: 'https://docs.rsshub.app/routes/traditional_media#chicago-tribune', source: '/' }] }, - 'haimaoba.com': { _name: '海猫吧', www: [{ title: '漫画更新', docs: 'https://docs.rsshub.app/routes/anime#hai-mao-ba', source: '/catalog/:id', target: '/haimaoba/:id' }] }, - 'pgyer.com': { _name: '蒲公英应用分发', www: [{ title: 'app更新', docs: 'https://docs.rsshub.app/routes/program-update#pu-gong-ying-ying-yong-fen-fa', source: '/:app', target: '/pgyer/:app' }] }, - 'wineyun.com': { _name: '酒云网', www: [{ title: '最新商品', docs: 'https://docs.rsshub.app/routes/other#jiu-yun-wang', source: ['/:category'], target: '/wineyun/:category' }] }, - 'playstation.com': { - _name: 'PlayStation', - store: [ - { title: '游戏列表', docs: 'https://docs.rsshub.app/routes/game#playstation', source: '/zh-hans-hk/grid/:id/:page', target: '/ps/list/:id' }, - { title: '折扣|价格', docs: 'https://docs.rsshub.app/routes/game#playstation', source: ['/:lang/product/:gridName'], target: '/ps/:lang/product/:gridName' }, - ], - www: [ - { title: '用户奖杯', docs: 'https://docs.rsshub.app/routes/game#playstation' }, - { title: '系统更新纪录', docs: 'https://docs.rsshub.app/routes/game#playstation' }, - ], - }, 'monsterhunter.com': { _name: '怪物猎人世界', www: [ @@ -74,14 +59,6 @@ { title: '最新消息', docs: 'https://docs.rsshub.app/routes/game#guai-wu-lie-ren-shi-jie', source: ['', '/*tpath'], target: '/mhw/news' }, ], }, - 'vgtime.com': { - _name: '游戏时光', - www: [ - { title: '新闻', docs: 'https://docs.rsshub.app/routes/game#you-xi-shi-guang', source: '/topic/index.', target: '/vgtime/news' }, - { title: '游戏发售表', docs: 'https://docs.rsshub.app/routes/game#you-xi-shi-guang', source: '/game/release.', target: '/vgtime/release' }, - { title: '关键词资讯', docs: 'https://docs.rsshub.app/routes/game#you-xi-shi-guang', source: '/search/list.', target: (params, url) => `/vgtime/keyword/${new URL(url).searchParams.get('keyword')}` }, - ], - }, 'bing.com': { _name: 'Bing', www: [{ title: '每日壁纸', docs: 'https://docs.rsshub.app/routes/picture#bing-bi-zhi', source: '', target: '/bing' }] }, 'wegene.com': { _name: 'WeGene', @@ -90,7 +67,6 @@ { title: '栏目', docs: 'https://docs.rsshub.app/routes/other#wegene', source: '/crowdsourcing', target: '/wegene/column/all/all' }, ], }, - '3ycy.com': { _name: '三界异次元', www: [{ title: '最近更新', docs: 'https://docs.rsshub.app/routes/anime#san-jie-yi-ci-yuan', source: '/', target: '/3ycy/home' }] }, 'emi-nitta.net': { _name: 'Emi Nitta', '.': [ @@ -98,9 +74,6 @@ { title: '新闻', docs: 'https://docs.rsshub.app/routes/other#xin-tian-hui-hai-guan-fang-wang-zhan', source: '/contents/news', target: '/emi-nitta/news' }, ], }, - 'alter-shanghai.cn': { _name: 'Alter', '.': [{ title: '新闻', docs: 'https://docs.rsshub.app/routes/shopping#alter-zhong-guo', source: '/cn/news', target: '/alter-cn/news' }] }, - 'itslide.com': { _name: 'ITSlide', www: [{ title: '最新', docs: 'https://docs.rsshub.app/routes/programming#itslide', source: '/*', target: '/itslide/new' }] }, - 'leboncoin.fr': { _name: 'leboncoin', www: [{ title: 'ads', docs: 'https://docs.rsshub.app/routes/shopping#leboncoin', source: '/recherche', target: (params, url) => '/leboncoin/ad/' + url.split('?')[1] }] }, 'yuancheng.work': { _name: '远程.work', '.': [ @@ -117,20 +90,9 @@ }, ], }, - 'chinatimes.com': { _name: '中時電子報', www: [{ title: '新聞', docs: 'https://docs.rsshub.app/routes/traditional-media#zhong-shi-dian-zi-bao', source: '/:caty', target: (params) => '/chinatimes/' + params.caty }] }, 'govopendata.com': { _name: '新闻联播文字版', cn: [{ title: '新闻联播文字版', docs: 'https://docs.rsshub.app/routes/traditional-media#xin-wen-lian-bo-wen-zi-ban', source: '/xinwenlianbo', target: '/xinwenlianbo/index' }] }, 'steampowered.com': { _name: 'Steam', store: [{ title: 'search', docs: 'https://docs.rsshub.app/routes/game#steam', source: '/search/', target: (params, url) => `/steam/search/${new URL(url).searchParams}` }] }, - 'xiaomi.cn': { _name: '小米社区', www: [{ title: '圈子', docs: 'https://docs.rsshub.app/routes/bbs#xiao-mi-she-qu', source: '/board/:boardId', target: '/mi/bbs/board/:boardId' }] }, 'suzhou.gov.cn': { _name: '苏州市政府', www: [{ title: '政府新闻', docs: 'https://docs.rsshub.app/routes/government#su-zhou-shi-ren-min-zheng-fu', source: '/szsrmzf/:uid/nav_list.', target: '/gov/suzhou/news/:uid' }] }, - 'mqube.net': { - _name: 'MQube', - www: [ - { title: '全站最近更新', docs: 'https://docs.rsshub.app/routes/multimedia#mqube', source: '/', target: '/mqube/latest' }, - { title: '全站每日排行', docs: 'https://docs.rsshub.app/routes/multimedia#mqube', source: '/', target: '/mqube/top' }, - { title: '个人最近更新', docs: 'https://docs.rsshub.app/routes/multimedia#mqube', source: '/user/:user', target: '/mqube/user/:user' }, - { title: '标签最近更新', docs: 'https://docs.rsshub.app/routes/multimedia#mqube', source: '/search/tag/:tag', target: '/mqube/tag/:tag' }, - ], - }, 'last.fm': { _name: 'Last.fm', www: [ @@ -139,24 +101,6 @@ { title: '站内 Top 榜单', docs: 'https://docs.rsshub.app/routes/multimedia#last-fm', source: '/charts', target: '/lastfm/top' }, ], }, - 'ddrk.me': { - _name: '低端影视', - www: [ - { title: '首页', docs: 'https://docs.rsshub.app/routes/multimedia#di-duan-ying-shi', source: '/', target: '/ddrk/index' }, - { title: '标签', docs: 'https://docs.rsshub.app/routes/multimedia#di-duan-ying-shi', source: '/tag/:tag', target: '/ddrk/tag/:tag' }, - { title: '分类', docs: 'https://docs.rsshub.app/routes/multimedia#di-duan-ying-shi', source: ['/category/:category', '/category/:uplevel/:category'], target: '/ddrk/category/:category' }, - { - title: '影视剧集更新', - docs: 'https://docs.rsshub.app/routes/multimedia#di-duan-ying-shi', - source: ['/:name', '/:name/:season'], - target: (params) => { - if (params.name !== 'category' && params.name !== 'tag' && params.name !== 'ddrklogin' && params.name !== 'about' && params.name !== 'deleted') { - return `/ddrk/update/${params.name}${params.season ? '/' + params.season : ''}`; - } - }, - }, - ], - }, 'hackerone.com': { _name: 'HackerOne', '.': [{ title: 'HackerOne Hacker Activity', docs: 'https://docs.rsshub.app/routes/other#hackerone-hacker-activity', source: '/hacktivity', target: '/hackerone/hacktivity' }] }, 'cowlevel.net': { _name: '奶牛关', '.': [{ title: '元素文章', docs: 'https://docs.rsshub.app/routes/game#nai-niu-guan', source: ['/element/:id', '/element/:id/article'], target: '/cowlevel/element/:id' }] }, 'beijing.gov.cn': { wjw: [{ title: '北京卫生健康委员会', docs: 'https://docs.rsshub.app/routes/government#bei-jing-shi-wei-sheng-jian-kang-wei-yuan-hui', source: '/xwzx_20031/:caty', target: '/gov/beijing/mhc/:caty' }] }, @@ -189,7 +133,6 @@ }, ], }, - 'zhaishuyuan.com': { _name: '斋书苑', '.': [{ title: '最新章节', docs: 'https://docs.rsshub.app/routes/reading#zhai-shu-yuan', source: ['/book/:id', '/read/:id'], target: '/novel/zhaishuyuan/:id' }] }, 'hbut.edu.cn': { _name: '湖北工业大学', www: [ @@ -224,7 +167,7 @@ docs: 'https://docs.rsshub.app/routes/multimedia#onejav', source: '/', target: (params, url, document) => { - const today = document.querySelector('div.card.mb-1.card-overview').getAttribute('data-date').replace(/-/g, ''); + const today = document.querySelector('div.card.mb-1.card-overview').dataset.date.replaceAll('-', ''); return `/onejav/day/${today}`; }, }, @@ -272,7 +215,7 @@ } else { return false; } - return `/sexinsex/${pid}/${typeid ? typeid : ''}`; + return `/sexinsex/${pid}/${typeid ?? ''}`; }, }, ], @@ -287,7 +230,7 @@ target: (params, url) => { const id = new URL(url).searchParams.get('fid'); const type = new URL(url).searchParams.get('type'); - return `/t66y/${id}/${type ? type : ''}`; + return `/t66y/${id}/${type ?? ''}`; }, }, ], @@ -312,22 +255,6 @@ { title: '首页 / 自考快递', docs: 'https://docs.rsshub.app/routes/government#bei-jing-jiao-yu-kao-shi-yuan', source: ['/zkkd'], target: '/gov/beijing/bjeea/zkkd' }, ], }, - 'ems.com.cn': { _name: '中国邮政速递物流', www: [{ title: '新闻', docs: 'https://docs.rsshub.app/routes/other#zhong-guo-you-zheng-su-di-wu-liu', source: '/aboutus/xin_wen_yu_shi_jian', target: '/ems/news' }] }, - 'popiapp.cn': { - _name: 'Popi 提问箱', - www: [ - { - title: '提问箱新回答', - docs: 'https://docs.rsshub.app/routes/social-media#popi-ti-wen-xiang', - source: '/:id', - target: (params) => { - if (params.id) { - return '/popiask/:id'; - } - }, - }, - ], - }, 'nppa.gov.cn': { _name: '国家新闻出版署', www: [ @@ -361,30 +288,8 @@ }, ], }, - 'buaq.net': { _name: '不安全资讯', '.': [{ title: '不安全资讯', docs: 'http://docs.rsshub.app/routes/new-media#bu-an-quan', source: '/', target: '/buaq' }] }, - 'jian-ning.com': { _name: '建宁闲谈', '.': [{ title: '文章', docs: 'https://docs.rsshub.app/routes/blog#jian-ning-xian-tan', source: '/*', target: '/blogs/jianning' }] }, - 'matataki.io': { - _name: 'matataki', - www: [ - { title: '最热作品', docs: 'https://docs.rsshub.app/routes/new-media#matataki', source: '/article/', target: '/matataki/posts/hot' }, - { title: '最新作品', docs: 'https://docs.rsshub.app/routes/new-media#matataki', source: '/article/latest', target: '/matataki/posts/latest' }, - { title: '作者创作', docs: 'https://docs.rsshub.app/routes/new-media#matataki', source: '/user/:uid', target: (params) => `/matataki/users/${params.uid}/posts` }, - { title: 'Fan票关联作品', docs: 'https://docs.rsshub.app/routes/new-media#matataki', source: ['/token/:tokenId', '/token/:tokenId/circle'], target: (params) => `/matataki/tokens/${params.tokenId}/posts` }, - { - title: '标签关联作品', - docs: 'https://docs.rsshub.app/routes/new-media#matataki', - source: ['/tag/:tagId'], - target: (params, url) => { - const tagName = new URL(url).searchParams.get('name'); - return `/matataki/tags/${params.tagId}/${tagName}/posts`; - }, - }, - { title: '收藏夹', docs: 'https://docs.rsshub.app/routes/new-media#matataki', source: '/user/:uid/favlist/:fid', target: (params) => `/matataki/users/${params.uid}/favorites/${params.fid}/posts` }, - ], - }, 'eventernote.com': { _name: 'Eventernote', www: [{ title: '声优活动及演唱会', docs: 'https://docs.rsshub.app/routes/anime#eventernote', source: '/actors/:name/:id/events', target: '/eventernote/actors/:name/:id' }] }, 'huya.com': { _name: '虎牙直播', '.': [{ title: '直播间开播', docs: 'https://docs.rsshub.app/routes/live#hu-ya-zhi-bo-zhi-bo-jian-kai-bo', source: '/:id', target: '/huya/live/:id' }] }, - 'craigslist.org': { _name: 'Craigslist', '.': [{ title: '商品搜索列表', docs: 'https://docs.rsshub.app/routes/shopping#craigslist' }] }, 'scboy.com': { _name: 'scboy 论坛', www: [ @@ -470,7 +375,6 @@ { title: '日记评论区', docs: 'https://docs.rsshub.app/routes/social-media#fur-affinity', source: '/journal/:id/', target: '/furaffinity/journal_comments/:id' }, ], }, - 'macwk.com': { _name: 'MacWk', '.': [{ title: '应用更新', docs: 'https://docs.rsshub.app/routes/program-update#macwk', source: '/soft/:name', target: '/macwk/soft/:name' }] }, 'foreverblog.cn': { _name: 'foreverblog', www: [ diff --git a/docker-compose.yml b/docker-compose.yml index e404598334deaa..1cd2d225ed5828 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,11 +36,12 @@ services: warp-socks: image: monius/docker-warp-socks:latest privileged: true + restart: always volumes: - /lib/modules:/lib/modules cap_add: - NET_ADMIN - - SYS_ADMIN + - SYS_MODULE sysctls: net.ipv6.conf.all.disable_ipv6: 0 net.ipv4.conf.all.src_valid_mark: 1 diff --git a/lib/api_router.js b/lib/api-router.js similarity index 88% rename from lib/api_router.js rename to lib/api-router.js index b452d60666babb..bed73e393cc4f2 100644 --- a/lib/api_router.js +++ b/lib/api-router.js @@ -6,8 +6,7 @@ router.get('/routes/:name?', (ctx) => { let counter = 0; const maintainer = require('./maintainer'); - Object.keys(maintainer).forEach((i) => { - const path = i; + for (const path of Object.keys(maintainer)) { const top = path.split('/')[1]; if (!ctx.params.name || top === ctx.params.name) { @@ -18,7 +17,7 @@ router.get('/routes/:name?', (ctx) => { } counter++; } - }); + } ctx.body = { counter, result }; }); diff --git a/lib/app.js b/lib/app.js index 39b91f574e1c78..720da7f712a588 100644 --- a/lib/app.js +++ b/lib/app.js @@ -20,13 +20,13 @@ const antiHotlink = require('./middleware/anti-hotlink'); const loadOnDemand = require('./middleware/load-on-demand'); const router = require('./router'); -const core_router = require('./core_router'); -const protected_router = require('./protected_router'); +const core_router = require('./core-router'); +const protected_router = require('./protected-router'); const mount = require('koa-mount'); // API related const apiTemplate = require('./middleware/api-template'); -const api_router = require('./api_router'); +const api_router = require('./api-router'); const apiResponseHandler = require('./middleware/api-response-handler'); process.on('uncaughtException', (e) => { @@ -37,8 +37,8 @@ const app = new Koa(); app.proxy = true; // favicon -app.use(favicon(__dirname + '/favicon.png', { maxAge: 31536000000 })); -app.use(serve(__dirname + '/static', { maxage: 31536000000 })); +app.use(favicon(__dirname + '/favicon.png', { maxAge: 31_536_000_000 })); +app.use(serve(__dirname + '/static', { maxage: 31_536_000_000 })); // global error handing app.use(onerror); diff --git a/lib/config.js b/lib/config.js index d435c4e50e0dd4..00c978a8a6ed5d 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,12 +1,12 @@ require('dotenv').config(); const randUserAgent = require('./utils/rand-user-agent'); +const got = require('got'); let envs = process.env; let value; const TRUE_UA = 'RSSHub/1.0 (+http://github.com/DIYgod/RSSHub; like FeedFetcher-Google)'; const calculateValue = () => { const bilibili_cookies = {}; - const twitter_tokens = {}; const email_config = {}; const discuz_cookies = {}; const medium_cookies = {}; @@ -16,9 +16,6 @@ const calculateValue = () => { if (name.startsWith('BILIBILI_COOKIE_')) { const uid = name.slice(16); bilibili_cookies[uid] = envs[name]; - } else if (name.startsWith('TWITTER_TOKEN_')) { - const id = name.slice(14); - twitter_tokens[id] = envs[name]; } else if (name.startsWith('EMAIL_CONFIG_')) { const id = name.slice(13); email_config[id] = envs[name]; @@ -48,21 +45,21 @@ const calculateValue = () => { socket: envs.SOCKET || null, // 监听 Unix Socket, null 为禁用 }, listenInaddrAny: envs.LISTEN_INADDR_ANY || 1, // 是否允许公网连接,取值 0 1 - requestRetry: parseInt(envs.REQUEST_RETRY) || 2, // 请求失败重试次数 - requestTimeout: parseInt(envs.REQUEST_TIMEOUT) || 30000, // Milliseconds to wait for the server to end the response before aborting the request - ua: envs.UA ? envs.UA : envs.NO_RANDOM_UA === 'true' || envs.NO_RANDOM_UA === '1' ? TRUE_UA : randUserAgent({ browser: 'chrome', os: 'mac os', device: 'desktop' }), + requestRetry: Number.parseInt(envs.REQUEST_RETRY) || 2, // 请求失败重试次数 + requestTimeout: Number.parseInt(envs.REQUEST_TIMEOUT) || 30000, // Milliseconds to wait for the server to end the response before aborting the request + ua: envs.UA ?? (envs.NO_RANDOM_UA === 'true' || envs.NO_RANDOM_UA === '1' ? TRUE_UA : randUserAgent({ browser: 'chrome', os: 'mac os', device: 'desktop' })), trueUA: TRUE_UA, // cors request allowOrigin: envs.ALLOW_ORIGIN, // cache cache: { - type: typeof envs.CACHE_TYPE === 'undefined' ? 'memory' : envs.CACHE_TYPE, // 缓存类型,支持 'memory' 和 'redis',设为空可以禁止缓存 - requestTimeout: parseInt(envs.CACHE_REQUEST_TIMEOUT) || 60, - routeExpire: parseInt(envs.CACHE_EXPIRE) || 5 * 60, // 路由缓存时间,单位为秒 - contentExpire: parseInt(envs.CACHE_CONTENT_EXPIRE) || 1 * 60 * 60, // 不变内容缓存时间,单位为秒 + type: envs.CACHE_TYPE === undefined ? 'memory' : envs.CACHE_TYPE, // 缓存类型,支持 'memory' 和 'redis',设为空可以禁止缓存 + requestTimeout: Number.parseInt(envs.CACHE_REQUEST_TIMEOUT) || 60, + routeExpire: Number.parseInt(envs.CACHE_EXPIRE) || 5 * 60, // 路由缓存时间,单位为秒 + contentExpire: Number.parseInt(envs.CACHE_CONTENT_EXPIRE) || 1 * 60 * 60, // 不变内容缓存时间,单位为秒 }, memory: { - max: parseInt(envs.MEMORY_MAX) || Math.pow(2, 8), // The maximum number of items that remain in the cache. This must be a positive finite intger. + max: Number.parseInt(envs.MEMORY_MAX) || Math.pow(2, 8), // The maximum number of items that remain in the cache. This must be a positive finite intger. // https://github.com/isaacs/node-lru-cache#options }, redis: { @@ -79,14 +76,16 @@ const calculateValue = () => { }, proxyStrategy: envs.PROXY_STRATEGY || 'all', // all / on_retry reverseProxyUrl: envs.REVERSE_PROXY_URL, + pacUri: envs.PAC_URI, + pacScript: envs.PAC_SCRIPT, // auth authentication: { name: envs.HTTP_BASIC_AUTH_NAME || 'usernam3', pass: envs.HTTP_BASIC_AUTH_PASS || 'passw0rd', }, // access control - blacklist: envs.BLACKLIST && envs.BLACKLIST.split(','), - whitelist: envs.WHITELIST && envs.WHITELIST.split(','), + denylist: envs.DENYLIST && envs.DENYLIST.split(','), + allowlist: envs.ALLOWLIST && envs.ALLOWLIST.split(','), allowLocalhost: envs.ALLOW_LOCALHOST, accessKey: envs.ACCESS_KEY, // logging @@ -97,7 +96,7 @@ const calculateValue = () => { showLoggerTimestamp: envs.SHOW_LOGGER_TIMESTAMP, sentry: { dsn: envs.SENTRY, - routeTimeout: parseInt(envs.SENTRY_ROUTE_TIMEOUT) || 30000, + routeTimeout: Number.parseInt(envs.SENTRY_ROUTE_TIMEOUT) || 30000, }, // feed config hotlink: { @@ -111,11 +110,20 @@ const calculateValue = () => { allow_user_supply_unsafe_domain: envs.ALLOW_USER_SUPPLY_UNSAFE_DOMAIN === 'true', }, suffix: envs.SUFFIX, - titleLengthLimit: parseInt(envs.TITLE_LENGTH_LIMIT) || 150, + titleLengthLimit: Number.parseInt(envs.TITLE_LENGTH_LIMIT) || 150, + openai: { + apiKey: envs.OPENAI_API_KEY, + model: envs.OPENAI_MODEL || 'gpt-3.5-turbo-16k', + temperature: envs.OPENAI_TEMPERATURE || 0.2, + maxTokens: envs.OPENAI_MAX_TOKENS || null, + endpoint: envs.OPENAI_API_ENDPOINT || 'https://api.openai.com/v1', + prompt: envs.OPENAI_PROMPT || 'Please summarize the following article and reply with markdown format.', + }, // Route-specific Configurations bilibili: { cookies: bilibili_cookies, + dmImgList: envs.BILIBILI_DM_IMG_LIST, }, bitbucket: { username: envs.BITBUCKET_USERNAME, @@ -128,9 +136,6 @@ const calculateValue = () => { bupt: { portal_cookie: envs.BUPT_PORTAL_COOKIE, }, - chuiniu: { - member: envs.CHUINIU_MEMBER, - }, civitai: { cookie: envs.CIVITAI_COOKIE, }, @@ -214,6 +219,9 @@ const calculateValue = () => { lastfm: { api_key: envs.LASTFM_API_KEY, }, + lightnovel: { + cookie: envs.SECURITY_KEY, + }, manhuagui: { cookie: envs.MHGUI_COOKIE, }, @@ -226,6 +234,9 @@ const calculateValue = () => { cookies: medium_cookies, articleCookie: envs.MEDIUM_ARTICLE_COOKIE || '', }, + mihoyo: { + cookie: envs.MIHOYO_COOKIE, + }, miniflux: { instance: envs.MINIFLUX_INSTANCE || 'https://reader.miniflux.app', token: envs.MINIFLUX_TOKEN || '', @@ -287,10 +298,11 @@ const calculateValue = () => { cookie: envs.TOPHUB_COOKIE, }, twitter: { - consumer_key: envs.TWITTER_CONSUMER_KEY, - consumer_secret: envs.TWITTER_CONSUMER_SECRET, - tokens: twitter_tokens, - authorization: envs.TWITTER_WEBAPI_AUTHORIZAION && envs.TWITTER_WEBAPI_AUTHORIZAION.split(','), + oauthTokens: envs.TWITTER_OAUTH_TOKEN?.split(','), + oauthTokenSecrets: envs.TWITTER_OAUTH_TOKEN_SECRET?.split(','), + username: envs.TWITTER_USERNAME, + password: envs.TWITTER_PASSWORD, + authenticationSecret: envs.TWITTER_AUTHENTICATION_SECRET, }, weibo: { app_key: envs.WEIBO_APP_KEY, @@ -327,6 +339,21 @@ const calculateValue = () => { }; calculateValue(); +if (envs.REMOTE_CONFIG) { + got.get(envs.REMOTE_CONFIG) + .then((response) => { + const data = JSON.parse(response.body); + if (data) { + envs = Object.assign(envs, data); + calculateValue(); + require('@/utils/logger').info('Remote config loaded.'); + } + }) + .catch((error) => { + require('@/utils/logger').error('Remote config load failed.', error); + }); +} + module.exports = { set: (env) => { envs = Object.assign(process.env, env); diff --git a/lib/core_router.js b/lib/core-router.js similarity index 100% rename from lib/core_router.js rename to lib/core-router.js diff --git a/lib/index.js b/lib/index.js index cf7e0f63fc0097..730a73c0964b90 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,7 +16,7 @@ if (config.enableCluster && cluster.isMaster && process.env.NODE_ENV !== 'test' if (fs.existsSync(config.connect.socket)) { fs.unlinkSync(config.connect.socket); } - server = app.listen(config.connect.socket, parseInt(config.listenInaddrAny) ? null : '127.0.0.1'); + server = app.listen(config.connect.socket, Number.parseInt(config.listenInaddrAny) ? null : '127.0.0.1'); logger.info('Listening Unix Socket ' + config.connect.socket); process.on('SIGINT', () => { fs.unlinkSync(config.connect.socket); @@ -24,7 +24,7 @@ if (config.enableCluster && cluster.isMaster && process.env.NODE_ENV !== 'test' }); } if (config.connect.port) { - server = app.listen(config.connect.port, parseInt(config.listenInaddrAny) ? null : '127.0.0.1'); + server = app.listen(config.connect.port, Number.parseInt(config.listenInaddrAny) ? null : '127.0.0.1'); logger.info('Listening Port ' + config.connect.port); } diff --git a/lib/maintainer.js b/lib/maintainer.js index 9efb9f448b62d8..5417992ef45f39 100644 --- a/lib/maintainer.js +++ b/lib/maintainer.js @@ -1,12 +1,12 @@ const dirname = __dirname + '/v2'; const fs = require('fs'); -const { join } = require('path'); +const path = require('path'); // Presence Check for (const dir of fs.readdirSync(dirname)) { - const dirPath = join(dirname, dir); - if (fs.existsSync(join(dirPath, 'router.js')) && !fs.existsSync(join(dirPath, 'maintainer.js'))) { - throw Error(`No maintainer.js in "${dirPath}".`); + const dirPath = path.join(dirname, dir); + if (fs.existsSync(path.join(dirPath, 'router.js')) && !fs.existsSync(path.join(dirPath, 'maintainer.js'))) { + throw new Error(`No maintainer.js in "${dirPath}".`); } } @@ -24,29 +24,34 @@ for (const dir in maintainerPath) { // typo check e.g., ✘ module.export, ✔ module.exports if (!Object.keys(routes).length) { - throw Error(`No maintainer in "${dir}".`); + throw new Error(`No maintainer in "${dir}".`); } for (const author of Object.values(routes)) { if (!Array.isArray(author)) { - throw Error(`Maintainers' name should be an array in "${dir}".`); + throw new TypeError(`Maintainers' name should be an array in "${dir}".`); } // check for [], [''] or ['Someone', ''] if (author.length < 1 || author.includes('')) { - throw Error(`Empty maintainer in "${dir}".`); + throw new Error(`Empty maintainer in "${dir}".`); } } for (const key in routes) { - maintainers['/' + dir + key] = routes[key]; + maintainers['/' + dir + (key.endsWith('/') ? key.substring(0, key.length - 1) : key)] = routes[key]; } } // 兼容旧版路由 const router = require('./router'); -router.stack.forEach((e) => { +for (const e of router.stack) { if (!maintainers[e.path]) { maintainers[e.path] = []; } -}); +} -module.exports = maintainers; +module.exports = Object.keys(maintainers) + .sort() + .reduce((obj, path) => { + obj[path] = maintainers[path]; + return obj; + }, {}); diff --git a/lib/middleware/access-control.js b/lib/middleware/access-control.js index 85d05936972504..a7b4ab2ba5aca6 100644 --- a/lib/middleware/access-control.js +++ b/lib/middleware/access-control.js @@ -5,9 +5,30 @@ const isLocalhost = require('is-localhost-ip'); const reject = (ctx) => { ctx.response.status = 403; - throw Error('Authentication failed. Access denied.'); + throw new Error('Authentication failed. Access denied.'); }; +const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/; +const cidrPattern = /((?:\d{1,3}\.){3}\d{1,3})\/(\d{1,2})/; + +const ipInCidr = (cidr, ip) => { + const cidrMatch = cidr.match(cidrPattern); + const ipMatch = ip.match(ipv4Pattern); + if (!cidrMatch || !ipMatch) { + return false; + } + const subnetMask = Number.parseInt(cidrMatch[2]); + const cidrIpBits = ipv4ToBitsring(cidrMatch[1]).substring(0, subnetMask); + const ipBits = ipv4ToBitsring(ip).substring(0, subnetMask); + return cidrIpBits === ipBits; +}; + +const ipv4ToBitsring = (ip) => + ip + .split('.') + .map((part) => ('00000000' + Number.parseInt(part).toString(2)).slice(-8)) + .join(''); + module.exports = async (ctx, next) => { const ip = ctx.ips[0] || ctx.ip; const requestPath = ctx.request.path; @@ -15,7 +36,7 @@ module.exports = async (ctx, next) => { const accessKey = ctx.query.key; const accessCode = ctx.query.code; - const isControlled = config.accessKey || config.whitelist || config.blacklist; + const isControlled = config.accessKey || config.allowlist || config.denylist; const allowLocalhost = config.allowLocalhost && (await isLocalhost(ip)); @@ -32,22 +53,16 @@ module.exports = async (ctx, next) => { return grant(); } - if (config.accessKey) { - if (config.accessKey === accessKey || accessCode === md5(requestPath + config.accessKey)) { - return grant(); - } + if (config.accessKey && (config.accessKey === accessKey || accessCode === md5(requestPath + config.accessKey))) { + return grant(); } - if (config.whitelist) { - if (config.whitelist.find((white) => ip.includes(white) || requestPath.includes(white) || requestUA.includes(white))) { - return grant(); - } + if (config.allowlist && config.allowlist.some((item) => ip.includes(item) || ipInCidr(item, ip) || requestPath.includes(item) || requestUA.includes(item))) { + return grant(); } - if (config.blacklist) { - if (!config.blacklist.find((black) => ip.includes(black) || requestPath.includes(black) || requestUA.includes(black))) { - return grant(); - } + if (config.denylist && !config.denylist.some((item) => ip.includes(item) || ipInCidr(item, ip) || requestPath.includes(item) || requestUA.includes(item))) { + return grant(); } reject(ctx); diff --git a/lib/middleware/anti-hotlink.js b/lib/middleware/anti-hotlink.js index 04948701009cda..82c56de4f58c5c 100644 --- a/lib/middleware/anti-hotlink.js +++ b/lib/middleware/anti-hotlink.js @@ -4,8 +4,8 @@ const logger = require('@/utils/logger'); const path = require('path'); const { art } = require('@/utils/render'); -const templateRegex = /\$\{([^{}]+)}/g; -const allowedUrlProperties = ['hash', 'host', 'hostname', 'href', 'origin', 'password', 'pathname', 'port', 'protocol', 'search', 'searchParams', 'username']; +const templateRegex = /\${([^{}]+)}/g; +const allowedUrlProperties = new Set(['hash', 'host', 'hostname', 'href', 'origin', 'password', 'pathname', 'port', 'protocol', 'search', 'searchParams', 'username']); const IframeWrapperTemplate = path.join(__dirname, 'templates/iframe.art'); // match path or sub-path @@ -26,7 +26,7 @@ const filterPath = (path) => { }; const interpolate = (str, obj) => - str.replace(templateRegex, (_, prop) => { + str.replaceAll(templateRegex, (_, prop) => { let needEncode = false; if (prop.endsWith('_ue')) { // url encode @@ -39,7 +39,7 @@ const parseUrl = (str) => { let url; try { url = new URL(str); - } catch (e) { + } catch { logger.error(`Failed to parse ${str}`); } @@ -70,6 +70,7 @@ const process = (html, image_hotlink_template, multimedia_hotlink_template, wrap if (image_hotlink_template) { replaceUrls($, 'img, picture > source', image_hotlink_template); replaceUrls($, 'video[poster]', image_hotlink_template, 'poster'); + replaceUrls($, '*[data-rsshub-image="href"]', image_hotlink_template, 'href'); } if (multimedia_hotlink_template) { replaceUrls($, 'video, video > source, audio, audio > source', multimedia_hotlink_template); @@ -87,12 +88,12 @@ const validateTemplate = (template) => { if (!template) { return; } - [...template.matchAll(templateRegex)].forEach((match) => { + for (const match of template.matchAll(templateRegex)) { const prop = match[1].endsWith('_ue') ? match[1].slice(0, -3) : match[1]; - if (!allowedUrlProperties.includes(prop)) { + if (!allowedUrlProperties.has(prop)) { throw new Error(`Invalid URL property: ${prop}`); } - }); + } }; module.exports = async (ctx, next) => { @@ -115,11 +116,7 @@ module.exports = async (ctx, next) => { // Force config hotlink template on conflict if (config.hotlink.template) { - if (!filterPath(ctx.request.path)) { - image_hotlink_template = undefined; - } else { - image_hotlink_template = config.hotlink.template; - } + image_hotlink_template = filterPath(ctx.request.path) ? config.hotlink.template : undefined; } if (!image_hotlink_template && !multimedia_hotlink_template && !shouldWrapInIframe) { @@ -138,11 +135,12 @@ module.exports = async (ctx, next) => { ctx.state.data.description = process(ctx.state.data.description, image_hotlink_template, multimedia_hotlink_template, shouldWrapInIframe); } - ctx.state.data.item && - ctx.state.data.item.forEach((item) => { + if (ctx.state.data.item) { + for (const item of ctx.state.data.item) { if (item.description) { item.description = process(item.description, image_hotlink_template, multimedia_hotlink_template, shouldWrapInIframe); } - }); + } + } } }; diff --git a/lib/middleware/cache/index.js b/lib/middleware/cache/index.js index 52c0b3aeaaec0f..bde7cdc0aa0594 100644 --- a/lib/middleware/cache/index.js +++ b/lib/middleware/cache/index.js @@ -1,4 +1,4 @@ -const md5 = require('@/utils/md5'); +const xxhash = require('xxhash-wasm'); const config = require('@/config').value; const logger = require('@/utils/logger'); const { RequestInProgressError } = require('@/errors'); @@ -57,22 +57,22 @@ module.exports = function (app) { ...cacheModule, tryGet: async (key, getValueFunc, maxAge = config.cache.contentExpire, refresh = true) => { if (typeof key !== 'string') { - throw Error('Cache key must be a string'); + throw new TypeError('Cache key must be a string'); } let v = await get(key, refresh); - if (!v) { - v = await getValueFunc(); - set(key, v, maxAge); - } else { + if (v) { let parsed; try { parsed = JSON.parse(v); - } catch (e) { + } catch { parsed = null; } if (parsed) { v = parsed; } + } else { + v = await getValueFunc(); + set(key, v, maxAge); } return v; @@ -81,8 +81,9 @@ module.exports = function (app) { }; return async (ctx, next) => { - const key = 'koa-redis-cache:' + md5(ctx.request.path); - const controlKey = 'path-requested:' + md5(ctx.request.path); + const { h64ToString } = await xxhash(); + const key = 'rsshub:koa-redis-cache:' + h64ToString(ctx.request.path); + const controlKey = 'rsshub:path-requested:' + h64ToString(ctx.request.path); if (!status.available) { return next(); @@ -111,7 +112,7 @@ module.exports = function (app) { ctx.state.data = JSON.parse(value); return; } - } catch (e) { + } catch { // } @@ -120,9 +121,9 @@ module.exports = function (app) { try { await next(); - } catch (e) { + } catch (error) { await globalCache.set(controlKey, '0', config.cache.requestTimeout); - throw e; + throw error; } if (ctx.response.get('Cache-Control') !== 'no-cache' && ctx.state && ctx.state.data) { diff --git a/lib/middleware/cache/redis.js b/lib/middleware/cache/redis.js index 1e9889d0f6f7f9..b7d57b97426e01 100644 --- a/lib/middleware/cache/redis.js +++ b/lib/middleware/cache/redis.js @@ -19,10 +19,10 @@ redisClient.on('connect', () => { }); const getCacheTtlKey = (key) => { - if (key.startsWith('cacheTtl:')) { - throw Error('"cacheTtl:" prefix is reserved for the internal usage, please change your cache key'); // blocking any attempt to get/set the cacheTtl + if (key.startsWith('rsshub:cacheTtl:')) { + throw new Error('"rsshub:cacheTtl:" prefix is reserved for the internal usage, please change your cache key'); // blocking any attempt to get/set the cacheTtl } - return `cacheTtl:${key}`; + return `rsshub:cacheTtl:${key}`; }; module.exports = { @@ -31,13 +31,13 @@ module.exports = { const cacheTtlKey = getCacheTtlKey(key); let [value, cacheTtl] = await redisClient.mget(key, cacheTtlKey); if (value && refresh) { - if (!cacheTtl) { + if (cacheTtl) { + redisClient.expire(cacheTtlKey, cacheTtl); + } else { // if cacheTtl is not set, that means the cache expire time is contentExpire cacheTtl = config.cache.contentExpire; // dont save cacheTtl to Redis, as it is the default value // redisClient.set(cacheTtlKey, cacheTtl, 'EX', cacheTtl); - } else { - redisClient.expire(cacheTtlKey, cacheTtl); } redisClient.expire(key, cacheTtl); value = value + ''; diff --git a/lib/middleware/header.js b/lib/middleware/header.js index 2292ddbed4eeac..762363f9d492f2 100644 --- a/lib/middleware/header.js +++ b/lib/middleware/header.js @@ -24,7 +24,7 @@ module.exports = async (ctx, next) => { return; } - const status = (ctx.status / 100) | 0; + const status = Math.trunc(ctx.status / 100); if (2 !== status) { return; } diff --git a/lib/middleware/load-on-demand.js b/lib/middleware/load-on-demand.js index 0c15bb42305437..837a17cc77d60e 100644 --- a/lib/middleware/load-on-demand.js +++ b/lib/middleware/load-on-demand.js @@ -11,7 +11,9 @@ module.exports = function (app) { if (p.length > 0) { modName = p[0]; - if (!loadedRoutes.has(modName)) { + if (loadedRoutes.has(modName)) { + mounted = true; + } else { const mod = routes[modName]; // Mount module if (mod) { @@ -21,8 +23,6 @@ module.exports = function (app) { mod(router); app.use(mount(`/${modName}`, router.routes())).use(router.allowedMethods()); } - } else { - mounted = true; } } diff --git a/lib/middleware/onerror.js b/lib/middleware/onerror.js index 04c9c4c9f97836..a4272b0aaef4d9 100644 --- a/lib/middleware/onerror.js +++ b/lib/middleware/onerror.js @@ -13,24 +13,22 @@ if (config.sentry.dsn) { Sentry.init({ dsn: config.sentry.dsn, }); - Sentry.configureScope((scope) => { - scope.setTag('node_name', config.nodeName); - }); + Sentry.getCurrentScope().setTag('node_name', config.nodeName); logger.info('Sentry inited.'); } try { gitHash = require('git-rev-sync').short(); -} catch (e) { +} catch { gitHash = (process.env.HEROKU_SLUG_COMMIT && process.env.HEROKU_SLUG_COMMIT.slice(0, 7)) || (process.env.VERCEL_GIT_COMMIT_SHA && process.env.VERCEL_GIT_COMMIT_SHA.slice(0, 7)) || 'unknown'; } module.exports = async (ctx, next) => { try { - const time = +new Date(); + const time = Date.now(); await next(); - if (config.sentry.dsn && +new Date() - time >= config.sentry.routeTimeout) { + if (config.sentry.dsn && Date.now() - time >= config.sentry.routeTimeout) { Sentry.withScope((scope) => { scope.setTag('route', ctx._matchedRoute); scope.setTag('name', ctx.request.path.split('/')[1]); @@ -38,12 +36,20 @@ module.exports = async (ctx, next) => { Sentry.captureException(new Error('Route Timeout')); }); } - } catch (err) { - let message = err; - if (err.name && (err.name === 'HTTPError' || err.name === 'RequestError')) { - message = `${err.message}: target website might be blocking our access, you can host your own RSSHub instance for a better usability.`; - } else if (err instanceof Error) { - message = process.env.NODE_ENV === 'production' ? err.message : err.stack; + } catch (error) { + if (error instanceof Error && !error.stack.split('\n')[1].includes('lib/middleware/parameter.js')) { + // Append v2 route path if a route throws an error + // since koa-mount will remove the mount path from ctx.request.path + // https://github.com/koajs/mount/issues/62 + ctx.request.path = (ctx.mountPath ?? '') + ctx.request.path; + ctx._matchedRoute = ctx._matchedRoute ? (ctx.mountPath ?? '') + ctx._matchedRoute : ctx._matchedRoute; + } + + let message = error; + if (error.name && (error.name === 'HTTPError' || error.name === 'RequestError')) { + message = `${error.message}: target website might be blocking our access, you can host your own RSSHub instance for a better usability.`; + } else if (error instanceof Error) { + message = process.env.NODE_ENV === 'production' ? error.message : error.stack; } logger.error(`Error in ${ctx.request.path}: ${message}`); @@ -51,7 +57,7 @@ module.exports = async (ctx, next) => { if (config.isPackage) { ctx.body = { error: { - message: err.message ? err.message : err, + message: error.message ?? error, }, }; } else { @@ -59,12 +65,12 @@ module.exports = async (ctx, next) => { 'Content-Type': 'text/html; charset=UTF-8', }); - if (err instanceof RequestInProgressError) { + if (error instanceof RequestInProgressError) { ctx.status = 503; - message = err.message; + message = error.message; ctx.set('Cache-Control', `public, max-age=${config.cache.requestTimeout}`); } else if (ctx.status === 403) { - message = err.message; + message = error.message; } else { ctx.status = 404; } @@ -106,7 +112,7 @@ module.exports = async (ctx, next) => { scope.setTag('route', ctx._matchedRoute); scope.setTag('name', ctx.request.path.split('/')[1]); scope.addEventProcessor((event) => Sentry.Handlers.parseRequest(event, ctx.request)); - Sentry.captureException(err); + Sentry.captureException(error); }); } } diff --git a/lib/middleware/parameter.js b/lib/middleware/parameter.js index edd0da8290ffae..5bcfbe14fc5ae2 100644 --- a/lib/middleware/parameter.js +++ b/lib/middleware/parameter.js @@ -4,6 +4,11 @@ const { simplecc } = require('simplecc-wasm'); const got = require('@/utils/got'); const config = require('@/config').value; const { RE2JS } = require('re2js'); +const md = require('markdown-it')({ + html: true, +}); +const htmlToText = require('html-to-text'); +const sanitizeHtml = require('sanitize-html'); let mercury_parser; @@ -17,22 +22,42 @@ const resolveRelativeLink = ($, elem, attr, baseUrl) => { // e.g. should leave {{ else if item.type === 'img_multi' }} - {{ each images i }} - + {{ each item.images i }} + {{ /each }} {{ else if item.type === 'img_sig' }} - + {{ /if }}
{{@ item.sum }} diff --git a/lib/v2/picnob/user.js b/lib/v2/picnob/user.js index 6c35449db2e1fc..f8c4dc29edd1e9 100644 --- a/lib/v2/picnob/user.js +++ b/lib/v2/picnob/user.js @@ -3,53 +3,90 @@ const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); const { art } = require('@/utils/render'); const path = require('path'); +const { puppeteerGet } = require('./utils'); module.exports = async (ctx) => { const baseUrl = 'https://www.picnob.com'; const { id } = ctx.params; const url = `${baseUrl}/profile/${id}/`; - const { data: response } = await got(url); - const $ = cheerio.load(response); + const browser = await require('@/utils/puppeteer')(); + // TODO: can't bypass cloudflare 403 error without puppeteer + let html; + let usePuppeteer = false; + try { + const { data } = await got(url, { + headers: { + accept: 'text/html', + referer: 'https://www.google.com/', + }, + }); + html = data; + } catch (error) { + if (error.message.includes('code 403')) { + html = await puppeteerGet(url, browser); + usePuppeteer = true; + } + } + const $ = cheerio.load(html); const profileName = $('h1.fullname').text(); const userId = $('input[name=userid]').attr('value'); - const { data } = await got(`${baseUrl}/api/posts`, { - searchParams: { - userid: userId, - }, - }); + let posts; + if (usePuppeteer) { + const data = await puppeteerGet(`${baseUrl}/api/posts?userid=${userId}`, browser); + posts = data.posts; + } else { + const { data } = await got(`${baseUrl}/api/posts`, { + headers: { + accept: 'application/json', + }, + searchParams: { + userid: userId, + }, + }); + posts = data.posts; + } - const list = data.posts.items.map(async (item) => { - const { shortcode, type } = item; - const link = `${baseUrl}/post/${shortcode}/`; - let images = []; - if (type === 'img_multi') { - images = await ctx.cache.tryGet(link, async () => { - const { data } = await got(link); - const $ = cheerio.load(data); - return [ - ...new Set( - $('.post_slide a') - .toArray() - .map((a) => { - a = $(a); - return { - ori: a.attr('href'), - url: a.find('img').attr('data-src'), - }; - }) - ), - ]; - }); - } - return { - title: item.sum_pure, - description: art(path.join(__dirname, 'templates/desc.art'), { item, images }), - link, - pubDate: parseDate(item.time, 'X'), - }; - }); + const list = await Promise.all( + posts.items.map(async (item) => { + const { shortcode, type, sum_pure, time } = item; + const link = `${baseUrl}/post/${shortcode}/`; + if (type === 'img_multi') { + item.images = await ctx.cache.tryGet(link, async () => { + let html; + if (usePuppeteer) { + html = await puppeteerGet(link, browser); + } else { + const { data } = await got(link); + html = data; + } + const $ = cheerio.load(html); + return [ + ...new Set( + $('.post_slide a') + .toArray() + .map((a) => { + a = $(a); + return { + ori: a.attr('href'), + url: a.find('img').attr('data-src'), + }; + }) + ), + ]; + }); + } + + return { + title: sum_pure, + description: art(path.join(__dirname, 'templates/desc.art'), { item }), + link, + pubDate: parseDate(time, 'X'), + }; + }) + ); + await browser.close(); ctx.state.data = { title: `${profileName} (@${id}) - Picnob`, diff --git a/lib/v2/picnob/utils.js b/lib/v2/picnob/utils.js new file mode 100644 index 00000000000000..aea8fa7bf6d4b0 --- /dev/null +++ b/lib/v2/picnob/utils.js @@ -0,0 +1,20 @@ +const puppeteerGet = async (url, browser) => { + let data; + const page = await browser.newPage(); + await page.setRequestInterception(true); + page.on('request', (request) => { + request.resourceType() === 'document' ? request.continue() : request.abort(); + }); + page.on('response', async (response) => { + data = await (response.request().url().includes('/api/posts') ? response.json() : response.text()); + }); + await page.goto(url, { + waitUntil: 'domcontentloaded', + }); + await page.close(); + return data; +}; + +module.exports = { + puppeteerGet, +}; diff --git a/lib/v2/picuki/profile.js b/lib/v2/picuki/profile.js index ef0cfb3187f26d..a65d6c61cb11e3 100644 --- a/lib/v2/picuki/profile.js +++ b/lib/v2/picuki/profile.js @@ -125,8 +125,9 @@ module.exports = async (ctx) => { return html; } - items = items.concat( - await Promise.all( + items = [ + ...items, + ...(await Promise.all( list.map(async (post) => { post = $(post); @@ -138,9 +139,9 @@ module.exports = async (ctx) => { .find('.photo-description') .text() .trim() - .replace(/[^\S\n]+/g, ' ') - .replace(/((?<=\n|^)[^\S\n])|([^\S\n](?=\n|$))/g, ''); - const title = postText.replace(/\n/g, ' ') || 'Untitled'; + .replaceAll(/[^\S\n]+/g, ' ') + .replaceAll(/((?<=\n|^)[^\S\n])|([^\S\n](?=\n|$))/g, ''); + const title = postText.replaceAll('\n', ' ') || 'Untitled'; const description = art(path.join(__dirname, 'templates/post.art'), { media: displayVideo ? media_displayVideo : deVideo(media_displayVideo), desc: postText, @@ -155,8 +156,8 @@ module.exports = async (ctx) => { pubDate, }; }) - ) - ); + )), + ]; await browser.close(); diff --git a/lib/v2/pikabu/radar.js b/lib/v2/pikabu/radar.js index 2bd3f3106111a2..79078a04c72420 100644 --- a/lib/v2/pikabu/radar.js +++ b/lib/v2/pikabu/radar.js @@ -4,19 +4,19 @@ module.exports = { '.': [ { title: 'Community', - docs: 'https://docs.rsshub.app/routes/en/bbs#pikabu', + docs: 'https://docs.rsshub.app/routes/bbs#pikabu', source: ['/community/:name'], target: '/pikabu/community/:name', }, { title: 'Tag', - docs: 'https://docs.rsshub.app/routes/en/bbs#pikabu', + docs: 'https://docs.rsshub.app/routes/bbs#pikabu', source: ['/tag/:name'], target: '/pikabu/tag/:name', }, { title: 'User', - docs: 'https://docs.rsshub.app/routes/en/bbs#pikabu', + docs: 'https://docs.rsshub.app/routes/bbs#pikabu', source: ['/:name'], target: '/pikabu/user/:name', }, diff --git a/lib/v2/pikabu/utils.js b/lib/v2/pikabu/utils.js index 85b8a87c8f8de5..d5acaafe61dec2 100644 --- a/lib/v2/pikabu/utils.js +++ b/lib/v2/pikabu/utils.js @@ -31,7 +31,7 @@ const fixVideo = (element) => { const webm = element.attr('data-webm'); videoHtml = art(path.join(__dirname, 'templates/video.art'), { preview, width, mp4, webm }); } else { - throw Error(`Unknown video type: ${dataType}`); + throw new Error(`Unknown video type: ${dataType}`); } element.replaceWith(videoHtml); }; diff --git a/lib/v2/pixabay/radar.js b/lib/v2/pixabay/radar.js index 40a006a8adadef..61453d4dbc8451 100644 --- a/lib/v2/pixabay/radar.js +++ b/lib/v2/pixabay/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'Search', - docs: 'https://docs.rsshub.app/routes/en/picture#pixabay', + docs: 'https://docs.rsshub.app/routes/picture#pixabay', source: ['/:searchType/search/:q'], target: '/pixabay/search/:q', }, diff --git a/lib/v2/pixabay/search.js b/lib/v2/pixabay/search.js index 4c697537cfbe83..5c01c2d31bbb01 100644 --- a/lib/v2/pixabay/search.js +++ b/lib/v2/pixabay/search.js @@ -31,7 +31,7 @@ module.exports = async (ctx) => { title: pageURL .substring(pageURL.lastIndexOf('/', pageURL.lastIndexOf('/') - 1) + 1, pageURL.lastIndexOf('/')) .replace(/(-\d+)$/, '') - .replace(/-/g, ' '), + .replaceAll('-', ' '), description: art(path.join(__dirname, 'templates/img.art'), { item }), link: pageURL, category: tags.split(', '), diff --git a/lib/v2/pixiv/api/getBookmarks.js b/lib/v2/pixiv/api/get-bookmarks.js similarity index 100% rename from lib/v2/pixiv/api/getBookmarks.js rename to lib/v2/pixiv/api/get-bookmarks.js diff --git a/lib/v2/pixiv/api/getIllustFollows.js b/lib/v2/pixiv/api/get-illust-follows.js similarity index 100% rename from lib/v2/pixiv/api/getIllustFollows.js rename to lib/v2/pixiv/api/get-illust-follows.js diff --git a/lib/v2/pixiv/api/getIllusts.js b/lib/v2/pixiv/api/get-illusts.js similarity index 100% rename from lib/v2/pixiv/api/getIllusts.js rename to lib/v2/pixiv/api/get-illusts.js diff --git a/lib/v2/pixiv/api/getRanking.js b/lib/v2/pixiv/api/get-ranking.js similarity index 76% rename from lib/v2/pixiv/api/getRanking.js rename to lib/v2/pixiv/api/get-ranking.js index d0166c8320f245..7f5ad0339d42ed 100644 --- a/lib/v2/pixiv/api/getRanking.js +++ b/lib/v2/pixiv/api/get-ranking.js @@ -3,7 +3,7 @@ const maskHeader = require('../constants').maskHeader; const assert = require('assert'); const queryString = require('query-string'); -const allowMode = ['day', 'week', 'month', 'day_male', 'day_female', 'day_ai', 'week_original', 'week_rookie', 'day_r18', 'day_r18_ai', 'day_male_r18', 'day_female_r18', 'week_r18', 'week_r18g']; +const allowMode = new Set(['day', 'week', 'month', 'day_male', 'day_female', 'day_ai', 'week_original', 'week_rookie', 'day_r18', 'day_r18_ai', 'day_male_r18', 'day_female_r18', 'week_r18', 'week_r18g']); /** * 获取某天的排行榜 @@ -13,7 +13,7 @@ const allowMode = ['day', 'week', 'month', 'day_male', 'day_female', 'day_ai', ' * @returns {Promise>} */ module.exports = function getRanking(mode, date, token) { - assert(allowMode.includes(mode), 'Mode not allow.'); + assert(allowMode.has(mode), 'Mode not allow.'); return got('https://app-api.pixiv.net/v1/illust/ranking', { headers: { ...maskHeader, diff --git a/lib/v2/pixiv/api/getUserDetail.js b/lib/v2/pixiv/api/get-user-detail.js similarity index 100% rename from lib/v2/pixiv/api/getUserDetail.js rename to lib/v2/pixiv/api/get-user-detail.js diff --git a/lib/v2/pixiv/api/searchIllust.js b/lib/v2/pixiv/api/search-illust.js similarity index 100% rename from lib/v2/pixiv/api/searchIllust.js rename to lib/v2/pixiv/api/search-illust.js diff --git a/lib/v2/pixiv/api/searchPopularIllust.js b/lib/v2/pixiv/api/search-popular-illust.js similarity index 100% rename from lib/v2/pixiv/api/searchPopularIllust.js rename to lib/v2/pixiv/api/search-popular-illust.js diff --git a/lib/v2/pixiv/bookmarks.js b/lib/v2/pixiv/bookmarks.js index 61112a7e75836b..3b9ebee9566c28 100644 --- a/lib/v2/pixiv/bookmarks.js +++ b/lib/v2/pixiv/bookmarks.js @@ -1,20 +1,20 @@ const { getToken } = require('./token'); -const getBookmarks = require('./api/getBookmarks'); -const getUserDetail = require('./api/getUserDetail'); +const getBookmarks = require('./api/get-bookmarks'); +const getUserDetail = require('./api/get-user-detail'); const config = require('@/config').value; const pixivUtils = require('./utils'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { if (!config.pixiv || !config.pixiv.refreshToken) { - throw 'pixiv RSS is disabled due to the lack of relevant config'; + throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } const id = ctx.params.id; const token = await getToken(ctx.cache.tryGet); if (!token) { - throw 'pixiv not login'; + throw new Error('pixiv not login'); } const [bookmarksResponse, userDetailResponse] = await Promise.all([getBookmarks(id, token), getUserDetail(id, token)]); diff --git a/lib/v2/pixiv/illustfollow.js b/lib/v2/pixiv/illustfollow.js index a40b73586dc42a..608906024f7d60 100644 --- a/lib/v2/pixiv/illustfollow.js +++ b/lib/v2/pixiv/illustfollow.js @@ -1,17 +1,17 @@ const { getToken } = require('./token'); -const getIllustFollows = require('./api/getIllustFollows'); +const getIllustFollows = require('./api/get-illust-follows'); const config = require('@/config').value; const pixivUtils = require('./utils'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { if (!config.pixiv || !config.pixiv.refreshToken) { - throw 'pixiv RSS is disabled due to the lack of relevant config'; + throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } const token = await getToken(ctx.cache.tryGet); if (!token) { - throw 'pixiv not login'; + throw new Error('pixiv not login'); } const response = await getIllustFollows(token); diff --git a/lib/v2/pixiv/novels.js b/lib/v2/pixiv/novels.js index 38da950ab2cf1c..884b02fdd48bf2 100644 --- a/lib/v2/pixiv/novels.js +++ b/lib/v2/pixiv/novels.js @@ -14,11 +14,11 @@ module.exports = async (ctx) => { const novels = Object.keys(allData.body.novels) .sort((a, b) => b - a) - .slice(0, parseInt(limit, 10)); + .slice(0, Number.parseInt(limit, 10)); const searchParams = new URLSearchParams(); - novels.forEach((novel) => { + for (const novel of novels) { searchParams.append('ids[]', novel); - }); + } const { data } = await got(`${baseUrl}/ajax/user/${id}/profile/novels`, { headers: { diff --git a/lib/v2/pixiv/pixiv-got.js b/lib/v2/pixiv/pixiv-got.js index f562bc17fb4010..2195cc350eaa6a 100644 --- a/lib/v2/pixiv/pixiv-got.js +++ b/lib/v2/pixiv/pixiv-got.js @@ -15,14 +15,14 @@ async function dohResolve(name, jsonDohEndpoint) { accept: 'application/dns-json', }, }); - if (response.data.Status !== 0) { - logger.error(`Error ${response.data.Status} when querying DoH endpoint ${jsonDohEndpoint}`); - } else { + if (response.data.Status === 0) { return response.data.Answer.map((item) => item.data); + } else { + logger.error(`Error ${response.data.Status} when querying DoH endpoint ${jsonDohEndpoint}`); } - } catch (e) { + } catch (error) { logger.error(`Failed to resolve ${name}`); - logger.debug(e); + logger.debug(error); } return []; } diff --git a/lib/v2/pixiv/ranking.js b/lib/v2/pixiv/ranking.js index d3915fc6d357dd..c56178d5f7d077 100644 --- a/lib/v2/pixiv/ranking.js +++ b/lib/v2/pixiv/ranking.js @@ -1,5 +1,5 @@ const { getToken } = require('./token'); -const getRanking = require('./api/getRanking'); +const getRanking = require('./api/get-ranking'); const config = require('@/config').value; const pixivUtils = require('./utils'); const { parseDate } = require('@/utils/parse-date'); @@ -57,15 +57,15 @@ const alias = { module.exports = async (ctx) => { if (!config.pixiv || !config.pixiv.refreshToken) { - throw 'pixiv RSS is disabled due to the lack of relevant config'; + throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } - const mode = alias[ctx.params.mode] ? alias[ctx.params.mode] : ctx.params.mode; + const mode = alias[ctx.params.mode] ?? ctx.params.mode; const date = ctx.params.date ? new Date(ctx.params.date) : new Date(); const token = await getToken(ctx.cache.tryGet); if (!token) { - throw 'pixiv not login'; + throw new Error('pixiv not login'); } const response = await getRanking(mode, ctx.params.date && date, token); @@ -86,6 +86,7 @@ module.exports = async (ctx) => { description: `

画师:${illust.user.name} - 阅览数:${illust.total_view} - 收藏数:${illust.total_bookmarks}


${images.join('')}`, link: `https://www.pixiv.net/artworks/${illust.id}`, author: illust.user.name, + category: illust.tags.map((tag) => tag.name), }; }), }; diff --git a/lib/v2/pixiv/search.js b/lib/v2/pixiv/search.js index f6fada5f5b38ca..8c89dcc909cbd0 100644 --- a/lib/v2/pixiv/search.js +++ b/lib/v2/pixiv/search.js @@ -1,13 +1,13 @@ const { getToken } = require('./token'); -const searchPopularIllust = require('./api/searchPopularIllust'); -const searchIllust = require('./api/searchIllust'); +const searchPopularIllust = require('./api/search-popular-illust'); +const searchIllust = require('./api/search-illust'); const config = require('@/config').value; const pixivUtils = require('./utils'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { if (!config.pixiv || !config.pixiv.refreshToken) { - throw 'pixiv RSS is disabled due to the lack of relevant config'; + throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } const keyword = ctx.params.keyword; @@ -16,15 +16,10 @@ module.exports = async (ctx) => { const token = await getToken(ctx.cache.tryGet); if (!token) { - throw 'pixiv not login'; + throw new Error('pixiv not login'); } - let response; - if (order === 'popular') { - response = await searchPopularIllust(keyword, token); - } else { - response = await searchIllust(keyword, token); - } + const response = await (order === 'popular' ? searchPopularIllust(keyword, token) : searchIllust(keyword, token)); let illusts = response.data.illusts; if (mode === 'safe' || mode === '1') { diff --git a/lib/v2/pixiv/token.js b/lib/v2/pixiv/token.js index 06911904ba4daa..87dcec02615157 100644 --- a/lib/v2/pixiv/token.js +++ b/lib/v2/pixiv/token.js @@ -29,9 +29,9 @@ const refreshToken = (tryGet) => responseType: 'json', resolveBodyOnly: true, }) - .catch((e) => { + .catch((error) => { logger.error('Pixiv refresh token failed.'); - logger.error(e); + logger.error(error); }), 3600, false diff --git a/lib/v2/pixiv/user.js b/lib/v2/pixiv/user.js index e3893ec8cfea94..46c2d6eb5c5211 100644 --- a/lib/v2/pixiv/user.js +++ b/lib/v2/pixiv/user.js @@ -1,18 +1,18 @@ const { getToken } = require('./token'); -const getIllusts = require('./api/getIllusts'); +const getIllusts = require('./api/get-illusts'); const config = require('@/config').value; const pixivUtils = require('./utils'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { if (!config.pixiv || !config.pixiv.refreshToken) { - throw 'pixiv RSS is disabled due to the lack of relevant config'; + throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } const id = ctx.params.id; const token = await getToken(ctx.cache.tryGet); if (!token) { - throw 'pixiv not login'; + throw new Error('pixiv not login'); } const response = await getIllusts(id, token); @@ -30,8 +30,9 @@ module.exports = async (ctx) => { title: illust.title, author: username, pubDate: parseDate(illust.create_date), - description: `

画师:${username} - 阅览数:${illust.total_view} - 收藏数:${illust.total_bookmarks}

${images.join('')}`, + description: `${illust.caption}

画师:${username} - 阅览数:${illust.total_view} - 收藏数:${illust.total_bookmarks}

${images.join('')}`, link: `https://www.pixiv.net/artworks/${illust.id}`, + category: illust.tags.map((t) => t.name), }; }), }; diff --git a/lib/v2/pixiv/utils.js b/lib/v2/pixiv/utils.js index 3810776a991640..3b6292772f9d06 100644 --- a/lib/v2/pixiv/utils.js +++ b/lib/v2/pixiv/utils.js @@ -4,8 +4,8 @@ module.exports = { getImgs(illust) { const images = []; if (illust.meta_pages?.length) { - for (let i = 0; i < illust.meta_pages.length; i++) { - const original = illust.meta_pages[i].image_urls.original.replace('https://i.pximg.net', config.pixiv.imgProxy); + for (const page of illust.meta_pages) { + const original = page.image_urls.original.replace('https://i.pximg.net', config.pixiv.imgProxy); images.push(`

`); } } else if (illust.meta_single_page.original_image_url) { diff --git a/lib/v2/pkmer/maintainer.js b/lib/v2/pkmer/maintainer.js new file mode 100644 index 00000000000000..01de10bd666e2d --- /dev/null +++ b/lib/v2/pkmer/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/recent': ['Gnoyong'], +}; diff --git a/lib/v2/pkmer/radar.js b/lib/v2/pkmer/radar.js new file mode 100644 index 00000000000000..5b3f9382c2763b --- /dev/null +++ b/lib/v2/pkmer/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'pkmer.cn': { + _name: 'PKMer', + '.': [ + { + title: '最近更新', + docs: 'https://docs.rsshub.app/routes/bbs#pkmer-zui-jin-geng-xin', + source: ['/page/*'], + target: '/pkmer/recent', + }, + ], + }, +}; diff --git a/lib/v2/pkmer/recent.js b/lib/v2/pkmer/recent.js new file mode 100644 index 00000000000000..8989f68864525d --- /dev/null +++ b/lib/v2/pkmer/recent.js @@ -0,0 +1,36 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +const baseUrl = 'https://pkmer.cn'; + +module.exports = async (ctx) => { + const { data: response } = await got(`${baseUrl}/page/1/`); + const $ = cheerio.load(response); + const items = process($); + + ctx.state.data = { + title: 'PKMer', + icon: 'https://cdn.pkmer.cn/covers/logo.png!nomark', + logo: 'https://cdn.pkmer.cn/covers/logo.png!nomark', + link: baseUrl, + allowEmpty: true, + item: items, + }; +}; + +function process($) { + const container = $('#pages > div.grid > .relative'); + const items = container.toArray().map((el) => { + el = $(el); + const title = el.find('h3'); + return { + title: title.text().trim(), + link: baseUrl + title.parent().attr('href'), + description: el.find('.leading-relaxed').prop('outerHTML') + el.find('.post-content').prop('outerHTML'), + pubDate: el.find('time').attr('datetime'), + author: el.find('h4').text().trim(), + itunes_item_image: el.find('img').attr('src'), + }; + }); + return items; +} diff --git a/lib/v2/pkmer/router.js b/lib/v2/pkmer/router.js new file mode 100644 index 00000000000000..87c73eeb718bdf --- /dev/null +++ b/lib/v2/pkmer/router.js @@ -0,0 +1,3 @@ +module.exports = function (router) { + router.get('/recent', require('./recent.js')); +}; diff --git a/lib/v2/pku/bbs/hot.js b/lib/v2/pku/bbs/hot.js index 55107966212fa4..9fd00a085dba8c 100644 --- a/lib/v2/pku/bbs/hot.js +++ b/lib/v2/pku/bbs/hot.js @@ -37,7 +37,7 @@ module.exports = async (ctx) => { guid: url, pubDate: timezone(parseDate(date, '发表于YYYY-MM-DD HH:mm:ss'), +8), }; - } catch (error) { + } catch { return { title, link: url, diff --git a/lib/v2/pku/cls/announcement.js b/lib/v2/pku/cls/announcement.js new file mode 100644 index 00000000000000..3476b9d9bbcb1f --- /dev/null +++ b/lib/v2/pku/cls/announcement.js @@ -0,0 +1,42 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +const homeUrl = 'https://bio.pku.edu.cn/homes/Index/news/21/21.html'; + +const baseUrl = 'https://bio.pku.edu.cn'; + +module.exports = async (ctx) => { + const response = await got(homeUrl); + + const $ = cheerio.load(response.data); + + const list = $('div.normal_list>ul a') + .toArray() + .map((item) => { + item = $(item); + return { + title: $(item).find('p').text().trim(), + pubDate: parseDate($(item).find('span.date').text()), + link: baseUrl + $(item).attr('href'), + }; + }); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: response } = await got(item.link); + const $ = cheerio.load(response); + + item.description = $('div.page div.common_width div.col-md-9').first().html(); + return item; + }) + ) + ); + + ctx.state.data = { + title: '北京大学生命科学学院通知公告', + link: homeUrl, + item: items, + }; +}; diff --git a/lib/v2/pku/eecs.js b/lib/v2/pku/eecs.js index 3b727725993651..74082468f73615 100644 --- a/lib/v2/pku/eecs.js +++ b/lib/v2/pku/eecs.js @@ -7,7 +7,7 @@ const { eecsMap } = require('./utils'); module.exports = async (ctx) => { const host = 'https://eecs.pku.edu.cn'; - let type = ctx.params && parseInt(ctx.params.type); + let type = ctx.params && Number.parseInt(ctx.params.type); if (type === undefined) { type = 0; } diff --git a/lib/v2/pku/maintainer.js b/lib/v2/pku/maintainer.js index 5a00d657a04ce9..da84e036956698 100644 --- a/lib/v2/pku/maintainer.js +++ b/lib/v2/pku/maintainer.js @@ -1,6 +1,7 @@ module.exports = { '/admission/sszs': ['pkuyjs'], '/bbs/hot': ['wooddance'], + '/cls/announcement': ['william-swl'], '/cls/lecture': ['TPOB'], '/eecs/:type?': ['Ir1d'], '/hr/:category?': ['nczitzk'], diff --git a/lib/v2/pku/radar.js b/lib/v2/pku/radar.js index 72fe004e935d50..084739556a1ae1 100644 --- a/lib/v2/pku/radar.js +++ b/lib/v2/pku/radar.js @@ -24,6 +24,12 @@ module.exports = { source: ['/homes/Index/news_jz/7/7.html', '/'], target: '/pku/cls/lecture', }, + { + title: '生命科学学院通知公告', + docs: 'https://docs.rsshub.app/routes/university#bei-jing-da-xue', + source: ['/homes/Index/news/21/21.html', '/'], + target: '/pku/cls/announcement', + }, ], eecs: [ { diff --git a/lib/v2/pku/router.js b/lib/v2/pku/router.js index e7a39bb567e7db..92d8fad3b5334c 100644 --- a/lib/v2/pku/router.js +++ b/lib/v2/pku/router.js @@ -1,6 +1,7 @@ module.exports = function (router) { router.get('/admission/sszs', require('./pkuyjs')); router.get('/bbs/hot', require('./bbs/hot')); + router.get('/cls/announcement', require('./cls/announcement')); router.get('/cls/lecture', require('./cls/lecture')); router.get('/eecs/:type?', require('./eecs')); router.get('/hr/:category?', require('./hr')); @@ -9,5 +10,5 @@ module.exports = function (router) { router.get('/scc/recruit/:type?', require('./scc/recruit')); router.get('/ss/notice', require('./ss/notice.js')); router.get('/ss/admission', require('./ss/admission.js')); - router.get('/ss/pgadmin', require('./ss/pg_admin.js')); + router.get('/ss/pgadmin', require('./ss/pg-admin.js')); }; diff --git a/lib/v2/pku/ss/pg_admin.js b/lib/v2/pku/ss/pg-admin.js similarity index 100% rename from lib/v2/pku/ss/pg_admin.js rename to lib/v2/pku/ss/pg-admin.js diff --git a/lib/v2/playpcesor/maintainer.js b/lib/v2/playpcesor/maintainer.js new file mode 100644 index 00000000000000..387d961bfe3f67 --- /dev/null +++ b/lib/v2/playpcesor/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/': ['cnkmmk'], +}; diff --git a/lib/v2/playpcesor/radar.js b/lib/v2/playpcesor/radar.js new file mode 100644 index 00000000000000..804427e500e8cb --- /dev/null +++ b/lib/v2/playpcesor/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'playpcesor.com': { + _name: '电脑玩物', + '.': [ + { + title: '博客', + docs: 'https://docs.rsshub.app/routes/blog#dian-nao-wan-wu', + source: ['/'], + target: '/playpcesor', + }, + ], + }, +}; diff --git a/lib/v2/playpcesor/router.js b/lib/v2/playpcesor/router.js new file mode 100644 index 00000000000000..574929e02f0d85 --- /dev/null +++ b/lib/v2/playpcesor/router.js @@ -0,0 +1,3 @@ +module.exports = function (router) { + router.get('/', require('./rss')); +}; diff --git a/lib/v2/playpcesor/rss.js b/lib/v2/playpcesor/rss.js new file mode 100644 index 00000000000000..6e37e0e422017d --- /dev/null +++ b/lib/v2/playpcesor/rss.js @@ -0,0 +1,32 @@ +const { parseDate } = require('@/utils/parse-date'); +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const url = 'https://www.playpcesor.com/'; + const response = await got({ method: 'get', url }); + const $ = cheerio.load(response.data); + + const list = $("article[class='post-outer-container']") + .map((i, e) => { + const element = $(e); + const title = element.find('h3 > a').text(); + const link = element.find('h3 > a').attr('href'); + const description = element.find('div[class="snippet-item r-snippetized"]').text(); + const dateraw = element.find('time').attr('datetime'); + + return { + title, + description, + link, + pubDate: parseDate(dateraw, 'YYYY-MM-DDTHH:mm:ss+08:00'), + }; + }) + .get(); + + ctx.state.data = { + title: '电脑玩物', + link: url, + item: list, + }; +}; diff --git a/lib/v2/plurk/top.js b/lib/v2/plurk/top.js index 7f7733b8191740..4b1639c1a95ac6 100644 --- a/lib/v2/plurk/top.js +++ b/lib/v2/plurk/top.js @@ -1,12 +1,12 @@ const got = require('@/utils/got'); const { baseUrl, getPlurk } = require('./utils'); -const categoryList = ['topReplurks', 'topFavorites', 'topResponded']; +const categoryList = new Set(['topReplurks', 'topFavorites', 'topResponded']); module.exports = async (ctx) => { const { category = 'topReplurks', lang = 'en' } = ctx.params; - if (!categoryList.includes(category)) { - throw Error(`Invalid category: ${category}`); + if (!categoryList.has(category)) { + throw new Error(`Invalid category: ${category}`); } const { data: apiResponse } = await got(`${baseUrl}/Stats/${category}`, { diff --git a/lib/v2/plurk/user.js b/lib/v2/plurk/user.js index 60db490615ef95..35136fbc7712e8 100644 --- a/lib/v2/plurk/user.js +++ b/lib/v2/plurk/user.js @@ -12,8 +12,8 @@ module.exports = async (ctx) => { $('body script[type]') .text() .match(/PUBLIC_PLURKS = (.*);\nPINNED_PLURK/)[1] - .replace(/new Date\((.*?)\)/g, '$1') - .replace(/null/g, '""') + .replaceAll(/new Date\((.*?)\)/g, '$1') + .replaceAll('null', '""') ); const userIds = publicPlurks.map((item) => item.user_id); diff --git a/lib/v2/plurk/utils.js b/lib/v2/plurk/utils.js index 3d1b13e435b7de..d436bd2b1f66ec 100644 --- a/lib/v2/plurk/utils.js +++ b/lib/v2/plurk/utils.js @@ -40,7 +40,7 @@ const getPlurk = (plurkGuid, item, author, tryGet) => }); return { - title: item.content_raw ? item.content_raw : $.text() || plurkGuid, + title: item.content_raw ?? ($.text() || plurkGuid), description: $.html(), guid: plurkGuid, link: item.rendered ? item.link_url : null, diff --git a/lib/v2/pmthinking/index.js b/lib/v2/pmthinking/index.js deleted file mode 100644 index 1334aedf692e09..00000000000000 --- a/lib/v2/pmthinking/index.js +++ /dev/null @@ -1,61 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const timezone = require('@/utils/timezone'); -const { parseDate } = require('@/utils/parse-date'); -const { art } = require('@/utils/render'); -const path = require('path'); - -module.exports = async (ctx) => { - const rootUrl = 'https://pmthinking.com'; - const currentUrl = `${rootUrl}/wp-json/b2/v1/getModulePostList`; - - const response = await got({ - method: 'post', - url: currentUrl, - json: { - index: 2, - post_paged: 1, - }, - }); - - const $ = cheerio.load(response.data.data); - - let items = $('h2 a') - .toArray() - .map((item) => { - item = $(item); - - return { - title: item.text(), - link: item.attr('href'), - }; - }); - - items = await Promise.all( - items.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - - const content = cheerio.load(detailResponse.data); - - item.author = content('.post-user-name').text(); - item.pubDate = timezone(parseDate(content('time[itemprop="datePublished"]').attr('datetime')), +8); - item.description = art(path.join(__dirname, 'templates/description.art'), { - image: content('.post-style-4-top img').attr('src'), - description: content('.entry-content').html(), - }); - - return item; - }) - ) - ); - - ctx.state.data = { - title: '产品沉思录 · Product Thinking', - link: rootUrl, - item: items, - }; -}; diff --git a/lib/v2/pmthinking/radar.js b/lib/v2/pmthinking/radar.js deleted file mode 100644 index 744bf78f962f09..00000000000000 --- a/lib/v2/pmthinking/radar.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - 'pmthinking.com': { - _name: '产品沉思录', - '.': [ - { - title: '首页', - docs: 'https://docs.rsshub.app/routes/new-media#chan-pin-chen-si-lu-shou-ye', - source: ['/'], - target: '/pmthinking', - }, - ], - }, -}; diff --git a/lib/v2/pmthinking/templates/description.art b/lib/v2/pmthinking/templates/description.art deleted file mode 100644 index c823516c9a36ad..00000000000000 --- a/lib/v2/pmthinking/templates/description.art +++ /dev/null @@ -1,4 +0,0 @@ -{{ if image }} - -{{ /if }} -{{@ description }} \ No newline at end of file diff --git a/lib/v2/pnas/index.js b/lib/v2/pnas/index.js index 5cf8576f9876d2..366e2753c63288 100644 --- a/lib/v2/pnas/index.js +++ b/lib/v2/pnas/index.js @@ -10,7 +10,7 @@ const logger = require('@/utils/logger'); module.exports = async (ctx) => { const baseUrl = 'https://www.pnas.org'; const { topicPath } = ctx.params; - const link = `${baseUrl}/${topicPath ? topicPath : 'latest'}`; + const link = `${baseUrl}/${topicPath ?? 'latest'}`; let cookieJar = await ctx.cache.get('pnas:cookieJar'); const cacheMiss = !cookieJar; @@ -46,7 +46,7 @@ module.exports = async (ctx) => { page.on('request', (request) => { request.resourceType() === 'document' ? request.continue() : request.abort(); }); - logger.debug(`Requesting ${item.link}`); + logger.http(`Requesting ${item.link}`); await page.goto(item.link, { waitUntil: 'domcontentloaded', referer: link, diff --git a/lib/v2/polkadot/home.js b/lib/v2/polkadot/home.js deleted file mode 100644 index b037a500eb1e8e..00000000000000 --- a/lib/v2/polkadot/home.js +++ /dev/null @@ -1,34 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); -module.exports = async (ctx) => { - const response = await got('https://polkadot.network/blog'); - - const $ = cheerio.load(response.data); - const list = $('.container .row .card > div > a:nth-child(2)'); - const items = await Promise.all( - list - .map((_, originItem) => { - const item = { - title: $(originItem).find('h5').text().trim(), - link: 'https://polkadot.network' + $(originItem).attr('href'), - }; - return ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got(item.link); - const content = cheerio.load(detailResponse.data); - const meta = JSON.parse(content('script[type="application/ld+json"]').text()); - item.author = meta.author.name; - item.description = content('main .container').eq(1).html(); - item.pubDate = parseDate(meta.datePublished); - return item; - }); - }) - .get() - ); - ctx.state.data = { - title: $('head title').text(), - link: 'https://polkadot.network/blog/', - image: $('link[rel=icon]').attr('href'), - item: items, - }; -}; diff --git a/lib/v2/polkadot/maintainer.js b/lib/v2/polkadot/maintainer.js deleted file mode 100644 index e3046ab0762d0a..00000000000000 --- a/lib/v2/polkadot/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/blog': ['iceqing'], -}; diff --git a/lib/v2/polkadot/radar.js b/lib/v2/polkadot/radar.js deleted file mode 100644 index cdd4812053a490..00000000000000 --- a/lib/v2/polkadot/radar.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - 'polkadot.network': { - _name: 'Polkadot', - '.': [ - { - title: 'Blog', - docs: 'https://docs.rsshub.app/routes/blog#polkadot', - source: ['/', '/blog/', '/blog/*'], - target: '/polkadot/blog', - }, - ], - }, -}; diff --git a/lib/v2/polkadot/router.js b/lib/v2/polkadot/router.js deleted file mode 100644 index 3234e414c9868a..00000000000000 --- a/lib/v2/polkadot/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (router) { - router.get('/blog', require('./home')); -}; diff --git a/lib/v2/polkaworld/home.js b/lib/v2/polkaworld/home.js deleted file mode 100644 index ffd6afdca715b7..00000000000000 --- a/lib/v2/polkaworld/home.js +++ /dev/null @@ -1,24 +0,0 @@ -const got = require('@/utils/got'); -const { parseDate } = require('@/utils/parse-date'); -const timezone = require('@/utils/timezone'); - -module.exports = async (ctx) => { - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 10; - const response = await got({ - method: 'get', - url: 'https://pub.polkaworld.pro/articles?_sort=PublishDate:desc&_limit=' + limit, - }); - const docList = response.data; - - ctx.state.data = { - title: 'PolkaWorld-资讯', - link: 'https://www.polkaworld.org/', - item: docList.map((item) => ({ - title: item.Title, - description: item.Title, - pubDate: timezone(parseDate(item.PublishDate), 0), - author: item.author.Name, - link: 'https://www.polkaworld.org/articles/' + item.URL, - })), - }; -}; diff --git a/lib/v2/polkaworld/maintainer.js b/lib/v2/polkaworld/maintainer.js deleted file mode 100644 index e33a028fbf8684..00000000000000 --- a/lib/v2/polkaworld/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/polkaworld/newest': ['iceqing'], -}; diff --git a/lib/v2/polkaworld/radar.js b/lib/v2/polkaworld/radar.js deleted file mode 100644 index d3040799e288e4..00000000000000 --- a/lib/v2/polkaworld/radar.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - 'polkaworld.org': { - _name: 'PolkaWorld', - www: [ - { - title: '最新资讯', - docs: 'https://docs.rsshub.app/routes/blog#polkaworld', - source: ['/', '/articles/:name'], - target: '/polkaworld/newest', - }, - ], - }, -}; diff --git a/lib/v2/polkaworld/router.js b/lib/v2/polkaworld/router.js deleted file mode 100644 index 7f76f5015cb365..00000000000000 --- a/lib/v2/polkaworld/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (router) { - router.get('/newest', require('./home')); -}; diff --git a/lib/v2/pornhub/category_url.js b/lib/v2/pornhub/category-url.js similarity index 94% rename from lib/v2/pornhub/category_url.js rename to lib/v2/pornhub/category-url.js index bdfe0fc85dca9b..8640648fd3ac29 100644 --- a/lib/v2/pornhub/category_url.js +++ b/lib/v2/pornhub/category-url.js @@ -7,7 +7,7 @@ module.exports = async (ctx) => { const { language = 'www', url = 'video' } = ctx.params; const link = `https://${language}.pornhub.com/${url}`; if (!isValidHost(language)) { - throw Error('Invalid language'); + throw new Error('Invalid language'); } const { data: response } = await got(link, { headers }); diff --git a/lib/v2/pornhub/category.js b/lib/v2/pornhub/category.js index df09774424e338..bdad4f120d8934 100644 --- a/lib/v2/pornhub/category.js +++ b/lib/v2/pornhub/category.js @@ -12,7 +12,7 @@ module.exports = async (ctx) => { }); const categoryId = isNaN(category) ? categories.find((item) => item.category === category)?.id : category; - const categoryName = isNaN(category) ? category : categories.find((item) => item.id === parseInt(category)).category; + const categoryName = isNaN(category) ? category : categories.find((item) => item.id === Number.parseInt(category)).category; const response = await ctx.cache.tryGet( `pornhub:category:${categoryName}`, @@ -25,7 +25,7 @@ module.exports = async (ctx) => { ); if (response.code) { - throw Error(response.message); + throw new Error(response.message); } const list = response.videos.map((item) => ({ diff --git a/lib/v2/pornhub/model.js b/lib/v2/pornhub/model.js index 950d0f2607c3f5..349e4f84cebd3d 100644 --- a/lib/v2/pornhub/model.js +++ b/lib/v2/pornhub/model.js @@ -7,7 +7,7 @@ module.exports = async (ctx) => { const { language = 'www', username, sort = '' } = ctx.params; const link = `https://${language}.pornhub.com/model/${username}/videos${sort ? `?o=${sort}` : ''}`; if (!isValidHost(language)) { - throw Error('Invalid language'); + throw new Error('Invalid language'); } const { data: response } = await got(link, { headers }); diff --git a/lib/v2/pornhub/pornstar.js b/lib/v2/pornhub/pornstar.js index 23fd812a32087e..98fc1bf84bb503 100644 --- a/lib/v2/pornhub/pornstar.js +++ b/lib/v2/pornhub/pornstar.js @@ -7,7 +7,7 @@ module.exports = async (ctx) => { const { language = 'www', username, sort = 'mr' } = ctx.params; const link = `https://${language}.pornhub.com/pornstar/${username}/videos?o=${sort}`; if (!isValidHost(language)) { - throw Error('Invalid language'); + throw new Error('Invalid language'); } const { data: response } = await got(link, { headers }); diff --git a/lib/v2/pornhub/router.js b/lib/v2/pornhub/router.js index 4ebf5156dc4529..5a5991922ae27d 100644 --- a/lib/v2/pornhub/router.js +++ b/lib/v2/pornhub/router.js @@ -1,7 +1,7 @@ module.exports = (router) => { router.get('/category/:caty', require('./category')); router.get('/search/:keyword', require('./search')); - router.get('/:language?/category_url/:url?', require('./category_url')); + router.get('/:language?/category_url/:url?', require('./category-url')); router.get('/:language?/model/:username/:sort?', require('./model')); router.get('/:language?/pornstar/:username/:sort?', require('./pornstar')); router.get('/:language?/users/:username', require('./users')); diff --git a/lib/v2/pornhub/users.js b/lib/v2/pornhub/users.js index 6fd8b98a3227d4..aff830af15efb1 100644 --- a/lib/v2/pornhub/users.js +++ b/lib/v2/pornhub/users.js @@ -7,7 +7,7 @@ module.exports = async (ctx) => { const { language = 'www', username } = ctx.params; const link = `https://${language}.pornhub.com/users/${username}/videos`; if (!isValidHost(language)) { - throw Error('Invalid language'); + throw new Error('Invalid language'); } const { data: response } = await got(link, { headers }); diff --git a/lib/v2/pornhub/utils.js b/lib/v2/pornhub/utils.js index 10d4af2b4663c3..1996ccad0ee48a 100644 --- a/lib/v2/pornhub/utils.js +++ b/lib/v2/pornhub/utils.js @@ -1,6 +1,7 @@ const { art } = require('@/utils/render'); -const { join } = require('path'); +const path = require('path'); const { parseRelativeDate } = require('@/utils/parse-date'); +const dayjs = require('dayjs'); const defaultDomain = 'https://www.pornhub.com'; @@ -9,7 +10,11 @@ const headers = { hasVisited: 1, }; -const renderDescription = (data) => art(join(__dirname, 'templates/description.art'), data); +const renderDescription = (data) => art(path.join(__dirname, 'templates/description.art'), data); +const extractDateFromImageUrl = (imageUrl) => { + const matchResult = imageUrl.match(/(\d{6})\/(\d{2})/); + return matchResult ? matchResult.slice(1, 3).join('') : null; +}; const parseItems = (e) => ({ title: e.find('span.title a').text().trim(), @@ -19,7 +24,7 @@ const parseItems = (e) => ({ previewVideo: e.find('img').data('mediabook'), }), author: e.find('.usernameWrap a').text(), - pubDate: parseRelativeDate(e.find('.added').text()), + pubDate: dayjs(extractDateFromImageUrl(e.find('img').data('mediumthumb'))) || parseRelativeDate(e.find('.added').text()), }); module.exports = { diff --git a/lib/v2/prestige-av/maintainer.js b/lib/v2/prestige-av/maintainer.js deleted file mode 100644 index d80b41bf19b345..00000000000000 --- a/lib/v2/prestige-av/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/series/:mid/:sort?': ['minimalistrojan'], -}; diff --git a/lib/v2/prestige-av/radar.js b/lib/v2/prestige-av/radar.js deleted file mode 100644 index d2db6d0e65dddc..00000000000000 --- a/lib/v2/prestige-av/radar.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - 'prestige-av.com': { - _name: 'Prestige 蚊香社', - '.': [ - { - title: '系列作品', - docs: 'https://docs.rsshub.app/routes/multimedia#prestige-wen-xiang-she', - source: ['/goods/goods_list.php'], - target: (_params, url) => { - const link = new URL(url); - if (link.searchParams.get('mode') === 'series') { - return link.searchParams.has('sort') ? `/prestige-av/series/${link.searchParams.get('mid')}/${link.searchParams.get('sort')}` : `/prestige-av/series/${link.searchParams.get('mid')}`; - } - }, - }, - ], - }, -}; diff --git a/lib/v2/prestige-av/router.js b/lib/v2/prestige-av/router.js deleted file mode 100644 index c0f678294880c1..00000000000000 --- a/lib/v2/prestige-av/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (router) => { - router.get('/series/:mid/:sort?', require('./series')); -}; diff --git a/lib/v2/prestige-av/series.js b/lib/v2/prestige-av/series.js deleted file mode 100644 index 2897d5acc666b0..00000000000000 --- a/lib/v2/prestige-av/series.js +++ /dev/null @@ -1,42 +0,0 @@ -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const browser = await require('@/utils/puppeteer')(); - - const id = ctx.params.mid; - const sort = ctx.params.sort ?? 'near'; - - const page = await browser.newPage(); - const link = `https://www.prestige-av.com/goods/goods_list.php?mode=series&mid=${id}&count=100&sort=${sort}`; - await page.setRequestInterception(true); - page.on('request', (request) => { - request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort(); - }); - await page.goto(link); - await page.waitForSelector('.buttons'); - await page.click('#AC'); - await page.waitForSelector('#body_goods'); - const html = await page.evaluate(() => document.documentElement.innerHTML); - browser.close(); - - const $ = cheerio.load(html); - const list = $('div#body_goods li'); - - ctx.state.data = { - title: `【Prestige】${$('div[class=search_title_layout_01]').children('h1').first().text().replace('シリーズ ▶ ', '').replace(/\s*/g, '')}`, - description: $('meta[name=Description]').attr('content'), - link: `https://www.prestige-av.com/goods/goods_list.php?mode=series&mid=${id}&sort=${sort}`, - item: - list && - list - .map((index, item) => { - item = $(item); - return { - title: item.find('span.title').text().replace(/\s*/g, ''), - description: item.find('div.spec_layout').children('p').first().text().replace(/\s*/g, '') + '
' + ``, - link: item.find('a').attr('href'), - }; - }) - .get(), - }; -}; diff --git a/lib/v2/priconne-redive/maintainer.js b/lib/v2/priconne-redive/maintainer.js new file mode 100644 index 00000000000000..884bdbf8de3ac8 --- /dev/null +++ b/lib/v2/priconne-redive/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/news': ['SayaSS'], +}; diff --git a/lib/v2/priconne-redive/news.js b/lib/v2/priconne-redive/news.js new file mode 100644 index 00000000000000..ee2c5901e468ac --- /dev/null +++ b/lib/v2/priconne-redive/news.js @@ -0,0 +1,54 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const parseContent = (htmlString) => { + const $ = cheerio.load(htmlString); + + $('.contents-body h3').remove(); + const time = $('.meta-info .time').text().trim(); + $('.meta-info').remove(); + const content = $('.contents-body'); + + return { + description: content.html(), + pubDate: new Date(time), + }; + }; + + const response = await got({ + method: 'get', + url: 'https://priconne-redive.jp/news/', + }); + const data = response.data; + const $ = cheerio.load(data); + const list = $('.article_box'); + + const out = await Promise.all( + list.map((index, item) => { + item = $(item); + const link = item.find('a').first().attr('href'); + return ctx.cache.tryGet(link, async () => { + const rssitem = { + title: item.find('h4').text(), + link, + }; + + const response = await got(link); + const result = parseContent(response.data); + + rssitem.description = result.description; + rssitem.pubDate = result.pubDate; + + return rssitem; + }); + }) + ); + + ctx.state.data = { + title: '公主链接日服-新闻', + link: 'https://priconne-redive.jp/news/', + language: 'ja', + item: out, + }; +}; diff --git a/lib/v2/priconne-redive/radar.js b/lib/v2/priconne-redive/radar.js new file mode 100644 index 00000000000000..cbf29d37f601f1 --- /dev/null +++ b/lib/v2/priconne-redive/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'priconne-redive.jp': { + _name: 'プリンセスコネクト!Re:Dive', + '.': [ + { + title: 'News', + docs: 'https://docs.rsshub.app/routes/game#princess-connect-re-dive-%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BB%E3%82%B9%E3%82%B3%E3%83%8D%E3%82%AF%E3%83%88%EF%BC%81re-dive', + source: ['/news'], + target: '/priconne-redive/news', + }, + ], + }, +}; diff --git a/lib/v2/priconne-redive/router.js b/lib/v2/priconne-redive/router.js new file mode 100644 index 00000000000000..630036b8e4f282 --- /dev/null +++ b/lib/v2/priconne-redive/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/news', require('./news')); +}; diff --git a/lib/v2/producthunt/today.js b/lib/v2/producthunt/today.js index 0cf40ee0c3108a..ca1e845052bb47 100644 --- a/lib/v2/producthunt/today.js +++ b/lib/v2/producthunt/today.js @@ -12,7 +12,7 @@ module.exports = async (ctx) => { const list = Object.values(data.props.apolloState) .filter((o) => o.__typename === 'Post') // only includes new post, not product - .filter((o) => o.hasOwnProperty('redirectToProduct') && o.redirectToProduct === null); + .filter((o) => Object.hasOwn(o, 'redirectToProduct') && o.redirectToProduct === null); const items = await Promise.all( list.map((item) => diff --git a/lib/v2/ps/maintainer.js b/lib/v2/ps/maintainer.js new file mode 100644 index 00000000000000..c706bf74d89d62 --- /dev/null +++ b/lib/v2/ps/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/monthly-games': ['justjustCC'], + '/trophy/:id': ['DIYgod'], +}; diff --git a/lib/v2/ps/monthly-games.js b/lib/v2/ps/monthly-games.js new file mode 100644 index 00000000000000..978a79c8a64058 --- /dev/null +++ b/lib/v2/ps/monthly-games.js @@ -0,0 +1,31 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const path = require('path'); +const { art } = require('@/utils/render'); + +module.exports = async (ctx) => { + const baseUrl = 'https://www.playstation.com/en-sg/ps-plus/whats-new/'; + + const { data: response } = await got(baseUrl); + const $ = cheerio.load(response); + + const list = $('.cmp-experiencefragment--your-latest-monthly-games .box') + .toArray() + .map((item) => { + item = $(item); + return { + title: item.find('h3').text(), + description: art(path.join(__dirname, 'templates/monthly-games.art'), { + img: item.find('.media-block__img source').attr('srcset'), + text: item.find('h3 + p').text(), + }), + link: item.find('.button a').attr('href'), + }; + }); + + ctx.state.data = { + title: 'PlayStation Plus Monthly Games', + link: baseUrl, + item: list, + }; +}; diff --git a/lib/v2/ps/radar.js b/lib/v2/ps/radar.js new file mode 100644 index 00000000000000..3e62655eea5477 --- /dev/null +++ b/lib/v2/ps/radar.js @@ -0,0 +1,17 @@ +module.exports = { + 'playstation.com': { + _name: 'PlayStation Store', + www: [ + { + title: 'Monthly Games', + docs: 'https://docs.rsshub.app/routes/game#playstation-store', + source: ['/en-sg/ps-plus/whats-new'], + target: '/ps/monthly-games', + }, + { + title: 'User trophy', + docs: 'https://docs.rsshub.app/routes/game#playstation-store', + }, + ], + }, +}; diff --git a/lib/v2/ps/router.js b/lib/v2/ps/router.js new file mode 100644 index 00000000000000..3dbdf3d4d602a9 --- /dev/null +++ b/lib/v2/ps/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/monthly-games', require('./monthly-games')); + router.get('/trophy/:id', require('./trophy')); +}; diff --git a/lib/v2/ps/templates/monthly-games.art b/lib/v2/ps/templates/monthly-games.art new file mode 100644 index 00000000000000..f14428b9b2f1af --- /dev/null +++ b/lib/v2/ps/templates/monthly-games.art @@ -0,0 +1 @@ +{{ text }} diff --git a/lib/routes/ps/trophy.js b/lib/v2/ps/trophy.js similarity index 96% rename from lib/routes/ps/trophy.js rename to lib/v2/ps/trophy.js index 25f88dc50000bb..78f75a163e7a74 100644 --- a/lib/routes/ps/trophy.js +++ b/lib/v2/ps/trophy.js @@ -69,13 +69,13 @@ module.exports = async (ctx) => { }) .get(); - return Promise.resolve(items); + return items; }) ); let result = []; - items.forEach((item) => { - result = result.concat(item); - }); + for (const item of items) { + result = [...result, ...item]; + } result = result.sort((a, b) => new Date(b.pubDate) - new Date(a.pubDate)); ctx.state.data = { diff --git a/lib/v2/pts/index.js b/lib/v2/pts/index.js index 403cd679d9f832..38b16a0e32bc7b 100644 --- a/lib/v2/pts/index.js +++ b/lib/v2/pts/index.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('h2 a') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 30) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 30) .toArray() .map((item) => { item = $(item); @@ -38,11 +38,11 @@ module.exports = async (ctx) => { const content = cheerio.load(detailResponse.data); - item.author = content('meta[name="author"]') - .attr('content') - .replace(/文//, '') - .split(//|\/|編譯|報導/)[0]; - item.pubDate = timezone(parseDate(content('meta[property="article:published_time"]').attr('content')), +8); + item.author = content('.reporter-container a') + .toArray() + .map((e) => ({ name: content(e).text() })); + item.pubDate = timezone(parseDate(content('.article-time .mr-2 time').text()), +8); + item.updated = timezone(parseDate(content('.article-time span:nth-child(2) time').text()), +8); item.category = content('.tag-list') .first() .find('.blue-tag') diff --git a/lib/v2/pts/live.js b/lib/v2/pts/live.js index 0a33dd7c0cfd68..0813872874d811 100644 --- a/lib/v2/pts/live.js +++ b/lib/v2/pts/live.js @@ -16,7 +16,7 @@ module.exports = async (ctx) => { url: apiUrl, }); - let items = response.data.data.blogArticleList.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 30).map((item) => ({ + let items = response.data.data.blogArticleList.slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 30).map((item) => ({ link: `${rootUrl}/live/api/liveblog/article?articleId=${item}&model=main`, })); diff --git a/lib/v2/pubmed/trending.js b/lib/v2/pubmed/trending.js index 3e761efd7df99d..adb0fae2e0a532 100644 --- a/lib/v2/pubmed/trending.js +++ b/lib/v2/pubmed/trending.js @@ -8,7 +8,7 @@ module.exports = async (ctx) => { const filters = ctx.params.filters; const rootUrl = 'https://pubmed.ncbi.nlm.nih.gov'; - const currentUrl = `${rootUrl}/trending${filters ? `?filter=${filters.replace(/,/g, '&filter=')}` : ''}`; + const currentUrl = `${rootUrl}/trending${filters ? `?filter=${filters.replaceAll(',', '&filter=')}` : ''}`; const response = await got({ method: 'get', diff --git a/lib/v2/pumc/mdadmission.js b/lib/v2/pumc/mdadmission.js index 0be2fbf845d937..5425a766614e40 100644 --- a/lib/v2/pumc/mdadmission.js +++ b/lib/v2/pumc/mdadmission.js @@ -3,7 +3,7 @@ const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 100; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 100; const rootUrl = 'https://mdadmission.pumc.edu.cn'; const currentUrl = `${rootUrl}/mdweb/site!noticeList?param.infoTypeId=&rows=${limit}&pages=1`; diff --git a/lib/v2/putty/changes.js b/lib/v2/putty/changes.js index 09fb4385bc371e..393b9d29017a8e 100644 --- a/lib/v2/putty/changes.js +++ b/lib/v2/putty/changes.js @@ -11,7 +11,7 @@ module.exports = async (ctx) => { url: currentUrl, }); - const $ = cheerio.load(response.data.replace(/href="releases/g, 'class="version" href="releases')); + const $ = cheerio.load(response.data.replaceAll('href="releases', 'class="version" href="releases')); const items = $('.version') .toArray() diff --git a/lib/v2/pwc/maintainer.js b/lib/v2/pwc/maintainer.js new file mode 100644 index 00000000000000..e65ff960509917 --- /dev/null +++ b/lib/v2/pwc/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/strategyand/sustainability': ['mintyfrankie'], +}; diff --git a/lib/v2/pwc/radar.js b/lib/v2/pwc/radar.js new file mode 100644 index 00000000000000..fd8e3ceb2b9cc5 --- /dev/null +++ b/lib/v2/pwc/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'pwc.com': { + _name: 'PwC Strategy&', + strategyand: [ + { + title: 'Sustainability', + docs: 'https://docs.rsshub.app/routes/other#pwc-strategy', + source: ['/at/en/functions/sustainability-strategy/publications.html', '/'], + target: '/pwc/strategyand/sustainability', + }, + ], + }, +}; diff --git a/lib/v2/pwc/router.js b/lib/v2/pwc/router.js new file mode 100644 index 00000000000000..82e9e51f1dbaa3 --- /dev/null +++ b/lib/v2/pwc/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/strategyand/sustainability', require('./sustainability')); +}; diff --git a/lib/v2/pwc/sustainability.js b/lib/v2/pwc/sustainability.js new file mode 100644 index 00000000000000..0dd381c038ddde --- /dev/null +++ b/lib/v2/pwc/sustainability.js @@ -0,0 +1,58 @@ +const cheerio = require('cheerio'); +const logger = require('@/utils/logger'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const baseUrl = 'https://www.strategyand.pwc.com/at/en/functions/sustainability-strategy/publications.html'; + const feedLang = 'en'; + const feedDescription = 'Sustainability Publications from PwC Strategy&'; + + const browser = await require('@/utils/puppeteer')(); + const page = await browser.newPage(); + logger.http(`Requesting ${baseUrl}`); + await page.setRequestInterception(true); + page.on('request', (request) => { + request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort(); + }); + await page.goto(baseUrl, { + waitUntil: 'domcontentloaded', + }); + const response = await page.content(); + page.close(); + + const $ = cheerio.load(response); + + const list = $('div#wrapper article') + .toArray() + .map((item) => { + item = $(item); + const a = item.find('a').first(); + const div = item.find('div.collection__item-content').first(); + + const link = a.attr('href'); + const title = div.find('h4').find('span').text(); + const pubDate = parseDate(div.find('time').attr('datetime'), 'DD/MM/YY'); + const description = div.find('p.paragraph').text(); + + return { + title, + link, + pubDate, + description, + }; + }); + + const items = list; + + // TODO: Add full text support + + browser.close(); + + ctx.state.data = { + title: 'PwC Strategy& - Sustainability Publications', + link: baseUrl, + language: feedLang, + description: feedDescription, + item: items, + }; +}; diff --git a/lib/v2/qbitai/category.js b/lib/v2/qbitai/category.js new file mode 100644 index 00000000000000..291bd9b1adb628 --- /dev/null +++ b/lib/v2/qbitai/category.js @@ -0,0 +1,27 @@ +const parser = require('@/utils/rss-parser'); + +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const { category } = ctx.params; + const link = encodeURI(`https://www.qbitai.com/category/${category}/feed`); + const feed = await parser.parseURL(link); + + const items = feed.items.map((item) => ({ + title: item.title, + pubDate: parseDate(item.pubDate), + link: item.link, + author: '量子位', + category: item.categories, + description: item['content:encoded'], + })); + + ctx.state.data = { + // 源标题 + title: `量子位-${category}`, + // 源链接 + link: `https://www.qbitai.com/category/${category}`, + // 源文章 + item: items, + }; +}; diff --git a/lib/v2/qbitai/maintainer.js b/lib/v2/qbitai/maintainer.js new file mode 100644 index 00000000000000..26f19df2a9803c --- /dev/null +++ b/lib/v2/qbitai/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/category/:category': ['FuryMartin'], + '/tag/:tag': ['FuryMartin'], +}; diff --git a/lib/v2/qbitai/radar.js b/lib/v2/qbitai/radar.js new file mode 100644 index 00000000000000..ec46f915a94d7a --- /dev/null +++ b/lib/v2/qbitai/radar.js @@ -0,0 +1,19 @@ +module.exports = { + 'qbitai.com': { + _name: '量子位', + '.': [ + { + title: '分类', + docs: 'https://docs.rsshub.app/routes/new-media#liang-zi-wei', + source: ['/category/:category'], + target: '/qbitai/category/:category', + }, + { + title: '标签', + docs: 'https://docs.rsshub.app/routes/new-media#liang-zi-wei', + source: ['/tag/:tag'], + target: '/qbitai/tag/:tag', + }, + ], + }, +}; diff --git a/lib/v2/qbitai/router.js b/lib/v2/qbitai/router.js new file mode 100644 index 00000000000000..511ccb2e348d96 --- /dev/null +++ b/lib/v2/qbitai/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/category/:category', require('./category')); + router.get('/tag/:tag', require('./tag')); +}; diff --git a/lib/v2/qbitai/tag.js b/lib/v2/qbitai/tag.js new file mode 100644 index 00000000000000..cd0fc9e2e7029b --- /dev/null +++ b/lib/v2/qbitai/tag.js @@ -0,0 +1,27 @@ +const parser = require('@/utils/rss-parser'); + +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const { tag } = ctx.params; + const link = encodeURI(`https://www.qbitai.com/tag/${tag}/feed`); + const feed = await parser.parseURL(link); + + const items = feed.items.map((item) => ({ + title: item.title, + pubDate: parseDate(item.pubDate), + link: item.link, + author: '量子位', + category: item.categories, + description: item['content:encoded'], + })); + + ctx.state.data = { + // 源标题 + title: `量子位-${tag}`, + // 源链接 + link: `https://www.qbitai.com/tag/${tag}`, + // 源文章 + item: items, + }; +}; diff --git a/lib/v2/qdaily/index.js b/lib/v2/qdaily/index.js deleted file mode 100644 index f1d1e88bbcdb01..00000000000000 --- a/lib/v2/qdaily/index.js +++ /dev/null @@ -1,87 +0,0 @@ -const cheerio = require('cheerio'); -const got = require('@/utils/got'); -const { parseDate } = require('@/utils/parse-date'); -const { art } = require('@/utils/render'); -const path = require('path'); - -module.exports = async (ctx) => { - const baseUrl = 'http://www.qdaily.com'; - const { type, id } = ctx.params; - - const typeMap = { - tag: { - type: 'tags', - apiPath: 'tags/tagmore', - apiSuffix: '.json', - }, - category: { - type: 'categories', - apiPath: 'categories/categorymore', - apiSuffix: '.json', - }, - column: { - type: 'special_columns', - apiPath: 'special_columns/show_more', - apiSuffix: '', - }, - }; - - const url = `${baseUrl}/${typeMap[type].type}/${id}.html`; - - const res = await got(url); - const $ = cheerio.load(res.data); - const lastKey = $('.packery-container').attr('data-lastkey'); - - const { data } = await got(`${baseUrl}/${typeMap[type].apiPath}/${id}/${lastKey}${typeMap[type].apiSuffix}`); - - const list = data.data.feeds.map((item) => ({ - title: item.post.title, - description: item.post.description, - pubDate: parseDate(item.post.publish_time), - link: `${baseUrl}/articles/${item.post.id}.html`, - category: item.post.category.title, - image: item.image, - })); - - const out = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.link, async () => { - const res = await got(item.link); - const $ = cheerio.load(res.data); - - $('.author-share, .embed-mask, .lazylad, .lazyloa, .lazylod, .lazylaad, .lazylood, .lazyloadd').remove(); - $('.article-detail-bd') - .find('img') - .each((_, img) => { - if (img.attribs['data-src']) { - img.attribs.src = img.attribs['data-src'].split('-w600')[0]; - delete img.attribs['data-src']; - } - }); - - item.description = art(path.join(__dirname, 'templates/article.art'), { - image: item.image.split('?')[0], - description: $('.article-detail-bd').html(), - }); - item.category = [ - ...new Set([ - item.category, - ...$('.tags .tag a') - .toArray() - .map((item) => $(item).text()), - ]), - ]; - - return item; - }) - ) - ); - - ctx.state.data = { - title: $('head title').text(), - description: $('head meta[name="description"]').attr('content'), - image: 'http://www.qdaily.com/favicon.ico', - link: url, - item: out, - }; -}; diff --git a/lib/v2/qdaily/maintainer.js b/lib/v2/qdaily/maintainer.js deleted file mode 100644 index 2b59f155e963f1..00000000000000 --- a/lib/v2/qdaily/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/:type/:id': ['WenhuWee', 'emdoe', 'SivaGao', 'HenryQW'], -}; diff --git a/lib/v2/qdaily/radar.js b/lib/v2/qdaily/radar.js deleted file mode 100644 index 14c83db06c2003..00000000000000 --- a/lib/v2/qdaily/radar.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - 'qdaily.com': { - _name: '好奇心日报', - '.': [ - { - title: '标签', - docs: 'https://docs.rsshub.app/routes/new-media#hao-qi-xin-ri-bao', - source: ['/tags/:id'], - target: (params) => `/qdaily/tag/${params.id.replace('.html', '')}`, - }, - { - title: '栏目', - docs: 'https://docs.rsshub.app/routes/new-media#hao-qi-xin-ri-bao', - source: ['/special_columns/:id'], - target: (params) => `/qdaily/column/${params.id.replace('.html', '')}`, - }, - { - title: '分类', - docs: 'https://docs.rsshub.app/routes/new-media#hao-qi-xin-ri-bao', - source: ['/categories/:id'], - target: (params) => `/qdaily/category/${params.id.replace('.html', '')}`, - }, - ], - }, -}; diff --git a/lib/v2/qdaily/router.js b/lib/v2/qdaily/router.js deleted file mode 100644 index dee9a31869651d..00000000000000 --- a/lib/v2/qdaily/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (router) => { - router.get('/:type/:id', require('./index')); -}; diff --git a/lib/v2/qdu/houqin.js b/lib/v2/qdu/houqin.js new file mode 100644 index 00000000000000..12bf0f03b0e269 --- /dev/null +++ b/lib/v2/qdu/houqin.js @@ -0,0 +1,63 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const timezone = require('@/utils/timezone'); +const { parseDate } = require('@/utils/parse-date'); + +const base = 'https://houqin.qdu.edu.cn/'; + +module.exports = async (ctx) => { + const response = await got({ + method: 'get', + url: `${base}index/tzgg.htm`, + }); + + const $ = cheerio.load(response.data); + const list = $('.n_newslist').children(); + const items = await Promise.all( + list.map((i, item) => { + item = $(item); + const itemTitle = item.find('a').text(); + let itemDate = timezone(parseDate(item.find('span').text()), 8); + const path = item.find('a').attr('href'); + const itemUrl = base + path; + return ctx.cache.tryGet(itemUrl, async () => { + let description = ''; + const result = await got(itemUrl); + const $ = cheerio.load(result.data); + if ( + $('.article_body') + .find('div > h4') + .text() + .match(/发布时间:(.*)编辑:/) !== null + ) { + itemDate = timezone( + parseDate( + $('.article_body') + .find('div > h4') + .text() + .match(/发布时间:(.*)编辑:/)[1] + .trim(), + 'YYYY年MM月DD日 HH:mm' + ), + 8 + ); + } + description = $('.v_news_content').html().trim(); + + return { + title: itemTitle, + link: itemUrl, + pubDate: itemDate, + description, + }; + }); + }) + ); + + ctx.state.data = { + title: '青岛大学 - 后勤管理处通知', + link: `${base}index/tzgg.htm`, + description: '青岛大学 - 后勤管理处通知', + item: items, + }; +}; diff --git a/lib/v2/qdu/jwc.js b/lib/v2/qdu/jwc.js index 22c6d07a1bac82..c199ebf3e6363b 100644 --- a/lib/v2/qdu/jwc.js +++ b/lib/v2/qdu/jwc.js @@ -20,24 +20,18 @@ module.exports = async (ctx) => { const itemDate = item.find('span').text(); const path = item.find('.active').attr('href'); let itemUrl = ''; - if (!path.startsWith('http')) { - itemUrl = base + path; - } else { - itemUrl = path; - } + itemUrl = path.startsWith('http') ? path : base + path; return ctx.cache.tryGet(itemUrl, async () => { let description = ''; - if (!path.startsWith('http')) { + if (path.startsWith('http')) { + description = itemTitle; + } else { const result = await got(itemUrl); const $ = cheerio.load(result.data); - if ($('title').text() === '系统提示') { - // 内网限制访问内容,仅返回标题 - description = itemTitle; - } else { - description = $('.v_news_content').html().trim(); - } - } else { - description = itemTitle; + description = + $('title').text() === '系统提示' + ? itemTitle // 内网限制访问内容,仅返回标题 + : $('.v_news_content').html().trim(); } return { title: itemTitle, diff --git a/lib/v2/qdu/maintainer.js b/lib/v2/qdu/maintainer.js index f4270d26f29089..4350fcb6e757dc 100644 --- a/lib/v2/qdu/maintainer.js +++ b/lib/v2/qdu/maintainer.js @@ -1,3 +1,4 @@ module.exports = { + '/houqin': ['abc1763613206'], '/jwc': ['abc1763613206'], }; diff --git a/lib/v2/qdu/radar.js b/lib/v2/qdu/radar.js index e9f0a50c5e02c9..9db67e88e30071 100644 --- a/lib/v2/qdu/radar.js +++ b/lib/v2/qdu/radar.js @@ -9,5 +9,13 @@ module.exports = { target: '/qdu/jwc', }, ], + houqin: [ + { + title: '后勤管理处通知', + docs: 'https://docs.rsshub.app/routes/university#qing-dao-da-xue', + source: ['/tzgg.htm', '/'], + target: '/qdu/houqin', + }, + ], }, }; diff --git a/lib/v2/qdu/router.js b/lib/v2/qdu/router.js index 2df83518651c4e..70712ea2b6885a 100644 --- a/lib/v2/qdu/router.js +++ b/lib/v2/qdu/router.js @@ -1,3 +1,4 @@ module.exports = (router) => { + router.get('/houqin', require('./houqin')); router.get('/jwc', require('./jwc')); }; diff --git a/lib/v2/qianp/news.js b/lib/v2/qianp/news.js index 31b0806e920f31..8ab55c8eea818e 100644 --- a/lib/v2/qianp/news.js +++ b/lib/v2/qianp/news.js @@ -1,13 +1,20 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); +const { getTokenAndSecret } = require('./utils'); module.exports = async (ctx) => { const baseUrl = 'https://qianp.com'; const { path = 'news/recommend' } = ctx.params; const url = `${baseUrl}/${path}/`; - const { data: response } = await got(url); + const { token, secret } = await getTokenAndSecret(ctx.cache.tryGet); + const headers = { + cookie: token ? `t=${token}; r=${secret - 100}` : undefined, + }; + const { data: response } = await got(url, { + headers, + }); const $ = cheerio.load(response); const list = $('.newslist .infor') @@ -24,7 +31,9 @@ module.exports = async (ctx) => { const items = await Promise.all( list.map((item) => ctx.cache.tryGet(item.link, async () => { - const { data: response } = await got(item.link); + const { data: response } = await got(item.link, { + headers, + }); const $ = cheerio.load(response); item.category = [...new Set($('meta[name=keywords]').attr('content').split(','))]; diff --git a/lib/v2/qianp/radar.js b/lib/v2/qianp/radar.js index 1370fe713b3351..2c7140ff43c048 100644 --- a/lib/v2/qianp/radar.js +++ b/lib/v2/qianp/radar.js @@ -6,7 +6,7 @@ module.exports = { title: '知识库/资讯', docs: 'https://docs.rsshub.app/routes/new-media#qian-pian-wang', source: ['/*path'], - target: (params) => (!params.path.endsWith('.html') ? `/qianp/news/${params.path}` : null), + target: (params) => (params.path.endsWith('.html') ? null : `/qianp/news/${params.path}`), }, ], }, diff --git a/lib/v2/qianp/utils.js b/lib/v2/qianp/utils.js new file mode 100644 index 00000000000000..ab274f5cfd911d --- /dev/null +++ b/lib/v2/qianp/utils.js @@ -0,0 +1,19 @@ +const got = require('@/utils/got'); + +const getTokenAndSecret = (tryGet) => + tryGet('qianp:token', async () => { + const response = await got('https://qianp.com/news/recommend/'); + const token = response.headers['set-cookie'] + .find((cookie) => cookie.startsWith('token=')) + ?.split(';')[0] + ?.split('=')[1]; + const secret = response.headers['set-cookie'] + .find((cookie) => cookie.startsWith('secret=')) + ?.split(';')[0] + ?.split('=')[1]; + return { token, secret }; + }); + +module.exports = { + getTokenAndSecret, +}; diff --git a/lib/v2/qidian/forum.js b/lib/v2/qidian/forum.js index 8911e247a86b87..0ff278dcbc24c0 100644 --- a/lib/v2/qidian/forum.js +++ b/lib/v2/qidian/forum.js @@ -19,8 +19,7 @@ module.exports = async (ctx) => { const list = $('li.post-wrap>.post'); const items = []; - for (let i = 0; i < list.length; ++i) { - const el = list[i]; + for (const el of list) { const title = $(el).children().eq(1).find('a'); items.push({ title: title.text(), diff --git a/lib/v2/qidiantu/index.js b/lib/v2/qidiantu/index.js deleted file mode 100644 index bce4d877baceb1..00000000000000 --- a/lib/v2/qidiantu/index.js +++ /dev/null @@ -1,87 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const timezone = require('@/utils/timezone'); -const { parseDate } = require('@/utils/parse-date'); - -module.exports = async (ctx) => { - const category = ctx.params.category ?? 'shouding'; - const type = ctx.params.type ?? (category === 'shouding' ? '' : '1'); - const isHistory = /t|y/i.test(ctx.params.is_history ?? 'false'); - - const today = new Date(); - today.setTime(new Date().getTime() - 24 * 60 * 60 * 1000); - const yesterday = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`; - - const rootUrl = 'https://www.qidiantu.com'; - const currentUrl = `${rootUrl}/${category === 'shouding' ? `${category}/` : `bang/${category}/${type}/${isHistory ? '' : yesterday}`}`; - - const response = await got({ - method: 'get', - url: currentUrl, - }); - - const $ = cheerio.load(response.data); - - $('a[role="button"]').remove(); - - let items = $(isHistory ? 'ul li a' : 'tbody tr') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 25) - .toArray() - .map((item) => { - item = $(item); - - return isHistory - ? { - title: item.text(), - link: `${rootUrl}${item.attr('href')}`, - } - : { - author: item.find('td').eq(3).text(), - title: item.find('td[value]').first().text(), - link: `${rootUrl}${item.find('td a').first().attr('href').replace(/\/d$/, '')}`, - category: item - .find(`td${category === 'shouding' ? '[value]' : ''}`) - .eq(1) - .text(), - }; - }); - - items = await Promise.all( - items.map((item) => - ctx.cache.tryGet(isHistory ? item.link : `${item.link}-${yesterday}`, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - - const content = cheerio.load(detailResponse.data); - - content('#demo').next().remove(); - - if (isHistory) { - item.description = content('.table-responsive').html(); - item.pubDate = parseDate(item.link.split('/').pop()); - } else { - item.description = content('.media').html() + content('.panel-body').html(); - item.pubDate = timezone( - parseDate( - content('tbody tr') - .last() - .text() - .match(/刷新时间:(\d{4}-\d{2}-\d{2} \d{2}:\d{2})/)[1] - ), - +8 - ); - } - - return item; - }) - ) - ); - - ctx.state.data = { - title: `${$('.panel-heading').text()} - 起点图`, - link: currentUrl, - item: items, - }; -}; diff --git a/lib/v2/qidiantu/maintainer.js b/lib/v2/qidiantu/maintainer.js deleted file mode 100644 index 01cddd0c9d2499..00000000000000 --- a/lib/v2/qidiantu/maintainer.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - '/:category?/:type?/:is_history?': ['nczitzk'], - '/shouding': ['nczitzk'], -}; diff --git a/lib/v2/qidiantu/radar.js b/lib/v2/qidiantu/radar.js deleted file mode 100644 index 6e213fc791a8aa..00000000000000 --- a/lib/v2/qidiantu/radar.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - 'qidiantu.com': { - _name: '起点图', - '.': [ - { - title: '首订', - docs: 'https://docs.rsshub.app/routes/reading#qi-dian-tu-shou-ding', - source: ['/shouding', '/'], - target: '/qidiantu/shouding', - }, - { - title: '榜单', - docs: 'https://docs.rsshub.app/routes/reading#qi-dian-tu-bang-dan', - source: ['/bang/:category/:type', '/'], - target: '/qidiantu/:category?/:type?/:is_history?', - }, - ], - }, -}; diff --git a/lib/v2/qidiantu/router.js b/lib/v2/qidiantu/router.js deleted file mode 100644 index 701fe52391ed89..00000000000000 --- a/lib/v2/qidiantu/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (router) { - router.get('/:category?/:type?/:is_history?', require('./index')); -}; diff --git a/lib/v2/qingting/podcast.js b/lib/v2/qingting/podcast.js index ff395d06fd2845..7d148a6d47cc91 100644 --- a/lib/v2/qingting/podcast.js +++ b/lib/v2/qingting/podcast.js @@ -30,7 +30,7 @@ module.exports = async (ctx) => { ctx.cache.tryGet(`qingting:podcast:${ctx.params.id}:${item.id}`, async () => { const link = `https://www.qingting.fm/channels/${ctx.params.id}/programs/${item.id}/`; - const path = `/audiostream/redirect/${ctx.params.id}/${item.id}?access_token=&device_id=MOBILESITE&qingting_id=&t=${new Date().getTime()}`; + const path = `/audiostream/redirect/${ctx.params.id}/${item.id}?access_token=&device_id=MOBILESITE&qingting_id=&t=${Date.now()}`; const sign = crypto.createHmac('md5', 'fpMn12&38f_2e').update(path).digest('hex').toString(); const [detailRes, mediaRes] = await Promise.all([ diff --git a/lib/v2/qlu/notice.js b/lib/v2/qlu/notice.js index 36ab66fb63881f..648c3f4955c20c 100644 --- a/lib/v2/qlu/notice.js +++ b/lib/v2/qlu/notice.js @@ -20,20 +20,15 @@ module.exports = async (ctx) => { const itemTitle = item.find('.news_title').children().text(); const itemDate = item.find('.news_year').text() + item.find('.news_days').text(); const path = item.find('.news_title').children().attr('href'); - let itemUrl = ''; - if (!path.startsWith('https://') && !path.startsWith('http://')) { - itemUrl = host + path; - } else { - itemUrl = path; - } + const itemUrl = path.startsWith('https') ? path : host + path; return ctx.cache.tryGet(itemUrl, async () => { let description = ''; - if (!path.startsWith('https://') && !path.startsWith('http://')) { + if (path.startsWith('https')) { + description = itemTitle; + } else { const result = await got(itemUrl); const $ = cheerio.load(result.data); description = $('.read').html().trim(); - } else { - description = itemTitle; } return { title: itemTitle, diff --git a/lib/v2/qoo-app/news.js b/lib/v2/qoo-app/news.js index 2506c2c408049c..d628b7e03ec52b 100644 --- a/lib/v2/qoo-app/news.js +++ b/lib/v2/qoo-app/news.js @@ -10,7 +10,7 @@ module.exports = async (ctx) => { const { data } = await got(apiUrl, { searchParams: { - per_page: ctx.query.limit ? parseInt(ctx.query.limit) : 100, + per_page: ctx.query.limit ? Number.parseInt(ctx.query.limit) : 100, }, }); @@ -30,12 +30,12 @@ module.exports = async (ctx) => { ctx.state.data = { title: 'QooApp : Anime Game Platform', description: - lang !== 'en' - ? 'QooApp 是專注二次元的專業平台,旨在聚集世界各地熱愛ACG的用戶,為他們創造有價值的服務和產品。從遊戲商店、新聞資訊、玩家社群,到線下聚會、漫畫閱讀、遊戲發行——QooApp不斷進化中,拓展突破次元的遊玩體驗。' - : 'QooApp is a professional platform specialising in Anime, Comics and Games (ACG) culture. We aim to unite ACG fans around the globe and help them as thoroughly as we can.', + lang === 'en' + ? 'QooApp is a professional platform specialising in Anime, Comics and Games (ACG) culture. We aim to unite ACG fans around the globe and help them as thoroughly as we can.' + : 'QooApp 是專注二次元的專業平台,旨在聚集世界各地熱愛ACG的用戶,為他們創造有價值的服務和產品。從遊戲商店、新聞資訊、玩家社群,到線下聚會、漫畫閱讀、遊戲發行——QooApp不斷進化中,拓展突破次元的遊玩體驗。', image: siteIcon, link: `${newsUrl}${lang ? `/${lang}` : ''}`, - language: lang !== 'en' ? 'zh' : 'en', + language: lang === 'en' ? 'en' : 'zh', item: items, }; }; diff --git a/lib/v2/qoo-app/notes/note.js b/lib/v2/qoo-app/notes/note.js index e4a148c3ab86ac..6e7bb12ae3ac6c 100644 --- a/lib/v2/qoo-app/notes/note.js +++ b/lib/v2/qoo-app/notes/note.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { searchParams: { sort: 'newest', for: 'web', - limit: ctx.query.limit ? parseInt(ctx.query.limit) : 100, + limit: ctx.query.limit ? Number.parseInt(ctx.query.limit) : 100, type: 'note', object_id: id, }, diff --git a/lib/v2/qoo-app/router.js b/lib/v2/qoo-app/router.js index ba23c0252ac99b..0d4f4cd9d8785c 100644 --- a/lib/v2/qoo-app/router.js +++ b/lib/v2/qoo-app/router.js @@ -7,5 +7,5 @@ module.exports = (router) => { router.get('/notes/:lang?/note/:id', require('./notes/note')); router.get('/notes/:lang?/topic/:topic', require('./notes/topic')); router.get('/notes/:lang?/user/:uid', require('./notes/user')); - router.get('/user/:lang?/appComment/:uid', require('./user/appComment')); + router.get('/user/:lang?/appComment/:uid', require('./user/app-comment')); }; diff --git a/lib/v2/qoo-app/user/appComment.js b/lib/v2/qoo-app/user/app-comment.js similarity index 100% rename from lib/v2/qoo-app/user/appComment.js rename to lib/v2/qoo-app/user/app-comment.js diff --git a/lib/v2/qq/ac/utils.js b/lib/v2/qq/ac/utils.js index b5b2190af40ff8..df3af92f2fd64a 100644 --- a/lib/v2/qq/ac/utils.js +++ b/lib/v2/qq/ac/utils.js @@ -18,7 +18,7 @@ module.exports = { const $ = cheerio.load(response.data); let items = $(`${time ? `.${time}-month-data ` : ''}.text-overflow`) - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 30) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 30) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/qq/kg/cache.js b/lib/v2/qq/kg/cache.js index 30d83a679540bf..e3241eb489e3b4 100644 --- a/lib/v2/qq/kg/cache.js +++ b/lib/v2/qq/kg/cache.js @@ -17,7 +17,7 @@ module.exports = { const itunes_item_image = data.detail.cover; const enclosure_url = data.detail.playurl; - ksong_mid = ksong_mid ? ksong_mid : data.detail.ksong_mid; + ksong_mid = ksong_mid ?? data.detail.ksong_mid; const ctime = data.detail.ctime; const comments = data.detail.comments; diff --git a/lib/v2/qq/live.js b/lib/v2/qq/live.js deleted file mode 100644 index 23a915e30a68ef..00000000000000 --- a/lib/v2/qq/live.js +++ /dev/null @@ -1,36 +0,0 @@ -const got = require('@/utils/got'); -const { parseDate } = require('@/utils/parse-date'); - -module.exports = async (ctx) => { - const id = ctx.params.id; - - const rootUrl = 'https://live.qq.com'; - const currentUrl = `${rootUrl}/${id}`; - - const response = await got({ - method: 'get', - url: currentUrl, - }); - - const data = response.data.match(/"is_live":(\d),"game_count".*"nickname":"(.*)","child_name".*"show_time":(\d+),"activity".*"room_name":"(.*)","user_chat_level"/); - - const items = - data[1] === '1' - ? [ - { - author: data[2], - guid: data[3], - title: data[4], - pubDate: parseDate(data[3] * 1000), - link: currentUrl, - }, - ] - : []; - - ctx.state.data = { - title: `${data[2]}直播间 - 企鹅直播`, - link: currentUrl, - item: items, - allowEmpty: true, - }; -}; diff --git a/lib/v2/qq/maintainer.js b/lib/v2/qq/maintainer.js index b5ef93f713e3e1..3d904715837c15 100644 --- a/lib/v2/qq/maintainer.js +++ b/lib/v2/qq/maintainer.js @@ -4,5 +4,4 @@ module.exports = { '/fact': ['hoilc'], '/kg/:userId': ['zhangxiang012'], '/kg/reply/:playId': ['zhangxiang012'], - '/live/:id': ['nczitzk'], }; diff --git a/lib/v2/qq/radar.js b/lib/v2/qq/radar.js index f273d28a8cd2c4..ab42693277da7d 100644 --- a/lib/v2/qq/radar.js +++ b/lib/v2/qq/radar.js @@ -15,14 +15,6 @@ module.exports = { target: '/qq/ac/comic/:id', }, ], - live: [ - { - title: '企鹅直播', - docs: 'https://docs.rsshub.app/routes/live#qi-e-zhi-bo-zhi-bo-jian-ti-xing', - source: ['/:id', '/'], - target: '/qq/live/:id', - }, - ], 'node.kg': [ { title: '用户作品列表 - 全民 K 歌', diff --git a/lib/v2/qq/router.js b/lib/v2/qq/router.js index 0d38e65656ed0e..b6297c74dfe0db 100644 --- a/lib/v2/qq/router.js +++ b/lib/v2/qq/router.js @@ -4,5 +4,4 @@ module.exports = function (router) { router.get('/fact', require('./fact')); router.get('/kg/:userId', require('./kg/user')); router.get('/kg/reply/:playId', require('./kg/reply')); - router.get('/live/:id', require('./live')); }; diff --git a/lib/v2/qqorw/index.js b/lib/v2/qqorw/index.js index 2f51f2763572e3..efc7c139128759 100644 --- a/lib/v2/qqorw/index.js +++ b/lib/v2/qqorw/index.js @@ -5,7 +5,7 @@ const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { const { category = '' } = ctx.params; - const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 10; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 10; const rootUrl = 'https://qqorw.cn'; const currentUrl = new URL(category, rootUrl).href; @@ -31,7 +31,7 @@ module.exports = async (ctx) => { .toArray() .map((c) => $(c).text()), pubDate: timezone(parseDate(item.find('p.auth-span span.muted').first().text().trim()), +8), - upvotes: item.find('span.count').text() ? parseInt(item.find('span.count').text(), 10) : 0, + upvotes: item.find('span.count').text() ? Number.parseInt(item.find('span.count').text(), 10) : 0, }; }); @@ -51,7 +51,7 @@ module.exports = async (ctx) => { .toArray() .map((c) => content(c).text().trim()); item.pubDate = item.pubDate ?? parseDate(content('i.fa-clock-o').parent().text().trim()); - item.upvotes = content('#Addlike span.count').text() ? parseInt(content('#Addlike span.count').text(), 10) : item.upvotes; + item.upvotes = content('#Addlike span.count').text() ? Number.parseInt(content('#Addlike span.count').text(), 10) : item.upvotes; return item; }) diff --git a/lib/v2/qqorw/maintainer.js b/lib/v2/qqorw/maintainer.js index ee544d6c969ccb..8d86e95b45ea72 100644 --- a/lib/v2/qqorw/maintainer.js +++ b/lib/v2/qqorw/maintainer.js @@ -1,4 +1,4 @@ module.exports = { + '/': ['nczitzk'], '/:category?': ['nczitzk'], - '': ['nczitzk'], }; diff --git a/lib/v2/questmobile/maintainer.js b/lib/v2/questmobile/maintainer.js new file mode 100644 index 00000000000000..204040069b5d16 --- /dev/null +++ b/lib/v2/questmobile/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/report/:industry?/:label?': ['nczitzk'], +}; diff --git a/lib/v2/questmobile/radar.js b/lib/v2/questmobile/radar.js new file mode 100644 index 00000000000000..6ab6b9df2a130b --- /dev/null +++ b/lib/v2/questmobile/radar.js @@ -0,0 +1,18 @@ +module.exports = { + 'questmobile.com.cn': { + _name: 'QuestMobile', + '.': [ + { + title: '行业研究报告', + docs: 'https://docs.rsshub.app/routes/new-media#questmobile-hang-ye-yan-jiu-bao-gao', + source: ['/research/reports/:industry/:label'], + target: (params) => { + const industry = params.industry; + const label = params.label; + + return `/questmobile/report/${industry}/${label}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/questmobile/report.js b/lib/v2/questmobile/report.js new file mode 100644 index 00000000000000..6700b676f6c0fb --- /dev/null +++ b/lib/v2/questmobile/report.js @@ -0,0 +1,127 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +/** + * Parses a tree array and returns an array of objects containing the key-value pairs. + * @param {Array} tree - The tree to parse. + * @param {Array} result - The result array to store the parsed key-value pairs. Default is an empty array. + * + * @returns {Array} - An array of objects containing the key-value pairs. + */ +const parseTree = (tree, result = []) => { + for (const obj of tree) { + const { key, value, children } = obj; + result.push({ key, value }); + + if (children && children.length > 0) { + parseTree(children, result); + } + } + + return result; +}; + +module.exports = async (ctx) => { + const { industry, label } = ctx.params; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 50; + + const rootUrl = 'https://www.questmobile.com.cn'; + const apiUrl = new URL('api/v2/report/article-list', rootUrl).href; + const apiTreeUrl = new URL('api/v2/report/industry-label-tree', rootUrl).href; + + const { + data: { + data: { industryTree, labelTree }, + }, + } = await got(apiTreeUrl); + + const industries = parseTree(industryTree); + const labels = parseTree(labelTree); + + const industryObj = industry ? industries.find((i) => i.key === industry || i.value === industry) : undefined; + const labelObj = label ? labels.find((i) => i.key === label || i.value === label) : industryObj ? undefined : labels.find((i) => i.key === industry || i.value === industry); + + const industryId = industryObj?.key ?? -1; + const labelId = labelObj?.key ?? -1; + + const currentUrl = new URL(`research/reports/${industryObj?.key ?? -1}/${labelObj?.key ?? -1}`, rootUrl).href; + + const { data: response } = await got(apiUrl, { + searchParams: { + version: 0, + pageSize: limit, + pageNo: 1, + industryId, + labelId, + }, + }); + + let items = response.data.slice(0, limit).map((item) => ({ + title: item.title, + link: new URL(`research/report/${item.id}`, rootUrl).href, + description: art(path.join(__dirname, 'templates/description.art'), { + image: { + src: item.coverImgUrl, + alt: item.title, + }, + introduction: item.introduction, + description: item.content, + }), + category: [...(item.industryList ?? []), ...(item.labelList ?? [])], + guid: `questmobile-report#${item.id}`, + pubDate: parseDate(item.publishTime), + })); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: detailResponse } = await got(item.link); + + const content = cheerio.load(detailResponse); + + content('div.text div.daoyu').remove(); + + item.title = content('div.title h1').text(); + item.description += art(path.join(__dirname, 'templates/description.art'), { + description: content('div.text').html(), + }); + item.author = content('div.source') + .text() + .replace(/^.*?:/, ''); + item.category = content('div.hy, div.keyword') + .find('span') + .toArray() + .map((c) => content(c).text()); + item.pubDate = parseDate(content('div.data span').prop('datetime')); + + return item; + }) + ) + ); + + const { data: currentResponse } = await got(currentUrl); + + const $ = cheerio.load(currentResponse); + + const author = $('meta[property="og:title"]').prop('content').split(/-/)[0]; + const categories = [industryObj?.value, labelObj?.value].filter(Boolean); + const image = $(`img[alt="${author}"]`).prop('src'); + const icon = $('link[rel="shortcut icon"]').prop('href'); + + ctx.state.data = { + item: items, + title: `${author}${categories.length === 0 ? '' : ` - ${categories.join(' - ')}`}`, + link: currentUrl, + description: $('meta[property="og:description"]').prop('content'), + language: 'zh', + image, + icon, + logo: icon, + subtitle: $('meta[name="keywords"]').prop('content'), + author, + allowEmpty: true, + }; +}; diff --git a/lib/v2/questmobile/router.js b/lib/v2/questmobile/router.js new file mode 100644 index 00000000000000..337e9db84dc900 --- /dev/null +++ b/lib/v2/questmobile/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/report/:industry?/:label?', require('./report')); +}; diff --git a/lib/v2/questmobile/templates/description.art b/lib/v2/questmobile/templates/description.art new file mode 100644 index 00000000000000..dd4ad1216ff8a5 --- /dev/null +++ b/lib/v2/questmobile/templates/description.art @@ -0,0 +1,17 @@ +{{ if image?.src }} +
+ {{ image.alt }} +
+{{ /if }} + +{{ if introduction }} +
{{ introduction }}
+{{ /if }} + +{{ if description }} + {{@ description }} +{{ /if }} \ No newline at end of file diff --git a/lib/v2/quicker/qa.js b/lib/v2/quicker/qa.js index 1fd2037920a8fb..28dc7d9e6bfbf4 100644 --- a/lib/v2/quicker/qa.js +++ b/lib/v2/quicker/qa.js @@ -8,7 +8,7 @@ module.exports = async (ctx) => { const state = ctx.params.state ?? ''; const rootUrl = 'https://getquicker.net'; - const currentUrl = `${rootUrl}/QA${category !== 'all' ? `?category=${category}` : ''}${state ? `?state=${state}` : ''}`; + const currentUrl = `${rootUrl}/QA${category === 'all' ? '' : `?category=${category}`}${state ? `?state=${state}` : ''}`; const response = await got({ method: 'get', @@ -18,7 +18,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('a.question-title') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 25) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 25) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/quicker/share.js b/lib/v2/quicker/share.js index d17cb5c320cbce..8c16cb06d6fc89 100644 --- a/lib/v2/quicker/share.js +++ b/lib/v2/quicker/share.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('table tbody tr') - .slice(1, ctx.query.limit ? parseInt(ctx.query.limit) : 25) + .slice(1, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 25) .toArray() .map((item) => { item = $(item).find('td a').first(); @@ -41,7 +41,7 @@ module.exports = async (ctx) => { content('section').last().remove(); content('#app').children().slice(0, 2).remove(); - const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replace(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); + const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); item.author = content('.user-link').first().text(); item.description = content('div[data-info="动作信息"]').html() ?? content('#app').html() ?? content('.row').eq(1).html(); diff --git a/lib/v2/quicker/user.js b/lib/v2/quicker/user.js index cdd64c19f90dd6..cdbedcc0b1bada 100644 --- a/lib/v2/quicker/user.js +++ b/lib/v2/quicker/user.js @@ -18,7 +18,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('table tbody tr') - .slice(1, ctx.query.limit ? parseInt(ctx.query.limit) : 25) + .slice(1, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 25) .toArray() .map((item) => { item = $(item).find('td a').first(); @@ -42,7 +42,7 @@ module.exports = async (ctx) => { content('section').last().remove(); content('#app').children().slice(0, 2).remove(); - const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replace(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); + const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); item.author = content('.user-link').first().text(); item.description = content('div[data-info="动作信息"]').html() ?? content('#app').html() ?? content('.row').eq(1).html(); diff --git a/lib/v2/qweather/3days.js b/lib/v2/qweather/3days.js index 67193b8dfa9298..f3f7e8c03a55ba 100644 --- a/lib/v2/qweather/3days.js +++ b/lib/v2/qweather/3days.js @@ -2,45 +2,72 @@ const got = require('@/utils/got'); const { art } = require('@/utils/render'); const path = require('path'); const config = require('@/config').value; -const rootUrl = 'https://devapi.qweather.com/v7/weather/3d?'; + +const WEATHER_API = 'https://devapi.qweather.com/v7/weather/3d'; +const AIR_QUALITY_API = 'https://devapi.qweather.com/v7/air/5d'; +const CIRY_LOOKUP_API = 'https://geoapi.qweather.com/v2/city/lookup'; +const author = 'QWeather'; module.exports = async (ctx) => { + if (!config.hefeng.key) { + throw new Error('QWeather RSS is disabled due to the lack of relevant config'); + } const id = await ctx.cache.tryGet(ctx.params.location + '_id', async () => { - const response = await got(`https://geoapi.qweather.com/v2/city/lookup?location=${ctx.params.location}&key=${config.hefeng.key}`); - const data = []; - for (const i in response.data.location) { - data.push(response.data.location[i]); - } - return data[0].id; + const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.params.location}&key=${config.hefeng.key}`); + return response.data.location[0].id; }); - const requestUrl = rootUrl + 'key=' + config.hefeng.key + '&location=' + id; - const responseData = await ctx.cache.tryGet( + const weatherData = await ctx.cache.tryGet( ctx.params.location, async () => { - const response = await got(requestUrl); + const response = await got(`${WEATHER_API}?key=${config.hefeng.key}&location=${id}`); return response.data; }, config.cache.contentExpire, false ); - const data = []; - for (const i in responseData.daily) { - data.push(responseData.daily[i]); - } - const items = data.map((item) => ({ - title: '预报日期:' + item.fxDate, + const airQualityData = await ctx.cache.tryGet( + `qweather:air:${ctx.params.location}`, + async () => { + const airQualityResponse = await got(`${AIR_QUALITY_API}?location=${id}&key=${config.hefeng.key}`); + return airQualityResponse.data; + }, + config.cache.contentExpire, + false + ); + // merge weather data with air quality data + const combined = { + updateTime: weatherData.updateTime, + fxLink: weatherData.fxLink, + daily: weatherData.daily.map((weatherItem) => { + const dailyAirQuality = airQualityData.daily.find((airQualityItem) => airQualityItem.fxDate === weatherItem.fxDate); + if (dailyAirQuality) { + return { + ...weatherItem, + aqi: dailyAirQuality.aqi, + aqiLevel: dailyAirQuality.level, + aqiCategory: dailyAirQuality.category, + aqiPrimary: dailyAirQuality.primary, + }; + } + return weatherItem; + }), + }; + const items = combined.daily.map((item) => ({ + title: `${item.fxDate}: ${item.textDay === item.textNight ? item.textDay : item.textDay + '转' + item.textNight} ${item.tempMin}~${item.tempMax}℃`, description: art(path.join(__dirname, 'templates/3days.art'), { item, }), - pubDate: responseData.updateTime, + pubDate: combined.updateTime, guid: '位置:' + ctx.params.location + '--日期:' + item.fxDate, - link: responseData.fxLink, + link: combined.fxLink, + author, })); ctx.state.data = { title: ctx.params.location + '未来三天天气', - description: ctx.params.location + '未来三天天气情况,使用和风彩云api', + description: ctx.params.location + '未来三天天气情况,使用和风彩云 API (包括空气质量)', item: items, - link: responseData.fxLink, + link: combined.fxLink, + author, }; }; diff --git a/lib/v2/qweather/maintainer.js b/lib/v2/qweather/maintainer.js index 6739e554889da6..a4ae1c85115af0 100644 --- a/lib/v2/qweather/maintainer.js +++ b/lib/v2/qweather/maintainer.js @@ -1,4 +1,4 @@ module.exports = { - '/3days/:location': ['Rein-Ou'], + '/3days/:location': ['Rein-Ou', 'la3rence'], '/now/:location': ['Rein-Ou'], }; diff --git a/lib/v2/qweather/templates/3days.art b/lib/v2/qweather/templates/3days.art index 5d97c1d928a388..8a217577df7d06 100644 --- a/lib/v2/qweather/templates/3days.art +++ b/lib/v2/qweather/templates/3days.art @@ -4,6 +4,8 @@
相对湿度:{{item.humidity}}%
+空气质量指数:{{item.aqi}} ({{item.aqiCategory}}) +
大气压强:{{item.pressure}}百帕
紫外线强度:{{item.uvIndex}} diff --git a/lib/v2/radio-canada/latest.js b/lib/v2/radio-canada/latest.js index 789a48307d1982..c5aa007c99b6a9 100644 --- a/lib/v2/radio-canada/latest.js +++ b/lib/v2/radio-canada/latest.js @@ -1,5 +1,6 @@ const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); +const cheerio = require('cheerio'); module.exports = async (ctx) => { const language = ctx.params.language ?? 'en'; @@ -28,7 +29,13 @@ module.exports = async (ctx) => { url: item.link, }); - item.description = detailResponse.data.match(/"body":{"html":"(.*)","attachments"/)[1].replace(/\\n/g, ''); + const $ = cheerio.load(detailResponse.data); + const rcState = $('script:contains("window._rcState_ = JSON.parse")') + .text() + .match(/JSON\.parse\((".*")\);/)[1]; + const rcStateJson = JSON.parse(JSON.parse(rcState)); + const news = Object.values(rcStateJson.pagesV2.pages)[0]; + item.description = news.data.newsStory.body.html.replaceAll('\\n', '
'); return item; }) diff --git a/lib/v2/radio/album.js b/lib/v2/radio/album.js index 8212df7771f343..456e2e614a329c 100644 --- a/lib/v2/radio/album.js +++ b/lib/v2/radio/album.js @@ -8,7 +8,7 @@ module.exports = async (ctx) => { const KEY = 'f0fc4c668392f9f9a447e48584c214ee'; const id = ctx.params.id; - const size = ctx.query.limit ? parseInt(ctx.query.limit) : 100; + const size = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 100; const rootUrl = 'https://www.radio.cn'; const apiRootUrl = 'https://ytmsout.radio.cn'; @@ -54,7 +54,7 @@ module.exports = async (ctx) => { const params = `albumId=${id}&pageNo=${pageNo}&pageSize=${pageSize}`; const apiUrl = `${apiRootUrl}/web/appSingle/pageByAlbum?${params}`; - const timestamp = new Date().getTime(); + const timestamp = Date.now(); const sign = CryptoJS.MD5(`${params}×tamp=${timestamp}&key=${KEY}`).toString().toUpperCase(); const response = await got({ @@ -101,6 +101,6 @@ module.exports = async (ctx) => { image: iconUrl, itunes_author: data.anchorpersons, description: data.descriptions ?? data.descriptionSimple, - itunes_category: data.atypeInfo.map((c) => c.categoryName).join(), + itunes_category: data.atypeInfo.map((c) => c.categoryName).join(','), }; }; diff --git a/lib/v2/radio/zhibo.js b/lib/v2/radio/zhibo.js index f0a58241d573ca..3f52df1ffea1f0 100644 --- a/lib/v2/radio/zhibo.js +++ b/lib/v2/radio/zhibo.js @@ -19,7 +19,7 @@ module.exports = async (ctx) => { const currentUrl = `${rootUrl}/pc-portal/sanji/zhibo_2.html?name=${id}`; const apiUrl = `${apiRootUrl}/web/appProgram/pageByColumn?${params}`; - const timestamp = new Date().getTime(); + const timestamp = Date.now(); const sign = CryptoJS.MD5(`${params}×tamp=${timestamp}&key=${KEY}`).toString().toUpperCase(); const response = await got({ diff --git a/lib/v2/radiofrance/geopolitique.js b/lib/v2/radiofrance/geopolitique.js deleted file mode 100644 index 50e0c87403b59a..00000000000000 --- a/lib/v2/radiofrance/geopolitique.js +++ /dev/null @@ -1,33 +0,0 @@ -const path = require('path'); -const got = require('@/utils/got'); -const parser = require('@/utils/rss-parser'); -const { art } = require('@/utils/render'); - -module.exports = async (ctx) => { - const feed = await parser.parseURL('https://radiofrance-podcast.net/podcast09/rss_10009.xml'); - - const items = await Promise.all( - // only retrieve the full text for the first 3 items - feed.items.slice(0, 3).map((item) => - ctx.cache.tryGet(item.link, async () => { - const uri = item.link.slice(item.link.indexOf('/franceinter')); - const apiurl = 'https://www.radiofrance.fr/api/v2.1/path?value=' + encodeURIComponent(uri); - const { data } = await got(apiurl); - - delete item.content; - delete item.contentSnippet; - - item.description = art(path.join(__dirname, 'templates/article.art'), data.content); - return item; - }) - ) - ); - - ctx.state.data = { - title: feed.title, - link: feed.link, - description: feed.description, - item: items, - language: feed.language, - }; -}; diff --git a/lib/v2/radiofrance/maintainer.js b/lib/v2/radiofrance/maintainer.js deleted file mode 100644 index 0abbe714b1717e..00000000000000 --- a/lib/v2/radiofrance/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/geopolitique': ['xdu'], -}; diff --git a/lib/v2/radiofrance/radar.js b/lib/v2/radiofrance/radar.js deleted file mode 100644 index 341ae5316143e4..00000000000000 --- a/lib/v2/radiofrance/radar.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - 'radiofrance.fr': { - _name: 'Radio France', - www: [ - { - title: 'Géopolitique', - docs: 'https://docs.rsshub.app/routes/traditional-media#fa-guo-guang-bo-dian-tai', - source: ['/franceinter/podcasts/geopolitique', '/'], - target: '/radiofrance/geopolitique', - }, - ], - }, -}; diff --git a/lib/v2/radiofrance/router.js b/lib/v2/radiofrance/router.js deleted file mode 100644 index 55bcc39947209e..00000000000000 --- a/lib/v2/radiofrance/router.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (router) => { - router.get('/geopolitique', require('./geopolitique')); -}; diff --git a/lib/v2/radiofrance/templates/article.art b/lib/v2/radiofrance/templates/article.art deleted file mode 100644 index 83bb4f424bdd7e..00000000000000 --- a/lib/v2/radiofrance/templates/article.art +++ /dev/null @@ -1,6 +0,0 @@ -

{{ standFirst }}

-{{ each bodyJson }} -{{ if $value.type === 'text' }} -

{{ $value.children[0].value }}

-{{ /if }} -{{ /each }} diff --git a/lib/v2/rarehistoricalphotos/radar.js b/lib/v2/rarehistoricalphotos/radar.js index ee93ada2391efd..c832d7accb90fb 100644 --- a/lib/v2/rarehistoricalphotos/radar.js +++ b/lib/v2/rarehistoricalphotos/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'Home', - docs: 'https://docs.rsshub.app/routes/en/picture#rare-historical-photos', + docs: 'https://docs.rsshub.app/routes/picture#rare-historical-photos', source: ['/'], target: '/rarehistoricalphotos', }, diff --git a/lib/v2/rattibha/radar.js b/lib/v2/rattibha/radar.js index b853ee9a120504..b63268076c0e58 100644 --- a/lib/v2/rattibha/radar.js +++ b/lib/v2/rattibha/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'User Threads', - docs: 'https://docs.rsshub.app/routes/en/social-media#rattibha', + docs: 'https://docs.rsshub.app/routes/social-media#rattibha', source: ['/:user'], target: '/rattibha/user/:user', }, diff --git a/lib/v2/rawkuma/manga.js b/lib/v2/rawkuma/manga.js index 28a7b5fd0358e7..191af8308128f1 100644 --- a/lib/v2/rawkuma/manga.js +++ b/lib/v2/rawkuma/manga.js @@ -6,7 +6,7 @@ const path = require('path'); module.exports = async (ctx) => { const { id } = ctx.params; - const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 50; const rootUrl = 'https://rawkuma.com'; const currentUrl = new URL(`/manga/${id}`, rootUrl).href; @@ -44,7 +44,7 @@ module.exports = async (ctx) => { const content = cheerio.load(detailResponse); - const imageMatches = detailResponse.match(/"images":(\[.*?\])\}\],"lazyload"/); + const imageMatches = detailResponse.match(/"images":(\[.*?])}],"lazyload"/); const images = imageMatches ? JSON.parse(imageMatches[1]) : []; diff --git a/lib/v2/reactnewsletter/maintainer.js b/lib/v2/reactnewsletter/maintainer.js index e9948c57ffa2bc..5e6a04e747cde4 100644 --- a/lib/v2/reactnewsletter/maintainer.js +++ b/lib/v2/reactnewsletter/maintainer.js @@ -1,4 +1,3 @@ module.exports = { - '': ['meixger'], '/': ['meixger'], }; diff --git a/lib/v2/reactnewsletter/radar.js b/lib/v2/reactnewsletter/radar.js index 0ca693028c1774..5de3068b74b617 100644 --- a/lib/v2/reactnewsletter/radar.js +++ b/lib/v2/reactnewsletter/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'React Newsletter', - docs: 'https://docs.rsshub.app/routes/en/programming#ui-dev-bytes', + docs: 'https://docs.rsshub.app/routes/programming#ui-dev-bytes', source: ['/issues', '/'], target: '/reactnewsletter', }, diff --git a/lib/v2/readhub/daily.js b/lib/v2/readhub/daily.js new file mode 100644 index 00000000000000..9dfbfdb7f3cc14 --- /dev/null +++ b/lib/v2/readhub/daily.js @@ -0,0 +1,43 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +const { rootUrl, processItems } = require('./util'); + +module.exports = async (ctx) => { + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 11; + + const currentUrl = new URL('daily', rootUrl).href; + + const { data: currentResponse } = await got(currentUrl); + + const dailyItems = JSON.parse(currentResponse.match(/{\\"daily\\":(.*?),\\"ts\\":\d+/)[1].replaceAll('\\"', '"')); + + let items = dailyItems.slice(0, limit).map((item) => ({ + title: item.title, + link: new URL(`topic/${item.uid}`, rootUrl).href, + guid: item.uid, + })); + + items = await processItems(items, ctx.cache.tryGet); + + const $ = cheerio.load(currentResponse); + + const author = $('meta[name="application-name"]').prop('content'); + const subtitle = $('meta[property="og:title"]').prop('content'); + const image = 'https://readhub-oss.nocode.com/static/readhub.png'; + const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl); + + ctx.state.data = { + item: items, + title: `${author} - ${subtitle}`, + link: currentUrl, + description: $('meta[name="description"]').prop('content'), + language: 'zh', + image, + icon, + logo: icon, + subtitle, + author, + allowEmpty: true, + }; +}; diff --git a/lib/v2/readhub/index.js b/lib/v2/readhub/index.js index 20f5281fe6d5a3..e64428a2f54b44 100644 --- a/lib/v2/readhub/index.js +++ b/lib/v2/readhub/index.js @@ -1,106 +1,62 @@ const got = require('@/utils/got'); +const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); +const path = require('path'); -const titles = { - topic: '热门话题', - news: '科技动态', - tech: '技术资讯', - technews: '技术资讯', - blockchain: '区块链快讯', - daily: '每日早报', -}; +const { rootUrl, apiTopicUrl, art, processItems } = require('./util'); module.exports = async (ctx) => { - let category = 'topic'; - let overview = false; - if (titles[ctx.params.category]) { - category = ctx.params.category; - overview = !!ctx.params.overview; - category = category === 'tech' ? 'technews' : category; - } else if (ctx.params.category) { - overview = true; - } + const { category = '' } = ctx.params; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 30; + + const currentUrl = new URL(category ?? '', rootUrl).href; - const rootUrl = 'https://readhub.cn'; - const apiRootUrl = 'https://api.readhub.cn'; - const currentUrl = `${rootUrl}/topic/${category}`; - const apiUrl = `${apiRootUrl}/${category === 'daily' ? 'topic/daily' : category}`; + const { data: currentResponse } = await got(currentUrl); - const response = await got({ - method: 'get', - url: apiUrl, + const type = currentResponse.match(/\[\\"type\\",\\"(\d+)\\",\\"d\\"]/)?.[1] ?? '1'; + + const { data: response } = await got(apiTopicUrl, { + searchParams: { + type, + page: 1, + size: limit, + }, }); - const list = response.data.data.map((item) => ({ - id: item.id, + let items = response.data.items.slice(0, limit).map((item) => ({ title: item.title, + link: item.url ?? new URL(`topic/${item.uid}`, rootUrl).href, + description: art(path.join(__dirname, 'templates/description.art'), { + description: item.summary, + news: item.newsAggList, + timeline: item.timeline, + }), + author: item.siteNameDisplay, + category: [...(item.entityList.map((c) => c.name) ?? []), ...(item.tagList.map((c) => c.name) ?? [])], + guid: item.uid, pubDate: parseDate(item.publishDate), - description: item.summaryAuto || item.summary || '', - link: item.url || item.mobileUrl || `${rootUrl}/topic/${item.id}`, - author: item.authorName || item.siteName || '', - hasInstantView: item.hasInstantView || false, })); - const items = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.id + overview, async () => { - if (item.hasInstantView || category === 'daily') { - const { data } = await got({ - method: 'get', - url: `${apiRootUrl}/topic/instantview?topicId=${item.id}`, - }); - - item.description = `访问原网址
${!overview ? data.content : ''}`; - } - - if (isNaN(item.id)) { - const { data } = await got({ - method: 'get', - url: `${apiRootUrl}/topic/${item.id}`, - }); - - if (data.summary && overview) { - item.description += `${data.summary}
`; - } - - if (data.newsArray) { - item.description += '
媒体报道
    '; - - for (const news of data.newsArray) { - item.description += - `
  • ${news.title}` + - (news.siteName ? `  ${news.siteName}` : '') + - `${news.publishDate ? `  ${news.publishDate.split('T')[0]}` : ''}
  • `; - } + items = await processItems(items, ctx.cache.tryGet); - item.description += '
'; - } + const $ = cheerio.load(currentResponse); - if (data.timeline && data.timeline.topics) { - const hasTimeline = data.timeline.commonEntities && data.timeline.commonEntities.length > 0; - - item.description += `
${hasTimeline ? '事件追踪' : '相关事件'}
    `; - - for (const news of data.timeline.topics) { - const createdAt = news.createdAt ? `${news.createdAt.split('T')[0]}` : ''; - - item.description += `
  • ${hasTimeline ? `${createdAt}  ` : ''}${news.title}${hasTimeline ? '' : `  ${createdAt}`}
  • `; - } - - item.description += '
'; - } - } - delete item.id; - delete item.hasInstantView; - - return item; - }) - ) - ); + const author = $('meta[property="og:site_name"]').prop('content'); + const subtitle = $(`a[data-path="/${category}"]`).text(); + const image = $('link[rel="preload"][as="image"]').prop('href'); + const icon = $('meta[property="og:image"]').prop('content'); ctx.state.data = { - title: `Readhub - ${titles[category]}`, - link: currentUrl, item: items, + title: `${author} - ${subtitle}`, + link: currentUrl, + description: $('meta[property="og:description"]').prop('content'), + language: 'zh', + image, + icon, + logo: icon, + subtitle, + author, + allowEmpty: true, }; }; diff --git a/lib/v2/readhub/maintainer.js b/lib/v2/readhub/maintainer.js index 4c09355e31437c..5443177db57370 100644 --- a/lib/v2/readhub/maintainer.js +++ b/lib/v2/readhub/maintainer.js @@ -1,3 +1,4 @@ module.exports = { - '/:category?/:overview?': ['WhiteWorld', 'nczitzk', 'Fatpandac'], + '/daily': ['nczitzk'], + '/:category?': ['WhiteWorld', 'nczitzk', 'Fatpandac'], }; diff --git a/lib/v2/readhub/radar.js b/lib/v2/readhub/radar.js index 4df725e9621eaf..697c970066389e 100644 --- a/lib/v2/readhub/radar.js +++ b/lib/v2/readhub/radar.js @@ -4,9 +4,19 @@ module.exports = { '.': [ { title: '分类', - docs: 'https://docs.rsshub.app/routes/new-media#readhub', + docs: 'https://docs.rsshub.app/routes/new-media#readhub-fen-lei', source: ['/', '/:category'], - target: (params) => `/readhub/${params.category ? params.category : ''}`, + target: (params) => { + const category = params.category; + + return `/readhub${category ? `/${category}` : ''}`; + }, + }, + { + title: '每日早报', + docs: 'https://docs.rsshub.app/routes/new-media#readhub-mei-ri-zao-bao', + source: ['/daily'], + target: '/readhub/daily', }, ], }, diff --git a/lib/v2/readhub/router.js b/lib/v2/readhub/router.js index da527b1da359aa..90098e33978214 100644 --- a/lib/v2/readhub/router.js +++ b/lib/v2/readhub/router.js @@ -1,6 +1,4 @@ module.exports = (router) => { - // deprecated - router.get('/category/:category?/:overview?', require('./index')); - - router.get('/:category?/:overview?', require('./index')); + router.get('/daily', require('./daily')); + router.get('/:category?', require('./')); }; diff --git a/lib/v2/readhub/templates/description.art b/lib/v2/readhub/templates/description.art new file mode 100644 index 00000000000000..a7570ea87c87a0 --- /dev/null +++ b/lib/v2/readhub/templates/description.art @@ -0,0 +1,40 @@ +{{ if description }} +

{{ description }}

+{{ /if }} + +{{ if news }} +

媒体报道

+ + + {{ each news n }} + + + + + {{ /each }} + +
+ {{ n.title }} + + {{ n.siteNameDisplay }} +
+{{ /if }} + +{{ if timeline }} +

事件追踪

+ + + {{ set topics = timeline.topics }} + {{ each topics t }} + + + + + {{ /each }} + +
+ {{ t.publishDate | formatDate 'YYYY-MM-DD HH:mm:ss' }} + + {{ t.title }} +
+{{ /if }} \ No newline at end of file diff --git a/lib/v2/readhub/util.js b/lib/v2/readhub/util.js new file mode 100644 index 00000000000000..b6890d4a548eae --- /dev/null +++ b/lib/v2/readhub/util.js @@ -0,0 +1,68 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); +const dayjs = require('dayjs'); + +const domain = 'readhub.cn'; +const rootUrl = `https://${domain}`; +const apiRootUrl = `https://api.${domain}`; +const apiTopicUrl = new URL('topic/list', apiRootUrl).href; + +const formatDate = (date, format) => dayjs(date).format(format); +const toTopicUrl = (id) => new URL(`topic/${id}`, rootUrl).href; + +art.defaults.imports = { + ...art.defaults.imports, + + formatDate, + toTopicUrl, +}; + +/** + * Process items asynchronously. + * + * @param {Array} items - The array of items to process. + * @param {function} tryGet - The tryGet function that handles the retrieval process. + * @returns {Promise>} Returns a Promise that resolves to an array of processed items. + */ +const processItems = async (items, tryGet) => + await Promise.all( + items.map((item) => + tryGet(item.link, async () => { + try { + if (!item.link.startsWith(rootUrl)) { + throw new Error(`"${item.link}" is an external URL`); + } + + const { data: detailResponse } = await got(item.link); + + const data = JSON.parse(detailResponse.match(/{\\"topic\\":(.*?)}]\\n"]\)<\/script>/)[1].replaceAll('\\"', '"')); + + item.title = data.title; + item.link = data.url ?? new URL(`topic/${data.uid}`, rootUrl).href; + item.description = art(path.join(__dirname, 'templates/description.art'), { + description: data.summary, + news: data.newsAggList, + timeline: data.timeline, + }); + item.author = data.siteNameDisplay; + item.category = [...(data.entityList.map((c) => c.name) ?? []), ...(data.tagList.map((c) => c.name) ?? [])]; + item.guid = `readhub-${data.uid}`; + item.pubDate = parseDate(data.publishDate.replaceAll(/\s/g, '')); + } catch { + item.guid = `readhub-${item.guid}`; + } + + return item; + }) + ) + ); + +module.exports = { + rootUrl, + apiRootUrl, + apiTopicUrl, + art, + processItems, +}; diff --git a/lib/v2/remnote/changelog.js b/lib/v2/remnote/changelog.js index 6f128c5fc94f42..9b4c419f48a0ac 100644 --- a/lib/v2/remnote/changelog.js +++ b/lib/v2/remnote/changelog.js @@ -19,7 +19,6 @@ module.exports = async (ctx) => { description: item.description_html, link: `https://${FQDN}/changelog/${item.slug}`, pubDate: parseDate(item.published_at.timestamp), - author: item.user.name, })); ctx.state.data = { diff --git a/lib/v2/remnote/maintainer.js b/lib/v2/remnote/maintainer.js index 8d1ec28b519e62..5a91f339060632 100644 --- a/lib/v2/remnote/maintainer.js +++ b/lib/v2/remnote/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/changelog': ['TonyRL'], + '/changelog': ['TonyRL', 'amakerlife'], }; diff --git a/lib/v2/reuters/common.js b/lib/v2/reuters/common.js index 0fb924f85be82e..5fa03002bc17f8 100644 --- a/lib/v2/reuters/common.js +++ b/lib/v2/reuters/common.js @@ -5,47 +5,57 @@ const { art } = require('@/utils/render'); const path = require('path'); module.exports = async (ctx) => { + const MUST_FETCH_BY_TOPICS = new Set(['authors']); + const CAN_USE_SOPHI = ['world']; + const category = ctx.params.category; const topic = ctx.params.topic ?? (category === 'authors' ? 'reuters' : ''); - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 20; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 20; + const useSophi = ctx.query.sophi === 'true' && 'topic' !== '' && CAN_USE_SOPHI.includes(category); - const MUST_FETCH_BY_TOPICS = ['authors']; const section_id = `/${category}/${topic ? `${topic}/` : ''}`; const { title, description, rootUrl, response } = await (async () => { - if (!MUST_FETCH_BY_TOPICS.includes(category)) { - const rootUrl = 'https://www.reuters.com/pf/api/v3/content/fetch/articles-by-section-alias-or-id-v1'; + if (MUST_FETCH_BY_TOPICS.has(category)) { + const rootUrl = 'https://www.reuters.com/pf/api/v3/content/fetch/articles-by-topic-v1'; const response = await got(rootUrl, { searchParams: { query: JSON.stringify({ offset: 0, size: limit, - section_id, + topic_url: section_id, website: 'reuters', }), }, }).json(); + return { - title: response.result.section.title, - description: response.result.section.section_about, + title: `${response.result.topics[0].name} | Reuters`, + description: response.result.topics[0].entity_id, rootUrl, response, }; } else { - const rootUrl = 'https://www.reuters.com/pf/api/v3/content/fetch/articles-by-topic-v1'; + const rootUrl = 'https://www.reuters.com/pf/api/v3/content/fetch/articles-by-section-alias-or-id-v1'; const response = await got(rootUrl, { searchParams: { query: JSON.stringify({ offset: 0, size: limit, - topic_url: section_id, + section_id, website: 'reuters', + ...(useSophi + ? { + fetch_type: 'sophi', + sophi_page: '*', + sophi_widget: 'topic', + } + : {}), }), }, }).json(); - return { - title: `${response.result.topics[0].name} | Reuters`, - description: response.result.topics[0].entity_id, + title: response.result.section.title, + description: response.result.section.section_about, rootUrl, response, }; @@ -56,62 +66,70 @@ module.exports = async (ctx) => { title: e.title, link: new URL(e.canonical_url, rootUrl).href, guid: e.id, + pubDate: parseDate(e.published_time), + updated: parseDate(e.updated_time), + author: e.authors.map((e) => e.name).join(', '), + category: e.kicker.names, + description: e.description, })); items = items.filter((e, i) => items.findIndex((f) => e.guid === f.guid) === i); - items = await Promise.all( + const results = await Promise.allSettled( items.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got(item.link); - const content = cheerio.load(detailResponse.data); - - if (detailResponse.url.startsWith('https://www.reuters.com/investigates/')) { - const ldJson = JSON.parse(content('script[type="application/ld+json"]').text()); - content('.special-report-article-container .container, #slide-dek, #slide-end, .share-in-article-container').remove(); - - item.title = ldJson.headline; - item.pubDate = parseDate(ldJson.dateCreated); - item.author = ldJson.creator; - item.category = ldJson.keywords; - item.description = content('.special-report-article-container').html(); - - return item; - } - - const matches = content('script#fusion-metadata') - .text() - .match(/Fusion.globalContent=(\{[\s\S]*?});/); - - if (matches) { - const data = JSON.parse(matches[1]); - - item.title = data.result.title || item.title; - item.description = art(path.join(__dirname, 'templates/description.art'), { - result: data.result, - }); - item.pubDate = parseDate(data.result.display_time); - item.author = data.result.authors.map((author) => author.name).join(', '); - item.category = data.result.taxonomy.keywords; - - return item; - } - - content('.title').remove(); - content('.article-metadata').remove(); - - item.title = content('meta[property="og:title"]').attr('content'); - item.pubDate = parseDate(detailResponse.data.match(/"datePublished":"(.*?)","dateModified/)[1]); - item.author = detailResponse.data - .match(/{"@type":"Person","name":"(.*?)"}/g) - .map((p) => p.match(/"name":"(.*?)"/)[1]) - .join(', '); - item.description = content('article').html(); - - return item; - }) + ctx.query.mode === 'fulltext' + ? ctx.cache.tryGet(item.link, async () => { + const detailResponse = await got(item.link); + const content = cheerio.load(detailResponse.data); + + if (detailResponse.url.startsWith('https://www.reuters.com/investigates/')) { + const ldJson = JSON.parse(content('script[type="application/ld+json"]').text()); + content('.special-report-article-container .container, #slide-dek, #slide-end, .share-in-article-container').remove(); + + item.title = ldJson.headline; + item.pubDate = parseDate(ldJson.dateCreated); + item.author = ldJson.creator; + item.category = ldJson.keywords; + item.description = content('.special-report-article-container').html(); + + return item; + } + + const matches = content('script#fusion-metadata') + .text() + .match(/Fusion.globalContent=({[\S\s]*?});/); + + if (matches) { + const data = JSON.parse(matches[1]); + + item.title = data.result.title || item.title; + item.description = art(path.join(__dirname, 'templates/description.art'), { + result: data.result, + }); + item.pubDate = parseDate(data.result.display_time); + item.author = data.result.authors.map((author) => author.name).join(', '); + item.category = data.result.taxonomy.keywords; + + return item; + } + + content('.title').remove(); + content('.article-metadata').remove(); + + item.title = content('meta[property="og:title"]').attr('content'); + item.pubDate = parseDate(detailResponse.data.match(/"datePublished":"(.*?)","dateModified/)[1]); + item.author = detailResponse.data + .match(/{"@type":"Person","name":"(.*?)"}/g) + .map((p) => p.match(/"name":"(.*?)"/)[1]) + .join(', '); + item.description = content('article').html(); + + return item; + }) + : item ) ); + items = results.filter((r) => r.status === 'fulfilled').map((r) => r.value); ctx.state.data = { title, diff --git a/lib/v2/reuters/maintainer.js b/lib/v2/reuters/maintainer.js index 3e66dfaba5cc86..91b65111de8e89 100644 --- a/lib/v2/reuters/maintainer.js +++ b/lib/v2/reuters/maintainer.js @@ -1,6 +1,4 @@ module.exports = { '/investigates': ['LyleLee'], - '/reuters/channel/:site/:channel': ['LyleLee'], // deprecated - '/reuters/theWire': ['LyleLee'], // deprecated '/:category/:topic?': ['LyleLee', 'HenryQW', 'proletarius101', 'black-desk', 'nczitzk'], }; diff --git a/lib/v2/reuters/migration_prompt.js b/lib/v2/reuters/migration_prompt.js deleted file mode 100644 index f0051806bbdc83..00000000000000 --- a/lib/v2/reuters/migration_prompt.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = () => { - throw Error('The route has been deprecated. Please refer to the docs for more details.'); -}; diff --git a/lib/v2/reuters/router.js b/lib/v2/reuters/router.js index e6968701dc31c4..36ad6828346196 100644 --- a/lib/v2/reuters/router.js +++ b/lib/v2/reuters/router.js @@ -1,6 +1,4 @@ module.exports = (router) => { - router.get('/channel/:site/:channel', require('./migration_prompt')); // deprecated - router.get('/theWire', require('./migration_prompt')); // deprecated router.get('/investigates', require('./investigates')); router.get('/:category/:topic?', require('./common')); }; diff --git a/lib/v2/rfa/index.js b/lib/v2/rfa/index.js index 191e11f9ef9961..e73e200d49b7da 100644 --- a/lib/v2/rfa/index.js +++ b/lib/v2/rfa/index.js @@ -17,14 +17,14 @@ module.exports = async (ctx) => { const selectors = ['div[id=topstorywidefull]', 'div.two_featured', 'div.three_featured', 'div.single_column_teaser', 'div.sectionteaser', 'div.specialwrap']; const list = []; - selectors.forEach((selector) => { + for (const selector of selectors) { $(selector).each((_, e) => { const item = {}; item.title = $(e).find('h2 a span').first().text(); item.link = $(e).find('h2 a').first().attr('href'); list.push(item); }); - }); + } const result = await Promise.all( list.map((item) => diff --git a/lib/v2/rfi/maintainer.js b/lib/v2/rfi/maintainer.js index ee48f58b836289..73ce08bdabe0ab 100644 --- a/lib/v2/rfi/maintainer.js +++ b/lib/v2/rfi/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/news/:lang?': ['nczitzk'], + '/:path*': ['nczitzk'], }; diff --git a/lib/v2/rfi/news.js b/lib/v2/rfi/news.js index 886579ab5ec2c3..ceed07565b97af 100644 --- a/lib/v2/rfi/news.js +++ b/lib/v2/rfi/news.js @@ -3,44 +3,53 @@ const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { - const LANGUAGE = new Map([ - ['en', '/en/live-news/'], - ['fr', '/fr/en-bref/'], - ['cn', '/cn/滚动新闻'], - ]); - - const rootUrl = 'https://www.rfi.fr'; - const currentUrl = `${rootUrl}${LANGUAGE.get(ctx.params.lang ?? 'cn')}`; - const response = await got({ - method: 'get', - url: currentUrl, - }); + const rootUrl = 'https://www.rfi.fr/'; + const path = ctx.params.path ?? 'en'; + const currentUrl = `${rootUrl}${path.endsWith('/') ? path : `${path}/`}`; + const response = await got(currentUrl); const $ = cheerio.load(response.data); - let items = $('.article__title') + $('aside[data-org-type="aside-content--highlighted"], aside[data-org-type="aside-content--sponsors"]').remove(); + + let items = $('.article__title a') .toArray() .map((item) => { item = $(item); return { title: item.text(), - link: `${rootUrl}${item.parent().parent().attr('href')}`, - pubDate: parseDate(item.prev().find('time').attr('datetime')), + link: new URL(item.attr('href'), currentUrl).href, }; }); items = await Promise.all( items.map((item) => ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - + const detailResponse = await got(item.link); const content = cheerio.load(detailResponse.data); - item.description = content('.t-content__body').html(); + content('.m-interstitial, .m-em-quote svg, .o-self-promo').remove(); + + const ldJson = JSON.parse(content('script[type="application/ld+json"]').text() || '[]').find((x) => x['@type'] === 'NewsArticle'); + + item.description = content('.t-content__chapo').prop('outerHTML') + content('.t-content__main-media').prop('outerHTML') + content('.t-content__body').html(); + item.pubDate = parseDate(ldJson?.datePublished); + item.updated = parseDate(ldJson?.dateModified); + item.author = ldJson?.author.map((author) => author.name).join(', '); + item.category = ldJson?.keywords.split(','); + + if (ldJson?.audio) { + item.itunes_item_image = ldJson.audio.thumbnailUrl; + // TODO: Use Temporal.Duration when https://tc39.es/proposal-temporal/ is GA + item.itunes_duration = ldJson.audio.duration + .match(/P0DT(\d+)H(\d+)M(\d+)S/) + .slice(1) + .map((x) => Number.parseInt(x)) + .reduce((a, b) => a * 60 + b); + item.enclosure_url = ldJson.audio.contentUrl; + item.enclosure_type = 'audio/mpeg'; + } return item; }) @@ -49,8 +58,12 @@ module.exports = async (ctx) => { ctx.state.data = { title: $('title').text(), + description: $('meta[name="description"]').attr('content'), link: currentUrl, + image: $('meta[property="og:image"]').attr('content'), + icon: new URL($('link[rel="apple-touch-icon"]').attr('href'), currentUrl).href, + logo: new URL($('link[rel="apple-touch-icon"]').attr('href'), currentUrl).href, item: items, - language: $("meta[property='og:locale']").attr('content'), + language: $('html').attr('lang'), }; }; diff --git a/lib/v2/rfi/radar.js b/lib/v2/rfi/radar.js index 6891f0266973bb..a78a782e4ca243 100644 --- a/lib/v2/rfi/radar.js +++ b/lib/v2/rfi/radar.js @@ -1,12 +1,12 @@ module.exports = { 'rfi.fr': { - _name: '法国国际广播电台', + _name: 'Radio France Internationale', '.': [ { - title: '滚动新闻', - docs: 'https://docs.rsshub.app/routes/multimedia#fa-guo-guo-ji-guang-bo-dian-tai-gun-dong-xin-wen', - source: ['/'], - target: '/rfi/news', + title: 'Generic News', + docs: 'https://docs.rsshub.app/routes/traditional-media##radio-france-internationale-fa-guo-guo-ji-guang-bo-dian-tai', + source: ['/*path'], + target: '/rfi/:path', }, ], }, diff --git a/lib/v2/rfi/router.js b/lib/v2/rfi/router.js index 84ffb5b5374278..7cef336a500035 100644 --- a/lib/v2/rfi/router.js +++ b/lib/v2/rfi/router.js @@ -1,3 +1,3 @@ module.exports = function (router) { - router.get('/news/:lang?', require('./news')); + router.get('/:path*', require('./news')); }; diff --git a/lib/v2/right/forum.js b/lib/v2/right/forum.js index 481cecc761fc21..94f2383ffea3f3 100644 --- a/lib/v2/right/forum.js +++ b/lib/v2/right/forum.js @@ -5,7 +5,7 @@ const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { const id = ctx.params.id ?? '31'; - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 20; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 20; const rootUrl = 'https://www.right.com.cn'; const currentUrl = `${rootUrl}/forum/forum-${id}-1.html`; diff --git a/lib/v2/routledge/book-series.js b/lib/v2/routledge/book-series.js new file mode 100644 index 00000000000000..0ad430541beb0f --- /dev/null +++ b/lib/v2/routledge/book-series.js @@ -0,0 +1,78 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (ctx) => { + const { bookName, bookId } = ctx.params; + const baseUrl = 'https://www.routledge.com'; + const pageUrl = `${baseUrl}/${bookName}/book-series/${bookId}`; + const { data: response } = await got(pageUrl, { + headers: { + accept: 'text/html, */*; q=0.01', + }, + searchParams: { + publishedFilter: 'alltitles', + pd: 'published,forthcoming', + pg: 1, + pp: 12, + so: 'pub', + view: 'list', + }, + }); + const $ = cheerio.load(response); + + const list = $('.row.book') + .toArray() + .map((item) => { + item = $(item); + const title = item.find('h3 a'); + const description = item.find('p.description'); + const meta = item.find('p.description').prev().text().split('\n'); + return { + title: title.text(), + link: title.attr('href'), + description: description.text(), + pubDate: parseDate(meta.pop().trim(), 'MMMM DD, YYYY'), + author: meta + .map((i) => i.trim()) + .filter(Boolean) + .join(', '), + }; + }); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data } = await got(item.link); + const $ = cheerio.load(data); + const isbn = $('meta[property="books:isbn"]').attr('content'); + const { data: image } = await got('https://productimages.routledge.com', { + searchParams: { + isbn, + size: 'amazon', + ext: 'jpg', + }, + }); + + const description = $('.sticky-div'); + description.find('button.accordion-button').contents().unwrap(); + description.find('.fa-shopping-cart').parent().parent().remove(); + + item.description = art(path.join(__dirname, 'templates/description.art'), { + image, + description: description.html(), + }); + return item; + }) + ) + ); + + ctx.state.data = { + title: $('head title').text(), + description: $('head meta[name="description"]').attr('content'), + link: pageUrl, + item: items, + }; +}; diff --git a/lib/v2/routledge/maintainer.js b/lib/v2/routledge/maintainer.js new file mode 100644 index 00000000000000..7c3f1201e895d5 --- /dev/null +++ b/lib/v2/routledge/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/:bookName/book-series/:bookId': ['TonyRL'], +}; diff --git a/lib/v2/routledge/radar.js b/lib/v2/routledge/radar.js new file mode 100644 index 00000000000000..2eb39553b6df2e --- /dev/null +++ b/lib/v2/routledge/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'routledge.com': { + _name: 'Routledge', + '.': [ + { + title: 'Book Series', + docs: 'https://docs.rsshub.app/routes/journals#routledge', + source: ['/:bookName/book-series/:bookId'], + target: '/routledge/:bookName/book-series/:bookId', + }, + ], + }, +}; diff --git a/lib/v2/routledge/router.js b/lib/v2/routledge/router.js new file mode 100644 index 00000000000000..51935b6e99be8a --- /dev/null +++ b/lib/v2/routledge/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/:bookName/book-series/:bookId', require('./book-series')); +}; diff --git a/lib/v2/qdaily/templates/article.art b/lib/v2/routledge/templates/description.art similarity index 70% rename from lib/v2/qdaily/templates/article.art rename to lib/v2/routledge/templates/description.art index 32915154eeda87..d7f9b4d48c9351 100644 --- a/lib/v2/qdaily/templates/article.art +++ b/lib/v2/routledge/templates/description.art @@ -2,4 +2,6 @@
{{ /if }} +{{ if description }} {{@ description }} +{{ /if }} diff --git a/lib/v2/rsc/journal.js b/lib/v2/rsc/journal.js index 5e04d34bf7b57b..5943bc79a62c10 100644 --- a/lib/v2/rsc/journal.js +++ b/lib/v2/rsc/journal.js @@ -7,7 +7,7 @@ const path = require('path'); module.exports = async (ctx) => { const { id, category = 'allrecentarticles' } = ctx.params; - const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 50; const rootUrl = 'https://pubs.rsc.org'; const currentUrl = new URL(`en/journals/journalissues/${id}#!recentarticles`, rootUrl).href; diff --git a/lib/v2/rsshub/maintainer.js b/lib/v2/rsshub/maintainer.js index 5ab179176c3389..158998b4511348 100644 --- a/lib/v2/rsshub/maintainer.js +++ b/lib/v2/rsshub/maintainer.js @@ -1,6 +1,5 @@ module.exports = { '/routes/:lang?': ['DIYgod'], - '/rsshub/sponsors': ['DIYgod'], '/transform/html/:url/:routeParams': ['ttttmr'], '/transform/json/:url/:routeParams': ['ttttmr'], '/transform/sitemap/:url/:routeParams?': ['flrngel'], diff --git a/lib/v2/rsshub/radar.js b/lib/v2/rsshub/radar.js index e8a59145b3e736..8327f7fec3d83e 100644 --- a/lib/v2/rsshub/radar.js +++ b/lib/v2/rsshub/radar.js @@ -8,12 +8,6 @@ module.exports = { source: ['/*'], target: '/rsshub/routes', }, - { - title: '有新赞助商啦', - docs: 'https://docs.rsshub.app/routes/program-update#rsshub', - source: ['/*'], - target: '/rsshub/sponsors', - }, ], }, }; diff --git a/lib/v2/rsshub/router.js b/lib/v2/rsshub/router.js index 2f90c082f82a93..7e580dcc58addd 100644 --- a/lib/v2/rsshub/router.js +++ b/lib/v2/rsshub/router.js @@ -1,6 +1,5 @@ module.exports = (router) => { router.get('/routes/:lang?', require('./routes')); - router.get('/sponsors', require('./sponsors')); router.get('/transform/html/:url/:routeParams', require('./transform/html')); router.get('/transform/json/:url/:routeParams', require('./transform/json')); router.get('/transform/sitemap/:url/:routeParams?', require('./transform/sitemap')); diff --git a/lib/v2/rsshub/sponsors.js b/lib/v2/rsshub/sponsors.js deleted file mode 100644 index c42197af4a127a..00000000000000 --- a/lib/v2/rsshub/sponsors.js +++ /dev/null @@ -1,47 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const response = await got({ - method: 'get', - url: 'https://docs.rsshub.app/', - }); - - const data = response.data; - - const $ = cheerio.load(data); - const special = $('#ming-xie-special-sponsors').next('p').find('a'); - const normal = $('#ming-xie-sponsors').next('p').find('a'); - - ctx.state.data = { - title: 'RSSHub 有新赞助商啦', - link: 'https://docs.rsshub.app/', - item: special - .map((index, item) => { - item = $(item); - const title = item.find('img').attr('alt'); - const link = item.attr('href') || 'https://docs.rsshub.app/'; - return { - title, - description: `${title}
`, - link, - }; - }) - .get() - .concat( - normal - .map((index, item) => { - item = $(item); - item.find('span').remove(); - const title = item.text(); - const link = item.attr('href') || 'https://docs.rsshub.app/'; - return { - title, - description: `${title}`, - link, - }; - }) - .get() - ), - }; -}; diff --git a/lib/v2/rsshub/transform/html.js b/lib/v2/rsshub/transform/html.js index 01e63ea9628869..5f12adbd7ca28a 100644 --- a/lib/v2/rsshub/transform/html.js +++ b/lib/v2/rsshub/transform/html.js @@ -14,32 +14,23 @@ module.exports = async (ctx) => { const routeParams = new URLSearchParams(ctx.params.routeParams); const $ = cheerio.load(response.data); - const rssTitle = routeParams.get('title') ? routeParams.get('title') : $('title').text(); - const item = routeParams.get('item') ? routeParams.get('item') : 'html'; + const rssTitle = routeParams.get('title') || $('title').text(); + const item = routeParams.get('item') || 'html'; const items = $(item) .toArray() .map((item) => { try { item = $(item); - let title; const titleEle = routeParams.get('itemTitle') ? item.find(routeParams.get('itemTitle')) : item; - if (routeParams.get('itemTitleAttr')) { - title = titleEle.attr(routeParams.get('itemTitleAttr')); - } else { - title = titleEle.text(); - } + const title = routeParams.get('itemTitleAttr') ? titleEle.attr(routeParams.get('itemTitleAttr')) : titleEle.text(); let link; const linkEle = routeParams.get('itemLink') ? item.find(routeParams.get('itemLink')) : item; if (routeParams.get('itemLinkAttr')) { link = linkEle.attr(routeParams.get('itemLinkAttr')); } else { - if (linkEle.is('a')) { - link = linkEle.attr('href'); - } else { - link = linkEle.find('a').attr('href'); - } + link = linkEle.is('a') ? linkEle.attr('href') : linkEle.find('a').attr('href'); } // 补全绝对链接 link = link.trim(); @@ -47,21 +38,11 @@ module.exports = async (ctx) => { link = `${new URL(url).origin}${link}`; } - let desc; const descEle = routeParams.get('itemDesc') ? item.find(routeParams.get('itemDesc')) : item; - if (routeParams.get('itemDescAttr')) { - desc = descEle.attr(routeParams.get('itemDescAttr')); - } else { - desc = descEle.html(); - } + const desc = routeParams.get('itemDescAttr') ? descEle.attr(routeParams.get('itemDescAttr')) : descEle.html(); - let pubDate; const pubDateEle = routeParams.get('itemPubDate') ? item.find(routeParams.get('itemPubDate')) : item; - if (routeParams.get('itemPubDateAttr')) { - pubDate = pubDateEle.attr(routeParams.get('itemPubDateAttr')); - } else { - pubDate = pubDateEle.html(); - } + const pubDate = routeParams.get('itemPubDateAttr') ? pubDateEle.attr(routeParams.get('itemPubDateAttr')) : pubDateEle.html(); return { title, @@ -69,7 +50,7 @@ module.exports = async (ctx) => { description: desc, pubDate, }; - } catch (e) { + } catch { return null; } }) diff --git a/lib/v2/rsshub/transform/json.js b/lib/v2/rsshub/transform/json.js index b9857eb717c8e6..9c183c95090463 100644 --- a/lib/v2/rsshub/transform/json.js +++ b/lib/v2/rsshub/transform/json.js @@ -8,9 +8,9 @@ function jsonGet(obj, attr) { } // a.b.c // a.b[0].c => a.b.0.c - attr.split('.').forEach((key) => { + for (const key of attr.split('.')) { obj = obj[key]; - }); + } return obj; } diff --git a/lib/v2/rsshub/transform/sitemap.js b/lib/v2/rsshub/transform/sitemap.js index 9fe1fabef69b67..18de638f3e102f 100644 --- a/lib/v2/rsshub/transform/sitemap.js +++ b/lib/v2/rsshub/transform/sitemap.js @@ -15,33 +15,31 @@ module.exports = async (ctx) => { const routeParams = new URLSearchParams(ctx.params.routeParams); const $ = cheerio.load(response.data, { xmlMode: true }); - const rssTitle = routeParams.get('title') ? routeParams.get('title') : $('urlset url').length && $('urlset url').first().find('loc').text() ? $('urlset url').first().find('loc').text() : 'Sitemap'; + const rssTitle = routeParams.get('title') || ($('urlset url').length && $('urlset url').first().find('loc').text() ? $('urlset url').first().find('loc').text() : 'Sitemap'); - let items; const urls = $('urlset url').toArray(); - if (urls && urls.length) { - items = urls - .map((item) => { - try { - const title = $(item).find('loc').text() || ''; - const link = $(item).find('loc').text() || ''; - const description = $(item).find('loc').text() || ''; - const pubDate = $(item).find('lastmod').text() || undefined; + const items = + urls && urls.length + ? urls + .map((item) => { + try { + const title = $(item).find('loc').text() || ''; + const link = $(item).find('loc').text() || ''; + const description = $(item).find('loc').text() || ''; + const pubDate = $(item).find('lastmod').text() || undefined; - return { - title, - link, - description, - pubDate, - }; - } catch (e) { - return null; - } - }) - .filter(Boolean); - } else { - items = []; - } + return { + title, + link, + description, + pubDate, + }; + } catch { + return null; + } + }) + .filter(Boolean) + : []; ctx.state.data = { title: rssTitle, diff --git a/lib/v2/ruancan/utils.js b/lib/v2/ruancan/utils.js index 465776162162ec..4bc21001d288de 100644 --- a/lib/v2/ruancan/utils.js +++ b/lib/v2/ruancan/utils.js @@ -14,7 +14,7 @@ module.exports = async (ctx, currentUrl) => { const $ = cheerio.load(response.data); let items = $('.item-title a') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 15) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 15) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/ruc/hr.js b/lib/v2/ruc/hr.js index 18befc901f4d33..4dc05b387c9cd3 100644 --- a/lib/v2/ruc/hr.js +++ b/lib/v2/ruc/hr.js @@ -41,7 +41,7 @@ module.exports = async (ctx) => { item.description = content('.neirong').html(); item.pubDate = parseDate(detailResponse.data.match(/日期:(\d{4}-\d{2}-\d{2})/)[1]); - } catch (err) { + } catch { item.description = 'Not Found'; } diff --git a/lib/v2/runtrail/maintainer.js b/lib/v2/runtrail/maintainer.js index 13972819d7b162..83fc5ed48db85a 100644 --- a/lib/v2/runtrail/maintainer.js +++ b/lib/v2/runtrail/maintainer.js @@ -1,4 +1,3 @@ module.exports = { - '': ['TonyRL'], '/': ['TonyRL'], }; diff --git a/lib/v2/runtrail/posts.js b/lib/v2/runtrail/posts.js index a5486e8669aed6..0ed141ec54eb3d 100644 --- a/lib/v2/runtrail/posts.js +++ b/lib/v2/runtrail/posts.js @@ -6,14 +6,14 @@ module.exports = async (ctx) => { const { data: response } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { searchParams: { _embed: true, - per_page: ctx.query.limit ? parseInt(ctx.query.limit) : 30, + per_page: ctx.query.limit ? Number.parseInt(ctx.query.limit) : 30, }, }); let data = response; if (typeof response !== 'object') { // remove php warnings before JSON - data = JSON.parse(response.match(/\[(.*)\]/)[0]); + data = JSON.parse(response.match(/\[(.*)]/)[0]); } const items = data.map((item) => ({ diff --git a/lib/v2/sakurazaka46/news.js b/lib/v2/sakurazaka46/news.js index f67cad15da3ace..04edf4c1e702a6 100644 --- a/lib/v2/sakurazaka46/news.js +++ b/lib/v2/sakurazaka46/news.js @@ -14,7 +14,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('.com-news-part .box a') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 30) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 30) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/saraba1st/digest.js b/lib/v2/saraba1st/digest.js index 07e4f3ab581bfe..89ee1182b1881a 100644 --- a/lib/v2/saraba1st/digest.js +++ b/lib/v2/saraba1st/digest.js @@ -8,7 +8,7 @@ const path = require('path'); module.exports = async (ctx) => { const tid = ctx.params.tid; - const cookieString = config.saraba1st.cookie ? config.saraba1st.cookie : ''; + const cookieString = config.saraba1st.cookie ?? ''; const res = await got('https://bbs.saraba1st.com/2b/' + tid + '.html', { headers: { Cookie: cookieString, @@ -51,7 +51,7 @@ module.exports = async (ctx) => { async function fetchContent(url) { // Fetch the subpageinof - const cookieString = config.saraba1st.cookie ? config.saraba1st.cookie : ''; + const cookieString = config.saraba1st.cookie ?? ''; const subres = await got(url, { headers: { Cookie: cookieString, @@ -75,5 +75,17 @@ async function fetchContent(url) { } }); + stubS.find('img').each(function () { + const img = subind(this); + const file = img.attr('file'); + if (file) { + img.attr('src', file); + img.removeAttr('zoomfile'); + img.removeAttr('file'); + img.removeAttr('onmouseover'); + img.removeAttr('onclick'); + } + }); + return stubS.html(); } diff --git a/lib/v2/saraba1st/thread.js b/lib/v2/saraba1st/thread.js index a7ae2875c9deae..aea4c8261e92aa 100644 --- a/lib/v2/saraba1st/thread.js +++ b/lib/v2/saraba1st/thread.js @@ -7,7 +7,7 @@ const timezone = require('@/utils/timezone'); module.exports = async (ctx) => { const tid = ctx.params.tid; - const cookieString = config.saraba1st.cookie ? config.saraba1st.cookie : ''; + const cookieString = config.saraba1st.cookie ?? ''; const res = await got('https://bbs.saraba1st.com/2b/forum.php', { searchParams: queryString.stringify({ @@ -35,10 +35,10 @@ module.exports = async (ctx) => { const floorUrl = each.find('td.plc .pi a').attr('href'); const contentHtml = $(each.find('td.t_f')); const imgsHtml = contentHtml.find('img'); - for (let i = 0; i < imgsHtml.length; i++) { - if (imgsHtml[i].attribs.src === 'https://static.saraba1st.com/image/common/none.gif') { - imgsHtml[i].attribs.src = imgsHtml[i].attribs.file; - const imgHtml = $(imgsHtml[i]); + for (const element of imgsHtml) { + if (element.attribs.src === 'https://static.saraba1st.com/image/common/none.gif') { + element.attribs.src = element.attribs.file; + const imgHtml = $(element); imgHtml.removeAttr('zoomfile'); imgHtml.removeAttr('file'); imgHtml.removeAttr('onmouseover'); diff --git a/lib/v2/sass/gs/index.js b/lib/v2/sass/gs/index.js index 522294dbc3b82e..f74406b0047cc2 100644 --- a/lib/v2/sass/gs/index.js +++ b/lib/v2/sass/gs/index.js @@ -17,22 +17,13 @@ module.exports = async (ctx) => { const itemDate = $(time).text(); const { href: path, title: itemTitle } = titleLink.children[0].attribs; - let itemUrl = ''; - if (path.startsWith('http')) { - itemUrl = path; - } else { - itemUrl = host + path; - } + const itemUrl = path.startsWith('http') ? path : host + path; return ctx.cache.tryGet(itemUrl, async () => { let description = ''; if (itemUrl) { const result = await got(itemUrl); const $ = cheerio.load(result.data); - if ($('.read .wp_articlecontent').length) { - description = $('.read .wp_articlecontent').html().trim(); - } else { - description = itemTitle; - } + description = $('.read .wp_articlecontent').length ? $('.read .wp_articlecontent').html().trim() : itemTitle; } else { description = itemTitle; } diff --git a/lib/v2/scau/maintainer.js b/lib/v2/scau/maintainer.js index 69798711be8e6e..29d91e0f7f9627 100644 --- a/lib/v2/scau/maintainer.js +++ b/lib/v2/scau/maintainer.js @@ -1,3 +1,4 @@ module.exports = { '/yzb': ['shengmaosu'], + '/yjsy': ['Chunssu'], }; diff --git a/lib/v2/scau/radar.js b/lib/v2/scau/radar.js index 8f63d388cb44c7..45aab7c7b1130b 100644 --- a/lib/v2/scau/radar.js +++ b/lib/v2/scau/radar.js @@ -9,5 +9,13 @@ module.exports = { target: '/scau/yzb', }, ], + yjsy: [ + { + title: '研究生院通知', + docs: 'https://docs.rsshub.app/routes/university#hua-nan-nong-ye-da-xue', + source: ['/208/list.htm', '/'], + target: '/scau/yjsy', + }, + ], }, }; diff --git a/lib/v2/scau/router.js b/lib/v2/scau/router.js index a282d01e1c8827..c3908d9de70082 100644 --- a/lib/v2/scau/router.js +++ b/lib/v2/scau/router.js @@ -1,3 +1,4 @@ module.exports = (router) => { router.get('/yzb', require('./yjs')); + router.get('/yjsy', require('./yjsy')); }; diff --git a/lib/v2/scau/yjs.js b/lib/v2/scau/yjs.js index 8f33faf4b92997..343cd4b09a47a4 100644 --- a/lib/v2/scau/yjs.js +++ b/lib/v2/scau/yjs.js @@ -9,9 +9,9 @@ module.exports = async (ctx) => { const list = $('#wp_news_w25 tr'); ctx.state.data = { - title: '华南农业大学研究生院', + title: '华南农业大学研招办', link, - description: '华南农业大学研究生院通知公告', + description: '华农研讯', item: list && list.toArray().map((item) => { diff --git a/lib/v2/scau/yjsy.js b/lib/v2/scau/yjsy.js new file mode 100644 index 00000000000000..1e0259a2e952d3 --- /dev/null +++ b/lib/v2/scau/yjsy.js @@ -0,0 +1,27 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const link = 'https://yjsy.scau.edu.cn/208/list1.htm'; + const response = await got(link); + const $ = cheerio.load(response.data); + const list = $('#wp_news_w25 tr td tr'); + + ctx.state.data = { + title: '华南农业大学研究生院', + link, + description: '通知公告', + item: + list && + list.toArray().map((item) => { + item = $(item); + const a = item.find('a').last(); + return { + title: a.text(), + link: a.attr('href'), + pubDate: parseDate(item.find('td').eq(2).text(), 'YYYY/MM/DD'), + }; + }), + }; +}; diff --git a/lib/v2/science/utils.js b/lib/v2/science/utils.js index 27eb2418ca0d18..42a4f3fc1d6f94 100644 --- a/lib/v2/science/utils.js +++ b/lib/v2/science/utils.js @@ -27,10 +27,10 @@ const fetchDesc = (list, browser, tryGet) => const content = $('.news-article-content--featured').length ? $('.news-article-content--featured').html() : $('.news-article-content').length - ? $('.news-article-content').html() - : $('.info-panel__formats a.btn__request-access').length || $('.info-panel__formats a.btn--access').length - ? '' - : $('section#bodymatter').html(); + ? $('.news-article-content').html() + : $('.info-panel__formats a.btn__request-access').length || $('.info-panel__formats a.btn--access').length + ? '' + : $('section#bodymatter').html(); item.description = art(path.join(__dirname, 'templates/article.art'), { abs: abstract, diff --git a/lib/v2/sciencenet/blog.js b/lib/v2/sciencenet/blog.js index d2a977cad01f88..a08d158335b5ae 100644 --- a/lib/v2/sciencenet/blog.js +++ b/lib/v2/sciencenet/blog.js @@ -21,7 +21,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(iconv.decode(response.data, 'gbk')); let items = $('tr td a[title]') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/sciencenet/user.js b/lib/v2/sciencenet/user.js index 877fc7092395b2..07749e3e773449 100644 --- a/lib/v2/sciencenet/user.js +++ b/lib/v2/sciencenet/user.js @@ -26,7 +26,7 @@ module.exports = async (ctx) => { $ = cheerio.load(response.data); let items = $('item') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/scmp/index.js b/lib/v2/scmp/index.js index 574acea8b5a50a..362408f5c41702 100644 --- a/lib/v2/scmp/index.js +++ b/lib/v2/scmp/index.js @@ -1,114 +1,61 @@ -const parser = require('@/utils/rss-parser'); const cheerio = require('cheerio'); const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); -const chromeMobileUserAgent = require('@/utils/rand-user-agent')({ browser: 'chrome', os: 'android', device: 'mobile' }); +const { parseItem } = require('./utils'); module.exports = async (ctx) => { const categoryId = ctx.params.category_id; const rssUrl = `https://www.scmp.com/rss/${categoryId}/feed`; - const feed = await parser.parseURL(rssUrl); - - const items = await Promise.all( - feed.items.map((item) => - ctx.cache.tryGet(item.link, async () => { - // Fetch the AMP version - const url = item.link.replace(/^https:\/\/www\.scmp\.com/, 'https://amp.scmp.com'); - const response = await got(url, { - headers: { - 'User-Agent': chromeMobileUserAgent, - }, - }); - const html = response.data; - const $ = cheerio.load(html); - const content = $('div.article-body.clearfix'); - - // Cover - const cover = $('.article-images > amp-carousel > .i-amphtml-slides-container >.i-amphtml-slide-item > amp-img > img'); - - if (cover.length > 0) { - $(``).insertBefore(content[0].childNodes[0]); - $(cover).remove(); - } - - // Summary - const summary = $('div.article-header__subhead > ul'); - - // Metadata (categories & updatedAt) - const updatedAt = $('meta[itemprop="dateModified"]').attr('content'); - const publishedAt = item.pubDate || $('meta[itemprop="datePublished"]').attr('content'); - - const categories = $('meta[name="keywords"]') - .attr('content') - .split(',') - .map((c) => c.trim()); - - // Images - content.find('amp-img').each((i, e) => { - const img = $(`${e.attribs.alt}`); - - // Caption follows, no need to handle caption - $(img).insertBefore(e); - $(e).remove(); - }); - - // iframes (youtube videos and interactive elements) - content.find('amp-iframe').each((i, e) => { - if ($(e).find('iframe').length > 0) { - const iframe = $(e).find('iframe')[0]; - $(iframe).insertBefore(e); - $(e).remove(); - } - }); - - content.find('div.video-wrapper > amp-iframe').each((i, e) => { - const iframe = $(``; + case 'leading': + case 'img': + return `
`${key}="${node.attribs[key]}"`) + .join(' ') + : `url="${node.url}"` // for leading + }>
${node.attribs?.title ?? node.title}
`; + case 'em': + case 'h3': + case 'li': + case 'ol': + case 'ul': + case 'p': + case 'strong': + case 'u': + return `<${node.type}>${renderHTML(node.children)}`; + case 'text': + return node.data; + case 'script': + case 'inline-ad-slot': + case 'inline-widget': + case 'track-viewed-percentage': + return ''; + default: + return `Unhandled type: ${node.type} ${JSON.stringify(node)}`; + } +}; + +const parseItem = async (item) => { + const { data: response, url } = await got(item.link); + + if (new URL(url).hostname !== 'www.scmp.com') { + // e.g., https://multimedia.scmp.com/ + return item; + } + + const $ = cheerio.load(response); + + const nextData = JSON.parse($('script#__NEXT_DATA__').text()); + const { article } = nextData.props.pageProps.payload.data; + + // item.nextData = article; + + item.summary = renderHTML(article.summary.json); + item.description = renderHTML(article.subHeadline.json) + renderHTML(article.images.find((i) => i.type === 'leading')) + renderHTML(article.body.json); + item.updated = parseDate(article.updatedDate, 'x'); + item.category = [...new Set([...article.topics.map((t) => t.name), ...article.sections.flatMap((t) => t.value.map((v) => v.name)), ...article.keywords.map((k) => k?.split(', '))])]; + + // N.B. gallery in article is not rendered + // e.g., { type: 'div', attribs: { class: 'scmp-photo-gallery', 'data-gallery-nid': '3239409' }} + // from https://www.scmp.com/news/china/politics/article/3239355/li-keqiang-former-premier-china-dead + + return item; +}; + +module.exports = { + renderHTML, + parseItem, +}; diff --git a/lib/v2/scnu/physics_school_announcements_and_news.js b/lib/v2/scnu/physics-school-announcements-and-news.js similarity index 100% rename from lib/v2/scnu/physics_school_announcements_and_news.js rename to lib/v2/scnu/physics-school-announcements-and-news.js diff --git a/lib/v2/scnu/router.js b/lib/v2/scnu/router.js index f2307ab2d991eb..cf7f5036568477 100644 --- a/lib/v2/scnu/router.js +++ b/lib/v2/scnu/router.js @@ -2,9 +2,9 @@ module.exports = (router) => { router.get('/cs/match', require('./cs/match')); router.get('/jw', require('./jw')); router.get('/library', require('./library')); - router.get('/physics-school-announcements', require('./physics_school_announcements_and_news').announcementsRouter); - router.get('/physics-school-news', require('./physics_school_announcements_and_news').newsRouter); - router.get('/physics-school-research-news', require('./physics_school_announcements_and_news').researchNewsRouter); + router.get('/physics-school-announcements', require('./physics-school-announcements-and-news').announcementsRouter); + router.get('/physics-school-news', require('./physics-school-announcements-and-news').newsRouter); + router.get('/physics-school-research-news', require('./physics-school-announcements-and-news').researchNewsRouter); router.get('/ss', require('./ss')); router.get('/yjs', require('./yjs')); }; diff --git a/lib/v2/sctv/programme.js b/lib/v2/sctv/programme.js index 1a48c4896e1040..0cb75e05d444c2 100644 --- a/lib/v2/sctv/programme.js +++ b/lib/v2/sctv/programme.js @@ -6,7 +6,7 @@ const path = require('path'); module.exports = async (ctx) => { const id = ctx.params.id ?? '1'; - const limit = ctx.params.limit ? parseInt(ctx.params.limit) : 15; + const limit = ctx.params.limit ? Number.parseInt(ctx.params.limit) : 15; const isFull = /t|y/i.test(ctx.params.isFull ?? 'true'); const rootUrl = 'https://www.sctv.com'; @@ -49,7 +49,7 @@ module.exports = async (ctx) => { let currentFullItems = []; if (isFull) { - currentFullItems = currentItems.filter((item) => /(\d{4}\.\d{2}\.\d{2})/.test(item.title)); + currentFullItems = currentItems.filter((item) => /(\d{4}(?:\.\d{2}){2})/.test(item.title)); } items = [...items, ...(currentFullItems.length === 0 ? currentItems : currentFullItems)]; @@ -74,7 +74,7 @@ module.exports = async (ctx) => { ctx.state.data = { title: `四川广播电视台 - ${name}`, link: currentUrl, - item: items.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 100), + item: items.slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 100), image: cover, }; }; diff --git a/lib/v2/scut/jwc/news.js b/lib/v2/scut/jwc/news.js index 1972e132d93761..0e85036778d04d 100644 --- a/lib/v2/scut/jwc/news.js +++ b/lib/v2/scut/jwc/news.js @@ -31,21 +31,15 @@ const generateArticlePubDate = (createDateStr) => { const isRedirectPage = (data) => !!data.link; -const resolveRelativeUrl = (html) => html.replace(/src="\//g, `src="${url.resolve(baseUrl, '.')}`).replace(/href="\//g, `href="${url.resolve(baseUrl, '.')}`); +const resolveRelativeUrl = (html) => html.replaceAll('src="/', `src="${url.resolve(baseUrl, '.')}`).replaceAll('href="/', `href="${url.resolve(baseUrl, '.')}`); const apiSuccessAssert = (data) => { if (!data.success) { - throw Error('article api error'); + throw new Error('article api error'); } }; -const generateBannerImgHtml = (bannerImageUrl) => { - if (bannerImageUrl) { - return `

`; - } else { - return ''; - } -}; +const generateBannerImgHtml = (bannerImageUrl) => (bannerImageUrl ? `

` : ''); const generateArticleLink = (id) => `

链接:电脑版 | 手机版

`; @@ -73,7 +67,7 @@ module.exports = async (ctx) => { const cache = await ctx.cache.get(articleUrl); if (cache) { - return Promise.resolve(JSON.parse(cache)); + return JSON.parse(cache); } const qs = querystring.stringify({ diff --git a/lib/v2/scut/jwc/notice.js b/lib/v2/scut/jwc/notice.js index c71a12d6589d0a..b5584784f51fa0 100644 --- a/lib/v2/scut/jwc/notice.js +++ b/lib/v2/scut/jwc/notice.js @@ -41,11 +41,11 @@ const generateArticlePubDate = (createDateStr) => { const isRedirectPage = (data) => !!data.link; -const resolveRelativeUrl = (html) => html.replace(/src="\//g, `src="${url.resolve(baseUrl, '.')}`).replace(/href="\//g, `href="${url.resolve(baseUrl, '.')}`); +const resolveRelativeUrl = (html) => html.replaceAll('src="/', `src="${url.resolve(baseUrl, '.')}`).replaceAll('href="/', `href="${url.resolve(baseUrl, '.')}`); const apiSuccessAssert = (data) => { if (!data.success) { - throw Error('article api error'); + throw new Error('article api error'); } }; @@ -79,7 +79,7 @@ module.exports = async (ctx) => { const cache = await ctx.cache.get(articleUrl); if (cache) { - return Promise.resolve(JSON.parse(cache)); + return JSON.parse(cache); } const qs = querystring.stringify({ diff --git a/lib/v2/scut/jwc/school.js b/lib/v2/scut/jwc/school.js index 0075a528b9981d..141fe1d7c8067b 100644 --- a/lib/v2/scut/jwc/school.js +++ b/lib/v2/scut/jwc/school.js @@ -38,11 +38,11 @@ const generateArticlePubDate = (createDateStr) => { const isRedirectPage = (data) => !!data.link; -const resolveRelativeUrl = (html) => html.replace(/src="\//g, `src="${url.resolve(baseUrl, '.')}`).replace(/href="\//g, `href="${url.resolve(baseUrl, '.')}`); +const resolveRelativeUrl = (html) => html.replaceAll('src="/', `src="${url.resolve(baseUrl, '.')}`).replaceAll('href="/', `href="${url.resolve(baseUrl, '.')}`); const apiSuccessAssert = (data) => { if (!data.success) { - throw Error('article api error'); + throw new Error('article api error'); } }; @@ -77,7 +77,7 @@ module.exports = async (ctx) => { const cache = await ctx.cache.get(articleUrl); if (cache) { - return Promise.resolve(JSON.parse(cache)); + return JSON.parse(cache); } const qs = querystring.stringify({ diff --git a/lib/v2/scut/router.js b/lib/v2/scut/router.js index 171153d5ec2aa5..e15b832024bbbe 100644 --- a/lib/v2/scut/router.js +++ b/lib/v2/scut/router.js @@ -3,7 +3,7 @@ module.exports = (router) => { router.get('/jwc/notice/:category?', require('./jwc/notice')); router.get('/jwc/school/:category?', require('./jwc/school')); router.get('/scet/notice', require('./scet/notice')); - router.get('/seie/news_center', require('./seie/news_center')); + router.get('/seie/news_center', require('./seie/news-ccenter')); router.get('/smae/:category?', require('./smae/notice')); router.get('/yjs', require('./yjs')); }; diff --git a/lib/v2/scut/seie/news_center.js b/lib/v2/scut/seie/news-ccenter.js similarity index 95% rename from lib/v2/scut/seie/news_center.js rename to lib/v2/scut/seie/news-ccenter.js index 8228458c0b91bf..f042d0000cf250 100644 --- a/lib/v2/scut/seie/news_center.js +++ b/lib/v2/scut/seie/news-ccenter.js @@ -38,7 +38,7 @@ module.exports = async (ctx) => { }); const contentHTML = content('.wp_articlecontent').html(); - item.description = contentHTML.replace(/^(
)+|(
)+$/g, '').trim(); + item.description = contentHTML.replaceAll(/^(
)+|(
)+$/g, '').trim(); return item; }) ) diff --git a/lib/v2/sdu/cmse.js b/lib/v2/sdu/cmse.js index 98bb737db30001..957b6afd688f67 100644 --- a/lib/v2/sdu/cmse.js +++ b/lib/v2/sdu/cmse.js @@ -7,7 +7,7 @@ const typelist = ['通知公告', '学院新闻', '本科生教育', '研究生 const urlList = ['zxzx/tzgg.htm', 'zxzx/xyxw.htm', 'zxzx/bksjy.htm', 'zxzx/yjsjy.htm', 'zxzx/xsdt.htm']; module.exports = async (ctx) => { - const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const type = ctx.params.type ? Number.parseInt(ctx.params.type) : 0; const link = new URL(urlList[type], host).href; const response = await got(link); diff --git a/lib/v2/sdu/cs.js b/lib/v2/sdu/cs.js index dac8f3a10e7929..a8bcf54e64e9e4 100644 --- a/lib/v2/sdu/cs.js +++ b/lib/v2/sdu/cs.js @@ -8,7 +8,7 @@ const typelist = ['学院公告', '学术报告', '科技简讯']; const urlList = ['xygg.htm', 'xsbg.htm', 'kjjx.htm']; module.exports = async (ctx) => { - const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const type = ctx.params.type ? Number.parseInt(ctx.params.type) : 0; const link = new URL(urlList[type], host).href; const response = await got(link); diff --git a/lib/v2/sdu/epe.js b/lib/v2/sdu/epe.js index f77a03ef4b8473..e71d37550325ac 100644 --- a/lib/v2/sdu/epe.js +++ b/lib/v2/sdu/epe.js @@ -8,7 +8,7 @@ const typelist = ['学院动态', '通知公告', '学术论坛']; const urlList = ['zxzx/xydt.htm', 'zxzx/tzgg.htm', 'zxzx/xslt.htm']; module.exports = async (ctx) => { - const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const type = ctx.params.type ? Number.parseInt(ctx.params.type) : 0; const link = new URL(urlList[type], host).href; const response = await got(link); diff --git a/lib/v2/sdu/extractor/sdrj.js b/lib/v2/sdu/extractor/sdrj.js index 49be530f8cb034..42adf19cf3212c 100644 --- a/lib/v2/sdu/extractor/sdrj.js +++ b/lib/v2/sdu/extractor/sdrj.js @@ -15,7 +15,7 @@ module.exports = (link, ctx) => const exactDateText = exactDateLine.match(/^发布时间:(?\d+\/\d+\/\d+\s\d{2}:\d{2}:\d{2})/).groups.date; exactDate = timezone(parseDate(exactDateText, 'YYYY/MM/DD HH:mm:ss'), +8); return { description: content, author, exactDate }; - } catch (e) { + } catch { return { description: content, author, exactDate }; } }); diff --git a/lib/v2/sdu/extractor/view.js b/lib/v2/sdu/extractor/view.js index f28e94a50ad7c3..ff5e39b9214de4 100644 --- a/lib/v2/sdu/extractor/view.js +++ b/lib/v2/sdu/extractor/view.js @@ -15,7 +15,7 @@ module.exports = (link, ctx) => const exactDateText = exactDateLine.match(/^发布日期:(?\d+年\d+月\d+日\s\d{2}:\d{2})/).groups.date; exactDate = timezone(parseDate(exactDateText, 'YYYY年MM月DD日 HH:mm'), +8); return { description: content, author, exactDate }; - } catch (e) { + } catch { return { description: content, author, exactDate }; } }); diff --git a/lib/v2/sdu/extractor/wh/jwc.js b/lib/v2/sdu/extractor/wh/jwc.js index fd9ad235b0cc28..ede8c49ada7dca 100644 --- a/lib/v2/sdu/extractor/wh/jwc.js +++ b/lib/v2/sdu/extractor/wh/jwc.js @@ -18,7 +18,7 @@ module.exports = (link, ctx) => const exactDateText = exactDateStr.match(/^创建时间:(?\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})/).groups.date; exactDate = timezone(parseDate(exactDateText, 'YYYY-MM-DD HH:mm:ss'), +8); return { description: content, exactDate }; - } catch (e) { + } catch { return { description: content, exactDate }; } }); diff --git a/lib/v2/sdu/extractor/wh/news.js b/lib/v2/sdu/extractor/wh/news.js index 18ed5f9915fa45..c5ff784a70b91a 100644 --- a/lib/v2/sdu/extractor/wh/news.js +++ b/lib/v2/sdu/extractor/wh/news.js @@ -15,7 +15,7 @@ module.exports = (link, ctx) => const exactDateText = exactDateLine.match(/^发布日期:(?\d+年\d+月\d+日\s\d{2}:\d{2})/).groups.date; exactDate = timezone(parseDate(exactDateText, 'YYYY年MM月DD日 HH:mm'), +8); return { description: content, author, exactDate }; - } catch (e) { + } catch { return { description: content, author, exactDate }; } }); diff --git a/lib/v2/sdu/mech.js b/lib/v2/sdu/mech.js index 428b7422fbd915..93c2cdf6218731 100644 --- a/lib/v2/sdu/mech.js +++ b/lib/v2/sdu/mech.js @@ -8,7 +8,7 @@ const urlList = ['xwdt/tzgg.htm', 'xwdt/ysxw.htm', 'xwdt/jxxx.htm', 'xwdt/xsdt.h const host = 'https://www.mech.sdu.edu.cn/'; module.exports = async (ctx) => { - const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const type = ctx.params.type ? Number.parseInt(ctx.params.type) : 0; const link = new URL(urlList[type], host).href; const response = await got(link); diff --git a/lib/v2/sdu/sc.js b/lib/v2/sdu/sc.js index 89cd9f2b8d08c0..9fe5c26df4f727 100644 --- a/lib/v2/sdu/sc.js +++ b/lib/v2/sdu/sc.js @@ -7,7 +7,7 @@ const typelist = ['通知公告', '学术动态', '本科教育', '研究生教 const urlList = ['tzgg.htm', 'kxyj/xsyg.htm', 'rcpy/bkjy.htm', 'rcpy/yjsjy.htm']; module.exports = async (ctx) => { - const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const type = ctx.params.type ? Number.parseInt(ctx.params.type) : 0; const link = new URL(urlList[type], host).href; const response = await got(link); @@ -44,7 +44,7 @@ module.exports = async (ctx) => { item.description = $('.content').html(); return item; - } catch (e) { + } catch { // intranet oa.sdu.edu.cn return item; } diff --git a/lib/v2/sdu/wh/jwc.js b/lib/v2/sdu/wh/jwc.js index a6047a4e8a4e60..e664d38f07516c 100644 --- a/lib/v2/sdu/wh/jwc.js +++ b/lib/v2/sdu/wh/jwc.js @@ -23,7 +23,7 @@ module.exports = async (ctx) => { const title = item.text(); const { description, author: exactAuthor, exactDate } = await ctx.cache.tryGet(link, () => extractor(link, ctx)); const author = exactAuthor ?? '教务处'; - const pubDate = exactDate ?? timezone(parseDate(dateText.slice(1, dateText.length - 1), 'YYYY-MM-DD'), +8); + const pubDate = exactDate ?? timezone(parseDate(dateText.slice(1, -1), 'YYYY-MM-DD'), +8); return { title, link, diff --git a/lib/v2/sec-wiki/weekly.js b/lib/v2/sec-wiki/weekly.js index bc064591273f3c..adc010539074dd 100644 --- a/lib/v2/sec-wiki/weekly.js +++ b/lib/v2/sec-wiki/weekly.js @@ -3,7 +3,7 @@ const got = require('@/utils/got'); module.exports = async (ctx) => { const { data } = await got('https://www.sec-wiki.com/weekly/index'); - const items = Array.from(data.matchAll(/\/weekly\/(\d+)">(.+?)<\/a><\/h5>\s*

(.+?)<\/p>/g)).map((item) => ({ + const items = [...data.matchAll(/\/weekly\/(\d+)">(.+?)<\/a><\/h5>\s*

(.+?)<\/p>/g)].map((item) => ({ title: item[2], link: `https://www.sec-wiki.com/weekly/${item[1]}`, description: item[3], diff --git a/lib/v2/secnews/index.js b/lib/v2/secnews/index.js deleted file mode 100644 index c131aa35ad68a4..00000000000000 --- a/lib/v2/secnews/index.js +++ /dev/null @@ -1,27 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const url = 'http://wiki.ioin.in/'; - const response = await got.get(String(url)); - const $ = cheerio.load(response.data); - const list = $('table>tbody>tr').get(); - const items = list.map((i) => { - const item = $(i); - const title = item.find('td:nth-child(2) a').text(); - const pla = item.find('.vul-type-item').text().replace(/\s+/g, ''); - const date = new Date(item.find('td:first-child').text().replace(/\s+/g, '')).toUTCString(); - const href = item.find('td:nth-child(2) a').attr('href'); - return { - title: `${title} ${pla !== '未知' ? pla : ''}`, - pubDate: date, - link: `${url}${href}`, - }; - }); - - ctx.state.data = { - title: 'sec-news', - link: 'http://wiki.ioin.in/', - item: items, - }; -}; diff --git a/lib/v2/secnews/maintainer.js b/lib/v2/secnews/maintainer.js deleted file mode 100644 index 8fd867f26c330b..00000000000000 --- a/lib/v2/secnews/maintainer.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '/index': ['kaiili'], -}; diff --git a/lib/v2/secnews/radar.js b/lib/v2/secnews/radar.js deleted file mode 100644 index 16f792012fe10f..00000000000000 --- a/lib/v2/secnews/radar.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - 'furstar.jp': { - _name: 'Furstar', - '.': [ - { - title: '安全文摘首頁', - docs: 'https://docs.rsshub.app/routes/shopping#an-quan-wen-zhai', - source: ['/', '/'], - target: '/secnews/index', - }, - ], - }, -}; diff --git a/lib/v2/secrss/author.js b/lib/v2/secrss/author.js index 02667778e84a49..ee221ff0c4da29 100644 --- a/lib/v2/secrss/author.js +++ b/lib/v2/secrss/author.js @@ -1,5 +1,6 @@ const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); +const cheerio = require('cheerio'); module.exports = async (ctx) => { const api = 'https://www.secrss.com/api/articles/group?author='; @@ -8,13 +9,23 @@ module.exports = async (ctx) => { const res = await got(`${api}${author}`); const dataArray = res.data.data.list; - const items = dataArray.map((item) => ({ - title: item.title, - description: item.summary, - pubDate: parseDate(item.original_timestamp, 'X'), - link: `${host}${item.url}`, - category: item.tag, - })); + const items = await Promise.all( + dataArray.map((item) => { + const itemUrl = `${host}${item.url}`; + return ctx.cache.tryGet(itemUrl, async () => { + const result = await got(itemUrl); + const $ = cheerio.load(result.data); + const description = $('.article-body').html().trim(); + return { + title: item.title, + link: itemUrl, + pubDate: parseDate(item.original_timestamp, 'X'), + description, + category: item.tag, + }; + }); + }) + ); ctx.state.data = { title: `安全内参-${author}`, diff --git a/lib/v2/secrss/category.js b/lib/v2/secrss/category.js index b2ded630dc2a5b..212c43268dcb45 100644 --- a/lib/v2/secrss/category.js +++ b/lib/v2/secrss/category.js @@ -1,6 +1,7 @@ const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); const timezone = require('@/utils/timezone'); +const cheerio = require('cheerio'); module.exports = async (ctx) => { const api = 'https://www.secrss.com/api/articles?tag='; @@ -9,14 +10,24 @@ module.exports = async (ctx) => { const res = await got(`${api}${category}`); const dataArray = res.data.data; - const items = dataArray.map((item) => ({ - title: item.title, - description: item.summary, - pubDate: timezone(parseDate(item.published_at), +8), - link: `${host}/articles/${item.id}`, - author: item.source_author, - category: item.tags.map((t) => t.title), - })); + const items = await Promise.all( + dataArray.map((item) => { + const itemUrl = `${host}/articles/${item.id}`; + return ctx.cache.tryGet(itemUrl, async () => { + const result = await got(itemUrl); + const $ = cheerio.load(result.data); + const description = $('.article-body').html().trim(); + return { + title: item.title, + link: itemUrl, + pubDate: timezone(parseDate(item.published_at), 8), + description, + author: item.source_author, + category: item.tags.map((t) => t.title), + }; + }); + }) + ); ctx.state.data = { title: `安全内参-${category}`, diff --git a/lib/v2/secrss/maintainer.js b/lib/v2/secrss/maintainer.js index d7a31b968ae3d4..4d0bb90ed92a6a 100644 --- a/lib/v2/secrss/maintainer.js +++ b/lib/v2/secrss/maintainer.js @@ -1,4 +1,4 @@ module.exports = { - '/author/:author': ['XinRoom'], - '/category/:category?': ['XinRoom'], + '/author/:author': ['XinRoom', 'SunBK201'], + '/category/:category?': ['XinRoom', 'SunBK201'], }; diff --git a/lib/v2/seekingalpha/radar.js b/lib/v2/seekingalpha/radar.js index 428ca28de794b9..35bb31332e7d64 100644 --- a/lib/v2/seekingalpha/radar.js +++ b/lib/v2/seekingalpha/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'Summary', - docs: 'https://docs.rsshub.app/routes/en/finance#seeking-alpha', + docs: 'https://docs.rsshub.app/routes/finance#seeking-alpha', source: ['/symbol/:symbol/:category', '/symbol/:symbol/earnings/:category'], target: '/seekingalpha/:symbol/:category', }, diff --git a/lib/v2/segmentfault/blogs.js b/lib/v2/segmentfault/blogs.js index 436d040c575f82..0ab77575c44167 100644 --- a/lib/v2/segmentfault/blogs.js +++ b/lib/v2/segmentfault/blogs.js @@ -1,34 +1,17 @@ const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); - -const host = 'https://segmentfault.com'; +const { host, acw_sc__v2, parseList, parseItems } = require('./utils'); module.exports = async (ctx) => { - const tag = ctx.params.tag; - const apiURL = `https://segmentfault.com/gateway/tag/${tag}/articles?loadMoreType=pagination&initData=true&page=1&sort=newest&pageSize=30`; + const { tag } = ctx.params; + const apiURL = `${host}/gateway/tag/${tag}/articles?loadMoreType=pagination&initData=true&page=1&sort=newest&pageSize=30`; const response = await got(apiURL); const data = response.data.rows; - const list = data.map((item) => ({ - title: item.title, - link: new URL(item.url, host).href, - author: item.user.name, - })); - - const items = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.link, async () => { - const response = await got(item.link); - const content = cheerio.load(response.data); + const list = parseList(data); - item.description = content('article').html(); - item.pubDate = parseDate(content('time').attr('datetime')); + const acwScV2Cookie = await acw_sc__v2(list[0].link, ctx.cache.tryGet); - return item; - }) - ) - ); + const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, ctx.cache.tryGet))); ctx.state.data = { title: `segmentfault-Blogs-${tag}`, diff --git a/lib/v2/segmentfault/channel.js b/lib/v2/segmentfault/channel.js index 5360159045e63d..00d5306043c3be 100644 --- a/lib/v2/segmentfault/channel.js +++ b/lib/v2/segmentfault/channel.js @@ -1,45 +1,33 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); - -const host = 'https://segmentfault.com'; +const { host, acw_sc__v2, parseList, parseItems } = require('./utils'); module.exports = async (ctx) => { - const name = ctx.params.name; + const { name } = ctx.params; const link = `${host}/channel/${name}`; - const response = await got(link); - const $ = cheerio.load(response.data); - - const channel_name = $('#leftNav > a.active').text(); + const { data: pageResponse } = await got(link); + const { data: apiResponse } = await got(`${host}/gateway/articles`, { + searchParams: { + query: 'channel', + slug: name, + offset: 0, + size: ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 20, + mode: 'scrollLoad', + }, + }); - const list = $('ul.bg-transparent.list-group.list-group-flush > li') - .slice(0, 10) - .map((_, item) => ({ - link: new URL($(item).find('div.content > h3.h5 > a').attr('href'), host).href, - title: $(item).find('div.content > h3.h5 > a').text(), - author: $(item).find('span.name').text(), - })) - .get(); + const $ = cheerio.load(pageResponse); + const channelName = $('#leftNav > a.active').text(); - const items = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got(item.link); - const content = cheerio.load(detailResponse.data); + const list = parseList(apiResponse.rows); - item.description = content('article') - .html() - .replace(/data-src="/g, `src="${host}`); - item.pubDate = parseDate(content('time').attr('datetime')); + const acwScV2Cookie = await acw_sc__v2(list[0].link, ctx.cache.tryGet); - return item; - }) - ) - ); + const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, ctx.cache.tryGet))); ctx.state.data = { - title: `segmentfault - ${channel_name}`, + title: `segmentfault - ${channelName}`, link, item: items, }; diff --git a/lib/v2/segmentfault/user.js b/lib/v2/segmentfault/user.js index 2ea5510c0d6efe..636d374981e727 100644 --- a/lib/v2/segmentfault/user.js +++ b/lib/v2/segmentfault/user.js @@ -1,45 +1,19 @@ const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); -const { acw_sc__v2 } = require('./utils'); -const host = 'https://segmentfault.com'; +const { host, acw_sc__v2, parseList, parseItems } = require('./utils'); module.exports = async (ctx) => { - const name = ctx.params.name; - const apiURL = `https://segmentfault.com/gateway/homepage/${name}/timeline?size=20&offset=`; + const { name } = ctx.params; + const apiURL = `${host}/gateway/homepage/${name}/timeline?size=20&offset=`; const response = await got(apiURL); const data = response.data.rows; - const author = data[0].user.name; - const list = data.map((item) => ({ - title: item.title, - description: item.excerpt, - link: new URL(item.url, host).href, - author, - })); + const list = parseList(data); + const { author } = list[0]; const acwScV2Cookie = await acw_sc__v2(list[0].link, ctx.cache.tryGet); - const items = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.link, async () => { - const response = await got(item.link, { - headers: { - cookie: `acw_sc__v2=${acwScV2Cookie};`, - }, - }); - const content = cheerio.load(response.data); - - item.description = content('article') - .html() - .replace(/data-src="/g, `src="${host}`); - item.pubDate = parseDate(content('time').attr('datetime')); - - return item; - }) - ) - ); + const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, ctx.cache.tryGet))); ctx.state.data = { title: `segmentfault - ${author}`, diff --git a/lib/v2/segmentfault/utils.js b/lib/v2/segmentfault/utils.js index 463b2a914fc7cc..bf19c3e8a16452 100644 --- a/lib/v2/segmentfault/utils.js +++ b/lib/v2/segmentfault/utils.js @@ -1,8 +1,12 @@ const zlib = require('zlib'); const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); const config = require('@/config').value; const { getAcwScV2ByArg1 } = require('@/v2/5eplay/utils'); +const host = 'https://segmentfault.com'; + const acw_sc__v2 = (link, tryGet) => tryGet( 'segmentfault:acw_sc__v2', @@ -29,6 +33,34 @@ const acw_sc__v2 = (link, tryGet) => false ); +const parseList = (data) => + data.map((item) => ({ + title: item.title, + link: new URL(item.url, host).href, + author: item.user.name, + pubDate: parseDate(item.created, 'X'), + })); + +const parseItems = (cookie, item, tryGet) => + tryGet(item.link, async () => { + const response = await got(item.link, { + headers: { + cookie: `acw_sc__v2=${cookie};`, + }, + }); + const content = cheerio.load(response.data); + + item.description = content('article').html(); + item.category = content('.badge-tag') + .toArray() + .map((item) => content(item).text()); + + return item; + }); + module.exports = { + host, acw_sc__v2, + parseList, + parseItems, }; diff --git a/lib/v2/sehuatang/index.js b/lib/v2/sehuatang/index.js index 8d9e9a90cb3256..5e298795db2b3e 100644 --- a/lib/v2/sehuatang/index.js +++ b/lib/v2/sehuatang/index.js @@ -56,7 +56,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); const list = $('#threadlisttableid tbody[id^=normalthread]') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 25) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 25) .toArray() .map((item) => { item = $(item); @@ -88,23 +88,21 @@ module.exports = async (ctx) => { $(image).replaceWith($(``)); } } - // if postMessage does not have any images, try to parse image url from `.pattl` - if (images.length === 0) { - const pattl = $('.pattl'); - const pattlImages = $(pattl).find('img'); - for (const pattlImage of pattlImages) { - const file = $(pattlImage).attr('file'); - if (!file || file === 'undefined') { - $(pattlImage).replaceWith(''); - } else { - $(pattlImage).replaceWith($(``)); - } + // also parse image url from `.pattl` + const pattl = $('.pattl'); + const pattlImages = $(pattl).find('img'); + for (const pattlImage of pattlImages) { + const file = $(pattlImage).attr('file'); + if (!file || file === 'undefined') { + $(pattlImage).replaceWith(''); + } else { + $(pattlImage).replaceWith($(``)); } - postMessage.append($(pattl)); } + postMessage.append($(pattl)); $('em[onclick]').remove(); - info.description = (postMessage.html() || '抓取原帖失败').replace(/ignore_js_op/g, 'div'); + info.description = (postMessage.html() || '抓取原帖失败').replaceAll('ignore_js_op', 'div'); info.pubDate = timezone(parseDate($('.authi em span').attr('title')), 8); const magnet = postMessage.find('div.blockcode li').first().text(); diff --git a/lib/v2/sehuatang/user.js b/lib/v2/sehuatang/user.js index 889ebcf17ffe98..fbcfbdff1c9443 100644 --- a/lib/v2/sehuatang/user.js +++ b/lib/v2/sehuatang/user.js @@ -8,7 +8,7 @@ const baseUrl = 'https://sehuatang.org/'; module.exports = async (ctx) => { if (!config.sehuatang.cookie) { - throw 'Sehuatang RSS is disabled due to the lack of relevant config'; + throw new Error('Sehuatang RSS is disabled due to the lack of relevant config'); } // 从Url参数中获取uid const uid = ctx.params.uid; @@ -25,7 +25,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); const list = $('#delform tr:not(.th)') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 25) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 25) .toArray() .map((item) => { item = $(item); @@ -62,23 +62,22 @@ module.exports = async (ctx) => { $(image).replaceWith($(``)); } } - // if postMessage does not have any images, try to parse image url from `.pattl` - if (images.length === 0) { - const pattl = $('.pattl'); - const pattlImages = $(pattl).find('img'); - for (const pattlImage of pattlImages) { - const file = $(pattlImage).attr('file'); - if (!file || file === 'undefined') { - $(pattlImage).replaceWith(''); - } else { - $(pattlImage).replaceWith($(``)); - } + // also parse image url from `.pattl` + const pattl = $('.pattl'); + const pattlImages = $(pattl).find('img'); + for (const pattlImage of pattlImages) { + const file = $(pattlImage).attr('file'); + if (!file || file === 'undefined') { + $(pattlImage).replaceWith(''); + } else { + $(pattlImage).replaceWith($(``)); } - postMessage.append($(pattl)); } + postMessage.append($(pattl)); + $('em[onclick]').remove(); - info.description = (postMessage.html() || '抓取原帖失败').replace(/ignore_js_op/g, 'div'); + info.description = (postMessage.html() || '抓取原帖失败').replaceAll('ignore_js_op', 'div'); const dateString = $('.authi em').first().text(); const datestampString = dateString.split(' ')[1]; diff --git a/lib/v2/sensortower/blog.js b/lib/v2/sensortower/blog.js index 167231d2f86e63..5cab8617e555dd 100644 --- a/lib/v2/sensortower/blog.js +++ b/lib/v2/sensortower/blog.js @@ -49,7 +49,7 @@ module.exports = async (ctx) => { item.title = detail.title; item.author = detail.author.name; item.pubDate = parseDate(detail.pubDate, 'MMMM YYYY'); - item.category = (detail.tags?.map((t) => t.title) ?? []).concat(detail.category?.map((c) => c.title) ?? []); + item.category = [...(detail.tags?.map((t) => t.title) ?? []), ...(detail.category?.map((c) => c.title) ?? [])]; item.description = art(path.join(__dirname, 'templates/description.art'), { header: content('header[data-csk-entry-type="blog"]').html(), description: content('div[data-csk-entry-type="blog"] div[data-testid="Text-root"]').html(), @@ -64,6 +64,6 @@ module.exports = async (ctx) => { title: 'Sensor Tower - Blog', link: currentUrl, item: items, - language: language ? language : 'en-US', + language: language ?? 'en-US', }; }; diff --git a/lib/v2/setn/index.js b/lib/v2/setn/index.js index 7665b4ca5e5e53..8dfc38f3ae87f9 100644 --- a/lib/v2/setn/index.js +++ b/lib/v2/setn/index.js @@ -32,9 +32,9 @@ const ids = { }; const getCurrentUrl = (category) => { - const rootUrl = rootUrls.hasOwnProperty(category) ? rootUrls[category] : defaultRootUrl; + const rootUrl = Object.hasOwn(rootUrls, category) ? rootUrls[category] : defaultRootUrl; - if (ids.hasOwnProperty(category)) { + if (Object.hasOwn(ids, category)) { return `${rootUrl}/ViewAll.aspx${ids[category] === '' ? '' : `?PageGroupID=${ids[category]}`}`; } @@ -43,7 +43,7 @@ const getCurrentUrl = (category) => { module.exports = async (ctx) => { const category = ctx.params.category ?? '即時'; - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 42; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 42; const currentUrl = getCurrentUrl(category); @@ -62,11 +62,11 @@ module.exports = async (ctx) => { item = $(item); const a = item.find('a').last(); - const link = a.attr('href').replace(/(\?|&)utm_campaign=.*/g, ''); + const link = a.attr('href').replaceAll(/(\?|&)utm_campaign=.*/g, ''); return { title: a.text(), - link: /^http/.test(link) ? link : `${rootUrls.hasOwnProperty(category) ? rootUrls[category] : defaultRootUrl}${link}`, + link: link.startsWith('http') ? link : `${Object.hasOwn(rootUrls, category) ? rootUrls[category] : defaultRootUrl}${link}`, }; }); diff --git a/lib/v2/seu/cse/index.js b/lib/v2/seu/cse/index.js index 6cd8eb994cf55a..bac2cb9c58479d 100644 --- a/lib/v2/seu/cse/index.js +++ b/lib/v2/seu/cse/index.js @@ -14,7 +14,7 @@ module.exports = async (ctx) => { }; const { type = 22535 } = ctx.params; - const id = type.length === 4 ? Object.keys(map).find((key) => map[key].initial === type) : parseInt(type); // backward compatible + const id = type.length === 4 ? Object.keys(map).find((key) => map[key].initial === type) : Number.parseInt(type); // backward compatible const link = new URL(`${id}/list.htm`, host).href; const { data: response } = await got(link); diff --git a/lib/v2/seu/yzb/index.js b/lib/v2/seu/yzb/index.js index c72268a763c25e..ab545a2c216499 100644 --- a/lib/v2/seu/yzb/index.js +++ b/lib/v2/seu/yzb/index.js @@ -12,7 +12,7 @@ module.exports = async (ctx) => { }; const { type } = ctx.params; - const id = type.length === 1 ? Object.keys(map).find((key) => map[key].id === parseInt(type)) : parseInt(type); // backward compatible + const id = type.length === 1 ? Object.keys(map).find((key) => map[key].id === Number.parseInt(type)) : Number.parseInt(type); // backward compatible const url = new URL(`${id}/list.htm`, host).href; const { data: response } = await got(url); diff --git a/lib/v2/sfacg/maintainer.js b/lib/v2/sfacg/maintainer.js new file mode 100644 index 00000000000000..52f450a7f50cd8 --- /dev/null +++ b/lib/v2/sfacg/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/novel/chapter/:id': ['keocheung'], +}; diff --git a/lib/v2/sfacg/novel-chapter.js b/lib/v2/sfacg/novel-chapter.js new file mode 100644 index 00000000000000..32d4c751d12a06 --- /dev/null +++ b/lib/v2/sfacg/novel-chapter.js @@ -0,0 +1,51 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +module.exports = async (ctx) => { + const { id } = ctx.params; + const limit = Number.parseInt(ctx.query.limit) || 20; + + const baseUrl = 'https://book.sfacg.com'; + + const { data: response } = await got(`${baseUrl}/Novel/${id}/MainIndex/`); + const $ = cheerio.load(response); + + const list = $('div.catalog-list ul li a') + .slice(-limit) + .toArray() + .map((item) => { + item = $(item); + return { + title: item.attr('title'), + link: `${baseUrl}${item.attr('href')}`, + }; + }); + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: response } = await got(item.link); + const $ = cheerio.load(response); + + item.description = $('div.article-content').html(); + + const rawDate = $('div.article-desc span').eq(1).text(); + item.pubDate = timezone(parseDate(rawDate.replace('更新时间:', '')), +8); + + return item; + }) + ) + ); + + const { data: intro } = await got(`${baseUrl}/Novel/${id}/`); + const $i = cheerio.load(intro); + + ctx.state.data = { + title: `SF轻小说 ${$('h1.story-title').text()}`, + link: `${baseUrl}/Novel/${id}`, + description: $i('p.introduce').text(), + image: $i('div.summary-pic img').attr('src').replace('http://', 'https://'), + item: items, + }; +}; diff --git a/lib/v2/sfacg/radar.js b/lib/v2/sfacg/radar.js new file mode 100644 index 00000000000000..674188b67b4423 --- /dev/null +++ b/lib/v2/sfacg/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'sfacg.com': { + _name: 'SF 轻小说', + book: [ + { + title: '章节', + docs: 'https://docs.rsshub.app/routes/reading#sf-qing-xiao-shuo', + source: ['/Novel/:id/*'], + target: '/sfacg/novel/chapter/:id', + }, + ], + }, +}; diff --git a/lib/v2/sfacg/router.js b/lib/v2/sfacg/router.js new file mode 100644 index 00000000000000..ce44237afc2a33 --- /dev/null +++ b/lib/v2/sfacg/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/novel/chapter/:id', require('./novel-chapter')); +}; diff --git a/lib/v2/shiep/config.js b/lib/v2/shiep/config.js index 5d1754da93e4cb..7051f3bd126f36 100644 --- a/lib/v2/shiep/config.js +++ b/lib/v2/shiep/config.js @@ -1,12 +1,12 @@ const config = { bwc: { title: '武装部保卫处', id: 'tzgg' }, - career: { title: '本科就业信息网', id: 'tzgg', listSelector: 'ul.newsList', pubDateSelector: 'li[class="span2 y"]', descriptionSelector: '.aContent' }, + career: { title: '本科就业信息网', id: 'tzgg', listSelector: 'ul.newsList', pubDateSelector: 'li.span2.y', descriptionSelector: '.aContent' }, cyb: { title: '资产经营公司/产业办', id: '367' }, dangban: { title: '党委办公室', id: '4014' }, - djfwzxdcs: { title: '党建服务中心/党建督查室', id: 'tzgg', listSelector: '.news', pubDateSelector: 'span[class="news_meta"]' }, + djfwzxdcs: { title: '党建服务中心/党建督查室', id: 'tzgg', listSelector: 'li.news', pubDateSelector: 'span.news_meta' }, dqxy: { title: '电气工程学院', id: '2462' }, dwllc: { title: '对外联络处', id: '2649' }, - dxxy: { title: '电子与信息工程学院', id: 'tzgg', pubDateSelector: 'div[class="article-publishdate"]' }, + dxxy: { title: '电子与信息工程学院', id: 'tzgg', pubDateSelector: 'div.article-publishdate' }, energy: { title: '能源与机械工程学院', id: '892' }, 'energy-saving': { title: '上海热交换系统节能工程技术研究中心', id: 'tzgg' }, english: { title: 'Shanghai University of Electric Power', id: 'events' }, @@ -17,24 +17,24 @@ const config = { gonghui: { title: '工会', id: '1806', listSelector: 'table.wp_article_list_table tr', pubDateSelector: 'td[align="right"]' }, 'green-energy': { title: '上海绿色能源并网技术研究中心', id: '118' }, hhsyzx: { title: '能源化学实验教学中心', id: '3709' }, - hhxy: { title: '环境与化学工程学院', id: '1231' }, + hhxy: { title: '环境与化学工程学院', id: '5559', listSelector: 'li.list-item', pubDateSelector: 'div.item-publishdate' }, hqglc: { title: '后勤管理处(后勤服务中心)', id: '1616' }, - ieetc: { title: '创新创业工程训练中心', id: 'cxcy', pubDateSelector: 'div[class="article-publishdate"]' }, + ieetc: { title: '创新创业工程训练中心', id: 'cxcy', pubDateSelector: 'div.article-publishdate' }, jgdw: { title: '机关党委', id: '3205' }, jgxy: { title: '经济与管理学院', id: '3633' }, jijian: { title: '纪委(监察专员办公室)', id: '59' }, jjc: { title: '基建处', id: '327' }, jjxy: { title: '继续教育学院(国际教育学院)', id: '2582' }, jsjxfzzx: { title: '教师教学发展中心', id: '3909' }, - jsjxy: { title: '计算机科学与技术学院', id: 'xygg', listSelector: 'div.xy-service-text', pubDateSelector: 'span' }, + jsjxy: { title: '计算机科学与技术学院', id: 'xygg', listSelector: 'div.xylist', pubDateSelector: 'span:nth-child(2)' }, jszyzx: { title: '技术转移中心', id: '4247' }, - jwc: { title: '教务处', id: '227' }, + jwc: { title: '教务处', id: '227', listSelector: 'div.text-list li', pubDateSelector: 'span.time' }, jxfz: { title: '电力装备设计与制造虚拟仿真中心', id: '3330' }, kczx: { title: '能源电力科创中心', id: '3946' }, kyc: { title: '科研处/融合办', id: '834' }, lgxq: { title: '临港新校区建设综合办公室', id: '377' }, library: { title: '图书馆', id: '4866' }, - metc: { title: '现代教育技术中心/信息办', id: 'tzgg', pubDateSelector: 'div[class="article-publishdate"]' }, + metc: { title: '现代教育技术中心/信息办', id: 'tzgg', pubDateSelector: 'div.article-publishdate' }, mpep: { title: '上海市电力材料防护与新材料重点实验室', id: '1134' }, news: { title: '新闻网', id: 'notice' }, nydlzk: { title: '能源电力智库', id: 'tzgg' }, @@ -47,9 +47,9 @@ const config = { slxy: { title: '数理学院', id: '2063' }, spgc: { title: '智能发电实验教学中心', id: '4449' }, sysyzcglc: { title: '实验室与资产管理处', id: '312' }, - tgb: { title: '离退休党委/退管办', id: 'notice', pubDateSelector: 'div[class="article-publishdate"]' }, + tgb: { title: '离退休党委/退管办', id: 'notice', pubDateSelector: 'div.article-publishdate' }, tw: { title: '团委', id: '2092' }, - tyb: { title: '体育学院', id: '2891', pubDateSelector: 'div[class="article-publishdate"]' }, + tyb: { title: '体育学院', id: '2891', pubDateSelector: 'div.article-publishdate' }, tzb: { title: '统战部', id: '3858' }, wenming: { title: '文明办', id: '2202' }, wgyxy: { title: '外国语学院', id: 'tzgg' }, @@ -59,8 +59,8 @@ const config = { xxgk: { title: '信息公开网', id: 'zxgkxx' }, yjsc: { title: '研究生院/研工部', id: '1161' }, zdhxy: { title: '自动化工程学院', id: '2002' }, - ztjy: { title: '学习路上', id: '5575' }, zs: { title: '本科招生网', id: 'zxxx' }, + ztjy: { title: '学习路上', id: '5575' }, zzb: { title: '组织部(老干部处、党校)', id: '1534' }, }; diff --git a/lib/v2/shiep/index.js b/lib/v2/shiep/index.js index b054f51d2ccc1c..2c5ecd90011b3d 100644 --- a/lib/v2/shiep/index.js +++ b/lib/v2/shiep/index.js @@ -10,7 +10,13 @@ module.exports = async (ctx) => { const type = ctx.params.type; if (!Object.keys(config).includes(type)) { - throw Error('Invalid type'); + throw new Error(`Invalid type: ${type}`); + } + + const { listSelector = '.list_item', pubDateSelector = '.Article_PublishDate', descriptionSelector = '.wp_articlecontent', title } = config[type]; + + if (!title) { + throw new Error(`Invalid type: ${type}`); } const host = `https://${type}.shiep.edu.cn`; @@ -20,40 +26,37 @@ module.exports = async (ctx) => { const response = await got(link); const $ = cheerio.load(response.data); - const listSelector = config[type].listSelector || '.list_item'; - const pubDateSelector = config[type].pubDateSelector || 'span[class="Article_PublishDate"]'; - const descriptionSelector = config[type].descriptionSelector || '.wp_articlecontent'; - - const items = $(listSelector) + const list = $(listSelector) .toArray() - .filter((item) => { - const date = dayjs($(item).find(pubDateSelector).text().trim()); - return date.isValid(); - }) .map((item) => { item = $(item); + const pubDateText = item.find(pubDateSelector).text().trim(); + const match = pubDateText.match(/\b(\d{4}-\d{2}-\d{2})\b/); return { - title: item.find('a').attr('title') || item.find('a').text(), + title: item.find('a').attr('title') || item.find('h3').text() || item.find('a').text(), link: new URL(item.find('a').attr('href'), host).href, - pubDate: parseDate(item.find(pubDateSelector).text().trim(), 'YYYY-MM-DD'), + pubDate: match ? parseDate(match[0], 'YYYY-MM-DD') : null, }; + }) + .filter((item) => { + const date = dayjs(item.pubDate); + return date.isValid(); }); - await Promise.all( - items.map((item) => + const items = await Promise.all( + list.map((item) => ctx.cache.tryGet(item.link, async () => { try { const response = await got(item.link); const $ = cheerio.load(response.data); - if ($(descriptionSelector).length > 0) { - item.description = art(path.resolve(__dirname, 'templates/description.art'), { - description: $(descriptionSelector).html(), - }); - } else { - item.description = '请进行统一身份认证后查看内容'; - } - } catch (e) { + item.description = + $(descriptionSelector).length > 0 + ? art(path.resolve(__dirname, 'templates/description.art'), { + description: $(descriptionSelector).html(), + }) + : '请进行统一身份认证后查看内容'; + } catch { item.description = '请在校内或通过校园VPN查看内容'; } return item; @@ -62,9 +65,8 @@ module.exports = async (ctx) => { ); ctx.state.data = { - title: '上海电力大学-' + config[type].title, + title: `上海电力大学-${title}`, link, - description: '上海电力大学-' + config[type].title, item: items, }; }; diff --git a/lib/v2/shiep/radar.js b/lib/v2/shiep/radar.js index 9c8a99377499c2..94e29aae753542 100644 --- a/lib/v2/shiep/radar.js +++ b/lib/v2/shiep/radar.js @@ -481,20 +481,20 @@ module.exports = { target: '/shiep/zdhxy/:id', }, ], - ztjy: [ + zs: [ { - title: '学习路上', + title: '本科招生网', docs: 'https://docs.rsshub.app/routes/university#shang-hai-dian-li-da-xue', source: ['/:id/list.htm'], - target: '/shiep/ztjy/:id', + target: '/shiep/zs/:id', }, ], - zs: [ + ztjy: [ { - title: '本科招生网', + title: '学习路上', docs: 'https://docs.rsshub.app/routes/university#shang-hai-dian-li-da-xue', source: ['/:id/list.htm'], - target: '/shiep/zs/:id', + target: '/shiep/ztjy/:id', }, ], zzb: [ diff --git a/lib/v2/shisu/maintainer.js b/lib/v2/shisu/maintainer.js new file mode 100644 index 00000000000000..6c139623493df7 --- /dev/null +++ b/lib/v2/shisu/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/news/:category': ['Duuckjing'], +}; diff --git a/lib/v2/shisu/news.js b/lib/v2/shisu/news.js new file mode 100644 index 00000000000000..bc58b68c50260e --- /dev/null +++ b/lib/v2/shisu/news.js @@ -0,0 +1,64 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +const url = 'https://news.shisu.edu.cn'; +const banner = 'https://news.shisu.edu.cn/news/index/39adf3d9ae414bc39c6d3b9316ae531f.png'; + +module.exports = async (ctx) => { + const { section = 'news' } = ctx.params; + const { data: r } = await got(`${url}/${section}/index.html`); + const $ = cheerio.load(r); + let itemsoup; + switch (section) { + case 'news': + itemsoup = $('#gallery-wrapper > article') + .toArray() + .map((i0) => { + const i = $(i0); + const img = i.find('img').attr('src'); + return { + title: i.find('a').text().trim(), + link: `${url}${i.find('a').attr('href')}`, + category: i.find('.in-con02 > span:nth-child(1)').text(), + itunes_item_image: `${url}${img}`, + }; + }); + break; + default: + itemsoup = $('li.clear') + .toArray() + .map((i0) => { + const i = $(i0); + return { + title: i.find('h3>a').attr('title').trim(), + link: `${url}${i.find('h3>a').attr('href')}`, + category: i.find('p>span:nth-child(1)').text(), + }; + }); + } + const items = await Promise.all( + itemsoup.map((j) => + ctx.cache.tryGet(j.link, async () => { + const { data: r } = await got(j.link); + const $ = cheerio.load(r); + const img = $('.tempWrap > ul > li:nth-child(1)> img').attr('src'); + j.description = $('.ot_main_r .content').html(); + j.author = $('.math_time_l > span:nth-child(3)').text().trim(); + j.pubDate = timezone(parseDate($('.math_time_l > span:nth-child(2)').text(), 'YYYY-MM-DD'), +8); + if (!j.itunes_item_image) { + j.itunes_item_image = img ? `${url}${img}` : banner; + } + return j; + }) + ) + ); + + ctx.state.data = { + title: `上外新闻|SISU TODAY -${section.charAt(0).toUpperCase() + section.slice(1)}`, + image: 'https://bkimg.cdn.bcebos.com/pic/8d5494eef01f3a296b70affa9825bc315c607c4d?x-bce-process=image/resize,m_lfit,w_536,limit_1/quality,Q_70', + link: `${url}/${section}/index.html`, + item: items, + }; +}; diff --git a/lib/v2/shisu/radar.js b/lib/v2/shisu/radar.js new file mode 100644 index 00000000000000..2afb8b85845888 --- /dev/null +++ b/lib/v2/shisu/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'shisu.edu.cn': { + _name: '上海外国语大学', + news: [ + { + title: '上外新闻', + docs: 'https://docs.rsshub.app/routes/university#shang-hai-wai-guo-yu-da-xue', + source: ['/:category/index.html'], + target: '/shisu/news/:category', + }, + ], + }, +}; diff --git a/lib/v2/shisu/router.js b/lib/v2/shisu/router.js new file mode 100644 index 00000000000000..37ca55cd62f6e6 --- /dev/null +++ b/lib/v2/shisu/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/news/:category', require('./news')); +}; diff --git a/lib/v2/shmeea/index.js b/lib/v2/shmeea/index.js index 14067fd34ddfb9..6fc1c22f3956d9 100644 --- a/lib/v2/shmeea/index.js +++ b/lib/v2/shmeea/index.js @@ -1,44 +1,53 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); +const timezone = require('@/utils/timezone'); +const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { - const baseURL = 'http://www.shmeea.edu.cn'; - const rootUrl = baseURL + '/page/08000/index.html'; - const response = await got({ - method: 'get', - url: rootUrl, - }); + const id = ctx.params.id ?? '08000'; + const baseURL = 'https://www.shmeea.edu.cn'; + const link = `${baseURL}/page/${id}/index.html`; - const data = response.data; + const response = await got(link); + const $ = cheerio.load(response.data); - const $ = cheerio.load(data); + const title = `上海市教育考试院-${$('#main .pageh4-tit').text().trim()}`; - const list = $('#main .pageList li'); + const list = $('#main .pageList li') + .toArray() + .map((item) => { + item = $(item); + return { + title: item.find('a').attr('title') || item.find('a').text(), + link: new URL(item.find('a').attr('href'), baseURL).href, + pubDate: parseDate(item.find('.listTime').text().trim(), 'YYYY-MM-DD'), + }; + }); const items = await Promise.all( - list.map(async (i, item) => { - item = $(item); - const link = baseURL + item.find('a').attr('href'); - const description = await ctx.cache.tryGet(link, async () => { - const result = await got.get(link); + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + if (!item.link.endsWith('.html') || new URL(item.link).hostname !== new URL(baseURL).hostname) { + return item; + } + const result = await got(item.link); const $ = cheerio.load(result.data); - return $('#ivs_content').html(); - }); - return { - title: item.find('a').text(), - pubDate: new Date(item.find('.listTime').text()), - link, - description, - }; - }) + const description = $('#ivs_content').html(); + const pbTimeText = $('#ivs_title .PBtime').text().trim(); + + item.description = description; + item.pubDate = pbTimeText ? timezone(parseDate(pbTimeText, 'YYYY-MM-DD HH:mm:ss'), +8) : item.pubDate; + + return item; + }) + ) ); ctx.state.data = { - title: '上海市教育考试院', - description: '消息速递', - link: baseURL, + title, + link, item: items, }; }; diff --git a/lib/v2/shmeea/maintainer.js b/lib/v2/shmeea/maintainer.js index 000328f1807d9f..091353e5315fbc 100644 --- a/lib/v2/shmeea/maintainer.js +++ b/lib/v2/shmeea/maintainer.js @@ -1,4 +1,4 @@ module.exports = { - '/': ['jialinghui'], + '/:id?': ['jialinghui', 'Misaka13514'], '/self-study': ['h2ws'], }; diff --git a/lib/v2/shmeea/radar.js b/lib/v2/shmeea/radar.js index 7f84a7b937ce1d..010c259f661e01 100644 --- a/lib/v2/shmeea/radar.js +++ b/lib/v2/shmeea/radar.js @@ -3,14 +3,17 @@ module.exports = { _name: '上海市教育考试院', www: [ { - title: '消息速递', - docs: 'https://docs.rsshub.app/routes/other#shang-hai-shi-jiao-yu-kao-shi-yuan', - source: ['/'], - target: '/shmeea', + title: '消息', + docs: 'https://docs.rsshub.app/routes/study#shang-hai-shi-jiao-yu-kao-shi-yuan', + source: ['/page/:id?/index.html'], + target: (params, url, document) => { + const li = document.querySelector('#main .pageList li'); + return li ? '/shmeea/:id?' : ''; + }, }, { title: '自学考试通知公告', - docs: 'https://docs.rsshub.app/routes/other#shang-hai-shi-jiao-yu-kao-shi-yuan', + docs: 'https://docs.rsshub.app/routes/study#shang-hai-shi-jiao-yu-kao-shi-yuan', source: ['/page/04000/index.html', '/'], target: '/shmeea/self-study', }, diff --git a/lib/v2/shmeea/router.js b/lib/v2/shmeea/router.js index b864ea159b3384..2493022da52e3d 100644 --- a/lib/v2/shmeea/router.js +++ b/lib/v2/shmeea/router.js @@ -1,4 +1,4 @@ module.exports = function (router) { - router.get('/', require('./index')); router.get('/self-study', require('./self-study')); + router.get('/:id?', require('./index')); }; diff --git a/lib/v2/shoac/maintainer.js b/lib/v2/shoac/maintainer.js new file mode 100644 index 00000000000000..5c371afc4abf63 --- /dev/null +++ b/lib/v2/shoac/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/recent-show': ['TonyRL'], +}; diff --git a/lib/v2/shoac/radar.js b/lib/v2/shoac/radar.js new file mode 100644 index 00000000000000..ee5ed3c3fbfb9d --- /dev/null +++ b/lib/v2/shoac/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'shoac.com.cn': { + _name: '上海东方艺术中心', + '.': [ + { + title: '演出月历', + docs: 'https://docs.rsshub.app/routes/shopping#shang-hai-dong-fang-yi-shu-zhong-xin', + source: ['/'], + target: '/shoac/recent-show', + }, + ], + }, +}; diff --git a/lib/v2/shoac/recent-show.js b/lib/v2/shoac/recent-show.js new file mode 100644 index 00000000000000..0c7551d1b7ae3c --- /dev/null +++ b/lib/v2/shoac/recent-show.js @@ -0,0 +1,72 @@ +const got = require('@/utils/got'); +const path = require('path'); +const { art } = require('@/utils/render'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const baseUrl = 'https://www.shoac.com.cn'; + + const headers = { + Channel: 'theatre_pc', + Location: '121.458563,31.250315', + Theater: 1323, + 'Flagship-Store': true, + }; + + const { data: products } = await got.post(`${baseUrl}/platform-backend/good/theater/dongyi-products`, { + headers, + json: { + page: 1, + size: 12, + calendar: false, + timeSort: true, + venueId: '', + }, + }); + + const list = products.data.records.map((item) => ({ + title: item.productNameShort, + category: [item.categoryName, item.subCategoryName], + link: `${baseUrl}/#/detail?projectId=${item.projectId}`, + projectId: item.projectId, + minPrice: item.minPrice, + maxPrice: item.maxPrice, + placeCname: item.placeCname, + })); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: detail } = await got(`${baseUrl}/platform-backend/good/project/detail/old/${item.projectId}`, { + headers, + searchParams: { + distributionSeriesId: '', + distributionChannelId: '', + }, + }); + const { data: show } = await got(`${baseUrl}/platform-backend/good/shows/old/${item.projectId}`, { + headers, + searchParams: { + distributionSeriesId: '', + distributionChannelId: '', + }, + }); + + item.description = art(path.join(__dirname, 'templates/detail.art'), { + item, + detail: detail.data, + show: show.data, + }); + item.pubDate = show.data.showInfoDetailList ? parseDate(show.data.showInfoDetailList[0].saleBeginTime, 'x') : null; + + return item; + }) + ) + ); + + ctx.state.data = { + title: '演出月历 - 上海东方艺术中心管理有限公司', + link: baseUrl, + item: items, + }; +}; diff --git a/lib/v2/shoac/router.js b/lib/v2/shoac/router.js new file mode 100644 index 00000000000000..103658ae82c47a --- /dev/null +++ b/lib/v2/shoac/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/recent-show', require('./recent-show')); +}; diff --git a/lib/v2/shoac/templates/detail.art b/lib/v2/shoac/templates/detail.art new file mode 100644 index 00000000000000..4cf80a6eef1670 --- /dev/null +++ b/lib/v2/shoac/templates/detail.art @@ -0,0 +1,25 @@ +{{ if detail.img }} +
+{{ /if }} + + + + + + + + + + + + + + + + + +
类型:{{ detail.productSubtypeName }}
时间:{{ detail.showStartToEndTime }}
地点:{{ detail.showPlaceName }}-{{ item.placeCname }}
{{ item.minPrice }}-{{ item.maxPrice }}
+
+{{ if detail.projectDesp }} +{{@ detail.projectDesp }} +{{ /if }} diff --git a/lib/v2/showstart/artist.js b/lib/v2/showstart/artist.js new file mode 100644 index 00000000000000..bb2e0d5d4e99ff --- /dev/null +++ b/lib/v2/showstart/artist.js @@ -0,0 +1,15 @@ +const { TITLE, HOST } = require('./const'); +const { fetchPerformerInfo } = require('./service'); + +module.exports = async (ctx) => { + const id = ctx.params.id; + const artist = await fetchPerformerInfo({ + performerId: id, + }); + ctx.state.data = { + title: `${TITLE} - ${artist.name}`, + description: artist.content, + link: `${HOST}/artist/${artist.id}`, + item: artist.activityList, + }; +}; diff --git a/lib/v2/showstart/brand.js b/lib/v2/showstart/brand.js new file mode 100644 index 00000000000000..0b7fc4d8c43487 --- /dev/null +++ b/lib/v2/showstart/brand.js @@ -0,0 +1,15 @@ +const { TITLE, HOST } = require('./const'); +const { fetchBrandInfo } = require('./service'); + +module.exports = async (ctx) => { + const id = ctx.params.id; + const brand = await fetchBrandInfo({ + brandId: id, + }); + ctx.state.data = { + title: `${TITLE} - ${brand.name}`, + description: brand.content, + link: `${HOST}/host/${brand.id}`, + item: brand.activityList, + }; +}; diff --git a/lib/v2/showstart/const.js b/lib/v2/showstart/const.js new file mode 100644 index 00000000000000..f7880faed03536 --- /dev/null +++ b/lib/v2/showstart/const.js @@ -0,0 +1,4 @@ +module.exports = { + HOST: 'https://www.showstart.com', + TITLE: '秀动网', +}; diff --git a/lib/v2/showstart/event.js b/lib/v2/showstart/event.js new file mode 100644 index 00000000000000..3406952b65a13f --- /dev/null +++ b/lib/v2/showstart/event.js @@ -0,0 +1,18 @@ +const { TITLE, HOST } = require('./const'); +const { fetchActivityList, fetchDictionary } = require('./service'); + +module.exports = async (ctx) => { + const cityCode = Number.parseInt(ctx.params.cityCode); + const showStyle = Number.parseInt(ctx.params.showStyle); + const items = await fetchActivityList({ + cityCode, + showStyle, + }); + const { cityName, showName } = await fetchDictionary(cityCode, showStyle); + const tags = [cityName, showName].filter(Boolean).join(' - '); + ctx.state.data = { + title: `${TITLE} - ${tags}`, + link: HOST, + item: items, + }; +}; diff --git a/lib/v2/showstart/maintainer.js b/lib/v2/showstart/maintainer.js new file mode 100644 index 00000000000000..6526631fcf6dd8 --- /dev/null +++ b/lib/v2/showstart/maintainer.js @@ -0,0 +1,6 @@ +module.exports = { + '/artist/:id': ['lchtao26'], + '/brand/:id': ['lchtao26'], + '/event/:cityCode/:showStyle?': ['lchtao26'], + '/search/:type/:keyword?': ['lchtao26'], +}; diff --git a/lib/v2/showstart/radar.js b/lib/v2/showstart/radar.js new file mode 100644 index 00000000000000..51c6e4af0957c3 --- /dev/null +++ b/lib/v2/showstart/radar.js @@ -0,0 +1,40 @@ +module.exports = { + 'showstart.com': { + _name: '秀动网', + www: [ + { + title: '演出更新', + docs: 'https://docs.rsshub.app/routes/shopping#xiu-dong-wang-yan-chu-geng-xin', + source: ['/event/list'], + target: (_, url) => { + const search = new URL(url).searchParams; + const cityCode = search.get('cityCode') || 0; + const showStyle = search.get('showStyle') || 0; + return `/showstart/event/${cityCode}/${showStyle}`; + }, + }, + { + title: '演出搜索', + docs: 'https://docs.rsshub.app/routes/shopping#xiu-dong-wang-yan-chu-sou-suo', + source: ['/event/list'], + target: (_, url) => { + const search = new URL(url).searchParams; + const keyword = search.get('keyword') || ''; + return `/showstart/search/event/${keyword}`; + }, + }, + { + title: '音乐人 - 演出更新', + docs: 'https://docs.rsshub.app/routes/shopping#yin-yue-ren-yan-chu-geng-xin', + source: ['/artist/:id'], + target: '/showstart/artist/:id', + }, + { + title: '厂牌 - 演出更新', + docs: 'https://docs.rsshub.app/routes/shopping#chang-pai-yan-chu-geng-xin', + source: ['/host/:id'], + target: '/showstart/brand/:id', + }, + ], + }, +}; diff --git a/lib/v2/showstart/router.js b/lib/v2/showstart/router.js new file mode 100644 index 00000000000000..8dd29cee311569 --- /dev/null +++ b/lib/v2/showstart/router.js @@ -0,0 +1,6 @@ +module.exports = (router) => { + router.get('/artist/:id', require('./artist')); + router.get('/brand/:id', require('./brand')); + router.get('/event/:cityCode/:showStyle?', require('./event')); + router.get('/search/:type/:keyword?', require('./search')); +}; diff --git a/lib/v2/showstart/search.js b/lib/v2/showstart/search.js new file mode 100644 index 00000000000000..cc222c1a25e62e --- /dev/null +++ b/lib/v2/showstart/search.js @@ -0,0 +1,51 @@ +const { TITLE, HOST } = require('./const'); +const { fetchActivityList, fetchPerformerList, fetchBrandList, fetchCityList, fetchStyleList } = require('./service'); + +module.exports = async (ctx) => { + const type = ctx.params.type || ''; + const keyword = ctx.params.keyword || ''; + + switch (type) { + case 'event': + ctx.state.data = { + title: `${TITLE} - 搜演出 - ${keyword || '全部'}`, + link: HOST, + item: await fetchActivityList({ keyword }), + }; + break; + case 'artist': + ctx.state.data = { + title: `${TITLE} - 搜艺人 - ${keyword || '全部'}`, + link: HOST, + item: await fetchPerformerList({ searchKeyword: keyword }), + }; + break; + case 'brand': + ctx.state.data = { + title: `${TITLE} - 搜厂牌 - ${keyword || '全部'}`, + link: HOST, + item: await fetchBrandList({ searchKeyword: keyword }), + }; + break; + case 'city': + ctx.state.data = { + title: `${TITLE} - 搜城市 - ${keyword || '全部'}`, + link: HOST, + item: await fetchCityList(keyword), + }; + break; + case 'style': + ctx.state.data = { + title: `${TITLE} - 搜风格 - ${keyword || '全部'}`, + link: HOST, + item: await fetchStyleList(keyword), + }; + break; + default: + ctx.state.data = { + title: `${TITLE} - 搜演出 - ${type || '全部'}`, + link: HOST, + item: await fetchActivityList({ keyword: type }), + }; + } +}; diff --git a/lib/v2/showstart/service.js b/lib/v2/showstart/service.js new file mode 100644 index 00000000000000..ac2bde1cc58fab --- /dev/null +++ b/lib/v2/showstart/service.js @@ -0,0 +1,167 @@ +const { HOST } = require('./const'); +const { getAccessToken, post, sortBy, uniqBy } = require('./utils'); + +async function fetchActivityList( + params = { + pageNo: '1', + pageSize: '30', + cityCode: '', + activityIds: '', + coupon: '', + keyword: '', + organizerId: '', + performerId: '', + showStyle: '', + showTime: '', + showType: '', + siteId: '', + sortType: '', + themeId: '', + timeRange: '', + tourId: '', + type: '', + tag: '', + } +) { + const accessToken = await getAccessToken(); + const resp = await post('/web/activity/list', accessToken, params); + return resp.result.result.map((item) => formatActivity(item)); +} + +const image = (src) => (src ? `` : ''); +const time = (time) => (time ? `

演出时间:${time}

` : ''); +const address = (cityName, siteName) => (cityName || siteName ? `

地址:${[cityName, siteName].join(' - ')}

` : ''); +const performers = (name) => (name ? `

艺人:${name}

` : ''); +const price = (price) => (price ? `

票价:${price}

` : ''); + +function formatActivity(item) { + return { + title: item.title, + link: `${HOST}/event/${item.id}`, + description: [image(item.poster), time(item.showTime), address(item.cityName, item.siteName), performers(item.performers), price(item.price)].join(''), + }; +} + +async function fetchPerformerList( + params = { + pageNo: '1', + pageSize: '30', + searchKeyword: '', + styleId: '', + } +) { + const accessToken = await getAccessToken(); + const resp = await post('/web/performer/list', accessToken, params); + return resp.result.result.map((item) => ({ + title: item.name, + link: `${HOST}/artist/${item.id}`, + description: `id: ${item.id}`, + })); +} + +async function fetchPerformerInfo( + params = { + performerId: '', + } +) { + const accessToken = await getAccessToken(); + const resp = await post('/web/performer/info', accessToken, params); + return { + id: params.id, + name: resp.result.name, + content: resp.result.content, + avatar: resp.result.avatar, + poster: resp.result.poster, + styles: resp.result.styles, + activityList: resp.result.activities.map((item) => formatActivity(item)), + }; +} + +async function fetchBrandInfo( + params = { + brandId: '', + } +) { + const accessToken = await getAccessToken(); + const resp = await post('/web/brand/info', accessToken, params); + return { + id: params.id, + name: resp.result.name, + content: resp.result.content, + avatar: resp.result.avatar, + poster: resp.result.poster, + activityList: resp.result.activities.map((item) => formatActivity(item)), + }; +} + +async function fetchBrandList( + params = { + pageNo: '1', + pageSize: '30', + searchKeyword: '', + } +) { + const accessToken = await getAccessToken(); + const resp = await post('/web/brand/list', accessToken, params); + return resp.result.result.map((item) => ({ + title: item.name, + link: `${HOST}/host/${item.id}`, + description: `id: ${item.id}`, + })); +} + +async function fetchParams() { + const accessToken = await getAccessToken(); + return post('/web/activity/list/params', accessToken); +} + +async function fetchCityList(keyword = '') { + const resp = await fetchParams(); + const cities = sortBy(resp.result, 'cityCode'); + return cities + .filter((item) => item.cityName.includes(keyword.trim())) + .map((item) => ({ + title: item.cityName, + link: `${HOST}/event/list?cityCode=${item.cityCode}`, + description: `cityCode: ${item.cityCode}`, + })); +} + +// styles is embed in each city item +// so we need to fetch all city items and then extract styles from them +async function fetchStyleList(keyword = '') { + const resp = await fetchParams(); + let styles = resp.result.flatMap((item) => item.styles); + styles = uniqBy(styles, 'key'); + styles = sortBy(styles, 'key'); + return styles + .filter((item) => item.showName.includes(keyword.trim())) + .map((item) => ({ + title: item.showName, + link: `${HOST}/event/list?showStyle=${item.key}`, + description: `showStyle: ${item.key}`, + })); +} + +async function fetchDictionary(cityCode, showStyle) { + const resp = await fetchParams(); + const target = resp.result.find((item) => item.cityCode === cityCode); + if (!target) { + return {}; + } + return { + cityName: target.cityName, + showName: target.styles.find((item) => item.key === showStyle)?.showName, + }; +} + +module.exports = { + fetchActivityList, + fetchCityList, + fetchStyleList, + fetchPerformerList, + fetchPerformerInfo, + fetchBrandList, + fetchBrandInfo, + fetchDictionary, +}; diff --git a/lib/v2/showstart/utils.js b/lib/v2/showstart/utils.js new file mode 100644 index 00000000000000..025a8be8febfb3 --- /dev/null +++ b/lib/v2/showstart/utils.js @@ -0,0 +1,92 @@ +const got = require('@/utils/got'); +const md5 = require('@/utils/md5'); + +const uuid = (length = 20) => { + const e = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + Date.now(); + const r = []; + for (let i = 0; i < length; i++) { + r.push(e.charAt(Math.floor(Math.random() * e.length))); + } + return r.join(''); +}; + +const cookieMap = new Map([['token', uuid(32).toLowerCase()]]); + +const devioceInfo = { + vendorName: '', + deviceMode: '', + deviceName: '', + systemName: '', + systemVersion: '', + cpuMode: ' ', // Note the space + cpuCores: '', + cpuArch: '', + memerySize: '', + diskSize: '', + network: '', + resolution: '1920*1080', + pixelResolution: '', +}; + +const getAccessToken = async () => { + const { result } = await post('/waf/gettoken'); + cookieMap.set('accessToken', result.accessToken.access_token); + cookieMap.set('idToken', result.idToken.id_token); + return cookieMap.get('accessToken'); +}; + +const post = async (requestPath, accessToken = md5(Date.now().toString()), payload) => { + const traceId = uuid(32) + Date.now(); + + const { data: response } = await got.post(`https://www.showstart.com/api${requestPath}`, { + headers: { + cdeviceinfo: encodeURIComponent(JSON.stringify(devioceInfo)), + cdeviceno: cookieMap.get('token'), + cookie: [...cookieMap.entries()].map(([key, value]) => `${key}=${value}`).join('; '), + crpsign: md5(accessToken + /* sign/cusut (empty) + idToken (empty) + userInfo.userId (empty) + */ 'web' + cookieMap.get('token') + (payload ? JSON.stringify(payload) : '') + requestPath + '999web' + traceId), + crtraceid: traceId, + csappid: 'web', + cterminal: 'web', + cusat: accessToken, + cusid: '', + cusit: '', + cusname: '', + cusut: '', + cversion: '999', + }, + json: payload, + }); + + return response; +}; + +function sortBy(items, key) { + return items.sort((a, b) => { + if (a[key] < b[key]) { + return -1; + } + if (a[key] > b[key]) { + return 1; + } + return 0; + }); +} + +function uniqBy(items, key) { + const set = new Set(); + return items.filter((item) => { + if (set.has(item[key])) { + return false; + } + set.add(item[key]); + return true; + }); +} + +module.exports = { + post, + getAccessToken, + uuid, + sortBy, + uniqBy, +}; diff --git a/lib/v2/shuiguopai/index.js b/lib/v2/shuiguopai/index.js index 392eac86850039..bac250651dd758 100644 --- a/lib/v2/shuiguopai/index.js +++ b/lib/v2/shuiguopai/index.js @@ -6,12 +6,12 @@ const { art } = require('@/utils/render'); const path = require('path'); module.exports = async (ctx) => { - const rootUrl = 'https://sgp3.fun'; + const rootUrl = 'https://sgptv.vip'; const apiRootUrl = 'https://api.cbbee0.com'; const listUrl = `${apiRootUrl}/v1_2/homePage`; const filmUrl = `${apiRootUrl}/v1_2/filmInfo`; - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 50; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50; const response = await got({ method: 'post', @@ -31,7 +31,8 @@ module.exports = async (ctx) => { title: item.title, guid: item.library_id, link: `${rootUrl}/play-details/${item.library_id}`, - pubDate: timezone(parseDate(item.show_time, 'MM月DD日 HH:mm'), +8), + pubDate: timezone(parseDate(item.show_time_origin, 'YYYY-MM-DD HH:mm:ss'), +8), + category: item.tags.map((t) => t.tag_title), })); items = await Promise.all( @@ -45,23 +46,28 @@ module.exports = async (ctx) => { const content = cheerio.load(detailResponse.data); content('iframe').remove(); - const infoResponse = await got({ - method: 'post', - url: filmUrl, - json: { - device_id: '', - film_id: detailResponse.data.match(/film_id:"([\d,]+)",/)[1], - hm: '008-api', - userToken: '', - }, - }); + let videos; + const filmId = detailResponse.data.match(/film_id:"([\d,]+)",/)?.[1]; + if (filmId) { + const infoResponse = await got({ + method: 'post', + url: filmUrl, + json: { + device_id: '', + film_id: filmId, + hm: '008-api', + userToken: '', + }, + }); + + const data = infoResponse.data.data; - const data = infoResponse.data.data; + videos = data.map((d) => d.download_url); - const videos = data.map((d) => d.download_url); + item.category = data.flatMap((d) => d.tags.map((t) => t.tag_title)); + item.author = data.map((d) => d.actor).join(' '); + } - item.category = data.flatMap((d) => d.tags.map((t) => t.tag_title)); - item.author = data.map((d) => d.actor).join(' '); item.description = art(path.join(__dirname, 'templates/description.art'), { videos, description: content('.content').html(), diff --git a/lib/v2/shuiguopai/templates/description.art b/lib/v2/shuiguopai/templates/description.art index 8687d7eee03128..4d4411a4375152 100644 --- a/lib/v2/shuiguopai/templates/description.art +++ b/lib/v2/shuiguopai/templates/description.art @@ -5,4 +5,4 @@ {{ /each }} {{ /if }} -{{@ description }} \ No newline at end of file +{{@ description }} diff --git a/lib/v2/sigsac/ccs.js b/lib/v2/sigsac/ccs.js index 4f6a4b4e043689..cb2cca56109dcf 100644 --- a/lib/v2/sigsac/ccs.js +++ b/lib/v2/sigsac/ccs.js @@ -26,7 +26,7 @@ module.exports = async (ctx) => { const title = item.find('b').text().trim(); return { title, - author: item.find('p').text().trim().replaceAll('\n', '').replace(/\s+/g, ' '), + author: item.find('p').text().trim().replaceAll('\n', '').replaceAll(/\s+/g, ' '), link: `${link}#${title}`, pubDate: parseDate(link.match(/CCS(\d{4})/)[1], 'YYYY'), }; @@ -39,7 +39,7 @@ module.exports = async (ctx) => { const title = item.find('td').eq(0).text().trim(); return { title, - author: item.find('td').eq(1).text().trim().replaceAll('\n', '').replace(/\s+/g, ' '), + author: item.find('td').eq(1).text().trim().replaceAll('\n', '').replaceAll(/\s+/g, ' '), link: `${link}#${title}`, pubDate: parseDate(link.match(/CCS(\d{4})/)[1], 'YYYY'), }; diff --git a/lib/v2/sina/finance/stock/usstock.js b/lib/v2/sina/finance/stock/usstock.js index f5fc2eab2bf4cf..1d42f729b4254a 100644 --- a/lib/v2/sina/finance/stock/usstock.js +++ b/lib/v2/sina/finance/stock/usstock.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { tm: Date.now(), up: '0', action: '0', - _: new Date().getTime(), + _: Date.now(), }, }); const list = response.data.map((item) => ({ diff --git a/lib/v2/sina/utils.js b/lib/v2/sina/utils.js index 4c660243caa436..8037643ea9a32a 100644 --- a/lib/v2/sina/utils.js +++ b/lib/v2/sina/utils.js @@ -3,7 +3,7 @@ const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); const timezone = require('@/utils/timezone'); const { art } = require('@/utils/render'); -const { join } = require('path'); +const path = require('path'); const getRollNewsList = (pageid, lid, limit) => got('https://feed.mix.sina.com.cn/api/roll/get', { @@ -17,7 +17,7 @@ const getRollNewsList = (pageid, lid, limit) => num: limit, page: 1, r: Math.random(), - _: new Date().getTime(), + _: Date.now(), }, }); @@ -41,16 +41,16 @@ const parseArticle = (item, tryGet) => const htmlPubDate = $('#pub_date, .date'); const htmlDate = htmlPubDate.length ? timezone(parseDate(htmlPubDate.text(), ['YYYY年MM月DD日 HH:mm', 'YYYY年MM月DD日HH:mm']), 8) : null; const metaDate = metaPublishTime.length ? parseDate(metaPublishTime.attr('content')) : htmlDate; // 2023-05-08T08:39:31+08:00 - item.pubDate = item.pubDate ? item.pubDate : metaDate; + item.pubDate = item.pubDate ?? metaDate; item.author = $('meta[property="article:author"]').attr('content'); if (item.link.startsWith('https://slide.sports.sina.com.cn/') || item.link.startsWith('https://slide.tech.sina.com.cn/')) { const slideData = JSON.parse( $('script') .text() - .match(/var slide_data = (\{.*?\})\s/)[1] + .match(/var slide_data = ({.*?})\s/)[1] ); - item.description = art(join(__dirname, 'templates/slide.art'), { slideData }); + item.description = art(path.join(__dirname, 'templates/slide.art'), { slideData }); } else if (item.link.startsWith('https://video.sina.com.cn/')) { const videoId = $('script') .text() @@ -64,7 +64,7 @@ const parseArticle = (item, tryGet) => applt: 'web', tags: 'sinaplayer_pc', jsonp: '', - plid: 2021012801, + plid: 2_021_012_801, prid: '', uid: '', tid: '', @@ -81,7 +81,7 @@ const parseArticle = (item, tryGet) => const videoData = videoResponse.data; const poster = videoData.image; const videoUrl = videoData.videos.find((v) => v.type === 'mp4').dispatch_result.url; - item.description = art(join(__dirname, 'templates/video.art'), { poster, videoUrl }); + item.description = art(path.join(__dirname, 'templates/video.art'), { poster, videoUrl }); item.pubDate = parseDate(videoData.create_time, 'X'); } else if (item.link.startsWith('https://news.sina.com.cn/') || item.link.startsWith('https://mil.news.sina.com.cn/')) { item.description = $('#article').html(); diff --git a/lib/v2/sinchew/index.js b/lib/v2/sinchew/index.js index 36934f4a070999..4583b2ef141511 100644 --- a/lib/v2/sinchew/index.js +++ b/lib/v2/sinchew/index.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('.title .internalLink') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 20) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 20) .toArray() .map((item) => { item = $(item); @@ -26,7 +26,7 @@ module.exports = async (ctx) => { return { title: item.attr('data-title'), - link: /^http/.test(link) ? link : `${rootUrl}${link}`, + link: link.startsWith('http') ? link : `${rootUrl}${link}`, pubDate: timezone(parseDate(item.text()), +8), }; }); diff --git a/lib/v2/sjtu/tongqu/activity.js b/lib/v2/sjtu/tongqu/activity.js index 0a812b146fe6fc..756abbd035be31 100644 --- a/lib/v2/sjtu/tongqu/activity.js +++ b/lib/v2/sjtu/tongqu/activity.js @@ -1,6 +1,6 @@ const got = require('@/utils/got'); const { art } = require('@/utils/render'); -const { join } = require('path'); +const path = require('path'); const urlRoot = 'https://tongqu.sjtu.edu.cn'; @@ -33,7 +33,7 @@ module.exports = async (ctx) => { title: e.name, link: new URL(`/act/${e.actid}`, urlRoot).href, category: e.typename, - description: art(join(__dirname, '../templates/activity.art'), { e }), + description: art(path.join(__dirname, '../templates/activity.art'), { e }), })); ctx.state.data = { diff --git a/lib/v2/slowmist/slowmist.js b/lib/v2/slowmist/slowmist.js index 7c2e992209e02e..1b7f6ded25180d 100644 --- a/lib/v2/slowmist/slowmist.js +++ b/lib/v2/slowmist/slowmist.js @@ -7,15 +7,25 @@ module.exports = async (ctx) => { let type = ctx.params.type; let title = '慢雾科技 - '; - if (type === 'news') { - title += '公司新闻'; - } else if (type === 'vul') { - title += '漏洞披露'; - } else if (type === 'research') { - title += '技术研究'; - } else { - type = 'news'; - title += '公司新闻'; + switch (type) { + case 'news': + title += '公司新闻'; + + break; + + case 'vul': + title += '漏洞披露'; + + break; + + case 'research': + title += '技术研究'; + + break; + + default: + type = 'news'; + title += '公司新闻'; } const url = `${baseUrl}/api/get_list?type=${type}`; diff --git a/lib/v2/smashingmagazine/category.js b/lib/v2/smashingmagazine/category.js index 97eac9f4de155b..2a1a6ec7fe2a81 100644 --- a/lib/v2/smashingmagazine/category.js +++ b/lib/v2/smashingmagazine/category.js @@ -5,12 +5,7 @@ const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { const { category } = ctx.params; const baseUrl = 'https://www.smashingmagazine.com'; - let route; - if (category) { - route = `/category/${category}`; - } else { - route = '/articles'; - } + const route = category ? `/category/${category}` : '/articles'; const { data: response } = await got(`${baseUrl}${route}`); const $ = cheerio.load(response); diff --git a/lib/v2/smashingmagazine/radar.js b/lib/v2/smashingmagazine/radar.js index 5cf3074c5752b4..c2b576b88e2e7f 100644 --- a/lib/v2/smashingmagazine/radar.js +++ b/lib/v2/smashingmagazine/radar.js @@ -4,13 +4,13 @@ module.exports = { '.': [ { title: 'Articles', - docs: 'https://docs.rsshub.app/routes/en/programming#a-list-apart', + docs: 'https://docs.rsshub.app/routes/programming#a-list-apart', source: ['/articles/'], target: '/smashingmagazine', }, { title: 'Category', - docs: 'https://docs.rsshub.app/routes/en/programming#a-list-apart', + docs: 'https://docs.rsshub.app/routes/programming#a-list-apart', source: ['/category/:category'], target: '/smashingmagazine/:category', }, diff --git a/lib/v2/smzdm/haowen_fenlei.js b/lib/v2/smzdm/haowen-fenlei.js similarity index 88% rename from lib/v2/smzdm/haowen_fenlei.js rename to lib/v2/smzdm/haowen-fenlei.js index 1f1be35cbb06af..ecf4219c0debd9 100644 --- a/lib/v2/smzdm/haowen_fenlei.js +++ b/lib/v2/smzdm/haowen-fenlei.js @@ -6,12 +6,7 @@ module.exports = async (ctx) => { const name = ctx.params.name; const sort = ctx.params.sort || '0'; - let link; - if (sort === '0') { - link = `https://post.smzdm.com/fenlei/${name}/`; - } else { - link = `https://post.smzdm.com/fenlei/${name}/hot_${sort}/`; - } + const link = sort === '0' ? `https://post.smzdm.com/fenlei/${name}/` : `https://post.smzdm.com/fenlei/${name}/hot_${sort}/`; const response = await got.get(link); const $ = cheerio.load(response.data); @@ -39,7 +34,7 @@ module.exports = async (ctx) => { item.description = $('article').html(); item.pubDate = timezone(parseDate($('meta[property="og:release_date"]').attr('content')), 8); item.author = $('meta[property="og:author"]').attr('content'); - } catch (err) { + } catch { // 404 } diff --git a/lib/v2/smzdm/router.js b/lib/v2/smzdm/router.js index 877fc4ab29ff04..7b317a9bdd891e 100644 --- a/lib/v2/smzdm/router.js +++ b/lib/v2/smzdm/router.js @@ -2,7 +2,7 @@ module.exports = (router) => { router.get('/article/:uid', require('./article')); router.get('/baoliao/:uid', require('./baoliao')); router.get('/haowen/:day?', require('./haowen')); - router.get('/haowen/fenlei/:name/:sort?', require('./haowen_fenlei')); + router.get('/haowen/fenlei/:name/:sort?', require('./haowen-fenlei')); router.get('/keyword/:keyword', require('./keyword')); router.get('/ranking/:rank_type/:rank_id/:hour', require('./ranking')); }; diff --git a/lib/v2/snowpeak/maintainer.js b/lib/v2/snowpeak/maintainer.js index 0cf798e8435ac4..29c9d64cf7d38d 100644 --- a/lib/v2/snowpeak/maintainer.js +++ b/lib/v2/snowpeak/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/us/new-arrivals': ['NavePnow'], + '/us/new-arrivals': ['EthanWng97'], }; diff --git a/lib/routes/sogou/doodles.js b/lib/v2/sogou/doodles.js similarity index 100% rename from lib/routes/sogou/doodles.js rename to lib/v2/sogou/doodles.js diff --git a/lib/v2/sogou/maintainer.js b/lib/v2/sogou/maintainer.js new file mode 100644 index 00000000000000..b94d21c1b217b7 --- /dev/null +++ b/lib/v2/sogou/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/doodles': ['xyqfer'], + '/search/:keyword': ['CaoMeiYouRen'], +}; diff --git a/lib/v2/sogou/radar.js b/lib/v2/sogou/radar.js new file mode 100644 index 00000000000000..a548c7fad78294 --- /dev/null +++ b/lib/v2/sogou/radar.js @@ -0,0 +1,16 @@ +module.exports = { + 'sogou.com': { + _name: '搜狗', + www: [ + { + title: '搜索', + docs: 'https://docs.rsshub.app/routes/other#sou-gou-sou-suo', + source: '/', + target: (params, url) => { + const keyword = new URL(url).searchParams.get('query'); + return `/sogou/search/${keyword}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/sogou/router.js b/lib/v2/sogou/router.js new file mode 100644 index 00000000000000..6dbfd72eb56f15 --- /dev/null +++ b/lib/v2/sogou/router.js @@ -0,0 +1,4 @@ +module.exports = function (router) { + router.get('/doodles', require('./doodles')); + router.get('/search/:keyword', require('./search')); +}; diff --git a/lib/v2/sogou/search.js b/lib/v2/sogou/search.js new file mode 100644 index 00000000000000..9cdb63454849fa --- /dev/null +++ b/lib/v2/sogou/search.js @@ -0,0 +1,54 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); +const { parseDate } = require('@/utils/parse-date'); +const config = require('@/config').value; + +const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images }); + +module.exports = async (ctx) => { + const { keyword } = ctx.params; + const url = `https://www.sogou.com/web?query=${encodeURIComponent(keyword)}`; + const key = `sogou-search:${url}`; + const items = await ctx.cache.tryGet( + key, + async () => { + const response = (await got(url)).data; + const $ = cheerio.load(response); + const result = $('#main'); + return result + .find('.vrwrap') + .map((i, el) => { + const element = $(el); + const imgs = element + .find('img') + .map((j, el2) => $(el2).attr('src')) + .toArray(); + const link = element.find('h3 a').first().attr('href'); + const title = element.find('h3').first().text(); + const description = element.find('.text-layout').first().text() || element.find('.space-txt').first().text() || element.find('[class^="translate"]').first().text(); + const author = element.find('.citeurl span').first().text() || ''; + const pubDate = parseDate(element.find('.citeurl .cite-date').first().text().trim()); + return { + link, + title, + description: renderDescription(description, imgs), + author, + pubDate, + }; + }) + .toArray() + .filter((e) => e?.link); + }, + config.cache.routeExpire, + false + ); + + ctx.state.data = { + title: `${keyword} - 搜狗搜索`, + description: `${keyword} - 搜狗搜索`, + link: url, + item: items, + }; +}; diff --git a/lib/v2/sogou/templates/description.art b/lib/v2/sogou/templates/description.art new file mode 100644 index 00000000000000..5f98f4ca33ce07 --- /dev/null +++ b/lib/v2/sogou/templates/description.art @@ -0,0 +1,6 @@ +{{@ description }} +{{if images}} + {{each images}} + + {{/each}} +{{/if}} diff --git a/lib/v2/sohu/mp.js b/lib/v2/sohu/mp.js index ffa4dea4ddb799..1dc239ebf96512 100644 --- a/lib/v2/sohu/mp.js +++ b/lib/v2/sohu/mp.js @@ -1,56 +1,65 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); +const path = require('path'); +const { art } = require('@/utils/render'); module.exports = async (ctx) => { const { id } = ctx.params; const authorArticleAPI = `https://v2.sohu.com/author-page-api/author-articles/pc/${id}`; - const response = await got.get(authorArticleAPI); - const list = response.data.data.pcArticleVOS.splice(0, 10); + const response = await got(authorArticleAPI); + const list = response.data.data.pcArticleVOS.map((item) => ({ + title: item.title, + link: item.link.startsWith('http') ? item.link : `https://${item.link}`, + pubDate: parseDate(item.publicTime), + })); let author, link; const items = await Promise.all( - list.map(async (e) => { - if (e.link && !e.link.match(/^https?:\/\//)) { - if (e.link.match(/^\/\//)) { - e.link = 'http:' + e.link; + list.map((e) => + ctx.cache.tryGet(e.link, async () => { + const { data: response } = await got(e.link); + const $ = cheerio.load(response); + + if (!author) { + const meta = $('span[data-role="original-link"]'); + author = meta.find('a').text(); + // can't get author's link on server, so use the RSSHub link + // link = meta.attr('href').split('==')[0]; + } + + if (/window\.sohu_mp\.article_video/.test($('script').text())) { + const videoSrc = $('script') + .text() + .match(/\s*url: "(.*?)",/)?.[1]; + e.description = art(path.join(__dirname, 'templates/video.art'), { + poster: $('script') + .text() + .match(/cover: "(.*?)",/)?.[1], + src: videoSrc, + type: videoSrc?.split('.').pop().toLowerCase(), + }); } else { - e.link = 'http://' + e.link; + const article = $('#mp-editor'); + + article.find('#backsohucom, p[data-role="editor-name"]').each((i, e) => { + $(e).remove(); + }); + + e.description = article.html(); } - } - const response = await ctx.cache.tryGet(e.link, async () => (await got.get(e.link)).data); - const $ = cheerio.load(response); - - if (!author) { - const meta = $('span[data-role="original-link"]'); - author = meta.find('a').text(); - // can't get author's link on server, so use the RSSHub link - // link = meta.attr('href').split('==')[0]; - } - - const article = $('#mp-editor'); - - article.find('#backsohucom, p[data-role="editor-name"]').each((i, e) => { - $(e).remove(); - }); - - const single = { - title: e.title, - link: e.link, - description: article.html(), - pubDate: parseDate(e.publicTime), - author, - }; - - return single; - }) + + e.author = author; + + return e; + }) + ) ); ctx.state.data = { title: `搜狐号 - ${author}`, link, - description: '', item: items, }; }; diff --git a/lib/v2/sohu/templates/video.art b/lib/v2/sohu/templates/video.art new file mode 100644 index 00000000000000..d6e27f87c32ced --- /dev/null +++ b/lib/v2/sohu/templates/video.art @@ -0,0 +1,7 @@ + diff --git a/lib/v2/solidot/_article.js b/lib/v2/solidot/_article.js index 5f808bffa4f03f..70fce0f03d4834 100644 --- a/lib/v2/solidot/_article.js +++ b/lib/v2/solidot/_article.js @@ -17,26 +17,26 @@ module.exports = async function get_article(url) { const $ = cheerio.load(data); const date_raw = $('div.talk_time').clone().children().remove().end().text(); - const date_str_zh = date_raw.replace(/^[^`]*发表于(.*分)[^`]*$/g, '$1'); // use [^`] to match \n + const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(.*分)[^`]*$/g, '$1'); // use [^`] to match \n const date_str = date_str_zh - .replace(/[年月]/g, '-') - .replace(/时/g, ':') - .replace(/[日分]/g, ''); + .replaceAll(/[年月]/g, '-') + .replaceAll('时', ':') + .replaceAll(/[分日]/g, ''); const title = $('div.block_m > div.ct_tittle > div.bg_htit > h2').text(); const category = $('div.icon_float > a').attr('title'); const author = $('div.talk_time > b') .text() - .replace(/^来自(.*)部门$/g, '$1'); + .replaceAll(/^来自(.*)部门$/g, '$1'); $('div.ct_tittle').remove(); $('div.talk_time').remove(); const description = $('div.block_m') .html() - .replace(/(href.*?)(.*?)<\/u>/g, `$1$2`) - .replace(/href="\//g, 'href="' + domain + '/') + .replaceAll(/(href.*?)(.*?)<\/u>/g, `$1$2`) + .replaceAll('href="/', 'href="' + domain + '/') // Preserve the not extremely disturbing donation ad // to support the site. - .replace(/()/g, `

$1`); + .replaceAll(/()/g, `

$1`); const item = { title, diff --git a/lib/v2/solidot/main.js b/lib/v2/solidot/main.js index 4154bdd8aae5a4..7eac165737a5e2 100644 --- a/lib/v2/solidot/main.js +++ b/lib/v2/solidot/main.js @@ -11,7 +11,7 @@ const { isValidHost } = require('@/utils/valid-host'); module.exports = async (ctx) => { const type = ctx.params.type ?? 'www'; if (!isValidHost(type)) { - throw Error('Invalid type'); + throw new Error('Invalid type'); } const base_url = `https://${type}.solidot.org`; @@ -25,8 +25,8 @@ module.exports = async (ctx) => { // get urls const a = $('div.block_m').find('div.bg_htit > h2 > a'); const urls = []; - for (let i = 0; i < a.length; ++i) { - urls.push($(a[i]).attr('href')); + for (const element of a) { + urls.push($(element).attr('href')); } // get articles diff --git a/lib/v2/sony/maintainer.js b/lib/v2/sony/maintainer.js index 7465a0d08bccc3..738a2d7a214b21 100644 --- a/lib/v2/sony/maintainer.js +++ b/lib/v2/sony/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/downloads/:productType/:productId': ['NavePnow'], + '/downloads/:productType/:productId': ['EthanWng97'], }; diff --git a/lib/v2/sourceforge/index.js b/lib/v2/sourceforge/index.js new file mode 100644 index 00000000000000..af4daf59bd75bd --- /dev/null +++ b/lib/v2/sourceforge/index.js @@ -0,0 +1,33 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const routeParams = ctx.params.routeParams; + + const baseURL = 'https://sourceforge.net'; + const link = `https://sourceforge.net/directory/?${routeParams.toString()}`; + + const response = await got.get(link); + const $ = cheerio.load(response.data); + const itemList = $('ul.projects li[itemprop=itemListElement]'); + + ctx.state.data = { + title: $('.content h1').text().trim(), + link, + item: itemList.toArray().map((element) => { + const item = $(element); + const title = item.find('.result-heading-title').text().trim(); + const link = `${baseURL}${item.find('.result-heading-title').attr('href')}`; + const description = item.find('.result-heading-texts').html(); + const pubDate = parseDate(item.find('time').attr('datetime'), 'YYYY-MM-DD'); + + return { + title, + link, + description, + pubDate, + }; + }), + }; +}; diff --git a/lib/v2/sourceforge/maintainer.js b/lib/v2/sourceforge/maintainer.js new file mode 100644 index 00000000000000..c5843ab97be053 --- /dev/null +++ b/lib/v2/sourceforge/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/:routeParams?': ['JimenezLi'], +}; diff --git a/lib/v2/sourceforge/radar.js b/lib/v2/sourceforge/radar.js new file mode 100644 index 00000000000000..a38640319709b9 --- /dev/null +++ b/lib/v2/sourceforge/radar.js @@ -0,0 +1,12 @@ +module.exports = { + 'sourceforge.net': { + _name: 'SourceForge', + www: [ + { + title: 'Software', + docs: 'https://docs.rsshub.app/routes/program-update#sourceforge', + source: '/directory', + }, + ], + }, +}; diff --git a/lib/v2/sourceforge/router.js b/lib/v2/sourceforge/router.js new file mode 100644 index 00000000000000..4e4a3fe0bf65b4 --- /dev/null +++ b/lib/v2/sourceforge/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/:routeParams?', require('./index')); +}; diff --git a/lib/v2/spotify/saved.js b/lib/v2/spotify/saved.js index 551f7d3cee8fbe..6ab1e670038e94 100644 --- a/lib/v2/spotify/saved.js +++ b/lib/v2/spotify/saved.js @@ -6,7 +6,7 @@ module.exports = async (ctx) => { const token = await utils.getPrivateToken(); const { limit } = ctx.params; - const pageSize = !isNaN(parseInt(limit)) ? parseInt(limit) : 50; + const pageSize = isNaN(Number.parseInt(limit)) ? 50 : Number.parseInt(limit); const itemsResponse = await got .get(`https://api.spotify.com/v1/me/tracks?limit=${pageSize}`, { diff --git a/lib/v2/spotify/top.js b/lib/v2/spotify/top.js index 05361b1f4f9f0e..084dc2f2572990 100644 --- a/lib/v2/spotify/top.js +++ b/lib/v2/spotify/top.js @@ -3,7 +3,7 @@ const got = require('@/utils/got'); module.exports = (type) => async (ctx) => { if (type !== 'tracks' && type !== 'artists') { - throw `Invalid type: ${type}`; + throw new Error(`Invalid type: ${type}`); } const token = await utils.getPrivateToken(); @@ -19,6 +19,6 @@ module.exports = (type) => async (ctx) => { ctx.state.data = { title: `Spotify: My Top ${type[0].toUpperCase() + type.slice(1)}`, allowEmpty: true, - item: type === 'tracks' ? items.map(utils.parseTrack) : items.map(utils.parseArtist), + item: type === 'tracks' ? items.map((element) => utils.parseTrack(element)) : items.map((element) => utils.parseArtist(element)), }; }; diff --git a/lib/v2/spotify/utils.js b/lib/v2/spotify/utils.js index 70f7184aa2ee90..3492e879506bcb 100644 --- a/lib/v2/spotify/utils.js +++ b/lib/v2/spotify/utils.js @@ -4,7 +4,7 @@ const got = require('@/utils/got'); // Token used to retrieve public information. async function getPublicToken() { if (!config.spotify || !config.spotify.clientId || !config.spotify.clientSecret) { - throw 'Spotify public RSS is disabled due to the lack of relevant config'; + throw new Error('Spotify public RSS is disabled due to the lack of relevant config'); } const { clientId, clientSecret } = config.spotify; @@ -26,7 +26,7 @@ async function getPublicToken() { // Note that we don't use PKCE since the client secret shall be safe on the server. async function getPrivateToken() { if (!config.spotify || !config.spotify.clientId || !config.spotify.clientSecret || !config.spotify.refreshToken) { - throw 'Spotify private RSS is disabled due to the lack of relevant config'; + throw new Error('Spotify private RSS is disabled due to the lack of relevant config'); } const { clientId, clientSecret, refreshToken } = config.spotify; diff --git a/lib/v2/sqmc/radar.js b/lib/v2/sqmc/radar.js index 0b10ff0c4a8699..d5e1620cefe00c 100644 --- a/lib/v2/sqmc/radar.js +++ b/lib/v2/sqmc/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: '官网信息', - docs: 'https://docs.rsshub.app/university.html#xin-xiang-yi-xue-yuan-san-quan-xue-yuan', + docs: 'https://docs.rsshub.app/routes/university#xin-xiang-yi-xue-yuan-san-quan-xue-yuan', source: ['/:category/list.htm'], target: '/sqmc/www/:category?', }, diff --git a/lib/v2/sse/convert.js b/lib/v2/sse/convert.js index cc4f81a3b0e7e4..827b200fd3a15d 100644 --- a/lib/v2/sse/convert.js +++ b/lib/v2/sse/convert.js @@ -11,7 +11,7 @@ module.exports = async (ctx) => { isPagination: true, 'pageHelp.pageSize': 20, flag: 0, - _: new Date().getTime(), + _: Date.now(), ...query.split('&').reduce((acc, cur) => { const [key, value] = cur.split('='); acc[key] = value; diff --git a/lib/v2/sse/disclosure.js b/lib/v2/sse/disclosure.js index 815936ebc841bc..98c11791acc586 100644 --- a/lib/v2/sse/disclosure.js +++ b/lib/v2/sse/disclosure.js @@ -3,7 +3,6 @@ const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { const query = ctx.params.query ?? ''; // beginDate=2018-08-18&endDate=2020-09-01&productId=600696 - const host = 'https://www.sse.com.cn'; const queries = query.split('&').reduce((acc, cur) => { const [key, value] = cur.split('='); acc[key] = value; @@ -22,7 +21,7 @@ module.exports = async (ctx) => { 'pageHelp.beginPage': 1, 'pageHelp.cacheSize': 1, 'pageHelp.endPage': 5, - _: new Date().getTime(), + _: Date.now(), ...queries, }, headers: { @@ -30,11 +29,13 @@ module.exports = async (ctx) => { }, }); + const pdfHost = 'https://static.sse.com.cn'; + const items = response.data.result.map((item) => ({ title: item.TITLE, - description: `${host}${item.URL}`, + description: `${pdfHost}${item.URL}`, pubDate: parseDate(item.ADDDATE), - link: `${host}${item.URL}`, + link: `${pdfHost}${item.URL}`, author: item.SECURITY_NAME, })); diff --git a/lib/v2/sse/inquire.js b/lib/v2/sse/inquire.js index 7237fbf4080808..420ded3dd58ccc 100644 --- a/lib/v2/sse/inquire.js +++ b/lib/v2/sse/inquire.js @@ -20,7 +20,7 @@ module.exports = async (ctx) => { stockcode: '', extWTFL: '', order: 'createTime|desc,stockcode|asc', - _: new Date().getTime(), + _: Date.now(), }, headers: { Referer: refererUrl, diff --git a/lib/v2/sse/lawandrules.js b/lib/v2/sse/lawandrules.js index bcb682875aa842..d582f1f13d8965 100644 --- a/lib/v2/sse/lawandrules.js +++ b/lib/v2/sse/lawandrules.js @@ -6,7 +6,7 @@ module.exports = async (ctx) => { const slug = ctx.params.slug ?? 'latest'; const rootUrl = 'https://www.sse.com.cn'; - const currentUrl = `${rootUrl}/lawandrules/guide/${slug.replace(/-/g, '/')}`; + const currentUrl = `${rootUrl}/lawandrules/guide/${slug.replaceAll('-', '/')}`; const response = await got(currentUrl); const $ = cheerio.load(response.data); diff --git a/lib/v2/sse/radar.js b/lib/v2/sse/radar.js index 5007b7c50af3c8..d2e64b5662a4ab 100644 --- a/lib/v2/sse/radar.js +++ b/lib/v2/sse/radar.js @@ -34,7 +34,7 @@ module.exports = { title: '本所业务指南与流程', docs: 'https://docs.rsshub.app/routes/finance#shang-hai-zheng-quan-jiao-yi-suo', source: ['/lawandrules/guide/*slug', '/'], - target: (params) => `/sse/lawandrules/${params.slug.replace(/\//g, '-')}`, + target: (params) => `/sse/lawandrules/${params.slug.replaceAll('/', '-')}`, }, ], }, diff --git a/lib/v2/sse/renewal.js b/lib/v2/sse/renewal.js index ec8c3c717968cf..3558a93e6ffd09 100644 --- a/lib/v2/sse/renewal.js +++ b/lib/v2/sse/renewal.js @@ -28,7 +28,7 @@ module.exports = async (ctx) => { keyword: '', auditApplyDateBegin: '', auditApplyDateEnd: '', - _: new Date().getTime(), + _: Date.now(), }, headers: { Referer: pageUrl, diff --git a/lib/v2/sspai/author.js b/lib/v2/sspai/author.js index 73c9f95b610343..75b137992cf96a 100644 --- a/lib/v2/sspai/author.js +++ b/lib/v2/sspai/author.js @@ -11,14 +11,14 @@ async function getUserId(slug) { }); if (response.data.error !== 0) { - throw 'User Not Found'; + throw new Error('User Not Found'); } return response.data.data.id; } module.exports = async (ctx) => { - const id = ctx.params.id.match(/^\d+$/) ? ctx.params.id : await getUserId(ctx.params.id); + const id = /^\d+$/.test(ctx.params.id) ? ctx.params.id : await getUserId(ctx.params.id); const api_url = `https://sspai.com/api/v1/articles?offset=0&limit=20&author_ids=${id}&include_total=false`; const resp = await got({ method: 'get', diff --git a/lib/v2/sspai/router.js b/lib/v2/sspai/router.js index 8e37bdb921f7b6..4495352031a40f 100644 --- a/lib/v2/sspai/router.js +++ b/lib/v2/sspai/router.js @@ -6,8 +6,8 @@ module.exports = (router) => { router.get('/index', require('./index')); router.get('/matrix', require('./matrix')); router.get('/series', require('./series')); - router.get('/series/:id', require('./seriesUpdate')); - router.get('/shortcuts', require('./shortcutsGallery')); + router.get('/series/:id', require('./series-update')); + router.get('/shortcuts', require('./shortcuts-gallery')); router.get('/tag/:keyword', require('./tag')); router.get('/topic/:id', require('./topic')); router.get('/topics', require('./topics')); diff --git a/lib/v2/sspai/seriesUpdate.js b/lib/v2/sspai/series-update.js similarity index 100% rename from lib/v2/sspai/seriesUpdate.js rename to lib/v2/sspai/series-update.js diff --git a/lib/v2/sspai/shortcutsGallery.js b/lib/v2/sspai/shortcuts-gallery.js similarity index 94% rename from lib/v2/sspai/shortcutsGallery.js rename to lib/v2/sspai/shortcuts-gallery.js index 97f5bd4b43c3d4..8f079436ece0b8 100644 --- a/lib/v2/sspai/shortcutsGallery.js +++ b/lib/v2/sspai/shortcuts-gallery.js @@ -12,7 +12,7 @@ module.exports = async (ctx) => { for (const shortcut of category.data || []) { items.push({ title: shortcut.name, - description: `作者:${shortcut.author_id}
${decodeURIComponent((shortcut.description || '').replace(/\+/g, '%20'))}`, + description: `作者:${shortcut.author_id}
${decodeURIComponent((shortcut.description || '').replaceAll('+', '%20'))}`, pubDate: parseDate(shortcut.utime * 1000), guid: shortcut.id, link: shortcut.url, diff --git a/lib/v2/sspu/jwc.js b/lib/v2/sspu/jwc.js new file mode 100644 index 00000000000000..661b55dd9d8e76 --- /dev/null +++ b/lib/v2/sspu/jwc.js @@ -0,0 +1,44 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +module.exports = async (ctx) => { + const { listId } = ctx.params; + const baseUrl = 'https://jwc.sspu.edu.cn'; + + const { data: response, url: link } = await got(`${baseUrl}/${listId}/list.htm`); + const $ = cheerio.load(response); + + const list = $('.news_list .news') + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 15) + .toArray() + .map((item) => { + item = $(item); + const title = item.find('.news_title a'); + return { + title: title.attr('title'), + link: `${baseUrl}${title.attr('href')}`, + }; + }); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: response } = await got(item.link); + const $ = cheerio.load(response); + + item.description = $('.wp_articlecontent').html(); + item.pubDate = timezone(parseDate($('.arti_update').text(), 'YYYY-MM-DD HH:mm:ss'), +8); + + return item; + }) + ) + ); + + ctx.state.data = { + title: $('head title').text(), + link, + item: items, + }; +}; diff --git a/lib/v2/sspu/maintainer.js b/lib/v2/sspu/maintainer.js new file mode 100644 index 00000000000000..81b227d6e143f4 --- /dev/null +++ b/lib/v2/sspu/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/jwc/:listId': ['TonyRL'], + '/pe/:id?': ['nczitzk'], +}; diff --git a/lib/v2/sspu/pe.js b/lib/v2/sspu/pe.js new file mode 100644 index 00000000000000..214d069f9ebb87 --- /dev/null +++ b/lib/v2/sspu/pe.js @@ -0,0 +1,65 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const { id = '342' } = ctx.params; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 30; + + const rootUrl = 'https://pe2016.sspu.edu.cn'; + const currentUrl = new URL(`${id}/list.htm`, rootUrl).href; + + const { data: response } = await got(currentUrl); + + const $ = cheerio.load(response); + + let items = $('table.wp_article_list_table a[title]') + .slice(0, limit) + .toArray() + .map((item) => { + item = $(item); + + return { + title: item.text(), + link: new URL(item.prop('href'), rootUrl).href, + pubDate: parseDate(item.prev().text()), + }; + }); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + if (item.link.endsWith('htm')) { + const { data: detailResponse } = await got(item.link); + + const content = cheerio.load(detailResponse); + + const info = content('div.time').text(); + + item.title = content('div.title').text(); + item.description = content('div.wp_articlecontent').html(); + item.author = info.match(/来源:(.*?)\s/)?.[1] ?? undefined; + item.pubDate = info.match(/发布时间:(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s/)?.[1] ?? undefined; + } + + return item; + }) + ) + ); + + const author = '上海第二工业大学'; + const subtitle = $('title').text(); + const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; + + ctx.state.data = { + item: items, + title: `${author} - ${subtitle}`, + link: currentUrl, + description: $('div.tyb_headtitle1').text(), + language: $('html').prop('lang'), + icon, + logo: icon, + subtitle, + author, + }; +}; diff --git a/lib/v2/sspu/radar.js b/lib/v2/sspu/radar.js new file mode 100644 index 00000000000000..36fb4a379808ac --- /dev/null +++ b/lib/v2/sspu/radar.js @@ -0,0 +1,21 @@ +module.exports = { + 'sspu.edu.cn': { + _name: '上海第二工业大学', + jwc: [ + { + title: '教务处', + docs: 'https://docs.rsshub.app/university#shang-hai-di-er-gong-ye-da-xue', + source: ['/jwc/:listId/list.htm'], + target: '/sspu/jwc/:listId', + }, + ], + pe2016: [ + { + title: '体育部', + docs: 'https://docs.rsshub.app/university#shang-hai-di-er-gong-ye-da-xue-ti-yu-bu', + source: ['/:id/list.htm'], + target: '/sspu/pe/:id', + }, + ], + }, +}; diff --git a/lib/v2/sspu/router.js b/lib/v2/sspu/router.js new file mode 100644 index 00000000000000..4309b4dcbbb2de --- /dev/null +++ b/lib/v2/sspu/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/jwc/:listId', require('./jwc')); + router.get('/pe/:id?', require('./pe')); +}; diff --git a/lib/v2/stcn/index.js b/lib/v2/stcn/index.js index a8de7b98ca30eb..545ae1afdcc3a8 100644 --- a/lib/v2/stcn/index.js +++ b/lib/v2/stcn/index.js @@ -26,8 +26,8 @@ module.exports = async (ctx) => { const link = item.attr('href'); return { - title: item.text().replace(/(^【|】$)/g, ''), - link: /^http/.test(link) ? link : `${rootUrl}${link}`, + title: item.text().replaceAll(/(^【|】$)/g, ''), + link: link.startsWith('http') ? link : `${rootUrl}${link}`, }; }); diff --git a/lib/v2/stockedge/radar.js b/lib/v2/stockedge/radar.js index ab317927121e1a..334b92ed174373 100644 --- a/lib/v2/stockedge/radar.js +++ b/lib/v2/stockedge/radar.js @@ -4,7 +4,7 @@ module.exports = { web: [ { title: 'Daily Updates News', - docs: 'https://docs.rsshub.app/routes/en/finance#stock-edge', + docs: 'https://docs.rsshub.app/routes/finance#stock-edge', source: ['/daily-updates/news'], target: '/stockedge/daily-updates/news', }, diff --git a/lib/v2/stratechery/radar.js b/lib/v2/stratechery/radar.js index d0f6234940da8d..d93a23717ce543 100644 --- a/lib/v2/stratechery/radar.js +++ b/lib/v2/stratechery/radar.js @@ -1,10 +1,10 @@ module.exports = { - 'miris.design': { + 'stratechery.com': { _name: 'Stratechery by Ben Thompson', blog: [ { title: 'Articles', - docs: 'https://docs.rsshub.app/routes/en/blog#stratechery-by-ben-thompson', + docs: 'https://docs.rsshub.app/routes/blog#stratechery-by-ben-thompson', }, ], }, diff --git a/lib/v2/studygolang/utils.js b/lib/v2/studygolang/utils.js index 798d82b1cc4c1c..3036ded29e1926 100644 --- a/lib/v2/studygolang/utils.js +++ b/lib/v2/studygolang/utils.js @@ -10,7 +10,7 @@ const md = require('markdown-it')({ module.exports = { FetchGoItems: async (ctx) => { const id = ctx.params.id ?? 'weekly'; - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 50; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50; const rootUrl = 'https://studygolang.com'; const currentUrl = `${rootUrl}/go/${id}`; @@ -56,7 +56,7 @@ module.exports = { try { item.description = md.render(content('.content').html()); - } catch (e) { + } catch { // no-empty } diff --git a/lib/v2/subhd/index.js b/lib/v2/subhd/index.js index e0f3dd11c3d00a..e7e8cba9717cfa 100644 --- a/lib/v2/subhd/index.js +++ b/lib/v2/subhd/index.js @@ -46,7 +46,7 @@ module.exports = async (ctx) => { return { link: `${rootUrl}${item.attr('href')}`, author: item.parent().parent().find('.text-dark').last().text(), - pubDate: timezone(parseDate(pubDate.indexOf('-') > -1 ? pubDate : `${today} ${pubDate}`), +8), + pubDate: timezone(parseDate(pubDate.includes('-') ? pubDate : `${today} ${pubDate}`), +8), title: `${item.parent().parent().find('.align-middle').text()} ${item.text().replace(/ - SubHD/, '')}`, }; }); diff --git a/lib/v2/supchina/index.js b/lib/v2/supchina/index.js index 1d640bac06c290..778cc23c4eb49b 100644 --- a/lib/v2/supchina/index.js +++ b/lib/v2/supchina/index.js @@ -14,7 +14,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('item') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50) .toArray() .map((item) => { item = $(item); @@ -26,7 +26,7 @@ module.exports = async (ctx) => { author: item .find('dc\\:creator') .html() - .match(/CDATA\[(.*?)\]/)[1], + .match(/CDATA\[(.*?)]/)[1], category: item .find('category') .toArray() @@ -34,7 +34,7 @@ module.exports = async (ctx) => { (c) => $(c) .html() - .match(/CDATA\[(.*?)\]/)[1] + .match(/CDATA\[(.*?)]/)[1] ), pubDate: parseDate(item.find('pubDate').text()), }; diff --git a/lib/v2/supchina/podcasts.js b/lib/v2/supchina/podcasts.js index 2936243b1cb5b3..de26b2e6aa34d6 100644 --- a/lib/v2/supchina/podcasts.js +++ b/lib/v2/supchina/podcasts.js @@ -14,7 +14,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('item') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/surfshark/blog.js b/lib/v2/surfshark/blog.js new file mode 100644 index 00000000000000..26297e85c0c53d --- /dev/null +++ b/lib/v2/surfshark/blog.js @@ -0,0 +1,91 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (ctx) => { + const { category } = ctx.params; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 15; + + const rootUrl = 'https://surfshark.com'; + const currentUrl = new URL(`blog${category ? `/${category}` : ''}`, rootUrl).href; + + const { data: response } = await got(currentUrl); + + const $ = cheerio.load(response); + + let items = $('div.article-info') + .slice(0, limit) + .toArray() + .map((item) => { + item = $(item); + + const a = item.find('a'); + + return { + title: a.prop('title'), + link: a.prop('href'), + author: item.find('div.author, div.name').text().split('in')[0].trim(), + category: item + .find('div.author, div.name') + .find('a') + .toArray() + .map((c) => $(c).text()), + pubDate: parseDate(item.find('div.date, div.time').text().split('·')[0].trim(), 'YYYY, MMMM D'), + }; + }); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: detailResponse } = await got(item.link); + + const content = cheerio.load(detailResponse); + + content('div.post-main-img').each(function () { + const image = content(this).find('img'); + + content(this).replaceWith( + art(path.join(__dirname, 'templates/description.art'), { + image: { + src: image + .prop('srcset') + .match(/(https?:.*?\.\w+\s)/g) + .pop() + .trim(), + alt: image.prop('alt'), + }, + }) + ); + }); + + item.title = content('div.blog-post-title').text(); + item.description = content('div.post-content').html(); + item.author = content('meta[name="author"]').prop('content'); + item.category = content('div.name') + .find('a') + .toArray() + .map((c) => content(c).text()); + item.pubDate = parseDate(content('meta[property="article:published_time"]').prop('content')); + + return item; + }) + ) + ); + + const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href; + + ctx.state.data = { + item: items, + title: $('title').text(), + link: currentUrl, + description: $('meta[property="og:description"]').prop('content'), + language: $('html').prop('lang'), + image: $('meta[property="og:image"]').prop('content'), + icon, + logo: icon, + subtitle: $('meta[property="og:title"]').prop('content'), + author: $('meta[property="og:site_name"]').prop('content'), + }; +}; diff --git a/lib/v2/surfshark/maintainer.js b/lib/v2/surfshark/maintainer.js new file mode 100644 index 00000000000000..d8eab4f114cb81 --- /dev/null +++ b/lib/v2/surfshark/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/blog/:category?': ['nczitzk'], +}; diff --git a/lib/v2/surfshark/radar.js b/lib/v2/surfshark/radar.js new file mode 100644 index 00000000000000..4108f82db578fe --- /dev/null +++ b/lib/v2/surfshark/radar.js @@ -0,0 +1,90 @@ +module.exports = { + 'surfshark.com': { + _name: 'Surfshark', + '.': [ + { + title: 'Blog', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/blog/:category*'], + target: (params, url) => { + url = new URL(url); + const path = params.category ?? url.href.match(/\/blog\/(.*?)/)[1]; + + return `/surfshark/blog${path ? `/${path}` : ''}`; + }, + }, + { + title: 'Blog - Cybersecurity', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/cybersecurity'], + target: '/surfshark/blog/cybersecurity', + }, + { + title: 'Blog - All things VPN', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/all-things-vpn'], + target: '/surfshark/blog/all-things-vpn', + }, + { + title: 'Blog - Internet censorship', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/internet-censorship'], + target: '/surfshark/blog/internet-censorship', + }, + { + title: 'Blog - Entertainment', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/entertainment'], + target: '/surfshark/blog/entertainment', + }, + { + title: 'Blog - News', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/news'], + target: '/surfshark/blog/news', + }, + { + title: 'Blog - Internet Security', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/cybersecurity/internet-security', '/blog/cybersecurity'], + target: '/surfshark/blog/cybersecurity/internet-security', + }, + { + title: 'Blog - Mobile Security', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/cybersecurity/mobile-security', '/blog/cybersecurity'], + target: '/surfshark/blog/cybersecurity/mobile-security', + }, + { + title: 'Blog - Identity Protection', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/cybersecurity/identity-protection', '/blog/cybersecurity'], + target: '/surfshark/blog/cybersecurity/identity-protection', + }, + { + title: 'Blog - Phishing', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/cybersecurity/phishing', '/blog/cybersecurity'], + target: '/surfshark/blog/cybersecurity/phishing', + }, + { + title: 'Blog - Must-knows', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/all-things-vpn/must-knows', '/blog/all-things-vpn'], + target: '/surfshark/blog/all-things-vpn/must-knows', + }, + { + title: 'Blog - Technology', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/all-things-vpn/technology', '/blog/all-things-vpn'], + target: '/surfshark/blog/all-things-vpn/technology', + }, + { + title: 'Blog - Tips & Advice', + docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog', + source: ['/blog/all-things-vpn/tips-and-advice', '/blog/all-things-vpn'], + target: '/surfshark/blog/all-things-vpn/tips-and-advice', + }, + ], + }, +}; diff --git a/lib/v2/surfshark/router.js b/lib/v2/surfshark/router.js new file mode 100644 index 00000000000000..fff6a211fa8434 --- /dev/null +++ b/lib/v2/surfshark/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/blog/:category*', require('./blog')); +}; diff --git a/lib/v2/surfshark/templates/description.art b/lib/v2/surfshark/templates/description.art new file mode 100644 index 00000000000000..16a5adf2bac7e3 --- /dev/null +++ b/lib/v2/surfshark/templates/description.art @@ -0,0 +1,5 @@ +{{ if image }} +
+ {{ image.alt }} +
+{{ /if }} \ No newline at end of file diff --git a/lib/v2/swissinfo/index.js b/lib/v2/swissinfo/index.js index 11274e7714f3a0..429f793eb99e52 100644 --- a/lib/v2/swissinfo/index.js +++ b/lib/v2/swissinfo/index.js @@ -26,7 +26,7 @@ module.exports = async (ctx) => { $ = cheerio.load(fragmentResponse.data); let items = $('.si-teaser__link') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 20) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 20) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/swpu/utils.js b/lib/v2/swpu/utils.js index a4111e58fd4422..d5d1ec5ff8f263 100644 --- a/lib/v2/swpu/utils.js +++ b/lib/v2/swpu/utils.js @@ -11,7 +11,7 @@ function joinUrl(url1, url2) { url1 = url1 + '/'; } if (url2.startsWith('/')) { - url2 = url2.substr(1); + url2 = url2.slice(1); } return url1 + url2; diff --git a/lib/v2/syosetu/chapter.js b/lib/v2/syosetu/chapter.js index d3c58e3f567375..408bd8078a1eaf 100644 --- a/lib/v2/syosetu/chapter.js +++ b/lib/v2/syosetu/chapter.js @@ -9,7 +9,7 @@ cookieJar.setCookieSync('over18=yes', 'https://novel18.syosetu.com/'); module.exports = async (ctx) => { const id = ctx.params.id; - const limit = parseInt(ctx.query.limit) || 5; + const limit = Number.parseInt(ctx.query.limit) || 5; const link = `https://ncode.syosetu.com/${id}`; const $ = cheerio.load(await get(link)); diff --git a/lib/v2/sysu/cse.js b/lib/v2/sysu/cse.js index a2d93b8bdaedc4..a0e6c3d26557c8 100644 --- a/lib/v2/sysu/cse.js +++ b/lib/v2/sysu/cse.js @@ -73,20 +73,20 @@ module.exports = async (ctx) => { } const item_data = []; - for (let i = 0; i < block_index.length; i++) { - const block_news = $('#block-views-homepage-block-' + block_index[i].index + '> div > div.view-content > div > ul > li > a'); - for (let j = 0; j < block_news.length; j++) { - item_data.push(getDetail(block_news[j], block_index[i].description_header)); + for (const element of block_index) { + const block_news = $('#block-views-homepage-block-' + element.index + '> div > div.view-content > div > ul > li > a'); + for (const block_new of block_news) { + item_data.push(getDetail(block_new, element.description_header)); } } function compareLink(a, b) { let a_str = a.link; - a_str = a_str.substr(a_str.length - 4, 4); - const a_int = parseInt(a_str); + a_str = a_str.slice(-4, a_str.length - 4 + 4); + const a_int = Number.parseInt(a_str); let b_str = b.link; - b_str = b_str.substr(b_str.length - 4, 4); - const b_int = parseInt(b_str); + b_str = b_str.slice(-4, b_str.length - 4 + 4); + const b_int = Number.parseInt(b_str); return b_int - a_int; } // 使得新的通知排在前面,假设通知的发布和链接地址是相关的,而且链接地址都是"/content/4961"这样,只有四位数的。 diff --git a/lib/v2/sysu/ygafz.js b/lib/v2/sysu/ygafz.js index 150f4d4bb447b1..4dc2583440d21c 100644 --- a/lib/v2/sysu/ygafz.js +++ b/lib/v2/sysu/ygafz.js @@ -16,7 +16,7 @@ module.exports = async (ctx) => { request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort(); }); - logger.debug(`Requesting ${url}`); + logger.http(`Requesting ${url}`); await page.goto(url, { waitUntil: 'domcontentloaded', }); diff --git a/lib/v2/szse/inquire.js b/lib/v2/szse/inquire.js index c608021f1f6558..2aef59358fa83c 100644 --- a/lib/v2/szse/inquire.js +++ b/lib/v2/szse/inquire.js @@ -9,7 +9,7 @@ module.exports = async (ctx) => { const keyword = ctx.params.keyword ?? ''; const rootUrl = 'https://www.szse.cn'; - const currentUrl = `${rootUrl}/api/report/ShowReport/data?SHOWTYPE=JSON&CATALOGID=main_wxhj&TABKEY=tab${parseInt(category) + 2}${select === '全部函件类别' ? '' : `&selecthjlb=${select}`}${keyword ? `&txtZqdm=${keyword}` : ''}`; + const currentUrl = `${rootUrl}/api/report/ShowReport/data?SHOWTYPE=JSON&CATALOGID=main_wxhj&TABKEY=tab${Number.parseInt(category) + 2}${select === '全部函件类别' ? '' : `&selecthjlb=${select}`}${keyword ? `&txtZqdm=${keyword}` : ''}`; const response = await got({ method: 'get', diff --git a/lib/v2/szse/notice.js b/lib/v2/szse/notice.js index f83fbad6ff0672..f1d2635722c66d 100644 --- a/lib/v2/szse/notice.js +++ b/lib/v2/szse/notice.js @@ -10,37 +10,35 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); // 正则表达式匹配Script标签的url和title变量 function getData(jscontent, option) { - // eslint-disable-next-line - const urlpattern = /(?<=curHref \= \'\.\/).*?(?=\'\;)/; - // eslint-disable-next-line - const titlepattern = /(?<=curTitle \=\').*?(?=\'\;)/; + const urlpattern = /(?<=curHref = ').*?(?=';)/; + const titlePattern = /(?<=curTitle =').*?(?=';)/; switch (option) { case 'title': { - const jsoutput = jscontent.match(titlepattern); + const jsoutput = jscontent.match(titlePattern); return jsoutput; } case 'url': { const jsoutput = jscontent.match(urlpattern); return jsoutput; } - default: { + default: // console.log('default'); break; - } } } const list = $('.article-list .newslist li') - .map(function () { + .toArray() + .map((element) => { + element = $(element); const info = { - title: getData($(this).find('script').html(), 'title'), - link: `http://www.szse.cn/disclosure/notice/company/${getData($(this).find('script').html(), 'url')}`, - date: $(this).find('span.time').text().trim(), // trim移除多余的空格 + title: getData(element.find('script').text(), 'title'), + link: new URL(getData(element.find('script').text(), 'url'), link).href, + date: element.find('span.time').text().trim(), // trim移除多余的空格 }; return info; - }) - .get(); + }); const out = await Promise.all( list.map(async (info) => { const title = info.title; @@ -48,7 +46,7 @@ module.exports = async (ctx) => { const itemUrl = url.resolve(host, info.link); const cache = await ctx.cache.get(itemUrl); if (cache) { - return Promise.resolve(JSON.parse(cache)); + return JSON.parse(cache); } const response = await got.get(itemUrl, { Referer: host, @@ -62,7 +60,7 @@ module.exports = async (ctx) => { pubDate: new Date(date).toDateString(), }; ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); + return single; }) ); ctx.state.data = { diff --git a/lib/v2/szse/projectdynamic.js b/lib/v2/szse/projectdynamic.js index 8eda37ebbfb8e6..d38570e9051545 100644 --- a/lib/v2/szse/projectdynamic.js +++ b/lib/v2/szse/projectdynamic.js @@ -83,7 +83,7 @@ module.exports = async (ctx) => { ); ctx.state.data = { - title: `${typeMap[type]}项目动态${status !== '0' ? ` (${statusMap[status]}) ` : stage !== '0' ? ` (${stageMap[stage]}) ` : ''} - 创业板发行上市审核信息公开网站 - 深圳证券交易所`, + title: `${typeMap[type]}项目动态${status === '0' ? (stage === '0' ? '' : ` (${stageMap[stage]}) `) : ` (${statusMap[status]}) `} - 创业板发行上市审核信息公开网站 - 深圳证券交易所`, link: `${rootUrl}/projectdynamic/${type}/index.html`, item: items, }; diff --git a/lib/v2/szu/yz/index.js b/lib/v2/szu/yz/index.js index e7f5c0c2a06deb..2982b8c8e80ae9 100644 --- a/lib/v2/szu/yz/index.js +++ b/lib/v2/szu/yz/index.js @@ -8,7 +8,7 @@ const map = new Map([ ]); module.exports = async (ctx) => { - let type = parseInt(ctx.params.type); + let type = Number.parseInt(ctx.params.type); const struct = { 1: { selector: { diff --git a/lib/v2/tableau/maintainer.js b/lib/v2/tableau/maintainer.js new file mode 100644 index 00000000000000..0c70993a2e0c75 --- /dev/null +++ b/lib/v2/tableau/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/app/discover/viz-of-the-day': ['KaiyoungYu'], +}; diff --git a/lib/v2/tableau/radar.js b/lib/v2/tableau/radar.js new file mode 100644 index 00000000000000..6c561a00265a8c --- /dev/null +++ b/lib/v2/tableau/radar.js @@ -0,0 +1,11 @@ +module.exports = { + 'tableau.com': { + _name: 'Tableau', + public: [ + { + title: 'Viz of the day', + docs: 'https://docs.rsshub.app/routes/study#tableau', + }, + ], + }, +}; diff --git a/lib/v2/tableau/router.js b/lib/v2/tableau/router.js new file mode 100644 index 00000000000000..15d5d08c6b11ff --- /dev/null +++ b/lib/v2/tableau/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/viz-of-the-day', require('./viz-of-the-day')); +}; diff --git a/lib/v2/tableau/viz-of-the-day.js b/lib/v2/tableau/viz-of-the-day.js new file mode 100644 index 00000000000000..941cad7d4b9d9a --- /dev/null +++ b/lib/v2/tableau/viz-of-the-day.js @@ -0,0 +1,26 @@ +const got = require('@/utils/got'); +// const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + // Your logic here + const rootUrl = 'https://public.tableau.com/api/gallery?page=0&count=20&galleryType=viz-of-the-day'; + const { data: response } = await got(rootUrl); + + const items = response.items.map((item) => ({ + title: item.title, + link: item.sourceUrl, + pubDate: parseDate(item.galleryItemPublicationDate), + author: item.authorName, + description: `

${item.description}

`, + itunes_item_image: item.screenshot, + })); + + ctx.state.data = { + // Your RSS output here + title: 'Tableau Viz of the Day', + link: 'https://public.tableau.com/app/discover/viz-of-the-day', + image: 'https://help.tableau.com/current/pro/desktop/en-us/Resources/tableau-logo.png', + item: items, + }; +}; diff --git a/lib/v2/taiwannews/hot.js b/lib/v2/taiwannews/hot.js index 23b78ef3d895b6..dac02c329a6aed 100644 --- a/lib/v2/taiwannews/hot.js +++ b/lib/v2/taiwannews/hot.js @@ -2,9 +2,9 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); const timezone = require('@/utils/timezone'); -const baseUrl = 'https://www.taiwannews.com.tw'; module.exports = async (ctx) => { + const baseUrl = 'https://www.taiwannews.com.tw'; const { lang = 'en' } = ctx.params; const url = `${baseUrl}/${lang}/index`; const response = await got(url); @@ -14,13 +14,14 @@ module.exports = async (ctx) => { .toArray() .map((item) => { item = $(item); - const a = item.find('a'); + const a = item.find('.entry-header a').first(); return { title: a.attr('title'), link: new URL(a.attr('href'), baseUrl).href, pubDate: timezone(parseDate(item.find('.entry-date span').eq(1).text(), 'YYYY/MM/DD HH:mm'), +8), }; - }); + }) + .filter((item) => item.title); const items = await Promise.all( list.map((item) => diff --git a/lib/v2/tangshufang/index.js b/lib/v2/tangshufang/index.js index 2837fd1c4de69f..b8847bba835ee9 100644 --- a/lib/v2/tangshufang/index.js +++ b/lib/v2/tangshufang/index.js @@ -4,7 +4,7 @@ const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { const category = ctx.params.category ?? ''; - const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 10; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit) : 10; const rootUrl = 'https://www.tangshufang.com'; const currentUrl = `${rootUrl}${category ? `/${category}` : ''}`; diff --git a/lib/v2/taoguba/blog.js b/lib/v2/taoguba/blog.js index 87d88fb92535d1..b0991606d9e4a3 100644 --- a/lib/v2/taoguba/blog.js +++ b/lib/v2/taoguba/blog.js @@ -18,7 +18,7 @@ module.exports = async (ctx) => { const author = $('meta[property="og:author"]').attr('content'); let items = $('.tittle_data') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 50) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/taoguba/index.js b/lib/v2/taoguba/index.js index a7afd03a44a77a..a22bd37cb32e46 100644 --- a/lib/v2/taoguba/index.js +++ b/lib/v2/taoguba/index.js @@ -17,7 +17,7 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); let items = $('.items-comment-list') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 70) + .slice(0, ctx.query.limit ? Number.parseInt(ctx.query.limit) : 70) .toArray() .map((item) => { item = $(item); diff --git a/lib/v2/tass/maintainer.js b/lib/v2/tass/maintainer.js new file mode 100644 index 00000000000000..9644afe025ca72 --- /dev/null +++ b/lib/v2/tass/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/:category?': ['TonyRL'], +}; diff --git a/lib/v2/tass/news.js b/lib/v2/tass/news.js new file mode 100644 index 00000000000000..443ad117d21da1 --- /dev/null +++ b/lib/v2/tass/news.js @@ -0,0 +1,58 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const { category = 'politics' } = ctx.params; + + const { data: categoryPage, url: link } = await got(`https://tass.com/${category}`); + const $ = cheerio.load(categoryPage); + + const sectionId = $('.container .section-page') + .attr('ng-init') + .match(/sectionId\s*=\s*(\d+?);/); + + const { data: response } = await got.post('https://tass.com/userApi/categoryNewsList', { + json: { + sectionId: sectionId[1], + limit: 20, + type: 'all', + }, + }); + + const list = response.newsList.map((item) => ({ + title: item.title, + description: item.lead, + link: `https://tass.com${item.link}`, + pubDate: parseDate(item.date, 'X'), + })); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: response } = await got(item.link); + const $ = cheerio.load(response); + + $('.news-media img').each((_, ele) => { + if (ele.attribs.src) { + ele.attribs.src = ele.attribs.src.replaceAll('/width/1020_b9261fa1', ''); + } + }); + + item.description = $('.news-header__lead').prop('outerHTML') + ($('.news-media').prop('outerHTML') ?? '') + $('.text-block').html(); + + return item; + }) + ) + ); + + ctx.state.data = { + title: $('head title').text(), + link, + language: 'en', + image: $('head meta[property="og:image"]').attr('content'), + icon: $('head link[rel="apple-touch-icon"]').attr('href'), + logo: $('head link[rel="apple-touch-icon"]').attr('href'), + item: items, + }; +}; diff --git a/lib/v2/tass/radar.js b/lib/v2/tass/radar.js new file mode 100644 index 00000000000000..4e3f016b1c919d --- /dev/null +++ b/lib/v2/tass/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'tass.com': { + _name: 'Russian News Agency TASS', + '.': [ + { + title: 'News', + docs: 'https://docs.rsshub.app/routes/traditional-media.html#russian-news-agency-tass', + source: ['/:category'], + target: '/tass/:category', + }, + ], + }, +}; diff --git a/lib/v2/tass/router.js b/lib/v2/tass/router.js new file mode 100644 index 00000000000000..c44c1d758a44a7 --- /dev/null +++ b/lib/v2/tass/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/:category?', require('./news')); +}; diff --git a/lib/v2/techcrunch/maintainer.js b/lib/v2/techcrunch/maintainer.js index e2844fa7365287..3f5cb89d7cf285 100644 --- a/lib/v2/techcrunch/maintainer.js +++ b/lib/v2/techcrunch/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/news': ['NavePnow'], + '/news': ['EthanWng97'], }; diff --git a/lib/v2/techpowerup/maintainer.js b/lib/v2/techpowerup/maintainer.js index 388438c4c8a5df..8ed3f79bfe36f9 100644 --- a/lib/v2/techpowerup/maintainer.js +++ b/lib/v2/techpowerup/maintainer.js @@ -1,5 +1,4 @@ module.exports = { - '': ['TonyRL'], '/': ['TonyRL'], '/review/:keyword?': ['TonyRL'], }; diff --git a/lib/v2/techpowerup/radar.js b/lib/v2/techpowerup/radar.js index 5b41baf49e12ab..973fc0dbf8ed45 100644 --- a/lib/v2/techpowerup/radar.js +++ b/lib/v2/techpowerup/radar.js @@ -4,13 +4,13 @@ module.exports = { '.': [ { title: 'Latest Content', - docs: 'https://docs.rsshub.app/routes/en/new-media#techpowerup', + docs: 'https://docs.rsshub.app/routes/new-media#techpowerup', source: ['/'], target: '/techpowerup', }, { title: 'Reviews', - docs: 'https://docs.rsshub.app/routes/en/new-media#techpowerup', + docs: 'https://docs.rsshub.app/routes/new-media#techpowerup', source: ['/review/search', '/review'], target: (_, url) => `/techpowerup/review${new URL(url).searchParams.has('q') ? `/${new URL(url).searchParams.get('q')}` : ''}`, }, diff --git a/lib/v2/telecompaper/news.js b/lib/v2/telecompaper/news.js index f35654d92d7ca1..f4d3a563492dcb 100644 --- a/lib/v2/telecompaper/news.js +++ b/lib/v2/telecompaper/news.js @@ -6,7 +6,7 @@ const FormData = require('form-data'); module.exports = async (ctx) => { const rootUrl = `https://www.telecompaper.com/${ctx.params.caty === 'industry-resources' ? ctx.params.caty : 'international/news/' + ctx.params.caty}`; - const year = ctx.params.year ? ctx.params.year : 'all'; + const year = ctx.params.year ?? 'all'; const country = ctx.params.country ? ctx.params.country.split('-').join(' ') : 'all'; const type = ctx.params.type ? ctx.params.type.split('-').join(' ') : 'all'; diff --git a/lib/v2/telecompaper/search.js b/lib/v2/telecompaper/search.js index de2ac6317b19c2..98c54c3e511ad2 100644 --- a/lib/v2/telecompaper/search.js +++ b/lib/v2/telecompaper/search.js @@ -27,10 +27,10 @@ module.exports = async (ctx) => { ctl00$header$searchTextMobile: 'Search keywords', ctl00$MainPlaceHolder$SearchText: keyword, ctl00$MainPlaceHolder$CompanyText: company, - ctl00$MainPlaceHolder$Sort: parseInt(sort), + ctl00$MainPlaceHolder$Sort: Number.parseInt(sort), ctl00$MainPlaceHolder$Results: 20, ctl00$MainPlaceHolder$Date: 'Timeframe1', - ctl00$MainPlaceHolder$Period: parseInt(period), + ctl00$MainPlaceHolder$Period: Number.parseInt(period), ctl00$MainPlaceHolder$txtStartDate: '', ctl00$MainPlaceHolder$txtEndDate: '', ctl00$MainPlaceHolder$chkEnglish: 'on', diff --git a/lib/v2/telegram/channel.js b/lib/v2/telegram/channel.js index 3c6a7b73b08d3f..ee3909a8366ab9 100644 --- a/lib/v2/telegram/channel.js +++ b/lib/v2/telegram/channel.js @@ -100,7 +100,7 @@ module.exports = async (ctx) => { : $('.tgme_widget_message_wrap:not(.tgme_widget_message_wrap:has(.service_message,.tme_no_messages_found))'); // also exclude service messages if (list.length === 0 && $('.tgme_channel_history').length === 0) { - throw `Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}`; + throw new Error(`Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}`); } const channelName = $('.tgme_channel_info_header_title').text(); @@ -225,7 +225,9 @@ module.exports = async (ctx) => { /* reply */ const replyContent = () => { const replyObj = item.find('.tgme_widget_message_reply'); - if (replyObj.length !== 0) { + if (replyObj.length === 0) { + return ''; + } else { const replyAuthorObj = replyObj.find('.tgme_widget_message_author_name'); const replyAuthor = replyAuthorObj.length ? replyAuthorObj.text() : ''; const viaBotObj = replyObj.find('.tgme_widget_message_via_bot'); @@ -245,21 +247,17 @@ module.exports = async (ctx) => { }, ], }; - if (replyLink !== '') { - return `
-

${replyAuthor}${viaBotText}:

+ return replyLink === '' + ? `
+

${replyAuthor}${viaBotText}:

${replyMetaText} ${replyText} -
`; - } else { - return `
-

${replyAuthor}${viaBotText}:

+
` + : `
+

${replyAuthor}${viaBotText}:

${replyMetaText} ${replyText}
`; - } - } else { - return ''; } }; @@ -381,7 +379,7 @@ module.exports = async (ctx) => { let second = 0, minute = 1; while (p.length > 0) { - second += minute * parseInt(p.pop(), 10); + second += minute * Number.parseInt(p.pop(), 10); minute *= 60; } return second.toString(); @@ -395,21 +393,14 @@ module.exports = async (ctx) => { const linkPreviewSiteObj = item.find('.link_preview_site_name'); const linkPreviewSite = linkPreviewSiteObj.length ? `${linkPreviewSiteObj.text()}
` : ''; const linkPreviewTitleObj = item.find('.link_preview_title'); - let linkPreviewTitle; - if (linkPreviewTitleObj.length) { - linkPreviewTitle = `${linkPreviewTitleObj.text()}
`; - } else { - linkPreviewTitle = ''; - } + const linkPreviewTitle = linkPreviewTitleObj.length ? `${linkPreviewTitleObj.text()}
` : ''; const linkPreviewDescriptionObj = item.find('.link_preview_description'); const linkPreviewDescription = linkPreviewDescriptionObj.length ? `

${linkPreviewDescriptionObj.html()}

` : ''; const linkPreviewImage = generateMedia('.link_preview_image') + generateMedia('.link_preview_right_image'); - if (linkPreviewSite.length > 0 || linkPreviewTitle.length > 0 || linkPreviewDescription.length > 0 || linkPreviewImage.length > 0) { - return `
${linkPreviewSite}${linkPreviewTitle}${linkPreviewDescription}${linkPreviewImage}
`; - } else { - return ''; - } + return linkPreviewSite.length > 0 || linkPreviewTitle.length > 0 || linkPreviewDescription.length > 0 || linkPreviewImage.length > 0 + ? `
${linkPreviewSite}${linkPreviewTitle}${linkPreviewDescription}${linkPreviewImage}
` + : ''; }; /* poll */ @@ -522,11 +513,11 @@ module.exports = async (ctx) => { /* media tag */ let mediaTag = ''; if (showMediaTagInTitle) { - msgTypes.forEach((type) => { + for (const type of msgTypes) { if (type !== REPLY || type !== FORWARDED || type !== VIA_BOT || (type === REPLY && showReplyTo) || (type === FORWARDED && showFwdFrom) || (type === VIA_BOT && showViaBot)) { mediaTag += showMediaTagAsEmoji ? mediaTagDict[type][1] : mediaTagDict[type][0]; } - }); + } } /* message text & title */ @@ -558,15 +549,11 @@ module.exports = async (ctx) => { if (messageTextObj.length > 0 && !titleCompleteFlag) { const _messageTextObj = $(messageTextObj.toString()); _messageTextObj.find('br').replaceWith('\n'); - const trimmedTitleText = _messageTextObj.text().replace(/\n/g, ' ').trim(); + const trimmedTitleText = _messageTextObj.text().replaceAll('\n', ' ').trim(); messageTitle += (messageTitle && trimmedTitleText ? ': ' : '') + trimmedTitleText; } - if (messageTitle === '') { - messageTitle = mediaTag || pubDate.toUTCString(); - } else { - messageTitle = `${mediaTag}${mediaTag ? ' ' : ''}${messageTitle}`; - } + messageTitle = messageTitle === '' ? mediaTag || pubDate.toUTCString() : `${mediaTag}${mediaTag ? ' ' : ''}${messageTitle}`; let description = ''; if (showFwdFrom) { @@ -599,7 +586,7 @@ module.exports = async (ctx) => { }; }) .get() - .filter((item) => item) + .filter(Boolean) .reverse(), }; }; diff --git a/lib/v2/telegram/maintainer.js b/lib/v2/telegram/maintainer.js index bf1397495a0dd4..2a2fb25eb5c958 100644 --- a/lib/v2/telegram/maintainer.js +++ b/lib/v2/telegram/maintainer.js @@ -1,5 +1,5 @@ module.exports = { - '/telegram/channel/:username/:routeParams?': ['DIYgod', 'Rongronggg9'], - '/telegram/stickerpack/:name': ['DIYgod'], - '/telegram/blog': ['fengkx'], + '/blog': ['fengkx'], + '/channel/:username/:routeParams?': ['DIYgod', 'Rongronggg9'], + '/stickerpack/:name': ['DIYgod'], }; diff --git a/lib/v2/telegram/stickerpack.js b/lib/v2/telegram/stickerpack.js index 2a64f170f652a4..42072b7ea88fa0 100644 --- a/lib/v2/telegram/stickerpack.js +++ b/lib/v2/telegram/stickerpack.js @@ -3,7 +3,7 @@ const config = require('@/config').value; module.exports = async (ctx) => { if (!config.telegram || !config.telegram.token) { - throw 'Telegram Sticker Pack RSS is disabled due to the lack of relevant config'; + throw new Error('Telegram Sticker Pack RSS is disabled due to the lack of relevant config'); } const name = ctx.params.name; diff --git a/lib/v2/tencent/cloud/column.js b/lib/v2/tencent/cloud/column.js deleted file mode 100644 index 60aca82c49436b..00000000000000 --- a/lib/v2/tencent/cloud/column.js +++ /dev/null @@ -1,51 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); - -module.exports = async (ctx) => { - const id = ctx.params.id ?? '86410'; - const tag = ctx.params.tag ?? ''; - - const rootUrl = 'https://cloud.tencent.com'; - const currentUrl = `${rootUrl}/developer/column/${id}${tag ? `/tag-${tag}` : ''}`; - - const response = await got({ - method: 'get', - url: currentUrl, - }); - - const data = JSON.parse(response.data.match(/window\.\$render\((.*?)\);/)[1]); - - let items = data.articleData.list.map((item) => ({ - title: item.title, - author: item.author.name, - link: `${rootUrl}/developer/article/${item.id}`, - pubDate: parseDate(item.updateTime * 1000), - category: item.tags.map((tag) => tag.name), - })); - - items = await Promise.all( - items.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - - const content = cheerio.load(detailResponse.data); - - content('.developer-code-block').remove(); - - item.description = content('.rno-markdown').html(); - - return item; - }) - ) - ); - - ctx.state.data = { - title: `${data.columnDetail.name} - ${data.documentBaseTitle}`, - link: currentUrl, - item: items, - }; -}; diff --git a/lib/v2/tencent/maintainer.js b/lib/v2/tencent/maintainer.js index ea5271f5c4224d..c25b41019655cc 100644 --- a/lib/v2/tencent/maintainer.js +++ b/lib/v2/tencent/maintainer.js @@ -1,5 +1,4 @@ module.exports = { - '/cloud/column/:id?/:tag?': ['nczitzk'], '/news/author/:mid': ['LogicJake', 'miles170'], '/news/coronavirus/data/:province?/:city?': ['CaoMeiYouRen'], '/news/coronavirus/total': ['CaoMeiYouRen'], diff --git a/lib/v2/tencent/news/author.js b/lib/v2/tencent/news/author.js index 6921419346e3b3..290fbf6a0c279e 100644 --- a/lib/v2/tencent/news/author.js +++ b/lib/v2/tencent/news/author.js @@ -1,8 +1,9 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); -const timezone = require('@/utils/timezone'); const config = require('@/config').value; +const { art } = require('@/utils/render'); +const path = require('path'); module.exports = async (ctx) => { const mid = ctx.params.mid; @@ -18,24 +19,47 @@ module.exports = async (ctx) => { const items = await Promise.all( news.map((item) => { const title = item.title; - const pubDate = timezone(parseDate(item.time), +8); + const pubDate = parseDate(item.timestamp, 'X'); const itemUrl = item.url; const author = item.source; const abstract = item.abstract; - return ctx.cache.tryGet(itemUrl, async () => { - const response = await got(itemUrl); - const $ = cheerio.load(response.data); - const article = $('#ArticleContent'); + return item.articletype === '4' + ? { + title, + description: abstract, + link: itemUrl, + author, + pubDate, + } + : ctx.cache.tryGet(itemUrl, async () => { + const response = await got(itemUrl); + const $ = cheerio.load(response.data); + const data = JSON.parse( + $('script:contains("window.DATA")') + .text() + .match(/window\.DATA = ({.+});/)[1] + ); + const $data = cheerio.load(data.originContent.text, null, false); - return { - title, - description: article.html() || abstract, - link: itemUrl, - author, - pubDate, - }; - }); + $data('*') + .contents() + .filter((_, elem) => elem.type === 'comment') + .replaceWith((_, elem) => + art(path.join(__dirname, '../templates/news/image.art'), { + attribute: elem.data.trim(), + originAttribute: data.originAttribute, + }) + ); + + return { + title, + description: $data.html() || abstract, + link: itemUrl, + author, + pubDate, + }; + }); }) ); diff --git a/lib/v2/tencent/pvp/newsindex.js b/lib/v2/tencent/pvp/newsindex.js index 58f2e64850163e..6f52505c6156bb 100644 --- a/lib/v2/tencent/pvp/newsindex.js +++ b/lib/v2/tencent/pvp/newsindex.js @@ -45,12 +45,14 @@ module.exports = async (ctx) => { if (type === 'all') { const results = await Promise.all( - Array.from(map).map(async (value) => { + [...map].map(async (value) => { const res = await getPage(value[1].channelid, value[1].name); return res; }) ); - results.forEach((result) => (item = item.concat(result))); + for (const result of results) { + item = [...item, ...result]; + } } else { item = await getPage(OutId, OutName); } diff --git a/lib/v2/tencent/qq/sdk/changelog.js b/lib/v2/tencent/qq/sdk/changelog.js index a1fd1efa50cc61..88243e83def83e 100644 --- a/lib/v2/tencent/qq/sdk/changelog.js +++ b/lib/v2/tencent/qq/sdk/changelog.js @@ -13,7 +13,7 @@ module.exports = async (ctx) => { title = 'Android SDK 历史变更'; link = 'https://wiki.connect.qq.com/android_sdk历史变更'; } else { - throw Error('not support platform'); + throw new Error('not support platform'); } const response = await got.get(link); diff --git a/lib/v2/tencent/radar.js b/lib/v2/tencent/radar.js index f7c5ec37e9b508..736bff1a47d71b 100644 --- a/lib/v2/tencent/radar.js +++ b/lib/v2/tencent/radar.js @@ -1,24 +1,12 @@ module.exports = { 'qq.com': { _name: '腾讯', - egame: [ - { - title: '企鹅电竞直播间', - docs: 'https://docs.rsshub.app/routes/live#qi-e-dian-jing-zhi-bo-jian-kai-bo', - source: '/:id', - target: (params) => { - if (params.id.match(/^\d+$/)) { - return '/egameqq/room/:id'; - } - }, - }, - ], 'mp.weixin': [ { title: '微信公众号栏目', docs: 'https://docs.rsshub.app/routes/new-media#gong-zhong-hao-lan-mu-fei-tui-song-li-shi-xiao-xi', source: '/mp/homepage', - target: (params, url) => `/wechat/mp/homepage/${new URL(url).searchParams.get('__biz')}/${new URL(url).searchParams.get('hid')}/${new URL(url).searchParams.get('cid') ? new URL(url).searchParams.get('cid') : ''}`, + target: (params, url) => `/wechat/mp/homepage/${new URL(url).searchParams.get('__biz')}/${new URL(url).searchParams.get('hid')}/${new URL(url).searchParams.get('cid') ?? ''}`, }, { title: '微信公众号话题', @@ -49,23 +37,6 @@ module.exports = { target: '/tencent/pvp/newsindex/all', }, ], - v: [ - { - title: '视频 - 播放列表', - docs: 'https://docs.rsshub.app/routes/multimedia#teng-xun-shi-pin', - source: '/x/cover/:id', - target: (params) => { - const id = params.id.match('(.*).html')[1]; - return id ? `/tencentvideo/playlist/${id}` : ''; - }, - }, - { - title: '视频 - 播放列表', - docs: 'https://docs.rsshub.app/routes/multimedia#teng-xun-shi-pin', - source: '/x/cover/:id/:detail', - target: '/tencentvideo/playlist/:id', - }, - ], 'wiki.connect': [ { title: 'QQ 互联 SDK 更新日志', @@ -75,15 +46,4 @@ module.exports = { }, ], }, - 'tencent.com': { - _name: '腾讯云', - '.': [ - { - title: '云+社区专栏', - docs: 'https://docs.rsshub.app/routes/programming#teng-xun-yun-yun-she-qu-zhuan-lan', - source: ['/developer/column/:id', '/developer/column/:id/:tag', '/'], - target: (params, url) => `/tencent/cloud/column/${url.match(/column\/(\d+)/)[1]}${/\/tag-\d+/.test(url) ? `/${url.match(/\/tag-(\d+)/)[1]}` : ''}`, - }, - ], - }, }; diff --git a/lib/v2/tencent/router.js b/lib/v2/tencent/router.js index 560eca80149c7e..7a9f334944abc0 100644 --- a/lib/v2/tencent/router.js +++ b/lib/v2/tencent/router.js @@ -1,5 +1,4 @@ module.exports = (router) => { - router.get('/cloud/column/:id?/:tag?', require('./cloud/column')); router.get('/news/author/:mid', require('./news/author')); router.get('/news/coronavirus/data/:province?/:city?', require('./news/coronavirus/data')); router.get('/news/coronavirus/total', require('./news/coronavirus/total')); diff --git a/lib/v2/tencent/templates/news/image.art b/lib/v2/tencent/templates/news/image.art new file mode 100644 index 00000000000000..45d65d2ecb2edc --- /dev/null +++ b/lib/v2/tencent/templates/news/image.art @@ -0,0 +1,4 @@ +{{ if attribute?.startsWith('IMG') && originAttribute[attribute] }} + {{ set image = originAttribute[attribute] }} + +{{ /if }} diff --git a/lib/v2/tesla/cx.js b/lib/v2/tesla/cx.js new file mode 100644 index 00000000000000..ef8d4d3c78501b --- /dev/null +++ b/lib/v2/tesla/cx.js @@ -0,0 +1,119 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (ctx) => { + const { category, city } = ctx.params; + const limit = ctx.query.limit ? Number.parseInt(ctx.query.limit, 10) : 10; + + const rootUrl = 'https://cx.tesla.cn'; + const rootApiUrl = 'https://community-api.tesla.cn'; + const rootMediaApi = 'https://china-community-app.tesla.cn'; + + const currentUrl = new URL(`user-right/list${category ? `/${category}` : ''}`, rootUrl).href; + const apiUrl = new URL('api/voucherpackage/merchant', rootApiUrl).href; + const apiCategoryUrl = new URL('api/category', rootApiUrl).href; + + const categoryToUrl = (category) => new URL(`user-right/list/${category}`, rootUrl).href; + const mediaToUrl = (media) => new URL(`community-media/${media}`, rootMediaApi).href; + + art.defaults.imports.categoryToUrl = categoryToUrl; + art.defaults.imports.mediaToUrl = mediaToUrl; + + const { data: categoryResponse } = await got(apiCategoryUrl, { + searchParams: { + type: 2, + }, + }); + + const categoryObject = categoryResponse.data.filter((c) => c.name === category).pop(); + + const { data: response } = await got(apiUrl, { + searchParams: { + pageSize: limit, + pageNumber: 0, + benefitCategoryId: categoryObject?.id ?? undefined, + category: categoryObject ? undefined : category === '充电免停' ? 2 : undefined, + city, + }, + }); + + let items = response.data.pageDatas.slice(0, limit).map((item) => ({ + title: item.venueName ?? item.title, + link: new URL(`user-right/detail/${item.id}`, rootUrl).href, + description: art(path.join(__dirname, 'templates/description.art'), { + image: item.coverImage + ? { + src: item.coverImage, + alt: item.venueName ?? item.title, + } + : undefined, + description: item.description?.replace(/\["|"]/g, '') ?? undefined, + data: item.parkingLocationId + ? { + title: item.venueName ?? item.title, + categories: [category], + description: `充电停车减免${item.parkingVoucherValue}小时`, + } + : undefined, + }), + category: item.categories, + guid: item.id, + pubDate: parseDate(item.publishedAt), + parkingLocationId: item.parkingLocationId, + })); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + if (item.parkingLocationId) { + item.guid = `tesla-user-right#${item.guid}`; + + delete item.parkingLocationId; + + return item; + } + + const apiItemUrl = new URL(`api/voucherpackage/merchant/${item.guid}`, rootApiUrl).href; + + const { data: detailResponse } = await got(apiItemUrl); + + const data = detailResponse.data; + + item.title = data.title ?? item.title; + item.description = art(path.join(__dirname, 'templates/description.art'), { + data, + }); + item.author = data.merchants ? data.merchants.map((a) => a.name).join('/') : undefined; + item.category = [...new Set([...item.category, ...data.categories])].filter(Boolean); + item.guid = `tesla-user-right#${item.guid}`; + + return item; + }) + ) + ); + + const { data: currentResponse } = await got(currentUrl); + + const $ = cheerio.load(currentResponse); + + const author = $('title').text(); + const description = `${city ?? ''}${category ?? ''}`; + const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; + + ctx.state.data = { + item: items, + title: `${author}权益中心${description ? ` - ${description}` : ''}`, + link: currentUrl, + description, + language: $('html').prop('lang'), + image: $('meta[property="og:image"]').prop('content'), + icon, + logo: icon, + subtitle: description, + author, + allowEmpty: true, + }; +}; diff --git a/lib/v2/tesla/maintainer.js b/lib/v2/tesla/maintainer.js index 62160fe89ead79..f5e80a808d6dc3 100644 --- a/lib/v2/tesla/maintainer.js +++ b/lib/v2/tesla/maintainer.js @@ -1,3 +1,4 @@ module.exports = { '/price': ['xiaokyo'], + '/cx/:category?/:city?': ['simonsmh', 'nczitzk'], }; diff --git a/lib/v2/tesla/price/get-price.js b/lib/v2/tesla/price/get-price.js index 6a544ba6871487..476bc816705345 100644 --- a/lib/v2/tesla/price/get-price.js +++ b/lib/v2/tesla/price/get-price.js @@ -10,7 +10,7 @@ async function getTeslaPrice(link) { for (let i = 0; i < $('p').length; i++) { const $ele = $('p').eq(i); - if ($ele.text().trim().indexOf('经销商报价') > -1) { + if ($ele.text().trim().includes('经销商报价')) { const priceDom = $ele.next(); price = ' => 经销商报价: ' + $(priceDom).text().trim(); break; diff --git a/lib/v2/tesla/radar.js b/lib/v2/tesla/radar.js index b3a44de74bf9bc..5e914a99aa8220 100644 --- a/lib/v2/tesla/radar.js +++ b/lib/v2/tesla/radar.js @@ -4,10 +4,22 @@ module.exports = { '.': [ { title: '价格', - docs: 'https://docs.rsshub.app/routes/shopping#te-si-la-zhong-guo', + docs: 'https://docs.rsshub.app/routes/shopping#te-si-la-zhong-guo-jia-ge', source: ['/model3/design', '/'], target: '/tesla/price', }, ], + cx: [ + { + title: '权益中心', + docs: 'https://docs.rsshub.app/routes/shopping#te-si-la-zhong-guo-quan-yi-zhong-xin', + source: ['/user-right/list/:category'], + target: (params) => { + const category = params.category; + + return `/tesla/cx${category ? `/${category}` : ''}`; + }, + }, + ], }, }; diff --git a/lib/v2/tesla/router.js b/lib/v2/tesla/router.js index d76b305b3e6d24..d04471d8a7fa17 100644 --- a/lib/v2/tesla/router.js +++ b/lib/v2/tesla/router.js @@ -1,3 +1,4 @@ module.exports = (router) => { + router.get('/cx/:category?/:city?', require('./cx')); router.get('/price', require('./price')); }; diff --git a/lib/v2/tesla/templates/description.art b/lib/v2/tesla/templates/description.art new file mode 100644 index 00000000000000..c148b3751705f7 --- /dev/null +++ b/lib/v2/tesla/templates/description.art @@ -0,0 +1,140 @@ +{{ if data }} + {{ if data.images }} + {{ each data.images image }} +
+ +
+ {{ /each }} + {{ /if }} + + {{ if data.categories }} + {{ each data.categories category }} + + {{ category }} + + {{ /each }} + {{ /if }} + + {{ if data.description }} +

+ {{ data.description }} +

+ {{ /if }} + + {{ if data.vouchers }} +

+ 相关权益 +

+
    + {{ each data.vouchers voucher }} +
  • +
    + {{ if voucher.title }} +

    + {{ voucher.title }} +

    + {{ /if }} + {{ if voucher.description }} +

    + 使用说明 +

    + {{@ voucher.description }} + {{ /if }} + {{ if voucher.activateDate || voucher.expirationDate }} +

    + 有效期 +

    + {{ if voucher.activateDate }} + + {{ voucher.activateDate.split(/T/)[0] }} + 起 + {{ /if }} + {{ if voucher.expirationDate }} + + {{ voucher.expirationDate.split(/T/)[0] }} + 止 + {{ /if }} + {{ /if }} + {{ if voucher.totalLimit }} +

    + 总计 +

    +

    + {{ voucher.totalLimit }} +

    + {{ /if }} +

    + 状态 +

    +

    + {{ if voucher.hasSurplus }} + 可领取 + {{ else }} + 已领完 + {{ /if }} +

    +
    +
  • + {{ /each }} +
+ {{ /if }} + + {{ if data.merchants }} +

+ 适用门店 +

+
    + {{ each data.merchants merchant }} +
  • +
    + {{ if merchant.name }} +

    + {{ merchant.name }} +

    + {{ /if }} + {{ if merchant.province }} + + {{ merchant.province }} + + {{ /if }} + {{ if merchant.city && merchant.city !== merchant.province }} + + {{ merchant.city }} + + {{ /if }} + {{ if merchant.area && merchant.area !== merchant.city }} + + {{ merchant.area }} + + {{ /if }} + {{ if merchant.address }} + + {{ merchant.address }} + + {{ /if }} + {{ if merchant.contact }} +

    + + {{ merchant.contact }} + +

    + {{ /if }} +
    +
  • + {{ /each }} +
+ {{ /if }} + +{{ else }} + + {{ if image }} +
+ {{ image.alt }} +
+ {{ /if }} + + {{ if description }} + {{@ description }} + {{ /if }} + +{{ /if }} \ No newline at end of file diff --git a/lib/v2/test/index.js b/lib/v2/test/index.js index 860dabc52a26fd..c79739db216008 100644 --- a/lib/v2/test/index.js +++ b/lib/v2/test/index.js @@ -6,7 +6,7 @@ let cacheIndex = 0; module.exports = async (ctx) => { if (ctx.params.id === 'error') { - throw Error('Error test'); + throw new Error('Error test'); } if (ctx.params.id === 'httperror') { await got({ @@ -15,93 +15,122 @@ module.exports = async (ctx) => { }); } let item = []; - if (ctx.params.id === 'filter') { - item = [ - { - title: 'Filter Title1', - description: 'Description1', + switch (ctx.params.id) { + case 'filter': + item = [ + { + title: 'Filter Title1', + description: 'Description1', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/-1`, + author: `DIYgod0`, + category: ['Category0', 'Category1'], + }, + { + title: 'Filter Title2', + description: 'Description2', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/0`, + author: `DIYgod0`, + category: ['Category0', 'Category1', 'Category2'], + }, + { + title: 'Filter Title3', + description: 'Description3', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/1`, + author: `DIYgod0`, + category: 'Category3', + }, + ]; + + break; + + case 'filter-illegal-category': + item.push({ + title: 'TitleIllegal', + description: 'DescriptionIllegal', pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/-1`, + link: `https://github.com/DIYgod/RSSHub/issues/1`, author: `DIYgod0`, - category: ['Category0', 'Category1'], - }, - { - title: 'Filter Title2', - description: 'Description2', + category: [1, 'CategoryIllegal', true, null, undefined, { type: 'object' }], + }); + + break; + + case 'long': + item.push({ + title: `Long Title `.repeat(50), + description: `Long Description `.repeat(10), pubDate: new Date(`2019-3-1`).toUTCString(), link: `https://github.com/DIYgod/RSSHub/issues/0`, author: `DIYgod0`, - category: ['Category0', 'Category1', 'Category2'], - }, - { - title: 'Filter Title3', - description: 'Description3', + }); + + break; + + case 'cache': { + const description = await ctx.cache.tryGet( + 'test', + () => ({ + text: `Cache${++cacheIndex}`, + }), + config.cache.routeExpire * 2 + ); + item.push({ + title: 'Cache Title', + description: description.text, pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/1`, + link: `https://github.com/DIYgod/RSSHub/issues/0`, author: `DIYgod0`, - category: 'Category3', - }, - ]; - } else if (ctx.params.id === 'long') { - item.push({ - title: `Long Title `.repeat(50), - description: `Long Description `.repeat(10), - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/0`, - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'cache') { - const description = await ctx.cache.tryGet( - 'test', - () => ({ - text: `Cache${++cacheIndex}`, - }), - config.cache.routeExpire * 2 - ); - item.push({ - title: 'Cache Title', - description: description.text, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/0`, - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'refreshCache') { - let refresh = await ctx.cache.get('refreshCache'); - let noRefresh = await ctx.cache.get('noRefreshCache', false); - if (!refresh) { - refresh = '0'; - await ctx.cache.set('refreshCache', '1'); + }); + + break; } - if (!noRefresh) { - noRefresh = '0'; - await ctx.cache.set('noRefreshCache', '1', undefined); + case 'refreshCache': { + let refresh = await ctx.cache.get('refreshCache'); + let noRefresh = await ctx.cache.get('noRefreshCache', false); + if (!refresh) { + refresh = '0'; + await ctx.cache.set('refreshCache', '1'); + } + if (!noRefresh) { + noRefresh = '0'; + await ctx.cache.set('noRefreshCache', '1', undefined); + } + item.push({ + title: 'Cache Title', + description: refresh + ' ' + noRefresh, + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/0`, + author: `DIYgod0`, + }); + + break; } - item.push({ - title: 'Cache Title', - description: refresh + ' ' + noRefresh, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/0`, - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'cacheUrlKey') { - const description = await ctx.cache.tryGet( - new URL('https://rsshub.app'), - () => ({ - text: `Cache${++cacheIndex}`, - }), - config.cache.routeExpire * 2 - ); - item.push({ - title: 'Cache Title', - description: description.text, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/0`, - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'complicated') { - item.push({ - title: `Complicated Title`, - description: ` + case 'cacheUrlKey': { + const description = await ctx.cache.tryGet( + new URL('https://rsshub.app'), + () => ({ + text: `Cache${++cacheIndex}`, + }), + config.cache.routeExpire * 2 + ); + item.push({ + title: 'Cache Title', + description: description.text, + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/0`, + author: `DIYgod0`, + }); + + break; + } + case 'complicated': + item.push( + { + title: `Complicated Title`, + description: ` @@ -111,22 +140,26 @@ module.exports = async (ctx) => { `, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `//mock.com/DIYgod/RSSHub`, - author: `DIYgod`, - }); - item.push({ - title: `Complicated Title`, - description: ` + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `//mock.com/DIYgod/RSSHub`, + author: `DIYgod`, + }, + { + title: `Complicated Title`, + description: ` `, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://mock.com/DIYgod/RSSHub`, - author: `DIYgod`, - }); - } else if (ctx.params.id === 'multimedia') { - item.push({ - title: `Multimedia Title`, - description: ` + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://mock.com/DIYgod/RSSHub`, + author: `DIYgod`, + } + ); + + break; + + case 'multimedia': + item.push({ + title: `Multimedia Title`, + description: ` `, - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://mock.com/DIYgod/RSSHub`, - author: `DIYgod`, - }); - } else if (ctx.params.id === 'sort') { - item.push({ - title: `Sort Title 0`, - link: `https://github.com/DIYgod/RSSHub/issues/s1`, - author: `DIYgod0`, - }); - item.push({ - title: `Sort Title 1`, - link: `https://github.com/DIYgod/RSSHub/issues/s1`, - author: `DIYgod0`, - }); - item.push({ - title: `Sort Title 2`, - link: `https://github.com/DIYgod/RSSHub/issues/s2`, - pubDate: new Date(1546272000000 - 10 * 10 * 1000).toUTCString(), - author: `DIYgod0`, - }); - item.push({ - title: `Sort Title 3`, - link: `https://github.com/DIYgod/RSSHub/issues/s3`, - pubDate: new Date(1546272000000).toUTCString(), - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'mess') { - item.push({ - title: `Mess Title`, - link: `/DIYgod/RSSHub/issues/0`, - pubDate: 1546272000000, - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'opencc') { - item.push({ - title: '小可愛', - description: '宇宙無敵', - link: `/DIYgod/RSSHub/issues/0`, - pubDate: new Date(1546272000000).toUTCString(), - author: `DIYgod0`, - }); - } else if (ctx.params.id === 'json') { - item.push( - { - title: 'Title0', - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/-3`, - }, - { - title: 'Title1', - description: 'Description1', - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/-2`, - author: `DIYgod0 `, - category: 'Category0', - }, - { - title: 'Title2 HTML in description', - description: 'RSSHub', pubDate: new Date(`2019-3-1`).toUTCString(), - updated: new Date(`2019-3-2`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/-1`, - author: [{ name: ' DIYgod1' }, { name: 'DIYgod2 ' }], - category: ['Category0', 'Category1'], - }, - { - title: 'Title3 HTML in content', - content: { - html: 'DIYgod/RSSHub', + link: `https://mock.com/DIYgod/RSSHub`, + author: `DIYgod`, + }); + + break; + + case 'sort': + item.push( + { + title: `Sort Title 0`, + link: `https://github.com/DIYgod/RSSHub/issues/s1`, + author: `DIYgod0`, }, - pubDate: new Date(`2019-3-1`).toUTCString(), - updated: new Date(`2019-3-2`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/issues/0`, - author: [{ name: ' DIYgod3' }, { name: 'DIYgod4 ' }, { name: 'DIYgod5 ' }], - category: ['Category1'], - enclosure_url: 'https://github.com/DIYgod/RSSHub/issues/0', - enclosure_type: 'image/jpeg', - enclosure_length: 3661, - itunes_duration: 36610, - }, - { - title: 'Title4 author is null', - pubDate: new Date(`2019-3-1`).toUTCString(), - link: `https://github.com/DIYgod/RSSHub/pull/11555`, - author: null, - } - ); + { + title: `Sort Title 1`, + link: `https://github.com/DIYgod/RSSHub/issues/s1`, + author: `DIYgod0`, + }, + { + title: `Sort Title 2`, + link: `https://github.com/DIYgod/RSSHub/issues/s2`, + pubDate: new Date(1_546_272_000_000 - 10 * 10 * 1000).toUTCString(), + author: `DIYgod0`, + }, + { + title: `Sort Title 3`, + link: `https://github.com/DIYgod/RSSHub/issues/s3`, + pubDate: new Date(1_546_272_000_000).toUTCString(), + author: `DIYgod0`, + } + ); + + break; + + case 'mess': + item.push({ + title: `Mess Title`, + link: `/DIYgod/RSSHub/issues/0`, + pubDate: 1_546_272_000_000, + author: `DIYgod0`, + }); + + break; + + case 'opencc': + item.push({ + title: '小可愛', + description: '宇宙無敵', + link: `/DIYgod/RSSHub/issues/0`, + pubDate: new Date(1_546_272_000_000).toUTCString(), + author: `DIYgod0`, + }); + + break; + + case 'brief': + item.push({ + title: '小可愛', + description: '

宇宙無敵


'.repeat(1000), + link: `/DIYgod/RSSHub/issues/0`, + pubDate: new Date(1_546_272_000_000).toUTCString(), + author: `DIYgod0`, + }); + + break; + + case 'json': + item.push( + { + title: 'Title0', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/-3`, + }, + { + title: 'Title1', + description: 'Description1', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/-2`, + author: `DIYgod0 `, + category: 'Category0', + }, + { + title: 'Title2 HTML in description', + description: 'RSSHub', + pubDate: new Date(`2019-3-1`).toUTCString(), + updated: new Date(`2019-3-2`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/-1`, + author: [{ name: ' DIYgod1' }, { name: 'DIYgod2 ' }], + category: ['Category0', 'Category1'], + }, + { + title: 'Title3 HTML in content', + content: { + html: 'DIYgod/RSSHub', + }, + pubDate: new Date(`2019-3-1`).toUTCString(), + updated: new Date(`2019-3-2`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/issues/0`, + author: [{ name: ' DIYgod3' }, { name: 'DIYgod4 ' }, { name: 'DIYgod5 ' }], + category: ['Category1'], + enclosure_url: 'https://github.com/DIYgod/RSSHub/issues/0', + enclosure_type: 'image/jpeg', + enclosure_length: 3661, + itunes_duration: 36610, + }, + { + title: 'Title4 author is null', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: `https://github.com/DIYgod/RSSHub/pull/11555`, + author: null, + } + ); + + break; + + case 'gpt': + item.push( + { + title: 'Title0', + description: 'Description0', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: 'https://github.com/DIYgod/RSSHub/issues/0', + }, + { + title: 'Title1', + description: + '快速开始\n' + + '如果您在使用 RSSHub 过程中遇到了问题或者有建议改进,我们很乐意听取您的意见!您可以通过 Pull Request 来提交您的修改。无论您对 Pull Request 的使用是否熟悉,我们都欢迎不同经验水平的开发者参与贡献。如果您不懂编程,也可以通过 报告错误 的方式来帮助我们。\n' + + '\n' + + '参与讨论\n' + + 'Telegram 群组 GitHub Issues GitHub 讨论\n' + + '\n' + + '开始之前\n' + + '要制作一个 RSS 订阅,您需要结合使用 Git、HTML、JavaScript、jQuery 和 Node.js。\n' + + '\n' + + '如果您对它们不是很了解,但想要学习它们,以下是一些好的资源:\n' + + '\n' + + 'MDN Web Docs 上的 JavaScript 指南\n' + + 'W3Schools\n' + + 'Codecademy 上的 Git 课程\n' + + '如果您想查看其他开发人员如何使用这些技术来制作 RSS 订阅的示例,您可以查看 我们的代码库 中的一些代码。\n' + + '\n' + + '提交新的 RSSHub 规则\n' + + '如果您发现一个网站没有提供 RSS 订阅,您可以使用 RSSHub 制作一个 RSS 规则。RSS 规则是一个短小的 Node.js 程序代码(以下简称 “路由”),它告诉 RSSHub 如何从网站中提取内容并生成 RSS 订阅。通过制作新的 RSS 路由,您可以帮助让您喜爱的网站的内容被更容易访问和关注。\n' + + '\n' + + '在您开始编写 RSS 路由之前,请确保源站点没有提供 RSS。一些网页会在 HTML 头部中包含一个 type 为 application/atom+xml 或 application/rss+xml 的 link 元素来指示 RSS 链接。\n' + + '\n' + + '这是在 HTML 头部中看到 RSS 链接可能会长成这样:。如果您看到这样的链接,这意味着这个网站已经有了一个 RSS 订阅,您不需要为它制作一个新的 RSS 路由。', + pubDate: new Date(`2019-3-1`).toUTCString(), + link: 'https://github.com/DIYgod/RSSHub/issues/1', + } + ); + + break; + + default: + // Do nothing } for (let i = 1; i < 6; i++) { item.push({ title: `Title${i}`, description: `Description${i}`, - pubDate: new Date((ctx.params.id === 'current_time' ? new Date() : 1546272000000) - i * 10 * 1000).toUTCString(), + pubDate: new Date((ctx.params.id === 'current_time' ? new Date() : 1_546_272_000_000) - i * 10 * 1000).toUTCString(), link: `https://github.com/DIYgod/RSSHub/issues/${i}`, author: `DIYgod${i}`, }); diff --git a/lib/v2/tfc-taiwan/maintainer.js b/lib/v2/tfc-taiwan/maintainer.js index 8880ce4c6de637..35f149f7498eb3 100644 --- a/lib/v2/tfc-taiwan/maintainer.js +++ b/lib/v2/tfc-taiwan/maintainer.js @@ -1,5 +1,4 @@ module.exports = { - '': ['TonyRL'], '/': ['TonyRL'], '/:type?': ['TonyRL'], // /info and /report '/:type/:id+': ['TonyRL'], // /category/:id+ and /topic/:id diff --git a/lib/v2/tfc-taiwan/utils.js b/lib/v2/tfc-taiwan/utils.js index 9cf6e49510acb1..cc38ea689dddbe 100644 --- a/lib/v2/tfc-taiwan/utils.js +++ b/lib/v2/tfc-taiwan/utils.js @@ -1,6 +1,6 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); -const { join } = require('path'); +const path = require('path'); const { art } = require('@/utils/render'); const asyncPool = require('tiny-async-pool'); const { parseDate } = require('@/utils/parse-date'); @@ -34,7 +34,7 @@ const parseItems = (list, tryGet) => $('.field-name-field-addthis, #fb-root, .fb-comments, .likecoin-embed, style[type="text/css"]').remove(); - item.description = art(join(__dirname, 'templates/article.art'), { + item.description = art(path.join(__dirname, 'templates/article.art'), { headerImage: item.image, content: $('#block-system-main .node-content').html(), }); diff --git a/lib/v2/theatlantic/maintainer.js b/lib/v2/theatlantic/maintainer.js index 1b6cbaac98db1a..6dd499c91aa8d5 100644 --- a/lib/v2/theatlantic/maintainer.js +++ b/lib/v2/theatlantic/maintainer.js @@ -1,3 +1,3 @@ module.exports = { - '/:category': ['NavePnow'], + '/:category': ['EthanWng97'], }; diff --git a/lib/v2/theatlantic/utils.js b/lib/v2/theatlantic/utils.js index a9f8d8b3539e77..a8d07fd3373559 100644 --- a/lib/v2/theatlantic/utils.js +++ b/lib/v2/theatlantic/utils.js @@ -26,9 +26,9 @@ const getArticleDetails = async (items, ctx) => { data = JSON.parse(list[keyWithContent].data).article; item.title = data.shareTitle; item.category = data.categories.map((category) => category.slug); - data.channels.forEach((channel) => { + for (const channel of data.channels) { item.category.push(channel.slug); - }); + } item.content = data.content.filter((item) => item.innerHtml !== undefined && item.innerHtml !== ''); item.caption = data.dek; item.imgUrl = data.leadArt.image?.url; diff --git a/lib/v2/theblockbeats/index.js b/lib/v2/theblockbeats/index.js new file mode 100644 index 00000000000000..371c9327f7a428 --- /dev/null +++ b/lib/v2/theblockbeats/index.js @@ -0,0 +1,53 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +const domain = 'theblockbeats.info'; +const rootUrl = `https://www.${domain}`; +const apiBase = `https://api.${domain}`; + +const channelMap = { + newsflash: { + title: '快讯', + link: `${rootUrl}/newsflash`, + api: `${apiBase}/v5/newsflash/select`, + }, + article: { + title: '文章', + link: `${rootUrl}/article`, + api: `${apiBase}/v5/Information/newsall`, + }, +}; + +module.exports = async (ctx) => { + const { channel = 'newsflash' } = ctx.params; + + const { data: response } = await got(channelMap[channel].api); + + const { data } = channel === 'newsflash' ? response.data : response; + let list = data.map((item) => ({ + title: item.title, + link: `${rootUrl}/${channel === 'newsflash' ? 'flash' : 'news'}/${item.id}`, + description: item.content ?? item.im_abstract, + pubDate: parseDate(item.add_time, 'X'), + })); + + if (channel !== 'newsflash') { + list = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { data: response } = await got(item.link); + const $ = cheerio.load(response); + item.description = $('div.news-content').html(); + return item; + }) + ) + ); + } + + ctx.state.data = { + title: `TheBlockBeats - ${channelMap[channel].title}`, + link: channelMap[channel].link, + item: list, + }; +}; diff --git a/lib/v2/blockbeats/maintainer.js b/lib/v2/theblockbeats/maintainer.js similarity index 100% rename from lib/v2/blockbeats/maintainer.js rename to lib/v2/theblockbeats/maintainer.js diff --git a/lib/v2/blockbeats/radar.js b/lib/v2/theblockbeats/radar.js similarity index 51% rename from lib/v2/blockbeats/radar.js rename to lib/v2/theblockbeats/radar.js index 3ccde68c4e83df..c62887f263396d 100644 --- a/lib/v2/blockbeats/radar.js +++ b/lib/v2/theblockbeats/radar.js @@ -4,15 +4,15 @@ module.exports = { rszhaopin: [ { title: '快讯', - docs: 'https://docs.rsshub.app/routes/new-media#lu-dong-xin-wen-kuai-xun', + docs: 'https://docs.rsshub.app/routes/finance#lv-dong', source: ['/'], - target: '/blockbeats/newsflash', + target: '/theblockbeats/newsflash', }, { title: '文章', - docs: 'https://docs.rsshub.app/routes/new-media#lu-dong-xin-wen-kuai-xun', + docs: 'https://docs.rsshub.app/routes/finance#lv-dong', source: ['/'], - target: '/blockbeats/article', + target: '/theblockbeats/article', }, ], }, diff --git a/lib/v2/appledaily/router.js b/lib/v2/theblockbeats/router.js similarity index 58% rename from lib/v2/appledaily/router.js rename to lib/v2/theblockbeats/router.js index 91b8719d6673ee..cf61ea5c7f7902 100644 --- a/lib/v2/appledaily/router.js +++ b/lib/v2/theblockbeats/router.js @@ -1,3 +1,3 @@ -module.exports = function (router) { +module.exports = (router) => { router.get('/:channel?', require('./index')); }; diff --git a/lib/v2/thecatcity/index.js b/lib/v2/thecatcity/index.js index 555ea5c186056f..ada3e831d6d760 100644 --- a/lib/v2/thecatcity/index.js +++ b/lib/v2/thecatcity/index.js @@ -1,6 +1,6 @@ const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); -const { termsMap } = require('./termsMap'); +const { termsMap } = require('./terms-map'); const baseUrl = 'https://thecatcity.com'; @@ -8,7 +8,7 @@ module.exports = async (ctx) => { const { term } = ctx.params; const { data } = await got(`${baseUrl}/node_api/v1/articles/posts`, { searchParams: { - pageId: 977080509047743, + pageId: 977_080_509_047_743, term, }, }); @@ -27,7 +27,7 @@ module.exports = async (ctx) => { ctx.cache.tryGet(item.guid, async () => { const { data } = await got(item.api, { searchParams: { - pageId: 977080509047743, + pageId: 977_080_509_047_743, }, }); item.description = data.data.post_content; diff --git a/lib/v2/thecatcity/radar.js b/lib/v2/thecatcity/radar.js index 06ff37c08f9ca7..5c8eaadd1a7f20 100644 --- a/lib/v2/thecatcity/radar.js +++ b/lib/v2/thecatcity/radar.js @@ -1,4 +1,4 @@ -const { termsMap } = require('./termsMap'); +const { termsMap } = require('./terms-map'); module.exports = { 'thecatcity.com': { diff --git a/lib/v2/thecatcity/termsMap.js b/lib/v2/thecatcity/terms-map.js similarity index 100% rename from lib/v2/thecatcity/termsMap.js rename to lib/v2/thecatcity/terms-map.js diff --git a/lib/v2/thehindu/radar.js b/lib/v2/thehindu/radar.js index 7ae8d469391480..38844ff50b5291 100644 --- a/lib/v2/thehindu/radar.js +++ b/lib/v2/thehindu/radar.js @@ -4,7 +4,7 @@ module.exports = { '.': [ { title: 'Topic', - docs: 'https://docs.rsshub.app/routes/en/traditional-media#the-hindu', + docs: 'https://docs.rsshub.app/routes/traditional-media#the-hindu', source: ['/topic/:topic'], target: '/thehindu/topic/:topic', }, diff --git a/lib/v2/theinitium/full.js b/lib/v2/theinitium/full.js index 742e3fb509afa1..7885a32d78f270 100644 --- a/lib/v2/theinitium/full.js +++ b/lib/v2/theinitium/full.js @@ -89,7 +89,7 @@ module.exports = async (ctx) => { const name = response.data.name || (response.data[model] && response.data[model].name) || '追踪'; // 从v1直升的channel和tags里面是digests,v2新增的author和follow出来都是results - const articles = response.data.results ? response.data.results : response.data.digests; + const articles = response.data.results ?? response.data.digests; // 如果model=author,那就是avatar;否则都是cover,要么就没封面 const image = response.data[model] && (response.data[model].cover || response.data[model].avatar); @@ -107,7 +107,7 @@ module.exports = async (ctx) => { content += '

' + data.byline + '

'; } if (data.content) { - content += data.content.replace('