From 75d43dd0868d3a169cb0bc94eabf354d26af5e9c Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 5 Jun 2026 20:04:39 +0800 Subject: [PATCH] chore: add eslint-plugin-regexp (#22189) * chore: add eslint-plugin-regexp * chore: autofix * chore: fix no-useless-flag * chore: fix optimal-lookaround-quantifier * chore: fix no-lazy-ends * chore: fix no-contradiction-with-assertion * chore: fix no-useless-assertions * chore: fix no-useless-quantifier * chore: fix remaining regexp issues * fix: regex for WFU news link validation --- .oxlintrc.json | 73 +++++++++++++++++-- lib/middleware/anti-hotlink.ts | 4 +- lib/middleware/template.tsx | 5 +- lib/routes/12371/zxfb.ts | 2 +- lib/routes/163/news/special.ts | 4 +- lib/routes/163/open/vip.tsx | 2 +- lib/routes/2048/index.tsx | 2 +- lib/routes/36kr/hot-list.ts | 2 +- lib/routes/36kr/index.ts | 4 +- lib/routes/36kr/utils.ts | 2 +- lib/routes/3dmgame/utils.ts | 2 +- lib/routes/4ksj/forum.tsx | 2 +- lib/routes/69shu/article.ts | 6 +- lib/routes/6park/index.ts | 2 +- lib/routes/6park/news.ts | 2 +- lib/routes/9to5/utils.ts | 2 +- lib/routes/abc/index.ts | 4 +- lib/routes/acfun/video.ts | 2 +- lib/routes/aip/journal.ts | 2 +- lib/routes/altotrain/news.ts | 2 +- lib/routes/anthropic/research.ts | 2 +- lib/routes/aqara/news.ts | 2 +- lib/routes/arcteryx/regear-new-arrivals.tsx | 2 +- lib/routes/bbc/utils.tsx | 2 +- lib/routes/bilibili/cache.ts | 4 +- lib/routes/bjsk/index.ts | 3 +- lib/routes/bjtu/gs.ts | 2 +- lib/routes/bjwxdxh/index.ts | 2 +- lib/routes/bloomberg/utils.ts | 2 +- lib/routes/booru/mmda.ts | 2 +- lib/routes/bse/index.ts | 2 +- lib/routes/caixin/category.ts | 2 +- lib/routes/caixin/utils-fulltext.ts | 2 +- lib/routes/cas/sim/kyjz.ts | 2 +- lib/routes/chinaratings/credit-research.ts | 2 +- lib/routes/chnmus/exhibition.tsx | 2 +- lib/routes/cih-index/report.ts | 2 +- lib/routes/cisia/index.ts | 2 +- lib/routes/cline/blog.ts | 2 +- lib/routes/cool18/index.ts | 2 +- lib/routes/ctinews/topic.ts | 2 +- lib/routes/dailypush/utils.ts | 2 +- lib/routes/daum/potplayer.ts | 6 +- lib/routes/dayanzai/index.ts | 2 +- lib/routes/dcard/utils.ts | 2 +- lib/routes/dealstreetasia/home.ts | 2 +- lib/routes/dedao/knowledge.tsx | 2 +- lib/routes/dedao/user.tsx | 2 +- lib/routes/dehenglaw/index.ts | 2 +- lib/routes/dianping/user.ts | 2 +- lib/routes/dlnews/category.tsx | 2 +- lib/routes/dnaindia/common.ts | 2 +- lib/routes/domp4/detail.ts | 2 +- lib/routes/dongqiudi/utils.ts | 2 +- lib/routes/dora-world/article.ts | 2 +- lib/routes/douban/other/replied.ts | 2 +- lib/routes/douban/other/replies.ts | 2 +- lib/routes/dribbble/utils.tsx | 2 +- lib/routes/ehentai/ehapi.ts | 2 +- lib/routes/fanqienovel/page.ts | 2 +- lib/routes/flashcat/blog.ts | 34 ++++----- lib/routes/gamer/ani/anime.ts | 2 +- lib/routes/getitfree/index.ts | 2 +- lib/routes/gigazine/en.ts | 2 +- lib/routes/globallawreview/index.ts | 2 +- lib/routes/google/album.ts | 2 +- lib/routes/google/scholar.ts | 2 +- lib/routes/gov/beijing/kw/index.ts | 4 +- lib/routes/gov/cac/index.ts | 2 +- lib/routes/gov/ccdi/utils.ts | 2 +- lib/routes/gov/cn/news/index.ts | 8 +- lib/routes/gov/general/general.ts | 4 +- lib/routes/gov/guangdong/tqyb/tfxtq.tsx | 2 +- lib/routes/gov/miit/wjfb.ts | 2 +- lib/routes/gov/miit/yjzj.ts | 2 +- lib/routes/gov/mofcom/article.ts | 2 +- lib/routes/gov/nrta/news.ts | 2 +- lib/routes/gov/nsfc/index.ts | 2 +- lib/routes/gov/stats/index.tsx | 2 +- lib/routes/gov/zhengce/govall.ts | 2 +- lib/routes/gov/zj/ningbogzw-notice.ts | 2 +- lib/routes/gov/zj/ningborsjnotice.ts | 2 +- lib/routes/guancha/personalpage.ts | 2 +- lib/routes/hk01/utils.tsx | 2 +- lib/routes/hkej/index.tsx | 2 +- lib/routes/hongkong/chp.ts | 2 +- lib/routes/hpoi/utils.ts | 4 +- lib/routes/hupu/utils.ts | 4 +- lib/routes/hypergryph/arknights/arktca.ts | 2 +- lib/routes/ifeng/feng.ts | 2 +- lib/routes/ifeng/news.tsx | 6 +- lib/routes/inewsweek/index.ts | 2 +- lib/routes/iwara/utils.ts | 2 +- lib/routes/ixigua/user-video.tsx | 2 +- lib/routes/jandan/utils.ts | 2 +- lib/routes/javbus/index.tsx | 4 +- lib/routes/jiemian/common.tsx | 2 +- lib/routes/jike/user.ts | 2 +- lib/routes/jike/utils.ts | 2 +- lib/routes/jimmyspa/books.ts | 2 +- lib/routes/kunchengblog/essay.ts | 2 +- .../leetcode/dailyquestion-solution-cn.ts | 2 +- lib/routes/lfsyd/utils.tsx | 2 +- lib/routes/line/utils.ts | 2 +- lib/routes/lorientlejour/index.tsx | 2 +- lib/routes/luolei/index.tsx | 2 +- lib/routes/magazinelib/latest-magazine.tsx | 2 +- lib/routes/mastodon/utils.ts | 4 +- lib/routes/maven/central.ts | 2 +- lib/routes/metacritic/index.tsx | 2 +- lib/routes/meteor/utils.ts | 2 +- lib/routes/mirror/index.ts | 2 +- lib/routes/modelscope/community.tsx | 2 +- lib/routes/mrinalxdev/blog.ts | 2 +- lib/routes/mydrivers/index.tsx | 2 +- lib/routes/mydrivers/rank.ts | 2 +- lib/routes/natgeo/dailyphoto.tsx | 2 +- .../nationalgeographic/latest-stories.tsx | 2 +- lib/routes/nature/utils.ts | 2 +- lib/routes/ncpssd/newlist.ts | 2 +- lib/routes/neu/yz.ts | 2 +- lib/routes/nga/forum.ts | 4 +- lib/routes/nga/post.ts | 36 ++++----- lib/routes/nhentai/util.tsx | 2 +- lib/routes/nikkei/cn/index.ts | 2 +- lib/routes/nintendo/eshop-hk.ts | 4 +- lib/routes/nintendo/system-update.ts | 2 +- lib/routes/nowcoder/discuss.ts | 2 +- lib/routes/odaily/activity.ts | 2 +- lib/routes/odaily/post.ts | 2 +- lib/routes/oeeee/utils.ts | 2 +- lib/routes/outagereport/index.ts | 4 +- lib/routes/papers/category.ts | 2 +- lib/routes/parliament/section77.ts | 4 +- lib/routes/patreon/feed.tsx | 2 +- lib/routes/pixiv/novel-api/content/utils.ts | 4 +- lib/routes/playno1/av.ts | 2 +- lib/routes/qingting/channel.ts | 2 +- lib/routes/qingting/podcast.ts | 2 +- lib/routes/quantamagazine/archive.ts | 6 +- lib/routes/rawkuma/manga.tsx | 2 +- lib/routes/readhub/index.ts | 2 +- lib/routes/readhub/util.ts | 2 +- lib/routes/reuters/common.tsx | 4 +- lib/routes/runyeah/posts.ts | 2 +- lib/routes/shisu/en.ts | 4 +- lib/routes/sina/utils.tsx | 2 +- lib/routes/sis001/common.ts | 4 +- lib/routes/smartlink/index.ts | 2 +- lib/routes/sohu/mobile.ts | 2 +- lib/routes/sohu/mp.tsx | 2 +- lib/routes/solidot/_article.ts | 2 +- lib/routes/sony/downloads.ts | 2 +- lib/routes/steam/curator.tsx | 2 +- lib/routes/steam/news.ts | 6 +- lib/routes/steam/workshop-search.tsx | 2 +- lib/routes/supchina/index.ts | 4 +- lib/routes/swjtu/gsee/yjs.ts | 2 +- lib/routes/swjtu/scai.ts | 2 +- lib/routes/swjtu/sports.ts | 2 +- lib/routes/swpu/utils.ts | 2 +- lib/routes/szse/disclosure/listed-notice.ts | 2 +- lib/routes/tass/news.ts | 2 +- lib/routes/tencent/news/author.tsx | 2 +- lib/routes/tesla/cx.ts | 2 +- lib/routes/threads/utils.ts | 2 +- lib/routes/transcriptforest/index.ts | 2 +- .../twitter/api/web-api/gql-id-resolver.ts | 2 +- lib/routes/twitter/utils.ts | 2 +- lib/routes/txrjy/fornumtopic.tsx | 4 +- lib/routes/udn/breaking-news.tsx | 4 +- lib/routes/upc/jwc.ts | 2 +- lib/routes/ups/track.ts | 4 +- lib/routes/uptimerobot/rss.tsx | 2 +- lib/routes/vcb-s/category.ts | 2 +- lib/routes/vcb-s/index.ts | 2 +- lib/routes/weibo/utils.ts | 6 +- lib/routes/wenku8/volume.ts | 2 +- lib/routes/wfu/news.ts | 2 +- lib/routes/wikipedia/current-events.ts | 8 +- lib/routes/wmc-bj/publish.tsx | 2 +- lib/routes/wnacg/common.tsx | 2 +- lib/routes/wordpress/index.ts | 4 +- lib/routes/wsj/news.ts | 2 +- lib/routes/xaufe/jiaowu.ts | 2 +- lib/routes/xhamster/index.ts | 2 +- lib/routes/xinpianchang/index.ts | 2 +- lib/routes/xueqiu/snb.ts | 2 +- lib/routes/xueqiu/user.ts | 2 +- lib/routes/xys/new.tsx | 2 +- lib/routes/yamibo/utils.ts | 6 +- lib/routes/yicai/utils.ts | 2 +- lib/routes/ynet/list.ts | 2 +- lib/routes/youtube/api/google.ts | 2 +- lib/routes/youtube/community.tsx | 2 +- lib/routes/youtube/custom.ts | 2 +- lib/routes/zaker/utils.ts | 2 +- lib/routes/zaobao/util.tsx | 2 +- lib/routes/zhihu/utils.ts | 4 +- lib/routes/zhonglun/index.ts | 2 +- lib/utils/camelcase-keys.ts | 2 +- lib/utils/common-config.ts | 2 +- lib/utils/parse-date.ts | 12 +-- lib/utils/valid-host.ts | 2 +- lib/utils/wechat-mp.ts | 2 +- package.json | 1 + pnpm-lock.yaml | 59 +++++++++++++++ scripts/workflow/format-description.ts | 2 +- 208 files changed, 418 insertions(+), 295 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 85967256d..f3a91894a 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -14,6 +14,7 @@ "jsPlugins": [ { "name": "n", "specifier": "eslint-plugin-n" }, { "name": "unicorn-js", "specifier": "eslint-plugin-unicorn" }, + { "name": "regexp", "specifier": "eslint-plugin-regexp" }, "@stylistic/eslint-plugin", "eslint-plugin-simple-import-sort", "oxlint-plugin-eslint", @@ -32,19 +33,19 @@ "no-const-assign": "error", "no-constant-binary-expression": "error", "no-constant-condition": "error", - // "no-control-regex": "error", -> off + "no-control-regex": "error", "no-debugger": "error", "no-dupe-class-members": "error", "no-dupe-else-if": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", - "no-empty-character-class": "error", + // "no-empty-character-class": "error", -> off, handled by eslint-plugin-regexp "no-empty-pattern": "error", "no-ex-assign": "error", "no-fallthrough": "error", "no-func-assign": "error", "no-import-assign": "error", - "no-invalid-regexp": "error", + // "no-invalid-regexp": "error", -> off, handled by eslint-plugin-regexp "no-irregular-whitespace": "error", "no-loss-of-precision": "error", "no-misleading-character-class": "error", @@ -65,7 +66,7 @@ "no-unsafe-optional-chaining": "error", "no-unused-private-class-members": "error", // "no-unused-vars": "error", -> off for @typescript-eslint/no-unused-vars - "no-useless-backreference": "error", + // "no-useless-backreference": "error", -> off, handled by eslint-plugin-regexp "use-isnan": "error", "valid-typeof": "error", // #endregion @@ -289,12 +290,74 @@ "unicorn/throw-new-error": "error", // #endregion + // #region --- regexp recommended --- + "regexp/confusing-quantifier": "warn", + "regexp/control-character-escape": "error", + "regexp/match-any": "error", + "regexp/negation": "error", + "regexp/no-contradiction-with-assertion": "error", + "regexp/no-dupe-characters-character-class": "error", + "regexp/no-dupe-disjunctions": "error", + "regexp/no-empty-alternative": "warn", + "regexp/no-empty-capturing-group": "error", + "regexp/no-empty-character-class": "error", + "regexp/no-empty-group": "error", + "regexp/no-empty-lookarounds-assertion": "error", + "regexp/no-empty-string-literal": "error", + "regexp/no-escape-backspace": "error", + "regexp/no-extra-lookaround-assertions": "error", + "regexp/no-invalid-regexp": "error", + "regexp/no-invisible-character": "error", + "regexp/no-lazy-ends": "warn", + "regexp/no-legacy-features": "error", + "regexp/no-misleading-capturing-group": "error", + "regexp/no-misleading-unicode-character": "error", + "regexp/no-missing-g-flag": "error", + "regexp/no-non-standard-flag": "error", + "regexp/no-obscure-range": "error", + "regexp/no-optional-assertion": "error", + "regexp/no-potentially-useless-backreference": "warn", + "regexp/no-super-linear-backtracking": "error", + "regexp/no-trivially-nested-assertion": "error", + "regexp/no-trivially-nested-quantifier": "error", + "regexp/no-unused-capturing-group": "error", + "regexp/no-useless-assertions": "error", + "regexp/no-useless-backreference": "error", + "regexp/no-useless-character-class": "error", + "regexp/no-useless-dollar-replacements": "error", + "regexp/no-useless-escape": "error", + "regexp/no-useless-flag": "warn", + "regexp/no-useless-lazy": "error", + "regexp/no-useless-non-capturing-group": "error", + "regexp/no-useless-quantifier": "error", + "regexp/no-useless-range": "error", + "regexp/no-useless-set-operand": "error", + "regexp/no-useless-string-literal": "error", + "regexp/no-useless-two-nums-quantifier": "error", + "regexp/no-zero-quantifier": "error", + "regexp/optimal-lookaround-quantifier": "warn", + "regexp/optimal-quantifier-concatenation": "error", + "regexp/prefer-character-class": "error", + "regexp/prefer-d": "error", + "regexp/prefer-plus-quantifier": "error", + "regexp/prefer-predefined-assertion": "error", + "regexp/prefer-question-quantifier": "error", + "regexp/prefer-range": "error", + "regexp/prefer-set-operation": "error", + "regexp/prefer-star-quantifier": "error", + "regexp/prefer-unicode-codepoint-escapes": "error", + "regexp/prefer-w": "error", + "regexp/simplify-set-operations": "error", + "regexp/sort-flags": "error", + "regexp/strict": "error", + "regexp/use-ignore-case": "error", + // #endregion + // --- custom rules --- // #region --- possible problems --- "array-callback-return": ["error", { "allowImplicit": true }], "no-await-in-loop": "error", - "no-control-regex": "off", "no-prototype-builtins": "off", "no-undef": "off", // typescript/eslint-recommended, ts(2552) // #endregion diff --git a/lib/middleware/anti-hotlink.ts b/lib/middleware/anti-hotlink.ts index 49a849570..7dd6e8976 100644 --- a/lib/middleware/anti-hotlink.ts +++ b/lib/middleware/anti-hotlink.ts @@ -6,7 +6,7 @@ import { config } from '@/config'; import type { Data } from '@/types'; import logger from '@/utils/logger'; -const templateRegex = /\${([^{}]+)}/g; +const templateRegex = /\$\{([^{}]+)\}/g; const allowedUrlProperties = new Set(['hash', 'host', 'hostname', 'href', 'origin', 'password', 'pathname', 'port', 'protocol', 'search', 'searchParams', 'username']); // match path or sub-path @@ -150,7 +150,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { if (item.enclosure_url && item.enclosure_type) { if (item.enclosure_type.startsWith('image/')) { item.enclosure_url = replaceUrl(imageHotlinkTemplate, item.enclosure_url); - } else if (/^(video|audio)\//.test(item.enclosure_type)) { + } else if (/^(?:video|audio)\//.test(item.enclosure_type)) { item.enclosure_url = replaceUrl(multimediaHotlinkTemplate, item.enclosure_url); } } diff --git a/lib/middleware/template.tsx b/lib/middleware/template.tsx index d9f5ed986..78673a950 100644 --- a/lib/middleware/template.tsx +++ b/lib/middleware/template.tsx @@ -28,7 +28,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { return ctx.json(ctx.get('json') || { message: 'plugin does not set debug json' }); } - if (/(\d+)\.debug\.html$/.test(outputType)) { + if (/\d+\.debug\.html$/.test(outputType)) { const index = Number.parseInt(outputType.match(/(\d+)\.debug\.html$/)?.[1] || '0'); return ctx.html(data?.item?.[index]?.description || `data.item[${index}].description not found`); } @@ -58,7 +58,8 @@ const middleware: MiddlewareHandler = async (ctx, next) => { // https://stackoverflow.com/questions/1497885/remove-control-characters-from-php-string/1497928#1497928 // remove unicode control characters // see #14940 #14943 #15262 - item.description = item.description.replaceAll(/[\u0000-\u0009\u000B\u000C\u000E-\u001F\u007F\u200B\uFFFF]/g, ''); + // oxlint-disable-next-line no-control-regex + item.description = item.description.replaceAll(/[\u0000-\u0009\v\f\u000E-\u001F\u007F\u200B\uFFFF]/g, ''); } if (typeof item.author === 'string') { diff --git a/lib/routes/12371/zxfb.ts b/lib/routes/12371/zxfb.ts index e50492205..b6b521b92 100644 --- a/lib/routes/12371/zxfb.ts +++ b/lib/routes/12371/zxfb.ts @@ -16,7 +16,7 @@ const handler = async (ctx) => { const $ = cheerio.load(response.data); - const pattern = /item=(\[{.*?}]);/; + const pattern = /item=(\[\{.*?\}\]);/; const newsList = JSON.parse($('script[language="javascript"]').text().match(pattern)?.[1].replaceAll("'", '"') || '[]'); const topNewsList = newsList.slice(0, limit).map((item) => ({ diff --git a/lib/routes/163/news/special.ts b/lib/routes/163/news/special.ts index ea7d1948c..86f93caba 100644 --- a/lib/routes/163/news/special.ts +++ b/lib/routes/163/news/special.ts @@ -102,7 +102,7 @@ async function handler(ctx) { const url = `https://3g.163.com/touch/reconstruct/article/list/${type}/0-20.html`; const response = await got(url); const data = response.data; - const matches = data.replaceAll(/\s/g, '').match(/artiList\((.*?)]}\)/); + const matches = data.replaceAll(/\s/g, '').match(/artiList\((.*?)\]\}\)/); const articlelist0 = matches[1].replace(/".*?wangning/, '"articles') + ']}'; const articlelist = JSON.parse(articlelist0); const articles = articlelist.articles; @@ -112,7 +112,7 @@ async function handler(ctx) { let url = article.url; if (url === null || article.skipType === 'video') { const skipurl = article.skipURL; - const vid = skipurl.match(/vid=(.*?)$/); + const vid = skipurl.match(/vid=(.*)$/); if (vid !== null) { url = `https://3g.163.com/exclusive/video/${vid[1]}.html`; } diff --git a/lib/routes/163/open/vip.tsx b/lib/routes/163/open/vip.tsx index 4ee699dbc..e34a1da59 100644 --- a/lib/routes/163/open/vip.tsx +++ b/lib/routes/163/open/vip.tsx @@ -65,7 +65,7 @@ async function handler() { const initialState = JSON.parse( $('script') .text() - .match(/window\.__INITIAL_STATE__=(.*);\(function\(\){var/)[1] + .match(/window\.__INITIAL_STATE__=(.*);\(function\(\)\{var/)[1] ); const list = Object.values(initialState.courseindex.myModules).flatMap((mod) => diff --git a/lib/routes/2048/index.tsx b/lib/routes/2048/index.tsx index 46c1e5356..4cf967857 100644 --- a/lib/routes/2048/index.tsx +++ b/lib/routes/2048/index.tsx @@ -164,7 +164,7 @@ async function handler(ctx) { } } if (!item.enclosure_url) { - const hashMatch = readTpcHtml.match(/哈希校验[^;]*;\s*([a-fA-F0-9]{40})\s*[;;]/); + const hashMatch = readTpcHtml.match(/哈希校验[^;]*;\s*([a-f0-9]{40})\s*[;;]/i); const magnetFromHash = hashMatch ? `magnet:?xt=urn:btih:${hashMatch[1]}` : null; const magnetFromText = magnetText.match(/magnet:\?xt=urn:btih:[^\s"'<>]+/)?.[0]; const magnetLink = magnetFromText ?? readTpcHtml.match(/magnet:\?xt=urn:btih:[^\s"'<>]+/)?.[0] ?? magnetFromHash ?? copyLink; diff --git a/lib/routes/36kr/hot-list.ts b/lib/routes/36kr/hot-list.ts index c28b4f287..61825e4e6 100644 --- a/lib/routes/36kr/hot-list.ts +++ b/lib/routes/36kr/hot-list.ts @@ -79,7 +79,7 @@ async function handler(ctx) { }, }); - const data = getProperty(JSON.parse(response.data.match(/window.initialState=({.*})/)[1]), categories[category].key); + const data = getProperty(JSON.parse(response.data.match(/window.initialState=(\{.*\})/)[1]), categories[category].key); let items = data .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10) diff --git a/lib/routes/36kr/index.ts b/lib/routes/36kr/index.ts index 6c24a1d83..e3b085f41 100644 --- a/lib/routes/36kr/index.ts +++ b/lib/routes/36kr/index.ts @@ -48,7 +48,7 @@ async function handler(ctx) { const $ = load(response.data); - const data = JSON.parse(response.data.match(/"itemList":(\[.*?])/)[1]); + const data = JSON.parse(response.data.match(/"itemList":(\[.*?\])/)[1]); let items = data .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30) @@ -64,7 +64,7 @@ async function handler(ctx) { }; }); - if (!/^\/(search|newsflashes)/.test(path)) { + if (!/^\/(?:search|newsflashes)/.test(path)) { items = await Promise.all(items.map((item) => ProcessItem(item, cache.tryGet))); } diff --git a/lib/routes/36kr/utils.ts b/lib/routes/36kr/utils.ts index 5149e1ae2..a909ece47 100644 --- a/lib/routes/36kr/utils.ts +++ b/lib/routes/36kr/utils.ts @@ -11,7 +11,7 @@ export const ProcessItem = (item, tryGet) => tryGet(item.link, async () => { const detailResponse = await ofetch(item.link); - const cipherTextList = detailResponse.match(/{"state":"(.*)","isEncrypt":true}/) ?? []; + const cipherTextList = detailResponse.match(/\{"state":"(.*)","isEncrypt":true\}/) ?? []; if (cipherTextList.length === 0) { const $ = load(detailResponse); diff --git a/lib/routes/3dmgame/utils.ts b/lib/routes/3dmgame/utils.ts index 37bda8242..e714f989c 100644 --- a/lib/routes/3dmgame/utils.ts +++ b/lib/routes/3dmgame/utils.ts @@ -11,7 +11,7 @@ const parseArticle = (item, tryGet) => if (item.link.startsWith('https://dl.3dmgame.com/')) { const lis = $('.patchtop .lis'); - const [, category, pubDate, author] = lis.text().match(/补丁类型:(.*?)\n.*整理时间:(.*?)\n.*补丁制作:(.*?)\n/s); + const [, category, pubDate, author] = lis.text().match(/补丁类型:([^\n]*)\n.*整理时间:([^\n]*)\n.*补丁制作:([^\n]*)\n/s); item.description = lis.html() + $('.L_title').html() + $('.GmL_1').html(); item.category = category; diff --git a/lib/routes/4ksj/forum.tsx b/lib/routes/4ksj/forum.tsx index 4762c3e87..ae514a5d4 100644 --- a/lib/routes/4ksj/forum.tsx +++ b/lib/routes/4ksj/forum.tsx @@ -114,7 +114,7 @@ async function handler(ctx) { const scriptUrl = new URL(scriptPath, rootUrl).href; const scriptResponse = await ofetch(scriptUrl); - const key = scriptResponse.match(/{var key="(.*?)"/)?.[1]; + const key = scriptResponse.match(/\{var key="(.*?)"/)?.[1]; const value = scriptResponse.match(/",value="(.*?)"/)?.[1]; const getPath = scriptResponse.match(/\.get\("(.*?&key=)"/)?.[1]; diff --git a/lib/routes/69shu/article.ts b/lib/routes/69shu/article.ts index bc13b6c30..39ca17180 100644 --- a/lib/routes/69shu/article.ts +++ b/lib/routes/69shu/article.ts @@ -54,8 +54,8 @@ const createItem = (url: string) => cache.tryGet(url, async () => { const html = await get(url); const $ = load(html); - const { articleid, chapterid, chaptername } = parseObject(/bookinfo\s?=\s?{[\S\s]+?}/, $('head>script:not([src])').text()); - const decryptionMap = parseObject(/_\d+\s?=\s?{[\S\s]+?}/, $('.txtnav+script').text()); + const { articleid, chapterid, chaptername } = parseObject(/bookinfo\s?=\s?\{[\s\S]+?\}/, $('head>script:not([src])').text()); + const decryptionMap = parseObject(/_\d+\s?=\s?\{[\s\S]+?\}/, $('.txtnav+script').text()); return { title: chaptername, @@ -70,7 +70,7 @@ const parseObject = (reg: RegExp, str: string): Record => { const obj = {}; const match = reg.exec(str); if (match) { - for (const line of match[0].matchAll(/(\w+):\s?["']?([\S\s]+?)["']?[\n,}]/g)) { + for (const line of match[0].matchAll(/(\w+):\s?["']?([\s\S]+?)["']?[\n,}]/g)) { obj[line[1]] = line[2]; } } diff --git a/lib/routes/6park/index.ts b/lib/routes/6park/index.ts index 4e2015f42..e3904f8f9 100644 --- a/lib/routes/6park/index.ts +++ b/lib/routes/6park/index.ts @@ -66,7 +66,7 @@ async function handler(ctx) { const content = load(detailResponse.data); item.title = content('title').text().replace(' -6park.com', ''); - item.author = detailResponse.data.match(/送交者: .*>(.*)<.*\[/)[1]; + item.author = detailResponse.data.match(/送交者:[^>]*>([^<]*)<\/a>/)[1].trim(); item.pubDate = timezone(parseDate(detailResponse.data.match(/于 (.*) 已读/)[1], 'YYYY-MM-DD h:m'), +8); item.description = content('pre') .html() diff --git a/lib/routes/6park/news.ts b/lib/routes/6park/news.ts index 2050185e5..30941bde8 100644 --- a/lib/routes/6park/news.ts +++ b/lib/routes/6park/news.ts @@ -76,7 +76,7 @@ async function handler(ctx) { const content = load(detailResponse.data); - const matches = detailResponse.data.match(/新闻来源:(.*?)于.*(\d{4}(?:-\d{2}){2} (?:\d{1,2}:){2}\d{1,2})/); + const matches = detailResponse.data.match(/新闻来源:([^于]*)于.*(\d{4}(?:-\d{2}){2} (?:\d{1,2}:){2}\d{1,2})/); item.title = content('h2').text(); item.author = matches[1].trim(); diff --git a/lib/routes/9to5/utils.ts b/lib/routes/9to5/utils.ts index 3cbb329b1..f4c39a2a8 100644 --- a/lib/routes/9to5/utils.ts +++ b/lib/routes/9to5/utils.ts @@ -22,7 +22,7 @@ const ProcessFeed = (data) => { content.find('div').each((i, e) => { if ($(e)[0].attribs.class) { const classes = $(e)[0].attribs.class; - if (/\w{10}\s\w{10}/g.test(classes)) { + if (/\w{10}\s\w{10}/.test(classes)) { $(e).remove(); } } diff --git a/lib/routes/abc/index.ts b/lib/routes/abc/index.ts index a4eea5198..89a9b57ed 100644 --- a/lib/routes/abc/index.ts +++ b/lib/routes/abc/index.ts @@ -49,7 +49,7 @@ async function handler(ctx) { const feedUrl = new URL(`news/feed/${documentId}/rss.xml`, rootUrl).href; const feedResponse = await ofetch(feedUrl); - currentUrl = feedResponse.match(/([\w-./:?]+)<\/link>/)[1]; + currentUrl = feedResponse.match(/([\w./:?-]+)<\/link>/)[1]; } const currentResponse = await ofetch(currentUrl); @@ -124,7 +124,7 @@ async function handler(ctx) { item.title = content('meta[property="og:title"]').prop('content'); item.description = ''; - const enclosurePattern = String.raw`"(?:MIME|content)?Type":"([\w]+/[\w]+)".*?"(?:fileS|s)?ize":(\d+),.*?"url":"([\w-.:/?]+)"`; + const enclosurePattern = String.raw`"(?:MIME|content)?Type":"(\w+/\w+)".*?"(?:fileS|s)?ize":(\d+),.*?"url":"([\w.:/?-]+)"`; const enclosureMatches = detailResponse.match(new RegExp(enclosurePattern, 'g')); diff --git a/lib/routes/acfun/video.ts b/lib/routes/acfun/video.ts index 39ca0200b..d6732a0dc 100644 --- a/lib/routes/acfun/video.ts +++ b/lib/routes/acfun/video.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const list = $('#ac-space-video-list a').toArray(); const image = $('head style:contains("user-photo")') .text() - .match(/.user-photo{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)?.[1]; + .match(/.user-photo\{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)?.[1]; return { title, diff --git a/lib/routes/aip/journal.ts b/lib/routes/aip/journal.ts index 68bea00e2..f5c718e57 100644 --- a/lib/routes/aip/journal.ts +++ b/lib/routes/aip/journal.ts @@ -43,7 +43,7 @@ async function handler(ctx) { const $ = load(response); const jrnlName = $('meta[property="og:title"]') .attr('content') - .match(/(?:[^=]*=)?\s*([^>]+)\s*/)[1]; + .match(/(?:[^=]*=)?\s*([^>]+)/)[1]; const publication = $('.al-article-item-wrap.al-normal'); const list = publication.toArray().map((item) => { diff --git a/lib/routes/altotrain/news.ts b/lib/routes/altotrain/news.ts index f541b8ed2..8bfbe028d 100644 --- a/lib/routes/altotrain/news.ts +++ b/lib/routes/altotrain/news.ts @@ -71,7 +71,7 @@ function extractItem(a: Cheerio, language: string) { const descEl = a.find('p').first(); const description = descEl.text().trim(); - const dateMatch = language === 'fr' ? description.match(/(\d{1,2} [a-zéû]+[.]? \d{4})/i) : description.match(/([A-Z][a-z]+[.]? \d{1,2}, \d{4})/); + const dateMatch = language === 'fr' ? description.match(/(\d{1,2} [a-zéû]+\.? \d{4})/i) : description.match(/([A-Z][a-z]+\.? \d{1,2}, \d{4})/); const pubDateStr = dateMatch ? dateMatch[1].trim() : ''; const pubDate = parseDate(pubDateStr); diff --git a/lib/routes/anthropic/research.ts b/lib/routes/anthropic/research.ts index cfba01932..c19564ebf 100644 --- a/lib/routes/anthropic/research.ts +++ b/lib/routes/anthropic/research.ts @@ -47,7 +47,7 @@ async function handler() { } } - const partRegex = /^([0-9a-zA-Z]+):([0-9a-zA-Z]+)?(\[.*)$/; + const partRegex = /^([0-9a-z]+):([0-9a-z]+)?(\[.*)$/i; const fd = textList .join('') .split('\n') diff --git a/lib/routes/aqara/news.ts b/lib/routes/aqara/news.ts index 898ea0dac..6dc95b0fe 100644 --- a/lib/routes/aqara/news.ts +++ b/lib/routes/aqara/news.ts @@ -23,7 +23,7 @@ async function handler(ctx) { const $ = load(response); let items = response - .match(/(parm\.newsTitle[\S\s]*?arr\.push\(parm\))/g) + .match(/(parm\.newsTitle[\s\S]*?arr\.push\(parm\))/g) .slice(0, limit) .map((item) => ({ title: item.match(/parm\.newsTitle = '(.*?)'/)[1], diff --git a/lib/routes/arcteryx/regear-new-arrivals.tsx b/lib/routes/arcteryx/regear-new-arrivals.tsx index 6785aa96c..7394abf81 100644 --- a/lib/routes/arcteryx/regear-new-arrivals.tsx +++ b/lib/routes/arcteryx/regear-new-arrivals.tsx @@ -42,7 +42,7 @@ async function handler() { const data = response.data; const $ = load(data); const contents = $('script:contains("window.__PRELOADED_STATE__")').text(); - const regex = /{.*}/; + const regex = /\{.*\}/; let items = JSON.parse(contents.match(regex)[0]).shop.items; items = items.filter((item) => item.availableSizes.length !== 0); diff --git a/lib/routes/bbc/utils.tsx b/lib/routes/bbc/utils.tsx index abfd5d3bc..54b8a277e 100644 --- a/lib/routes/bbc/utils.tsx +++ b/lib/routes/bbc/utils.tsx @@ -355,7 +355,7 @@ export const extractInitialData = ($: CheerioAPI): any => { const initialDataText = JSON.parse( $('script:contains("window.__INITIAL_DATA__")') .text() - .match(/window\.__INITIAL_DATA__\s*=\s*(.*);/)?.[1] ?? '"{}"' + .match(/window\.__INITIAL_DATA__\s*=\s*(\S.*)?;/)?.[1] ?? '"{}"' ); return JSON.parse(initialDataText); diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index 213727ec6..c37546af9 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -117,7 +117,7 @@ const getWbiVerifyString = () => { // 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, // 62, 11, 36, 20, 34, 44, 52, // ]; - const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+]/)); + const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+\]/)); const o = []; for (const t of array) { r.charAt(t) && o.push(r.charAt(t)); @@ -350,7 +350,7 @@ const getArticleDataFromCvid = async (cvid, uid) => { const newFormatData = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*(\S.*?)?;\(/)[1] ); if (newFormatData?.readInfo?.opus?.content?.paragraphs) { diff --git a/lib/routes/bjsk/index.ts b/lib/routes/bjsk/index.ts index 11bf8d02d..e5deceeea 100644 --- a/lib/routes/bjsk/index.ts +++ b/lib/routes/bjsk/index.ts @@ -55,7 +55,8 @@ async function handler(ctx) { item.description = $('.article-main').html(); item.author = $('.info') .text() - .match(/作者:(.*)\s+来源/)[1]; + .match(/作者:(.*?)来源/)[1] + .trim(); return item; }) ) diff --git a/lib/routes/bjtu/gs.ts b/lib/routes/bjtu/gs.ts index dec0963cf..4711d23ba 100644 --- a/lib/routes/bjtu/gs.ts +++ b/lib/routes/bjtu/gs.ts @@ -130,7 +130,7 @@ const getItem = (item, selector) => { const newsDate = item .find('span') .text() - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)[0]; + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)[0]; const infoTitle = newsInfo.text(); const link = rootURL + newsInfo.attr('href'); diff --git a/lib/routes/bjwxdxh/index.ts b/lib/routes/bjwxdxh/index.ts index b90d7a8af..d8060cc9a 100644 --- a/lib/routes/bjwxdxh/index.ts +++ b/lib/routes/bjwxdxh/index.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const content = load(response.data); const info = content('div.info') .text() - .match(/作者:(.*?)\s+发布于:(.*?\s+.*?)\s/); + .match(/作者:(\S*)\s+发布于:(\S*\s+.*?)\s/); item.author = info[1]; item.pubDate = timezone(parseDate(info[2], 'YYYY-MM-DD HH:mm:ss'), +8); item.description = content('div#con').html().trim().replaceAll('\n', ''); diff --git a/lib/routes/bloomberg/utils.ts b/lib/routes/bloomberg/utils.ts index 215c3b716..3d9da6572 100644 --- a/lib/routes/bloomberg/utils.ts +++ b/lib/routes/bloomberg/utils.ts @@ -55,7 +55,7 @@ const apiEndpoints = { }, }; -const pageTypeRegex1 = /\/(?[\w-]*?)\/(?\d{4}-\d{2}-\d{2}\/.*)/; +const pageTypeRegex1 = /\/(?[\w-]*)\/(?\d{4}-\d{2}-\d{2}\/.*)/; const pageTypeRegex2 = /(?features\/|graphics\/)(?.*)/; const regex = [pageTypeRegex1, pageTypeRegex2]; diff --git a/lib/routes/booru/mmda.ts b/lib/routes/booru/mmda.ts index 69cc18361..779af1236 100644 --- a/lib/routes/booru/mmda.ts +++ b/lib/routes/booru/mmda.ts @@ -95,7 +95,7 @@ async function handler(ctx) { statisticsTages.find('li, br, strong').remove(); const statisticsStr = statisticsTages.text(); - const regex = /(?[^\s:]+)\s*:\s*(?.+)/gm; + const regex = /(?[^\s:]+)\s*:\s*(?.+)/g; const result = {}; for (const match of statisticsStr.matchAll(regex)) { const { key, value } = match.groups ?? ({} as { key: string; value: string }); diff --git a/lib/routes/bse/index.ts b/lib/routes/bse/index.ts index 6154cf51c..0add93200 100644 --- a/lib/routes/bse/index.ts +++ b/lib/routes/bse/index.ts @@ -183,7 +183,7 @@ async function handler(ctx) { }, }); - const data = JSON.parse(response.data.match(/null\(\[({.*})]\)/)[1]); + const data = JSON.parse(response.data.match(/null\(\[(\{.*\})\]\)/)[1]); let items: DataItem[]; diff --git a/lib/routes/caixin/category.ts b/lib/routes/caixin/category.ts index 0fd9f0e20..9e4e7dc83 100644 --- a/lib/routes/caixin/category.ts +++ b/lib/routes/caixin/category.ts @@ -60,7 +60,7 @@ async function handler(ctx) { const entity = JSON.parse( $('script') .text() - .match(/var entity = ({.*?})/)[1] + .match(/var entity = (\{.*?\})/)[1] ); const { diff --git a/lib/routes/caixin/utils-fulltext.ts b/lib/routes/caixin/utils-fulltext.ts index e17c54b6b..bc90876c3 100644 --- a/lib/routes/caixin/utils-fulltext.ts +++ b/lib/routes/caixin/utils-fulltext.ts @@ -14,7 +14,7 @@ export async function getFulltext(url: string) { if (!config.caixin.cookie) { return; } - if (!/(\d+)\.html/.test(url)) { + if (!/\d+\.html/.test(url)) { return; } const articleID = url.match(/(\d+)\.html/)[1]; diff --git a/lib/routes/cas/sim/kyjz.ts b/lib/routes/cas/sim/kyjz.ts index 116fc6ccb..1e61deea2 100644 --- a/lib/routes/cas/sim/kyjz.ts +++ b/lib/routes/cas/sim/kyjz.ts @@ -55,7 +55,7 @@ async function handler() { const $ = load(response.data); const author = $('.qtinfo.hidden-lg.hidden-md.hidden-sm').text(); - const reg = /文章来源:(.*?)\|/g; + const reg = /文章来源:(.*?)\|/; item.title = $('p.wztitle').text().trim(); item.author = reg.exec(author)[1].toString().trim(); diff --git a/lib/routes/chinaratings/credit-research.ts b/lib/routes/chinaratings/credit-research.ts index 7baab932b..55259a9f6 100644 --- a/lib/routes/chinaratings/credit-research.ts +++ b/lib/routes/chinaratings/credit-research.ts @@ -59,7 +59,7 @@ export const handler = async (ctx: Context): Promise => { const metaStr: string = $$('div.newshead p span, div.title p span').text(); const pubDateStr: string | undefined = metaStr?.match(/(\d{4}-\d{2}-\d{2})/)?.[1]; - const authors: DataItem['author'] = metaStr?.match(/来源:(.*?)/)?.[1]; + const authors: DataItem['author'] = metaStr?.match(/来源:(.*)/)?.[1]; const upDatedStr: string | undefined = pubDateStr; let processedItem: DataItem = { diff --git a/lib/routes/chnmus/exhibition.tsx b/lib/routes/chnmus/exhibition.tsx index 96bf680ef..8fae9bb87 100644 --- a/lib/routes/chnmus/exhibition.tsx +++ b/lib/routes/chnmus/exhibition.tsx @@ -17,7 +17,7 @@ const extractDates = (durationStr: string) => { return { startDate, endDate }; } - const parts = durationStr.split(/——|-|—|~/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency + const parts = durationStr.split(/——|[-—~]/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency const startStr = parts[0]; const endStr = parts[1]; diff --git a/lib/routes/cih-index/report.ts b/lib/routes/cih-index/report.ts index b3f6a0263..a61197bdb 100644 --- a/lib/routes/cih-index/report.ts +++ b/lib/routes/cih-index/report.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*({.*?});/)?.[1] || '{}' + .match(/window\.__INITIAL_STATE__\s*=\s*(\{.*?\});/)?.[1] || '{}' ); const { dataResult, indNavLists, secondNameFilter, tagList, param } = initialState.data; diff --git a/lib/routes/cisia/index.ts b/lib/routes/cisia/index.ts index c1e5832b9..df9098622 100644 --- a/lib/routes/cisia/index.ts +++ b/lib/routes/cisia/index.ts @@ -35,7 +35,7 @@ export const handler = async (ctx) => { items = await Promise.all( items.map((item) => cache.tryGet(item.link, async () => { - if (!/^https?:\/\/www\.cisia\.org(\/[^\s]*)?$/.test(item.link)) { + if (!/^https?:\/\/www\.cisia\.org(?:\/\S*)?$/.test(item.link)) { return item; } diff --git a/lib/routes/cline/blog.ts b/lib/routes/cline/blog.ts index 7455759c8..4ae95ce50 100644 --- a/lib/routes/cline/blog.ts +++ b/lib/routes/cline/blog.ts @@ -21,7 +21,7 @@ function extractArticlesFromDOM($: CheerioAPI): DataItem[] { // Extract date and author with single regex const metaText = element.find('.text-sm.text-slate-500').text().trim(); - const metaMatch = metaText.match(/^([^•]+)\s*•\s*([A-Za-z]+\s+\d{1,2},?\s+\d{4})/); + const metaMatch = metaText.match(/^([^•]+)•\s*([A-Z]+\s+\d{1,2},?\s+\d{4})/i); const author = metaMatch ? metaMatch[1].trim() : 'Cline Team'; const pubDate = metaMatch ? parseDate(metaMatch[2]) : undefined; diff --git a/lib/routes/cool18/index.ts b/lib/routes/cool18/index.ts index f9d9da1b1..96b9f7b50 100644 --- a/lib/routes/cool18/index.ts +++ b/lib/routes/cool18/index.ts @@ -65,7 +65,7 @@ function buildUrl(rootUrl: string, type: PostType, keyword: string | undefined, function extractHomeList($: CheerioAPI, rootUrl: string, limit: number): DataItem[] { try { const scriptText = $('script:contains("_PageData")').text(); - const match = scriptText.match(/const\s+_PageData\s*=\s*(\[[\s\S]*?]);/); + const match = scriptText.match(/const\s+_PageData\s*=\s*(\[[\s\S]*?\]);/); if (!match?.[1]) { return []; diff --git a/lib/routes/ctinews/topic.ts b/lib/routes/ctinews/topic.ts index ec1d4b745..9560bcacb 100644 --- a/lib/routes/ctinews/topic.ts +++ b/lib/routes/ctinews/topic.ts @@ -88,7 +88,7 @@ async function handler(ctx) { const $ = load(response); if (item.link?.includes('/videos/')) { const ldJson = JSON.parse($('script[type="application/ld+json"]:contains("VideoObject")').text()); - const videoId = ldJson.embedUrl.match(/embed\/([a-zA-Z0-9_-]+)/)?.[1]; + const videoId = ldJson.embedUrl.match(/embed\/([\w-]+)/)?.[1]; item.description = `
` + diff --git a/lib/routes/dailypush/utils.ts b/lib/routes/dailypush/utils.ts index 1fd24ec3a..8a55e7305 100644 --- a/lib/routes/dailypush/utils.ts +++ b/lib/routes/dailypush/utils.ts @@ -143,7 +143,7 @@ function extractCategories(article: ReturnType, $: CheerioAPI): stri const tagText = tagElement.text().trim(); // Skip summary/stats links and navigation - if (tagHref && tagText && !tagHref.includes('article/') && !tagHref.includes('Summary') && tagText.length < 50 && !/^(Summary|stats|About|Tags|Toggle|Trending|Latest|Previous|Next)$/i.test(tagText)) { + if (tagHref && tagText && !tagHref.includes('article/') && !tagHref.includes('Summary') && tagText.length < 50 && !/^(?:Summary|stats|About|Tags|Toggle|Trending|Latest|Previous|Next)$/i.test(tagText)) { return tagText; } return null; diff --git a/lib/routes/daum/potplayer.ts b/lib/routes/daum/potplayer.ts index 919afa216..960c98389 100644 --- a/lib/routes/daum/potplayer.ts +++ b/lib/routes/daum/potplayer.ts @@ -20,14 +20,14 @@ export const handler = async (ctx: Context): Promise => { // Group 3: Trailing hyphens (unused, but for context) // Group 4: Update content // Uses global and multiline flags for all matches and line start/end anchors - const updateRegex = /^(-+)\s*\n(.*?)\s*\n(-+)\s*\n([\s\S]*?)(?=\n-{2,}|<\/p>)/gm; + const updateRegex = /^-+[^\S\n]*\n(.*)\r?\n-+[^\S\n]*\n([\s\S]*?)(?=\n-{2}|<\/p>)/gm; const items: DataItem[] = []; let match: RegExpExecArray | null; while ((match = updateRegex.exec(response)) !== null && items.length < limit) { - const headerLine: string | undefined = match[2].trim(); - const description: string | undefined = match[4].trim()?.replaceAll(/(\s[+-])/g, '
$1'); + const headerLine: string | undefined = match[1].trim(); + const description: string | undefined = match[2].trim()?.replaceAll(/(\s[+-])/g, '
$1'); let version = 'N/A'; let pubDateStr: string | undefined = undefined; diff --git a/lib/routes/dayanzai/index.ts b/lib/routes/dayanzai/index.ts index abb37cb3a..11db2149f 100644 --- a/lib/routes/dayanzai/index.ts +++ b/lib/routes/dayanzai/index.ts @@ -41,7 +41,7 @@ async function handler(ctx) { const response = await got.get(currentUrl); const $ = load(response.data); const lists = $('div.c-box > div > div.c-zx-list > ul > li'); - const reg = /日期:(.*?(\s\(.*?\))?)\s/; + const reg = /日期:(.*?(?:\s\(.*?\))?)\s/; const list = lists.toArray().map((item) => { item = $(item).find('div'); let date = reg.exec(item.find('div.r > p.other').text())[1]; diff --git a/lib/routes/dcard/utils.ts b/lib/routes/dcard/utils.ts index 083c736d6..35bdc8594 100644 --- a/lib/routes/dcard/utils.ts +++ b/lib/routes/dcard/utils.ts @@ -28,7 +28,7 @@ const ProcessFeed = async (items, cookies, browser, limit, cache) => { const data = JSON.parse(response); let body = data.content; body = body.replaceAll(/(?=https?:\/\/).*?(?<=\.(jpe?g|gif|png))/gi, (m) => ``); - body = body.replaceAll(/(?=https?:\/\/).*(??)$/gim, (m) => `${m}`); + body = body.replaceAll(/(?=https?:\/\/).+(??)$/gim, (m) => `${m}`); body = body.replaceAll('\n', '
'); return body; diff --git a/lib/routes/dealstreetasia/home.ts b/lib/routes/dealstreetasia/home.ts index e0959e5b0..177bbf62e 100644 --- a/lib/routes/dealstreetasia/home.ts +++ b/lib/routes/dealstreetasia/home.ts @@ -60,7 +60,7 @@ async function fetchPage() { link: item.post_url || item.link || '', description: item.post_excerpt || item.excerpt || '', pubDate: item.post_date ? new Date(item.post_date).toUTCString() : item.date ? new Date(item.date).toUTCString() : '', - category: item.category_link ? item.category_link.replaceAll(/(<([^>]+)>)/gi, '') : '', // Clean HTML if category_link exists + category: item.category_link ? item.category_link.replaceAll(/(<([^>]+)>)/g, '') : '', // Clean HTML if category_link exists image: item.image_url ? item.image_url.replace(/\?.*$/, '') : '', // Remove query parameters if image_url exists })); diff --git a/lib/routes/dedao/knowledge.tsx b/lib/routes/dedao/knowledge.tsx index 076b8e9a7..d5eb7c28c 100644 --- a/lib/routes/dedao/knowledge.tsx +++ b/lib/routes/dedao/knowledge.tsx @@ -5,7 +5,7 @@ import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -const mentionPattern = /<\u2267\u2746>{"name":"(.*?)","uid":"\d+","at":"1"}<\/\u2266\u2746>/g; +const mentionPattern = /<\u2267\u2746>\{"name":"(.*?)","uid":"\d+","at":"1"\}<\/\u2266\u2746>/g; const formatNoteText = (text = '') => text.replaceAll('\n\n', '

').replaceAll(mentionPattern, ' @$1'); diff --git a/lib/routes/dedao/user.tsx b/lib/routes/dedao/user.tsx index 164618039..39ebb3772 100644 --- a/lib/routes/dedao/user.tsx +++ b/lib/routes/dedao/user.tsx @@ -11,7 +11,7 @@ const types = { 12: '视频', }; -const mentionPattern = /<\u2267\u2746>{"name":"(.*?)","uid":"\d+","at":"1"}<\/\u2266\u2746>/g; +const mentionPattern = /<\u2267\u2746>\{"name":"(.*?)","uid":"\d+","at":"1"\}<\/\u2266\u2746>/g; const formatNoteText = (text = '') => text.replaceAll('\n\n', '

').replaceAll(mentionPattern, ' @$1'); diff --git a/lib/routes/dehenglaw/index.ts b/lib/routes/dehenglaw/index.ts index c5966d874..325ecf55d 100644 --- a/lib/routes/dehenglaw/index.ts +++ b/lib/routes/dehenglaw/index.ts @@ -70,7 +70,7 @@ export const handler = async (ctx) => { return { title: $('title') .text() - .replace(/\|.*?$/, `| ${$('li.onthis').text()}`), + .replace(/\|.*$/, `| ${$('li.onthis').text()}`), description: $('meta[name="Description"]').prop('content'), link: currentUrl, item: items, diff --git a/lib/routes/dianping/user.ts b/lib/routes/dianping/user.ts index 6698f1aae..9f7cf14ab 100644 --- a/lib/routes/dianping/user.ts +++ b/lib/routes/dianping/user.ts @@ -75,7 +75,7 @@ async function handler(ctx) { headerGeneratorOptions: PRESETS.MODERN_IOS, }); - const nickNameReg = /window\.nickName = "(.*?)"/g; + const nickNameReg = /window\.nickName = "(.*?)"/; const nickName = nickNameReg.exec(pageResponse as string)?.[1]; const response = await ofetch(`https://m.dianping.com/member/ajax/NobleUserFeeds?userId=${id}`, { diff --git a/lib/routes/dlnews/category.tsx b/lib/routes/dlnews/category.tsx index a30277817..d813b90c0 100644 --- a/lib/routes/dlnews/category.tsx +++ b/lib/routes/dlnews/category.tsx @@ -69,7 +69,7 @@ const extractArticle = (item) => const { data: response } = await got(item.link); const $ = load(response); const scriptTagContent = $('script#fusion-metadata').text(); - const jsonData = JSON.parse(scriptTagContent.match(/Fusion\.globalContent=({.*?});Fusion\.globalContentConfig/)[1]).content_elements; + const jsonData = JSON.parse(scriptTagContent.match(/Fusion\.globalContent=(\{.*?\});Fusion\.globalContentConfig/)[1]).content_elements; const filteredData = []; for (const v of jsonData) { if (v.type === 'header' && v.content.includes('What we’re reading')) { diff --git a/lib/routes/dnaindia/common.ts b/lib/routes/dnaindia/common.ts index e501d9b50..180919d42 100644 --- a/lib/routes/dnaindia/common.ts +++ b/lib/routes/dnaindia/common.ts @@ -44,7 +44,7 @@ export async function handler(ctx) { .map((item) => $(item).find('a').text()); // Process date const timeText = $('p.dna-update').text(); - const dateMatch = timeText.match(/Updated\s*:\s*([\w\s,:\d]+?)(?:\s*\||$)/); + const dateMatch = timeText.match(/Updated\s*:([\w\s,:]+)/); let time = dateMatch ? dateMatch[1].trim() : ''; time = time.replace(/\s+IST$/, ''); const pubDate = timezone(parseDate(time), +5.5); diff --git a/lib/routes/domp4/detail.ts b/lib/routes/domp4/detail.ts index ddeaf4515..a8500872b 100644 --- a/lib/routes/domp4/detail.ts +++ b/lib/routes/domp4/detail.ts @@ -30,7 +30,7 @@ function getDomList($, detailUrl) { export function getItemList($, detailUrl, second) { const encoded = $('.article script[type]') .text() - .match(/return p}\('(.*)',(\d+),(\d+),'(.*)'.split\(/); + .match(/return p\}\('(.*)',(\d+),(\d+),'(.*)'.split\(/); // 若 script 标签没有内容,直接解析 dom if (!encoded) { return getDomList($, detailUrl); diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index c927dd768..7d1b116ca 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -145,7 +145,7 @@ const ProcessFeedType3 = (item, response) => { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*((?:\S.*?)??);\(/)[1] ); // filter out undefined item diff --git a/lib/routes/dora-world/article.ts b/lib/routes/dora-world/article.ts index c903ee1cc..4c44d1392 100644 --- a/lib/routes/dora-world/article.ts +++ b/lib/routes/dora-world/article.ts @@ -85,6 +85,6 @@ async function getContent(nextBuildId: string, contentId: string) { content .html() ?.replaceAll(rubyRegex, '$1($2)') - ?.replaceAll(/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '') ?? ''; + ?.replaceAll(/[^\t\n\r\u0020-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/g, '') ?? ''; return description; } diff --git a/lib/routes/douban/other/replied.ts b/lib/routes/douban/other/replied.ts index 23767e6ce..c231d97ee 100644 --- a/lib/routes/douban/other/replied.ts +++ b/lib/routes/douban/other/replied.ts @@ -56,7 +56,7 @@ async function handler(ctx) { method: 'get', url: item.link, }); - const match = detailResponse.data.match(/'comments':(.*)}],/); + const match = detailResponse.data.match(/'comments':(.*)\}\],/); if (match.length > 1) { const content = load(detailResponse.data); diff --git a/lib/routes/douban/other/replies.ts b/lib/routes/douban/other/replies.ts index 7d2515a61..f77c32e03 100644 --- a/lib/routes/douban/other/replies.ts +++ b/lib/routes/douban/other/replies.ts @@ -55,7 +55,7 @@ async function handler(ctx) { url: item.link, }); - const comments = JSON.parse(detailResponse.data.match(/'comments':(.*)}],/)[1] + '}]'); + const comments = JSON.parse(detailResponse.data.match(/'comments':(.*)\}\],/)[1] + '}]'); for (const c of comments) { if (c.id === item.link.split('#')[1]) { diff --git a/lib/routes/dribbble/utils.tsx b/lib/routes/dribbble/utils.tsx index c7438b556..c82870fce 100644 --- a/lib/routes/dribbble/utils.tsx +++ b/lib/routes/dribbble/utils.tsx @@ -17,7 +17,7 @@ async function loadContent(link) { const shotData = JSON.parse( $('script') .text() - .match(/shotData:\s({.+?}),\n/)?.[1] ?? '{}' + .match(/shotData:\s(\{.+?\}),\n/)?.[1] ?? '{}' ); // Join multiple shots together by selecting elements with class 'media-shot' or 'main-shot' or 'block-media-wrapper' diff --git a/lib/routes/ehentai/ehapi.ts b/lib/routes/ehentai/ehapi.ts index ba9284bea..271f2fd86 100644 --- a/lib/routes/ehentai/ehapi.ts +++ b/lib/routes/ehentai/ehapi.ts @@ -161,7 +161,7 @@ function getBittorrent(cache, bittorrent_page_url) { const match = onclick.match(/'(.*?)'/); if (match) { bittorrent_url = match[1]; - const match_p = bittorrent_url.match(/torrent\?p=(.*?)$/); + const match_p = bittorrent_url.match(/torrent\?p=(.*)$/); if (match_p) { p = match_p[1]; } diff --git a/lib/routes/fanqienovel/page.ts b/lib/routes/fanqienovel/page.ts index 58bc7e73e..11fc5bf9d 100644 --- a/lib/routes/fanqienovel/page.ts +++ b/lib/routes/fanqienovel/page.ts @@ -76,7 +76,7 @@ async function handler(ctx: Context): Promise { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*);/)?.[1] ?? '{}' + .match(/window\.__INITIAL_STATE__\s*=\s*(\S.*);/)?.[1] ?? '{}' ); const page = initialState.page as Page; diff --git a/lib/routes/flashcat/blog.ts b/lib/routes/flashcat/blog.ts index 8c01e7510..0cb95e409 100644 --- a/lib/routes/flashcat/blog.ts +++ b/lib/routes/flashcat/blog.ts @@ -29,34 +29,32 @@ export const route: Route = { }; async function handlerRoute(): Promise { - const response = await ofetch('https://flashcat.cloud/blog/'); + const baseUrl = 'https://flashcat.cloud'; + const link = `${baseUrl}/blog/`; + const response = await ofetch(link); const $ = load(response); - const items = $('.post-preview') + const items = $('.fc-content-card') .toArray() .map((elem) => { - const $elem = $(elem); + const $item = $(elem); + const [author, date] = $item + .find('.fc-content-card-meta') + .text() + .split('·') + .map((s) => s.trim()); return { - title: $elem.find('.post-title').text(), - description: $elem.find('.post-content-preview').text(), - link: $elem.find('a').attr('href'), - pubDate: parseDate( - $elem - .find('.post-meta') - .text() - .match(/on\s+(\w+,\s+\w+\s+\d{1,2},\s+\d{4})/)?.[1] || '' - ), - author: - $elem - .find('.post-meta') - .text() - .match(/by\s+(.+?)\s+on/)?.[1] || '', + title: $item.find('.fc-content-card-title').text(), + description: $item.find('.fc-content-card-summary').text().trim(), + link: new URL($item.find('.fc-content-card-link').attr('href')!, baseUrl).href, + pubDate: date ? parseDate(date, 'YYYY-MM-DD') : undefined, + author, }; }); return { title: 'Flashcat 快猫星云博客', - link: 'https://flashcat.cloud/blog/', + link, item: items, }; } diff --git a/lib/routes/gamer/ani/anime.ts b/lib/routes/gamer/ani/anime.ts index b73df2011..0f8a527fa 100644 --- a/lib/routes/gamer/ani/anime.ts +++ b/lib/routes/gamer/ani/anime.ts @@ -41,7 +41,7 @@ async function handler(ctx) { } const anime = response.data.anime; - const title = anime.title.replaceAll(/\[\d+?]$/g, '').trim(); + const title = anime.title.replaceAll(/\[\d+\]$/g, '').trim(); const items = anime.volumes[0] .map((item) => ({ diff --git a/lib/routes/getitfree/index.ts b/lib/routes/getitfree/index.ts index 47998a802..988886abf 100644 --- a/lib/routes/getitfree/index.ts +++ b/lib/routes/getitfree/index.ts @@ -31,7 +31,7 @@ async function handler(ctx) { const { data: response } = await got(apiUrl); - const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*])$/)[1])).slice(0, limit).map((item) => { + const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*\])$/)[1])).slice(0, limit).map((item) => { const terminologies = item._embedded['wp:term']; const content = load(item.content?.rendered ?? item.content); diff --git a/lib/routes/gigazine/en.ts b/lib/routes/gigazine/en.ts index bd9127f7a..1f0f191b9 100644 --- a/lib/routes/gigazine/en.ts +++ b/lib/routes/gigazine/en.ts @@ -22,7 +22,7 @@ const getAbsoluteUrl = (path: string | undefined) => (path ? new URL(path, ROOT_ const getArticleAuthor = ($: ReturnType) => $('#article .items p') .text() - .match(/Posted by\s+(.+)$/)?.[1] + .match(/Posted by\s+(\S.*)$/)?.[1] ?.trim(); const getArticleCategories = ($: ReturnType) => [ ...new Set( diff --git a/lib/routes/globallawreview/index.ts b/lib/routes/globallawreview/index.ts index ea64e6d40..7fcf22106 100644 --- a/lib/routes/globallawreview/index.ts +++ b/lib/routes/globallawreview/index.ts @@ -50,7 +50,7 @@ async function handler(ctx) { item .find('p.p4') .text() - .match(/] (\d+\.\d+);/)[1], + .match(/\] (\d+\.\d+);/)[1], ], enclosure_url: link, enclosure_length: diff --git a/lib/routes/google/album.ts b/lib/routes/google/album.ts index 39f15f9a9..d62eae3da 100644 --- a/lib/routes/google/album.ts +++ b/lib/routes/google/album.ts @@ -28,7 +28,7 @@ async function handler(ctx) { const real_url = response.request.options.url.href; - const info = JSON.parse(response.data.match(/AF_initDataCallback.*?data:(\[[\S\s]*])\s/m)[1]) || []; + const info = JSON.parse(response.data.match(/AF_initDataCallback.*?data:(\[[\s\S]*\])\s/)[1]) || []; const album_name = info[3][1]; const owner_name = info[3][5][2]; diff --git a/lib/routes/google/scholar.ts b/lib/routes/google/scholar.ts index 69465a1ad..661518a0c 100644 --- a/lib/routes/google/scholar.ts +++ b/lib/routes/google/scholar.ts @@ -34,7 +34,7 @@ async function handler(ctx) { let description = `Google Scholar Monitor Query: ${query}`; if (params.includes('as_q=')) { - const reg = /as_q=(.*?)&/g; + const reg = /as_q=(.*?)&/; query = reg.exec(params)[1]; description = `Google Scholar Monitor Advanced Query: ${query}`; } else { diff --git a/lib/routes/gov/beijing/kw/index.ts b/lib/routes/gov/beijing/kw/index.ts index bab38a33d..e1e06e6c5 100644 --- a/lib/routes/gov/beijing/kw/index.ts +++ b/lib/routes/gov/beijing/kw/index.ts @@ -23,10 +23,10 @@ async function handler(ctx) { const title = $('a.bt_link').last().text().replace('>', ''); const dataJs = $('div.left.zhengce_right > script[language="javascript"]').html() || $('div.centent_width > script[language="javascript"]').html(); let items = dataJs - .match(/urls\[i]='(.*?)';headers\[i]="(.*?)";year\[i]='(\d+)';month\[i]='(\d+)';day\[i]='(\d+)';/g) + .match(/urls\[i\]='(.*?)';headers\[i\]="(.*?)";year\[i\]='(\d+)';month\[i\]='(\d+)';day\[i\]='(\d+)';/g) .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25) .map((item) => { - const result = item.match(/urls\[i]='(.*?)';headers\[i]="(.*?)";year\[i]='(\d+)';month\[i]='(\d+)';day\[i]='(\d+)';/); + const result = item.match(/urls\[i\]='(.*?)';headers\[i\]="(.*?)";year\[i\]='(\d+)';month\[i\]='(\d+)';day\[i\]='(\d+)';/); return { title: load(result[2])('a').attr('title') || result[2], link: new URL(result[1], rootUrl).href, diff --git a/lib/routes/gov/cac/index.ts b/lib/routes/gov/cac/index.ts index 1aec93ff5..5ce00fb02 100644 --- a/lib/routes/gov/cac/index.ts +++ b/lib/routes/gov/cac/index.ts @@ -29,7 +29,7 @@ async function handler(ctx) { .toArray() .map((item) => { const href = $(item).attr('href'); - if (href && /(?:http:)?\/\/www\.cac\.gov\.cn(.*?)\/(A.*?\.htm)/.test(href)) { + if (href && /(?:http:)?\/\/www\.cac\.gov\.cn.*?\/A.*?\.htm/.test(href)) { const matchArray = href.match(/(?:http:)?\/\/www\.cac\.gov\.cn(.*?)\/(A.*?\.htm)/); if (matchArray && matchArray.length > 2) { const path = matchArray[1]; diff --git a/lib/routes/gov/ccdi/utils.ts b/lib/routes/gov/ccdi/utils.ts index ff1366a58..39a394241 100644 --- a/lib/routes/gov/ccdi/utils.ts +++ b/lib/routes/gov/ccdi/utils.ts @@ -10,7 +10,7 @@ const cookieJar = new CookieJar(); const owner = '中央纪委国家监委网站'; const rootUrl = 'https://www.ccdi.gov.cn'; -const regex = /(?[A-Z_]+)=(?(?:.*?(?=; max-age)|[\dA-Fa-f]+))/gm; +const regex = /(?[A-Z_]+)=(?.*?(?=; max-age)|[\dA-Fa-f]+)/g; const parseCookie = async (body) => { let m; diff --git a/lib/routes/gov/cn/news/index.ts b/lib/routes/gov/cn/news/index.ts index 326d19245..3fa9e6252 100644 --- a/lib/routes/gov/cn/news/index.ts +++ b/lib/routes/gov/cn/news/index.ts @@ -88,13 +88,13 @@ async function handler(ctx) { let pubDate; let author; let category; - if (/dysMiddleResultConItemTitle/g.test(item.html())) { + if (/dysMiddleResultConItemTitle/.test(item.html())) { if (contentUrl.includes('content')) { fullTextGet = await got.get(contentUrl); fullTextData = load(fullTextGet.data); fullTextData('.shuzi').remove(); // 移除videobg的图片 fullTextData('#myFlash').remove(); // 移除flash - description = /pages_content/g.test(fullTextData.html()) ? fullTextData('.pages_content').html() : fullTextData('#UCAP-CONTENT').html(); + description = /pages_content/.test(fullTextData.html()) ? fullTextData('.pages_content').html() : fullTextData('#UCAP-CONTENT').html(); } else { description = item.find('a').text(); // 忽略获取吹风会的全文 } @@ -105,13 +105,13 @@ async function handler(ctx) { pubDate = timezone(parseDate(fullTextData('meta[name="firstpublishedtime"]').attr('content'), 'YYYY-MM-DD HH:mm:ss'), 8); author = fullTextData('meta[name="author"]').attr('content'); category = fullTextData('meta[name="keywords"]').attr('content').split(/[,;]/); - if (/zhengceku/g.test(contentUrl)) { + if (/zhengceku/.test(contentUrl)) { // 政策文件库 description = fullTextData('.pages_content').html(); } else { fullTextData('.shuzi').remove(); // 移除videobg的图片 fullTextData('#myFlash').remove(); // 移除flash - description = /UCAP-CONTENT/g.test($1) ? fullTextData('#UCAP-CONTENT').html() : fullTextData('body').html(); + description = /UCAP-CONTENT/.test($1) ? fullTextData('#UCAP-CONTENT').html() : fullTextData('body').html(); } } else { description = item.find('a').text(); // 忽略获取吹风会的全文 diff --git a/lib/routes/gov/general/general.ts b/lib/routes/gov/general/general.ts index 7766acb8a..db03fe427 100644 --- a/lib/routes/gov/general/general.ts +++ b/lib/routes/gov/general/general.ts @@ -186,7 +186,7 @@ const gdgov = async (info, ctx) => { title: data.art_title, description: renderZcjdpt(data), pubDate: timezone(parseDate(data.pub_time), +8), - author: /(本|本网|本站)/.test(data.pub_unite) ? authorisme : data.pub_unite, + author: /本/.test(data.pub_unite) ? authorisme : data.pub_unite, }; }); } else if (idlink.host === 'mp.weixin.qq.com') { @@ -217,7 +217,7 @@ const gdgov = async (info, ctx) => { title, description, pubDate: timezone(parseDate(pubDate, pubDate_format), +8), - author: /本|本网|本站/.test(author) ? authorisme : author, + author: /本/.test(author) ? authorisme : author, }; }); } diff --git a/lib/routes/gov/guangdong/tqyb/tfxtq.tsx b/lib/routes/gov/guangdong/tqyb/tfxtq.tsx index 3dde4b33a..a2c09e3e2 100644 --- a/lib/routes/gov/guangdong/tqyb/tfxtq.tsx +++ b/lib/routes/gov/guangdong/tqyb/tfxtq.tsx @@ -34,7 +34,7 @@ async function handler() { const tfxtqJsUrl = `${rootUrl}/data/gzWeather/weatherTips.js`; const response = await got.get(tfxtqJsUrl); - const data = JSON.parse(`[{${response.data.match(/Tips = {(.*?)}/)[1]}}]`); + const data = JSON.parse(`[{${response.data.match(/Tips = \{(.*?)\}/)[1]}}]`); const items = data.map((item) => ({ title: item.title, diff --git a/lib/routes/gov/miit/wjfb.ts b/lib/routes/gov/miit/wjfb.ts index 13c53ff14..0f4252db3 100644 --- a/lib/routes/gov/miit/wjfb.ts +++ b/lib/routes/gov/miit/wjfb.ts @@ -72,7 +72,7 @@ async function handler(ctx) { item.description = content('#con_con') .html() - ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); + ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); return item; }) diff --git a/lib/routes/gov/miit/yjzj.ts b/lib/routes/gov/miit/yjzj.ts index 2b9deec1d..8138f23f6 100644 --- a/lib/routes/gov/miit/yjzj.ts +++ b/lib/routes/gov/miit/yjzj.ts @@ -71,7 +71,7 @@ async function handler() { item.description = content('#con_con') .html() - ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); + ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); return item; }) diff --git a/lib/routes/gov/mofcom/article.ts b/lib/routes/gov/mofcom/article.ts index 6fd06e75f..d861364e4 100644 --- a/lib/routes/gov/mofcom/article.ts +++ b/lib/routes/gov/mofcom/article.ts @@ -47,7 +47,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { let responses = await got(item.link); // xwfb/xwlxfbh || xwfb/xwztfbh - const redirect = responses.data.match(/_cofing1={href:"(.*)",type/) || responses.data.match(/window\.location\.href='(.*)'/); + const redirect = responses.data.match(/_cofing1=\{href:"(.*)",type/) || responses.data.match(/window\.location\.href='(.*)'/); if (redirect) { responses = await got(redirect[1], { headers: { diff --git a/lib/routes/gov/nrta/news.ts b/lib/routes/gov/nrta/news.ts index 8cd9d7ee0..519f29240 100644 --- a/lib/routes/gov/nrta/news.ts +++ b/lib/routes/gov/nrta/news.ts @@ -45,7 +45,7 @@ async function handler(ctx) { url: currentUrl, }); - const regex = /(?=\s*<)/gi; + const regex = /(?=\s*<)/gi; const data = response.data.replaceAll(regex, '$1'); const $ = load(data, { diff --git a/lib/routes/gov/nsfc/index.ts b/lib/routes/gov/nsfc/index.ts index 92a432043..124e308b1 100644 --- a/lib/routes/gov/nsfc/index.ts +++ b/lib/routes/gov/nsfc/index.ts @@ -46,7 +46,7 @@ async function handler(ctx) { title: item.prop('title') ?? item.text(), link: new URL(item.prop('href'), rootUrl).href, guid: `nsfc-${item.prop('id')}`, - pubDate: parseDate(item.next().text().replace(/\[]/g, '', ['YYYY-MM-DD', 'YY-MM-DD'])), + pubDate: parseDate(item.next().text().replace(/\[\]/g, '', ['YYYY-MM-DD', 'YY-MM-DD'])), }; }); diff --git a/lib/routes/gov/stats/index.tsx b/lib/routes/gov/stats/index.tsx index c7d31461d..b43af12be 100644 --- a/lib/routes/gov/stats/index.tsx +++ b/lib/routes/gov/stats/index.tsx @@ -115,7 +115,7 @@ async function handler(ctx) { // articles from www.news.cn or www.gov.cn - if (/(news\.cn|www\.gov\.cn)/.test(item.link)) { + if (/news\.cn|www\.gov\.cn/.test(item.link)) { if (content('.year').text()) { item.pubDate = timezone(parseDate(`${content('.year').text()}/${content('.day').text()} ${content('.time').text()}`, 'YYYY/MM/DD HH:mm:ss'), +8); item.author = content('.source') diff --git a/lib/routes/gov/zhengce/govall.ts b/lib/routes/gov/zhengce/govall.ts index a1f13b5aa..ca70133c1 100644 --- a/lib/routes/gov/zhengce/govall.ts +++ b/lib/routes/gov/zhengce/govall.ts @@ -52,7 +52,7 @@ async function handler(ctx) { }); const query = `${params.toString()}&${advance}`; const res = await got.get(link, { - searchParams: query.replaceAll(/([\u4E00-\u9FA5])/g, (str) => encodeURIComponent(str)), + searchParams: query.replaceAll(/[\u4E00-\u9FA5]/g, (str) => encodeURIComponent(str)), }); const $ = load(res.data); diff --git a/lib/routes/gov/zj/ningbogzw-notice.ts b/lib/routes/gov/zj/ningbogzw-notice.ts index eda9789d0..61b26a86a 100644 --- a/lib/routes/gov/zj/ningbogzw-notice.ts +++ b/lib/routes/gov/zj/ningbogzw-notice.ts @@ -35,7 +35,7 @@ export const route: Route = { return { title: `宁波市国资委-${noticeCate}:${title.text()}`, link: `http://gzw.ningbo.gov.cn${title.attr('href')}`, - pubDate: parseDate($('p').text().replaceAll(/\[|]/g, '')), + pubDate: parseDate($('p').text().replaceAll(/\[|\]/g, '')), author: '宁波市国资委', description: title.text(), }; diff --git a/lib/routes/gov/zj/ningborsjnotice.ts b/lib/routes/gov/zj/ningborsjnotice.ts index e3f54a2b0..d9e06dad3 100644 --- a/lib/routes/gov/zj/ningborsjnotice.ts +++ b/lib/routes/gov/zj/ningborsjnotice.ts @@ -35,7 +35,7 @@ export const route: Route = { return { title: `宁波人社公告-${noticeCate}:${title.text()}`, link: `http://rsj.ningbo.gov.cn${title.attr('href')}`, - pubDate: parseDate($('.news_date').text().replaceAll(/\[|]/g, '')), + pubDate: parseDate($('.news_date').text().replaceAll(/\[|\]/g, '')), author: '宁波市人力资源和社会保障局', description: title.text(), }; diff --git a/lib/routes/guancha/personalpage.ts b/lib/routes/guancha/personalpage.ts index 74f482dde..13f8118d9 100644 --- a/lib/routes/guancha/personalpage.ts +++ b/lib/routes/guancha/personalpage.ts @@ -38,7 +38,7 @@ async function handler(ctx) { const minuteRelativeTime = /(\d+)\s*分钟前/; const hourRelativeTime = /(\d+)\s*小时前/; const yesterdayRelativeTime = /昨天\s*(\d+):(\d+)/; - const shortDate = /(\d+)-(\d+)\s*(\d+):(\d+)/; + const shortDate = /(\d+)-(\d+)\s+(\d+):(\d+)/; // offset to ADD for transforming China time to UTC const chinaToUtcOffset = -8 * 3600 * 1000; diff --git a/lib/routes/hk01/utils.tsx b/lib/routes/hk01/utils.tsx index 863be3af2..52ede4cd3 100644 --- a/lib/routes/hk01/utils.tsx +++ b/lib/routes/hk01/utils.tsx @@ -110,7 +110,7 @@ const ProcessItems = (items, limit, tryGet) => url: item.link, }); - const content = JSON.parse(detailResponse.data.match(/"__NEXT_DATA__" type="application\/json">({"props":.*})<\/script>/)[1]); + const content = JSON.parse(detailResponse.data.match(/"__NEXT_DATA__" type="application\/json">(\{"props":.*\})<\/script>/)[1]); item.description = renderDescription({ image: content.props.initialProps.pageProps.article.originalImage.cdnUrl, diff --git a/lib/routes/hkej/index.tsx b/lib/routes/hkej/index.tsx index eeeacd849..a1be1a98c 100644 --- a/lib/routes/hkej/index.tsx +++ b/lib/routes/hkej/index.tsx @@ -158,7 +158,7 @@ async function handler(ctx) { .toArray() .map((e) => content(e).text().trim()); item.description = renderDesc(articleImg, content('div#article-content').html()); - item.pubDate = timezone(/(今|昨)/.test(pubDate) ? parseRelativeDate(pubDate) : parseDate(pubDate, 'YYYY M D'), +8); + item.pubDate = timezone(/今|昨/.test(pubDate) ? parseRelativeDate(pubDate) : parseDate(pubDate, 'YYYY M D'), +8); return item; }) diff --git a/lib/routes/hongkong/chp.ts b/lib/routes/hongkong/chp.ts index 04de3eac8..916cd5f1d 100644 --- a/lib/routes/hongkong/chp.ts +++ b/lib/routes/hongkong/chp.ts @@ -70,7 +70,7 @@ async function handler(ctx) { url: apiUrl, }); - const list = JSON.parse(response.data.match(/"data":(\[{.*}])}/)[1]).map((item) => { + const list = JSON.parse(response.data.match(/"data":(\[\{.*\}\])\}/)[1]).map((item) => { let link: string; if (item.UrlPath_en) { diff --git a/lib/routes/hpoi/utils.ts b/lib/routes/hpoi/utils.ts index 3ca6700c4..5168114c2 100644 --- a/lib/routes/hpoi/utils.ts +++ b/lib/routes/hpoi/utils.ts @@ -24,7 +24,7 @@ const MAPs = { }; const ProcessFeed = async (type, id, order) => { - let link = MAPs[type].url.replace(/{id}/, id).replace(/{order}/, order || 'add'); + let link = MAPs[type].url.replace(/\{id\}/, id).replace(/\{order\}/, order || 'add'); let response = await got({ method: 'get', url: link, @@ -35,7 +35,7 @@ const ProcessFeed = async (type, id, order) => { let $ = load(response.data); if (type === 'work') { - const overviewLink = MAPs.overview.url.replace(/{id}/, id); + const overviewLink = MAPs.overview.url.replace(/\{id\}/, id); const overviewResponse = await got({ method: 'get', url: overviewLink, diff --git a/lib/routes/hupu/utils.ts b/lib/routes/hupu/utils.ts index 2e6dba710..7cce28d15 100644 --- a/lib/routes/hupu/utils.ts +++ b/lib/routes/hupu/utils.ts @@ -238,8 +238,8 @@ export function getEntryDetails(item: DataItem): Promise { // Possible formats: 10:21, 45分钟前, 09-15 19:57 const currentYear = new Date().getFullYear(); const currentDate = new Date(); - const monthDayTimePattern = /^(\d{2})-(\d{2}) (\d{2}):(\d{2})$/; - const timeOnlyPattern = /^(\d{1,2}):(\d{2})$/; + const monthDayTimePattern = /^\d{2}-\d{2} \d{2}:\d{2}$/; + const timeOnlyPattern = /^\d{1,2}:\d{2}$/; let processedDateString = pubDateString; if (monthDayTimePattern.test(pubDateString)) { diff --git a/lib/routes/hypergryph/arknights/arktca.ts b/lib/routes/hypergryph/arknights/arktca.ts index 2a53156a8..53a08ca48 100644 --- a/lib/routes/hypergryph/arknights/arktca.ts +++ b/lib/routes/hypergryph/arknights/arktca.ts @@ -48,7 +48,7 @@ async function handler() { allUrlList.map(async (item) => { const { data: response } = await got(item); const $$ = load(response); - const regVol = /(?<=Vol. )(\w+)/; + const regVol = /(?<=Vol. )\w+/; const match = regVol.exec($$('div.vp-page-title').find('h1').text()); const volume = match ? match[0] : ''; const links = $$('div.theme-hope-content > ul a') diff --git a/lib/routes/ifeng/feng.ts b/lib/routes/ifeng/feng.ts index edea32c3d..3f5b86db2 100644 --- a/lib/routes/ifeng/feng.ts +++ b/lib/routes/ifeng/feng.ts @@ -67,7 +67,7 @@ async function handler(ctx) { const _allData = JSON.parse( $('script') .text() - .match(/var allData = ({.*?});/)[1] + .match(/var allData = (\{.*?\});/)[1] ); if (type === 'doc') { item.description = extractDoc(_allData.docData.contentData.contentList); diff --git a/lib/routes/ifeng/news.tsx b/lib/routes/ifeng/news.tsx index b8b86ec9d..050b5119a 100644 --- a/lib/routes/ifeng/news.tsx +++ b/lib/routes/ifeng/news.tsx @@ -29,7 +29,7 @@ async function handler(ctx) { const $ = load(response.data); - const newsStream = JSON.parse(response.data.match(/"newsstream":(\[.*?]),"cooperation"/)[1]); + const newsStream = JSON.parse(response.data.match(/"newsstream":(\[.*?\]),"cooperation"/)[1]); let items = newsStream.slice(0, limit).map((item) => ({ title: item.title, @@ -47,9 +47,9 @@ async function handler(ctx) { }); item.author = detailResponse.data.match(/"editorName":"(.*?)",/)[1]; - item.category = detailResponse.data.match(/},"keywords":"(.*?)",/)[1].split(','); + item.category = detailResponse.data.match(/\},"keywords":"(.*?)",/)[1].split(','); const image = item.description; - const description = JSON.parse(detailResponse.data.match(/"contentList":(\[.*?]),/)[1]).map((content) => content.data); + const description = JSON.parse(detailResponse.data.match(/"contentList":(\[.*?\]),/)[1]).map((content) => content.data); item.description = renderToString( <> {image ? ( diff --git a/lib/routes/inewsweek/index.ts b/lib/routes/inewsweek/index.ts index 24233919e..0fc92c9ca 100644 --- a/lib/routes/inewsweek/index.ts +++ b/lib/routes/inewsweek/index.ts @@ -62,7 +62,7 @@ async function handler(ctx) { parseDate( $('div.editor') .html() - .split(/(\s\s+)/)[2] + .split(/(\s{2,})/)[2] ), +8 ); diff --git a/lib/routes/iwara/utils.ts b/lib/routes/iwara/utils.ts index 573e3a1ef..90f05e4a3 100644 --- a/lib/routes/iwara/utils.ts +++ b/lib/routes/iwara/utils.ts @@ -19,7 +19,7 @@ export const parseThumbnail = (type: 'video' | 'image', item: any) => { } // regex borrowed from https://stackoverflow.com/a/3726073 - const match = /https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w-]*)(&(amp;)?[\w=?]*)?/.exec(item.embedUrl); + const match = /https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w-]*)(?:&(?:amp;)?[\w=?]*)?/.exec(item.embedUrl); if (match) { return ``; } diff --git a/lib/routes/ixigua/user-video.tsx b/lib/routes/ixigua/user-video.tsx index 6e87671c0..0b0ad9cea 100644 --- a/lib/routes/ixigua/user-video.tsx +++ b/lib/routes/ixigua/user-video.tsx @@ -44,7 +44,7 @@ async function handler(ctx) { throw new Error('Failed to find SSR_HYDRATED_DATA'); } - const jsonData = JSON.parse(jsData.match(/var\s+data\s*=\s*({.*?});/s)?.[1].replaceAll('undefined', 'null') || '{}'); + const jsonData = JSON.parse(jsData.match(/var\s+data\s*=\s*(\{.*?\});/s)?.[1].replaceAll('undefined', 'null') || '{}'); const { AuthorVideoList: { videoList: videoInfos }, diff --git a/lib/routes/jandan/utils.ts b/lib/routes/jandan/utils.ts index 040d4ff00..95af85481 100644 --- a/lib/routes/jandan/utils.ts +++ b/lib/routes/jandan/utils.ts @@ -20,7 +20,7 @@ export const extractPageId = async (url: string, referer: string): Promise { const content = $(script).html() || ''; - const match = content.match(/PAGE\s*=\s*{\s*id\s*:\s*(\d+)\s*}/); + const match = content.match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/); if (match) { pageId = match[1]; } diff --git a/lib/routes/javbus/index.tsx b/lib/routes/javbus/index.tsx index f5070c88b..bef6d7791 100644 --- a/lib/routes/javbus/index.tsx +++ b/lib/routes/javbus/index.tsx @@ -12,7 +12,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const toSize = (raw) => { - const matches = raw.match(/(\d+(\.\d+)?)(\w+)/); + const matches = raw.match(/(\d+(\.\d+)?)(\D\w*)/); return matches[3] === 'GB' ? matches[1] * 1024 : matches[1]; }; @@ -154,7 +154,7 @@ async function handler(ctx) { // To fetch magnets. try { - const matches = detailResponse.data.match(/var gid = (\d+);[\S\s]*var uc = (\d+);[\S\s]*var img = '(.*)';/); + const matches = detailResponse.data.match(/var gid = (\d+);[\s\S]*var uc = (\d+);[\s\S]*var img = '(.*)';/); const magnetResponse = await got({ method: 'get', diff --git a/lib/routes/jiemian/common.tsx b/lib/routes/jiemian/common.tsx index 1a2261340..5ced6cb40 100644 --- a/lib/routes/jiemian/common.tsx +++ b/lib/routes/jiemian/common.tsx @@ -34,7 +34,7 @@ export const handler = async (ctx): Promise => { const href = item.prop('href'); const link = href ? (href.startsWith('/') ? new URL(href, rootUrl).href : href) : undefined; - if (link && /\/(article|video)\/\w+\.html/.test(link)) { + if (link && /\/(?:article|video)\/\w+\.html/.test(link)) { items[link] = { title: item.text(), link, diff --git a/lib/routes/jike/user.ts b/lib/routes/jike/user.ts index be9308c43..dafa0a0b0 100644 --- a/lib/routes/jike/user.ts +++ b/lib/routes/jike/user.ts @@ -139,7 +139,7 @@ async function handler(ctx) { const single = { title: `${typeMap[item.type]}了: ${shortenTitle}`, - description: `${content}${linkTemplate}${imgTemplate}`.replace(/(
|\s)+$/, ''), + description: `${content}${linkTemplate}${imgTemplate}`.replace(/(?:
|\s)+$/, ''), pubDate: parseDate(item.createdAt), link: getLink(item.id, item.type), _extra: repostContent && { diff --git a/lib/routes/jike/utils.ts b/lib/routes/jike/utils.ts index 9fbe7b082..9fa9653a9 100644 --- a/lib/routes/jike/utils.ts +++ b/lib/routes/jike/utils.ts @@ -117,7 +117,7 @@ const topicDataHanding = (data, ctx) => // default: // break; // } - const imgUrl = /\.[\da-z]+?\?imageMogr2/.test(pic.picUrl) ? pic.picUrl.split('?imageMogr2/')[0] : pic.picUrl.replace(/thumbnail\/.+/, ''); + const imgUrl = /\.[\da-z]+\?imageMogr2/.test(pic.picUrl) ? pic.picUrl.split('?imageMogr2/')[0] : pic.picUrl.replace(/thumbnail\/.+/, ''); description += `
`; // description += `
{ + imgTag.replaceAll(/\b(src|data-src)="(?!http|\/\/)([^"]*)"/g, (_, attrName, relativePath) => { const absoluteImageUrl = new URL(relativePath, baseUrl).href; return `${attrName}="${absoluteImageUrl}"`; }) diff --git a/lib/routes/kunchengblog/essay.ts b/lib/routes/kunchengblog/essay.ts index 3bb85f90f..82ef34f8e 100644 --- a/lib/routes/kunchengblog/essay.ts +++ b/lib/routes/kunchengblog/essay.ts @@ -53,7 +53,7 @@ async function handler(ctx) { .map((item) => { const source = consumer.sourceContentFor(item).replaceAll(/\s\n/g, ''); - const processedSource = source.replaceAll(/(\w+)={+([^{}]+)}+/g, (match, key, value) => { + const processedSource = source.replaceAll(/(\w+)=\{+([^{}]+)\}+/g, (match, key, value) => { const processedValue = value.slice(1, -1).replaceAll('"', "'").trim(); return `${key}="${processedValue}"`; }); diff --git a/lib/routes/leetcode/dailyquestion-solution-cn.ts b/lib/routes/leetcode/dailyquestion-solution-cn.ts index eb19f5c5a..bcc30f5b0 100644 --- a/lib/routes/leetcode/dailyquestion-solution-cn.ts +++ b/lib/routes/leetcode/dailyquestion-solution-cn.ts @@ -186,7 +186,7 @@ async function handler() { const handleText = (s) => { // 处理多语言代码展示问题 - s = s.replaceAll(/(```)([\d#+A-Za-z-]+)\s*?(\[.*?])?\n/g, '\r\n###$2\r\n$1$2\r\n'); + s = s.replaceAll(/(```)([\d#+A-Z-]+)\s*?(\[.*?\])?\n/gi, '\r\n###$2\r\n$1$2\r\n'); return s; }; return { diff --git a/lib/routes/lfsyd/utils.tsx b/lib/routes/lfsyd/utils.tsx index 160de968b..0a5f05fe4 100644 --- a/lib/routes/lfsyd/utils.tsx +++ b/lib/routes/lfsyd/utils.tsx @@ -46,7 +46,7 @@ const ProcessForm = (form, type) => { }; const cleanHtml = (htmlString) => { - const regex = /(

|

)(.*?)?(标准|狂野)日报投稿.*?<\/strong>(.*?)?(<\/p>|<\/div>)(.|\n)*$/; + const regex = /(

|

)(.*?)(标准|狂野)日报投稿.*?<\/strong>(.*?)(<\/p>|<\/div>)(.|\n)*$/; const $ = load(htmlString.replace(regex, '')); $('.yingdi-car,.bbspost,.deck-set').each((i, e) => { diff --git a/lib/routes/line/utils.ts b/lib/routes/line/utils.ts index 087ddc365..190ab0461 100644 --- a/lib/routes/line/utils.ts +++ b/lib/routes/line/utils.ts @@ -20,7 +20,7 @@ const parseItems = (list) => Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const edition = item.link.match(/today\.line\.me\/(\w+?)\/v[23]\/.*$/)[1]; + const edition = item.link.match(/today\.line\.me\/(\w+)\/v[23]\/.*$/)[1]; let data; try { const response = await got(`${baseUrl}/webapi/portal/page/setting/article`, { diff --git a/lib/routes/lorientlejour/index.tsx b/lib/routes/lorientlejour/index.tsx index 0d54c8adf..f996e275d 100644 --- a/lib/routes/lorientlejour/index.tsx +++ b/lib/routes/lorientlejour/index.tsx @@ -86,7 +86,7 @@ async function viewCategory(category: string) { } async function handler(ctx) { - const categoryId = (ctx.req.param('category') ?? '977-Lebanon').split('|').map((item) => item.match(/^(\d+)/i)[0] ?? item); + const categoryId = (ctx.req.param('category') ?? '977-Lebanon').split('|').map((item) => item.match(/^(\d+)/)[0] ?? item); const limit = ctx.req.query('limit') ?? 25; let token; diff --git a/lib/routes/luolei/index.tsx b/lib/routes/luolei/index.tsx index 49e7a0822..50ce10c8b 100644 --- a/lib/routes/luolei/index.tsx +++ b/lib/routes/luolei/index.tsx @@ -70,7 +70,7 @@ export const handler = async (ctx) => { const { data: themeResponse } = await got(themeUrl); let items = themeResponse - .match(/{"title":".*?"string":".*?"}}/g) + .match(/\{"title":".*?"string":".*?"\}\}/g) .slice(0, limit) .map((item) => { item = JSON.parse( diff --git a/lib/routes/magazinelib/latest-magazine.tsx b/lib/routes/magazinelib/latest-magazine.tsx index 2c101052c..ee1f4c4b1 100644 --- a/lib/routes/magazinelib/latest-magazine.tsx +++ b/lib/routes/magazinelib/latest-magazine.tsx @@ -46,7 +46,7 @@ async function handler(ctx) { if (subTitle === undefined) { subTitle = ''; } else { - subTitle = subTitle.replaceAll(/[^\dA-Za-z]+/g, ' ').toUpperCase(); + subTitle = subTitle.replaceAll(/[^\dA-Z]+/gi, ' ').toUpperCase(); subTitle = ` - ${subTitle}`; } diff --git a/lib/routes/mastodon/utils.ts b/lib/routes/mastodon/utils.ts index 0bebaab81..8b33870b7 100644 --- a/lib/routes/mastodon/utils.ts +++ b/lib/routes/mastodon/utils.ts @@ -40,8 +40,8 @@ const parseStatuses = (data) => const accountRepostedBy = item.reblog ? item.account : null; item = item.reblog ?? item; - const content = item.content ? item.content.replaceAll(/|<\/span.*?>/gm, '') : ''; - const contentRemovedHtml = content.replaceAll(/<(?:.|\n)*?>/gm, '\n'); + const content = item.content ? item.content.replaceAll(/|<\/span.*?>/g, '') : ''; + const contentRemovedHtml = content.replaceAll(/<(?:.|\n)*?>/g, '\n'); const author = `${item.account.display_name} (@${item.account.acct})`; const link = item.url; diff --git a/lib/routes/maven/central.ts b/lib/routes/maven/central.ts index c6a1a6634..dee6acf2c 100644 --- a/lib/routes/maven/central.ts +++ b/lib/routes/maven/central.ts @@ -45,7 +45,7 @@ export const route: Route = { * Handles cases without delimiters: 5.0.0beta2, 7.0.0canary * Handles secondary versions: 1.0.0-M6.1 */ -const UNSTABLE_VERSION_REGEX = /[-_.]?(rc|m|snapshot|alpha|beta|preview|canary)[.\d]*$/i; +const UNSTABLE_VERSION_REGEX = /[-_.]?(?:rc|m|snapshot|alpha|beta|preview|canary)[.\d]*$/i; /** * Regex to extract date in the format YYYY-MM-DD HH:mm (e.g., 2024-09-22 04:19) diff --git a/lib/routes/metacritic/index.tsx b/lib/routes/metacritic/index.tsx index 9f402f8ce..4c867dab6 100644 --- a/lib/routes/metacritic/index.tsx +++ b/lib/routes/metacritic/index.tsx @@ -81,7 +81,7 @@ async function handler(ctx) { if (platforms.length || networks.length) { const labels = {}; - const labelPattern = String.raw`{label:"([^"]+)",value:(\d+),href:a,meta:{mcDisplayWeight`; + const labelPattern = String.raw`\{label:"([^"]+)",value:(\d+),href:a,meta:\{mcDisplayWeight`; for (const m of currentResponse.match(new RegExp(labelPattern, 'g'))) { const matches = m.match(new RegExp(labelPattern)); diff --git a/lib/routes/meteor/utils.ts b/lib/routes/meteor/utils.ts index 8b7de2293..124f57a07 100644 --- a/lib/routes/meteor/utils.ts +++ b/lib/routes/meteor/utils.ts @@ -25,7 +25,7 @@ const getBoards = (tryGet) => }); const renderDesc = (desc) => { - const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-]+)&?/g; + const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be.*(?:v=|v\/|\/)([\w-]+)&?/g; const matchYouTube = desc.match(youTube); const matchImgur = desc.match(/https:\/\/i.imgur.com\/\w*.(jpg|png|gif|jpeg)/g); const matchVideo = desc.match(/(https:\/\/storage\.meteor\.today\/video\/[\da-f]{24}\.)(mp4|mov|avi|flv|wmv|mpeg|mkv)/gi); diff --git a/lib/routes/mirror/index.ts b/lib/routes/mirror/index.ts index 658715621..4a0b4754a 100644 --- a/lib/routes/mirror/index.ts +++ b/lib/routes/mirror/index.ts @@ -39,7 +39,7 @@ async function handler(ctx) { const response = await got(currentUrl); - const data = JSON.parse(response.data.match(/"__NEXT_DATA__" type="application\/json">({"props":.*})<\/script>/)[1]); + const data = JSON.parse(response.data.match(/"__NEXT_DATA__" type="application\/json">(\{"props":.*\})<\/script>/)[1]); const items = Object.keys(data.props.pageProps.__APOLLO_STATE__) .filter((key) => key.startsWith('entry:')) diff --git a/lib/routes/modelscope/community.tsx b/lib/routes/modelscope/community.tsx index 341202c55..4a7a5314a 100644 --- a/lib/routes/modelscope/community.tsx +++ b/lib/routes/modelscope/community.tsx @@ -72,7 +72,7 @@ async function handler(ctx) { const initialData = JSON.parse( $('script') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*({.*?});/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*(\{.*?\});/)[1] ); item.description = renderDescription(item.thumb, item.description, initialData.pageData.detail.ext.content); diff --git a/lib/routes/mrinalxdev/blog.ts b/lib/routes/mrinalxdev/blog.ts index 5b2ff9c3d..e9485d133 100644 --- a/lib/routes/mrinalxdev/blog.ts +++ b/lib/routes/mrinalxdev/blog.ts @@ -41,7 +41,7 @@ async function handler() { const text = $el.text().trim(); // Extract date from link text (e.g., "2nd October, 2025 Redis 101 : From a Beginners POV") - const dateMatch = text.match(/^(\d{1,2}(?:st|nd|rd|th)\s+\w+,\s+\d{4})\s+(.+)$/); + const dateMatch = text.match(/^(\d{1,2}(?:st|nd|rd|th)\s+\w+,\s+\d{4})\s+(\S.*)$/); let date: string | undefined; let title: string; diff --git a/lib/routes/mydrivers/index.tsx b/lib/routes/mydrivers/index.tsx index 9cabfc039..b9754e5e1 100644 --- a/lib/routes/mydrivers/index.tsx +++ b/lib/routes/mydrivers/index.tsx @@ -53,7 +53,7 @@ async function handler(ctx) { let newTitle = ''; - if (!/^(\w+\/\w+)$/.test(category)) { + if (!/^\w+\/\w+$/.test(category)) { newTitle = `${title} - ${Object.hasOwn(categories, category) ? categories[category] : categories[Object.keys(categories)[0]]}`; category = `ac/${category}`; } diff --git a/lib/routes/mydrivers/rank.ts b/lib/routes/mydrivers/rank.ts index 7f5f115cc..88da71f1f 100644 --- a/lib/routes/mydrivers/rank.ts +++ b/lib/routes/mydrivers/rank.ts @@ -47,7 +47,7 @@ async function handler(ctx) { let items = $('a') .toArray() - .filter((item) => /\/(\d+)\.html?/.test($(item).prop('href'))) + .filter((item) => /\/\d+\.html?/.test($(item).prop('href'))) .slice(0, limit) .map((item) => { item = $(item); diff --git a/lib/routes/natgeo/dailyphoto.tsx b/lib/routes/natgeo/dailyphoto.tsx index 347b66384..935740022 100644 --- a/lib/routes/natgeo/dailyphoto.tsx +++ b/lib/routes/natgeo/dailyphoto.tsx @@ -51,7 +51,7 @@ async function handler() { const response = await cache.tryGet(apiUrl, async () => (await got(apiUrl)).data, config.cache.contentExpire, false); const $ = load(response); - const natgeo = JSON.parse($.html().match(/window\['__natgeo__']=(.*);/)[1]); + const natgeo = JSON.parse($.html().match(/window\['__natgeo__'\]=(.*);/)[1]); const media = natgeo.page.content.mediaspotlight.frms[0].mods[0].edgs[1].media; const items = media.map((item) => ({ diff --git a/lib/routes/nationalgeographic/latest-stories.tsx b/lib/routes/nationalgeographic/latest-stories.tsx index afda5cbc6..c3ba067ce 100644 --- a/lib/routes/nationalgeographic/latest-stories.tsx +++ b/lib/routes/nationalgeographic/latest-stories.tsx @@ -11,7 +11,7 @@ const findNatgeo = ($) => JSON.parse( $('script') .text() - .match(/\['__natgeo__']=({.*?});/)[1] + .match(/\['__natgeo__'\]=(\{.*?\});/)[1] ); type StoryMedia = { diff --git a/lib/routes/nature/utils.ts b/lib/routes/nature/utils.ts index bebefabc0..f30ada7aa 100644 --- a/lib/routes/nature/utils.ts +++ b/lib/routes/nature/utils.ts @@ -105,7 +105,7 @@ const getDataLayer = (html) => JSON.parse( html('script[data-test=dataLayer]') .text() - .match(/window\.dataLayer = \[(.*)];/s)[1] + .match(/window\.dataLayer = \[(.*)\];/s)[1] ); const cookieJar = new CookieJar(); diff --git a/lib/routes/ncpssd/newlist.ts b/lib/routes/ncpssd/newlist.ts index 8201212a2..61421915c 100644 --- a/lib/routes/ncpssd/newlist.ts +++ b/lib/routes/ncpssd/newlist.ts @@ -37,7 +37,7 @@ async function handler() { const title = $(p) .find('a') .text() - .replaceAll(/(\r\n|\n|\r)/gm, '') + .replaceAll(/(\r\n|\n|\r)/g, '') .trim(); const articleUrl = baseUrl + diff --git a/lib/routes/neu/yz.ts b/lib/routes/neu/yz.ts index 1b8ae5844..8e1959361 100644 --- a/lib/routes/neu/yz.ts +++ b/lib/routes/neu/yz.ts @@ -40,7 +40,7 @@ const parsePage = async (items, type) => { })(), author: type === DOWNLOAD_ID ? DOWNLOAD_AUTHOR : '', }; - if (type === DOWNLOAD_ID && /\.(pdf|docx?|xlsx?|zip|rar|7z)$/i.test(url)) { + if (type === DOWNLOAD_ID && /\.(?:pdf|docx?|xlsx?|zip|rar|7z)$/i.test(url)) { resultItem.description = `

${title}


点击进入下载地址传送门~ diff --git a/lib/routes/nga/forum.ts b/lib/routes/nga/forum.ts index 920454dc7..b7768df13 100644 --- a/lib/routes/nga/forum.ts +++ b/lib/routes/nga/forum.ts @@ -35,11 +35,11 @@ async function handler(ctx) { } const formatContent = (content) => content - .replaceAll(/\[img](.+?)\[\/img]/g, (match, p1) => { + .replaceAll(/\[img\](.+?)\[\/img\]/g, (match, p1) => { const src = p1.replaceAll(/\?.*/g, ''); return ``; }) - .replaceAll(/\[url](.+?)\[\/url]/g, '$1'); + .replaceAll(/\[url\](.+?)\[\/url\]/g, '$1'); const homePage = await got.post('https://ngabbs.com/app_api.php?__lib=subject&__act=list', { headers: { 'X-User-Agent': X_UA, diff --git a/lib/routes/nga/post.ts b/lib/routes/nga/post.ts index 6f0a7782f..a13284a4e 100644 --- a/lib/routes/nga/post.ts +++ b/lib/routes/nga/post.ts @@ -48,7 +48,7 @@ async function handler(ctx) { const getLastPageId = async (tid, authorId) => { const $ = await getPage(tid, authorId); const nav = $('#pagebtop'); - const match = nav.html().match(/{0:'\/read\.php\?tid=(\d+).*?',1:(\d+),.*?}/); + const match = nav.html().match(/\{0:'\/read\.php\?tid=(\d)[^']*',1:(\d+),[^}]*\}/); return match ? match[2] : 1; }; @@ -62,33 +62,33 @@ async function handler(ctx) { const formatContent = (str) => { // 简单样式 - str = deepReplace(str, /\[(b|u|i|del|code|sub|sup)](.+?)\[\/\1]/g, '<$1>$2'); + str = deepReplace(str, /\[([bui]|del|code|sub|sup)\](.+?)\[\/\1\]/g, '<$1>$2'); str = str - .replaceAll(/\[dice](.+?)\[\/dice]/g, 'ROLL : $1') - .replaceAll(/\[color=(.+?)](.+?)\[\/color]/g, '$2') - .replaceAll(/\[font=(.+?)](.+?)\[\/font]/g, '$2') - .replaceAll(/\[size=(.+?)](.+?)\[\/size]/g, '$2') - .replaceAll(/\[align=(.+?)](.+?)\[\/align]/g, '$2'); + .replaceAll(/\[dice\](.+?)\[\/dice\]/g, 'ROLL : $1') + .replaceAll(/\[color=([^\]]+)\](.+?)\[\/color\]/g, '$2') + .replaceAll(/\[font=([^\]]+)\](.+?)\[\/font\]/g, '$2') + .replaceAll(/\[size=([^\]]+)\](.+?)\[\/size\]/g, '$2') + .replaceAll(/\[align=([^\]]+)\](.+?)\[\/align\]/g, '$2'); // 列表 - str = deepReplace(str, /\[\*](.+?)(?=\[\*]|\[\/list])/g, '
  • $1
  • '); - str = deepReplace(str, /\[list](.+?)\[\/list]/g, '
      $1
    '); + str = deepReplace(str, /\[\*\](.+?)(?=\[\*\]|\[\/list\])/g, '
  • $1
  • '); + str = deepReplace(str, /\[list\](.+?)\[\/list\]/g, '
      $1
    '); // 图片 - str = str.replaceAll(/\[img](.+?)\[\/img]/g, (m, src) => ``); + str = str.replaceAll(/\[img\](.+?)\[\/img\]/g, (m, src) => ``); // 折叠 - str = deepReplace(str, /\[collapse(?:=(.+?))?](.+?)\[\/collapse]/g, '
    $1$2
    '); + str = deepReplace(str, /\[collapse(?:=([^\]]+))?\](.+?)\[\/collapse\]/g, '
    $1$2
    '); // 引用 - str = deepReplace(str, /\[quote](.+?)\[\/quote]/g, '
    $1
    ') - .replaceAll(/\[@(.+?)]/g, '@$1') - .replaceAll(/\[uid=(\d+)](.+?)\[\/uid]/g, '@$2') - .replaceAll(/\[tid=(\d+)](.+?)\[\/tid]/g, '$2') - .replaceAll(/\[pid=(\d+),(\d+),(\d+)](.+?)\[\/pid]/g, (m, pid, tid, page, str) => { + str = deepReplace(str, /\[quote\](.+?)\[\/quote\]/g, '
    $1
    ') + .replaceAll(/\[@(.+?)\]/g, '@$1') + .replaceAll(/\[uid=(\d+)\](.+?)\[\/uid\]/g, '@$2') + .replaceAll(/\[tid=(\d+)\](.+?)\[\/tid\]/g, '$2') + .replaceAll(/\[pid=(\d+),(\d+),(\d+)\](.+?)\[\/pid\]/g, (m, pid, tid, page, str) => { const url = `https://nga.178.com/read.php?tid=${tid}&page=${page}#pid${pid}Anchor`; return `${str}`; }); // 链接 - str = str.replaceAll(/\[url=(.+?)](.+?)\[\/url]/g, '$2'); + str = str.replaceAll(/\[url=([^\]]+)\](.+?)\[\/url\]/g, '$2'); // 分割线 - str = str.replaceAll(/\[h](.+?)\[\/h]/g, '

    $1

    '); + str = str.replaceAll(/\[h\](.+?)\[\/h\]/g, '

    $1

    '); return str; }; diff --git a/lib/routes/nhentai/util.tsx b/lib/routes/nhentai/util.tsx index aa953f25b..416e9b6a0 100644 --- a/lib/routes/nhentai/util.tsx +++ b/lib/routes/nhentai/util.tsx @@ -148,7 +148,7 @@ const getDetail = async (simple) => { const galleryImgs = $('.gallerythumb img') .toArray() .map((ele) => new URL($(ele).attr('data-src'), baseUrl).href) - .map((src) => src.replace(/(.+)(\d+)t\.(.+)/, (_, p1, p2, p3) => `${p1}${p2}.${p3}`)) // thumb to high-quality + .map((src) => src.replace(/(.+)(\d)t\.(.+)/, (_, p1, p2, p3) => `${p1}${p2}.${p3}`)) // thumb to high-quality .map((src) => src.replace(/t(\d+)\.nhentai\.net/, 'i$1.nhentai.net')) .map((src) => src.replace(/\.(jpg|png|gif)\.webp$/, '.$1')) // 移除重複的.webp後綴 .map((src) => src.replace(/\.webp\.webp$/, '.webp')); // 處理.webp.webp的情況 diff --git a/lib/routes/nikkei/cn/index.ts b/lib/routes/nikkei/cn/index.ts index 191a4e510..4441eaea3 100644 --- a/lib/routes/nikkei/cn/index.ts +++ b/lib/routes/nikkei/cn/index.ts @@ -66,7 +66,7 @@ async function handler(ctx) { let language: string; let path = getSubPath(ctx); - if (/^\/cn\/(cn|zh)/.test(path)) { + if (/^\/cn\/(?:cn|zh)/.test(path)) { language = path.match(/^\/cn\/(cn|zh)/)[1]; path = path.match(new RegExp(String.raw`\/cn\/` + language + '(.*)'))[1]; } else { diff --git a/lib/routes/nintendo/eshop-hk.ts b/lib/routes/nintendo/eshop-hk.ts index 4b8607752..4b4fd80f9 100644 --- a/lib/routes/nintendo/eshop-hk.ts +++ b/lib/routes/nintendo/eshop-hk.ts @@ -50,7 +50,7 @@ async function handler(ctx) { const gallery = JSON.parse( $('[type=text/x-magento-init]') .text() - .match(/{\n\s+"\[data-gal{2}ery-role=gal{2}ery-placeholder]": {\n\s+"mage(?:\/gal{2}ery){2}".*?}{4}(?:\s+}\n){3}/s) + .match(/\{\n\s+"\[data-gal{2}ery-role=gal{2}ery-placeholder\]": \{\n\s+"mage(?:\/gal{2}ery){2}".*?\}{4}(?:\s+\}\n){3}/s) ); description = renderEshopHkDescription({ @@ -60,7 +60,7 @@ async function handler(ctx) { host: 'store.nintendo.com.hk', }); } else if (item.link.startsWith('https://ec.nintendo.com/')) { - const jsonData = JSON.parse(response.match(/NXSTORE\.titleDetail\.jsonData = ({.*?});/)[1]); + const jsonData = JSON.parse(response.match(/NXSTORE\.titleDetail\.jsonData = (\{.*?\});/)[1]); const { data: priceData } = await got('https://ec.nintendo.com/api/HK/zh/guest_prices', { searchParams: { ns_uids: jsonData.id, diff --git a/lib/routes/nintendo/system-update.ts b/lib/routes/nintendo/system-update.ts index c4fdb2766..149b470b7 100644 --- a/lib/routes/nintendo/system-update.ts +++ b/lib/routes/nintendo/system-update.ts @@ -47,7 +47,7 @@ async function handler() { .toArray() .map((element) => $(element).html()) .join('\n'); - const matched_version = /(\d\.)+\d/.exec(heading); + const matched_version = /(?:\d\.)+\d/.exec(heading); return { title: heading, diff --git a/lib/routes/nowcoder/discuss.ts b/lib/routes/nowcoder/discuss.ts index 458af0926..2044b149a 100644 --- a/lib/routes/nowcoder/discuss.ts +++ b/lib/routes/nowcoder/discuss.ts @@ -53,7 +53,7 @@ async function handler(ctx) { const out = await Promise.all( list.map((info) => { const title = info.title || 'tzgg'; - const itemUrl = new URL(info.link, host).href.replace(/^(.*)\?(.*)$/, '$1'); + const itemUrl = new URL(info.link, host).href.replace(/^([^\n\r\u2028\u2029]*)\?[^\n\r?\u2028\u2029]*$/, '$1'); return cache.tryGet(itemUrl, async () => { const response = await got.get(itemUrl); diff --git a/lib/routes/odaily/activity.ts b/lib/routes/odaily/activity.ts index 554b90bfa..f401d4f60 100644 --- a/lib/routes/odaily/activity.ts +++ b/lib/routes/odaily/activity.ts @@ -54,7 +54,7 @@ async function handler(ctx) { url: item.link, }); - const content = load(detailResponse.data.match(/"content":"(.*)"}},"secondaryList":/)[1]); + const content = load(detailResponse.data.match(/"content":"(.*)"\}\},"secondaryList":/)[1]); content('img').each((_, el) => { content(el).attr( diff --git a/lib/routes/odaily/post.ts b/lib/routes/odaily/post.ts index c2cef7b46..2de881e3c 100644 --- a/lib/routes/odaily/post.ts +++ b/lib/routes/odaily/post.ts @@ -68,7 +68,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { const detailResponse = await got(item.link); - const ssr = JSON.parse(`{${detailResponse.data.match(/window\.__INITIAL_STATE__ = {(.*)}/)[1]}}`); + const ssr = JSON.parse(`{${detailResponse.data.match(/window\.__INITIAL_STATE__ = \{(.*)\}/)[1]}}`); const content = load(ssr.post.detail.content, null, false); content('img').each((_, img) => { diff --git a/lib/routes/oeeee/utils.ts b/lib/routes/oeeee/utils.ts index 706dc335f..9f8217b06 100644 --- a/lib/routes/oeeee/utils.ts +++ b/lib/routes/oeeee/utils.ts @@ -20,7 +20,7 @@ const parseArticle = (item, tryGet) => item.description += content('.post-cont') .html() - .replaceAll(/data:image\S*=="\s*\n*\s*original="/g, '') ?? ''; + .replaceAll(/data:image\S*=="\s*original="/g, '') ?? ''; if (!item.pubDate) { item.pubDate = timezone(parseDate(content('.introduce').text().split()), +8); } diff --git a/lib/routes/outagereport/index.ts b/lib/routes/outagereport/index.ts index d155a1876..59a2ee124 100644 --- a/lib/routes/outagereport/index.ts +++ b/lib/routes/outagereport/index.ts @@ -35,8 +35,8 @@ async function handler(ctx) { // use RegExp because of irregular class name const gaugeRegexp = /class="Gauge__Count.*?>(\d+)<\/text>/; // Core Pattern - const gaugeTextRegexp = /class="Gauge__MessageWrapper.*?class="Gauge__Message.*?>(.*?)<\/span>/; // Core Pattern - const rssDescribeRegexp = /

    ]*>(.*?)<\/p>/; // data to be shown on RSS feed and RSS items const gaugeCount = Number(html.match(gaugeRegexp)[1]); diff --git a/lib/routes/papers/category.ts b/lib/routes/papers/category.ts index 60aa2f245..7249f3fab 100644 --- a/lib/routes/papers/category.ts +++ b/lib/routes/papers/category.ts @@ -81,7 +81,7 @@ export const handler = async (ctx: Context): Promise => { const description: string = renderDescription({ pdfUrl: enclosureUrl, - kimiUrl: `${targetUrl.replace(/[a-zA-Z0-9.]+$/, 'kimi')}?paper=${doi}`, + kimiUrl: `${targetUrl.replace(/[a-z0-9.]+$/i, 'kimi')}?paper=${doi}`, authors, summary: $el.find('p.summary').text(), }); diff --git a/lib/routes/parliament/section77.ts b/lib/routes/parliament/section77.ts index ff84df3a1..1eaa8a6b3 100644 --- a/lib/routes/parliament/section77.ts +++ b/lib/routes/parliament/section77.ts @@ -136,7 +136,7 @@ async function handler(ctx) { ]; const voteText = $('.row.bg-status .col-md-4.text-right').text().trim(); - const voteRegex = /^ผู้แสดงความคิดเห็น\s*(\d+)\s*คน\s*(\d+(?:\.\d+)?)%\s*(\d+(?:\.\d+)?)%/g.exec(voteText); + const voteRegex = /^ผู้แสดงความคิดเห็น\s*(\d+)\s*คน\s*(\d+(?:\.\d+)?)%\s*\d+(?:\.\d+)?%/.exec(voteText); if (voteRegex) { const voteTotal = Number.parseInt(voteRegex[0]); @@ -148,7 +148,7 @@ async function handler(ctx) { } const dateText = $('.banner-detail .banner-detail-caption .blockquote p:last-child').text(); - const dateRegex = /^รับฟังตั้งแต่วันที่\s(\d{1,2})\s*([\u0E00-\u0E7F]+)\s*(\d{4})/g.exec(dateText); + const dateRegex = /^รับฟังตั้งแต่วันที่\s(\d{1,2})\s*([\u0E00-\u0E7F]+)\s*(\d{4})/.exec(dateText); if (dateRegex) { item.pubDate = timezone( diff --git a/lib/routes/patreon/feed.tsx b/lib/routes/patreon/feed.tsx index 388c8043a..3e09492ce 100644 --- a/lib/routes/patreon/feed.tsx +++ b/lib/routes/patreon/feed.tsx @@ -121,7 +121,7 @@ async function handler(ctx) { const ogUrl = $('meta[property="og:url"]').attr('content'); if (ogUrl?.startsWith(`${baseUrl}/cw/`)) { const ogImage = $('meta[property="og:image"]').attr('content'); - const creatorId = decodeURIComponent(ogImage || '').match(/card-teaser-image\/creator\/(\d+?)\?/)?.[1]; + const creatorId = decodeURIComponent(ogImage || '').match(/card-teaser-image\/creator\/(\d+)\?/)?.[1]; if (creatorId) { const creator = await ofetch(`${baseUrl}/api/campaigns/${creatorId}`); return { diff --git a/lib/routes/pixiv/novel-api/content/utils.ts b/lib/routes/pixiv/novel-api/content/utils.ts index ef5ec5629..9e1a68cd8 100644 --- a/lib/routes/pixiv/novel-api/content/utils.ts +++ b/lib/routes/pixiv/novel-api/content/utils.ts @@ -93,10 +93,10 @@ export async function parseNovelContent(content: string, images: Record){2,}/g, '

    ') // ruby 標籤(為日文漢字標註讀音) // ruby tags (for Japanese kanji readings) - .replaceAll(/\[\[rb:(.*?)>(.*?)\]\]/g, '$1$2') + .replaceAll(/\[\[rb:([^>\n\r\u2028\u2029]*)>(.*?)\]\]/g, '$1$2') // 外部連結 // external links - .replaceAll(/\[\[jumpuri:(.*?)>(.*?)\]\]/g, '$1') + .replaceAll(/\[\[jumpuri:([^>\n\r\u2028\u2029]*)>(.*?)\]\]/g, '$1') // 頁面跳轉,但由於 [newpage] 使用 hr 分隔,沒有頁數,沒必要跳轉,所以只顯示文字 // Page jumps, but since [newpage] uses hr separators, without the page numbers, jumping isn't needed, so just display text .replaceAll(/\[jump:(\d+)\]/g, 'Jump to page $1') diff --git a/lib/routes/playno1/av.ts b/lib/routes/playno1/av.ts index 04956e769..e0de6cf58 100644 --- a/lib/routes/playno1/av.ts +++ b/lib/routes/playno1/av.ts @@ -56,7 +56,7 @@ async function handler(ctx) { author: item .find('.fire_right') .text() - .match(/作者:(.*)\s*\|/)[1] + .match(/作者:([^|]*)\|/)[1] .trim(), }; }); diff --git a/lib/routes/qingting/channel.ts b/lib/routes/qingting/channel.ts index 6a9e47dfd..c1afb06fb 100644 --- a/lib/routes/qingting/channel.ts +++ b/lib/routes/qingting/channel.ts @@ -42,7 +42,7 @@ async function handler(ctx) { items.map((item) => cache.tryGet(item.link, async () => { response = await ofetch(item.link); - const data = JSON.parse(response.match(/},"program":(.*?),"plist":/)[1]); + const data = JSON.parse(response.match(/\},"program":(.*?),"plist":/)[1]); item.description = data.richtext; return item; }) diff --git a/lib/routes/qingting/podcast.ts b/lib/routes/qingting/podcast.ts index b99513f34..bbde582e2 100644 --- a/lib/routes/qingting/podcast.ts +++ b/lib/routes/qingting/podcast.ts @@ -83,7 +83,7 @@ async function handler(ctx) { }, }); - const detail = JSON.parse(detailRes.match(/},"program":(.*?),"plist":/)[1]); + const detail = JSON.parse(detailRes.match(/\},"program":(.*?),"plist":/)[1]); const rssItem = { title: item.title, diff --git a/lib/routes/quantamagazine/archive.ts b/lib/routes/quantamagazine/archive.ts index c9bf893db..a383a60d6 100644 --- a/lib/routes/quantamagazine/archive.ts +++ b/lib/routes/quantamagazine/archive.ts @@ -13,16 +13,16 @@ const processArticleContent = (html: string | null, articleLink?: string): strin } // Handle LaTeX formulas - let processed = html.replaceAll(/\$latex([\S\s]+?)\$/g, ''); + let processed = html.replaceAll(/\$latex([\s\S]+?)\$/g, ''); // Handle embedded images with captions - processed = processed.replaceAll(/

    ?/g, (_match, src, cap) => { + processed = processed.replaceAll(/
    ?/g, (_match, src, cap) => { const imgUrl = src.replaceAll(/\\([^nu])/g, '$1'); const img = ``; const noBS = cap.replaceAll(/\\([^nu])/g, '$1'); const removeNL = noBS.replaceAll(String.raw`\n`, ''); - const caption = removeNL.replaceAll(/\\u(\d{1,3}[a-z]\d?|\d{4}?)/g, (_omit, s) => String.fromCodePoint(Number.parseInt(s, 16))); + const caption = removeNL.replaceAll(/\\u(\d{1,3}[a-z]\d?|\d{4})/g, (_omit, s) => String.fromCodePoint(Number.parseInt(s, 16))); return `
    ${img}
    ${caption}
    `; }); diff --git a/lib/routes/rawkuma/manga.tsx b/lib/routes/rawkuma/manga.tsx index cbf47ff3b..4c3783909 100644 --- a/lib/routes/rawkuma/manga.tsx +++ b/lib/routes/rawkuma/manga.tsx @@ -70,7 +70,7 @@ async function handler(ctx) { const content = 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/routes/readhub/index.ts b/lib/routes/readhub/index.ts index c5138ee4f..689d162e2 100644 --- a/lib/routes/readhub/index.ts +++ b/lib/routes/readhub/index.ts @@ -37,7 +37,7 @@ async function handler(ctx) { const { data: currentResponse } = await got(currentUrl); - const type = currentResponse.match(/\[\\"type\\",\\"(\d+)\\",\\"d\\"]/)?.[1] ?? '1'; + const type = currentResponse.match(/\[\\"type\\",\\"(\d+)\\",\\"d\\"\]/)?.[1] ?? '1'; const { data: response } = await got(apiTopicUrl, { searchParams: { diff --git a/lib/routes/readhub/util.ts b/lib/routes/readhub/util.ts index 2bb28b445..825214c66 100644 --- a/lib/routes/readhub/util.ts +++ b/lib/routes/readhub/util.ts @@ -26,7 +26,7 @@ const processItems = async (items, tryGet) => const { data: detailResponse } = await got(item.link); - const data = JSON.parse(detailResponse.match(/{\\"topic\\":(.*?)}]\\n"]\)<\/script>/)[1].replaceAll(String.raw`\"`, '"')); + const data = JSON.parse(detailResponse.match(/\{\\"topic\\":(.*?)\}\]\\n"\]\)<\/script>/)[1].replaceAll(String.raw`\"`, '"')); item.title = data.title; item.link = data.url ?? new URL(`topic/${data.uid}`, rootUrl).href; diff --git a/lib/routes/reuters/common.tsx b/lib/routes/reuters/common.tsx index f440cf2a0..1c8decff6 100644 --- a/lib/routes/reuters/common.tsx +++ b/lib/routes/reuters/common.tsx @@ -288,7 +288,7 @@ async function handler(ctx) { const matches = content('script#fusion-metadata') .text() - .match(/Fusion.globalContent=({[\S\s]*?});/); + .match(/Fusion.globalContent=(\{[\s\S]*?\});/); if (matches) { const data = JSON.parse(matches[1]); @@ -310,7 +310,7 @@ async function handler(ctx) { 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) + .match(/\{"@type":"Person","name":"(.*?)"\}/g) .map((p) => p.match(/"name":"(.*?)"/)[1]) .join(', '); item.description = content('article').html(); diff --git a/lib/routes/runyeah/posts.ts b/lib/routes/runyeah/posts.ts index 5e241ff79..a21a477cd 100644 --- a/lib/routes/runyeah/posts.ts +++ b/lib/routes/runyeah/posts.ts @@ -29,7 +29,7 @@ async function handler(ctx) { 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/routes/shisu/en.ts b/lib/routes/shisu/en.ts index 62438ea48..5845a98a1 100644 --- a/lib/routes/shisu/en.ts +++ b/lib/routes/shisu/en.ts @@ -49,8 +49,8 @@ async function process(baseUrl: string, section: any) { const $ = load(r); j.description = $('.details-con') .html()! - .replaceAll(/[\S\s]*?<\/o:p>/g, '') - .replaceAll(/(]*> <\/p>\s*)+/gm, '

     

    '); + .replaceAll(/[\s\S]*?<\/o:p>/g, '') + .replaceAll(/(]*> <\/p>\s*)+/g, '

     

    '); return j; }) ) diff --git a/lib/routes/sina/utils.tsx b/lib/routes/sina/utils.tsx index e1a7c3025..68c44e813 100644 --- a/lib/routes/sina/utils.tsx +++ b/lib/routes/sina/utils.tsx @@ -48,7 +48,7 @@ const parseArticle = (item, tryGet) => const slideData = JSON.parse( $('script') .text() - .match(/var slide_data = ({.*?})\s/)[1] + .match(/var slide_data = (\{.*?\})\s/)[1] ); item.description = renderToString( <> diff --git a/lib/routes/sis001/common.ts b/lib/routes/sis001/common.ts index 1dbcb7291..89b8a12d7 100644 --- a/lib/routes/sis001/common.ts +++ b/lib/routes/sis001/common.ts @@ -53,7 +53,7 @@ async function getThread(cookie: string, item: DataItem) { $('.postinfo') .eq(0) .text() - .match(/发表于 (.*)\s*只看该作者/)[1], + .match(/发表于 (.*)(?:[\n\r\u2028\u2029]\s*)?只看该作者/)[1], 'YYYY-M-D HH:mm' ), 8 @@ -65,7 +65,7 @@ async function getThread(cookie: string, item: DataItem) { .html() ?.replaceAll('\n', '') .replaceAll(/\u3000{2}.+?(((?:
    ){2})|( ))/g, (str) => `

    ${str.replaceAll('
    ', '')}

    `) - .replaceAll(/

    \u3000{6,}(.+?)<\/p>/g, '

    $1

    ') + .replaceAll(/

    \u3000{6,}([^\u3000\n\r\u2028\u2029].*?|\u3000)<\/p>/g, '

    $1

    ') .replaceAll(' ', '') .replace(/

    +

    /, '') + ($('.defaultpost .postattachlist').html() ?? ''); return item; diff --git a/lib/routes/smartlink/index.ts b/lib/routes/smartlink/index.ts index e0d04bebb..4fd221133 100644 --- a/lib/routes/smartlink/index.ts +++ b/lib/routes/smartlink/index.ts @@ -15,7 +15,7 @@ function parseTitle(smartlinkUrl: string): string { let titleSlug = dateIndex !== -1 && dateIndex < pathSegments.length - 1 ? pathSegments[dateIndex + 1] : pathSegments.at(-1) || ''; // Remove .html/.htm extension if present - titleSlug = titleSlug.replace(/\.(html?|htm)$/i, ''); + titleSlug = titleSlug.replace(/\.(html?)$/i, ''); // Convert hyphens to spaces and capitalize each word return toTitleCase(titleSlug.replaceAll('-', ' ')); diff --git a/lib/routes/sohu/mobile.ts b/lib/routes/sohu/mobile.ts index 5fc65c032..2d40aab9d 100644 --- a/lib/routes/sohu/mobile.ts +++ b/lib/routes/sohu/mobile.ts @@ -36,7 +36,7 @@ async function handler() { // 从HTML中提取JSON数据 const $ = cheerio.load(response); const jsonScript = $('script:contains("WapHomeRenderData")').text(); - const jsonMatch = jsonScript?.match(/window\.WapHomeRenderData\s*=\s*({.*})/s); + const jsonMatch = jsonScript?.match(/window\.WapHomeRenderData\s*=\s*(\{.*\})/s); if (!jsonMatch?.[1]) { throw new Error('WapHomeRenderData 数据未找到'); } diff --git a/lib/routes/sohu/mp.tsx b/lib/routes/sohu/mp.tsx index 1b9a3a07e..8b1cfdf43 100644 --- a/lib/routes/sohu/mp.tsx +++ b/lib/routes/sohu/mp.tsx @@ -140,7 +140,7 @@ async function handler(ctx) { const blockRenderData = JSON.parse( $('script:contains("column_2_text")') .text() - .match(/({.*})/)?.[1] + .match(/(\{.*\})/)?.[1] ); const renderData = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('FeedSlideloadAuthor'))]; const briefIntroductionCard = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('BriefIntroductionCard'))].param.data.list[0]; diff --git a/lib/routes/solidot/_article.ts b/lib/routes/solidot/_article.ts index 827570f18..1dd70b9de 100644 --- a/lib/routes/solidot/_article.ts +++ b/lib/routes/solidot/_article.ts @@ -18,7 +18,7 @@ export default async function get_article(url) { const $ = load(data); const date_raw = $('div.talk_time').clone().children().remove().end().text(); - const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(.*分)[^`]*$/g, '$1'); // use [^`] to match \n + const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(?=(.*分))\1[^`]*$/g, '$1'); // use [^`] to match \n const date_str = date_str_zh .replaceAll(/[年月]/g, '-') .replaceAll('时', ':') diff --git a/lib/routes/sony/downloads.ts b/lib/routes/sony/downloads.ts index 0e6f89f42..f8cd5f114 100644 --- a/lib/routes/sony/downloads.ts +++ b/lib/routes/sony/downloads.ts @@ -42,7 +42,7 @@ async function handler(ctx) { const $ = load(data); const contents = $('script:contains("window.__PRELOADED_STATE__.downloads")').text(); - const regex = /window\.__PRELOADED_STATE__\.downloads\s*=\s*({.*?});\s*window\.__PRELOADED_STATE__/s; + const regex = /window\.__PRELOADED_STATE__\.downloads\s*=\s*(\{.*?\});\s*window\.__PRELOADED_STATE__/s; const match = contents.match(regex); let results = {}; diff --git a/lib/routes/steam/curator.tsx b/lib/routes/steam/curator.tsx index 7dd9d2e4e..03c969dd9 100644 --- a/lib/routes/steam/curator.tsx +++ b/lib/routes/steam/curator.tsx @@ -62,7 +62,7 @@ Examples: const reviewContent = el.find('.recommendation_desc').text().trim(); const reviewDateText = el.find('.curator_review_date').text().trim(); - const notCurrentYearPattern = /,\s\b\d{4}\b$/; + const notCurrentYearPattern = /,\s\d{4}$/; const reviewPubDate = notCurrentYearPattern.test(reviewDateText) ? parseDate(reviewDateText) : parseDate(`${reviewDateText}, ${new Date().getFullYear()}`); const description = renderToString(); diff --git a/lib/routes/steam/news.ts b/lib/routes/steam/news.ts index ccc7c3970..ed6894e80 100644 --- a/lib/routes/steam/news.ts +++ b/lib/routes/steam/news.ts @@ -179,12 +179,12 @@ const linebreakRenderer = (tree: BBobCoreTagNodeTree) => const plainUrlRenderer = (tree: BBobCoreTagNodeTree) => tree.walk((node) => { - if (typeof node === 'string' && /https?:\/\/[^\s]+/.test(node)) { + if (typeof node === 'string' && /https?:\/\/\S+/.test(node)) { let lastIndex = 0; let match: RegExpExecArray | null; const content: NodeContent[] = []; - const urlRe = /https?:\/\/[^\s]+/g; + const urlRe = /https?:\/\/\S+/g; while ((match = urlRe.exec(node)) !== null) { if (match.index > lastIndex) { content.push(node.slice(lastIndex, match.index)); @@ -250,7 +250,7 @@ const customPreset: PresetFactory = presetHTML5.extend((tags) => ({ previewyoutube: (node) => ({ tag: 'iframe', attrs: { - src: `https://www.youtube-nocookie.com/embed/${(getUniqAttr(node.attrs) as string).match(/[A-Za-z0-9_-]+/)?.[0]}`, + src: `https://www.youtube-nocookie.com/embed/${(getUniqAttr(node.attrs) as string).match(/[\w-]+/)?.[0]}`, title: 'YouTube video player', frameborder: '0', allowFullScreen: '1', diff --git a/lib/routes/steam/workshop-search.tsx b/lib/routes/steam/workshop-search.tsx index ef49063c4..cadf71086 100644 --- a/lib/routes/steam/workshop-search.tsx +++ b/lib/routes/steam/workshop-search.tsx @@ -66,7 +66,7 @@ Language Parameter: // const script_tag = item.next('script'); // console.log(`script_tag:${script_tag.text()}`); const hoverContent = item.next('script').text(); - const regex = /SharedFileBindMouseHover\(\s*"sharedfile_\d+",\s*(?:true|false),\s*({.*?})\s*\);/; + const regex = /SharedFileBindMouseHover\(\s*"sharedfile_\d+",\s*(?:true|false),\s*(\{.*?\})\s*\);/; const match = hoverContent.match(regex); let entryDescription = ''; diff --git a/lib/routes/supchina/index.ts b/lib/routes/supchina/index.ts index a2965c097..984ac98d5 100644 --- a/lib/routes/supchina/index.ts +++ b/lib/routes/supchina/index.ts @@ -43,7 +43,7 @@ async function handler(ctx) { author: item .find(String.raw`dc\:creator`) .html() - .match(/CDATA\[(.*?)]/)[1], + .match(/CDATA\[(.*?)\]/)[1], category: item .find('category') .toArray() @@ -51,7 +51,7 @@ async function handler(ctx) { (c) => $(c) .html() - .match(/CDATA\[(.*?)]/)[1] + .match(/CDATA\[(.*?)\]/)[1] ), pubDate: parseDate(item.find('pubDate').text()), }; diff --git a/lib/routes/swjtu/gsee/yjs.ts b/lib/routes/swjtu/gsee/yjs.ts index 5e2bd3fea..83515ddad 100644 --- a/lib/routes/swjtu/gsee/yjs.ts +++ b/lib/routes/swjtu/gsee/yjs.ts @@ -13,7 +13,7 @@ const getItem = (item) => { const newsDate = item .find('dd') .text() - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)[0]; + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)[0]; const infoTitle = newsInfo.text(); const link = rootURL + newsInfo.find('a').last().attr('href').slice(2); diff --git a/lib/routes/swjtu/scai.ts b/lib/routes/swjtu/scai.ts index a9dcf8798..45056316b 100644 --- a/lib/routes/swjtu/scai.ts +++ b/lib/routes/swjtu/scai.ts @@ -67,7 +67,7 @@ const getItem = (item, cache) => { // 'date' may be undefined. and 'parseDate' will return current time. // 转其他院的通知,获取不到具体时间,先从列表页获取具体信息 if (dateText) { - const dateMatch = dateText.match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/); + const dateMatch = dateText.match(/\d{4}(.)\d{1,2}\1\d{1,2}/); if (!dateMatch || !dateMatch[0]) { return null; } diff --git a/lib/routes/swjtu/sports.ts b/lib/routes/swjtu/sports.ts index edaaa63d5..c3967bc62 100644 --- a/lib/routes/swjtu/sports.ts +++ b/lib/routes/swjtu/sports.ts @@ -44,7 +44,7 @@ const getItem = (item, cache) => { $('div.info span:nth-of-type(3)') .text() .slice(3) - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)?.[0] + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)?.[0] ); const description = $('div.detail-wrap').html(); return { diff --git a/lib/routes/swpu/utils.ts b/lib/routes/swpu/utils.ts index 11cb95227..4ae3ccdd1 100644 --- a/lib/routes/swpu/utils.ts +++ b/lib/routes/swpu/utils.ts @@ -1,5 +1,5 @@ function isCompleteUrl(url) { - return /^\w+?:\/\/.*?\//.test(url); + return /^\w+:\/\/.*?\//.test(url); } function joinUrl(url1, url2) { diff --git a/lib/routes/szse/disclosure/listed-notice.ts b/lib/routes/szse/disclosure/listed-notice.ts index 37dba8010..075aec987 100644 --- a/lib/routes/szse/disclosure/listed-notice.ts +++ b/lib/routes/szse/disclosure/listed-notice.ts @@ -10,7 +10,7 @@ import timezone from '@/utils/timezone'; function isValidDate(dateString: string): boolean { // 正则表达式检查格式:YYYY-MM-DD - const regex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/; + const regex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/; if (!regex.test(dateString)) { return false; } diff --git a/lib/routes/tass/news.ts b/lib/routes/tass/news.ts index 8463e762e..429298022 100644 --- a/lib/routes/tass/news.ts +++ b/lib/routes/tass/news.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const sectionId = $('.container .section-page') .attr('ng-init') - .match(/sectionId\s*=\s*(\d+?);/); + .match(/sectionId\s*=\s*(\d+);/); const { data: response } = await got.post('https://tass.com/userApi/categoryNewsList', { json: { diff --git a/lib/routes/tencent/news/author.tsx b/lib/routes/tencent/news/author.tsx index 23de40840..02c6da3bc 100644 --- a/lib/routes/tencent/news/author.tsx +++ b/lib/routes/tencent/news/author.tsx @@ -68,7 +68,7 @@ async function handler(ctx): Promise { const data = JSON.parse( $('script:contains("window.DATA")') .text() - .match(/window\.DATA = ({.+});/)[1] + .match(/window\.DATA = (\{.+\});/)[1] ); const $data = load(data.originContent?.text || '', null, false); if ($data) { diff --git a/lib/routes/tesla/cx.ts b/lib/routes/tesla/cx.ts index 8ea59460c..116fead80 100644 --- a/lib/routes/tesla/cx.ts +++ b/lib/routes/tesla/cx.ts @@ -145,7 +145,7 @@ async function handler(ctx) { alt: item.venueName ?? item.title, } : undefined, - description: item.description?.replaceAll(/\["|"]/g, '') ?? undefined, + description: item.description?.replaceAll(/\["|"\]/g, '') ?? undefined, data: item.parkingLocationId ? { title: item.venueName ?? item.title, diff --git a/lib/routes/threads/utils.ts b/lib/routes/threads/utils.ts index d26a88751..2570f653f 100644 --- a/lib/routes/threads/utils.ts +++ b/lib/routes/threads/utils.ts @@ -31,7 +31,7 @@ const extractTokens = async (user): Promise<{ lsd: string }> => { const $ = load(response); const data = $('script:contains("LSD"):first').text(); - const lsd = data.match(/"LSD",\[],{"token":"([\w@-]+)"},/)?.[1]; + const lsd = data.match(/"LSD",\[\],\{"token":"([\w@-]+)"\},/)?.[1]; if (!lsd) { throw new NotFoundError('LSD token not found'); diff --git a/lib/routes/transcriptforest/index.ts b/lib/routes/transcriptforest/index.ts index 254f5492c..072bc3506 100644 --- a/lib/routes/transcriptforest/index.ts +++ b/lib/routes/transcriptforest/index.ts @@ -41,7 +41,7 @@ async function handler(ctx) { const { data: firstResponse } = await got(rootUrl); - const data = JSON.parse(firstResponse.match(/({"props".*"scriptLoader":\[]})<\/script>/)?.[1]); + const data = JSON.parse(firstResponse.match(/(\{"props".*"scriptLoader":\[\]\})<\/script>/)?.[1]); const buildId = data.buildId; const defaultLocale = data.defaultLocale; diff --git a/lib/routes/twitter/api/web-api/gql-id-resolver.ts b/lib/routes/twitter/api/web-api/gql-id-resolver.ts index aac492bf1..91c304c90 100644 --- a/lib/routes/twitter/api/web-api/gql-id-resolver.ts +++ b/lib/routes/twitter/api/web-api/gql-id-resolver.ts @@ -30,7 +30,7 @@ async function fetchTwitterPage(): Promise { function extractQueryIds(scriptContent: string): Record { const ids: Record = {}; - const matches = scriptContent.matchAll(/queryId:"([^"]+?)".+?operationName:"([^"]+?)"/g); + const matches = scriptContent.matchAll(/queryId:"([^"]+)".+?operationName:"([^"]+)"/g); for (const match of matches) { const [, queryId, operationName] = match; if (operationNames.includes(operationName)) { diff --git a/lib/routes/twitter/utils.ts b/lib/routes/twitter/utils.ts index afe8532f0..cadd107c0 100644 --- a/lib/routes/twitter/utils.ts +++ b/lib/routes/twitter/utils.ts @@ -11,7 +11,7 @@ const getOriginalImg = (url) => { format = 'jpg'; } return `${m[1]}?format=${format}&name=orig`; - } else if ((m = url.match(/^(https?:\/\/\w+\.twimg\.com\/.+)(\?.+)$/i))) { + } else if ((m = url.match(/^(https?:\/\/\w+\.twimg\.com\/[^?]+)(\?.+)$/i))) { const pars = getQueryParams(url); if (!pars.format || !pars.name) { return url; diff --git a/lib/routes/txrjy/fornumtopic.tsx b/lib/routes/txrjy/fornumtopic.tsx index 18ecdd689..d3dcf2422 100644 --- a/lib/routes/txrjy/fornumtopic.tsx +++ b/lib/routes/txrjy/fornumtopic.tsx @@ -70,12 +70,12 @@ async function handler(ctx) { .remove() .end() .html() - ?.replaceAll(/()/g, '$1$2') + ?.replaceAll(/()/g, '$1$2') .replaceAll(/()/g, '$1src$2'); const pattlHtml = content(item) .find('div.pattl') .html() - ?.replaceAll(/()/g, '$1$2') + ?.replaceAll(/()/g, '$1$2') .replaceAll(/()/g, '$1src$2'); const author = content(item).find('a.xw1').text().trim(); diff --git a/lib/routes/udn/breaking-news.tsx b/lib/routes/udn/breaking-news.tsx index 4fa9fe943..85099d70f 100644 --- a/lib/routes/udn/breaking-news.tsx +++ b/lib/routes/udn/breaking-news.tsx @@ -62,7 +62,7 @@ async function handler(ctx) { .eq(0) .text() .trim() - .replaceAll(/[\b\t\n]/g, ''); + .replaceAll(/[\t\n]/g, ''); const data = metadata.startsWith('[') ? JSON.parse(metadata)[0] : JSON.parse(metadata); // e.g. https://udn.com/news/story/7331/6576320 const content = $('.article-content__editor'); @@ -90,7 +90,7 @@ async function handler(ctx) { // 轉角24小時 description = $('.story_body_content') .html() - .split(//g) + .split(//g) .slice(1, -1) .join(''); } diff --git a/lib/routes/upc/jwc.ts b/lib/routes/upc/jwc.ts index b0c67c28d..a69e5da01 100644 --- a/lib/routes/upc/jwc.ts +++ b/lib/routes/upc/jwc.ts @@ -48,7 +48,7 @@ const handler = async (ctx) => { const scriptContent = $('body script').first().html(); let dataObj = null; if (scriptContent) { - const match = scriptContent.match(/data\s*:\s*function\s*\(\)\s*{\s*return\s*{[^}]*data\s*:\s*({[\s\S]*?})/); + const match = scriptContent.match(/data\s*:\s*function\s*\(\)\s*\{\s*return\s*\{[^}]*data\s*:\s*(\{[\s\S]*?\})/); if (match && match[1]) { const dataStr = match[1]; dataObj = JSON.parse(dataStr); diff --git a/lib/routes/ups/track.ts b/lib/routes/ups/track.ts index 5421c6d30..e7646f54d 100644 --- a/lib/routes/ups/track.ts +++ b/lib/routes/ups/track.ts @@ -68,7 +68,7 @@ async function handler(ctx) { const dateTimeStr = dateTimeRaw .trim() - .replace(/(\d{1,}\/\d{1,}\/\d{4})(\d{1,}:\d{1,}\s[AP]\.?M\.?)/, '$1 $2') + .replace(/(\d+\/\d+\/\d{4})(\d+:\d+\s[AP]\.?M\.?)/, '$1 $2') .replaceAll('P.M.', 'PM') .replaceAll('A.M.', 'AM'); @@ -78,7 +78,7 @@ async function handler(ctx) { .find(`#stApp_milestoneActivityLocation${i}`) .text() .trim() - .replaceAll(/\s*\n+\s*/g, '\n'); + .replaceAll(/\s*\n\s*/g, '\n'); const lines = activityCellText .split('\n') diff --git a/lib/routes/uptimerobot/rss.tsx b/lib/routes/uptimerobot/rss.tsx index 6b1afae07..a5fdbdd8e 100644 --- a/lib/routes/uptimerobot/rss.tsx +++ b/lib/routes/uptimerobot/rss.tsx @@ -6,7 +6,7 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { fallback, queryToBoolean } from '@/utils/readable-social'; -const titleRegex = /(.+)\s+is\s+([A-Z]+)\s+\((.+)\)/; +const titleRegex = /(.*\S)\s+is\s+([A-Z]+)\s+\((.+)\)/; const formatTime = (s) => { const duration = dayjs.duration(s - 0, 'seconds'); diff --git a/lib/routes/vcb-s/category.ts b/lib/routes/vcb-s/category.ts index bfd8880b9..890a15845 100644 --- a/lib/routes/vcb-s/category.ts +++ b/lib/routes/vcb-s/category.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const items = data.map((item) => { const description = renderDescription({ - post: item.content.rendered.replaceAll(/
    ]*>(.*?)<\/pre>/gs, '
    $1
    ').replaceAll(/]+>(.*?)<\/div>/gs, '
    $1
    '), medias: item._embedded['wp:featuredmedia'], }); diff --git a/lib/routes/vcb-s/index.ts b/lib/routes/vcb-s/index.ts index df8ec6c9f..1895f826d 100644 --- a/lib/routes/vcb-s/index.ts +++ b/lib/routes/vcb-s/index.ts @@ -33,7 +33,7 @@ async function handler(ctx) { const items = data.map((item) => { const description = renderDescription({ - post: item.content.rendered.replaceAll(/
    ]*>(.*?)<\/pre>/gs, '
    $1
    ').replaceAll(/]+>(.*?)<\/div>/gs, '
    $1
    '), medias: item._embedded['wp:featuredmedia'], }); diff --git a/lib/routes/weibo/utils.ts b/lib/routes/weibo/utils.ts index c4df2ea2f..67511437d 100644 --- a/lib/routes/weibo/utils.ts +++ b/lib/routes/weibo/utils.ts @@ -26,11 +26,11 @@ const formatDescriptionText = (html, { showEmojiInDescription, showLinkIconInDes let formattedHtml = html; if (!showEmojiInDescription) { - formattedHtml = formattedHtml.replaceAll(/]*?alt=["']?([^>]+?)["']?\s[^>]*?\/><\/span>/g, '$1'); + formattedHtml = formattedHtml.replaceAll(/]*?alt=["']?([^>\s"']+)["']?\s[^>]*?\/><\/span>/g, '$1'); } if (!showLinkIconInDescription) { - formattedHtml = formattedHtml.replaceAll(/(]*>)]*><\/span>[^<>]*?([^<>]*?)<\/span><\/a>/g, '$1$2'); + formattedHtml = formattedHtml.replaceAll(/(]*>)]*><\/span>[^<>]*([^<>]*)<\/span><\/a>/g, '$1$2'); } return formattedHtml; @@ -141,7 +141,7 @@ const weiboUtils = { })(), formatTitle: (html) => html - .replaceAll(/]*?alt=["']?([^>]+?)["']?\s[^>]*?\/?><\/span>/g, '$1') // 表情转换 + .replaceAll(/]*?alt=["']?([^>\s"']+)["']?\s[^>]*?\/?><\/span>/g, '$1') // 表情转换 .replaceAll(/(]*>)<\/span>/g, '') // 去掉所有图标 .replaceAll(//g, '[图片]') // impossible to have inline script in weibo posts, but CodeQL complains about it diff --git a/lib/routes/wenku8/volume.ts b/lib/routes/wenku8/volume.ts index ff0381de5..addd9ea65 100644 --- a/lib/routes/wenku8/volume.ts +++ b/lib/routes/wenku8/volume.ts @@ -42,7 +42,7 @@ async function handler(ctx) { title: `轻小说文库 ${$('#title').text()} 最新卷`, link, item: await cache.tryGet(volumeUrl, async () => - [...(await get(volumeUrl)).matchAll(/\s{2}(\S.*)\r?\n([\S\s]+?)\r?\n\r?\n/g)] + [...(await get(volumeUrl)).matchAll(/\s{2}(\S.*)\r?\n([\s\S]+?)\r?\n\r?\n/g)] .map((chapter, index) => ({ title: chapter[1], description: chapter[2] diff --git a/lib/routes/wfu/news.ts b/lib/routes/wfu/news.ts index c1945c2a7..613e796ed 100644 --- a/lib/routes/wfu/news.ts +++ b/lib/routes/wfu/news.ts @@ -24,7 +24,7 @@ async function loadContent(link) { let response; // 如果不是 大学的站点, 直接返回简单的标题即可 // 判断 是否外站链接,如果是 则直接返回页面 不做单独的解析 - const https_reg = /^https:\/\/www.wfu.edu.cn(.*)/; + const https_reg = /^https:\/\/www\.wfu\.edu\.cn\/.*/; if (!https_reg.test(link)) { return { description }; } diff --git a/lib/routes/wikipedia/current-events.ts b/lib/routes/wikipedia/current-events.ts index 8ae433d94..82cd50125 100644 --- a/lib/routes/wikipedia/current-events.ts +++ b/lib/routes/wikipedia/current-events.ts @@ -51,12 +51,12 @@ function parseCurrentEventsTemplate(wikitext: string): string | null { // Look for {{Current events|content=...}} template // The closing }} is always at the end of wikitext - const contentMatch = wikitext.match(/\{\{Current events\s*\|[\s\S]*?content\s*=\s*([\s\S]*)\}\}$/); + const contentMatch = wikitext.match(/\{\{Current events\s*\|[\s\S]*?content(?=(\s*=))\1\s*((?:\S[\s\S]*)?)\}\}$/); if (!contentMatch) { return null; } - let content = contentMatch[1].trim(); + let content = contentMatch[2].trim(); // Strip comments to detect empty content content = stripComments(content); @@ -84,7 +84,7 @@ function convertWikiLinks(html: string): string { function convertExternalLinks(html: string): string { // Convert external links [URL Text] or [URL] - html = html.replaceAll(/\[([^\s\]]+)\s+([^\]]+)\]/g, '$2'); + html = html.replaceAll(/\[([^\s\]]+)\s+([^\s\]][^\]]*|\s)\]/g, '$2'); html = html.replaceAll(/\[([^\s\]]+)\]/g, '$1'); return html; } @@ -186,7 +186,7 @@ function processListsAndLines(html: string): string { } // Check for bullet points - const bulletMatch = trimmedLine.match(/^(\*+)\s*(.*)$/); + const bulletMatch = trimmedLine.match(/^(\*+)(?!\*)\s*((?:\S.*)?)$/); if (bulletMatch) { const depth = bulletMatch[1].length; const content = bulletMatch[2]; diff --git a/lib/routes/wmc-bj/publish.tsx b/lib/routes/wmc-bj/publish.tsx index e16afd072..822fa2653 100644 --- a/lib/routes/wmc-bj/publish.tsx +++ b/lib/routes/wmc-bj/publish.tsx @@ -46,7 +46,7 @@ async function handler(ctx) { ), category: categories, guid: `${currentUrl}#${datetime}`, - pubDate: timezone(parseDate(/^[A-Za-z]{3}/.test(datetime) ? datetime.replace(/^\w+/, '') : datetime, ['DD MMM HH:mm', 'MM/DD HH:mm']), +0), + pubDate: timezone(parseDate(/^[A-Z]{3}/i.test(datetime) ? datetime.replace(/^\w+/, '') : datetime, ['DD MMM HH:mm', 'MM/DD HH:mm']), +0), }, ]; diff --git a/lib/routes/wnacg/common.tsx b/lib/routes/wnacg/common.tsx index 16cf18bfb..5c1edf68e 100644 --- a/lib/routes/wnacg/common.tsx +++ b/lib/routes/wnacg/common.tsx @@ -85,7 +85,7 @@ export async function handler(ctx) { const imgListMatch = $('script') .text() - .match(/var imglist = (\[.*]);"\);/)[1]; + .match(/var imglist = (\[.*\]);"\);/)[1]; const imgList = JSON.parse(imgListMatch.replaceAll('url:', '"url":').replaceAll('caption:', '"caption":').replaceAll('fast_img_host+\\', '').replaceAll('\\', '')); diff --git a/lib/routes/wordpress/index.ts b/lib/routes/wordpress/index.ts index a0558f897..225f0d44d 100644 --- a/lib/routes/wordpress/index.ts +++ b/lib/routes/wordpress/index.ts @@ -17,7 +17,7 @@ async function handler(ctx) { throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } - if (!/^(https?):\/\/[^\s#$./?].\S*$/i.test(url)) { + if (!/^https?:\/\/[^\s#$./?].\S*$/i.test(url)) { throw new Error('Invalid URL'); } @@ -39,7 +39,7 @@ async function handler(ctx) { try { const { data: response } = await got(apiUrl); - const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*])$/)[1])).slice(0, limit).map((item) => { + const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*\])$/)[1])).slice(0, limit).map((item) => { const terminologies = item._embedded['wp:term']; const guid = item.guid?.rendered ?? item.guid; diff --git a/lib/routes/wsj/news.ts b/lib/routes/wsj/news.ts index c9c575f0e..034cc4016 100644 --- a/lib/routes/wsj/news.ts +++ b/lib/routes/wsj/news.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const $ = load(response.data); const contents = $('script:contains("window.__STATE__")').text(); - const data = JSON.parse(contents.match(/{.*}/)[0]).data; + const data = JSON.parse(contents.match(/\{.*\}/)[0]).data; const filteredKeys = Object.entries(data) .filter(([key, value]) => { if (!key.startsWith('article')) { diff --git a/lib/routes/xaufe/jiaowu.ts b/lib/routes/xaufe/jiaowu.ts index 6f8a568aa..43cac6d1c 100644 --- a/lib/routes/xaufe/jiaowu.ts +++ b/lib/routes/xaufe/jiaowu.ts @@ -80,7 +80,7 @@ async function handler(ctx) { url: item.link, }); const $ = load(response.body); - item.author = /作者:(\S*)\s{4}/g.exec($('p', '.main_contit').text())[1]; + item.author = /作者:(\S*)\s{4}/.exec($('p', '.main_contit').text())[1]; item.description = $('#vsb_content').html(); return item; }) diff --git a/lib/routes/xhamster/index.ts b/lib/routes/xhamster/index.ts index c92562d5a..144b6d364 100644 --- a/lib/routes/xhamster/index.ts +++ b/lib/routes/xhamster/index.ts @@ -60,7 +60,7 @@ interface Initials { } function extractInitials(scriptContent: string): Initials { - const match = scriptContent.match(/window\.initials\s*=\s*([\s\S]*?);?$/); + const match = scriptContent.match(/window\.initials\s*=\s*(\S[\s\S]*?);?$/); if (!match) { throw new Error('initials not found'); } diff --git a/lib/routes/xinpianchang/index.ts b/lib/routes/xinpianchang/index.ts index 55a662320..3509d86e8 100644 --- a/lib/routes/xinpianchang/index.ts +++ b/lib/routes/xinpianchang/index.ts @@ -34,7 +34,7 @@ async function handler(ctx) { const { data, response } = await getData(currentUrl, cache.tryGet); - let items = JSON.parse(response.match(/"list":(\[.*?]),"total"/)[1]); + let items = JSON.parse(response.match(/"list":(\[.*?\]),"total"/)[1]); items = await processItems(items.slice(0, limit), cache.tryGet); diff --git a/lib/routes/xueqiu/snb.ts b/lib/routes/xueqiu/snb.ts index 141f971b2..32ac6e1ab 100644 --- a/lib/routes/xueqiu/snb.ts +++ b/lib/routes/xueqiu/snb.ts @@ -35,7 +35,7 @@ async function handler(ctx) { }); const data = response.data; - const pattern = /SNB.cubeInfo = {(.+)}/; + const pattern = /SNB.cubeInfo = \{(.+)\}/; const info = pattern.exec(data); const obj = JSON.parse('{' + info[1] + '}'); const rebalancing_histories = obj.sell_rebalancing.rebalancing_histories; diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index 5b111f133..641d81f90 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -92,7 +92,7 @@ async function handler(ctx) { const content = await mainPage.evaluate(() => { const articleContent = document.querySelector('.article__bd')?.innerHTML || ''; - const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?});/); + const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?\});/); return { articleContent, statusData: statusMatch ? statusMatch[1] : null, diff --git a/lib/routes/xys/new.tsx b/lib/routes/xys/new.tsx index 921714e76..c50e50dfd 100644 --- a/lib/routes/xys/new.tsx +++ b/lib/routes/xys/new.tsx @@ -67,7 +67,7 @@ async function handler(ctx) { .filter((item) => !item.link.endsWith('.zip')) .map((item) => cache.tryGet(item.link, async () => { - const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-]+)&?/g; + const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be.*(?:v=|v\/|\/)([\w-]+)&?/g; const matchYoutube = item.link.match(youTube); if (matchYoutube) { diff --git a/lib/routes/yamibo/utils.ts b/lib/routes/yamibo/utils.ts index 437d5d14a..1fcdb07f4 100644 --- a/lib/routes/yamibo/utils.ts +++ b/lib/routes/yamibo/utils.ts @@ -45,12 +45,12 @@ export async function fetchThread( // sometimes may trigger anti-crawling measures if (data.startsWith('