feat: migrate deprecated routes (#23071)

* feat: migrate batch-0

* feat: migrate batch-a

batch-0
Migrated (3 routes)
┌────────────────────────────────┬───────────────────────┐
│            old path            │       new path        │
├────────────────────────────────┼───────────────────────┤
│ /755/user/:username            │ /7gogo/user/:username │
├────────────────────────────────┼───────────────────────┤
│ /99percentinvisible/transcript │ unchanged             │
├────────────────────────────────┼───────────────────────┤
│ /forum4399/:mtag               │ /4399/forums/:mtag    │
└────────────────────────────────┴───────────────────────┘
Not needed — already covered by an existing route (3 units, 6 files)
┌────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                    deleted                     │                                                covered by                                                │
├────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 10000link/news.js (/10000link/news/:category?) │ 10000link/info.ts → /10000link/info/newslists/My01                                                       │
├────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 199it/{index,category,tag,utils}.js            │ 199it/index.tsx → /199it/:category{.+}?;                                                                 │
├────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 60s-science/transcript.js (/60s-science)       │ scientificamerican/podcast.ts → /scientificamerican/podcast/:id?                                         │
└────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Dropped (1) — (/12379)

batch-a
Migrated (12 routes, 26 new files) — acm/amturingaward, acwifi, allpoetry, aozora/newbook, archdaily, arxiv/:query, asahi, asml/press-releases, autotrader/:query, av01/actor, av01/tag, axis-studios/work.
Not needed — already covered (8 registrations)
┌─────────────────────────────────────────────────┬────────────────────────────────────────────────────────────┐
│                     deleted                     │                         covered by                         │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ adquan/index.js (/adquan/:type?)                │ adquan/index.ts → /adquan                                  │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ aflcio/blog.js                                  │ aflcio/blog.ts                                             │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ amazfitwatchfaces/{fresh,updated,top,search}.js │ amazfitwatchfaces/index.ts → /:device/:sort/:searchParams? │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ appsales/index.js                               │ app-sales/ namespace                                       │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ anytxt/release-notes.js                         │ anytxt/release-notes.ts                                    │
├─────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
│ aqicn/index.js                                  │ aqicn/aqi.ts;                                              │
└─────────────────────────────────────────────────┴────────────────────────────────────────────────────────────┘
Dropped (3) — algocasts/all.js (algocasts.io 404), aiyanxishe/home.js (api.yanxishe.com no longer resolves), amd/graphicsdrivers.js

* feat: migrate batch-b

Migrated (8 registrations, 14 new files)
┌─────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐
│                        old path                         │                              new path                              │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /babykingdom/:id/:order?                                │ /baby-kingdom/:id/:order?                                          │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /bahamut/creation/:author/:category?                    │ /gamer/creation/:author/:folder? (param renamed category→folder)   │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /bahamut/creation_index/:category?/:subcategory?/:type? │ /gamer/creation_index/... (unchanged shape)                        │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /banyuetan/byt/:time?                                   │ unchanged — new byt.ts added into the existing banyuetan namespace │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /benedictevans                                          │ /ben-evans                                                         │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /blow-studio                                            │ /blowstudio                                                        │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /blur-studio                                            │ /blur                                                              │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
│ /booth.pm/shop/:subdomain                               │ unchanged (dir booth-pm → booth.pm)                                │
└─────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘
Not needed — already covered (3)
┌─────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                   deleted                   │                                                              covered by                                                               │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ banyuetan/index.js (/banyuetan/:name)       │ existing banyuetan/index.ts → /banyuetan/:id?                                                                                         │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ bandisoft/index.js (/bandisoft/:id?/:lang?) │ existing bandisoft/history.ts → /bandisoft/history/:id?/:language? (same {lang}.bandisoft.com/{id}/history/ pages)                    │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ bookwalkertw/news.js (/bookwalkertw/news)   │ existing bookwalker/search.tsx → /bookwalker/search/order=sell_desc&series_display=1&d=1 - the exact URL the deprecated route fetched │
└─────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Dropped (2)
- bibgame/category.js (/bibgame/:category?/:type?) - bibgame.com is now a parked-domain lander.
- bsblog123/index.js (/bsblog123) - bsblog123.com no longer resolves.

* feat: migrate batch-c

Migrated (26)
cell/cell, cell/cover, centbrowser/history, cgtn/most, cgtn/opinions, chaoli, checkee, chicagotribune, chinafile, chocolatey/software, chouti, chouti/top, chsi/zszcgd, chuhaibiji, cninfo/announcement, coolbuy/newest, cowlevel/element, creaders/headline, csc/notice, ctei/news, ctfhub/search, ctfhub/upcoming.

Path changed (3):
┌───────────────────────────────────┬──────────────────────┐
│                old                │         new          │
├───────────────────────────────────┼──────────────────────┤
│ /checkra1n/releases               │ /checkra.in/releases │
├───────────────────────────────────┼──────────────────────┤
│ /cnu/selected, /cnu/discovery/... │ /cnu.cc/...          │
├───────────────────────────────────┼──────────────────────┤
│ /cpython/:pre?                    │ /python/:pre?        │
└───────────────────────────────────┴──────────────────────┘

Not needed — already covered (3)
┌────────────────────────┬────────────────────────┐
│        deleted         │  covered by            │
├────────────────────────┼────────────────────────┤
│ cbndata/information.js │ cbndata/information.ts │
├────────────────────────┼────────────────────────┤
│ ccg/index.js           │ ccg/index.ts           │
├────────────────────────┼────────────────────────┤
│ chinadaily/english.js  │ chinadaily/language.ts │
└────────────────────────┴────────────────────────┘

Dropped (3)

- cfan/news.js
- citavi/index.js
- clb/commentary.js

* feat: migrate batch-d

Migrated (18 units)

daodu, daxiaamu/home, dbaplus/activity, dcinside/board, dekudeals, deloitte/industries, dgtle ×5 (index, whale, whale-rank, trade, keyword), digitaling ×3, discuss, disqus/posts, dongmanmanhua/comic.

Moved to top-level namespaces:
┌────────────────────────────────────────────────┬───────────┐
│                      old                       │    new    │
├────────────────────────────────────────────────┼───────────┤
│ /dgut/xsc/:type?                               │ unchanged │
├────────────────────────────────────────────────┼───────────┤
│ /dlmu/news/:type, /dlmu/grs/zsgz/:type         │ unchanged │
├────────────────────────────────────────────────┼───────────┤
│ /dlu/jiaowu/news                               │ unchanged │
├────────────────────────────────────────────────┼───────────┤
│ /dpu/jiaowu/news/:type?, /dpu/wlfw/news/:type? │ /dlpu/…   │
└────────────────────────────────────────────────┴───────────┘

Not needed — already covered (6)
┌─────────────────────────────────────────────┬───────────────────────────────────────────────────────────────┐
│                   deleted                   │                          covered by                           │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ dailyart/index.js (/dailyart/:language?)    │ getdailyart/index.ts → /getdailyart/:language? (path changed) │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ dbaplus/tab.js (/dbaplus/:tab?)             │ dbaplus/rss.ts (/) + dbaplus/new.ts (/news/:id?)              │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ deepl/blog.js                               │ deepl/blog.ts — identical path                                │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ dw/index.js (/dw/:lang?/:caty?)             │ dw/news.ts + dw/rss.ts                                        │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ duozhi/index.js                             │ duozhi/index.ts                                               │
├─────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ universities/dgut/jwc.js (/dgut/jwc/:type?) │ dgut/jwb.ts → /dgut/jwb/:type?;                               │
└─────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘

Dropped (3 units, 6 registrations)
- ddrk
- dida365/habit-checkins.js
- ditto/changes.js

* feat: migrate batch-e

Migrated (11 units)
edrawsoft/mindmap, emi-nitta, etherscan/transactions, etoland/board, europapress, eztv/imdb, ebb, eeo.

Path changed (3):
┌──────────────────────┬───────────────────────┐
│         old          │          new          │
├──────────────────────┼───────────────────────┤
│ /eleme/open/announce │ /ele.me/open/announce │
├──────────────────────┼───────────────────────┤
│ /everything/changes  │ /voidtools/changes    │
├──────────────────────┼───────────────────────┤
│ /engadget/:lang?     │ /engadget/            │
└──────────────────────┴───────────────────────┘

Not needed (1 unit, 2 registrations + 2 dangling)
/dytt and /dytt/index

Dropped (2 units, 4 registrations)
- enclavebooks/{category,user,collection}.js

* feat: migrate batch-f

┌────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────┐
│                                old                                 │                          new                          │
├────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /fanfou                                                            │ unchanged                                             │
├────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /fashionnetwork/news/:sectors?/:categories?/:language?             │ /fashionnetwork/news/:sectors?/:categories?/:country? │
├────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /fashionnetwork/headline/:country?                                 │ unchanged                                             │
├────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /fgo/news                                                          │ /fate-go/news                                         │
├────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /fnal/news/:category?                                              │ unchanged                                             │
└────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────┘
Universities (a-f)
- fzu/news-min.ts deleted

* feat: migrate batch-g

Migrated

gab/user, gameres, gitlab, gocomics, gushiwen, guat, guet, gwern, gzmtu, gdou, ngd, gov/caict, gov/cnca, gov/harbin/kjj, gov/mohrss/sbjm, gov/mohurd/policy, gov/shanxi/rst, gov/wuhan, gov/beijing/mhc.
/gdoujwc → /gdou/jwc;
/ngd

path changed (14)
┌─────────────────────────────────────────────────────────┬────────────────────────────────────────────┐
│                           old                           │                    new                     │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /geektime/column/:cid                                   │ /geekbang/column/:cid                      │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /guardian/:type                                         │ /theguardian                               │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /grandchallenge/                                        │ /grand-challenge/                          │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /go.jp/mofa                                             │ /mofa                                      │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/beijing/bjeea/:type                                │ /bjeea/:type                               │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/hubei/hbsia/:caty                                  │ /hbsea/:id                                 │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/guangdong/{edu,eea}/:caty                          │ /gov/gd/{edu,eea}/:caty                    │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/hunan/notice/:type                                 │ /gov/ccgp-hunan/notice/:type?              │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/veterans/:type                                     │ /gov/mva/:type                             │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/veterans/hebei/:type                               │ /gov/hebei/tyjrswt/:type                   │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/taiwan/mnd                                         │ /gov/mnd                                   │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/xinwen/tujie/:caty                                 │ /gov/cn/xinwen/tujie/:caty                 │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/city/:name/:category (nanjing-only dispatcher)     │ /gov/nanjing/:category                     │
├─────────────────────────────────────────────────────────┼────────────────────────────────────────────┤
│ /gov/province/:name/:category (jiangsu-only dispatcher) │ /gov/jiangsu/:category                     │
└─────────────────────────────────────────────────────────┴────────────────────────────────────────────┘
gov/shanghai/sthj → gov/sh/sthj
gov/cppcc

Not needed (2)
- gnn/gnn.js - covered by batch-b's /gamer/gnn/:category?
- getdailyart - from batch-d

Dropped (8 units)
- galaxylab, guanggoo, gov/chongqing/ljxq
- gov/sapprft/7026 → gov/nppa
- gaoqing/latest
- gov/statecouncil/briefing
- gov/shuju

* refactor: cleanup

* feat: migrate batch-h

Migrated (12 units)

hackerone, hainanu/ssszs, hanime/video, hentaimama/videos, hexo, huya/live, hbut/news, henu/news, hfut/tzgg, hnust, hhu/libNews.
hdx -> /huodongxing/explore

Migrated - public path changed (3)
┌─────────────────────────────────┬───────────────────────────────────────────┐
│               old               │                    new                    │
├─────────────────────────────────┼───────────────────────────────────────────┤
│ /porn-images-xxx/:type?/:name?  │ /hentai-cosplays-xxx/:type?/:name?        │
├─────────────────────────────────┼───────────────────────────────────────────┤
│ /hatena/anonymous_diary/archive │ /hatelabo/anonymous_diary/archive         │
├─────────────────────────────────┼───────────────────────────────────────────┤
│ /hbut/cs/:type                  │ /hbut/scs/:type (school renamed cs → scs) │
└─────────────────────────────────┴───────────────────────────────────────────┘

Not needed (2)
- hkej/index.js -> master's hkej/index.tsx
- /hubu/news.js -> hubu/index.ts

Dropped (3 units)
- haohaozhu
- hudongba
- hhu/libNewsc

* feat: migrate batch-i

Migrated (8 units)
┌───────────────┬────────────────────────────────┐
│     route     │              path              │
├───────────────┼────────────────────────────────┤
│ ichunqiu      │ /ichunqiu                      │
├───────────────┼────────────────────────────────┤
│ iciba         │ /iciba/:days?/:img_type?       │
├───────────────┼────────────────────────────────┤
│ icity         │ /icity/:id                     │
├───────────────┼────────────────────────────────┤
│ icourse163    │ /icourse163/newest             │
├───────────────┼────────────────────────────────┤
│ idownloadblog │ /idownloadblog                 │
├───────────────┼────────────────────────────────┤
│ ifnews        │ /ifnews/:cid                   │
├───────────────┼────────────────────────────────┤
│ im2maker      │ /im2maker/:channel?            │
├───────────────┼────────────────────────────────┤
│ itslaw        │ /itslaw/judgements/:conditions │
└───────────────┴────────────────────────────────┘

Dropped (2 units)
- interesting-sky
- invisionapp/inside-design

* feat: migrate batch-j

Migrated (5 units)
┌────────────────────────────────────────────────┬────────────────────────────────────┐
│                      old                       │                new                 │
├────────────────────────────────────────────────┼────────────────────────────────────┤
│ /jgsu/jwc                                      │ unchanged                          │
├────────────────────────────────────────────────┼────────────────────────────────────┤
│ /jlbtc/:category?, /jlbtc/jwc/:id?,            │ unchanged (jwc param now           │
│ /jlbtc/kyc/:category?                          │ :category{.+}?)                    │
├────────────────────────────────────────────────┼────────────────────────────────────┤
│ /jnu/yw/:type?                                 │ unchanged                          │
├────────────────────────────────────────────────┼────────────────────────────────────┤
│ /ju/jwc/:type?                                 │ /jiangnan/jwc/:type?               │
├────────────────────────────────────────────────┼────────────────────────────────────┤
│ /jx3/:caty?                                    │ /xoyo/jx3/:caty?                   │
│                                                │                                    │
└────────────────────────────────────────────────┴────────────────────────────────────┘

Dropped (2)
- universities/jlu/oa.js (/jlu/oa)
- universities/jnu/xysx/index.js (/jnu/xysx/:type)

* feat: migrate batch-k

Migrated (7 units)
┌─────────────────────────────────────┬──────────────────┐
│                 old                 │       new        │
├─────────────────────────────────────┼──────────────────┤
│ /kaiyan/index                       │ /kaiyanapp/index │
├─────────────────────────────────────┼──────────────────┤
│ /kaoyan                             │ unchanged        │
├─────────────────────────────────────┼──────────────────┤
│ /kchuhai                            │ /kchuhai/report  │
├─────────────────────────────────────┼──────────────────┤
│ /kenshin/:category?/:type?          │ unchanged        │
├─────────────────────────────────────┼──────────────────┤
│ /keyakizaka46/{news,blog}           │ unchanged        │
├─────────────────────────────────────┼──────────────────┤
│ /kongfz/{people/:id,shop/:id/:cat?} │ unchanged        │
├─────────────────────────────────────┼──────────────────┤
│ /kmust/jwc/:type?                   │ unchanged        │
└─────────────────────────────────────┴──────────────────┘

Not needed (1)
- kuai/id.js (/kuai/:id) → merged into kuai/index.ts as /kuai/:category?

Dropped (3 units, 4 registrations)
- kingarthur (/kingarthur/:type)
- kmust/job/{careers,jobfairs}
- kotaku/story

* feat: migrate batch-l

law/* aggregate
┌───────────┬──────────────────────────────────────────────┐
│    old    │                     new                      │
├───────────┼──────────────────────────────────────────────┤
│ /law/dh   │ dehenglaw                                    │
├───────────┼──────────────────────────────────────────────┤
│ /law/jh   │ junhe                                        │
├───────────┼──────────────────────────────────────────────┤
│ /law/zl   │ zhonglun                                     │
├───────────┼──────────────────────────────────────────────┤
│ /law/gf   │ /grandwaylaw                                 │
├───────────┼──────────────────────────────────────────────┤
│ /law/hq   │ /haiwen-law                                  │
├───────────┼──────────────────────────────────────────────┤
│ /law/hw   │ /glo                                         │
├───────────┼──────────────────────────────────────────────┤
│ /law/jctd │ /jtn                                         │
├───────────┼──────────────────────────────────────────────┤
│ /law/jtc  │ /allbrightlaw                                │
├───────────┼──────────────────────────────────────────────┤
│ /law/ts   │ /tongshang                                   │
└───────────┴──────────────────────────────────────────────┘

Migrated - same public path (9 units)
lagou/jobs, latexstudio/home, letterboxd, lifetimes, lizhi/user, lwn/alerts, and lit, lntu/jwnews, lyu/news

Migrated - public path changed (3 units)
┌─────────────────────────────────────────┬────────────────────────────────────┐
│                        old              │         new                        │
├─────────────────────────────────────────┼────────────────────────────────────┤
│ /lastfm/{recent,loved}/:user,           │ /last.fm/                          │
│ /lastfm/top/:country?                   │                                    │
├─────────────────────────────────────────┼────────────────────────────────────┤
│ /liyuan-forums/threads[...]             │ /liyuans/threads                   │
├─────────────────────────────────────────┼────────────────────────────────────┤
│ /lolapp/{recommend,article/:uuid}       │ /qq/mlol/{recommend,article/:uuid} │
└─────────────────────────────────────────┴────────────────────────────────────┘

Dropped (1 unit,)
- laosiji

* feat: migrate batch-m

Migrated - same public path (20 units)

mafengwo, mamibuy, manictime/releases, maoyan, marginnote/tag, mathunion/fields-medal, meihua, meipai/user, meituan/open/announce, melon/chart, mihoyo, missevan, moxingfans, mubu/explore, muchong, monotype/article, mcdonalds, and mit/csail/news, mit/ocw-top.

Migrated - public path changed (5)
┌──────────────────────────────────────────────┬─────────────────────────────────────────────┐
│                     old                      │                new                          │
├──────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ /method-studios/:menu?                       │ /company3                                   │
├──────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ /mhw/{update,news}                           │ /monsterhunter/{update,world-iceborne-news} │
├──────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ /microsoft-store/updates/:productid/:market? │ /microsoft/store/updates/                   │
├──────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ /manxiaosi/book/:id                          │ /jjmhw/book/:id                             │
├──────────────────────────────────────────────┼─────────────────────────────────────────────┤
│ /mit/graduateadmissions/:type/:name          │ /mit/oge/:type?/:name?                      │
└──────────────────────────────────────────────┴─────────────────────────────────────────────┘

Dropped (7 units)
magireco, mcbbs, micmicidol, rthk/mediadigest, manhuadb, mofish

* refactor: cleanup

* feat: migrate batch-n

Migrated - same public path (8)
nobelprize/:caty?, nosetime, noi, nchu/jwc, nciae, njfu/jwc, njtech/jwc.

Migrated - public path changed (2)
┌──────────────────────┬──────────────────────┐
│         old          │   new                │
├──────────────────────┼──────────────────────┤
│ /nace/blog/:sort?    │ /naceweb/blog/:sort? │
├──────────────────────┼──────────────────────┤
│ /novel/uukanshu/:uid │ /uukanshu/:uid       │
└──────────────────────┴──────────────────────┘

Not needed - already covered (3)
- novel/{biquge,biqugeinfo}.js → biquge
- nku/jwc → nankai/jwc.ts
- nudt/yjszs → nudt/yjszs.ts

Dropped (6)
navisec, noi/rg-news, now/rank, nwpu, nuc, nvidia/webdriverupdate

* feat: migrate batch-o

Migrated (3)
┌────────────┬─────────────────────┐
│   route    │        path         │
├────────────┼─────────────────────┤
│ ofweek     │ /ofweek/news        │
├────────────┼─────────────────────┤
│ onejav     │ /onejav/:type/:key? │
├────────────┼─────────────────────┤
│ onenotegem │ /onenotegem/release │
└────────────┴─────────────────────┘

Not needed (2)
- one/index.js -> wufazhuce/one.ts
- ornl/news -> ornl/all-news

Dropped (1 units)
- owspace/read

* feat: migrate batch-p

Migrated - same public path (5 units)
patchwork.kernel.org/comments/:id, phrack, piapro, polimi/news/:language?, proletar/:type?/:id?.

Migrated - public path changed (2)
┌────────────────────────────┬───────────────────────────────┐
│            old             │         new                   │
├────────────────────────────┼───────────────────────────────┤
│ /parcel/hermesuk/:tracking │ /evri/:tracking (rebrand)     │
├────────────────────────────┼───────────────────────────────┤
│ /project-zero-issues       │ /chromium/project-zero-issues │
└────────────────────────────┴───────────────────────────────┘

Not needed - already covered (1)
- potplayer/update.js → master daum/potplayer.ts

Dropped (3)
- paidai, pmcaff, ptpress/book

* feat: migrate batch-q

Migrated - same public path (5)
┌───────────┬─────────────────────────────┐
│   route   │            path             │
├───────────┼─────────────────────────────┤
│ qnap      │ /qnap/release-notes/:id     │
├───────────┼─────────────────────────────┤
│ qttabbar  │ /qttabbar/change-log        │
├───────────┼─────────────────────────────┤
│ queshu    │ /queshu/{sale,book/:bookid} │
├───────────┼─────────────────────────────┤
│ qutoutiao │ /qutoutiao/category/:cid    │
├───────────┼─────────────────────────────┤
│ qzcea     │ /qzcea/:caty?               │
└───────────┴─────────────────────────────┘

Migrated - public path changed (1)
- /qcloud/mlvb/changelog → /tencent/qcloud/mlvb/changelog

* feat: migrate batch-r

Migrated - same public path (3 units)
rescuetime/release-notes/:os?, ruby-china, rmlt/idea*.
/rmlt/idea/:category? -> new /rmlt/idea

Migrated - public path changed (4)
┌────────────────────────────┬────────────────────────────┐
│            old             │                   new      │
├────────────────────────────┼────────────────────────────┤
│ /react/react-native-weekly │ /reactnativenewsletter     │
├────────────────────────────┼────────────────────────────┤
│ /remote-work/:caty?        │ /yuancheng.work/:caty?     │
├────────────────────────────┼────────────────────────────┤
│ /rf/article                │ /eefocus/rf/article        │
├────────────────────────────┼────────────────────────────┤
│ /rthk-news/:lang/:category │ /rthk/news/:lang/:category │
└────────────────────────────┴────────────────────────────┘

* feat: migrate batch-t

Migrated - same public path (13)
samsungmembers, secshi, sesame/release_notes, sexinsex, simonsfoundation, simpread, sketch, socialbeta/home, soundcloud, ssydt/article, sznews/ranking, shanbay/checkin, scu, shanghaitech, shou/www, swufe/seie, swust, sctu/jwc.

Migrated - public path changed (7)
┌────────────────────────────────────────────┬───────────────────────────────────────────────────────────────┐
│                    old                     │                              new                              │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /siren/news                                │ /hypergryph/siren/news                                        │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /socialclub/events/:game?                  │ /rockstargames/socialclub/events/:game?                       │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /security/pulses                           │ /sec.today/pulses                                             │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /souyun/today                              │ /sou-yun/today                                                │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /soul/:id, /soul/posts/hot/:pid*           │ /soulapp/:id, /soulapp/posts/tag/:tid                         │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /shanbay/footprints/:category?             │ /shanbay/news/:category?                                      │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /sctu/xgxy                                 │ /sctu/it                                                      │
├────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /slu/                                      │ /lixin/                                                       │
└────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘

Dropped (4)
- scboy/thread
- sznews/press
- steamgifts/discussions
- semiconductors/latest-news -> semiconductors/index

* feat: migrate batch-t

Migrated - same public path (7)
technologyreview, testerhome/newest, thunderbird/release, tianyancha/hot, touhougarakuta/:language/:type, trakt/collection, thu -> tsinghua/{career,info}.

Migrated - public path changed (7)
┌──────────────────────────────────────────────────────────┬──────────────────────────────────────┐
│                           old                            │                 new                  │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /totalcommander/whatsnew                                 │ /ghisler/whatsnew                    │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /tingshen                                                │ /gov/court                           │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /tucaoqq/post/:project/:key                              │ /qq/txc/post/:project                │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /weixin/miniprogram/{framework,release,devtools,wxcloud} │ /qq/weixin/miniprogram/              │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /wechat-open/pay/announce                                │ /qq/weixin/pay/announce              │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /tprtc/{news,cqzr,qyzc}                                  │ /tpre/                               │
├──────────────────────────────────────────────────────────┼──────────────────────────────────────┤
│ /topbook/{overview/:id?,today}                           │ /topbook/:id?                        │
└──────────────────────────────────────────────────────────┴──────────────────────────────────────┘
/tingdiantz/95598/:province/:city/:district? -> /sttcq/:province/:city/:district?
/tingdiantz/nanjing -> /sgcc/95598/blackoutNotice/:adcode

Dropped (4)
- tanchinese
- titsguru
- wechat-open/community/{announce,question} - login wall

sm2 benchmark
┌───────────────────────────┬────────┬─────────┬─────────┬───────┬──────────────────────┐
│            lib            │  sm3   │ sm4 enc │ sm4 dec │  sm2  │ per request envelope │
├───────────────────────────┼────────┼─────────┼─────────┼───────┼──────────────────────┤
│ sm-crypto@0.5.6           │ 245k/s │ 166k/s  │ 3.0k/s  │ 72/s  │ 14.3ms               │
├───────────────────────────┼────────┼─────────┼─────────┼───────┼──────────────────────┤
│ sm-crypto-v2@1.15.1       │ 281k/s │ 130k/s  │ 2.5k/s  │ 343/s │ 3.3ms                │
├───────────────────────────┼────────┼─────────┼─────────┼───────┼──────────────────────┤
│ gm-crypto@0.1.12          │ 265k/s │ 173k/s  │ 2.6k/s  │ 58/s  │ 17.5ms               │
└───────────────────────────┴────────┴─────────┴─────────┴───────┴──────────────────────┘

* refactor: cleanup

* feat: migrate batch-u

┌─────────────────────────────────────────────┬───────────────────────────────────────┐
│                     old                     │            new                        │
├─────────────────────────────────────────────┼───────────────────────────────────────┤
│ /us/supremecourt/argument_audio/:year?      │ /supremecourt/argument_audio/:year?   │
├─────────────────────────────────────────────┼───────────────────────────────────────┤
│ /cia/foia-annual-report (handler under us/) │ /cia/foia-annual-report               │
├─────────────────────────────────────────────┼───────────────────────────────────────┤
│ /treasury/press-releases/:category?/:title? │ unchanged                             │
├─────────────────────────────────────────────┼───────────────────────────────────────┤
│ /ustr/press-releases/:year?/:month?         │ /ustr/press-releases                  │
└─────────────────────────────────────────────┴───────────────────────────────────────┘

Migrated - same public path (6 units)
uisdc/hangye/:caty?, un/scveto, unit-image/films/:type?, uwants/:fid, usst/jwc, utdallas/isso

Migrated - public path changed (1)
- /ui-cn/{article,user/:id} → /ui.cn/

* feat: migrate batch-v

Migrated - public path changed (4)
┌────────────────────────────┬──────────────────────────────────┐
│            old             │               new                │
├────────────────────────────┼──────────────────────────────────┤
│ /vgn/:platform?            │ /vgnlab                          │
├────────────────────────────┼──────────────────────────────────┤
│ /voa/:language/:channel?   │ /voanews/:language/:channel?     │
├────────────────────────────┼──────────────────────────────────┤
│ /vol/:mode?                │ /kzo.moe                         │
├────────────────────────────┼──────────────────────────────────┤
│ /vscode/marketplace/:type? │ /visualstudio/marketplace/:type? │
└────────────────────────────┴──────────────────────────────────┘

Migrated - same public path (1)
- /vulture/:tag/:excludetags?

Not needed - already covered (1)
- vocus/user.js

* feat: migrate batch-w

Migrated - same public path (7 units)
wegene, weidian/goods/:id, wenxuecity, whb/:category, wto/dispute-settlement/:year?, wzbc/:type?, webtoons/:lang/:category/:name/:id.

Migrated - public path changed (4)
┌─────────────────────────────────────┬───────────────────────────────────┐
│                 old                 │            new                    │
├─────────────────────────────────────┼───────────────────────────────────┤
│ /watchface/:watch_type?/:list_type? │ /bandbbs/:watch_type?/:list_type? │
├─────────────────────────────────────┼───────────────────────────────────┤
│ /webtoons/naver/:id                 │ /naver/comic/:id                  │
├─────────────────────────────────────┼───────────────────────────────────┤
│ /worldhappiness/archive             │ /worldhappiness/analysis          │
├─────────────────────────────────────┼───────────────────────────────────┤
│ /worldhappiness/blog                │ /worldhappiness/news              │
└─────────────────────────────────────┴───────────────────────────────────┘

Not needed - already covered (1)
- wainao/index.js

Dropped (2 units)
- weexcn
- wikipedia/mainland

* feat: migrate batch-x

Migrated - same public path (8)
xclient/app/:name, xmind/mindmap/:lang?, xuangubao/subject/:subject_id, xyplorer/whatsnew, xynu/zkb/:category, xmu/aero/:type, xiachufang/popular/:timeframe?.

Migrated - reshaped (3)
┌────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────┐
│                                    old                                     │                              new                              │
├────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /xiachufang/user/{cooked,created}/:id                                      │ /xiachufang/user/:id/:type?                                   │
├────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /xuetangx/course/:cid/:type, /course/list/:mode/:credential/:status/:type? │ /course/:sign, /course/list/:classify?/:status?/:sellingType? │
├────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────┤
│ /xinwenlianbo/index                                                        │ /govopendata/xinwenlianbo                                     │
└────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘
/xiaomieu/releases -> /xiaomi.eu/releases
No Need(1)
- x410/news

* feat: migrate batch-y

Migrated - same public path (7)
- yidoutang
- yinxiang
- ynnu
- ynu
- yzu
- youdao
- youzan

Migrated - path changed (1)
┌───────────────────────┬───────────────────────┐
│          old          │          new          │
├───────────────────────┼───────────────────────┤
│ /yahoo-author/:author │ /yahoo/author/:author │
└───────────────────────┴───────────────────────┘

Dropped - dead (1)
- yuzu-emu (/yuzu-emu/entry)

* feat: migrate batch-z

Migrated - same public path (6)
- zhimap
- zhishifenzi
- zimuku
- zsnews
- zjgsu
- zzuli

Migrated - reshaped (3)
┌───────────────────────────────────────────────────────┬───────────────────────────────────────────────────────┐
│                          old                          │                          new                          │
├───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /zucc/news/latest, /zucc/cssearch/latest/:webVpn/:key │ /hzcu/news/latest, /hzcu/cssearch/latest/:webVpn/:key │
├───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /zjut/:type, /zjut/design/:type                       │ /zjut/:type?, /zjut/design/:type                      │
├───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────┤
│ /zzu/soft/news/:type                                  │ /zzu/soft/news/:type?                                 │
└───────────────────────────────────────────────────────┴───────────────────────────────────────────────────────┘

No Need (2)
- zongheng/chapter
- zzu/news

Dropped (1)
- zreading

* feat: migrate batch-z

* refactor: cleanup

* refactor: cleanup

* feat: remove router.js

* fix: increase timeout

* feat: fix author

jacky2001114 -> yangkghjh #288
Lava-Swimmer -> LandonLi #4393
xfangbao -> salviox #4756

* fix: exported utils name

* fix: codeql

* fix: remove fake date

* feat!: drop swust

* fix: update oxlint ignore

* fix: address review

* fix: remove dead comments

* fix: address review

* fix: address review
This commit is contained in:
Tony
2026-08-22 12:18:37 +08:00
committed by GitHub
parent 7f958db83e
commit 8ce50e4a39
1463 changed files with 31503 additions and 32724 deletions
+7
View File
@@ -110,6 +110,7 @@ type ConfigEnvKeys =
| 'EH_STAR'
| 'EH_IMG_PROXY'
| `EMAIL_CONFIG_${string}`
| 'ETHERSCAN_API_KEY'
| 'F95ZONE_COOKIE'
| 'FANBOX_SESSION_ID'
| 'FANFOU_CONSUMER_KEY'
@@ -420,6 +421,9 @@ export type Config = {
email: {
config: Record<string, string | undefined>;
};
etherscan: {
apiKey?: string;
};
f95zone: {
cookie?: string;
};
@@ -926,6 +930,9 @@ const calculateValue = () => {
email: {
config: email_config,
},
etherscan: {
apiKey: envs.ETHERSCAN_API_KEY,
},
f95zone: {
cookie: envs.F95ZONE_COOKIE,
},
-1575
View File
File diff suppressed because it is too large Load Diff
-59
View File
@@ -1,59 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
module.exports = async (ctx) => {
const category = ctx.params.category;
const pageUrl = `http://info.10000link.com/newslists.aspx?chid=${category}`;
const host = 'http://info.10000link.com';
const response = await got({
method: 'get',
url: pageUrl,
});
const $ = cheerio.load(response.data);
const list = $('.l_newshot li').get();
const ProcessFeed = async (link) => {
const response = await got({
method: 'get',
url: link,
});
const $ = cheerio.load(response.data);
return $('#news_body').html();
};
const items = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const $a = $('h1 a');
const link = url.resolve(host, $a.attr('href'));
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const description = await ProcessFeed(link);
const single = {
title: $a.text(),
description,
link,
pubDate: new Date($('.ymd_w').text()).toUTCString(),
author: $('.day-lx span:first-child').text(),
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: `万联网 ${$('.t-h2').text().replace(' |资讯频道', '')}`,
link: pageUrl,
description: $('meta[name="Description"]').attr('content'),
item: items,
};
};
-23
View File
@@ -1,23 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const currentUrl = `http://www.12379.cn/data/index_map_all.html`;
const response = await got({
method: 'get',
url: currentUrl,
});
const data = JSON.parse(response.data.slice('var alertData ='.length, 'var alertData ='.length + response.data.length + 1));
const list = data.map((item) => ({
title: item.headline,
link: `http://www.12379.cn/html/new2018/alarmcontent.shtml?file=${item.identifier}.html`,
description: item.description,
pubDate: new Date(item.sendTime).toUTCString(),
}));
ctx.state.data = {
title: `当前生效预警 - 国家突发事件预警信息发布网`,
link: currentUrl,
item: list,
};
};
-10
View File
@@ -1,10 +0,0 @@
const utils = require('./utils');
const rootUrl = 'https://www.199it.com/archives/category';
module.exports = async (ctx) => {
const keyword = ctx.params.caty.split('|').join('/');
const currentUrl = `${rootUrl}/${keyword}`;
ctx.state.data = await utils(ctx, keyword, currentUrl);
};
-31
View File
@@ -1,31 +0,0 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const url = `http://www.199it.com/newly`;
const res = await got.get(url);
const $ = cheerio.load(res.data);
const list = $('.entry-content').get();
const out = list.slice(0, 9).map((item) => {
const $ = cheerio.load(item);
const time = $('.post-time time').attr('datetime');
const title = $('.entry-title a').attr('title');
const partial = $('.entry-title a').attr('href');
const address = partial;
const single = {
title,
pubDate: new Date(time).toUTCString(),
link: address,
guid: address,
};
ctx.cache.set(address, JSON.stringify(single));
return single;
});
ctx.state.data = {
title: '199it',
link: url,
item: out,
};
};
-10
View File
@@ -1,10 +0,0 @@
const utils = require('./utils');
const rootUrl = 'https://www.199it.com/archives/tag';
module.exports = async (ctx) => {
const keyword = ctx.params.tag.split('|').join('/');
const currentUrl = `${rootUrl}/${keyword}`;
ctx.state.data = await utils(ctx, keyword, currentUrl);
};
-44
View File
@@ -1,44 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx, keyword, currentUrl) => {
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('article.entry-list')
.slice(0, 10)
.map((_, item) => {
item = $(item);
const a = item.find('a[title]');
return {
title: a.text(),
link: a.attr('href'),
pubDate: new Date(item.find('time').attr('datetime')).toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: `https:${item.link}`,
});
const content = cheerio.load(detailResponse.data);
item.description = content('div.entry-content').html();
return item;
})
)
);
return {
title: '199IT - ' + keyword,
link: currentUrl,
item: items,
};
};
@@ -1,60 +0,0 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const url = `https://www.scientificamerican.com/podcast/60-second-science/`;
const cover = `https://static.scientificamerican.com/sciam/cache/file/484FA146-8892-477F-B38D18775563CC9E_small.jpg`;
const res = await got.get(url);
const $ = cheerio.load(res.data);
const list = $('.underlined_text.t_small').get();
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const fullTitle = $(item).attr('aria-label');
const title = fullTitle.substring(0, fullTitle.length - 11);
const address = $(item).attr('href');
const cache = await ctx.cache.get(address);
if (cache) {
return JSON.parse(cache);
}
const res = await got.get(address);
const capture = cheerio.load(res.data);
const tStr = '.article-header__divider > ul > li > span:nth-child(2)';
const time = capture(tStr).text();
const aStr = '.article-header__divider > ul > li > span:nth-child(1) > span';
const author = capture(aStr).text();
capture('.transcript__close').remove();
const intro = capture('.article-media__object').html() + '<br><br>';
const contents =
intro +
capture('.transcript__inner')
.html()
.replaceAll(/<p>\[?<em>\(?The above text.*this podcast.*?em.*?p>/g, '');
const track = capture('.podcasts__media > div > a').attr('href');
const single = {
title,
description: contents,
enclosure_url: track,
enclosure_type: 'audio/mpeg',
link: address,
guid: address,
author,
pubDate: new Date(time).toUTCString(),
};
ctx.cache.set(address, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
itunes_author: 'Scientific American',
itunes_category: 'Science',
image: cover,
title: '60-Second Science',
link: url,
item: out,
};
};
-71
View File
@@ -1,71 +0,0 @@
const got = require('@/utils/got');
const postTypes = {
1: '文字',
2: '表情',
3: '纯图片',
4: '评论',
5: '转发',
6: '纯视频',
7: '图片文字',
8: '视频文字',
};
module.exports = async (ctx) => {
const username = ctx.params.username;
const url = `https://api.7gogo.jp/web/v2/talks/${username}/posts?talkId=${username}&direction=PREV`;
const response = await got.get(url);
const list = response.data.data;
const user_display_name = list[0].user.name;
const user_description = list[0].user.description;
const user_image = list[0].user.coverImageUrl;
const GetContent = (post) =>
post.body &&
post.body
.map((item) => {
switch (item.bodyType) {
case 1:
return `<p>${item.text.replaceAll(/\n/gm, '<br/>')}</p>`;
case 2:
return `<img src="${item.image}">`;
case 3:
return `<img src="${item.image}">`;
case 4:
return `<blockquote>${item.comment.user.name}:<br/>${item.comment.comment.body.replaceAll(/\n/gm, '<br/>')}</blockquote>`;
case 7:
return `<blockquote>${item.talk.name}:<br/>${GetContent(item.post)}</blockquote>`;
case 8:
return `<video src="${item.movieUrlHq}" poster="${item.thumbnailUrl}" controls loop>Video</video>`;
default:
return '';
}
})
.join('');
const GetTitle = (post) => {
const texts = post.body.filter((s) => s.bodyType === 1);
const type_name = postTypes[post.postType];
return texts.length === 0 ? type_name : texts[0].text.split('\n')[0];
};
const ProcessFeed = (data) =>
data.slice(0, 20).map((item) => ({
title: GetTitle(item.post),
author: user_display_name,
description: GetContent(item.post),
pubDate: new Date(item.post.time * 1000).toUTCString(),
link: `https://7gogo.jp/${username}/${item.post.postId}`,
}));
ctx.state.data = {
title: `${user_display_name} - 755`,
image: user_image,
description: user_description,
link: `https://7gogo.jp/${username}`,
item: ProcessFeed(list),
};
};
@@ -1,46 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const host = 'https://99percentinvisible.org/';
module.exports = async (ctx) => {
const link = url.resolve(host, 'archives/');
const response = await got.get(link);
const $ = cheerio.load(response.data);
const list = $('.content .post-blocks a.post-image')
.map((i, e) => $(e).attr('href'))
.get();
const out = await Promise.all(
list
.filter((e) => e.startsWith(url.resolve(host, 'episode/')))
.map(async (itemUrl) => {
const cache = await ctx.cache.get(itemUrl);
if (cache) {
return JSON.parse(cache);
}
const response = await got.get(itemUrl);
const $ = cheerio.load(response.data);
const single = {
title: $('article header h1').text().trim(),
link: itemUrl,
author: '99% Invisible',
description: $('article .page-content').html(),
pubDate: new Date($('article header .entry-meta time').text()).toUTCString(),
};
ctx.cache.set(itemUrl, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: '99% Invisible Transcript',
link,
item: out,
};
};
@@ -1,68 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const rootUrl = 'https://amturing.acm.org';
const currentUrl = `${rootUrl}/byyear.cfm`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const items = await Promise.all(
$('.award-winners-list li')
.slice(0, 5)
.map(async (_, item) => {
item = $(item);
const year = item.find('span').text().replaceAll(/\(|\)/g, '');
const pubDate = new Date(`${year}-12-31`).toUTCString();
const winnersUrls = [];
let winnerResponses = [];
item.find('a').each(function () {
winnersUrls.push(`${rootUrl}${$(this).attr('href')}`);
});
winnerResponses = winnersUrls.map((url) =>
got({
method: 'get',
url,
})
);
const description = await Promise.all(winnerResponses).then((winners) => {
let winnerDescription = '';
for (const winner of winners) {
const content = cheerio.load(winner.data);
const image = content('.featured-photo').html();
const column = content('#tertiary-navigation').parent();
content('#tertiary-navigation').remove();
winnerDescription += image + column.html();
}
return winnerDescription;
});
return {
pubDate,
description,
title: year,
link: currentUrl,
};
})
.get()
);
ctx.state.data = {
title: 'A.M. Turing Award Winners',
link: currentUrl,
item: items,
};
};
-48
View File
@@ -1,48 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const baseUrl = 'https://www.acwifi.net/';
const response = await got({
method: 'get',
url: baseUrl,
});
const $ = cheerio.load(response.data);
const list = $('div.widget.widget_recent_entries li').get();
const ProcessFeed = (data) => {
const $ = cheerio.load(data);
return {
desc: $('article.article-content').html(),
};
};
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const $a = $('a');
const link = $a.attr('href');
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const response = await got({
method: 'get',
url: link,
});
const feed = ProcessFeed(response.data);
const description = feed.desc;
const single = {
title: $a.text(),
description,
link,
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = { title: '路由器交流', link: baseUrl, item: out };
};
-67
View File
@@ -1,67 +0,0 @@
const url = require('url');
const got = require('@/utils/got');
const cheerio = require('cheerio');
const config = {
index: {
link: 'https://www.adquan.com/',
title: '首页',
},
info: {
link: 'https://www.adquan.com/info',
title: '行业观察',
},
creative: {
link: 'https://creative.adquan.com/',
title: '案例库',
},
};
module.exports = async (ctx) => {
const cfg = ctx.params.type ? config[ctx.params.type] : config.index;
const response = await got({
method: 'get',
url: cfg.link,
});
const $ = cheerio.load(response.data);
let where;
switch (cfg.title) {
case '首页':
where = $('div.w_l_inner h2.wrok_l_title a');
break;
case '行业观察':
where = $('div.article_rel div.rel_write a p').parent();
break;
case '案例库':
where = $('#showcontent li h2 a');
break;
}
const list = where
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: url.resolve(cfg.link, item.attr('href')),
};
})
.get();
ctx.state.data = {
title: `广告网 - ${cfg.title}`,
link: cfg.link,
item: await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({ method: 'get', url: item.link });
const content = cheerio.load(detailResponse.data);
item.pubDate = new Date(content('p.text_time2 span').eq(0).text()).toUTCString();
item.description = content('div.con_Text').html();
return item;
})
)
),
};
};
-48
View File
@@ -1,48 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const rootUrl = 'https://aflcio.org';
const currentUrl = `${rootUrl}/blog`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('article')
.map((_, item) => {
item = $(item);
const title = item.find('h1 a');
return {
title: title.text(),
link: `${rootUrl}${title.attr('href')}`,
pubDate: new Date(item.find('time').attr('datetime')).toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('.section-article-body').html();
item.author = detailResponse.data.match(/entityName":"(.*)","entityType"/)[1];
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
-134
View File
@@ -1,134 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const id = ctx.params.id;
const sort = ctx.params.sort || 'new';
let url = 'https://api.yanxishe.com/api?page=1&size=30&parent_tag=';
url += id === 'all' ? '&tag=' : `&tag=${id}`;
if (sort === 'hot') {
url += '&is_hot=1&is_recommend=0';
} else if (sort === 'recommend') {
url += '&is_hot=0&is_recommend=1';
} else {
url += '&is_hot=0&is_recommend=0';
}
const response = await got({
method: 'GET',
url,
headers: {
Referer: `https://www.yanxishe.com/`,
},
});
const ProcessFeed = (type, data, id) => {
let description = '';
let author = '';
let link;
switch (type) {
case 'blog': // 博客
description = data.content;
author = data.user.nickname;
link = data.relation_special ? `https://www.yanxishe.com/columnDetail/${id}` : `https://www.yanxishe.com/blogDetail/${id}`;
break;
case 'question': // 问答
description = data.content;
author = data.user.nickname;
link = `https://www.yanxishe.com/questionDetail/${id}`;
break;
case 'article': // 翻译
description = `<table><tr><td width="50%"> ${data.title} </td><td> ${data.zh_title} </td></tr>`;
for (const element of data.paragraphs) {
description += `<tr><td> ${element.content} </td><td> ${element.zh_content.content} </td></tr>`;
}
description += `</table>
<style>
table,
table tr th,
table tr td {
border: 1px solid #000000;
padding: 10px;
}
table {
border-collapse: collapse;
}
</style>`;
author = data.user.nickname;
link = `https://www.yanxishe.com/TextTranslation/${id}`;
break;
case 'paper': // 论文
description = `<h3>标题</h3><p> ${data.paper.title} </p>`;
description += `<h3>作者</h3><p> ${data.paper.author.toString()} </p>`;
description += `<h3>下载地址</h3><p><a href=" ${data.paper.url} ">${data.paper.url}</a></p>`;
description += `<h3>发布时间</h3><p> ${data.paper.publish_time} </p>`;
description += `<h3>摘要</h3><p> ${data.paper.description} </p>`;
description += `<h3>推荐理由</h3><p><b> ${data.paper.userInfo.nickname} </b>: ${data.paper.recommend_reason} </p>`;
author = data.paper.userInfo.nickname;
link = `https://paper.yanxishe.com/review/${id}`;
break;
default:
description = '暂不支持此类型,请到 https://github.com/DIYgod/RSSHub/issues 反馈';
break;
}
// 提取内容
return { author, description, link };
};
const items = await Promise.all(
response.data.data.all.map(async (item) => {
let itemUrl = `https://api.yanxishe.com/api/sthread/${item.id}?token=null`;
switch (item.type) {
case 'blog':
itemUrl += '&type=null';
break;
case 'article':
itemUrl = `https://api.yanxishe.com/api/stranslate/article/${item.id}?token=null&type=null`;
break;
case 'paper':
itemUrl = `https://api.yanxishe.com/api/spaper/detail?token=null&id=${item.id}`;
break;
default:
throw new Error(`Unknown type: ${item.type}`);
}
const cache = await ctx.cache.get(itemUrl);
if (cache) {
return JSON.parse(cache);
}
const response = await got({
method: 'get',
url: itemUrl,
});
const result = ProcessFeed(item.type, response.data.data, item.id);
const single = {
title: item.zh_title,
description: result.description,
pubDate: new Date(Number.parseFloat(item.published_time + '000')).toUTCString(),
link: result.link,
author: result.author,
};
ctx.cache.set(itemUrl, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: `AI研习社`,
link: `https://ai.yanxishe.com/`,
description: '专注AI技术发展与AI工程师成长的求知平台',
item: items,
};
};
-93
View File
@@ -1,93 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const getCacheParsed = async (ctx, key) => {
const value = await ctx.cache.get(key);
if (value) {
return JSON.parse(value);
}
};
const generateResponse = (items) => ({
// 源标题
title: 'AlgoCasts',
// 源链接
link: `https://www.algocasts.com`,
// 源说明
description: `AlgoCasts 旨在用心做好每一个算法讲解视频。每个视频包含两个部分:题目的剖析讲解以及编码,力求在讲解清楚到位的基础上,尽可能地保持视频精简短小,让大家可以在碎片时间里进行学习,并收获这些算法题背后的思想与乐趣。`,
item: items.map((item) => ({
// 文章标题
title: `${item.title} | AlgoCasts 视频更新`,
// 文章正文
description: item.description || '',
// 文章链接
link: item.link,
})),
});
const makeFull = (ctx, infos) => {
const limit = 10; // 仅获取最近十条信息(避免无意义请求)
infos = infos.slice(0, limit);
return Promise.all(
infos.map(async (item) => {
const cacheKey = `episode-${item.episode}`;
// 尝试从缓存查找相关数据
let info = await getCacheParsed(ctx, cacheKey);
// console.log(info);
if (info) {
// 找到 -> 直接返回
return info;
} else {
// 未找到 -> 返回
info = item;
}
// 抓取详细信息
const response = await got({
method: 'get',
url: info.link,
});
const $ = cheerio.load(response.data);
const badges = [];
$('.badge').each((index, badge) => {
const text = $(badge).text();
if (text !== '讨论') {
badges.push(text);
}
});
info.description = `<h1>${info.title}</h1><div>${badges.join(' | ')}</div><div>${$('#my-content p').html()}</div>`;
ctx.cache.set(cacheKey, info);
// console.log(info);
return info;
})
);
};
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: 'http://algocasts.io/episodes',
});
const $ = cheerio.load(response.data);
const infos = [];
$('tr')
.slice(1)
.each((i, e) => {
const id = $(e).find('th').text();
const titleLabel = $(e).find('td a');
const title = `${id}. ${titleLabel.text()}`;
const episode = titleLabel.attr('href').trim().split('/')[2];
const link = `http://algocasts.io/episodes/${episode}`;
infos.push({ id, title, episode, link });
});
ctx.state.data = generateResponse(await makeFull(ctx, infos));
};
-42
View File
@@ -1,42 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
module.exports = async (ctx) => {
const { order = 'best' } = ctx.params;
const link = `https://allpoetry.com/poems/about/spotlight-oldpoem?order=${order}}`;
const host = 'https://allpoetry.com/';
const response = await got({
method: 'get',
url: link,
});
const data = response.data;
const $ = cheerio.load(data);
const items = $('.sub')
.get()
.map((e) => {
let itemUrl = $(e).find('h1.title > a').attr('href');
itemUrl = url.resolve(host, itemUrl);
const single = {
title: $(e).find('h1.title > a').text(),
description: $(e).find('div.poem_body').html(),
link: itemUrl,
author: $(e).find('div.bio >a ').attr('data-name'),
guid: itemUrl,
};
return single;
});
ctx.state.data = {
title: `All Poetry - ${order.toUpperCase() + order.slice(1)}`,
link,
item: items,
};
};
@@ -1,7 +0,0 @@
const utils = require('./utils');
module.exports = async (ctx) => {
const currentUrl = `${ctx.params.model}/fresh?${ctx.params.lang ? 'lang=' + ctx.params.lang : ''}${ctx.params.type ? '&compatible=' + ctx.params.type : ''}`;
ctx.state.data = await utils(ctx, currentUrl);
};
@@ -1,7 +0,0 @@
const utils = require('./utils');
module.exports = async (ctx) => {
const currentUrl = `search/${ctx.params.model}/tags/${ctx.params.keyword ?? ''}${ctx.params.sortBy ? '?sortby=' + ctx.params.sortBy : ''}`;
ctx.state.data = await utils(ctx, currentUrl);
};
@@ -1,9 +0,0 @@
const utils = require('./utils');
module.exports = async (ctx) => {
const currentUrl = `${ctx.params.model}/top?${ctx.params.lang ? 'lang=' + ctx.params.lang : ''}${ctx.params.type ? '&compatible=' + ctx.params.type : ''}${ctx.params.sortBy ? '&sortby=' + ctx.params.sortBy : ''}${
ctx.params.time ? '&topof=' + ctx.params.time : ''
}`;
ctx.state.data = await utils(ctx, currentUrl);
};
@@ -1,7 +0,0 @@
const utils = require('./utils');
module.exports = async (ctx) => {
const currentUrl = `${ctx.params.model}/updated?${ctx.params.lang ? 'lang=' + ctx.params.lang : ''}${ctx.params.type ? '&compatible=' + ctx.params.type : ''}`;
ctx.state.data = await utils(ctx, currentUrl);
};
@@ -1,45 +0,0 @@
const url = require('url');
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx, currentUrl) => {
const rootUrl = `https://amazfitwatchfaces.com/`;
currentUrl = `${rootUrl}${currentUrl}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('#wf-row div.col-md-3')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
link: url.resolve(rootUrl, $(item).find('a.wf-act').attr('href')),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({ method: 'get', url: item.link });
const content = cheerio.load(detailResponse.data);
const date = content('i.fa-calendar').parent().find('span').text();
const ymd = date.split(' ')[0];
item.title = `[${content('code').text()}] ${content('title').text().split(' - ')[0]}`;
item.description = `<img src="${content('#watchface-preview').attr('src')}"><br>${content('div.unicodebidi').text()}`;
item.pubDate = new Date(`${ymd.split('.')[2]}-${ymd.split('.')[1]}-${ymd.split('.')[0]} ${date.split(' ')[1]}`).toUTCString();
return item;
})
)
);
return {
title: `${$('title').text().split('|')[0]} | Amazfit Watch Faces`,
link: currentUrl,
item: items,
};
};
@@ -1,114 +0,0 @@
const got = require('@/utils/got');
const config = require('@/config').value;
const cheerio = require('cheerio');
const url = 'https://download.amd.com/drivers/xml/driver_09_us.xml';
// 06/10/2020 -> Wed Jun 10 2020 00:00:00
function convertDate(dateStr) {
const [month, day, year] = dateStr.split('/').map((x) => Number.parseInt(x, 10));
const date = new Date();
date.setFullYear(year, month - 1, day);
date.setHours(0, 0, 0, 0);
return date;
}
function versionToRss(info, isBeta) {
const date = convertDate(info.releaseDate);
return {
title: `Radeon Software ${info.edition} Edition ${info.version}` + (isBeta ? ' Beta' : ''),
description: `内部版本: ${info.internal}<br>发布时间: ${date.getFullYear()}-${date.getMonth() + 1}-${date.getDay()}`,
pubDate: date.toUTCString(),
link: info.releaseNotes,
};
}
module.exports = async (ctx) => {
let data = await ctx.cache.get(url, false);
if (!data) {
const res = await got.get(url);
data = res.data;
ctx.cache.set(url, data, config.cache.contentExpire);
}
const $ = cheerio.load(data, { xmlMode: true });
const productfamily = $('driverSelector > platform > productfamily')[0];
let product;
for (const node of productfamily.childNodes) {
if (node.tagName !== 'product') {
continue;
}
const ids = node.attribs.ids.split(', ');
if (!ids.includes(ctx.params.id)) {
continue;
}
if (ctx.params.rid && node.attribs.rid !== ctx.params.rid) {
continue;
}
product = node;
break;
}
if (product) {
let latestWHQL = '';
let latestBeta = '';
let whql, beta;
for (const node of product.childNodes) {
if (node.tagName !== 'version') {
continue;
}
if (node.attribs.number > latestWHQL) {
latestWHQL = node.attribs.number;
whql = {
version: node.attribs.number,
internal: node.attribs.internal,
edition: node.attribs.WHQLEdition,
releaseDate: node.attribs.whqlreleasedate,
releaseNotes: node.attribs.rnoteswin,
};
}
if (node.attribs.betaname > latestBeta) {
latestBeta = node.attribs.betaname;
beta = {
version: node.attribs.betaname,
internal: node.attribs.beta,
edition: node.attribs.BETAEdition,
releaseDate: node.attribs.betareleasedate,
releaseNotes: node.attribs.betarnoteswin,
};
}
}
const item = [];
if (whql || beta) {
if (whql) {
try {
item.push(versionToRss(whql, false));
} catch {
// ignore
}
}
if (beta) {
try {
item.push(versionToRss(beta, true));
} catch {
// ignore
}
}
}
ctx.state.data = {
title: `${product.attribs.label} (ids:${product.attribs.ids} rid:${product.attribs.rid})`,
link: 'https://www.amd.com/zh-hans/support',
allowEmpty: true,
item,
};
} else {
ctx.throw(404, 'Product Not Found');
}
};
@@ -1,33 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const rootUrl = 'https://anytxt.net';
const currentUrl = `${rootUrl}/download`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const items = $('.has-medium-font-size')
.map((_, item) => {
item = $(item);
const date = item.text().replace(':', '');
return {
title: date,
link: currentUrl,
pubDate: new Date(date).toUTCString(),
description: `<ol>${item.next().html()}</ol>`,
};
})
.get();
ctx.state.data = {
title: 'Release Notes - AnyTXT',
link: currentUrl,
item: items,
};
};
-93
View File
@@ -1,93 +0,0 @@
// Warning: The author knows nothing about javascript!
// params:
// count? : count of new-books
const got = require('@/utils/got'); // get web content
const cheerio = require('cheerio'); // html parser
const entities = require('entities');
const base_url = 'https://www.aozora.gr.jp/index_pages/';
module.exports = async (ctx) => {
// get the update list
const response = await got({
method: 'get',
url: base_url + 'whatsnew1.html',
});
const data = response.data; // content is html format
const $ = cheerio.load(data);
const list = $('table.list tr');
// get how many new-books. amount in this page is 50
let count = Number.parseInt(ctx.params.count);
if (Number.isNaN(count) || count < 1) {
count = 10; // default count of new-book list
} else if (count > 50) {
count = 50;
}
// parse book urls
const detail_urls = [];
for (let i = 1; i < count + 1; ++i) {
// i = 1: first tr is table title, ignore
const link = base_url + $(list[i]).find('a').attr('href');
detail_urls.push(link);
}
// get book-cards
const responses = await Promise.all(detail_urls.map((url) => got(url)));
const cards = responses.map(({ data }) => data);
// get real data to feed
const book_list = [];
for (let i = 0; i < count; ++i) {
const $ = cheerio.load(cards[i]);
const link = $('meta[property="og:url"]').attr('content');
const link_dir = link.replaceAll(/\/[^/]*$/g, '/');
const title_info = $('table[summary="タイトルデータ"] > tbody > tr');
let author = '';
let title = '';
let title_sub = '';
for (const element of title_info) {
const tmp = entities.decodeXML($(element).html()); // should convert from escaped to unicode
if (tmp.includes('作品名:')) {
title = $(element).find('td:nth-child(2)').text();
}
if (tmp.includes('副題:')) {
title_sub = $(element).find('td:nth-child(2)').text();
}
if (tmp.includes('著者名:')) {
author = $(element).find('td:nth-child(2)').text();
}
}
if (title_sub !== '') {
title += ' —— ' + title_sub;
}
const pub_date_raw = $('table[summary="底本データ"] > tbody > tr:nth-child(3) > td:nth-child(2)').text();
const pub_date_num = pub_date_raw.replaceAll(/.*|日/g, '').replaceAll(/[年月]/g, '-');
const pub_date = new Date(pub_date_num).toUTCString();
const full_text_relative_link = $('table.download > tbody > tr:nth-child(3) > td:nth-child(3) > a').attr('href');
const full_text_link = link_dir + full_text_relative_link;
const full_text_link_html = '<a href="' + full_text_link + '">いますぐXHTML版で読む</a><br>';
const summury = $('table[summary="作品データ"]')
.html()
.replaceAll('href="', 'href="' + link_dir);
const item = {
title,
author,
pubDate: pub_date,
link,
description: full_text_link_html + summury,
};
book_list.push(item);
}
// feed the data
ctx.state.data = {
title: '青空文庫新着リスト',
link: base_url + 'whatsnew1.html',
item: book_list,
};
};
-66
View File
@@ -1,66 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
ctx.params.caty = ctx.params.caty || 'highlights';
ctx.params.time = ctx.params.time || '24h';
const times = ['24h', 'week', 'alltime'];
const rootUrl = `https://www.app-sales.net/${ctx.params.caty}/#!`;
const response = await got({
method: 'get',
url: rootUrl,
});
const $ = cheerio.load(response.data);
if (ctx.params.caty === 'mostwanted') {
for (const i of times) {
if (i !== ctx.params.time) {
$(`#charts-${i}`).remove();
}
}
}
$('i,img.play-icon-small').remove();
const items = $('div.sale-list-item')
.map((_, item) => {
item = $(item);
const icon = item.find('div.app-icon').html();
const name = item.find('p.app-name').text();
const developer = item.find('p.app-dev').text();
const oldPrice = item.find('div.price-old').text();
const newPrice = item.find('div.price-new').text();
const discount = item.find('div.price-disco').text();
const rating = item.find('p.rating').text();
const downloads = item.find('p.downloads').text();
const bookmarks = item.find('p.bookmarks').text();
const description =
`${icon}<p>${name}</p><table cellspacing="5">` +
`<tr><td> Developer </td><td> ${developer} </td></tr>` +
`<tr><td> Bookmarks </td><td> ${bookmarks} </td></tr>` +
(rating ? `<tr><td> Rating </td><td> ${rating} </td></tr>` : '') +
(downloads ? `<tr><td> Downloads </td><td> ${downloads} </td></tr>` : '') +
(discount ? `<tr><td> Discount </td><td> ${discount} </td></tr>` : '') +
(oldPrice ? `<tr><td> Old Price </td><td> ${oldPrice} </td></tr>` : '') +
(newPrice ? `<tr><td> New Price </td><td> ${newPrice} </td></tr>` : '') +
'</table>';
return {
title: `${name} ${newPrice ? '[' + newPrice + ']' : ''}`,
link: item.find('a.waves-effect').attr('href'),
description,
author: developer,
};
})
.get();
ctx.state.data = {
title: $('title').text(),
link: rootUrl,
item: items,
};
};
-46
View File
@@ -1,46 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const city = ctx.params.city;
const pollution = ctx.params.pollution || [];
const pollutionType = {
so2: 'so2',
no2: 'no2',
co: 'co',
o3: 'O3',
pm25: 'PM2.5',
pm10: 'PM10',
};
const area = isNaN(city) ? city : `@${city}`;
const response = await got({
method: 'get',
url: `http://aqicn.org/aqicn/json/android/${area}/json`,
});
const data = response.data;
const pollutionDetailed =
pollution.length === 0
? ''
: pollution.split(',').reduce((result, item) => {
result += `${pollutionType[item].toUpperCase()}:<b>${
typeof data.historic[pollutionType[item]] === 'object' ? data.historic[pollutionType[item]][Object.keys(data.historic[pollutionType[item]])[0]] : data.historic[pollutionType[item]][0]
}</b><br>`;
return result;
}, '');
ctx.state.data = {
title: `${data.namena}AQI`,
link: `https://aqicn.org/city/${data.ids.path}`,
description: `${data.namena}AQI-aqicn.org`,
item: [
{
title: `${data.namena}实时空气质量(AQI)${data.utimecn}`,
description: `${data.infocn}<br>风力:<b>${data.cwind[0]}</b>级<br>AQI:<b>${data.aqi}</b><br>${pollutionDetailed}<img src="${data.wgt}">`,
pubDate: new Date(data.time * 1000).toUTCString(),
guid: data.time,
link: `https://aqicn.org/city/${data.ids.path}`,
},
],
};
};
-47
View File
@@ -1,47 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const parser = require('@/utils/rss-parser');
module.exports = async (ctx) => {
const feed = await parser.parseURL('https://feeds.feedburner.com/ArchdailyCN');
const ProcessFeed = async (link) => {
const response = await got({
method: 'get',
url: link,
});
const $ = cheerio.load(response.data);
// 提取内容
return $('#single-content').html();
};
const items = await Promise.all(
feed.items.map(async (item) => {
const cache = await ctx.cache.get(item.link);
if (cache) {
return JSON.parse(cache);
}
const description = await ProcessFeed(item.link);
const single = {
title: item.title,
description,
pubDate: item.pubDate,
link: item.link,
author: item.author,
};
ctx.cache.set(item.link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
};
};
-28
View File
@@ -1,28 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const currentUrl = `http://export.arxiv.org/api/query?${ctx.params.query}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('entry')
.map((_, item) => {
item = $(item);
return {
title: item.find('title').text(),
link: item.find('link[type="text/html"]').attr('href'),
pubDate: new Date(item.find('published').text()).toUTCString(),
description: item.find('summary').text(),
};
})
.get();
ctx.state.data = {
title: 'arXiv (' + ctx.params.query + ')',
link: currentUrl,
item: list,
};
};
-90
View File
@@ -1,90 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const categories = {
obituaries: '/obituaries',
yoron: '/politics/yoron',
baseball: '/sports/baseball',
soccer: '/sports/soccer',
sumo: '/sports/sumo',
winter_figureskate: '/sports/winter/figureskate',
golf: '/sports/golf',
general: '/sports/general',
olympics: '/olympics',
paralympics: '/paralympics',
eco: '/eco',
igo: '/igo',
shougi: '/shougi',
eldercare: '/national/eldercare',
hataraku: '/special/hataraku',
food: '/culture/food',
gassho: '/edu/gassho',
suisogaku: '/edu/suisogaku',
hagukumu: '/edu/hagukumu',
msta: '/msta',
};
module.exports = async (ctx) => {
const genre = ctx.params.genre || '';
const category = ctx.params.category || '';
const rootUrl = 'https://www.asahi.com';
let currentUrl;
if (genre) {
if (category) {
currentUrl = category in categories ? `${rootUrl}${categories[category]}` : `${rootUrl}/${genre}/list/${category}.html`;
} else {
currentUrl = `${rootUrl}/${genre}`;
}
} else {
currentUrl = `${rootUrl}${'/news/history.html'}`;
}
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
$('.Time').remove();
const list = $('#MainInner .Section .List li a')
.slice(0, 6)
.map((_, item) => {
item = $(item);
const link = item.attr('href');
return {
link: link.includes('//') ? `https:${link}` : `${rootUrl}${link}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
content('._30SFw, .-Oj2D, .notPrint').remove();
item.description = content('._3YqJ1').html();
item.title = content('meta[name="TITLE"]').attr('content');
item.pubDate = Date.parse(content('meta[name="pubdate"]').attr('content'));
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
description: '朝日新聞社のニュースサイト、朝日新聞デジタルの社会ニュースについてのページです',
};
};
@@ -1,39 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const rootUrl = 'https://www.asml.com';
const currentUrl = `${rootUrl}/en/news/press-releases`;
const response = await got({
method: 'get',
url: currentUrl,
});
const list = response.data.match(/<a class="results-list__listitem" href="(.*)">/g).map((item) => ({
link: `${rootUrl}${item.split('href="')[1].replace('">', '')}`,
}));
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.title = content('title').text().replace('| ASML', '');
item.description = content('.longcopy').html();
item.pubDate = new Date(detailResponse.data.match(/"datePublished": "(.*)",/)[1]).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: 'Press releases | ASML',
link: currentUrl,
item: items,
};
};
-69
View File
@@ -1,69 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const link = `https://www.autotrader.co.uk/results-car-search?${ctx.params.query}`;
const response = await got.get(link);
const $ = cheerio.load(response.data.html);
const idList = $('li.search-page__result').slice(0, 10).get();
const items = await Promise.all(
idList.map(async (item) => {
const link = `https://www.autotrader.co.uk/classified/advert/${item.attribs.id}`;
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const response = await got.get(link);
const $ = cheerio.load(response.data);
let keyFacts = '<table><tr>';
$('.keyFacts__list .keyFacts__label').each((i, e) => {
keyFacts += `<td>${$(e).text()}</td>`;
if ((i + 1) % 4 === 0) {
keyFacts += '</tr><tr>';
}
});
keyFacts += '</tr><tr>';
$('.fpaSpecifications__economy .fpaSpecifications__listItem').each((i, e) => {
keyFacts += `<td>${$(e).find('.fpaSpecifications__term').text()}: ${$(e).find('.fpaSpecifications__description').text()}</td>`;
if ((i + 1) % 4 === 0) {
keyFacts += '</tr><tr>';
}
});
keyFacts += '</tr></table><br/>';
let images = '';
$('.fpa-image-overlay img').each((i, e) => {
images += `<img src='${$(e).attr('data-src')}'/><br/>`;
});
const description = keyFacts + images + $('meta[name="og:description"]').attr('content');
const title = `${$('.fpaGallery__priceLabel').text()}${$('meta[name="og:title"]').attr('content')}`;
const single = {
title,
description,
pubDate: new Date().toISOString(),
link,
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: 'Auto Trader',
link,
item: items,
};
};
-36
View File
@@ -1,36 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const name = ctx.params.name;
const url = ctx.params.type ? `https://www.av01.tv/actor/${name}&o=${ctx.params.type}` : `https://www.av01.tv/actor/${name}`;
const response = await got({
method: 'get',
url,
});
const data = response.data;
const $ = cheerio.load(data);
const list = $('div.col-sm-6.col-md-4.col-lg-4');
ctx.state.data = {
title: `${name}的影片`,
description: `${name}的影片`,
link: `https://www.av01.tv/actor/${name}`,
item:
list &&
list
.map((index, item) => {
item = $(item);
const itemPicUrl = item.find('img').attr('data-src');
return {
title: item.find('span.video-title.title-truncate.m-t-5').text(),
description: `翻译${item.find('div.video-added.title-truncate').text()}<br><img src="${itemPicUrl}">`,
link: `https://www.av01.tv/${item.find('a').attr('href')}`,
};
})
.get(),
};
};
-36
View File
@@ -1,36 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const name = ctx.params.name;
const url = ctx.params.type ? `https://www.av01.tv/tag/${name}&o=${ctx.params.type}` : `https://www.av01.tv/tag/${name}`;
const response = await got({
method: 'get',
url,
});
const data = response.data;
const $ = cheerio.load(data);
const list = $('div.col-sm-6.col-md-4.col-lg-4');
ctx.state.data = {
title: `${name}类型的影片`,
description: `${name}类型的影片`,
link: `https://www.av01.tv/tag/${name}`,
item:
list &&
list
.map((index, item) => {
item = $(item);
const itemPicUrl = item.find('img').attr('data-src');
return {
title: item.find('span.video-title.title-truncate.m-t-5').text(),
description: `翻译${item.find('div.video-added.title-truncate').text()}<br><img src="${itemPicUrl}">`,
link: `https://www.av01.tv/${item.find('a').attr('href')}`,
};
})
.get(),
};
};
@@ -1,75 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const { type, tag } = ctx.params || '';
const response = await got(`https://axisstudiosgroup.com/${type}/${tag}`);
const data = response.data;
const $ = cheerio.load(data); // 使用 cheerio 加载返回的 HTML
const list = $('a.overlay-link')
.toArray()
.map((item) => {
item = $(item);
return {
title: item.find('h2').text().trim(),
link: new URL(item.attr('href'), 'https://axisstudiosgroup.com').href,
};
});
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const response2 = await got(item.link);
const $2 = cheerio.load(response2.data);
$2('.slider-nav').remove(); // 图片导航
$2('.social-wrapper').remove(); // 社交按钮
$2('source').remove(); // 莫名其妙的图片
$2('video').remove(); // 视频标签
$2('button').remove(); // 按钮
$2('div.modal.fade').remove(); // 莫名奇妙的图片
$2('i').remove();
$2('*')
.contents()
.filter((_, el) => el.type === 'comment')
.remove();
const mcdrPlaylistControl = $2('.mcdr.playlist-control a');
const youtube = mcdrPlaylistControl.data('video-type') === 'video/youtube' ? mcdrPlaylistControl.data('video-url').replace('https://www.youtube.com/watch?v=', '') : '';
const mp4 = mcdrPlaylistControl.data('video-type') === 'video/mp4' ? mcdrPlaylistControl.data('video-url') : '';
const content = $2('.container-fluid>div:nth-child(2)');
content.find('video').attr('controls', 'controls');
content.find('picture').each((_, el) => {
el = $2(el);
el.replaceWith(el.html());
});
content.find('div').each((_, el) => {
el = $2(el);
if (el.children().length) {
el.replaceWith(el.children());
}
});
let video = '';
if (mp4) {
video = `<video width="100%" controls="controls" width="640" height="360" source src="${mp4}" type="video/mp4"></video><br>`;
}
if (youtube) {
video = `<iframe id="ytplayer" type="text/html" width="640" height="360" src='https://www.youtube-nocookie.com/embed/${youtube}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><br>`;
}
item.description = video + content.html();
return item;
})
)
);
ctx.state.data = {
title: `Axis Studios | ${type} ${tag ?? ''}`,
link: 'http://axisstudiosgroup.com',
description: $('description').text(),
item: items,
};
};
@@ -1,64 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const host = 'https://www.baby-kingdom.com';
module.exports = async (ctx) => {
const id = ctx.params.id;
const order = ctx.params.order;
let link = `https://www.baby-kingdom.com/forum.php?mod=forumdisplay&fid=${id}`;
switch (order) {
case 'dateline':
link += '&filter=author&orderby=dateline';
break;
case 'reply':
link += '&filter=reply&orderby=replies';
break;
case 'view':
link += '&filter=reply&orderby=views';
break;
case 'lastpost':
link += '&filter=lastpost&orderby=lastpost';
break;
case 'heat':
link += '&filter=heat&orderby=heats';
break;
default:
// Do nothing
}
const response = await got.get(link);
const $ = cheerio.load(response.data);
const title = $('h1.xs2 a').text();
const out = $('tbody[id^="normalthread"]')
.slice(0, 10)
.map(function () {
const info = {
title: $(this).find('td:nth-child(2) > a:nth-child(1)').text(),
link: url.resolve(host, $(this).find('td:nth-child(2) > a:nth-child(1)').attr('href')),
author: $(this).find('td.by.by_author cite a').text(),
};
return info;
})
.get();
ctx.state.data = {
title: `${title}-親子王國`,
link,
item: out,
};
};
@@ -1,73 +0,0 @@
const utils = require('./utils');
const type_map = {
0: '達人專欄',
1: '達人專欄',
2: '最新創作',
3: '最新推薦',
4: '熱門創作',
5: '精選閣樓',
};
const category_map = {
0: '不限',
1: '日誌',
2: '小說',
3: '繪圖',
4: 'Cosplay',
5: '同人商品',
};
const subcategory_map = {
0: '不限',
1: 'ACG相關',
2: '生活休閒',
3: '巴哈相關',
4: '歡樂惡搞',
5: '心情日記',
6: '模型/公仔',
7: '視聽娛樂',
8: '興趣嗜好',
9: '其他',
10: '愛情',
11: '奇幻',
12: '科幻',
13: '武俠',
14: '推理驚悚',
15: '歡樂惡搞',
16: 'BL/GL',
17: '輕小說',
18: '其他',
19: '紙上塗鴉',
20: '草稿/線稿',
21: '單色人物',
22: '彩色人物',
23: '完稿作品',
24: '漫畫',
25: '勇者造型',
26: '其他',
27: 'ACG相關',
28: '特攝',
29: '布袋戲',
30: '電影',
31: '其他',
32: '男性向',
33: '女性向',
34: '其他',
};
module.exports = async (ctx) => {
const type = ctx.params.type ?? '1';
const category = ctx.params.category ?? '0';
const subcategory = ctx.params.subcategory ?? '0';
const url = `https://home.gamer.com.tw/index.php?k1=${category}&k2=${subcategory}&vt=${type}&sm=3`;
const { items } = await utils.ProcessFeed(url, ctx);
ctx.state.data = {
title: `巴哈姆特创作大厅${category === '0' ? '' : ' - ' + category_map[category]}${subcategory === '0' ? '' : ' - ' + subcategory_map[subcategory]} - ${type_map[type]}`,
link: url,
item: items,
};
};
-15
View File
@@ -1,15 +0,0 @@
const utils = require('./utils');
module.exports = async (ctx) => {
const author = ctx.params.author;
const url = ctx.params.category ? `https://home.gamer.com.tw/creationCategory.php?owner=${author}&c=${ctx.params.category}` : `https://home.gamer.com.tw/creation.php?owner=${author}`;
const { title, items } = await utils.ProcessFeed(url, ctx);
ctx.state.data = {
title,
link: url,
item: items,
};
};
-65
View File
@@ -1,65 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const base = 'https://home.gamer.com.tw/';
module.exports = {
ProcessFeed: async (url, ctx) => {
const response = await got.get(url);
const $ = cheerio.load(response.data);
const title = $('title').text();
const list = $('.BH-lbox > .HOME-mainbox1').toArray();
const parseContent = (htmlString) => {
const $ = cheerio.load(htmlString);
const content = $('.MSG-list8C');
const images = $('img');
for (const image of images) {
$(image).attr('src', $(image).attr('data-src'));
}
return {
description: content.html(),
};
};
const items = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const title = $('.HOME-mainbox1b > h1 > a');
const link = base + title.attr('href');
const author = $('.HOME-mainbox1b > .ST1 > a').text();
const time = $('.HOME-mainbox1b > .ST1').text().split('│')[1];
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const topic = {
title: title.text().trim(),
link,
author,
pubDate: new Date(time),
};
try {
const detail_response = await got.get(link);
const result = parseContent(detail_response.data);
if (!result.description) {
return '';
}
topic.description = result.description;
} catch {
return '';
}
ctx.cache.set(link, JSON.stringify(topic));
return topic;
})
);
return { title, items };
},
};
-42
View File
@@ -1,42 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { isValidHost } = require('@/utils/valid-host');
module.exports = async (ctx) => {
const lang = ctx.params.lang || 'en';
const id = ctx.params.id || 'bandizip';
if (!isValidHost(lang)) {
throw new Error('Invalid language code');
}
const rootUrl = `https://${lang}.bandisoft.com`;
const currentUrl = `${rootUrl}/${id}/history/`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const items = $('h2')
.map((_, item) => {
item = $(item);
const title = item.text();
item.children('font').remove();
return {
title,
link: currentUrl,
description: item.next().html(),
pubDate: new Date(item.text()).toUTCString(),
};
})
.get();
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
-50
View File
@@ -1,50 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const time = ctx.params.time || 'shishidashiku';
const rootUrl = 'http://www.banyuetan.org';
const currentUrl = `${rootUrl}/byt/${time}/index.html`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.byt_mian_image_list_con ul li a')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
link: item.attr('href'),
title: item.attr('title'),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('#detail_content').html();
item.author = content('.detail_tit_source').text().replace('来源:', '');
item.pubDate = new Date(content('meta[property="og:release_date"]').attr('content')).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
-56
View File
@@ -1,56 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const name = ctx.params.name;
const link = `http://www.banyuetan.org/byt/${name}/index.html`;
const response = await got.get(link);
const $ = cheerio.load(response.data);
const list = $('ul.clearFix li')
.slice(0, 10)
.map(function () {
const info = {
title: $(this).find('h3 a').text(),
link: $(this).find('h3 a').attr('href'),
date: $(this).find('span.tag3').text(),
};
return info;
})
.get();
const out = await Promise.all(
list.map(async (info) => {
const title = info.title;
const date = info.date;
const itemUrl = info.link;
const cache = await ctx.cache.get(itemUrl);
if (cache) {
return JSON.parse(cache);
}
const response = await got.get(itemUrl);
const $ = cheerio.load(response.data);
const description = $('div.detail_content').html() || '文章已被删除';
const single = {
title,
link: itemUrl,
description,
pubDate: new Date(date).toUTCString(),
};
ctx.cache.set(itemUrl, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: `${name}-半月谈`,
link,
item: out,
};
};
@@ -1,41 +0,0 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const url = `https://www.ben-evans.com/benedictevans/`;
const res = await got.get(url);
const $ = cheerio.load(res.data);
const list = $('section > article').get();
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const title = $('.BlogList-item-title').text();
const address = 'https://www.ben-evans.com' + $('.BlogList-item-title').attr('href');
const time = $('.Blog-meta-item--date').text();
const cache = await ctx.cache.get(address);
if (cache) {
return JSON.parse(cache);
}
const res = await got.get(address);
const capture = cheerio.load(res.data);
const contents = capture('.col.sqs-col-12.span-12').html();
const single = {
title,
author: 'Benedict Evans',
description: contents,
link: address,
guid: address,
pubDate: new Date(time).toUTCString(),
};
ctx.cache.set(address, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: `Benedict Evans`,
link: url,
item: out,
};
};
-56
View File
@@ -1,56 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const timezone = require('@/utils/timezone');
module.exports = async (ctx) => {
const category = ctx.params.category || 'pcgame';
const type = ctx.params.type || '';
const rootUrl = 'https://www.bibgame.com';
const currentUrl = `${rootUrl}/${category}/${type}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.info_box a')
.slice(0, 15)
.map((_, item) => {
item = $(item);
const link = item.attr('href');
return {
title: item.text(),
link: link.includes('http') ? link : `${rootUrl}${link}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
content('#comment').parent().remove();
item.description = content('.abstract').html();
item.pubDate = timezone(new Date(content('.time_box').text().trim()), +8);
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
@@ -1,31 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const url = 'https://jingwei.link/';
const response = await got({
method: 'get',
url,
});
const $ = cheerio.load(response.data);
const resultItem = $("article[class='article-item']")
.map((index, elem) => {
elem = $(elem);
const $esction = elem.find('section[class="post-preview"]');
return {
title: $esction.find('h2').text(),
description: $esction.find('h3').text(),
link: $esction.find('a').attr('href'),
author: '敬维',
};
})
.get();
ctx.state.data = {
title: '敬维-以认真的态度做完美的事情',
link: url,
item: resultItem,
};
};
-30
View File
@@ -1,30 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const url = 'https://leemeng.tw/blog.html';
const response = await got({
method: 'get',
url,
});
const $ = cheerio.load(response.data);
const resultItem = $('article')
.map((index, elem) => {
elem = $(elem);
const $link = elem.find('[rel="bookmark"]');
const title = $link.text();
const link = $link.attr('href');
return {
title,
link,
};
})
.get();
ctx.state.data = {
title: 'LeeMeng - 部落格',
link: url,
item: resultItem,
};
};
-24
View File
@@ -1,24 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const id = ctx.params.id || 2020;
const url = `https://wangwusiwj.blogspot.com/${id}`;
const response = await got({ method: 'get', url });
const $ = cheerio.load(response.data);
const list = $('div.post')
.map((i, e) => ({
title: $(e).find('h3 > a').text(),
description: $(e).find('.post-body').html(),
link: $(e).find('h3 > a').attr('href'),
author: '王五四',
}))
.get();
ctx.state.data = {
title: '王五四文集',
link: url,
item: list,
};
};
-50
View File
@@ -1,50 +0,0 @@
const { parseDate } = require('@/utils/parse-date');
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const url = 'https://www.yinwang.org';
const response = await got({ method: 'get', url });
const $ = cheerio.load(response.data);
const list = $("li[class='list-group-item title']")
.map((i, e) => {
const element = $(e);
const title = element.find('a').text();
const link = url + element.find('a').attr('href');
const dateraw = /\d{4}(?:\/\d{2}){2}/.exec(link)[0];
return {
title,
description: '',
link,
author: '王垠',
pubDate: parseDate(dateraw, 'YYYY/MM/DD'),
};
})
.get();
const result = await Promise.all(
list.map(async (item) => {
const link = item.link;
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const itemReponse = await got.get(link);
const itemElement = cheerio.load(itemReponse.data);
item.description = itemElement('.inner').html();
ctx.cache.set(link, JSON.stringify(item));
return item;
})
);
ctx.state.data = {
title: '王垠的博客 - 当然我在扯淡',
link: url,
item: result,
};
};
-71
View File
@@ -1,71 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: `https://www.blowstudio.es/work/`,
});
const data = response.data;
const $ = cheerio.load(data); // 使用 cheerio 加载返回的 HTML
const list = $('.portfolios.normal > ul > li').get().slice(0, 10);
const articledata = await Promise.all(
list.map(async (item) => {
const link = `https://www.blowstudio.es${$(item).find('a').attr('href')}/`;
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const response2 = await got({
method: 'get',
url: link,
});
const articleHtml = response2.data;
const $2 = cheerio.load(articleHtml);
const imglist = $2('div.attachment-grid img').get();
const mainvideo = $2('.single-page-vimeo-div').attr('data-video-id');
const img = imglist.map((item) => ({
img: $2(item).attr('src'),
}));
const single = {
mainvideo,
describe: $2('div.portfolio-content > div:nth-child(2)').html(),
title: $2('div.portfolio-content > div:nth-child(1)').text(),
images: img,
link,
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: 'Blow Studio',
link: 'http://blowstudio.es',
description: $('description').text(),
item: list.map((item, index) => {
let content = '';
const videostyle = `width="640" height="360"`;
const imgstyle = `style="max-width: 650px; height: auto; object-fit: contain; flex: 0 0 auto;"`;
content += `<iframe ${videostyle} src='https://player.vimeo.com/video/${articledata[index].mainvideo}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><br>`;
content += articledata[index].describe;
if (articledata[index].images) {
for (let p = 0; p < articledata[index].images.length; p++) {
content += `<img ${imgstyle} src="${articledata[index].images[p].img}"><br>`;
}
}
return {
title: articledata[index].title,
description: content,
link: articledata[index].link,
};
}),
};
};
@@ -1,79 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: `http://blur.com`,
});
const data = response.data;
const $ = cheerio.load(data); // 使用 cheerio 加载返回的 HTML
const list = $('.page-title a').get();
const articledata = await Promise.all(
list.map(async (item) => {
const link = $(item).attr('href');
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const response2 = await got({
method: 'get',
url: link,
});
const articleHtml = response2.data;
const $2 = cheerio.load(articleHtml);
const imglist = $2('.gridded img').get();
const videolist = $2('.gridded iframe').get();
const img = imglist.map((item) => ({
img: $2(item).attr('src'),
}));
const video = videolist.map((item) => ({
video: $2(item).attr('src'),
}));
const single = {
mainvideo: $2('div.project-title').attr('data-video'),
describe: $2('p').text(),
title: $2('h1.page-title').text(),
client: $2('div.client').text(),
images: img,
video,
link,
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: 'Blur Studio',
link: 'http://blur.com',
description: $('description').text(),
item: list.map((item, index) => {
const Num = /\d+/;
let content = '';
const videostyle = `width="640" height="360"`;
const imgstyle = `style="max-width: 650px; height: auto; object-fit: contain; flex: 0 0 auto;"`;
content += `Client:${articledata[index].client}<br>${articledata[index].describe}`;
content += Num.test(articledata[index].mainvideo)
? `<iframe ${videostyle} src='https://player.vimeo.com/video/${articledata[index].mainvideo}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><br>`
: `<iframe ${videostyle} src='https://youtube.com/embed/${articledata[index].mainvideo}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><br>`;
if (articledata[index].images) {
for (let p = 0; p < articledata[index].images.length; p++) {
content += `<img ${imgstyle} src="${articledata[index].images[p].img}"><br>`;
}
}
if (articledata[index].video) {
for (let v = 0; v < articledata[index].video.length; v++) {
content += `<iframe ${videostyle} src='${articledata[index].video[v].video}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><br>`;
}
}
return {
title: articledata[index].title,
description: content,
link: articledata[index].link,
};
}),
};
};
@@ -1,32 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: 'https://www.bookwalker.com.tw/search?order=sell_desc&series_display=1&d=1',
});
const data = response.data;
const $ = cheerio.load(data);
const list = $('div[class="bwbookitem"]');
let bookid;
ctx.state.data = {
title: 'BOOKWALKERTW热门新书',
link: 'https://www.bookwalker.com.tw/search?order=sell_desc&series_display=1&d=1',
item:
list &&
list
.map((index, item) => {
item = $(item);
bookid = item.find('img').attr('data-src').split('/')[4];
return {
title: item.find('.bookname').text(),
description: `${item.find('.booknamesub').text()}&nbsp;|&nbsp;${item.find('.bprice').text()}<br><img src="https://taiwan-image.bookwalker.com.tw/product/${bookid}/zoom_big_${bookid}.jpg">`,
link: item.find('a').attr('href'),
};
})
.get(),
};
};
-58
View File
@@ -1,58 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { isValidHost } = require('@/utils/valid-host');
const maxPages = 5;
module.exports = async (ctx) => {
const { subdomain } = ctx.params;
if (!isValidHost(subdomain)) {
throw new Error('Invalid subdomain');
}
const shopUrl = `https://${subdomain}.booth.pm`;
let shopName;
const items = [];
for (let page = 1; page <= maxPages; page++) {
const pageUrl = `${shopUrl}/items?page=${page}`;
// eslint-disable-next-line no-await-in-loop
const response = await got({
method: 'get',
url: pageUrl,
});
const data = response.data;
const $ = cheerio.load(data);
shopName = $('div.shop-name > span').text();
const pageItems = $('li.item');
if (pageItems.length === 0) {
break;
}
for (const pageItem of pageItems) {
// extract item name
const itemName = $('h2.item-name > a', pageItem).text();
// extract item url
const itemUrl = shopUrl + $('h2.item-name > a', pageItem).attr('href');
// extract item preview url
const itemPreviewUrl = $('div.swap-image > img', pageItem).attr('src');
items.push({
title: itemName,
description: `<img src='${itemPreviewUrl}'/>`,
link: itemUrl,
});
}
}
ctx.state.data = {
title: shopName,
link: shopUrl,
description: shopName,
allowEmpty: true,
item: items,
};
};
-29
View File
@@ -1,29 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const baseUrl = 'https://bsblog123.com/';
const response = await got({
method: 'get',
url: baseUrl,
});
const $ = cheerio.load(response.data); // 使用 cheerio 加载返回的 HTML
const list = $('div[class="whitebg bloglist"] li');
ctx.state.data = {
title: `冰山博客 ~ 资讯`,
link: `https://bsblog123.com`,
description: '冰山博客 ~ 资讯',
item:
list &&
list
.map((index, item) => {
item = $(item);
return {
title: $('h3.blogtitle', item).text(),
link: $('h3 a', item).attr('href'),
description: $('p', item).text(),
};
})
.get(),
};
};
@@ -1,52 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const category = ctx.params.category || '';
const rootUrl = 'https://www.cbndata.com';
const apiUrl = `${rootUrl}/api/v3/informations?page=1&per_page=10${category ? `&tag_id=${category}` : ''}`;
const response = await got({
method: 'get',
url: apiUrl,
});
const list = response.data.data.map((item) => ({
title: item.id,
link: `${rootUrl}/api/v3/informations/show?id=${item.id}`,
pubDate: Date.parse(item.date),
}));
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
item.link = `${rootUrl}/information/${item.title}`;
item.title = detailResponse.data.data.title;
item.author = detailResponse.data.data.author;
item.description = detailResponse.data.data.content;
return item;
})
)
);
let title = '看点';
for (const tag of response.data.home_tags) {
if (tag.id.toString() === category) {
title = tag.name;
break;
}
}
ctx.state.data = {
title: `${title} - CBNData消费站`,
link: `${rootUrl}/information${category ? `?tag_id=${category}` : ''}`,
item: items,
};
};
-49
View File
@@ -1,49 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const category = ctx.params.category || 'news';
const rootUrl = 'http://www.ccg.org.cn';
const currentUrl = `${rootUrl}/${category}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.huodong-list li a')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
link: item.attr('href'),
title: item.find('h5').text(),
pubDate: new Date(item.find('span').text().replaceAll(/年|月/g, '-').replace(/日/, '')).toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('.pinpai-page').html();
return item;
})
)
);
ctx.state.data = {
title: `${$('title').text()} - 全球化智库`,
link: currentUrl,
item: items,
};
};
-113
View File
@@ -1,113 +0,0 @@
// cell.com is extremely slow, and redirect too many times.
// Thus, the content page are replaced by www.sciencedirect.com.
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const baseURL = 'https://www.cell.com';
const category = ctx.params.category;
let pageURL = `${baseURL}/cell/current.rss`;
let categoryTitle = 'Latest issue';
if (category === 'inpress') {
pageURL = `${baseURL}/cell/inpress.rss`;
categoryTitle = 'Articles in press';
}
const alternativeURL = 'https://www.sciencedirect.com/science/article/pii/';
const pageResponse = await got.get(pageURL);
const pageCapture = cheerio.load(pageResponse.data);
const list = pageCapture('item')
.get()
.filter((item) => {
const $ = cheerio.load(item);
const section = $('prism\\:section').text();
return ['Article', 'Resource'].includes(section);
});
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const section = $('prism\\:section').text();
const address = alternativeURL + $('item').attr('rdf:about').replace('?rss=yes', '').split('/').pop();
const title = $('dc\\:title').text();
const author = $('dc\\:creator').text();
const pubDate = new Date($('dc\\:date').text()).toUTCString();
const cache = await ctx.cache.get(address);
if (cache) {
return JSON.parse(cache);
}
const itemPage = await got.get(address);
const itemCapture = cheerio.load(itemPage.data);
// section + keywords content
const keywords = itemCapture('div.keywords-section > div.keyword')
.map((i, el) => $(el).text())
.get()
.join('; ');
const sectionContents = `
<div id="content-section">
<span style="color: #007dbc; text-transform: uppercase; font-weight: 700;">${section}</span>
<br>
<p style="color: grey; font-weight: 700;">[${keywords}]</p>
</div>
`;
// graphical content
const brief = $('description').text();
const graphical = itemCapture('div.abstract.graphical').find('img').attr('src');
let graphicalContents = '';
if (graphical !== '') {
graphicalContents = `
<div id="content-graphical">
<h2 style="color: #007dbc; font-weight: 700;">Graphical Abstract</h2>
<center>
<figure>
<img src="${graphical}" height="400" alt="">
</figure>
</center>
<span>${brief}</span>
</div>`;
}
// highlight content
const highlight = itemCapture('div.abstract.author-highlights dl').html();
let highlightContents = '';
if (highlight !== '') {
highlightContents = `
<div id="content-highlight">
<h2 style="color: #007dbc; font-weight: 700;">Highlights</h2>
${highlight}
</div>`;
}
// summary content
const summary = itemCapture('div.abstract.author p').html();
let summaryContents = '';
if (summary !== '') {
summaryContents = `
<div id="content-summary">
<h2 style="color: #007dbc; font-weight: 700;">Summary</h2>
${summary}
</div>`;
}
const contents = sectionContents + graphicalContents + highlightContents + summaryContents;
const single = {
title,
author,
description: contents,
link: address,
guid: address,
doi: $('dc\\:identifier').text(),
pubDate,
};
ctx.cache.set(address, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: `Cell | ${categoryTitle}`,
description: `Cell, a research journal`,
link: baseURL,
item: out,
};
};
-106
View File
@@ -1,106 +0,0 @@
// The content is generateed by undocumentated resource of Cell journals
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
module.exports = async (ctx) => {
const baseURL = 'https://www.cell.com';
const journals = [
'cell',
'cancer-cell',
'cell-chemical-biology',
'cell-host-microbe',
'cell-metabolism',
'cell-reports',
'cell-reports-physical-science',
'cell-stem-cell',
'cell-systems',
'chem',
'current-biology',
'developmental-cell',
'immunity',
'joule',
'matter',
'molecular-cell',
'neuron',
'one-earth',
'structure',
];
const out = await Promise.all(
journals.map(async (journal) => {
// get the lastest volumn and issue id
const pageURL = `${baseURL}/${journal}/current`;
const pageMeta = await ctx.cache.tryGet(
pageURL,
async () => {
const cookieData = await got
.extend({
followRedirect: false,
})
.get(`https://secure.jbs.elsevierhealth.com/action/getSharedSiteSession?redirect=${encodeURI(pageURL)}`)
.then((response) => {
const cookieString = response.headers['set-cookie'].join(' ');
const server = cookieString.match(/SERVER=(\S+);/)[1];
const jsession = cookieString.match(/JSESSIONID=(\S+);/)[1];
return `SERVER=${server}; JSESSIONID=${jsession};`;
});
const pageResponse = await got.get(pageURL, {
headers: {
cookie: cookieData,
},
followRedirect: false,
});
const $ = cheerio.load(pageResponse.data);
const coverImg = url.resolve('https://', $('#fullCover > img').attr('src'));
const coverDescription = $('#fullCover > img').attr('alt');
const issueDate = $('h1.toc-header__issue-date').text();
const issueNum = $('.toc-header__details span')
.contents()
.map(function () {
return this.type === 'text' ? $(this).text() : '';
})
.get()
.slice(0, 2)
.join(', ');
const issueName = $('meta[name="name"]').attr('content').replace('Issue: ', '') || `Cell ...`;
const m = $('meta[name="pbContext"]')
.attr('content')
.match(/issue:issue:pii\\:(S\d{4})(\d{4})(\d{2})(X\d{4})(\w)/);
const issueID = `${m[1]}-${m[2]}(${m[3]})${m[4]}-${m[5]}`;
return {
coverImg,
coverDescription,
issueName,
issueDate,
issueNum,
issueID,
};
},
12 * 60 * 60 // set cache timeout to 12h
);
const address = `${baseURL}/${journal}/issue?pii=${pageMeta.issueID}`;
const contents = `<div align="center"><img src="${pageMeta.coverImg}" alt="${pageMeta.coverDescription}"></div>`;
const single = {
title: `${pageMeta.issueName} | ${pageMeta.issueNum} (${pageMeta.issueDate})`,
author: '@y9c',
description: contents,
link: address,
guid: address,
pubDate: new Date(pageMeta.issueDate).toUTCString(),
};
ctx.cache.set(address, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: 'Cell Covers Story',
description: 'Find out the cover story of some Cell journals.',
link: baseURL,
item: out,
};
};
@@ -1,31 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const url = 'http://centbrowser.cn/history.html';
const response = await got.get(url);
const $ = cheerio.load(response.data);
const list = $('.list').toArray();
ctx.state.data = {
title: 'Cent Browser 更新日志',
link: url,
item: list.map((update) => {
update = $(update);
const version = update.find('p').first().attr('id');
const date = update
.find('p > i')
.text()
.match(/\d+-\d+-\d+/)[0];
update.find('p').remove();
return {
title: version,
author: 'Cent Browser',
description: update.html(),
link: `${url}#${date}`,
pubDate: new Date(date),
};
}),
};
};
-49
View File
@@ -1,49 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const pageUrl = 'http://www.cfan.com.cn/news/';
const response = await got.get(pageUrl);
const $ = cheerio.load(response.data);
const list = $('.ui-video-list').get();
const ProcessFeed = async (link) => {
const response = await got({
method: 'get',
url: link,
});
const $ = cheerio.load(response.data);
return $('.maincontent').html() || '文章已被删除';
};
const items = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const link = $('.left-post a').attr('href');
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const description = await ProcessFeed(link);
const single = {
title: $('.left-post-title').text(),
description,
link,
pubDate: new Date($('.left-post-date').text()).toUTCString(),
};
ctx.cache.set(link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: $('title').text(),
link: pageUrl,
item: items,
};
};
-48
View File
@@ -1,48 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
ctx.params.type = ctx.params.type || 'read';
ctx.params.time = ctx.params.time || 'all';
const rootUrl = `https://www.cgtn.com/most-${ctx.params.type}`;
const response = await got({
method: 'get',
url: rootUrl,
});
const $ = cheerio.load(response.data);
const list = $(`#${ctx.params.time}Items div.most-read-item-box div.cg-title`)
.map((_, item) => {
item = $(item);
const a = item.find('a');
return {
title: a.text(),
link: a.attr('href'),
pubDate: new Date(Number.parseInt(a.attr('data-time'))).toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('#cmsMainContent').html();
return item;
})
)
);
ctx.state.data = {
title: `CGTN - Most ${ctx.params.type}`,
link: rootUrl,
item: items,
};
};
-50
View File
@@ -1,50 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const rootUrl = `https://www.cgtn.com/opinions`;
const response = await got({
method: 'get',
url: rootUrl,
});
const $ = cheerio.load(response.data);
$('.cg-pic').parent().remove();
const list = $(`.cg-title h4`)
.slice(0, 15)
.map((_, item) => {
item = $(item);
const a = item.find('a');
return {
title: a.text(),
link: a.attr('href'),
pubDate: new Date(Number.parseInt(a.attr('data-time'))).toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.author = content('.news-author-name').text();
item.description = content('#cmsMainContent').html();
return item;
})
)
);
ctx.state.data = {
title: 'CGTN - Opinions',
link: rootUrl,
item: items,
};
};
-50
View File
@@ -1,50 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const channel = ctx.params.channel || 'all';
const rootUrl = 'https://chaoli.club';
const currentUrl = `${rootUrl}/index.php/conversations/${channel}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.title a')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: `${rootUrl}${item.attr('href')}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.author = content('.info h3').eq(0).text();
item.description = content('.postBody').eq(0).html();
item.pubDate = new Date(Number.parseInt(content('.info .time').attr('data-timestamp')) * 1000).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
-57
View File
@@ -1,57 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const { parseRelativeDate } = require('@/utils/parse-date');
const timezone = require('@/utils/timezone');
const host = 'https://www.checkee.info';
module.exports = async (ctx) => {
const dispdate = ctx.params.dispdate;
const link = `https://www.checkee.info/main.php?dispdate=${dispdate}`;
const response = await got.get(link);
const $ = cheerio.load(response.data);
const list = $(':nth-child(11) tbody tr')
.map(function () {
return {
id: $(this).find(':nth-child(2)').text(),
visaType: $(this).find(':nth-child(3)').text(),
visaEntry: $(this).find(':nth-child(4)').text(),
usConsulate: $(this).find(':nth-child(5)').text(),
major: $(this).find(':nth-child(6)').text(),
status: $(this).find(':nth-child(7)').text(),
checkDate: $(this).find(':nth-child(8)').text(),
completeDate: $(this).find(':nth-child(9)').text() === '0000-00-00' ? 'Not Completed' : $(this).find(':nth-child(9)').text(),
link: $(this).find(':nth-child(11) a').attr('href'),
note: $(this).find(':nth-child(11) a').attr('title') || '',
};
})
.get();
list.shift();
const out = list.map((item) => {
const title =
`${item.visaType} ${item.visaEntry}, ${item.usConsulate}, ` +
`Major ${item.major}, ${item.status}. Check date: ${item.checkDate}` +
(item.completeDate === 'Not Completed' ? '' : `, Complete: ${item.completeDate}`) +
` | ID: ${item.id}`;
const itemUrl = url.resolve(host, item.link);
const date_value = item.checkDate;
const description = item.note === '' ? title : item.note;
return {
title,
link: itemUrl,
description,
pubDate: timezone(parseRelativeDate(date_value), 8),
};
});
ctx.state.data = {
title: `Check Reporter Results: ${dispdate}`,
link,
item: out,
};
};
@@ -1,32 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const homepage = 'https://checkra.in/releases/';
const response = await got({
method: 'get',
url: homepage,
});
const $ = cheerio.load(response.data);
const releases = $('.release');
ctx.state.data = {
title: `Checkra1n All Releases`,
link: homepage,
item: releases
.map((i, item) => {
const $item = $(item);
const address = 'https://checkra.in' + $item.find('a').attr('href');
return {
title: $item.find('h3').first().text(),
description: $item.find('.changelog').html(),
link: address,
guid: address,
};
})
.get(),
};
};
@@ -1,77 +0,0 @@
const cheerio = require('cheerio');
const parser = require('@/utils/rss-parser');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const category = ctx.params.category;
const subcategory = ctx.params.subcategory;
const rssUrl = subcategory
? `https://www.chicagotribune.com/arcio/rss/category/${category}/${subcategory}/?query=display_date:%5Bnow-2d+TO+now%5D+AND+revision.published:true&sort=display_date:desc#nt=instory-link`
: `https://www.chicagotribune.com/arcio/rss/category/${category}/?query=display_date:%5Bnow-2d+TO+now%5D+AND+revision.published:true&sort=display_date:desc#nt=instory-link`;
const feed = await parser.parseURL(rssUrl);
const items = await Promise.all(
feed.items.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const response = await got({
url: item.link,
method: 'get',
});
const html = response.body;
const $ = cheerio.load(html);
const content = $('article > main > section#left');
const categories = $('meta[property="article:section"]')
.attr('content')
.split(',')
.map((e) => e.trim())
.filter((e) => e !== '');
// Disable image lazyloading
content.find('img').each((i, e) => {
e = $(e);
e.attr('src', e.attr('data-src'));
});
// Remove unwanted DOMs
const unwanted_element_selectors = [
'[data-pb-name="Recommender"]',
'.inline-ad',
'[data-type="newsletter-promo-card"]',
'[data-type="interstitial_link"]',
'[data-type="recommender"]',
'.recommender',
'[class*="pb-f-ads-"]',
];
for (const selector of unwanted_element_selectors) {
content.find(selector).each((i, e) => {
$(e).remove();
});
}
return {
title: item.title.trim(),
id: item.guid,
pubDate: new Date(item.pubDate).toUTCString(),
author: item.creator.trim(),
link: item.link,
summary: item.content.trim(),
description: content.html(),
category: categories,
icon: 'https://www.chicagotribune.com/pb/resources/images/ct_icons/144-iTunesArtwork.png',
logo: 'https://upload.wikimedia.org/wikipedia/commons/c/c4/Chicago_Tribune_Logo.svg',
};
})
)
);
ctx.state.data = {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
language: 'en-us',
icon: 'https://www.chicagotribune.com/pb/resources/images/ct_icons/144-iTunesArtwork.png',
logo: 'https://upload.wikimedia.org/wikipedia/commons/c/c4/Chicago_Tribune_Logo.svg',
};
};
@@ -1,33 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const { category } = ctx.params;
const baseUrl = 'https://language.chinadaily.com.cn';
const url = `${baseUrl}/${category}`;
const response = await got(url);
const $ = cheerio.load(response.data);
const categoryName = $('.CT_title').first().text();
const items = $('.gy_box')
.map((_, ele) => {
const $item = cheerio.load(ele);
const link = $item('.gy_box_img').attr('href');
const imgUrl = $item('.gy_box_img img').attr('src');
const realImgUrl = imgUrl.includes('http') ? imgUrl : `https:${imgUrl}`;
const title = $item('.gy_box_txt2').text();
const desc = $item('.gy_box_txt3').text();
return {
title,
link: link.trim().replace(/^\/\//, 'https://'),
description: [`<img src="${realImgUrl}"/>`, desc].join('<br/>'),
};
})
.get();
ctx.state.data = {
title: `中国日报 - ${categoryName}`,
description: `中国日报 - ${categoryName}`,
link: url,
item: items,
};
};
-71
View File
@@ -1,71 +0,0 @@
const Parser = require('rss-parser');
const cheerio = require('cheerio');
const got = require('@/utils/got');
const dayjs = require('dayjs');
module.exports = async (ctx) => {
const category = ctx.params.category || 'all';
const rssUrl = `https://feeds.feedburner.com/chinafile/${category}`;
// The custom UA in @/utils/parser (mimic browser) results in a HTML page
// which cannot be parsed
const parser = new Parser();
const feed = await parser.parseURL(rssUrl);
const items = await Promise.all(
feed.items.map((item) =>
ctx.cache.tryGet(item.link, async () => {
let url = item.link;
const response = await got({ url });
const html = response.body;
const $ = cheerio.load(html);
const content = $('article');
// Cover
const cover = $('.view-featured-photo');
if (cover.length > 0) {
cover.insertBefore(content[0].childNodes[0]);
$(cover).remove();
}
// Summary
const summary = $('meta[name="description"]').attr('content');
const updatedAt = $('meta[name="og:updated_time"]').attr('content');
const categories = $('meta[name="news_keywords"]')
.attr('content')
.split(',')
.map((c) => c.trim());
url = $('link[rel="canonical"]').attr('href');
const pubDate = dayjs(item.pubDate.replace(' - ', ' ').replace('am', ' am').replace('pm', ' pm'));
return {
title: item.title,
id: item.guid,
pubDate,
updated: updatedAt,
author: item.creator,
link: url,
summary,
description: content.html(),
category: categories,
icon: 'https://www.chinafile.com/sites/default/files/chinafile_favicon.png',
logo: 'https://www.chinafile.com/sites/all/themes/cftwo/assets/images/logos/logo-large.png',
};
})
)
);
ctx.state.data = {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
language: 'en-us',
icon: 'https://www.chinafile.com/sites/default/files/chinafile_favicon.png',
logo: 'https://www.chinafile.com/sites/all/themes/cftwo/assets/images/logos/logo-large.png',
};
};
@@ -1,55 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
async function get_software_raw_html(link) {
const response = await got({
method: 'get',
url: link,
});
return response.body;
}
function pick_versions(raw_html) {
const $ = cheerio.load(raw_html);
const items = [];
$('#versionhistory table tbody tr').each((_row_index, row) => {
const software = {};
$('td', row).each((col_index, col) => {
if ($(col).hasClass('version')) {
const href = $('a', col).attr('href');
let version = '';
if (href) {
version = href.split('/').at(-1);
}
software.version = version;
}
if (col_index === 0) {
software.software = $(col).text().trim();
}
if (col_index === 2) {
software.time = $(col).text();
}
});
items.push(software);
});
return items;
}
module.exports = async (ctx) => {
const software_name = ctx.params.name;
const software_link = `https://chocolatey.org/packages/${software_name}`;
const raw = await get_software_raw_html(software_link);
const items = pick_versions(raw);
const rsshub_object = {
title: software_name,
link: software_link,
item: items.map((i) => {
const link = `https://chocolatey.org/packages/${software_name}/${i.version}`;
const ret = { title: i.software, link, description: `update_time: ${i.time}` };
return ret;
}),
};
ctx.state.data = rsshub_object;
};
-35
View File
@@ -1,35 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const subject = ctx.params.subject || 'hot';
const host = 'https://m.chouti.com';
let api = `${host}/api/m/zone/${subject}?afterTime=0`;
let pageUrl = `https://m.chouti.com/zone/${subject}`;
if (subject === 'hot') {
api = `${host}/api/m/link/hot?afterTime=0`;
pageUrl = `https://m.chouti.com/all/hot`;
}
const response = await got({
method: 'get',
url: api,
headers: {
Referer: host,
},
});
const resultItem = response.data.data.map((item) => ({
title: item.title,
author: item.nick,
description: `${item.title}<br><img src="${item.originalImgUrl || item.imgUrl || item.original_img_url || item.img_url}" /><br><a href="${host}/link/${item.id}">评论</a>`,
link: item.url,
pubDate: new Date(item.created_time / 1000).toUTCString(),
}));
ctx.state.data = {
title: '抽屉新热榜',
description: '抽屉新热榜,汇聚每日搞笑段子、热门图片、有趣新闻。它将微博、门户、社区、bbs、社交网站等海量内容聚合在一起,通过用户推荐生成最热榜单。看抽屉新热榜,每日热门、有趣资讯尽收眼底。',
link: pageUrl,
item: resultItem,
};
};
-28
View File
@@ -1,28 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const hour = ctx.params.hour || '24';
const response = await got({
method: 'get',
url: `https://dig.chouti.com/top/${hour}hr?_=${Date.now()}`,
headers: {
Referer: 'https://dig.chouti.com/',
},
});
const resultItem = response.data.data.map((item) => ({
title: item.title,
author: item.nick,
description: `${item.title}<br><img src="${item.originalImgUrl || item.imgUrl || item.original_img_url || item.img_url}" /><br><a href="https://dig.chouti.com/link/${item.id}">评论</a>`,
link: item.url,
pubDate: new Date(item.created_time / 1000).toUTCString(),
}));
ctx.state.data = {
title: `抽屉新热榜-${hour}小时最热榜`,
description: '抽屉新热榜,汇聚每日搞笑段子、热门图片、有趣新闻。它将微博、门户、社区、bbs、社交网站等海量内容聚合在一起,通过用户推荐生成最热榜单。看抽屉新热榜,每日热门、有趣资讯尽收眼底。',
link: 'https://dig.chouti.com/',
item: resultItem,
};
};
-51
View File
@@ -1,51 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const category = ctx.params.category || 'dnzszc';
const rootUrl = 'https://gaokao.chsi.com.cn';
const currentUrl = `${rootUrl}/gkxx/zszcgd/${category}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.list ul li a')
.slice(0, 15)
.map((_, item) => {
item = $(item);
const link = item.attr('href');
return {
title: item.text(),
pubDate: new Date(item.prev().text()).toUTCString(),
link: link.includes('http') ? link : `${rootUrl}${link}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('.list').html();
return item;
})
)
);
ctx.state.data = {
title: `${$('.backb').text()} - 教育部阳光高考信息公开平台`,
link: currentUrl,
item: items,
};
};
-30
View File
@@ -1,30 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const baseUrl = 'https://www.chuhaibiji.com/';
const response = await got({
method: 'get',
url: baseUrl,
});
const $ = cheerio.load(response.data); // 使用 cheerio 加载返回的 HTML
const list = $('ul[class="post-loop post-loop-default tab-wrap clearfix active"] li[class="item"]');
ctx.state.data = {
title: `出海笔记 ~ 资讯`,
link: `https://www.secshi.com/`,
description: '出海笔记 ~ 资讯',
item:
list &&
list
.map((index, item) => {
item = $(item);
return {
title: $('h2 a', item).text(),
link: $('h2 a', item).attr('href'),
description: $('p', item).text(),
pubDate: new Date($('span[class="item-meta-li date"]', item).text()).toUTCString(),
};
})
.get(),
};
};
-52
View File
@@ -1,52 +0,0 @@
const url = require('url');
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseRelativeDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
ctx.params.caty = ctx.params.caty || '';
ctx.params.caty = ctx.params.caty === '' ? '' : `/navi/${ctx.params.caty}`;
const rootUrl = 'http://forum.softhead-citavi.com';
const currentUrl = `${rootUrl}${ctx.params.caty}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('h5.media-heading a')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: url.resolve(currentUrl, item.attr('href')),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('div.panel-body').html();
item.pubDate = parseRelativeDate(content('small.gray').eq(0).text().split(' • ')[1]);
return item;
})
)
);
ctx.state.data = {
title: `${$('a.current').text()} - Citavi 中文网站论坛`,
link: currentUrl,
item: items,
};
};
-48
View File
@@ -1,48 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const lang = ctx.params.lang || '';
const rootUrl = 'https://clb.org.hk';
const currentUrl = `${rootUrl}/${lang === '' ? '/zh-hans/section/%E8%AF%84%E8%AE%BA%E4%B8%8E%E7%89%B9%E5%86%99' : 'commentary-and-analysis'}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.field-content a')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: `${rootUrl}${item.attr('href')}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('.field-name-body').html();
item.pubDate = new Date(content('meta[property="article:published_time"]').attr('content')).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
@@ -1,73 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const column = ctx.params.column; // szse 深圳证券交易所; sse 上海证券交易所; hke 港股; fund 基金
const code = ctx.params.code; // 股票代码
const orgId = ctx.params.orgId;
const category = ctx.params.category || 'all'; // 分类
const searchKey = ctx.params.search || ''; // 标题关键字
let plate = '';
const url = `http://www.cninfo.com.cn/new/disclosure/stock?stockCode=${code}&orgId=${orgId}`;
const apiUrl = `http://www.cninfo.com.cn/new/hisAnnouncement/query`;
switch (column) {
case 'szse':
plate = 'sz';
break;
case 'sse':
plate = 'sh';
break;
case 'third':
plate = 'neeq';
break;
case 'hke':
plate = 'hke';
break;
case 'fund':
plate = 'fund';
break;
}
const response = await got.post(apiUrl, {
headers: {
Referer: url,
},
form: {
stock: `${code},${orgId}`,
tabName: 'fulltext',
pageSize: 30,
pageNum: 1,
column,
category: category === 'all' ? '' : category,
plate,
seDate: '',
searchkey: searchKey,
secid: '',
sortName: '',
sortType: '',
isHLtitle: true,
},
});
const announcementsList = response.data.announcements;
let name = '';
const out = announcementsList.map((item) => {
const title = item.announcementTitle;
const date = item.announcementTime;
const announcementTime = new Date(item.announcementTime).toISOString().slice(0, 10);
const link = 'http://www.cninfo.com.cn/new/disclosure/detail' + `?plate=${plate}` + `&orgId=${orgId}` + `&stockCode=${code}` + `&announcementId=${item.announcementId}` + `&announcementTime=${announcementTime}`;
name = item.secName;
const single = {
title,
link,
pubDate: new Date(date).toUTCString(),
};
return single;
});
ctx.state.data = {
title: `${name}公告-巨潮资讯`,
link: url,
item: out,
};
};
-59
View File
@@ -1,59 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const utils = require('./utils');
const types = {
hot: '热门',
recommend: '推荐',
recent: '最新',
};
module.exports = async (ctx) => {
const type = ctx.params.type ?? 'hot';
const category = ctx.params.category ?? '0';
const category_name = category === '0' ? '全部' : category;
const url = `http://www.cnu.cc/discoveryPage/${type}-${encodeURIComponent(category)}`;
const list_response = await got.get(url);
const $ = cheerio.load(list_response.data);
const list = $('.grid-item.work-thumbnail').toArray();
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const title = $('.title');
const link = $('a.thumbnail').attr('href');
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const rssitem = {
title: title.text().trim(),
link,
};
try {
const response = await got.get(link);
const result = utils.parseContent(response.data);
rssitem.author = result.author;
rssitem.description = result.description;
rssitem.pubDate = result.pubDate;
} catch {
return '';
}
ctx.cache.set(link, JSON.stringify(rssitem));
return rssitem;
})
);
ctx.state.data = {
title: `CNU视觉联盟 - ${types[type]} - ${category_name}`,
link: url,
item: out.filter((item) => item !== ''),
};
};
-52
View File
@@ -1,52 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const utils = require('./utils');
module.exports = async (ctx) => {
const url = 'http://www.cnu.cc/selectedPage';
const list_response = await got.get(url);
const $ = cheerio.load(list_response.data);
const list = $('.selectedUL > .bannerLi').toArray();
const out = await Promise.all(
list.map(async (item) => {
const $ = cheerio.load(item);
const title = $('.workTitle');
const link = title.parent().attr('href');
const cache = await ctx.cache.get(link);
if (cache) {
return JSON.parse(cache);
}
const rssitem = {
title: title.text().trim(),
link,
};
try {
const response = await got.get(link);
const result = utils.parseContent(response.data);
if (!result.description) {
return '';
}
rssitem.author = result.author;
rssitem.description = result.description;
rssitem.pubDate = result.pubDate;
} catch {
return '';
}
ctx.cache.set(link, JSON.stringify(rssitem));
return rssitem;
})
);
ctx.state.data = {
title: `CNU视觉联盟 - 每日精选`,
link: url,
item: out.filter((item) => item !== ''),
};
};
-28
View File
@@ -1,28 +0,0 @@
const cheerio = require('cheerio');
const pic_base_url = 'http://imgoss.cnu.cc/';
const parseContent = (htmlString) => {
const $ = cheerio.load(htmlString);
const author = $('.author-info > a');
const time = $('.author-info > .timeago');
const content = $('#work_body');
const imgs_json = JSON.parse($('#imgs_json').text());
for (const element of imgs_json) {
content.append(`<img src="${pic_base_url}${element.img}" />`);
content.append(`<div class='img_description'>${element.content}</div>`);
content.append(`<p>${element.text}</p>`);
}
return {
author: author.text().trim(),
description: content.html(),
pubDate: new Date(time.text().trim()),
};
};
module.exports = {
parseContent,
};
-26
View File
@@ -1,26 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: 'https://coolbuy.com/api/v1.4/product_preview/?order_by=-id&limit=20&page=0&offset=0',
});
const data = response.data.objects;
ctx.state.data = {
title: '玩物志-最新',
link: 'https://coolbuy.com/',
description: '值得买的未来生活',
item: data.map((item) => ({
title: item.title,
link: item.visit_url,
description: `
<img src="${item.cover_image}"><br>
<img src="${item.display_image}"><br><br>
${item.summary}<br>
价格: ${item.price}
`,
})),
};
};
-30
View File
@@ -1,30 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const id = ctx.params.id;
const url = `https://cowlevel.net/element/${id}/article`;
const response = await got({
method: 'get',
url: `https://cowlevel.net/search/article-search?tag_id=${id}&game_url_slug=&per_page=10&page=1&q=&sort=&sort_type=desc&is_rich_content=1&url_slug=`,
headers: {
Referer: url,
},
});
const element_name = response.data.data.aggs.tags[0].tag.name;
const list = response.data.data.list;
ctx.state.data = {
title: `奶牛关 - ${element_name}`,
link: url,
item: list.map((item) => ({
title: item.title,
author: item.user.name,
description: item.content,
pubDate: new Date(item.update_time * 1000),
link: `https://cowlevel.net/article/${item.id}`,
})),
};
};
-30
View File
@@ -1,30 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const dayjs = require('dayjs');
module.exports = async (ctx) => {
const includePreRelease = ctx.params.pre;
const response = await got(`https://www.python.org/downloads`);
const $ = cheerio.load(response.data);
const items = [];
$('.list-row-container.menu li').each(function () {
const $listElement = $(this);
const title = $listElement.find('span.release-number').text();
if (title) {
if (!includePreRelease && title.includes('rc')) {
return;
}
items.push({
title: $listElement.find('span.release-number').text(),
description: title,
link: $listElement.find('.release-enhancements a').attr('href'),
pubDate: dayjs($listElement.find('.release-date').text()),
});
}
});
ctx.state.data = {
title: 'cpython',
link: 'https://www.python.org/',
item: items,
};
};
@@ -1,45 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const iconv = require('iconv-lite');
module.exports = async (ctx) => {
const currentUrl = 'http://news.creaders.net/headline/';
const response = await got.get(currentUrl, {
responseType: 'buffer',
});
const $ = cheerio.load(iconv.decode(response.data, 'gbk'));
const list = $('ul.newslist li')
.slice(0, 10)
.map((_, item) => {
item = $(item);
const a = item.find('a').eq(1);
return {
title: a.text(),
link: a.attr('href'),
pubDate: new Date(item.find('time').text() + ' GMT+8').toUTCString(),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const res = await got.get(item.link, {
responseType: 'buffer',
});
const content = cheerio.load(iconv.decode(res.data, 'gbk'));
item.description = content('#newsContent').html();
return item;
})
)
);
ctx.state.data = {
title: '万维读者 - 焦点新闻',
link: currentUrl,
item: items,
};
};
-99
View File
@@ -1,99 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const iconv = require('iconv-lite');
const baseUrl = 'https://www.csc.edu.cn';
const typeMap = {
lxtz: {
name: '遴选通知',
url: '/chuguo/list/24',
},
xmzl: {
name: '综合项目专栏',
url: '/chuguo/list/26',
},
wtjd: {
name: '常见问题解答',
url: '/chuguo/list/27',
},
lqgg: {
name: '录取公告',
url: '/chuguo/list/28',
},
xwzx: {
name: '新闻资讯',
url: '/news',
},
xwgg: {
name: '新闻公告',
url: '/news/gonggao',
},
};
module.exports = async (ctx) => {
const type = ctx.params.type || 'jjyw';
const link = baseUrl + typeMap[type].url;
const response = await got({
method: 'get',
url: link,
headers: {
Referer: baseUrl,
},
responseType: 'buffer',
});
const responseHtml = iconv.decode(response.data, 'gbk');
const $ = cheerio.load(responseHtml);
const urlList = $('.list-a li')
.slice(0, 10)
.map((i, e) => $('a', e).attr('href'))
.get();
const titleList = $('.list-a li')
.slice(0, 10)
.map((i, e) => $('a', e).attr('title'))
.get();
const dateList = $('.list-a li')
.slice(0, 10)
.map((i, e) => $('span', e).text())
.get();
const out = await Promise.all(
urlList.map(async (itemUrl, index) => {
itemUrl = url.resolve(baseUrl, itemUrl);
const cache = await ctx.cache.get(itemUrl);
if (cache) {
return JSON.parse(cache);
}
const response = await got({
method: 'get',
url: itemUrl,
responseType: 'buffer',
});
const responseHtmlItem = iconv.decode(response.data, 'gbk');
const $ = cheerio.load(responseHtmlItem);
const single = {
title: titleList[index],
link: itemUrl,
description: $('.contents')
.html()
.replaceAll('src="/', `src="${url.resolve(baseUrl, '.')}`)
.replaceAll('href="/', `href="${url.resolve(baseUrl, '.')}`)
.trim(),
pubDate: dateList[index],
};
ctx.cache.set(itemUrl, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: '国家留学网-' + typeMap[type].name,
link,
item: out,
};
};
-50
View File
@@ -1,50 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id || 'bwzq';
const rootUrl = 'http://news.ctei.cn';
const currentUrl = `${rootUrl}/${id}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('.news_text ul li a')
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: `${currentUrl}${item.attr('href').replace(/\./, '')}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.description = content('.TRS_Editor').html();
item.pubDate = parseDate(item.link.match(/\/t(\d{8})_\d+.htm/)[1], 'YYYYMMDD');
return item;
})
)
);
ctx.state.data = {
title: $('title').text(),
link: currentUrl,
item: items,
};
};
-59
View File
@@ -1,59 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const host = 'https://api.ctfhub.com';
const opt = { like: {} };
for (const i of ['title', 'form', 'class']) {
if (ctx.params[i] !== undefined) {
opt.like[i] = ctx.params[i];
}
}
if (opt.like.class !== undefined) {
const types = await got
.post(`${host}/User_API/Event/getType`, {
headers: {
'content-type': 'application/json',
},
body: '{}',
})
.json();
opt.like.class = types.data[opt.like.class];
}
if (opt.like.form !== undefined) {
opt.like.form = ['线上', '线下'][Number.parseInt(opt.like.form)];
}
if (Object.keys(opt.like).length === 0) {
delete opt.like;
}
opt.offset = 0;
opt.limit = Number.parseInt(ctx.params.limit) || 10;
const response = await got
.post(`${host}/User_API/Event/getAll`, {
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(opt),
})
.json();
if (response.status !== true) {
return;
}
const data = response.data;
ctx.state.data = {
title: 'CTFHub Calendar',
link: 'https://www.ctfhub.com/#/calendar',
description: '提供全网最全最新的 CTF 赛事信息,关注赛事定制自己专属的比赛日历吧。',
item: data.items.map((item) => ({
title: item.title,
description: '',
pubDate: new Date(item.start_time * 1000).toUTCString(),
link: item.official_url,
})),
};
};
-58
View File
@@ -1,58 +0,0 @@
const got = require('@/utils/got');
const md5 = require('@/utils/md5');
const host = 'https://api.ctfhub.com';
async function get_details(event_id) {
const res = await got
.post(`${host}/User_API/Event/getInfo`, {
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({ event_id }),
})
.json();
if (res.status !== true) {
return '';
}
return res.data;
}
module.exports = async (ctx) => {
const opt = {
offset: 0,
limit: Number.parseInt(ctx.params.limit) || 5,
};
const response = await got
.post(`${host}/User_API/Event/getUpcoming`, {
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(opt),
})
.json();
if (response.status !== true) {
return;
}
const items = await Promise.all(
response.data.items.map(async (item) => {
const det = await get_details(item.id);
return {
title: item.title,
description: det.details,
pubDate: new Date(item.start_time * 1000).toUTCString(),
link: det.official_url,
guid: md5(item.title + item.start_time),
};
})
);
ctx.state.data = {
title: 'CTFHub Calendar',
link: 'https://www.ctfhub.com/#/calendar',
description: '提供全网最全最新的 CTF 赛事信息,关注赛事定制自己专属的比赛日历吧。',
item: items,
};
};
-33
View File
@@ -1,33 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const { body } = await got.post('https://panel.getdailyart.com/APIMobile/artworks/query', {
headers: {
Language: ctx.params.language || 'en',
AuthSalt: 'ZG9jY29tcGFuaW9uc2FsdA==',
},
json: {
take: 10,
skip: 0,
local_date: new Date().toISOString().split('T')[0],
},
responseType: 'json',
});
const data = body.entities;
ctx.state.data = {
title: `DailyArt`,
link: 'https://www.getdailyart.com/',
item: data.map((item) => ({
title: item.title,
description: `<img src='${item.photo_ipad}'><br>
${item.info}<br>
${item.size || ''}<br>
${item.genre && item.genre.name ? item.genre.name : ''}<br>
${item.museum && item.museum.name ? item.museum.name : ''}`,
pubDate: new Date(item.publish_date).toUTCString(),
link: item.photo_retina,
})),
};
};
-51
View File
@@ -1,51 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const caty = ctx.params.caty || 'all';
const rootUrl = 'https://daodu.tech';
const currentUrl = `${rootUrl}${caty === 'all' ? '' : '/archive_' + caty}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const list = $('h2[itemprop="headline"] a, .post-header .archive__article__title a')
.slice(0, 15)
.map((_, item) => {
item = $(item);
return {
title: item.text(),
link: item.attr('href'),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
content('.member-only').remove();
item.description = content('.post-entry-text').html();
item.pubDate = new Date(content('meta[property="article:published_time"]').attr('content')).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: $('title').text().replace('一覽表', ''),
link: currentUrl,
item: items,
};
};
-45
View File
@@ -1,45 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const parser = require('@/utils/rss-parser');
module.exports = async (ctx) => {
const feed = await parser.parseURL('http://www.daxiaamu.com/rss');
const ProcessFeed = async (link) => {
const response = await got({
method: 'get',
url: link,
});
const $ = cheerio.load(response.data);
return $('.main-content').html();
};
const items = await Promise.all(
feed.items.map(async (item) => {
const cache = await ctx.cache.get(item.link);
if (cache) {
return JSON.parse(cache);
}
const description = await ProcessFeed(item.link);
const single = {
title: item.title,
description,
pubDate: item.pubDate,
link: item.link,
author: item.author,
};
ctx.cache.set(item.link, JSON.stringify(single));
return single;
})
);
ctx.state.data = {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
};
};
-35
View File
@@ -1,35 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const timezone = require('@/utils/timezone');
module.exports = async (ctx) => {
const type = ctx.params.type || 'online';
const rootUrl = 'https://dbaplus.cn';
const currentUrl = `${rootUrl}/activity-${type === 'online' ? '12' : '48'}-1.html`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
const items = $('.media')
.map((_, item) => {
item = $(item);
return {
title: item.find('.media-heading').text(),
link: item.find('.pull-left').attr('href'),
description: `<img src="${item.find('.media-object').attr('src')}">`,
pubDate: timezone(new Date(item.find('.time').text().replace('时间:', '')), +8),
};
})
.get();
ctx.state.data = {
title: $('title').text().split('')[0],
link: currentUrl,
item: items,
};
};
-50
View File
@@ -1,50 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const tab = ctx.params.tab || 'All';
const rootUrl = 'https://dbaplus.cn';
const response = await got({
method: 'get',
url: rootUrl,
});
const $ = cheerio.load(response.data);
const list = $(`#tab${tab} li`)
.slice(0, 15)
.map((_, item) => {
item = $(item);
return {
link: item.find('a').eq(0).attr('href'),
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.title = content('h2.title').text();
item.author = content('span.user').text();
item.description = content('.new-detailed').html();
item.pubDate = new Date(content('span.time').eq(0).text()).toUTCString();
return item;
})
)
);
ctx.state.data = {
title: `${response.data.match(`<a href="#tab${tab}" data-toggle="tab">(.*)</a>`)[1]} - dbaplus社群`,
link: rootUrl,
item: items,
};
};
-66
View File
@@ -1,66 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const domain = 'https://m.dcinside.com';
const headers = {
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Cache-Control': 'max-age=0',
Connection: 'keep-alive',
Host: 'm.dcinside.com',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Mobile Safari/537.36',
};
async function getData(ctx, link) {
const { body } = await got.get(link, { headers });
const $ = cheerio.load(body);
const { path } = url.parse(link);
const key = `dcinside${path}`;
let result = await ctx.cache.get(key);
if (result) {
return result;
}
result = {
title: $('head title').text().trim(),
pubDate: new Date($('body > div.container > div > div > section > div.gallview-tit-box > div > ul > li:nth-child(2)').text()),
link,
description: $('body > div.container > div > div > section > div.gall-thum-btm > div > div.thum-txt').html(),
};
ctx.cache.set(key, result);
return result;
}
function getItems(ctx, $) {
return Promise.all(
$('body > div > div > div > section > ul.gall-detail-lst > li:not([class]) > div > a.lt')
.toArray()
.map((row) => getData(ctx, row.attribs.href))
);
}
module.exports = async (ctx) => {
const { id } = ctx.params;
const link = `${domain}/board/${id}`;
const { body } = await got.get(link, { headers });
const $ = cheerio.load(body);
const rss = {
title: $('head title').text(),
description: $('head title').text(),
link,
item: await getItems(ctx, $),
};
rss.item = rss.item.sort((a, b) => b.pubDate - a.pubDate);
ctx.state.data = rss;
};
-32
View File
@@ -1,32 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const name = ctx.params.name;
const season = ctx.params.season;
let link = `https://ddys.pro/${name}/`;
if (season) {
link += `${season}/`;
}
const response = await got(link);
const $ = cheerio.load(response.body);
const title = $('title').html();
const description = $('.abstract').html();
const tracks = JSON.parse($('.wp-playlist-script').html()).tracks.reverse();
const total = tracks.length;
ctx.state.data = {
title,
link,
description,
item: tracks.map(({ caption, description }, index) => ({
title: caption,
link: `${link}?ep=${total - index}`,
description,
})),
};
};
-37
View File
@@ -1,37 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const md5 = require('@/utils/md5');
module.exports = async (ctx) => {
const url = ctx.params.tag ? `https://ddys.pro/tag/${encodeURIComponent(ctx.params.tag)}/` : ctx.params.category ? `https://ddrk.me/category/${ctx.params.category}/` : 'https://ddys.pro/';
const response = await got(url);
const $ = cheerio.load(response.data);
const title = $('title').html();
const list = $('.post-box-list .post-box-container').toArray();
ctx.state.data = {
title,
link: url,
item: list.map((single, index) => {
const item = $(single);
const name = item.find('.post-box-title').text();
const poster = item
.find('.post-box-image')
.attr('style')
.match(/url\((.*?)\)/)[1];
const text = item.find('.post-box-text p').html();
const time = new Date();
time.setMinutes(time.getMinutes() - index);
return {
title: name,
author: '低端影视',
description: `<img src="${poster}" style="max-width: 100%;" >${text ?? ''}`,
link: item.find('.post-box-title a').attr('href'),
pubDate: time,
guid: md5(name),
};
}),
};
};
-34
View File
@@ -1,34 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const lang = ctx.params.lang || 'en';
const rootUrl = 'https://www.deepl.com';
const currentUrl = `${rootUrl}/zh/blog`;
const blogUrl = `${rootUrl}/gatsby/locales/${lang}/blog.json`;
const response = await got({
method: 'get',
url: blogUrl,
});
const items = [];
const data = response.data;
for (const key in data) {
const matches = key.match(/post_content_(\d{8})/);
if (matches) {
const date = matches[1];
items.push({
description: data[key],
link: `${currentUrl}/${date}`,
title: data['post_title_' + date],
pubDate: new Date(`${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`).toUTCString(),
});
}
}
ctx.state.data = {
title: response.data.title,
link: currentUrl,
item: items,
};
};
-44
View File
@@ -1,44 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
module.exports = async (ctx) => {
const type = ctx.params.type;
const host = 'https://www.dekudeals.com/';
const link = url.resolve(host, type);
const response = await got.get(link);
const $ = cheerio.load(response.data);
const title = $('#search-title').text();
const list = $('.search-main > .item-grid2 > div.cell');
const out = await Promise.all(
list
.map((index, item) => {
item = $(item);
const title = item.find('.name').text();
const link = url.resolve(host, item.find('.main-link').attr('href'));
const img = item.find('.main-link img').attr('src');
const price = item.find('strong').html();
const oldprice = item.find('s.text-muted').html();
const description = `<img src="${img}"><br><s>${oldprice}</s><br>${price}`;
const single = {
title,
link,
description,
};
return Promise.resolve(single);
})
.get()
);
ctx.state.data = {
title: `${title}—dekudeals`,
link,
item: out,
};
};
@@ -1,62 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const category = ctx.params.category || 'energy-resources-industrials';
const options = {
consumer: {
icid: 'top_consumer',
label: '消费行业',
},
'energy-resources-industrials': {
icid: 'top_energy-resources-industrials',
label: '能源、资源及工业行业',
},
'financial-services': {
icid: 'top_financial-services',
label: '金融服务行业',
},
'government-public-services': {
icid: 'top_government-public-services',
label: '政府及公共服务',
},
'life-sciences-healthcare': {
icid: 'top_life-sciences-healthcare',
label: '生命科学与医疗行业',
},
'technology-media-telecommunications': {
icid: 'top_technology-media-telecommunications',
label: '科技、传媒及电信行业',
},
};
const response = await got({
url: 'https://www2.deloitte.com/cn/zh/industries/energy-resources-industrials.html',
searchParams: {
icid: options[category].icid,
},
});
const $ = cheerio.load(response.data);
const articles = $('.featuredpromo a.promo-focus')
.map((index, ele) => ({
title: $(ele).text().trim(),
link: $(ele).attr('href'),
}))
.get();
const item = await Promise.all(
articles.slice(0, 10).map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got.get(`https://www2.deloitte.com/${item.link}`);
const s = cheerio.load(detailResponse.data);
item.description = s('.two-columns-c0').html();
return item;
})
)
);
ctx.state.data = {
title: `Deloitte - ${options[category].label}`,
link: `https://www2.deloitte.com/cn/zh/industries/energy-resources-industrials.html?icid=top_energy-resources-`,
item,
};
};
-57
View File
@@ -1,57 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const response = await got({
method: 'get',
url: 'https://opser.api.dgtle.com/v1/app/index?page=1',
});
const article_items = response.data.items.filter((item) => item.type in { 1: '', 4: '' });
const items = await Promise.all(
article_items.map(async (item) => {
let title = '';
let olink = ''; // 原始链接
let dlink = ''; // description 链接
let category = '';
let description = '';
if (1 === item.type) {
title = item.title;
dlink = `https://opser.api.dgtle.com/v1/article/view/${item.aid}`;
olink = `https://www.dgtle.com/article-${item.aid}-1.html`;
category = '文章';
description = await ctx.cache.tryGet(dlink, async () => {
const resp = await got.get(dlink);
return resp.data.content;
});
} else if (4 === item.type) {
title = item.summary;
dlink = `https://opser.api.dgtle.com/v1/feeds/inst/${item.aid}`;
olink = `https://www.dgtle.com/inst-${item.aid}-1.html`;
category = '兴趣动态';
description = await ctx.cache.tryGet(dlink, async () => {
const resp = await got.get(dlink);
return resp.data.imgs_url.reduce((content, dimg) => (content += `<img src=${dimg.path.split('?')[0]} />`), resp.data.content);
});
}
return {
title,
description,
pubDate: new Date(item.send_at * 1000).toUTCString(),
link: olink,
category,
author: item.author.username,
};
})
);
ctx.state.data = {
title: '数字尾巴 - 首页',
link: 'https://www.dgtle.com',
description: '数字尾巴首页内容',
item: items,
};
};
-30
View File
@@ -1,30 +0,0 @@
const got = require('@/utils/got');
const sanitizeHtml = require('sanitize-html');
module.exports = async (ctx) => {
const keyword = ctx.params.keyword;
const url = `https://www.dgtle.com/search?search_word=${encodeURIComponent(keyword)}`;
const response = await got({
method: 'get',
url: `https://www.dgtle.com/search/sale?search_word=${encodeURIComponent(keyword)}&page=1`,
headers: {
Referer: url,
},
});
const list = response.data.data.dataList;
ctx.state.data = {
title: `数字尾巴 - 闲置 - ${keyword}`,
link: url,
item: list.map((item) => ({
title: sanitizeHtml(item.title, { allowedTags: [], allowedAttributes: {} }),
author: item.author.username,
description: `<p>价格: ¥${item.price}</p><p>地址: ${item.address}</p><p>${item.content}</p><img src="${item.cover}" style="max-width: 100%;"/>`,
pubDate: new Date(item.created_at * 1000),
link: `https://www.dgtle.com/sale-${item.id}-1.html`,
})),
};
};

Some files were not shown because too many files have changed in this diff Show More