* feat(route): add EFE Noticias route
Add route for EFE (Agencia EFE) Spanish news agency with 10 categories:
mundo, espana, economia, cultura, ciencia-y-tecnologia, deportes,
salud, medio-ambiente, educacion, euro-efe.
Features:
- Article images via enclosure and inline in description
- Ad filtering (auto-banner, srr-main, promotional logos)
- Clean img attributes for RSS reader compatibility
- Configurable item limit via query parameter
* fix(route/efe): address review comments
- Remove `limit` from `parameters` (query param, not path param)
- Scope article link selector to `.elementor-loop-container` to avoid anti-pattern #1
- Remove unnecessary `.first()` on `.elementor-widget-theme-post-content`
- Remove `.srr-main` from cleanup (not inside content area), keep `decoding`/`loading` on images
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: address review comments for EFE route
- Fix parameters to structured object with only path params
- Scope selector to .elementor-post article links only
- Remove unnecessary .first() on title
- Stop stripping image attributes (decoding, loading, width, etc.)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: revert overly specific selector that broke all routes
.elementor-post class doesn't exist on EFE site, use attribute
selector to pre-filter article URLs instead.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: fetch articles sequentially to avoid 429 rate limiting
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: suppress no-await-in-loop lint for sequential rate-limited fetches
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: use specific selector for article title links only
Select only .elementor-widget-theme-post-title a inside .e-loop-item
to avoid matching image links, category links, or duplicate entries.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: use single quotes for static string (oxlint stylistic/quotes)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: use Promise.all and remove Set (selector matches once per article)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor: use Array.map instead of push for link collection
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(efe): use pMap to limit concurrency and avoid 429 rate limiting
---------
Co-authored-by: mlkgrnt <mlkgrnt@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* route(naver): add Naver search RSS route
Supports all search categories: all (통합검색), blog (블로그), cafe (카페), news (뉴스), video (동영상)
* fix: use string replaceAll instead of regex in cleanText
Replace regex patterns with plain strings to satisfy oxlint unicorn/prefer-string-replace-all rule.
* fix: remove console.log and fix cleanText double-unescaping
- Remove console.log to satisfy oxlint no-console rule
- Use single-pass regex replaceAll to prevent HTML entity double-unescaping (CodeQL)
* fix(naver): address review comments and fix cafe/all category bugs
- Fix ALL category: use templateIds array to extract all content types (web, blog, news, video) instead of only webItem
- Fix CAFE category: change URL to ssc=tab.m_cafe.all format and use cheerio HTML parsing since cafe page returns pure HTML instead of JSON SSR
- Address Rule 11: move author/source metadata from description to dedicated author field
- Address Rule 14: return undefined for unparseable dates instead of fake current time
- Use single route path instead of array
- Add maintainer GitHub ID
- Fix cleanText double-unescaping issue with single-pass regex callback
* refactor(naver): split extractItems to reduce cyclomatic complexity
Split the 37-complexity extractItems into extractVideoItem and extractGenericItem.
Fixes CodeFactor "Complex Method" warning.
* refactor(naver): split functions to pass CodeFactor complexity check
Extract extractLink, buildItemFromTemplate, buildVideoDescription, and
buildVideoResult helpers to bring all methods under complexity threshold.
* fix(route): address review - use DataItem, Array.map, and entities package
- Replace manual item type with DataItem from @/types
- Use toArray().map().filter() instead of each() + push()
- Use decodeHTML from 'entities' instead of manual replaceAll
* fix(route): fix oxlint regex warnings
- Use non-capturing groups for regex used only with .test()
- Escape { } in regex literal
- Use character class [분일주] for single-char alternatives
* feat(route): add Animal Politico
* fix(route/animalpolitico): shorter name for politica route, replaced correct feed link
* fix(route/animalpolitico): shorter name for estados route
* fix(route/animalpolitico): replaced wrong route category
* fix(route/animalpolitico): used template literals for better readability on GraphQL query
* fix(route/animalpolitico): change namespace (1/2)
* fix(route/animalpolitico): change namespace (2/2)
* refactor: use patchwright
* refactor: migrate setRequestInterception and waitUntil
* fix: overwrite ua
* refactor: drop puppeteer cookie/requestfinished shims
Migrate page.cookies/setCookie and browser.cookies/setCookie call sites to the native context APIs (page.context().cookies/addCookies), rewrite requestfinished handlers to await request.response() instead of consuming the synchronous puppeteer-shaped payload, and remove the now-redundant browser.close context-cleanup wrapper. Playwright already handles all of these natively; the shims existed only for the puppeteer -> playwright migration.
* refactor: remove page.authenticate, page.setUserAgent, browser.newPage(), browser.userAgent() shims
update cookie utility tests
* refactor: replace browser instance with context in Playwright usage across multiple routes
- Updated all instances of `browser` to `context` in Playwright-related functions for consistency and improved resource management.
- Ensured proper closure of context after operations to prevent memory leaks.
- Adjusted request interception methods to utilize the new context variable.
* fix: direct type import
* refactor: remove Puppeteer compatibility layers and unify page handling with Playwright
* fix: test case
* chore: bump patchright to v1.60.0
* chore: pin patchright to 1.59.1
nft can't resolve browser.json due to https://github.com/microsoft/playwright/pull/40093
* chore: pin to node 24.15 for playwright
browser install on node 24.16 stucks due to https://github.com/microsoft/playwright/issues/40724
* fix: update patchright and patchright-core to version 1.60.2 and adjust minify-docker.js for compatibility
* fix: import BrowserContext from patchright
* feat(route): add Le Monde route
* feat(route): add Le Monde in English route
Add English version of Le Monde RSS route with 40+ categories
covering World, Europe, France, Economy, Culture, Sports, etc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(route/le-monde): remove unnecessary cache.tryGet wrapper
cache.tryGet was wrapping a synchronous callback with no async work,
making it a no-op. The items are already fully parsed from the RSS
feed, so direct assignment is sufficient.
* fix: address review comments for Le Monde route
- Rename namespace from le-monde/le-monde-en to lemonde
- Remove unnecessary .first() calls on RSS elements
- Remove default ofetch responseType option
- Use static cheerio import instead of dynamic import
- Extract <category> tags into category field
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: sort imports to satisfy oxlint
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: update example paths to match lemonde namespace
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: trigger CI to re-test with updated route paths
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: remove non-existent <content> element fallback from image URL
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: mlkgrnt <mlkgrnt@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: transform json number item link
* docs(route/rsshub): use concrete json transform example
* Revert "docs(route/rsshub): use concrete json transform example"
This reverts commit e4d916451afa2207959312d999dfbc6c71919e55.
---------
Co-authored-by: jack <jack@example.com>
Co-authored-by: jack <jack@jacks-MacBook-Air.local>
* feat: add shanxi museum temporary exhibition route
* chore: trigger ci
* fix: remove pubDate if no return information
* fix: remove redundancy
* fix:fix the timezone error and refactor the resData and list
* fix: remove ! check for imgurl
* fix: remove empty message and add trycatch
* feat(route/sjtu): add CS school academic activities
* refactor(route/sjtu/cs/xshd): use JSX for description and respect dedicated item fields
- Switch to .tsx with renderToString (Rule 25)
- Move 时间→pubDate (with HH:mm), 报告人→author, 封面→image (Rule 11)
- Drop hand-written referrerpolicy attributes (Rule 39)
- pubDate now preserves hour/minute when source provides it
* fix(route/sjtu/cs/xshd): separate publish date from event time
- pubDate now reflects announcement publish date:
- in-site articles: detail page 发布日期 (authoritative)
- WeChat-redirect items: extracted from image URL path /upload/image/YYYYMMDD/
- 举办时间 (event time) moved back into description (no longer duplicates pubDate)
* fix(route/sjtu/cs/xshd): remove manual title trimming (Rule 16)
* fix(route/sjtu/cs/xshd): address review comments
- Use response URL to detect WeChat redirect
- Fetch WeChat MP content via fetchArticle
- Remove try-catch around detail requests
- Remove dangerouslySetInnerHTML from JSX rendering
* fix(route/sjtu/cs/xshd): use ofetch consistently
* fix(route/sjtu/cs/xshd): fallback on WeChat MP WAF errors
* fix(route/sjtu/cs/xshd): fallback when detail enrichment fails
The route enriches list items by fetching detail pages, but some SJTU
detail URLs are redirect stubs to WeChat MP. In CI, those intermediate
detail requests can fail with no response before the route reaches the
WeChat MP parser, causing Promise.all to reject and the whole feed to
return 503.
Switch detail enrichment aggregation from Promise.all to
Promise.allSettled and fall back to list-page metadata for only the
failed item. Successful items are still enriched with in-site article
content or WeChat MP content.
This follows the existing resilient detail-fetching pattern in
lib/routes/usenix/usenix.ts and keeps the scoped WeChatMpError fallback
around fetchArticle(), similar to lib/routes/wechat/sogou.ts.
* feat(route): add the Palace Museum route
* fix: fix trailing space, array and radar target
* fix: fix array
* fix: follow rule22
* refactor(dpm): migrate exhibitions route to JSX format (.tsx)
* fix(dpm): use arrow function in cheerio filter
* fix: update example
* fix: remove redundancy and fix error for endDate and fullduration
* chore: trigger gh actions retry
* fix: remove trim for fullduration
* fix:remove ! check for imgurl and fullduration
* fix: remove exhibition_item
* feat: add China National Museum exhibition route
* Update route.ts
* Update zl.ts
* Update zl.ts
* Update zl.ts
* fix: refactor to JSX and fix cache import
* fix: refactor to JSX, fix duration and fix import
* fix: replaceAll, if and regualr expression range
* Update zl.ts
* fix: refactor extra output and import namespace
* fix: fix error
* fix: adjust fullduration logic
* feat: refactor route file with type and subtype used
* fix: reduce the complexity
* fix: remove trim() for title
* fix: remove redundancy and fix error
* fix: remove toLowerCase redundancy
* fix: remove trim for title
* fix: Apply suggestions from code review and error fix
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
---------
Here is a page with description-box:
https://www.cna.com.tw/news/aipl/202605070117.aspx
Explanation for the term 憲法第130條 is injected in the middle of a
paragraph and makes that paragraph hard to read. This change fixes that.
* route(gigazine): add English news full-text feed
Adds /gigazine/en route that fetches full article content from
GIGAZINE's English news listing. Uses sequential fetching with
delays to respect Cloudflare rate limits; subsequent loads are
served from cache.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* route(gigazine): improve english feed resilience
* test(gigazine): add english route coverage
* test(gigazine): move route coverage out of route tree
* test(api): cover route status cache fallbacks
* refactor(gigazine): apply PR review feedback to english route
- Use ofetch built-in auto-retry instead of hand-rolled circuit breaker
- Remove config.trueUA; let request-rewriter inject random browser UA
- Simplify Number.parseInt call (drop redundant ?? '' fallback)
- Update tests to match new retry and UA behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove unrelated changes and unnecessary test file
* refactor(gigazine): simplify selectors and cleanup logic per review
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(route): support fulltext mode for nyaa
* feat(route): replace parameter name
* feat(route): migrate from query parameters to route paths
* feat(route): use mode=fulltext as trigger and update namespace
* delete fulltext routes
* feat(route): add /humanlayer/blog route
Add RSS feed for HumanLayer blog (www.humanlayer.dev/blog), scraping
blog listing and full article content with cheerio.
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(route): use CSS :not() selector instead of JS filter for humanlayer blog
Address PR review feedback: move /blog/tags/ exclusion into the CSS selector
instead of using a separate .filter() call.
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(route): extract tags into category field for humanlayer blog
Parse #-prefixed tags from the meta line and populate the category field.
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(route): address review feedback for humanlayer blog
- Remove redundant :not() selector (tag links use different class)
- Simplify tag extraction from parts.slice(3).join() to parts[3]
- Fix tag regex to match hyphenated tags like best-practices
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(route): remove custom limit for humanlayer blog, use built-in
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: HAPI <noreply@hapi.run>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: zj1123581321 <zj1123581321@users.noreply.github.com>