├── .codecov.yml ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report_en.md │ ├── bug_report_zh.md │ ├── feature_request_en.md │ ├── feature_request_zh.md │ ├── rss_request_en.md │ └── rss_request_zh.md ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── .yarnrc ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.arm32v7 ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── assets └── radar-rules.js ├── docker-compose.yml ├── docs ├── .vuepress │ ├── components │ │ ├── Route.vue │ │ └── RouteEn.vue │ ├── config.js │ ├── public │ │ ├── _headers │ │ └── logo.png │ └── styles │ │ ├── index.styl │ │ └── palette.styl ├── README.md ├── anime.md ├── api.md ├── en │ ├── README.md │ ├── install │ │ └── README.md │ ├── joinus │ │ └── README.md │ └── support │ │ └── README.md ├── forecast.md ├── format.js ├── game.md ├── government.md ├── install │ └── README.md ├── joinus │ └── README.md ├── live.md ├── multimedia.md ├── other.md ├── parameter.md ├── picture.md ├── program-update.md ├── programming.md ├── reading.md ├── shopping.md ├── social-media.md ├── support │ └── README.md ├── traditional-media.md ├── travel.md └── university.md ├── lib ├── api_router.js ├── config.js ├── favicon.png ├── index.js ├── middleware │ ├── access-control.js │ ├── api-response-handler.js │ ├── api-template.js │ ├── cache.js │ ├── debug.js │ ├── header.js │ ├── onerror.js │ ├── parameter.js │ ├── template.js │ └── utf8.js ├── protected_router.js ├── router.js ├── routes │ ├── 12306 │ │ └── zxdt.js │ ├── 005tv │ │ └── zx.js │ ├── 10000link │ │ └── news.js │ ├── 1point3acres │ │ ├── posts.js │ │ └── threads.js │ ├── 21caijing │ │ └── channel.js │ ├── 36kr │ │ ├── newsflashes.js │ │ └── search │ │ │ └── article.js │ ├── 3dm │ │ ├── game.js │ │ └── news_center.js │ ├── 8btc │ │ └── author.js │ ├── 99percentinvisible │ │ └── transcript.js │ ├── 9to5 │ │ ├── subsite.js │ │ └── utils.js │ ├── a9vg │ │ └── a9vg.js │ ├── acfun │ │ ├── bangumi.js │ │ └── video.js │ ├── afdian │ │ ├── dynamic.js │ │ └── explore.js │ ├── aisixiang │ │ ├── column.js │ │ ├── ranking.js │ │ └── utils.js │ ├── aiyanxishe │ │ └── home.js │ ├── algocasts │ │ └── all.js │ ├── aliyun │ │ └── database_month.js │ ├── allpoetry │ │ └── order.js │ ├── andyt │ │ └── index.js │ ├── anigamer │ │ ├── anime.js │ │ └── new_anime.js │ ├── anime1 │ │ ├── anime.js │ │ └── search.js │ ├── animen │ │ └── news.js │ ├── anitama │ │ └── channel.js │ ├── aozora │ │ └── newbook.js │ ├── apkpure │ │ └── versions.js │ ├── apple │ │ ├── appstore │ │ │ ├── gofans.js │ │ │ ├── in-app-purchase.js │ │ │ ├── price.js │ │ │ ├── update.js │ │ │ └── xianmian.js │ │ └── exchange_repair.js │ ├── aptonic │ │ └── action.js │ ├── aqicn │ │ └── index.js │ ├── aqk │ │ ├── category.js │ │ └── vul.js │ ├── archdaily │ │ └── home.js │ ├── arknights │ │ └── news.js │ ├── asahichinese-f │ │ └── index.js │ ├── asahichinese-j │ │ └── index.js │ ├── atfd │ │ └── index.js │ ├── autotrader │ │ └── index.js │ ├── babykingdom │ │ └── index.js │ ├── baidu │ │ ├── doodles.js │ │ └── topwords.js │ ├── bangumi │ │ ├── calendar │ │ │ ├── _base.js │ │ │ └── today.js │ │ ├── group │ │ │ ├── reply.js │ │ │ └── topic.js │ │ ├── person │ │ │ └── index.js │ │ └── subject │ │ │ ├── comments.js │ │ │ ├── ep.js │ │ │ ├── index.js │ │ │ └── offcial-subject-api.js │ ├── banyuetan │ │ └── index.js │ ├── bbc │ │ ├── index.js │ │ └── utils.js │ ├── bihu │ │ └── activaties.js │ ├── bilibili │ │ ├── article.js │ │ ├── audio.js │ │ ├── bangumi.js │ │ ├── blackboard.js │ │ ├── cache.js │ │ ├── coin.js │ │ ├── danmaku.js │ │ ├── dynamic.js │ │ ├── fav.js │ │ ├── followers.js │ │ ├── followings.js │ │ ├── followings_article.js │ │ ├── followings_video.js │ │ ├── linkNews.js │ │ ├── liveArea.js │ │ ├── liveRoom.js │ │ ├── liveSearch.js │ │ ├── mallIP.js │ │ ├── mallNew.js │ │ ├── page.js │ │ ├── partion-ranking.js │ │ ├── partion.js │ │ ├── ranking.js │ │ ├── reply.js │ │ ├── topic.js │ │ ├── userChannel.js │ │ ├── userFav.js │ │ ├── user_bangumi.js │ │ ├── utils.js │ │ ├── video.js │ │ └── vsearch.js │ ├── bing │ │ └── index.js │ ├── bishijie │ │ └── kuaixun.js │ ├── bjnews │ │ └── news.js │ ├── bjp │ │ └── apod.js │ ├── bjx │ │ └── huanbao.js │ ├── blogread │ │ └── newest.js │ ├── blogs │ │ ├── jingwei_link.js │ │ └── wangyin.js │ ├── boc │ │ └── whpj.js │ ├── bof │ │ └── home.js │ ├── caixin │ │ ├── article.js │ │ ├── blog.js │ │ └── category.js │ ├── cartoonmad │ │ └── comic.js │ ├── ccdi │ │ └── scdc.js │ ├── cctv │ │ ├── category.js │ │ └── utils │ │ │ ├── mzzlbg.js │ │ │ └── news.js │ ├── cfan │ │ └── news.js │ ├── changba │ │ └── user.js │ ├── checkee │ │ └── index.js │ ├── chinadaily │ │ └── english.js │ ├── chouti │ │ └── index.js │ ├── ciweimao │ │ └── chapter.js │ ├── cnbeta │ │ └── home.js │ ├── cneb │ │ ├── guoneinews.js │ │ └── yjxx.js │ ├── cninfo │ │ ├── fund_announcement.js │ │ └── stock_announcement.js │ ├── codeceo │ │ ├── category.js │ │ └── home.js │ ├── coolbuy │ │ └── newest.js │ ├── csrc │ │ ├── fashenwei.js │ │ └── news.js │ ├── curseforge │ │ ├── files.js │ │ └── generalfiles.js │ ├── cyzone │ │ ├── author.js │ │ └── label.js │ ├── d2 │ │ └── daily.js │ ├── dapenti │ │ ├── subject.js │ │ ├── tugua.js │ │ └── utils.js │ ├── daxiaamu │ │ └── home.js │ ├── dbmv │ │ └── index.js │ ├── dcard │ │ ├── section.js │ │ └── utils.js │ ├── dekudeals │ │ └── index.js │ ├── dgtle │ │ ├── keyword.js │ │ └── trade.js │ ├── dhl │ │ └── shipment-tracking.js │ ├── dianping │ │ └── user.js │ ├── digitaling │ │ ├── article.js │ │ ├── index.js │ │ └── project.js │ ├── dilbert │ │ └── strip.js │ ├── dilidili │ │ └── fanju.js │ ├── disqus │ │ └── posts.js │ ├── dockerhub │ │ └── build.js │ ├── dockone │ │ └── weekly.js │ ├── docschina │ │ └── jsweekly.js │ ├── donews │ │ ├── index.js │ │ └── utils.js │ ├── dongmanmanhua │ │ └── comic.js │ ├── dongqiudi │ │ ├── daily.js │ │ ├── player_news.js │ │ ├── result.js │ │ ├── special.js │ │ ├── team_news.js │ │ ├── top_news.js │ │ └── utils.js │ ├── douban │ │ ├── book │ │ │ └── rank.js │ │ ├── bookstore.js │ │ ├── commercialpress │ │ │ └── latest.js │ │ ├── doulist.js │ │ ├── event │ │ │ └── hot.js │ │ ├── explore.js │ │ ├── explore_column.js │ │ ├── group.js │ │ ├── later.js │ │ ├── latest_book.js │ │ ├── latest_music.js │ │ ├── people │ │ │ └── status.js │ │ ├── playing.js │ │ ├── topic.js │ │ └── ustop.js │ ├── douyin │ │ ├── like.js │ │ ├── user.js │ │ └── utils.js │ ├── douyu │ │ └── room.js │ ├── dribbble │ │ ├── keyword.js │ │ ├── popular.js │ │ ├── user.js │ │ └── utils.js │ ├── dsndsht23 │ │ ├── index.js │ │ └── pictures.js │ ├── duozhi │ │ └── index.js │ ├── duozhuayu │ │ └── search.js │ ├── dwnews │ │ ├── rank.js │ │ ├── utils.js │ │ └── yaowen.js │ ├── dysfz │ │ └── index.js │ ├── dytt │ │ └── index.js │ ├── earthquake │ │ ├── ceic.js │ │ └── index.js │ ├── eastday │ │ └── sh.js │ ├── ebb │ │ └── index.js │ ├── eeo │ │ └── index.js │ ├── eleme │ │ ├── open-be │ │ │ └── announce.js │ │ └── open │ │ │ └── announce.js │ ├── embassy │ │ ├── index.js │ │ └── supportedList.js │ ├── enclavebooks │ │ ├── category.js │ │ ├── collection.js │ │ └── user.js │ ├── eztv │ │ └── imdb.js │ ├── facebook │ │ ├── article.js │ │ └── page.js │ ├── fdroid │ │ └── apprelease.js │ ├── ff14 │ │ └── ff14_zh.js │ ├── fir │ │ └── update.js │ ├── firefox │ │ └── release.js │ ├── fitchratings │ │ └── site.js │ ├── flyertea │ │ ├── creditcard.js │ │ ├── preferential.js │ │ └── utils.js │ ├── ft │ │ ├── channel.js │ │ └── utils.js │ ├── fx678 │ │ └── kx.js │ ├── galgame │ │ ├── mmgal.js │ │ ├── sayhuahuo.js │ │ └── zdfx.js │ ├── gamersky │ │ ├── ent.js │ │ └── news.js │ ├── gaoqing │ │ ├── latest.js │ │ └── utils.js │ ├── gaoqingla │ │ └── latest.js │ ├── gcores │ │ └── category.js │ ├── geekpark │ │ └── breakingnews.js │ ├── geektime │ │ ├── column.js │ │ └── news.js │ ├── getitfree │ │ ├── category.js │ │ ├── search.js │ │ └── utils.js │ ├── gitchat │ │ └── newest.js │ ├── gitea │ │ └── blog.js │ ├── github │ │ ├── branches.js │ │ ├── file.js │ │ ├── follower.js │ │ ├── issue.js │ │ ├── pulls.js │ │ ├── repos.js │ │ ├── search.js │ │ ├── star.js │ │ └── trending.js │ ├── gitlab │ │ └── explore.js │ ├── gnn │ │ └── gnn.js │ ├── google │ │ ├── citations.js │ │ ├── doodles.js │ │ └── scholar.js │ ├── gouhuo │ │ ├── cache.js │ │ ├── index.js │ │ └── strategy.js │ ├── gov │ │ ├── city │ │ │ ├── index.js │ │ │ └── nanjing │ │ │ │ ├── getContent.js │ │ │ │ └── index.js │ │ ├── fmprc │ │ │ ├── fyrbt.js │ │ │ └── utils.js │ │ ├── mee │ │ │ └── gs.js │ │ ├── news │ │ │ └── index.js │ │ ├── province │ │ │ ├── index.js │ │ │ └── jiangsu │ │ │ │ ├── getContent.js │ │ │ │ └── index.js │ │ ├── shanxi │ │ │ └── rst.js │ │ ├── statecouncil │ │ │ └── briefing.js │ │ ├── suzhou │ │ │ ├── doc.js │ │ │ └── news.js │ │ ├── veterans │ │ │ ├── bnxx.js │ │ │ ├── index.js │ │ │ └── zcjd.js │ │ └── zhengce │ │ │ ├── govall.js │ │ │ ├── wenjian.js │ │ │ └── zuixin.js │ ├── gracg │ │ └── user.js │ ├── gradcafe │ │ └── result.js │ ├── greasyfork │ │ └── scripts.js │ ├── guanchazhe │ │ └── topic.js │ ├── guanzhi │ │ └── guanzhi.js │ ├── guardian │ │ ├── guardian.js │ │ └── utils.js │ ├── guokr │ │ ├── calendar.js │ │ └── scientific.js │ ├── gushiwen │ │ └── recommend.js │ ├── hackernews │ │ └── story.js │ ├── hanime │ │ └── video.js │ ├── hexo │ │ ├── next.js │ │ └── yilia.js │ ├── hko │ │ └── weather.js │ ├── hopper │ │ └── index.js │ ├── houmo │ │ └── booksource.js │ ├── houxu │ │ ├── events.js │ │ ├── live.js │ │ └── lives.js │ ├── hpoi │ │ ├── index.js │ │ └── info.js │ ├── hupu │ │ └── bbs.js │ ├── huxiu │ │ ├── author.js │ │ ├── search.js │ │ ├── tag.js │ │ └── utils.js │ ├── huya │ │ └── live.js │ ├── icourse163 │ │ └── newest.js │ ├── idownloadblog │ │ └── index.js │ ├── ifanr │ │ └── index.js │ ├── im2maker │ │ └── index.js │ ├── imaijia │ │ └── category.js │ ├── imuseum │ │ └── index.js │ ├── index.js │ ├── indienova │ │ └── article.js │ ├── infoq │ │ ├── recommend.js │ │ ├── topic.js │ │ └── utils.js │ ├── infzm │ │ └── news.js │ ├── instagram │ │ ├── tag.js │ │ └── user.js │ ├── instapaper │ │ └── person.js │ ├── iplay │ │ ├── home.js │ │ └── utils.js │ ├── iqiyi │ │ ├── dongman.js │ │ └── video.js │ ├── iresearch │ │ └── report.js │ ├── itjuzi │ │ ├── invest.js │ │ └── merge.js │ ├── jandan │ │ └── pic.js │ ├── japanpost │ │ ├── index.js │ │ └── utils.js │ ├── javbus │ │ ├── genre.js │ │ ├── home.js │ │ ├── series.js │ │ ├── star.js │ │ ├── uncensored │ │ │ ├── genre.js │ │ │ ├── home.js │ │ │ ├── series.js │ │ │ └── star.js │ │ ├── util.js │ │ └── western │ │ │ ├── genre.js │ │ │ ├── home.js │ │ │ ├── series.js │ │ │ └── star.js │ ├── jianshu │ │ ├── collection.js │ │ ├── home.js │ │ ├── trending.js │ │ ├── user.js │ │ └── utils.js │ ├── jiemian │ │ └── list.js │ ├── jingdong │ │ └── zhongchou.js │ ├── jinritoutiao │ │ └── keyword.js │ ├── jpmorganchase │ │ └── research.js │ ├── jskou │ │ └── index.js │ ├── juejin │ │ ├── books.js │ │ ├── category.js │ │ ├── collection.js │ │ ├── favorites.js │ │ ├── pins.js │ │ ├── posts.js │ │ ├── shares.js │ │ ├── tag.js │ │ ├── trending.js │ │ └── utils.js │ ├── juesheng │ │ └── index.js │ ├── kaiyan │ │ └── index.js │ ├── keep │ │ └── user.js │ ├── kingkong │ │ └── room.js │ ├── kirara │ │ └── news.js │ ├── kkj │ │ └── news.js │ ├── konachan │ │ └── post_popular_recent.js │ ├── kpmg │ │ └── insights.js │ ├── laosiji │ │ ├── feed.js │ │ ├── hot.js │ │ └── hotshow.js │ ├── latexstudio │ │ └── home.js │ ├── leboncoin │ │ └── ad.js │ ├── leetcode │ │ ├── articles.js │ │ ├── check-cn.js │ │ ├── check-us.js │ │ └── utils.js │ ├── lfsyd │ │ └── index.js │ ├── linkedkeeper │ │ └── index.js │ ├── liwushuo │ │ └── index.js │ ├── lkong │ │ ├── forum.js │ │ └── thread.js │ ├── lolapp │ │ └── recommend.js │ ├── ltaaa │ │ ├── _article.js │ │ └── main.js │ ├── luogu │ │ └── daily.js │ ├── lwn │ │ └── alerts.js │ ├── mafengwo │ │ └── note.js │ ├── mail │ │ └── imap.js │ ├── maitta │ │ └── index.js │ ├── manhuadb │ │ └── comics.js │ ├── manhuagui │ │ └── comic.js │ ├── maoyan │ │ ├── hot.js │ │ └── upcoming.js │ ├── matters │ │ ├── author.js │ │ ├── hot.js │ │ ├── latest.js │ │ ├── tags.js │ │ └── topics.js │ ├── maxnews │ │ └── dota2.js │ ├── meipai │ │ ├── user.js │ │ └── utils.js │ ├── meituan │ │ └── tech │ │ │ └── home.js │ ├── metacritic │ │ └── release.js │ ├── metred │ │ └── fuli.js │ ├── mi │ │ ├── crowdfunding.js │ │ ├── miui │ │ │ └── index.js │ │ └── youpin │ │ │ ├── crowdfunding.js │ │ │ ├── new.js │ │ │ └── utils.js │ ├── mihoyo │ │ ├── bh2.js │ │ └── bh3.js │ ├── miniapp │ │ ├── article.js │ │ └── store │ │ │ └── newest.js │ ├── mlhang │ │ └── latest.js │ ├── mobdata │ │ └── report.js │ ├── mofcom │ │ └── article.js │ ├── monsterhunter │ │ └── update.js │ ├── mp4ba │ │ └── index.js │ ├── mpaypass │ │ ├── main.js │ │ └── news.js │ ├── mzitu │ │ ├── category.js │ │ ├── home.js │ │ ├── post.js │ │ ├── tag.js │ │ ├── tags.js │ │ └── util.js │ ├── namoc │ │ ├── announcement.js │ │ ├── exhibition.js │ │ ├── media.js │ │ ├── news.js │ │ └── specials.js │ ├── natgeo │ │ ├── dailyphoto.js │ │ └── natgeo.js │ ├── nautilus │ │ └── topics.js │ ├── nba │ │ └── app_news.js │ ├── ncm │ │ ├── artist.js │ │ ├── djradio.js │ │ ├── playlist.js │ │ └── userplaylist.js │ ├── nfmovies │ │ └── index.js │ ├── nga │ │ ├── forum.js │ │ └── post.js │ ├── nhentai │ │ ├── other.js │ │ ├── search.js │ │ └── util.js │ ├── nhk │ │ └── news_web_easy.js │ ├── ningmeng │ │ └── song.js │ ├── nintendo │ │ ├── direct.js │ │ ├── eshop_hk.js │ │ ├── eshop_jp.js │ │ ├── eshop_us.js │ │ ├── news.js │ │ └── utils.js │ ├── nogizaka46 │ │ └── news.js │ ├── nosetime │ │ ├── comment.js │ │ └── home.js │ ├── novel │ │ ├── biquge.js │ │ ├── booksky.js │ │ ├── shuquge.js │ │ ├── uukanshu.js │ │ └── wenxuemi.js │ ├── nowcoder │ │ └── discuss.js │ ├── nvidia │ │ └── webdriverupdate.js │ ├── nytimes │ │ ├── index.js │ │ ├── morning_post.js │ │ └── utils.js │ ├── oilprice │ │ └── index.js │ ├── one │ │ └── index.js │ ├── oschina │ │ ├── news.js │ │ ├── topic.js │ │ ├── u.js │ │ └── user.js │ ├── outagereport │ │ └── service.js │ ├── owspace │ │ └── read.js │ ├── paidai │ │ ├── bbs.js │ │ ├── index.js │ │ ├── news.js │ │ └── utils.js │ ├── parcel │ │ └── hermesuk.js │ ├── patchwork.kernel.org │ │ ├── cache.js │ │ └── comments.js │ ├── pediy │ │ ├── topic.js │ │ └── utils.js │ ├── people │ │ ├── env.js │ │ ├── opinion.js │ │ └── xjpjh.js │ ├── pigtails │ │ └── index.js │ ├── pingwest │ │ ├── status.js │ │ ├── tag.js │ │ ├── user.js │ │ └── utils.js │ ├── pixiv │ │ ├── api │ │ │ ├── getBookmarks.js │ │ │ ├── getIllusts.js │ │ │ ├── getRanking.js │ │ │ ├── getUserDetail.js │ │ │ ├── searchIllust.js │ │ │ └── searchPopularIllust.js │ │ ├── bookmarks.js │ │ ├── constants.js │ │ ├── ranking.js │ │ ├── search.js │ │ ├── token.js │ │ └── user.js │ ├── plainlaw │ │ └── archives.js │ ├── polimi │ │ └── news.js │ ├── ps │ │ ├── list.js │ │ └── trophy.js │ ├── psnine │ │ ├── game.js │ │ ├── index.js │ │ ├── news.js │ │ ├── shuzhe.js │ │ └── trade.js │ ├── qdaily │ │ ├── index.js │ │ └── notch │ │ │ ├── explore.js │ │ │ └── index.js │ ├── qidian │ │ ├── chapter.js │ │ ├── forum.js │ │ ├── free-next.js │ │ └── free.js │ ├── qtfyfl │ │ └── category.js │ ├── queshu │ │ ├── book.js │ │ └── sale.js │ ├── qutoutiao │ │ └── category.js │ ├── radio │ │ └── radio.js │ ├── readhub │ │ └── category.js │ ├── rs05 │ │ └── rs05.js │ ├── rsshub │ │ └── rss.js │ ├── sankakucomplex │ │ └── post.js │ ├── sans │ │ └── summit_archive.js │ ├── saraba1st │ │ └── thread.js │ ├── security │ │ └── pulses.js │ ├── segmentfault │ │ └── channel.js │ ├── sexinsex │ │ └── index.js │ ├── sf │ │ └── sffq-announce.js │ ├── shanbay │ │ └── checkin.js │ ├── shuhui │ │ └── comics.js │ ├── sina │ │ ├── chuangshiji.js │ │ ├── discovery.js │ │ └── rollnews.js │ ├── sixthtone │ │ └── news.js │ ├── smzdm │ │ ├── haowen.js │ │ ├── haowen_fenlei.js │ │ ├── keyword.js │ │ └── ranking.js │ ├── sogou │ │ └── doodles.js │ ├── sohu │ │ └── mp.js │ ├── solidot │ │ ├── _article.js │ │ └── main.js │ ├── soul │ │ └── index.js │ ├── soundcloud │ │ ├── tracks.js │ │ └── utils.js │ ├── sse │ │ └── convert.js │ ├── sspai │ │ ├── author.js │ │ ├── column.js │ │ ├── matrix.js │ │ ├── series.js │ │ ├── shortcutsGallery.js │ │ ├── topic.js │ │ └── topics.js │ ├── steam │ │ ├── news.js │ │ ├── search.js │ │ └── steamgifts │ │ │ └── discussions.js │ ├── storyfm │ │ └── index.js │ ├── t66y │ │ ├── index.js │ │ └── post.js │ ├── tanwu │ │ └── products.js │ ├── taobao │ │ └── zhongchou.js │ ├── taoguba │ │ ├── index.js │ │ └── user.js │ ├── telegram │ │ ├── channel.js │ │ └── stickerpack.js │ ├── tencent │ │ ├── bugly │ │ │ └── changelog.js │ │ ├── dajia │ │ │ ├── author.js │ │ │ ├── index.js │ │ │ └── zhuanlan.js │ │ ├── gameinstitute │ │ │ └── community.js │ │ ├── guyu │ │ │ └── channel.js │ │ ├── news │ │ │ └── author.js │ │ ├── qcloud │ │ │ └── mlvb │ │ │ │ └── changelog.js │ │ ├── tucaoqq │ │ │ └── post.js │ │ ├── video │ │ │ └── playlist.js │ │ └── wechat │ │ │ ├── announce.js │ │ │ ├── csm.js │ │ │ ├── ershcimi.js │ │ │ ├── miniprogram │ │ │ └── plugins.js │ │ │ ├── tgchannel.js │ │ │ ├── uread.js │ │ │ └── wemp.js │ ├── test │ │ └── index.js │ ├── testerhome │ │ └── newest.js │ ├── the-economist │ │ ├── full.js │ │ └── gre-vocabulary.js │ ├── thepaper │ │ ├── channel.js │ │ ├── featured.js │ │ └── utils.js │ ├── thunderbird │ │ └── release.js │ ├── tieba │ │ ├── forum.js │ │ └── post.js │ ├── tingdiantz │ │ ├── 95598.js │ │ └── nanjing.js │ ├── tingshuitz │ │ ├── dalian.js │ │ ├── dongguan.js │ │ ├── guangzhou.js │ │ ├── hangzhou.js │ │ ├── nanjing.js │ │ ├── wuhan.js │ │ ├── xian.js │ │ ├── xiaoshan.js │ │ └── yangjiang.js │ ├── titsguru │ │ ├── category.js │ │ ├── daily.js │ │ ├── home.js │ │ ├── model.js │ │ └── util.js │ ├── tophub │ │ └── index.js │ ├── topys │ │ └── article.js │ ├── toutiao │ │ ├── today.js │ │ └── user.js │ ├── tprtc │ │ ├── cqzr.js │ │ ├── news.js │ │ └── qyzc.js │ ├── tssstatus │ │ └── index.js │ ├── tuicool │ │ └── mags.js │ ├── twitter │ │ ├── followings.js │ │ ├── likes.js │ │ ├── list.js │ │ ├── user.js │ │ └── utils.js │ ├── typora │ │ └── changelog.js │ ├── ui-cn │ │ ├── article.js │ │ └── user.js │ ├── un │ │ └── scveto.js │ ├── universities │ │ ├── bit │ │ │ ├── cs │ │ │ │ ├── cs.js │ │ │ │ └── utils.js │ │ │ └── jwc │ │ │ │ ├── jwc.js │ │ │ │ └── utils.js │ │ ├── buaa │ │ │ ├── news │ │ │ │ └── index.js │ │ │ └── utils.js │ │ ├── bupt │ │ │ ├── utils.js │ │ │ └── yz.js │ │ ├── cas │ │ │ └── sim │ │ │ │ └── academic.js │ │ ├── cczu │ │ │ ├── jwc.js │ │ │ └── news.js │ │ ├── cpu │ │ │ ├── home.js │ │ │ ├── jwc.js │ │ │ └── yjsy.js │ │ ├── cqu │ │ │ ├── jwc │ │ │ │ └── announcement.js │ │ │ └── news │ │ │ │ ├── jzyg.js │ │ │ │ └── utils.js │ │ ├── cqust │ │ │ ├── jw.js │ │ │ └── lib.js │ │ ├── csu │ │ │ └── job.js │ │ ├── cuc │ │ │ └── yz.js │ │ ├── cuit │ │ │ └── cxxww.js │ │ ├── dgut │ │ │ ├── jwc.js │ │ │ └── xsc.js │ │ ├── dlu │ │ │ └── jiaowu │ │ │ │ └── news.js │ │ ├── dpu │ │ │ ├── jiaowu │ │ │ │ └── news.js │ │ │ └── wlfw │ │ │ │ └── news.js │ │ ├── gdou │ │ │ └── jwc │ │ │ │ ├── jwtz.js │ │ │ │ └── utils.js │ │ ├── gdut │ │ │ └── news.js │ │ ├── henu │ │ │ └── news.js │ │ ├── heu │ │ │ └── ugs │ │ │ │ └── news.js │ │ ├── hit │ │ │ ├── jwc.js │ │ │ └── today.js │ │ ├── hust │ │ │ └── aia │ │ │ │ ├── news.js │ │ │ │ └── notice.js │ │ ├── ju │ │ │ └── jwc.js │ │ ├── kmust │ │ │ ├── job │ │ │ │ ├── careers.js │ │ │ │ └── jobfairs.js │ │ │ └── jwc.js │ │ ├── lit │ │ │ ├── jwc.js │ │ │ ├── tw.js │ │ │ └── xwzx.js │ │ ├── mit │ │ │ └── graduateadmissions.js │ │ ├── nchu │ │ │ └── jwc.js │ │ ├── nciae │ │ │ ├── news.js │ │ │ ├── tzgg.js │ │ │ └── xsxx.js │ │ ├── njtech │ │ │ └── jwc.js │ │ ├── njupt │ │ │ └── jwc.js │ │ ├── njust │ │ │ ├── cwc │ │ │ │ └── index.js │ │ │ ├── gs │ │ │ │ └── index.js │ │ │ └── jwc │ │ │ │ └── index.js │ │ ├── nku │ │ │ └── jwc │ │ │ │ └── index.js │ │ ├── nuaa │ │ │ ├── cs │ │ │ │ └── index.js │ │ │ ├── jwc │ │ │ │ └── jwc.js │ │ │ └── yjsy │ │ │ │ └── yjsy.js │ │ ├── nuist │ │ │ ├── bulletin.js │ │ │ ├── cas.js │ │ │ ├── jwc.js │ │ │ ├── library │ │ │ │ └── lib.js │ │ │ ├── scs.js │ │ │ ├── sese.js │ │ │ ├── xgc.js │ │ │ └── yjs.js │ │ ├── pku │ │ │ ├── eecs.js │ │ │ └── rccp │ │ │ │ └── mzyt.js │ │ ├── scnu │ │ │ ├── cs │ │ │ │ └── match.js │ │ │ ├── jw.js │ │ │ └── library.js │ │ ├── sctu │ │ │ ├── information-engineer-faculty │ │ │ │ ├── context.js │ │ │ │ └── index.js │ │ │ └── jwc │ │ │ │ ├── context.js │ │ │ │ └── index.js │ │ ├── scu │ │ │ └── jwc.js │ │ ├── scut │ │ │ └── jwc │ │ │ │ ├── news.js │ │ │ │ └── notice.js │ │ ├── sdu │ │ │ ├── cmse.js │ │ │ ├── cs.js │ │ │ ├── epe.js │ │ │ ├── mech.js │ │ │ └── sc.js │ │ ├── seu │ │ │ ├── cse │ │ │ │ └── index.js │ │ │ ├── radio │ │ │ │ └── academic.js │ │ │ └── yzb │ │ │ │ └── index.js │ │ ├── shanghaitech │ │ │ └── sist │ │ │ │ └── activity.js │ │ ├── shmtu │ │ │ ├── jwc.js │ │ │ └── www.js │ │ ├── shu │ │ │ └── jwc.js │ │ ├── sjtu │ │ │ ├── gs │ │ │ │ ├── tzgg.js │ │ │ │ └── utils.js │ │ │ ├── jwc.js │ │ │ └── seiee │ │ │ │ ├── academic.js │ │ │ │ ├── bjwb.js │ │ │ │ ├── utils.js │ │ │ │ └── xsb.js │ │ ├── swufe │ │ │ └── seie │ │ │ │ └── index.js │ │ ├── swust │ │ │ ├── cs.js │ │ │ ├── jwc_news.js │ │ │ └── jwc_notice.js │ │ ├── sysu │ │ │ └── sdcs.js │ │ ├── szu │ │ │ └── yz │ │ │ │ ├── index.js │ │ │ │ └── utils.js │ │ ├── tju │ │ │ └── sse │ │ │ │ ├── _article.js │ │ │ │ └── notice.js │ │ ├── uestc │ │ │ ├── auto.js │ │ │ ├── cs.js │ │ │ ├── jwc.js │ │ │ └── news.js │ │ ├── ustb │ │ │ └── tj │ │ │ │ └── news.js │ │ ├── wzbc │ │ │ └── news.js │ │ ├── xidian │ │ │ └── jwc.js │ │ ├── zjgsu │ │ │ ├── gsgg │ │ │ │ └── scripts.js │ │ │ ├── tzgg │ │ │ │ ├── scripts.js │ │ │ │ └── utils.js │ │ │ └── xszq │ │ │ │ └── scripts.js │ │ └── zju │ │ │ ├── career │ │ │ └── index.js │ │ │ ├── grs │ │ │ └── index.js │ │ │ └── physics │ │ │ └── index.js │ ├── uraaka-joshi │ │ ├── uraaka-joshi-user.js │ │ └── uraaka-joshi.js │ ├── v2ex │ │ ├── post.js │ │ └── topics.js │ ├── verge │ │ └── index.js │ ├── vgtime │ │ ├── keyword.js │ │ ├── news.js │ │ └── release.js │ ├── vocus │ │ ├── publication.js │ │ ├── user.js │ │ └── utils.js │ ├── vol │ │ └── lastupdate.js │ ├── vuevideo │ │ └── user.js │ ├── wallstreetcn │ │ └── news.js │ ├── weatheralarm │ │ └── index.js │ ├── wechat-open │ │ ├── community │ │ │ ├── pay-announce.js │ │ │ ├── xcx-announce.js │ │ │ └── xyx-announce.js │ │ └── pay │ │ │ └── announce.js │ ├── wegene │ │ ├── column.js │ │ └── newest.js │ ├── weibo │ │ ├── keyword.js │ │ ├── search │ │ │ └── hot.js │ │ ├── super_index.js │ │ ├── user.js │ │ └── utils.js │ ├── weidian │ │ └── goods.js │ ├── wenku8 │ │ └── chapter.js │ ├── weseepro │ │ ├── circle.js │ │ ├── newest-direct.js │ │ └── newest.js │ ├── westore │ │ └── new.js │ ├── whalegogo │ │ └── home.js │ ├── who │ │ └── news-room.js │ ├── wikihow │ │ ├── category.js │ │ └── index.js │ ├── wikipedia │ │ ├── mainland.js │ │ └── utils.js │ ├── wired │ │ └── tag.js │ ├── woshipm │ │ ├── bookmarks.js │ │ ├── popular.js │ │ └── user_article.js │ ├── xclient │ │ └── app.js │ ├── xiachufang │ │ ├── popular.js │ │ ├── user │ │ │ ├── cooked.js │ │ │ └── created.js │ │ └── utils.js │ ├── xiaoheihe │ │ ├── discount.js │ │ ├── news.js │ │ └── user.js │ ├── xiaomieu │ │ └── releases.js │ ├── xici │ │ └── index.js │ ├── ximalaya │ │ └── album.js │ ├── xueqiu │ │ ├── favorite.js │ │ ├── fund.js │ │ ├── hots.js │ │ ├── snb.js │ │ ├── stock_info.js │ │ ├── user.js │ │ └── user_stock.js │ ├── xuetangx │ │ ├── course_info.js │ │ └── course_list.js │ ├── yande.re │ │ └── post_popular_recent.js │ ├── yicai │ │ └── brief.js │ ├── yidoutang │ │ ├── case.js │ │ ├── guide.js │ │ ├── index.js │ │ └── mtest.js │ ├── youku │ │ └── channel.js │ ├── youtube │ │ ├── channel.js │ │ ├── playlist.js │ │ ├── user.js │ │ └── utils.js │ ├── youzan │ │ └── goods.js │ ├── yuque │ │ └── doc.js │ ├── yxdzqb │ │ └── index.js │ ├── yystv │ │ ├── category.js │ │ └── recommend.js │ ├── zaker │ │ └── source.js │ ├── zaobao │ │ ├── realtime.js │ │ └── znews.js │ ├── zcfy │ │ ├── hot.js │ │ └── index.js │ ├── zcool │ │ ├── recommend.js │ │ ├── top.js │ │ └── user.js │ ├── zhibo8 │ │ ├── forum.js │ │ └── post.js │ ├── zhihu │ │ ├── activities.js │ │ ├── answers.js │ │ ├── bookstore │ │ │ └── newest.js │ │ ├── collection.js │ │ ├── daily.js │ │ ├── hotlist.js │ │ ├── pin │ │ │ ├── daily.js │ │ │ ├── hotlist.js │ │ │ ├── people.js │ │ │ └── utils.js │ │ ├── question.js │ │ ├── topic.js │ │ ├── utils.js │ │ ├── weekly.js │ │ └── zhuanlan.js │ ├── zhilian │ │ └── index.js │ ├── zimuku │ │ └── index.js │ ├── zimuzu │ │ └── resource.js │ ├── ziroom │ │ └── room.js │ ├── zongheng │ │ └── chapter.js │ ├── zreading │ │ └── home.js │ └── zzz │ │ └── index.js ├── utils │ ├── common-config.js │ ├── common-utils.js │ ├── date.js │ ├── got.js │ ├── logger.js │ ├── md5.js │ ├── puppeteer.js │ ├── request-wrapper.js │ ├── rss-parser.js │ └── wait.js └── views │ ├── atom.art │ ├── rss.art │ └── welcome.art ├── logs └── .gitkeep ├── package.json ├── process.json ├── renovate.json ├── test ├── .eslintrc ├── config.js ├── middleware │ ├── access-control.js │ ├── cache.js │ ├── debug.js │ ├── error.js │ ├── header.js │ ├── parameter.js │ └── template.js ├── router.js └── utils │ ├── common-config.js │ ├── common-utils.js │ ├── date.js │ ├── got.js │ ├── md5.js │ ├── puppeteer.js │ ├── request-wrapper.js │ ├── rss-parser.js │ └── wait.js └── yarn.lock /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | Dockerfile* 4 | docker-compose* 5 | .dockerignore 6 | .gitignore 7 | README.md 8 | LICENSE 9 | .vscode 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | # Use 4 spaces for the Python files 13 | [*.py] 14 | indent_size = 4 15 | max_line_length = 80 16 | 17 | # The JSON files contain newlines inconsistently 18 | [*.json] 19 | insert_final_newline = ignore 20 | 21 | # Minified JavaScript files shouldn't be changed 22 | [**.min.js] 23 | indent_style = ignore 24 | insert_final_newline = ignore 25 | 26 | # Makefiles always use tabs for indentation 27 | [Makefile] 28 | indent_style = tab 29 | 30 | # Batch files use tabs for indentation 31 | [*.bat] 32 | indent_style = tab 33 | 34 | [*.md] 35 | trim_trailing_whitespace = false 36 | 37 | # Matches the exact files either package.json or .travis.yml 38 | [{package.json,.travis.yml}] 39 | indent_size = 2 40 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | .vscode 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: DIYgod 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Submit discovered bugs 4 | --- 5 | 6 | 11 | 12 | ### Involved route 13 | 14 | ### What is expected? 15 | 16 | ### What is actually happening? 17 | 18 | ### Self-deployed information 19 | 20 | 24 | 25 | | Env | Value | 26 | | ------------------ | ------------- | 27 | | OS | | 28 | | Node version | | 29 | | if Docker, version | | 30 | 31 | ### Additional info (logs errors etc) 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_zh.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug 报告 3 | about: 早起的小可爱有虫抓 4 | --- 5 | 6 | 11 | 12 | ### 路由地址 13 | 14 | ### 预期是什么? 15 | 16 | ### 实际发生了什么? 17 | 18 | ### 部署相关信息 19 | 20 | 24 | 25 | | Env | Value | 26 | | ------------------ | ------------- | 27 | | OS | | 28 | | Node version | | 29 | | if Docker, version | | 30 | 31 | ### 额外信息(日志、报错等) 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🍭 Feature Request 3 | about: Submit a new feature request 4 | --- 5 | 6 | 10 | 11 | ### What feature is it? 12 | 13 | ### What problem does this feature solve? 14 | 15 | ### Additional description 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_zh.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🍭 功能需求 3 | about: 提交新的功能需求 4 | --- 5 | 6 | 10 | 11 | ### 这是一个什么样的功能? 12 | 13 | ### 这个功能可以解决什么问题? 14 | 15 | ### 额外描述 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/rss_request_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🍰 RSS Request 3 | about: Submit a new RSS request 4 | --- 5 | 6 | 12 | 13 | ### Website URL 14 | 15 | ### Website description 16 | 17 | ### What content should be included? 18 | 19 | - [ ] content one 20 | - [ ] content two 21 | 22 | ### Additional description 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/rss_request_zh.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🍰 RSS 需求 3 | about: 提交新的 RSS 需求 4 | --- 5 | 6 | 12 | 13 | ### 网站地址 14 | 15 | ### 网站描述 16 | 17 | ### 需要生成什么内容? 18 | 19 | - [ ] 内容一 20 | - [ ] 内容二 21 | 22 | ### 额外描述 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | error.log 4 | combined.log 5 | package-lock.json 6 | .vscode 7 | .idea 8 | .DS_Store 9 | docs/.vuepress/dist 10 | lib/config/app.json 11 | lib/config/config.js 12 | yarn-error.log 13 | tmp 14 | *.swp 15 | *.iml 16 | coverage 17 | .env 18 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.13.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | docs/.vuepress/dist 3 | package-lock.json 4 | .github/ 5 | renovate.json 6 | coverage 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 233, 3 | "tabWidth": 4, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: stable 4 | 5 | services: 6 | - redis-server 7 | 8 | install: 9 | - yarn 10 | 11 | script: 12 | - npm run test 13 | 14 | after_script: 15 | - npm install codecov 16 | - ./node_modules/.bin/codecov 17 | 18 | cache: 19 | yarn: true 20 | directories: 21 | - node_modules 22 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | --exact true 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## 请参见[参与我们](https://docs.rsshub.app/joinus/) 2 | 3 | ## Please refer to [Join Us](https://docs.rsshub.app/en/joinus/) 4 | -------------------------------------------------------------------------------- /Dockerfile.arm32v7: -------------------------------------------------------------------------------- 1 | FROM arm32v7/node:8.16-slim 2 | MAINTAINER junfeng 3 | 4 | # 使用163镜像,若不需要可以注释 5 | RUN echo -e "deb http://mirrors.ustc.edu.cn/debian/ stretch main \ndeb http://mirrors.ustc.edu.cn/debian/ stretch-updates main " > /etc/apt/sources.list 6 | 7 | # 复用缓存, 如果github文件下载失败,可以手动下载,放至项目根目录,注释下方ADD语句,改用下方COPY语句 8 | ADD https://github.com/junfengP/dumb-init/releases/download/v1.2.0/dumb-init-armhf /usr/local/bin/dumb-init 9 | # COPY dumb-init-armhf /usr/local/bin/dumb-init 10 | RUN chmod +x /usr/local/bin/dumb-init 11 | 12 | ARG USE_CHINA_NPM_REGISTRY=1; 13 | ENV NODE_ENV production 14 | 15 | WORKDIR /app 16 | 17 | COPY package.json /app 18 | 19 | RUN if [ "$USE_CHINA_NPM_REGISTRY" = 1 ]; then \ 20 | echo 'use npm mirror'; npm config set registry https://registry.npm.taobao.org; \ 21 | fi; 22 | 23 | # 跳过Chromium下载,puppeteer不会下载chrome-arm 24 | RUN export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ 25 | && npm install --production 26 | 27 | COPY . /app 28 | 29 | EXPOSE 1200 30 | ENTRYPOINT ["dumb-init", "--"] 31 | CMD ["npm", "run", "start"] 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DIYgod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node lib/index.js 2 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RSSHub", 3 | "description": "万物皆可 RSS", 4 | "repository": "https://github.com/DIYgod/RSSHub", 5 | "website": "https://docs.rsshub.app/", 6 | "logo": "https://i.loli.net/2019/04/23/5cbeb7e41414c.png", 7 | "keywords": ["RSS"], 8 | "env": { 9 | "NODE_MODULES_CACHE": { 10 | "value": "false", 11 | "required": true 12 | }, 13 | "PORT": { 14 | "value": "80", 15 | "required": false 16 | }, 17 | "PIXIV_USERNAME": { 18 | "required": false 19 | }, 20 | "PIXIV_PASSWORD": { 21 | "required": false 22 | }, 23 | "DISQUS_API_KEY": { 24 | "required": false 25 | }, 26 | "TWITTER_CONSUMER_KEY": { 27 | "required": false 28 | }, 29 | "TWITTER_CONSUMER_SECRET": { 30 | "required": false 31 | }, 32 | "YOUTUBE_KEY": { 33 | "required": false 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | service.rsshub: 6 | image: diygod/rsshub 7 | restart: always 8 | ports: 9 | - "1200:1200" 10 | environment: 11 | NODE_ENV: production 12 | CACHE_TYPE: redis 13 | REDIS_URL: 'redis://db.redis:6379/' 14 | PUPPETEER_WS_ENDPOINT: 'ws://service.browserless:3000' 15 | depends_on: 16 | - db.redis 17 | - service.browserless 18 | 19 | service.browserless: 20 | image: browserless/chrome 21 | restart: always 22 | 23 | db.redis: 24 | image: redis 25 | restart: always 26 | volumes: 27 | - redis-data:/data 28 | 29 | volumes: 30 | redis-data: 31 | -------------------------------------------------------------------------------- /docs/.vuepress/public/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | cache-control: public, max-age=60 3 | -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Remsh/RSSHub/2ca197b622d453e9cca3207a4615e3dc5801a3e1/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | .navbar .home-link .site-name { 2 | color: #F5712C; 3 | } 4 | 5 | .page .custom-block.tip { 6 | border-color: #F5712C; 7 | } 8 | 9 | .theme-container .page .content__default .logo-img { 10 | margin-top: 3.6rem; 11 | } 12 | 13 | .page .content__default .logo-text { 14 | padding-top: 2.6rem; 15 | padding-bottom: 2rem; 16 | } 17 | 18 | .icon.outbound { 19 | display: none; 20 | } 21 | 22 | a { 23 | word-break: break-all; 24 | } 25 | 26 | #关于 { 27 | display: none; 28 | } 29 | 30 | #app .global-ui .sw-update-popup { 31 | border: 1px solid #F5712C; 32 | } 33 | 34 | .routes .sidebar-group-items > li > .sidebar-sub-headers > .sidebar-sub-header > a { 35 | color: $accentColor; 36 | } 37 | -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | $accentColor = #F5712C 2 | -------------------------------------------------------------------------------- /docs/en/support/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # Support RSSHub 6 | 7 | RSSHub is open source and completely free under the MIT license. However, just like any other open source project, as the project grows, the hosting, development and maintenance requires funding support. 8 | 9 | You can support RSSHub via donations. 10 | 11 | ## Recurring Donation 12 | 13 | Recurring donors will be rewarded via express issue response, or even have your name displayed on our GitHub page and website. 14 | 15 | - Become a Backer or a Sponser on [Patreon](https://www.patreon.com/DIYgod) 16 | - Contact us directly: i#diygod.me 17 | 18 | ## One-time Donation 19 | 20 | We accept donations via the following ways: 21 | 22 | - [WeChat Pay](https://i.loli.net/2019/03/23/5c950ebbc373e.png) 23 | - [Alipay](https://i.loli.net/2019/03/23/5c950ebbc980e.png) 24 | - [Paypal](https://www.paypal.me/DIYgod) 25 | -------------------------------------------------------------------------------- /docs/support/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # 支持 RSSHub 6 | 7 | RSSHub 是采用 MIT 许可的开源项目, 使用完全免费. 但是随着项目规模的增长, 也需要有相应的资金支持才能持续项目的维护与开发. 8 | 9 | 你可以通过下列的方法来赞助 RSSHub 的开发. 10 | 11 | ## 周期性赞助 12 | 13 | 周期性赞助可以获得额外的回报, 比如更快的 GitHub 响应或者你的名字会出现在 RSSHub 的 GitHub 仓库和现在我们的官网中. 14 | 15 | - 通过 [Patreon](https://www.patreon.com/DIYgod) 赞助 16 | - 给我们发邮件联系赞助事宜: i#diygod.me 17 | 18 | ## 一次性赞助 19 | 20 | 我们通过以下方式接受赞助: 21 | 22 | - [微信支付](https://i.loli.net/2019/03/23/5c950ebbc373e.png) 23 | - [支付宝](https://i.loli.net/2019/03/23/5c950ebbc980e.png) 24 | 25 | ## 周边产品 26 | 27 | 也可以购买我们[官方授权的周边产品](https://telegra.ph/RSSHub-周边-08-20-2),每售出一件周边,我们将获得售价 10% 的捐赠。 28 | -------------------------------------------------------------------------------- /lib/api_router.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const router = new Router(); 3 | const routes = require('./router'); 4 | 5 | router.get('/routes/:name?', (ctx) => { 6 | const allRoutes = Array.from(routes.stack); 7 | allRoutes.shift(); 8 | const result = {}; 9 | let counter = 0; 10 | 11 | allRoutes.forEach((i) => { 12 | const path = i.path; 13 | const top = path.split('/')[1]; 14 | 15 | if (!ctx.params.name || top === ctx.params.name) { 16 | if (result[top]) { 17 | result[top].routes.push(path); 18 | } else { 19 | result[top] = { routes: [path] }; 20 | } 21 | counter++; 22 | } 23 | }); 24 | 25 | ctx.body = { counter, result }; 26 | }); 27 | 28 | module.exports = router; 29 | -------------------------------------------------------------------------------- /lib/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Remsh/RSSHub/2ca197b622d453e9cca3207a4615e3dc5801a3e1/lib/favicon.png -------------------------------------------------------------------------------- /lib/middleware/api-template.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx, next) => { 2 | await next(); 3 | if (ctx.request.path.startsWith('/api/')) { 4 | return ctx.res.ok({ 5 | message: `request returned ${ctx.body.counter} ${ctx.body.counter > 1 ? 'routes' : 'route'}`, 6 | data: ctx.body.result, 7 | }); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /lib/middleware/debug.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx, next) => { 2 | if (!ctx.debug.paths[ctx.request.path]) { 3 | ctx.debug.paths[ctx.request.path] = 0; 4 | } 5 | ctx.debug.paths[ctx.request.path]++; 6 | 7 | const ip = ctx.ips[0] || ctx.ip; 8 | if (!ctx.debug.ips[ip]) { 9 | ctx.debug.ips[ip] = 0; 10 | } 11 | ctx.debug.ips[ip]++; 12 | ctx.debug.request++; 13 | 14 | await next(); 15 | 16 | if (!ctx.debug.routes[ctx._matchedRoute]) { 17 | ctx.debug.routes[ctx._matchedRoute] = 0; 18 | } 19 | ctx.debug.routes[ctx._matchedRoute]++; 20 | 21 | if (ctx.response.get('X-Koa-Redis-Cache') || ctx.response.get('X-Koa-Memory-Cache')) { 22 | ctx.debug.hitCache++; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /lib/middleware/onerror.js: -------------------------------------------------------------------------------- 1 | const logger = require('@/utils/logger'); 2 | 3 | module.exports = async (ctx, next) => { 4 | try { 5 | await next(); 6 | } catch (err) { 7 | logger.error(`Error in ${ctx.request.path}: ${err instanceof Error ? err.stack : err}`); 8 | ctx.set({ 9 | 'Content-Type': 'text/html; charset=UTF-8', 10 | }); 11 | ctx.body = `RSSHub 发生了一些意外:
${err instanceof Error ? err.stack : err}
`; 12 | ctx.status = 404; 13 | 14 | if (!ctx.debug.errorPaths[ctx.request.path]) { 15 | ctx.debug.errorPaths[ctx.request.path] = 0; 16 | } 17 | ctx.debug.errorPaths[ctx.request.path]++; 18 | 19 | if (!ctx.debug.errorRoutes[ctx._matchedRoute]) { 20 | ctx.debug.errorRoutes[ctx._matchedRoute] = 0; 21 | } 22 | ctx.debug.errorRoutes[ctx._matchedRoute]++; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /lib/middleware/utf8.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/2507608/error-input-is-not-proper-utf-8-indicate-encoding-using-phps-simplexml-lo/40552083#40552083 2 | // https://stackoverflow.com/questions/1497885/remove-control-characters-from-php-string/1497928#1497928 3 | module.exports = async (ctx, next) => { 4 | await next(); 5 | ctx.body = typeof ctx.body !== 'object' ? ctx.body.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/g, '') : ctx.body; 6 | }; 7 | -------------------------------------------------------------------------------- /lib/protected_router.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const router = new Router(); 3 | const auth = require('koa-basic-auth'); 4 | const config = require('./config'); 5 | 6 | router.use('/(.*)', auth(config.authentication)); 7 | 8 | // RSSHub 9 | router.get('/rsshub/rss', require('./routes/rsshub/rss')); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /lib/routes/1point3acres/posts.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { id } = ctx.params; 5 | 6 | const { posts } = (await got.get(`https://instant.1point3acres.com/v2/api/user/post?pg=1&ps=10&user_id=${id}`)).data; 7 | const [{ author_name: author }] = posts; 8 | 9 | ctx.state.data = { 10 | title: `${author}的回复 - 一亩三分地`, 11 | link: `https://instant.1point3acres.com/profile/${id}`, 12 | description: `${author}的回复 - 一亩三分地`, 13 | item: posts.map((item) => ({ 14 | title: item.message, 15 | author, 16 | description: item.message, 17 | pubDate: new Date(item.create_time + ' GMT+8').toUTCString(), 18 | link: `https://instant.1point3acres.com/thread/${item.thread_id}/post/${item.id}`, 19 | })), 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/1point3acres/threads.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { id } = ctx.params; 5 | 6 | const { threads } = (await got.get(`https://instant.1point3acres.com/v2/api/user/thread?pg=1&ps=10&user_id=${id}`)).data; 7 | const [{ author_name: author }] = threads; 8 | 9 | ctx.state.data = { 10 | title: `${author}的主题帖 - 一亩三分地`, 11 | link: `https://instant.1point3acres.com/profile/${id}`, 12 | description: `${author}的主题帖 - 一亩三分地`, 13 | item: threads.map((item) => ({ 14 | title: item.title, 15 | author, 16 | description: item.description, 17 | pubDate: new Date(item.update_time + ' GMT+8').toUTCString(), 18 | link: `https://instant.1point3acres.com/thread/${item.id}`, 19 | })), 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/9to5/utils.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | 3 | const ProcessFeed = (data) => { 4 | const $ = cheerio.load(data); 5 | const content = $('div[itemprop="articleBody"]'); 6 | 7 | const cover = $('meta[property="og:image"]'); 8 | if (cover.length > 0) { 9 | $(``).insertBefore(content[0].firstChild); 10 | } 11 | 12 | // remove useless DOMs 13 | content 14 | .find('hr') 15 | .nextAll() 16 | .remove(); 17 | 18 | content.find('hr, ins.adsbygoogle, script').each((i, e) => { 19 | $(e).remove(); 20 | }); 21 | 22 | content.find('div').each((i, e) => { 23 | if ($(e)[0].attribs.class) { 24 | const classes = $(e)[0].attribs.class; 25 | if (classes.match(/\w{10}\s\w{10}/g)) { 26 | $(e).remove(); 27 | } 28 | } 29 | }); 30 | 31 | return content.html(); 32 | }; 33 | 34 | module.exports = { 35 | ProcessFeed, 36 | }; 37 | -------------------------------------------------------------------------------- /lib/routes/anigamer/anime.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { anime } = await got.get(`https://api.gamer.com.tw/mobile_app/anime/v1/video.php?sn=0&anime_sn=${ctx.params.sn}`).then((r) => r.data); 5 | ctx.state.data = { 6 | title: anime.title, 7 | link: `https://ani.gamer.com.tw/animeRef.php?sn=${anime.anime_sn}`, 8 | description: ` ` + anime.content.trim(), 9 | item: anime.volumes[0].map((item) => ({ 10 | title: `${anime.title} 第 ${item.volume} 集`, 11 | link: `https://ani.gamer.com.tw/animeVideo.php?sn=${item.video_sn}`, 12 | })), 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/routes/anigamer/new_anime.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { new_anime } = await got.get('https://api.gamer.com.tw/mobile_app/anime/v1/index.php').then((r) => r.data); 5 | ctx.state.data = { 6 | title: '動畫瘋最後更新', 7 | link: 'https://ani.gamer.com.tw/', 8 | item: new_anime.date.map((item) => { 9 | const date = new Date(); 10 | const month = item.info.split('/')[0] - 1; 11 | const day = item.info.split(' ')[0].split('/')[1]; 12 | const pubdatetemp = new Date(date.getFullYear(), month, day); 13 | const pubdate = pubdatetemp > date ? new Date(date.getFullYear() - 1, month, day) : pubdatetemp; 14 | 15 | return { 16 | title: item.title, 17 | description: item.info, 18 | link: `https://ani.gamer.com.tw/animeRef.php?sn=${item.anime_sn}`, 19 | pubDate: pubdate.toUTCString(), 20 | }; 21 | }), 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/anime1/anime.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { time, name } = ctx.params; 6 | const $ = await got.get(`https://anime1.me/category/${encodeURIComponent(time)}/${encodeURIComponent(name)}`).then((r) => cheerio.load(r.data)); 7 | const title = $('.page-title') 8 | .text() 9 | .trim(); 10 | ctx.state.data = { 11 | title, 12 | link: `https://anime1.me/category/${time}/${name}`, 13 | description: title, 14 | item: $('article') 15 | .toArray() 16 | .map((art) => { 17 | const $el = $(art); 18 | const title = $el.find('.entry-title a').text(); 19 | return { 20 | title: $el.find('.entry-title a').text(), 21 | link: $el.find('.entry-title a').attr('href'), 22 | description: title, 23 | pubDate: new Date($el.find('time').attr('datetime')).toUTCString(), 24 | }; 25 | }), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/anime1/search.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { keyword } = ctx.params; 6 | const $ = await got.get(`https://anime1.me/?s=${encodeURIComponent(keyword)}`).then((r) => cheerio.load(r.data)); 7 | const title = $('.page-title') 8 | .text() 9 | .trim(); 10 | ctx.state.data = { 11 | title, 12 | link: `https://anime1.me/?s=${keyword}`, 13 | description: title, 14 | item: $('article:has(.cat-links)') 15 | .toArray() 16 | .map((art) => { 17 | const $el = $(art); 18 | const title = $el.find('.entry-title a').text(); 19 | return { 20 | title: $el.find('.entry-title a').text(), 21 | link: $el.find('.entry-title a').attr('href'), 22 | description: title, 23 | pubDate: new Date($el.find('time').attr('datetime')).toUTCString(), 24 | }; 25 | }), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/apple/appstore/xianmian.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { 5 | data: { objects: data }, 6 | } = await got.get('http://app.so/api/v5/appso/discount/?platform=web&limit=10'); 7 | 8 | ctx.state.data = { 9 | title: '每日精品限免 / 促销应用', 10 | link: 'http://app.so/xianmian/', 11 | description: '鲜面连线 by AppSo:每日精品限免 / 促销应用', 12 | item: data.map((item) => ({ 13 | title: `「${item.discount_info[0].discounted_price === '0.00' ? '免费' : '降价'}」${item.app.name}`, 14 | description: ` 15 | 16 |
17 | 原价:¥${item.discount_info[0].original_price} -> 现价:¥${item.discount_info[0].discounted_price} 18 |
19 | 平台:${item.app.download_link[0].device} 20 |
21 | ${item.content} 22 | `, 23 | pubDate: new Date(item.updated_at * 1000).toUTCString(), 24 | link: item.app.download_link[0].link, 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/aptonic/action.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const url = require('url'); 3 | const cheerio = require('cheerio'); 4 | 5 | module.exports = async (ctx) => { 6 | const link = `https://aptonic.com/actions/`; 7 | 8 | const res = await got.get(link); 9 | const $ = cheerio.load(res.body); 10 | 11 | ctx.state.data = { 12 | title: 'Dropzone Actions', 13 | link, 14 | item: $('table.actions >> tr') 15 | .get() 16 | .map((item) => { 17 | item = $(item); 18 | return { 19 | title: item.find('h2').text(), 20 | description: ` 21 | 22 |
23 | ${$(item.find('td')[1]) 24 | .children() 25 | .remove() 26 | .end() 27 | .text()} 28 | `, 29 | pubDate: new Date().toUTCString(), 30 | link: url.resolve(link, item.find('td.icon a').attr('href')), 31 | }; 32 | }), 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/aqicn/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const city = ctx.params.city; 5 | const area = isNaN(city) ? city : `@${city}`; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `http://aqicn.org/aqicn/json/android/${area}/json`, 10 | }); 11 | const data = response.data; 12 | 13 | ctx.state.data = { 14 | title: `${data.namena}AQI`, 15 | link: `https://aqicn.org/city/${data.ids.path}`, 16 | description: `${data.namena}AQI-aqicn.org`, 17 | item: [ 18 | { 19 | title: `${data.namena}实时空气质量(AQI)${data.utimecn}`, 20 | description: `${data.infocn}
风力:${data.cwind[0]}
AQI:${data.aqi}
`, 21 | pubDate: new Date(data.time * 1000).toUTCString(), 22 | guid: data.time, 23 | link: `https://aqicn.org/city/${data.ids.path}`, 24 | }, 25 | ], 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/aqk/category.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const api = 'https://api.anquanke.com/data/v1/posts?size=10&page=1&category='; 5 | const type = ctx.params.category; 6 | const host = 'https://www.anquanke.com'; 7 | const res = await got.get(`${api}${type}`); 8 | const dataArray = res.data.data; 9 | 10 | const items = dataArray.map((item) => ({ 11 | title: item.title, 12 | description: item.desc, 13 | pubDate: item.date, 14 | link: `${host}/${type === 'week' ? 'week' : 'post'}/id/${item.id}`, 15 | })); 16 | 17 | ctx.state.data = { 18 | title: `安全客-${dataArray[0].category_name}`, 19 | link: 'https://www.anquanke.com', 20 | item: items, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/baidu/doodles.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'get', 7 | url: 'http://logo.baidu.com/main/show_data/0/0/0/0', 8 | }); 9 | 10 | const $ = cheerio.load(response.data); 11 | 12 | ctx.state.data = { 13 | title: '百度趣画', 14 | link: 'http://logo.baidu.com/', 15 | item: $('.col') 16 | .map((index, item) => { 17 | item = $(item); 18 | 19 | return { 20 | title: `${item.find('.title').text()}-${item.find('.date').text()}`, 21 | description: ``, 22 | link: item.find('.more a').attr('href'), 23 | }; 24 | }) 25 | .get(), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/bangumi/group/topic.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const resolve_url = require('url').resolve; 4 | 5 | const base_url = 'https://bgm.tv/'; 6 | 7 | module.exports = async (ctx) => { 8 | const groupID = ctx.params.id; 9 | const link = 'https://bgm.tv/group/' + groupID + '/forum'; 10 | const html = (await got.get(link)).data; 11 | const $ = cheerio.load(html); 12 | const title = 'Bangumi - ' + $('.SecondaryNavTitle').text(); 13 | 14 | ctx.state.data = { 15 | title: `${title}`, 16 | link: link, 17 | item: $('.topic_list .topic') 18 | .map((_, elem) => ({ 19 | link: resolve_url(base_url, $('.subject a', elem).attr('href')), 20 | title: $('.subject a', elem).attr('title'), 21 | pubDate: new Date($('.lastpost .time', elem).text()).toUTCString(), 22 | description: 'Author: ' + $('.author a', elem).text() + '
' + 'Replay: ' + $('.posts', elem).text(), 23 | })) 24 | .get(), 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/bangumi/subject/ep.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (subjectID) => { 4 | const url = `https://api.bgm.tv/subject/${subjectID}?responseGroup=large`; 5 | const epsInfo = (await got.get(url)).data; 6 | const activeEps = []; 7 | 8 | epsInfo.eps.forEach((e) => { 9 | if (e.status === 'Air') { 10 | activeEps.push(e); 11 | } 12 | }); 13 | 14 | return { 15 | title: `${epsInfo.name_cn}`, 16 | link: `https://bgm.tv/subject/${subjectID}`, 17 | description: epsInfo.summary, 18 | item: activeEps.reverse().map((e) => ({ 19 | title: `ep.${e.sort} ${e.name_cn}`, 20 | description: `ep.${e.sort} ${e.name_cn}

${e.desc.replace(/\n+/g, '
')}

`, 21 | pubDate: new Date(e.airdate).toUTCString(), 22 | guid: e.id, 23 | link: e.url, 24 | })), 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/bangumi/subject/index.js: -------------------------------------------------------------------------------- 1 | const getComments = require('./comments.js'); 2 | const getFromAPI = require('./offcial-subject-api.js'); 3 | 4 | const getEps = require('./ep.js'); 5 | 6 | module.exports = async (ctx) => { 7 | const id = ctx.params.id; 8 | let response; 9 | if (ctx.params.type) { 10 | switch (ctx.params.type) { 11 | case 'comments': 12 | response = await getComments(id, Number(ctx.request.query.minLength) || 0); 13 | break; 14 | case 'blogs': 15 | response = await getFromAPI('blog')(id); 16 | break; 17 | case 'topics': 18 | response = await getFromAPI('topic')(id); 19 | break; 20 | default: 21 | throw Error(`暂不支持对${ctx.params.type}的订阅`); 22 | } 23 | } else { 24 | response = await getEps(id); 25 | } 26 | ctx.state.data = response; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/bilibili/coin.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cache = require('./cache'); 3 | 4 | module.exports = async (ctx) => { 5 | const uid = ctx.params.uid; 6 | 7 | const name = await cache.getUsernameFromUID(ctx, uid); 8 | 9 | const response = await got({ 10 | method: 'get', 11 | url: `https://api.bilibili.com/x/space/coin/video?vmid=${uid}&jsonp=jsonp`, 12 | headers: { 13 | Referer: `https://space.bilibili.com/${uid}/`, 14 | }, 15 | }); 16 | const data = response.data.data; 17 | 18 | ctx.state.data = { 19 | title: `${name} 的 bilibili 投币视频`, 20 | link: `https://space.bilibili.com/${uid}`, 21 | description: `${name} 的 bilibili 投币视频`, 22 | item: data.map((item) => ({ 23 | title: item.title, 24 | description: `${item.desc}
`, 25 | pubDate: new Date(item.time * 1000).toUTCString(), 26 | link: `https://www.bilibili.com/video/av${item.aid}`, 27 | })), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/bilibili/mallIP.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `https://mall.bilibili.com/mall-c/search/category?keyword=&filters=&priceFlow=&priceCeil=&sortType=recommend&sortOrder=&pageIndex=1&state=&type=ip&id=${id}`, 9 | headers: { 10 | Referer: `https://mall.bilibili.com/list.html?ip=${id}`, 11 | }, 12 | }); 13 | 14 | const data = response.data.data; 15 | 16 | ctx.state.data = { 17 | title: `${data.pageTitle} - 会员购`, 18 | link: `https://mall.bilibili.com/list.html?ip=${id}`, 19 | item: data.list.map((item) => ({ 20 | title: item.name, 21 | description: `${item.name}
${item.brief}
¥${item.price}
`, 22 | link: `https://mall.bilibili.com/detail.html?itemsId=${item.itemsId}`, 23 | })), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/bilibili/mallNew.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://mall.bilibili.com/mall-c/home/calendar/list?page=new&startWeekNO=0&limitWeekSize=3', 7 | headers: { 8 | Referer: 'https://mall.bilibili.com/date.html?page=new', 9 | }, 10 | }); 11 | 12 | const data = response.data.data.vo.weeks; 13 | const days = [...data[0].days, ...data[1].days]; 14 | const items = []; 15 | days.forEach((day) => { 16 | items.push(...day.presaleItems); 17 | }); 18 | 19 | ctx.state.data = { 20 | title: '会员购新品上架', 21 | link: 'https://mall.bilibili.com/date.html?page=new', 22 | item: items.map((item) => ({ 23 | title: item.name, 24 | description: `${item.name}
${item.priceDesc ? `${item.pricePrefix}${item.priceSymbol}${item.priceDesc[0]}` : ''}

APP 内打开`, 25 | link: item.itemUrlForH5, 26 | })), 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/bilibili/page.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const aid = ctx.params.aid; 5 | const response = await got({ 6 | method: 'get', 7 | url: `https://api.bilibili.com/x/web-interface/view?aid=${aid}`, 8 | headers: { 9 | Referer: `https://www.bilibili.com/video/av${aid}`, 10 | }, 11 | }); 12 | 13 | const { title: name, pages: data } = response.data.data; 14 | 15 | ctx.state.data = { 16 | title: `视频 ${name} 的选集列表`, 17 | link: `https://www.bilibili.com/video/av${aid}`, 18 | description: `视频 ${name} 的视频选集列表`, 19 | item: data.map((item) => ({ 20 | title: item.part, 21 | description: `${item.part} - ${name}`, 22 | pubDate: new Date().toUTCString(), 23 | link: `https://www.bilibili.com/video/av${aid}?p=${item.page}`, 24 | })), 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/bilibili/reply.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cache = require('./cache'); 3 | 4 | module.exports = async (ctx) => { 5 | const aid = ctx.params.aid; 6 | const name = await cache.getVideoNameFromAid(ctx, aid); 7 | 8 | const response = await got({ 9 | method: 'get', 10 | url: `https://api.bilibili.com/x/v2/reply?type=1&oid=${aid}&sort=0`, 11 | headers: { 12 | Referer: `https://www.bilibili.com/video/av${aid}`, 13 | }, 14 | }); 15 | 16 | const data = response.data.data.replies; 17 | 18 | ctx.state.data = { 19 | title: `${name} 的 评论`, 20 | link: `https://www.bilibili.com/video/av${aid}`, 21 | description: `${name} 的评论`, 22 | item: data.map((item) => ({ 23 | title: `${item.member.uname} : ${item.content.message}`, 24 | description: `#${item.floor}
${item.member.uname} : ${item.content.message}`, 25 | pubDate: new Date(item.ctime * 1000).toUTCString(), 26 | link: `https://www.bilibili.com/video/av${aid}/#reply${item.rpid}`, 27 | })), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/bing/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | baseUrl: 'https://cn.bing.com', 7 | url: '/HPImageArchive.aspx', 8 | params: { 9 | format: 'js', 10 | idx: 0, 11 | n: 7, 12 | mkt: 'zh-CN', 13 | }, 14 | }); 15 | const data = response.data; 16 | ctx.state.data = { 17 | title: 'Bing每日壁纸', 18 | link: `https://cn.bing.com/`, 19 | item: data.images.map((item) => ({ 20 | title: item.copyright, 21 | description: ``, 22 | link: item.copyrightlink, 23 | })), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/bishijie/kuaixun.js: -------------------------------------------------------------------------------- 1 | const buildData = require('@/utils/common-config'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = `https://www.bishijie.com/kuaixun/`; 5 | const host = `https://www.bishijie.com`; 6 | ctx.state.data = await buildData({ 7 | link, 8 | url: link, 9 | title: `%title%`, 10 | params: { 11 | title: '币世界快讯列表', 12 | host, 13 | }, 14 | item: { 15 | item: '.livetop ul', 16 | title: `$('li.lh32 h2 a').text()`, 17 | link: `'%host%' + $('li.lh32 h2 a').attr('href')`, 18 | description: `$('li.lh32 div a').html()`, 19 | pubDate: `new Date($('li.lh32').parent().attr('id')*1000).toUTCString()`, 20 | guid: `$('li.lh32').parent().data('id')`, 21 | }, 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/blogread/newest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = 'http://blogread.cn/news/newest.php'; 6 | const response = await got({ 7 | method: 'get', 8 | url, 9 | }); 10 | const $ = cheerio.load(response.data); 11 | const resultItem = $('.media') 12 | .map((index, elem) => { 13 | elem = $(elem); 14 | const $link = elem.find('dt a'); 15 | 16 | return { 17 | title: $link.text(), 18 | description: elem 19 | .find('dd') 20 | .eq(0) 21 | .text(), 22 | link: $link.attr('href'), 23 | author: elem 24 | .find('.small a') 25 | .eq(0) 26 | .text(), 27 | }; 28 | }) 29 | .get(); 30 | 31 | ctx.state.data = { 32 | title: '技术头条', 33 | link: url, 34 | item: resultItem, 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /lib/routes/blogs/jingwei_link.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = 'https://jingwei.link/'; 6 | const response = await got({ 7 | method: 'get', 8 | url, 9 | }); 10 | const $ = cheerio.load(response.data); 11 | const resultItem = $("article[class='article-item']") 12 | .map((index, elem) => { 13 | elem = $(elem); 14 | 15 | const $esction = elem.find('section[class="post-preview"]'); 16 | 17 | return { 18 | title: $esction.find('h2').text(), 19 | description: $esction.find('h3').text(), 20 | link: $esction.find('a').attr('href'), 21 | author: '敬维', 22 | }; 23 | }) 24 | .get(); 25 | 26 | ctx.state.data = { 27 | title: '敬维-以认真的态度做完美的事情', 28 | link: url, 29 | item: resultItem, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/caixin/article.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'http://mapiv5.caixin.com//m/api/getWapIndexListByPage?page=1&callback=jQuery213030389065931348935_1560140003113&_=1560140003179', 7 | headers: { 8 | Referer: `http://mapiv5.caixin.com/`, 9 | Host: 'mapiv5.caixin.com', 10 | }, 11 | }); 12 | 13 | const reg = /(.*jQuery.*\()([\s\S]*)(\))/; 14 | const datatmp = response.data.replace(reg, '$2'); 15 | const data = JSON.parse(datatmp).data.list; 16 | 17 | ctx.state.data = { 18 | title: `财新网 - 首页`, 19 | link: `http://www.caixin.com/`, 20 | description: '财新网 - 首页', 21 | item: data.map((item) => ({ 22 | title: item.title, 23 | description: item.summary, 24 | link: item.web_url, 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/cctv/category.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx) => { 2 | const { category } = ctx.params; 3 | let responseData; 4 | 5 | if (category === 'mzzlbg') { 6 | // 每周质量报告 7 | const getMzzlbg = require('./utils/mzzlbg'); 8 | responseData = await getMzzlbg(); 9 | } else { 10 | // 央视新闻 11 | const getNews = require('./utils/news'); 12 | responseData = await getNews(category, ctx); 13 | } 14 | 15 | ctx.state.data = responseData; 16 | }; 17 | -------------------------------------------------------------------------------- /lib/routes/ciweimao/chapter.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `http://www.ciweimao.com/chapter-list/${id}/book_detail`, 10 | }); 11 | const $ = cheerio.load(response.data); 12 | 13 | const name = $('.book-catalog>.hd>h3').text(); 14 | 15 | const chapter_item = []; 16 | 17 | $('.book-chapter>.book-chapter-box>ul>li>a').each(function() { 18 | chapter_item.push({ 19 | title: $(this).text(), 20 | link: $(this).attr('href'), 21 | }); 22 | }); 23 | 24 | ctx.state.data = { 25 | title: `刺猬猫 ${name}`, 26 | link: `http://www.ciweimao.com/book/${id}`, 27 | item: chapter_item, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/cneb/guoneinews.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const list = (await got({ 5 | method: 'get', 6 | url: 'http://www.cneb.gov.cn/guoneinews/guoneidata/', 7 | })).data.rollData; 8 | 9 | const items = list.map((item) => { 10 | const single = { 11 | title: item.title, 12 | description: item.description.replace(//g, ''), 13 | pubDate: new Date(item.dateTime + ' UTC+08:00').toString(), 14 | link: item.url, 15 | }; 16 | return single; 17 | }); 18 | 19 | ctx.state.data = { 20 | title: '国内新闻_国家应急广播网', 21 | link: 'http://www.cneb.gov.cn/guoneinews/', 22 | description: '国家应急广播', 23 | item: items, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/cneb/yjxx.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const warningMessagesJsonp = (await got({ 5 | method: 'get', 6 | url: 'http://uc.cneb.gov.cn:8080/getWarningMessages?callback=func&start=0&pagetype=PAGE1409368873582889&rows=100&_=' + +new Date(), 7 | })).data.trim(); 8 | 9 | let warningMessages = []; 10 | 11 | try { 12 | const jsonString = warningMessagesJsonp.slice(5, -1); 13 | warningMessages = JSON.parse(jsonString).docs; 14 | } catch (error) { 15 | // console.error(error); 16 | } 17 | 18 | const items = warningMessages.map((item) => { 19 | const single = { 20 | title: item.brief, 21 | description: item.content, 22 | pubDate: new Date(item.publishdate + ' UTC+08:00').toString(), 23 | link: item.url, 24 | }; 25 | return single; 26 | }); 27 | 28 | ctx.state.data = { 29 | title: '预警信息_国家应急广播网', 30 | link: 'http://www.cneb.gov.cn/yjxx/', 31 | description: '国家应急广播', 32 | item: items, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/coolbuy/newest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://coolbuy.com/api/v1.4/product_preview/?order_by=-id&limit=20&page=0&offset=0', 7 | }); 8 | 9 | const data = response.data.objects; 10 | 11 | ctx.state.data = { 12 | title: '玩物志-最新', 13 | link: 'https://coolbuy.com/', 14 | description: '值得买的未来生活', 15 | item: data.map((item) => ({ 16 | title: item.title, 17 | link: item.visit_url, 18 | description: ` 19 |
20 |

21 | ${item.summary}
22 | 价格: ${item.price}元 23 | `, 24 | })), 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/dapenti/subject.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await utils.parseFeed({ subjectid: ctx.params.id }); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/routes/dapenti/tugua.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await utils.parseFeed({ subjectid: 70 }); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/routes/dockerhub/build.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { owner, image, tag = 'latest' } = ctx.params; 5 | 6 | const namespace = `${owner}/${image}`; 7 | 8 | const link = `https://hub.docker.com/r/${namespace}`; 9 | 10 | const data = await got.get(`https://hub.docker.com/v2/repositories/${namespace}/tags/?page_size=5`); 11 | const metadata = await got.get(`https://hub.docker.com/v2/repositories/${namespace}`); 12 | 13 | const list = data.data.results.filter((a) => a.name === tag); 14 | 15 | const out = list.map((item) => ({ 16 | title: `${namespace}:${tag} was built. ${(item.images[0].size / 1000000).toFixed(2)} MB`, 17 | link, 18 | author: owner, 19 | pubDate: new Date(item.last_updated).toUTCString(), 20 | guid: item.last_updated, 21 | })); 22 | 23 | ctx.state.data = { 24 | title: `${namespace}:${tag} build history`, 25 | description: metadata.description, 26 | link, 27 | item: out, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/donews/utils.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const got = require('@/utils/got'); 3 | const date = require('@/utils/date'); 4 | 5 | const ProcessFeed = async (link) => { 6 | const response = await got.get(link); 7 | 8 | const $ = cheerio.load(response.data); 9 | 10 | const meta = $($('#main .fl')[0]); 11 | 12 | return { 13 | title: $('h1').text() || $($('h2')[1]).text(), 14 | author: meta.find('span:nth-child(1)').text(), 15 | description: $('.article-con').html(), 16 | pubDate: date(meta.find('span:nth-child(2)').text(), 8), 17 | link, 18 | }; 19 | }; 20 | 21 | module.exports = { 22 | ProcessFeed, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/dongqiudi/player_news.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | const link = `https://www.dongqiudi.com/player/${id}.html`; 6 | const api = `https://www.dongqiudi.com/data/person/archive?person=${id}`; 7 | 8 | await utils.ProcessFeed(ctx, link, api); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/routes/dongqiudi/team_news.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const team = ctx.params.team; 5 | const link = `https://www.dongqiudi.com/team/${team}.html`; 6 | const api = `https://www.dongqiudi.com/data/team/archive?team=${team}`; 7 | 8 | await utils.ProcessFeed(ctx, link, api); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/routes/douban/bookstore.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = 'https://market.douban.com/book/'; 5 | const response = await got({ 6 | method: 'get', 7 | url: 'https://market.douban.com/api/freyr/books?page=1&page_size=20&type=book', 8 | headers: { 9 | Referer: link, 10 | }, 11 | }); 12 | 13 | const data = response.data.data; 14 | 15 | ctx.state.data = { 16 | title: '豆瓣书店', 17 | link, 18 | description: '在豆瓣书店,遇见美好·書生活', 19 | item: data.map(({ title, url, price, square_pic, rectangle_pic, desc }) => ({ 20 | title, 21 | link: url, 22 | description: ` 23 |
24 |
25 | ${desc}
26 | 价格: ${price}元 27 | `, 28 | })), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/douban/event/hot.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { locationId = 0 } = ctx.params; 5 | const referer = 'https://m.douban.com/app_topic/event_hot'; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `https://m.douban.com/rexxar/api/v2/subject_collection/event_hot/items?os=ios&for_mobile=1&callback=&start=0&count=20&loc_id=${locationId}`, 10 | headers: { 11 | Referer: referer, 12 | }, 13 | }); 14 | 15 | ctx.state.data = { 16 | title: `豆瓣同城-热门活动-${locationId}`, 17 | link: referer, 18 | item: response.data.subject_collection_items.map(({ title, url, cover, subtype, info, price_range }) => { 19 | const description = `
20 | ${info}/${subtype}/${price_range} 21 | `; 22 | 23 | return { 24 | title, 25 | description, 26 | link: url, 27 | }; 28 | }), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/douban/later.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://api.douban.com/v2/movie/coming_soon', 7 | }); 8 | const movieList = response.data.subjects; 9 | 10 | ctx.state.data = { 11 | title: '即将上映的电影', 12 | link: 'https://movie.douban.com/cinema/later/', 13 | item: movieList.map((item) => ({ 14 | title: item.title, 15 | description: `标题:${item.title}
影片类型:${item.genres.join(' | ')}
评分:${item.rating.stars === '00' ? '无' : item.rating.average}
`, 16 | link: item.alt, 17 | })), 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/routes/douban/latest_book.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | const url = 'https://book.douban.com/latest'; 5 | 6 | module.exports = async (ctx) => { 7 | const res = await got.get(url); 8 | const $ = cheerio.load(res.data); 9 | const list = $('#content') 10 | .find('li') 11 | .get(); 12 | ctx.state.data = { 13 | title: '豆瓣新书速递', 14 | link: url, 15 | item: list.map((item, index) => ({ 16 | title: `${index < 20 ? '[虚构类]' : '[非虚构类]'}${$(item) 17 | .find('h2') 18 | .text() 19 | .trim()}`, 20 | link: $(item) 21 | .find('a') 22 | .first() 23 | .attr('href'), 24 | description: $(item).html(), 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/douban/ustop.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://api.douban.com/v2/movie/us_box', 7 | }); 8 | const movieList = response.data.subjects; 9 | 10 | ctx.state.data = { 11 | title: '豆瓣电影北美票房榜', 12 | link: 'https://movie.douban.com/chart', 13 | item: movieList.map((item) => { 14 | item = item.subject; 15 | return { 16 | title: item.title, 17 | description: `标题:${item.title}
影片类型:${item.genres.join(' | ')}
评分:${item.rating.stars === '00' ? '无' : item.rating.average}
`, 18 | link: item.alt, 19 | }; 20 | }), 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/douyu/room.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `http://open.douyucdn.cn/api/RoomApi/room/${id}`, 9 | headers: { 10 | Referer: `https://www.douyu.com/${id}`, 11 | }, 12 | }); 13 | 14 | const data = response.data.data; 15 | 16 | let item; 17 | if (data.online !== 0) { 18 | item = [ 19 | { 20 | title: `开播: ${data.room_name}`, 21 | pubDate: new Date(data.start_time).toUTCString(), 22 | guid: data.start_time, 23 | link: `https://www.douyu.com/${id}`, 24 | }, 25 | ]; 26 | } 27 | 28 | ctx.state.data = { 29 | title: `${data.owner_name}的斗鱼直播间`, 30 | link: `https://www.douyu.com/${id}`, 31 | item: item, 32 | allowEmpty: true, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/dribbble/keyword.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const keyword = ctx.params.keyword; 5 | 6 | ctx.state.data = await utils.getData(`Keyword ${keyword}`, `https://dribbble.com/search?q=${keyword}&s=latest`); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/dribbble/popular.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const timeframe = ctx.params.timeframe; 5 | 6 | ctx.state.data = await utils.getData('Popular Shots', `https://dribbble.com/shots${timeframe ? `?timeframe=${timeframe}` : ''}`); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/dribbble/user.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const name = ctx.params.name; 5 | 6 | ctx.state.data = await utils.getData(name, `https://dribbble.com/${name}`); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/dysfz/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const responseData = (await got.get(`http://www.wuhaozhan.net/movie/list/?p=1`)).data; 6 | const $ = cheerio.load(responseData); 7 | const list = $('.pure-u-16-24').get(); 8 | const data = { 9 | title: '电影首发站', 10 | link: 'http://www.wuhaozhan.net/movie/list/', 11 | description: '高清电影', 12 | item: list.map((item) => ({ 13 | title: $(item) 14 | .find('h2') 15 | .text(), 16 | description: $(item) 17 | .find('.l-des') 18 | .text(), 19 | pubDate: new Date( 20 | $(item) 21 | .find('.dt') 22 | .text() 23 | ).toUTCString(), 24 | link: $(item) 25 | .find('.l-a') 26 | .attr('href'), 27 | })), 28 | }; 29 | ctx.state.data = data; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/ebb/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const lzstring = require('lz-string'); 3 | module.exports = async (ctx) => { 4 | const url = 'https://ebb.io/_/anime_list'; 5 | const response = await got({ 6 | method: 'get', 7 | url: url, 8 | headers: { 9 | Referer: url, 10 | }, 11 | return: 'string', 12 | }); 13 | const responseData = JSON.parse(lzstring.decompressFromUTF16(response.data)); 14 | const result = responseData.map((item) => ({ 15 | title: item.name_chi, 16 | link: `https://ebb.io/anime/${item.anime_id}x${item.season_id}`, 17 | description: `${item.season_title} - ${item.episode_title}`, 18 | guid: `${item.anime_id}-${item.season_id}-${item.episode_title}`, 19 | })); 20 | ctx.state.data = { title: 'ebb.io', link: 'https://ebb.io', description: '最新連載', item: result }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/eleme/open-be/announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const data = (await got({ 5 | method: 'get', 6 | url: 'https://open-be.ele.me/dev/notice/list?curpage=1&perpage=200', 7 | })).data; 8 | 9 | ctx.state.data = { 10 | title: '饿百零售开放平台-公告', 11 | link: 'https://open-be.ele.me/dev/notice', 12 | item: data.data.body.notice_info.map((item) => ({ 13 | title: item.title, 14 | description: `更新时间: ${item.update_time}`, 15 | pubDate: item.create_time, 16 | link: `https://open-be.ele.me/dev/notice?id=${item.id}`, 17 | })), 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/routes/eztv/imdb.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { imdb_id } = ctx.params; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `https://eztv.io/api/get-torrents?imdb_id=${imdb_id}`, 9 | headers: { 10 | Referer: 'https://eztv.io', 11 | }, 12 | }); 13 | 14 | const { torrents } = response.data; 15 | 16 | ctx.state.data = { 17 | title: `EZTV's Torrents of IMBD ID: ${imdb_id}`, 18 | link: 'https://eztv.io', 19 | description: `EZTV's Torrents of IMBD ID: ${imdb_id}`, 20 | item: torrents.map((item) => ({ 21 | title: item.title, 22 | description: ``, 23 | enclosure_url: item.magnet_url, 24 | enclosure_type: 'application/x-bittorrent', 25 | pubDate: new Date(item.date_released_unix * 1000).toUTCString(), 26 | link: item.torrent_url, 27 | })), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/facebook/article.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (url) => { 5 | const { data: html } = await got.get(url); 6 | const $ = cheerio.load(html); 7 | const $ct = $($('#m_story_permalink_view').get(0)).find('div>div>div>div>p'); 8 | $ct.find('br').replaceWith('\n'); 9 | const content = $ct 10 | .map((i, p) => $(p).text()) 11 | .toArray() 12 | .join('\n'); 13 | const imgs = $ct 14 | .parent() 15 | .next() 16 | .find('img') 17 | .toArray() 18 | .map((img) => $(img).attr('src')); 19 | const { searchParams: q } = new URL(url); 20 | return { url: `https://www.facebook.com/story.php?story_fbid=${q.get('story_fbid')}&id=${q.get('id')}`, html, title: $($('h3 strong a').get(0)).text(), content, imgs }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/fir/update.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | const APIUrl = `https://download.fir.im/${id}`; 6 | 7 | const webUrl = `https://fir.im/${id}`; 8 | 9 | const res = await got({ 10 | method: 'get', 11 | url: APIUrl, 12 | headers: { 13 | Referer: webUrl, 14 | }, 15 | }); 16 | 17 | const data = res.data; 18 | 19 | const title = data.app.name + ' 更新'; 20 | 21 | const item = {}; 22 | item.title = data.app.name + ' ' + data.app.releases.master.version + '(' + data.app.releases.master.build + ')'; 23 | item.description = data.app.releases.master.changelog; 24 | item.link = `https://fir.im/x9zu?release_id=${data.app.releases.master.id}`; 25 | item.pubDate = new Date(data.app.releases.master.created_at * 1000).toUTCString(); 26 | ctx.state.data = { 27 | title: title, 28 | link: webUrl, 29 | item: [item], 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/firefox/release.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { platform } = ctx.params; 6 | let devicePlatform = platform.replace('-', '/'); 7 | if (devicePlatform === 'desktop') { 8 | devicePlatform = ''; 9 | } 10 | 11 | const link = `https://www.mozilla.org/en-US/firefox/${devicePlatform}/notes/`; 12 | const response = await got.get(link); 13 | const $ = cheerio.load(response.data); 14 | const version = $('.c-release-version').text(); 15 | const pubDate = new Date($('.c-release-date').text()).toUTCString(); 16 | 17 | ctx.state.data = { 18 | title: `Firefox ${platform} release note`, 19 | link, 20 | item: [ 21 | { 22 | title: `Firefox ${platform} ${version} release note`, 23 | link, 24 | description: $('.c-release-notes').html(), 25 | guid: `${platform} ${version}`, 26 | pubDate, 27 | }, 28 | ], 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/fitchratings/site.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const type = ctx.params.type; 6 | 7 | const link = `https://www.fitchratings.com/site/${type}`; 8 | const listData = await got.get(link); 9 | const $list = cheerio.load(listData.data); 10 | ctx.state.data = { 11 | title: `${type} - 惠誉评级`, 12 | link, 13 | item: await Promise.all( 14 | $list('div.card-text-container') 15 | .slice(0, 10) 16 | .map(async (_, el) => { 17 | const $el = $list(el); 18 | const $a = $el.find('h4 a'); 19 | 20 | const href = $a.attr('href'); 21 | const title = $a.text(); 22 | const description = $el.find('div p').text(); 23 | 24 | return { 25 | title: title, 26 | description, 27 | link: href, 28 | }; 29 | }) 30 | .get() 31 | ), 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/routes/ft/channel.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await utils.getData({ 5 | site: ctx.params.language === 'chinese' ? 'www' : 'big5', 6 | channel: ctx.params.channel, 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/routes/galgame/sayhuahuo.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const res = await got({ 5 | method: 'get', 6 | url: 'https://index.say-huahuo.com/vn.json', 7 | }); 8 | 9 | const data = JSON.parse(res.body).reverse(); 10 | 11 | ctx.state.data = { 12 | title: 'galgame汉化硬盘galgame资源下载-花火学园论坛', 13 | link: 'https://index.say-huahuo.com/', 14 | description: '花火学园', 15 | item: data.map((item) => ({ 16 | title: item.title, 17 | description: ``, 18 | pubDate: new Date(parseInt(item.date.substr(0, 4)), parseInt(item.date.substr(4, 2)), parseInt(item.date.substr(6, 2))).toUTCString(), 19 | link: item.url, 20 | })), 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/galgame/zdfx.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const host = 'https://bbs.zdfx.net/'; 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: host, 9 | }); 10 | const $ = cheerio.load(response.data); 11 | const list = $('.slideother a'); 12 | 13 | const process = list.map((index, item) => { 14 | const a = $(item); 15 | const img_tag = '.slideshow a:nth-child(' + (index + 1).toString() + ')'; 16 | 17 | return { 18 | title: a.text(), 19 | description: $(img_tag).html(), 20 | link: host + a.attr('href'), 21 | }; 22 | }); 23 | 24 | ctx.state.data = { 25 | title: '终点分享', 26 | link: host, 27 | description: '终点分享最新汉化通知', 28 | item: process.get(), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/gaoqing/latest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'https://gaoqing.fm/', 9 | headers: { 10 | Referer: 'https://gaoqing.fm/', 11 | }, 12 | }); 13 | 14 | const data = response.data; 15 | 16 | const $ = cheerio.load(data); 17 | const list = $('#result1 > li').get(); 18 | 19 | let result = await util.ProcessFeed(list, ctx.cache); 20 | result = result.slice(0, 10); 21 | 22 | ctx.state.data = { 23 | title: '高清电台', 24 | link: 'https://gaoqing.fm', 25 | description: $('meta[name="description"]').attr('content'), 26 | item: result, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/geekpark/breakingnews.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const url = 'https://mainssl.geekpark.net/api/v1/posts'; 5 | const link = 'https://www.geekpark.net'; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url, 10 | }); 11 | const data = response.data.posts; 12 | 13 | ctx.state.data = { 14 | title: '极客公园 - 资讯', 15 | description: 16 | '极客公园聚焦互联网领域,跟踪最新的科技新闻动态,关注极具创新精神的科技产品。目前涵盖前沿科技、游戏、手机评测、硬件测评、出行方式、共享经济、人工智能等全方位的科技生活内容。现有前沿社、挖App、深度报道、极客养成指南等多个内容栏目。', 17 | link, 18 | item: data.map(({ title, content, published_at, id }) => ({ 19 | title, 20 | link: `https://www.geekpark.net/news/${id}`, 21 | description: content, 22 | pubDate: new Date(published_at).toUTCString(), 23 | })), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/getitfree/search.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const qs = require('query-string'); 4 | const utils = require('./utils'); 5 | 6 | module.exports = async (ctx) => { 7 | const { keyword } = ctx.params; 8 | const link = 'https://getitfree.cn'; 9 | const query = { 10 | s: keyword, 11 | }; 12 | const res = await got(link, { 13 | query, 14 | }); 15 | const $ = cheerio.load(res.data); 16 | 17 | const item = utils.parseListItem($, '#page-content'); 18 | 19 | ctx.state.data = { 20 | title: `正版中国搜索 - ${keyword}`, 21 | description: `正版中国搜索 - ${keyword}`, 22 | link: `${link}?${qs.stringify(query)}`, 23 | item, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/github/branches.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const config = require('@/config'); 3 | 4 | module.exports = async (ctx) => { 5 | const user = ctx.params.user; 6 | const repo = ctx.params.repo; 7 | 8 | const host = `https://github.com/${user}/${repo}`; 9 | const url = `https://api.github.com/repos/${user}/${repo}/branches`; 10 | 11 | const response = await got({ 12 | method: 'get', 13 | url, 14 | params: { 15 | access_token: config.github && config.github.access_token, 16 | }, 17 | }); 18 | const data = response.data; 19 | 20 | ctx.state.data = { 21 | title: `${user}/${repo} Branches`, 22 | link: `${host}/branches/all`, 23 | item: data.map((item) => ({ 24 | title: item.name, 25 | description: item.name, 26 | link: `${host}/commits/${item.name}`, 27 | })), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/github/pulls.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const config = require('@/config'); 3 | const md = require('markdown-it')({ 4 | html: true, 5 | }); 6 | 7 | module.exports = async (ctx) => { 8 | const user = ctx.params.user; 9 | const repo = ctx.params.repo; 10 | 11 | const host = `https://github.com/${user}/${repo}/pulls`; 12 | const url = `https://api.github.com/repos/${user}/${repo}/pulls`; 13 | 14 | const response = await got({ 15 | method: 'get', 16 | url, 17 | params: { 18 | sort: 'created', 19 | access_token: config.github && config.github.access_token, 20 | }, 21 | }); 22 | const data = response.data; 23 | 24 | ctx.state.data = { 25 | title: `${user}/${repo} Pull requests`, 26 | link: host, 27 | item: data.map((item) => ({ 28 | title: item.title, 29 | description: md.render(item.body) || 'No description', 30 | pubDate: new Date(item.created_at).toUTCString(), 31 | link: `${host}/${item.number}`, 32 | })), 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/github/repos.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const config = require('@/config'); 3 | 4 | module.exports = async (ctx) => { 5 | const user = ctx.params.user; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `https://api.github.com/users/${user}/repos`, 10 | params: { 11 | sort: 'created', 12 | access_token: config.github && config.github.access_token, 13 | }, 14 | }); 15 | const data = response.data; 16 | ctx.state.data = { 17 | title: `${user}'s GitHub repositories`, 18 | link: `https://github.com/${user}`, 19 | item: 20 | data && 21 | data.map((item) => ({ 22 | title: item.name, 23 | description: item.description || 'No description', 24 | pubDate: new Date(item.created_at).toUTCString(), 25 | link: item.html_url, 26 | })), 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/gov/city/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx) => { 2 | const { name, category } = ctx.params; 3 | let url = ''; 4 | 5 | switch (name) { 6 | case 'nanjing': 7 | url = 'http://www.nanjing.gov.cn'; 8 | break; 9 | default: 10 | console.log('URL pattern not matched'); 11 | } 12 | 13 | if (url === '') { 14 | ctx.throw(404, 'Cannot find page'); 15 | return; 16 | } 17 | 18 | try { 19 | const getRSS = require(`./${name}`); 20 | const responseData = await getRSS(url, category); 21 | ctx.state.data = responseData; 22 | } catch (error) { 23 | console.error(error); 24 | ctx.throw(404, 'Cannot find page'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/gov/city/nanjing/index.js: -------------------------------------------------------------------------------- 1 | const getContent = require('./getContent'); 2 | 3 | const TOTAL_PAGE = 3; 4 | 5 | module.exports = async (homePage, category) => { 6 | let url = ''; 7 | 8 | switch (category) { 9 | case 'news': 10 | url = `${homePage}/njxx/`; 11 | break; 12 | case 'department': 13 | url = `${homePage}/bmdt/`; 14 | break; 15 | case 'district': 16 | url = `${homePage}/gqdt/`; 17 | break; 18 | case 'livelihood': 19 | url = `${homePage}/mszx/`; 20 | break; 21 | default: 22 | console.log('URL pattern not matched'); 23 | } 24 | 25 | if (url === '') { 26 | throw new Error('Nanjing, Cannot find page'); 27 | } 28 | 29 | const responseData = await getContent(url, TOTAL_PAGE); 30 | return responseData; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/gov/fmprc/fyrbt.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | const host = 'https://www.fmprc.gov.cn/web/wjdt_674879/fyrbt_674889/'; 6 | 7 | module.exports = async (ctx) => { 8 | const response = await got.get(host, { 9 | responseType: 'buffer', 10 | }); 11 | 12 | const $ = cheerio.load(response.data); 13 | 14 | const list = $('.rebox_news ul li').get(); 15 | 16 | const result = await util.ProcessFeed(list, ctx.cache); 17 | 18 | ctx.state.data = { 19 | title: '中华人民共和国外交部-发言人表态', 20 | link: host, 21 | description: '中华人民共和国外交部-发言人表态', 22 | item: result, 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/gov/province/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx) => { 2 | const { name, category } = ctx.params; 3 | let url = ''; 4 | 5 | switch (name) { 6 | case 'jiangsu': 7 | url = 'http://www.jiangsu.gov.cn'; 8 | break; 9 | default: 10 | console.log('URL pattern not matched'); 11 | } 12 | 13 | if (url === '') { 14 | ctx.throw(404, 'Cannot find page'); 15 | return; 16 | } 17 | 18 | try { 19 | const getRSS = require(`./${name}`); 20 | const responseData = await getRSS(url, category); 21 | ctx.state.data = responseData; 22 | } catch (error) { 23 | console.error(error); 24 | ctx.throw(404, 'Cannot find page'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/guanzhi/guanzhi.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { 5 | data: { data }, 6 | } = await got.get('https://interface.meiriyiwen.com/article/today'); 7 | 8 | ctx.state.data = { 9 | title: '观止', 10 | link: 'https://meiriyiwen.com/', 11 | description: '每天一篇精选优质短篇', 12 | item: [ 13 | { 14 | title: data.title, 15 | description: data.content, 16 | author: data.author, 17 | pubDate: new Date(`${data.date.curr.substr(0, 4)}-${data.date.curr.substr(4, 2)}-${data.date.curr.substr(6)} 00:00:01`).toUTCString(), 18 | guid: data.date.curr, 19 | link: 'https://meiriyiwen.com/', 20 | }, 21 | ], 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/guokr/scientific.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got.get('https://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=0'); 5 | 6 | const result = response.data.result; 7 | 8 | ctx.state.data = { 9 | title: '果壳网 科学人', 10 | link: 'https://www.guokr.com/scientific', 11 | description: '果壳网 科学人', 12 | item: result.map((item) => ({ 13 | title: item.title, 14 | description: `${item.summary}
`, 15 | pubDate: item.date_published, 16 | link: item.url, 17 | author: item.author.nickname, 18 | })), 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/routes/hexo/yilia.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const got = require('@/utils/got'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = `http://${ctx.params.url}`; 6 | const res = await got.get(url); 7 | const $ = cheerio.load(res.data); 8 | const authorInfo = $('.left-col').find('#header'); 9 | const title = authorInfo.find('.header-author').text(); 10 | const subtitle = authorInfo.find('.header-subtitle').text(); 11 | const articleNodeList = $('article'); 12 | const articles = Array.from(articleNodeList).map((article) => { 13 | const each = $(article); 14 | const titleEl = each.find('.article-title'); 15 | 16 | return { 17 | title: titleEl.text(), 18 | link: encodeURI(`${url}${titleEl.attr('href')}`), 19 | description: each.find('.article-entry').text(), 20 | pubDate: each.find('time').attr('datetime'), 21 | }; 22 | }); 23 | 24 | ctx.state.data = { 25 | title, 26 | link: url, 27 | description: subtitle, 28 | item: articles, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/huxiu/author.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const utils = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const { id } = ctx.params; 7 | const link = `https://www.huxiu.com/member/${id}.html`; 8 | const response = await got({ 9 | method: 'get', 10 | url: link, 11 | headers: { 12 | Referer: link, 13 | }, 14 | }); 15 | 16 | const $ = cheerio.load(response.data); 17 | const author = $('.user-name') 18 | .text() 19 | .trim(); 20 | 21 | const list = $('.message-box > .mod-art > a') 22 | .slice(0, 10) 23 | .get() 24 | .map((e) => $(e).attr('href')); 25 | 26 | const items = await utils.ProcessFeed(list, ctx.cache); 27 | 28 | const authorInfo = `虎嗅网 - ${author}`; 29 | ctx.state.data = { 30 | title: authorInfo, 31 | link, 32 | description: authorInfo, 33 | item: items, 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /lib/routes/huxiu/search.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const utils = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const { keyword } = ctx.params; 7 | const link = `https://www.huxiu.com/search.html?s=${encodeURIComponent(keyword)}&sort=dateline:desc`; 8 | 9 | const response = await got({ 10 | method: 'get', 11 | url: link, 12 | headers: { 13 | Referer: link, 14 | }, 15 | }); 16 | 17 | const data = response.data; 18 | const $ = cheerio.load(data); 19 | 20 | const list = $('.search-wrap-list-ul > li > h2 > a') 21 | .get() 22 | .map((e) => $(e).attr('href')); 23 | 24 | const items = await utils.ProcessFeed(list, ctx.cache); 25 | 26 | const info = `虎嗅网 - ${keyword}`; 27 | ctx.state.data = { 28 | title: info, 29 | link, 30 | description: info, 31 | item: items, 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/routes/huxiu/tag.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const utils = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const { id } = ctx.params; 7 | const link = `https://www.huxiu.com/tags/${id}.html`; 8 | const response = await got({ 9 | method: 'get', 10 | url: link, 11 | headers: { 12 | Referer: link, 13 | }, 14 | }); 15 | 16 | const data = response.data; 17 | const $ = cheerio.load(data); 18 | const list = $('.related-article li a') 19 | .get() 20 | .map((e) => $(e).attr('href')); 21 | 22 | const items = await utils.ProcessFeed(list, ctx.cache); 23 | 24 | const info = `虎嗅 - ${$('.tag-title').text()}`; 25 | ctx.state.data = { 26 | title: info, 27 | link, 28 | description: info, 29 | item: items, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/huya/live.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id; 6 | const url = `https://www.huya.com/${id}`; 7 | const response = await got({ 8 | method: 'get', 9 | url: url, 10 | }); 11 | 12 | const $ = cheerio.load(response.data); 13 | const timestamp = parseInt(response.data.match(/"startTime":"?(\d+)?/)[1]) * 1000; 14 | 15 | let item; 16 | if (response.data.match(/"isOn":(\w{4})/)[1] === 'true') { 17 | item = [ 18 | { 19 | title: $('#J_roomTitle').text(), 20 | guid: timestamp, 21 | pubDate: new Date(timestamp).toUTCString(), 22 | link: url, 23 | }, 24 | ]; 25 | } 26 | 27 | ctx.state.data = { 28 | title: `${$('.host-name').text()}的虎牙直播`, 29 | link: url, 30 | item: item, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/infoq/recommend.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const utils = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const apiUrl = 'https://www.infoq.cn/public/v1/my/recommond'; 6 | const pageUrl = 'https://www.infoq.cn'; 7 | 8 | const resp = await got({ 9 | method: 'post', 10 | url: apiUrl, 11 | headers: { 12 | Referer: pageUrl, 13 | }, 14 | json: true, 15 | data: { 16 | size: 5, 17 | }, 18 | }); 19 | 20 | const data = resp.data.data; 21 | const items = await utils.ProcessFeed(data, ctx.cache); 22 | 23 | ctx.state.data = { 24 | title: 'InfoQ推荐', 25 | link: pageUrl, 26 | description: 'InfoQ推荐', 27 | item: items, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/instapaper/person.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const name = ctx.params.name; 6 | const link = `https://www.instapaper.com/p/${name}`; 7 | 8 | const response = await got.get(link); 9 | const $ = cheerio.load(response.data); 10 | 11 | const out = $('article.article_item.article_browse') 12 | .slice(0, 10) 13 | .map(function() { 14 | const info = { 15 | title: $(this) 16 | .find('div.js_title_row.title_row a') 17 | .attr('title'), 18 | link: $(this) 19 | .find('div.js_title_row.title_row a') 20 | .attr('href'), 21 | description: $(this) 22 | .find('div.article_preview') 23 | .text(), 24 | }; 25 | return info; 26 | }) 27 | .get(); 28 | 29 | ctx.state.data = { 30 | title: `${name}-Instapaper分享`, 31 | link: link, 32 | item: out, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/iplay/home.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const url = `https://www.iplaysoft.com/`; 7 | const response = await got({ 8 | method: 'get', 9 | url: url, 10 | headers: { 11 | Referer: url, 12 | }, 13 | }); 14 | 15 | const $ = cheerio.load(response.data); 16 | const list = $('#postlist .entry').get(); 17 | 18 | const result = await util.ProcessFeed(list, ctx.cache); 19 | 20 | ctx.state.data = { 21 | title: $('title') 22 | .text() 23 | .split('-')[0], 24 | link: url, 25 | description: $('meta[name="description"]').attr('content'), 26 | item: result, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/itjuzi/invest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://www.itjuzi.com/api/index/invse', 7 | }); 8 | 9 | const data = response.data.data; 10 | 11 | ctx.state.data = { 12 | title: 'IT桔子-投融资事件', 13 | link: 'https://www.itjuzi.com/', 14 | item: data.map((item) => { 15 | const invest = item.invst.map((item) => item.name).join('、'); 16 | 17 | return { 18 | title: `${item.name} / ${item.round} / ${item.money}`, 19 | link: `https://www.itjuzi.com/company/${item.invse_com_id}`, 20 | description: ` 21 |

22 | ${item.name}
23 | ${item.slogan}
24 | ${item.round} / ${item.money} / ${item.time}
25 | 投资方: ${invest} 26 | `, 27 | pubDate: new Date(item.time).toUTCString(), 28 | guid: item.id, 29 | }; 30 | }), 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/itjuzi/merge.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://www.itjuzi.com/api/index/merge', 7 | }); 8 | 9 | const data = response.data.data; 10 | 11 | ctx.state.data = { 12 | title: 'IT桔子-并购事件', 13 | link: 'https://www.itjuzi.com/', 14 | item: data.map((item) => { 15 | const party = item.party.map((item) => item.name || item.invst_name).join('、'); 16 | 17 | return { 18 | title: `${item.name}-${item.slogan}`, 19 | link: `https://www.itjuzi.com/merger/${item.id}`, 20 | description: ` 21 |

22 | ${item.name}
23 | ${item.slogan}
24 | 股权占比: ${item.ratio} / 金额: ${item.money} / ${item.time}
25 | 并购方: ${party} 26 | `, 27 | pubDate: new Date(item.time).toUTCString(), 28 | guid: item.id, 29 | }; 30 | }), 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/javbus/genre.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { gid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.com/genre/${gid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/home.js: -------------------------------------------------------------------------------- 1 | const { createHandler } = require('./util'); 2 | 3 | module.exports = createHandler('https://www.javbus.com/'); 4 | -------------------------------------------------------------------------------- /lib/routes/javbus/series.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { seriesid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.com/series/${seriesid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/star.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { sid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.com/star/${sid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/uncensored/genre.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { gid } = ctx.params; 5 | ctx.state.data = await getPage(`https://www.javbus.com/uncensored/genre/${gid}`, ctx); 6 | }; 7 | -------------------------------------------------------------------------------- /lib/routes/javbus/uncensored/home.js: -------------------------------------------------------------------------------- 1 | const { createHandler } = require('../util'); 2 | 3 | module.exports = createHandler('https://www.javbus.com/uncensored'); 4 | -------------------------------------------------------------------------------- /lib/routes/javbus/uncensored/series.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { seriesid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.com/uncensored/series/${seriesid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/uncensored/star.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { sid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.com/uncensored/star/${sid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/western/genre.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { gid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.work/genre/${gid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/western/home.js: -------------------------------------------------------------------------------- 1 | const { createHandler } = require('../util'); 2 | 3 | module.exports = createHandler('https://www.javbus.work/'); 4 | -------------------------------------------------------------------------------- /lib/routes/javbus/western/series.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { seriesid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.work/series/${seriesid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/javbus/western/star.js: -------------------------------------------------------------------------------- 1 | const { getPage } = require('../util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { sid } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://www.javbus.work/star/${sid}`, ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/jianshu/collection.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const id = ctx.params.id; 7 | 8 | const response = await got({ 9 | method: 'get', 10 | url: `https://www.jianshu.com/c/${id}`, 11 | headers: { 12 | Referer: `https://www.jianshu.com/c/${id}`, 13 | }, 14 | }); 15 | 16 | const data = response.data; 17 | 18 | const $ = cheerio.load(data); 19 | const list = $('.note-list li').get(); 20 | 21 | const result = await util.ProcessFeed(list, ctx.cache); 22 | 23 | ctx.state.data = { 24 | title: $('title').text(), 25 | link: `https://www.jianshu.com/c/${id}`, 26 | description: $('meta[name="description"]').attr('content') || $('title').text(), 27 | item: result, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/jianshu/home.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'https://www.jianshu.com', 9 | headers: { 10 | Referer: 'https://www.jianshu.com', 11 | }, 12 | }); 13 | 14 | const data = response.data; 15 | 16 | const $ = cheerio.load(data); 17 | const list = $('.note-list li').get(); 18 | 19 | const result = await util.ProcessFeed(list, ctx.cache); 20 | 21 | ctx.state.data = { 22 | title: '简书首页', 23 | link: 'https://www.jianshu.com', 24 | description: $('meta[name="description"]').attr('content'), 25 | item: result, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/jianshu/trending.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const link = `https://www.jianshu.com/trending/${ctx.params.timeframe}`; 7 | const response = await got({ 8 | method: 'get', 9 | url: link, 10 | headers: { 11 | Referer: link, 12 | }, 13 | }); 14 | 15 | const data = response.data; 16 | 17 | const $ = cheerio.load(data); 18 | const list = $('.note-list li').get(); 19 | 20 | const result = await util.ProcessFeed(list, ctx.cache); 21 | 22 | ctx.state.data = { 23 | title: `简书 ${ctx.params.timeframe === 'weekly' ? '7' : '30'} 日热门`, 24 | link, 25 | description: `简书 ${ctx.params.timeframe === 'weekly' ? '7' : '30'} 日热门`, 26 | item: result, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/jianshu/user.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const id = ctx.params.id; 7 | 8 | const response = await got({ 9 | method: 'get', 10 | url: `https://www.jianshu.com/u/${id}`, 11 | headers: { 12 | Referer: `https://www.jianshu.com/u/${id}`, 13 | }, 14 | }); 15 | 16 | const data = response.data; 17 | 18 | const $ = cheerio.load(data); 19 | const list = $('.note-list li').get(); 20 | 21 | const result = await util.ProcessFeed(list, ctx.cache); 22 | 23 | ctx.state.data = { 24 | title: $('title').text(), 25 | link: `https://www.jianshu.com/u/${id}`, 26 | description: $('meta[name="description"]').attr('content') || $('title').text(), 27 | item: result, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/jinritoutiao/keyword.js: -------------------------------------------------------------------------------- 1 | const got = require('../../utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const keyword = ctx.params.keyword; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `https://www.toutiao.com/api/search/content/?offset=0&format=json&keyword=${encodeURIComponent(keyword)}&autoload=true&count=20&cur_tab=1&from=search_tab`, 9 | headers: { 10 | Referer: `https://www.toutiao.com/search/?keyword=${encodeURIComponent(keyword)}`, 11 | }, 12 | }); 13 | let data = response.data.data; 14 | data = data.filter(function(item) { 15 | return !item.cell_type; 16 | }); 17 | 18 | ctx.state.data = { 19 | title: `今日头条: ${keyword}`, 20 | link: `https://www.toutiao.com/search/?keyword=${keyword}`, 21 | description: `${keyword}`, 22 | item: data.map((item) => ({ 23 | title: `${item.media_name}: ${item.title}`, 24 | description: `${item.abstract}`, 25 | pubDate: `${new Date(parseInt(item.create_time) * 1000)}`, 26 | link: `${item.article_url}`, 27 | })), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/jpmorganchase/research.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | const url = 'https://www.jpmorganchase.com'; 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: `${url}/corporate/institute/research.htm`, 9 | }); 10 | 11 | const title = 'All Reports'; 12 | const $ = cheerio.load(response.data); 13 | 14 | const items = $('.globalphilsect') 15 | .map((index, item) => { 16 | item = $(item); 17 | return { 18 | title: item.find('.chaseanalytics-track-link').text(), 19 | link: `${url}${item.find('.chaseanalytics-track-link').attr('href')}`, 20 | description: item.find('.lead-in + p').text(), 21 | pubDate: item.find('.lead-in').text(), 22 | }; 23 | }) 24 | .get(); 25 | ctx.state.data = { 26 | title: `${title} - JPMorgan Chase Institute`, 27 | link: url, 28 | description: `${title} - JPMorgan Chase Institute`, 29 | item: items, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/jskou/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const type = ctx.params.type; 5 | const response = await got(`http://jskou.com:3003/contents/list?type=${type || 0}&page=0&pageSize=30`); 6 | const item = response.data.data.map((item) => { 7 | const tag = item.tag ? `[${item.tag}]` : ''; 8 | return { 9 | title: item.title, 10 | description: `${tag} ${item.title}`, 11 | pubDate: item.time, 12 | link: item.link, 13 | }; 14 | }); 15 | ctx.state.data = { 16 | title: type === '0' ? '前端艺术家每日资讯整理' : '飞冰早报整理', 17 | link: 'http://fe.jskou.com/', 18 | description: '前端艺术家1群每日资讯,飞冰早报', 19 | item, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/juejin/books.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://xiaoce-timeline-api-ms.juejin.im/v1/getListByLastTime?uid=&client_id=&token=&src=web&alias=&pageNum=1', 7 | }); 8 | 9 | const data = response.data.d; 10 | 11 | ctx.state.data = { 12 | title: '掘金小册', 13 | link: 'https://juejin.im/books', 14 | item: data.map(({ title, id, img, desc, createdAt, price }) => ({ 15 | title, 16 | link: `https://juejin.im/book/${id}`, 17 | description: ` 18 |
19 | ${title}

20 | ${desc}
21 | 价格: ${price}元 22 | `, 23 | pubDate: new Date(createdAt).toUTCString(), 24 | guid: id, 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/juejin/posts.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const util = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `https://timeline-merger-ms.juejin.im/v1/get_entry_by_self?src=web&targetUid=${id}&type=post&order=createdAt`, 10 | headers: { 11 | Host: 'timeline-merger-ms.juejin.im', 12 | Origin: 'https://juejin.im', 13 | Referer: `https://juejin.im/user/${id}/posts`, 14 | }, 15 | }); 16 | const data = response.data.d.entrylist; 17 | const username = data && data[0] && data[0].user && data[0].user.username; 18 | const resultItems = await util.ProcessFeed(data, ctx.cache); 19 | 20 | ctx.state.data = { 21 | title: `掘金专栏-${username}`, 22 | link: `https://juejin.im/user/${id}/posts`, 23 | description: `掘金专栏-${username}`, 24 | item: resultItems, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/juejin/shares.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const util = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.userId; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: `https://timeline-merger-ms.juejin.im/v1/get_entry_by_self?src=web&targetUid=${id}&type=article&order=createdAt`, 10 | headers: { 11 | Host: 'timeline-merger-ms.juejin.im', 12 | Origin: 'https://juejin.im', 13 | Referer: `https://juejin.im/user/${id}/shares`, 14 | }, 15 | }); 16 | const data = response.data.d.entrylist; 17 | const username = data && data[0] && data[0].user && data[0].user.username; 18 | const resultItems = await util.ProcessFeed(data, ctx.cache); 19 | 20 | ctx.state.data = { 21 | title: `掘金分享-${username}`, 22 | link: `https://juejin.im/user/${id}/shares`, 23 | description: `掘金分享-${username}`, 24 | item: resultItems, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/kingkong/room.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | const url = `https://www.kingkong.com.tw/${id}`; 6 | 7 | const api = `https://api-kk.lv-play.com/webapi/v1/room/info?room_id=${id}`; 8 | const response = await got({ 9 | method: 'get', 10 | url: api, 11 | }); 12 | 13 | let item; 14 | const name = response.data.data.live_info.nickname; 15 | if (response.data.data.live_info.live_status === 1) { 16 | item = [ 17 | { 18 | title: name + '开播了', 19 | link: url, 20 | guid: response.data.data.live_info.live_id, 21 | description: response.data.data.live_info.describe, 22 | }, 23 | ]; 24 | } 25 | 26 | ctx.state.data = { 27 | title: `${name}的kingkong直播`, 28 | link: url, 29 | item: item, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/kirara/news.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got.get('https://kirarafantasia.com/wp-json/wp/v2/posts'); 5 | 6 | const posts = response.data || []; 7 | 8 | ctx.state.data = { 9 | title: 'NEWS|きららファンタジア 公式サイト', 10 | link: 'https://kirarafantasia.com/news/', 11 | description: '「まんがタイムきらら」の人気キャラクターたちが、RPGの世界に大集合!あなたの毎日が「きらら」でいっぱいに! #きらファン', 12 | item: posts.map((post) => { 13 | const image = ``; 14 | 15 | const html = image; 16 | 17 | const [y, m, d] = post.date.match(/\d+/g); 18 | const date = new Date(y, m - 1, d); 19 | 20 | return { 21 | title: post.title, 22 | link: post.url, 23 | pubDate: date.toUTCString(), 24 | published: date.toISOString(), 25 | description: html, 26 | content: { html }, 27 | }; 28 | }), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/laosiji/feed.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const parseDate = require('@/utils/date'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'post', 7 | url: 'https://www.laosiji.com/api/topic/feed/list', 8 | }); 9 | 10 | const data = response.data.body.sns.list; 11 | 12 | ctx.state.data = { 13 | title: '老司机-首页', 14 | link: 'http://www.laosiji.com/new_web/index.html', 15 | description: '老司机-首页', 16 | item: data.map(({ title, resourceid, image, publishtime }) => ({ 17 | title, 18 | link: `http://www.laosiji.com/thread/${resourceid}.html`, 19 | description: ``, 20 | pubDate: parseDate(publishtime, 8), 21 | })), 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/laosiji/hot.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'http://www.laosiji.com/thread/hotList', 7 | }); 8 | 9 | const data = response.data; 10 | 11 | ctx.state.data = { 12 | title: '老司机-24小时热门', 13 | link: 'http://www.laosiji.com/new_web/index.html', 14 | description: '老司机-24小时热门', 15 | item: data.map(({ title, description, id, imageInfo, createtime }) => ({ 16 | title: title === '' ? description : title, 17 | link: `http://www.laosiji.com/thread/${id}.html`, 18 | description: `${description}`, 19 | pubDate: new Date(createtime).toUTCString(), 20 | })), 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/laosiji/hotshow.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const parseDate = require('@/utils/date'); 3 | 4 | module.exports = async (ctx) => { 5 | const { id } = ctx.params; 6 | const link = `http://www.laosiji.com/hotshow/detail/${id}`; 7 | const response = await got({ 8 | method: 'post', 9 | url: 'http://www.laosiji.com/api/hotShow/program', 10 | headers: { 11 | Referer: link, 12 | }, 13 | form: true, 14 | data: { 15 | hotShowId: id, 16 | sort: 1, 17 | pageNo: 1, 18 | }, 19 | }); 20 | 21 | const data = response.data.body.hotshow; 22 | 23 | ctx.state.data = { 24 | title: `老司机-${data.name}`, 25 | link, 26 | item: data.sns.list.map(({ title, resourceid, image, publishtime }) => ({ 27 | title, 28 | link: `http://www.laosiji.com/thread/${resourceid}.html`, 29 | description: ``, 30 | pubDate: parseDate(publishtime, 8), 31 | })), 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/routes/liwushuo/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const baseUrl = 'https://www.liwushuo.com'; 5 | const link = `${baseUrl}/api/channels/1/items`; 6 | const res = await got(link, { 7 | query: { 8 | limit: 21, 9 | }, 10 | }); 11 | 12 | const item = res.data.data.items.map((item) => { 13 | const { content_url, cover_image_url, created_at, share_msg, title } = item; 14 | return { 15 | title, 16 | link: content_url, 17 | pubDate: new Date(created_at * 1000).toUTCString(), 18 | description: [``, share_msg].join('
'), 19 | }; 20 | }); 21 | 22 | ctx.state.data = { 23 | title: '礼物说 - 精选', 24 | description: '礼物说 - 精选', 25 | link: baseUrl, 26 | item, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/luogu/daily.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id || 92685; 6 | 7 | const link = `https://www.luogu.org/discuss/show/${id}`; 8 | const response = await got.get(link); 9 | const $ = cheerio.load(response.data); 10 | const title = $('#app-body-new div h1').text(); 11 | 12 | const out = $('div.am-comment-main > div > p') 13 | .slice(0, 10) 14 | .map(function() { 15 | const info = { 16 | title: 17 | $(this) 18 | .find('strong') 19 | .text() || $(this).text(), 20 | description: $(this).html(), 21 | link: $(this) 22 | .find('a') 23 | .attr('href'), 24 | }; 25 | return info; 26 | }) 27 | .get(); 28 | 29 | ctx.state.data = { 30 | title: title, 31 | link: link, 32 | item: out, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/manhuadb/comics.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const host = 'https://www.manhuadb.com'; 3 | const cheerio = require('cheerio'); 4 | 5 | module.exports = async (ctx) => { 6 | const id = ctx.params.id; 7 | const comicPage = host + `/manhua/${id}`; 8 | const response = await got({ 9 | method: 'get', 10 | url: comicPage, 11 | }); 12 | const data = response.data; 13 | const $ = cheerio.load(data); 14 | const list = $('li[data-sort] > a'); 15 | const comicTitle = $('.comic-title').text(); 16 | ctx.state.data = { 17 | title: '漫画DB - ' + comicTitle, 18 | link: comicPage, 19 | description: '漫画DB', 20 | item: list 21 | .map((i, item) => ({ 22 | title: $(item) 23 | .text() 24 | .trim(), 25 | description: $(item) 26 | .text() 27 | .trim(), 28 | link: host + $(item).attr('href'), 29 | })) 30 | .get(), 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/maoyan/hot.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://m.maoyan.com/ajax/movieOnInfoList', 7 | }); 8 | const data = response.data.movieList; 9 | const items = await Promise.all( 10 | data.map(async (item) => { 11 | const rating = item.sc > 0 ? `评分:${item.sc}` : ''; 12 | 13 | return { 14 | title: `${item.nm} ${rating}`, 15 | description: `
${rating}
演员:${item.star}
上映信息:${item.showInfo}`, 16 | link: `https://maoyan.com/films/${item.id}`, 17 | pubDate: new Date(item.rt).toUTCString(), 18 | }; 19 | }) 20 | ); 21 | 22 | ctx.state.data = { 23 | title: `猫眼电影 - 正在热映`, 24 | link: `https://maoyan.com/films`, 25 | description: `猫眼电影 - 正在热映`, 26 | item: items, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/maoyan/upcoming.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://m.maoyan.com/ajax/comingList?token=', 7 | }); 8 | const data = response.data.coming; 9 | const items = await Promise.all( 10 | data.map(async (item) => ({ 11 | title: `${item.nm} ${item.comingTitle}`, 12 | description: `
演员:${item.star}
上映信息:${item.showInfo || item.comingTitle}`, 13 | link: `https://maoyan.com/films/${item.id}`, 14 | pubDate: new Date(item.rt).toUTCString(), 15 | })) 16 | ); 17 | 18 | ctx.state.data = { 19 | title: `猫眼电影 - 正在热映`, 20 | link: `https://maoyan.com/films?showType=2`, 21 | description: `猫眼电影 - 正在热映`, 22 | item: items, 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/meipai/utils.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const url = require('url'); 3 | 4 | const ProcessFeed = async (list) => { 5 | const host = 'https://www.meipai.com'; 6 | 7 | return await Promise.all( 8 | list.map(async (item) => { 9 | const $ = cheerio.load(item); 10 | 11 | const $title = $('.detail-cover-title'); 12 | const $desciption = $('.feed-description'); 13 | 14 | // 详情页面的地址(视频页面地址) 15 | const itemUrl = url.resolve(host, $desciption.attr('href')); 16 | 17 | // RSS内容(美拍提供了友好的网页版视频展示) 18 | const imgSrc = $('.feed-v-wrap img').attr('src'); 19 | const text = $desciption.text() + ``; 20 | 21 | // 列表上提取到的信息 22 | return { 23 | title: $title.text(), 24 | description: text, 25 | link: itemUrl, 26 | author: $('.feed-name').text(), 27 | guid: itemUrl, 28 | }; 29 | }) 30 | ); 31 | }; 32 | 33 | module.exports = { 34 | ProcessFeed, 35 | }; 36 | -------------------------------------------------------------------------------- /lib/routes/mi/crowdfunding.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'post', 6 | url: 'http://api.m.mi.com/v1/microwd/home', 7 | headers: { 8 | 'Mishop-Client-Id': '180100031055', 9 | 'User-Agent': 'MiShop/4.3.68 (iPhone; iOS 12.0.1; Scale/3.00)', 10 | 'IOS-App-Version': '4.3.68', 11 | 'IOS-Version': 'system=12.0.1&device=iPhone10,3', 12 | }, 13 | }); 14 | let list = []; 15 | response.data.data.list && 16 | response.data.data.list.forEach((item) => { 17 | list = list.concat(item.items); 18 | }); 19 | 20 | ctx.state.data = { 21 | title: '小米众筹', 22 | link: '', 23 | item: 24 | list && 25 | list.map((item) => ({ 26 | title: item.product_name, 27 | description: `
价格:${item.product_price}元`, 28 | })), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/mi/youpin/crowdfunding.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const utils = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'get', 7 | url: 'https://home.mi.com/lasagne/page/5', 8 | }); 9 | 10 | const data = response.data.floors; 11 | 12 | ctx.state.data = { 13 | title: '小米有品众筹', 14 | link: 'https://home.mi.com/crowdfunding?&pageid=5', 15 | description: '小米有品众筹', 16 | item: utils.generateData(data), 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/routes/mi/youpin/new.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const utils = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'get', 7 | url: 'https://home.mi.com/lasagne/page/4', 8 | }); 9 | 10 | const data = response.data.floors; 11 | 12 | ctx.state.data = { 13 | title: '小米有品每日上新', 14 | link: 'https://home.mi.com/newproduct?pageid=4', 15 | description: '小米有品每日上新', 16 | item: utils.generateData(data), 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/routes/mlhang/latest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const api_url = 'https://www.mlhang.com/search/contents?start=0&count=10'; 3 | const host = 'https://www.mlhang.com'; 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: api_url, 9 | }); 10 | const data = response.data.data.docs; 11 | 12 | ctx.state.data = { 13 | title: '马良行|建筑行业知识分享平台', 14 | link: host, 15 | description: '马良行文章更新提醒', 16 | item: data.map((item) => ({ 17 | title: item['title.0.name'], 18 | description: item.detail, 19 | pubDate: new Date(item.updateTime * 1), 20 | link: host + item.resurl, 21 | })), 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/mobdata/report.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const date = require('@/utils/date'); 3 | 4 | module.exports = async (ctx) => { 5 | const apiUrl = 'http://admin.mob.com/api/mobdata/report/list'; 6 | const pageUrl = 'http://mobdata.mob.com/mobdata/report'; 7 | 8 | const resp = await got({ 9 | method: 'post', 10 | url: apiUrl, 11 | headers: { 12 | Referer: pageUrl, 13 | 'Content-Type': 'application/json', 14 | }, 15 | }); 16 | 17 | const list = resp.data.list; 18 | const items = list.map((item) => ({ 19 | title: item.title, 20 | description: `${item.desc}
查看报告`, 21 | pubDate: date(item.created_at), 22 | link: item.report_path, 23 | })); 24 | ctx.state.data = { 25 | title: 'MobData分析报告', 26 | link: pageUrl, 27 | description: 'MobData分析报告', 28 | item: items, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/monsterhunter/update.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = 'http://game.capcom.com/world/cn/infomation.html'; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url, 10 | }); 11 | 12 | const data = response.data; 13 | 14 | const $ = cheerio.load(data); 15 | const list = $('.version'); 16 | 17 | ctx.state.data = { 18 | title: '怪物猎人更新情报', 19 | link: url, 20 | item: 21 | list && 22 | list 23 | .map((index, item) => { 24 | item = $(item); 25 | return { 26 | title: item.find('h3').text(), 27 | description: item.children('div').html(), 28 | link: url, 29 | guid: item.find('h3').text(), 30 | }; 31 | }) 32 | .get(), 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/mzitu/category.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { getItem } = require('./util'); 3 | 4 | const map = { 5 | xinggan: '性感妹子', 6 | japan: '日本妹子', 7 | taiwan: '台湾妹子', 8 | mm: '清纯妹子', 9 | }; 10 | 11 | module.exports = async (ctx) => { 12 | const { category } = ctx.params; 13 | 14 | const url = `https://www.mzitu.com/${category}`; 15 | const response = await got({ 16 | method: 'get', 17 | url, 18 | }); 19 | const items = await getItem(ctx, response.data); 20 | 21 | ctx.state.data = { 22 | title: `妹子图-${map[category]} `, 23 | link: url, 24 | item: items, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/mzitu/home.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { getItem } = require('./util'); 3 | 4 | module.exports = async (ctx) => { 5 | const { type = '' } = ctx.params; 6 | let url; 7 | let title = '妹子图'; 8 | 9 | if (type === 'hot') { 10 | title += '-最热'; 11 | url = 'https://www.mzitu.com/hot/'; 12 | } else if (type === 'best') { 13 | title += '-推荐'; 14 | url = 'https://www.mzitu.com/best/'; 15 | } else { 16 | title += '-最新'; 17 | url = 'https://www.mzitu.com/'; 18 | } 19 | 20 | const response = await got({ 21 | method: 'get', 22 | url, 23 | }); 24 | 25 | const items = await getItem(ctx, response.data); 26 | 27 | ctx.state.data = { 28 | title: title, 29 | link: url, 30 | item: items, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/mzitu/tag.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const { getItem } = require('./util'); 4 | 5 | module.exports = async (ctx) => { 6 | let tag = ctx.params.tag; 7 | tag = tag === undefined || tag === 'undefined' ? '' : tag; 8 | 9 | const link = `https://www.mzitu.com/tag/${tag}`; 10 | const response = await got({ 11 | method: 'get', 12 | url: link, 13 | }); 14 | const $ = cheerio.load(response.data); 15 | const items = await getItem(ctx, response.data); 16 | 17 | let name = $('div.currentpath').text(); 18 | name = name.split('»')[1]; 19 | 20 | ctx.state.data = { 21 | title: name, 22 | link: link, 23 | item: items, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/mzitu/tags.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const link = 'https://www.mzitu.com/zhuanti'; 6 | 7 | const response = await got({ 8 | method: 'get', 9 | url: link, 10 | }); 11 | const $ = cheerio.load(response.data); 12 | const list = $('div.postlist > dl > dd').get(); 13 | 14 | ctx.state.data = { 15 | title: '妹子图专题', 16 | link: link, 17 | description: '妹子图美女专题栏目,为您精心准备各种美女图片专题,包括名站美女写真,妹子特点分类,美女大全等专题。', 18 | item: list.map((item) => ({ 19 | title: $(item) 20 | .find('img') 21 | .attr('alt'), 22 | link: $(item) 23 | .find('a') 24 | .attr('href'), 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/natgeo/dailyphoto.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const today = new Date(); 5 | const year = today.getFullYear(); 6 | const month = today.getMonth() + 1; 7 | 8 | const api = `https://www.nationalgeographic.com/content/photography/en_US/photo-of-the-day/_jcr_content/.gallery.${year}-${month}.json`; 9 | const response = await got.get(api); 10 | const items = response.data.items; 11 | 12 | const out = items.slice(0, 10).map((item) => { 13 | const info = { 14 | title: item.altText, 15 | author: item.credit, 16 | link: item['full-path-url'], 17 | description: `` + item.caption, 18 | }; 19 | return info; 20 | }); 21 | 22 | ctx.state.data = { 23 | title: 'Photo of the Day', 24 | link: 'https://www.nationalgeographic.com/photography/photo-of-the-day/', 25 | item: out, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/ncm/artist.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `https://music.163.com/api/artist/albums/${id}`, 9 | headers: { 10 | Referer: 'https://music.163.com/', 11 | }, 12 | }); 13 | 14 | const data = response.data; 15 | 16 | ctx.state.data = { 17 | title: data.artist.name, 18 | link: `https://music.163.com/#/artist/album?id=${id}`, 19 | description: `网易云音乐歌手专辑 - ${data.artist.name}`, 20 | item: data.hotAlbums.map((item) => { 21 | const singer = item.artists.length === 1 ? item.artists[0].name : item.artists.reduce((prev, cur) => (prev.name || prev) + '/' + cur.name); 22 | return { 23 | title: `${item.name} - ${singer}`, 24 | description: `歌手:${singer}
专辑:${item.name}
日期:${new Date(item.publishTime).toLocaleDateString()}
`, 25 | link: `https://music.163.com/#/album?id=${item.id}`, 26 | }; 27 | }), 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/nhentai/other.js: -------------------------------------------------------------------------------- 1 | const { getSimple, getDetails } = require('./util'); 2 | 3 | const supportedKeys = ['parody', 'character', 'tag', 'artist', 'group', 'language', 'category']; 4 | 5 | module.exports = async (ctx) => { 6 | const { keyword, key, mode } = ctx.params; 7 | const isSimple = mode !== 'detail'; 8 | 9 | if (supportedKeys.indexOf(key) === -1) { 10 | ctx.state.data = { 11 | title: 'nHentai - unsupported', 12 | }; 13 | return; 14 | } 15 | 16 | const simples = await getSimple(`https://nhentai.net/${key}/${keyword.toLowerCase().replace(' ', '-')}`); 17 | 18 | ctx.state.data = { 19 | title: `nHentai - ${key} - ${keyword}`, 20 | link: 'https://nhentai.net', 21 | description: 'hentai', 22 | item: isSimple ? simples : await getDetails(ctx.cache, simples), 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/nhentai/search.js: -------------------------------------------------------------------------------- 1 | const { getSimple, getDetails } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { keyword, mode } = ctx.params; 5 | const isSimple = mode !== 'detail'; 6 | 7 | const simples = await getSimple(`https://nhentai.net/search/?q=${keyword}`); 8 | 9 | ctx.state.data = { 10 | title: `nHentai - search - ${keyword}`, 11 | link: 'https://nhentai.net', 12 | description: 'hentai', 13 | item: isSimple ? simples : await getDetails(ctx.cache, simples), 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/routes/nintendo/eshop_jp.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: `https://search.nintendo.jp/nintendo_soft/search.json?opt_sshow=1&fq=ssitu_s%3Aonsale%20OR%20ssitu_s%3Apreorder%20OR%20memo_bg%3Aforced&limit=24&page=1&c=50310840317994813&opt_osale=1&opt_hard=1_HAC&sort=sodate%20desc%2Cscore`, 7 | }); 8 | const data = response.data.result.items.slice(0, 9); 9 | 10 | ctx.state.data = { 11 | title: `Nintendo eShop (日服) 新游戏`, 12 | link: `https://www.nintendo.co.jp/software/switch/index.html`, 13 | description: `Nintendo eShop (日服) 新上架的游戏`, 14 | item: data.map((item) => ({ 15 | title: item.title, 16 | description: `
发售日期:${item.pdate}
价格:${item.dprice}円`, 17 | link: `https://ec.nintendo.com/JP/ja/titles/${item.id}`, 18 | })), 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/routes/nintendo/news.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const util = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'get', 7 | url: `https://www.nintendo.com.hk/data/json/topics.json`, 8 | }); 9 | const data = response.data.filter((val) => val.only_for !== 'tw' && val.url.startsWith('/topics/article/')).slice(0, 9); 10 | 11 | // 获取新闻正文 12 | const result = await util.ProcessNews(data, ctx.cache); 13 | 14 | ctx.state.data = { 15 | title: `Nintendo (香港) 主页资讯`, 16 | link: `https://www.nintendo.com.hk/topics/index.html`, 17 | description: `Nintendo香港有限公司官网刊登的资讯`, 18 | item: result.map((item) => ({ 19 | title: item.title, 20 | description: item.content, 21 | link: `https://www.nintendo.com.hk${item.url}`, 22 | pubDate: item.data, 23 | })), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/nosetime/home.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = `https://www.nosetime.com/perfume.php`; 5 | 6 | const response = await got({ 7 | method: 'GET', 8 | url: 'https://www.nosetime.com/app/smart.php?page=1&type=discovery', 9 | headers: { 10 | Referer: link, 11 | }, 12 | }); 13 | 14 | const items = response.data.items.map((item) => ({ 15 | title: `${'★★★★★☆☆☆☆☆'.slice(5 - item.uwscore, 10 - item.uwscore)} ${item.ifullname}`, 16 | description: item.udcontent, 17 | pubDate: new Date(item.uface * 1000).toUTCString(), 18 | link: `https://www.nosetime.com/xiangshui/${item.iid}-${item.iurl}.html`, 19 | author: item.uname, 20 | })); 21 | 22 | ctx.state.data = { 23 | title: `香水时代`, 24 | link: link, 25 | description: '最新香水评论|发现香水圈的新鲜事 - 香水时代NoseTime.com', 26 | item: items, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/nvidia/webdriverupdate.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const plist = require('plist'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = 'https://gfe.nvidia.com/mac-update'; 6 | 7 | const res = await got({ 8 | method: 'get', 9 | url: url, 10 | }); 11 | 12 | const list = plist.parse(res.data).updates; 13 | 14 | const resultItem = list.map(function(i) { 15 | const item = {}; 16 | item.title = i.version; 17 | 18 | item.description = i.version; 19 | item.guid = i.checksum; 20 | item.link = i.downloadURL; 21 | return item; 22 | }); 23 | 24 | ctx.state.data = { 25 | title: 'Nvidia WebDriver Update', 26 | link: url, 27 | item: resultItem, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/paidai/utils.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const got = require('@/utils/got'); 3 | const date = require('@/utils/date'); 4 | 5 | const ProcessFeed = async (info) => { 6 | const title = info.title; 7 | const itemUrl = info.link; 8 | const itemDate = info.date; 9 | 10 | const response = await got.get(itemUrl); 11 | 12 | const $ = cheerio.load(response.data); 13 | const description = $('#topic_content') 14 | .html() 15 | .trim(); 16 | 17 | const single = { 18 | title: title, 19 | link: itemUrl, 20 | description: description, 21 | pubDate: date(itemDate), 22 | }; 23 | return single; 24 | }; 25 | 26 | module.exports = { 27 | ProcessFeed, 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/patchwork.kernel.org/cache.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = { 4 | getPatchnameFromID: async (ctx, id) => { 5 | const key = 'patchwork-kernel-org-patchname-from-id-' + id; 6 | let name = await ctx.cache.get(key); 7 | if (!name) { 8 | const patchDetail = await got({ 9 | method: 'get', 10 | url: `https://patchwork.kernel.org/api/patches/${id}/`, 11 | }); 12 | name = patchDetail.data.name; 13 | ctx.cache.set(key, name); 14 | } 15 | return name; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/routes/patchwork.kernel.org/comments.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cache = require('./cache'); 3 | const he = require('he'); 4 | 5 | module.exports = async (ctx) => { 6 | const id = ctx.params.id; 7 | 8 | const name = await cache.getPatchnameFromID(ctx, id); 9 | 10 | const host = `https://patchwork.kernel.org/patch/${id}/`; 11 | const url = `https://patchwork.kernel.org/api/patches/${id}/comments/`; 12 | 13 | const response = await got({ 14 | method: 'get', 15 | url, 16 | params: { 17 | order: '-date', 18 | }, 19 | }); 20 | const data = response.data; 21 | 22 | ctx.state.data = { 23 | title: `${name} - Comments`, 24 | link: host, 25 | item: data.map((item) => ({ 26 | title: item.subject, 27 | description: he.escape(he.escape(item.content)).replace(/\n/g, '
'), 28 | pubDate: new Date(item.date).toUTCString(), 29 | link: item.web_url, 30 | })), 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/pigtails/index.js: -------------------------------------------------------------------------------- 1 | const cheerio = require('cheerio'); 2 | const got = require('@/utils/got'); 3 | 4 | const base_url = 'https://pigtails.moe'; 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: base_url, 9 | headers: { 10 | Referer: base_url, 11 | }, 12 | }); 13 | 14 | const $ = cheerio.load(response.data); 15 | 16 | const items = []; 17 | $('.posts > .post > a').each((idx, item) => { 18 | const $item = $(item); 19 | const img_url = $('.thumb', item) 20 | .attr('style') 21 | .split(')')[0] 22 | .split('(')[1]; 23 | items.push({ 24 | title: $item.text(), 25 | description: ``, 26 | link: `${base_url}${$item.attr('href')}`, 27 | }); 28 | }); 29 | 30 | ctx.state.data = { 31 | title: 'Awesome Pigtails', 32 | description: 'Share awesome pigtails.', 33 | link: base_url, 34 | item: items, 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/getBookmarks.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | 4 | /** 5 | * 获取用户的收藏 6 | * 7 | * @param {string} user_id 目标用户id 8 | * @param {string} token pixiv oauth token 9 | * @returns {Promise>} 10 | */ 11 | module.exports = async function getBookmarks(user_id, token) { 12 | return await got({ 13 | method: 'get', 14 | url: 'https://app-api.pixiv.net/v1/user/bookmarks/illust', 15 | headers: { 16 | ...maskHeader, 17 | Authorization: 'Bearer ' + token, 18 | }, 19 | params: { 20 | user_id: user_id, 21 | restrict: 'public', 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/getIllusts.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | 4 | /** 5 | * 获取用户插画作品 6 | * @param {string} user_id 目标用户id 7 | * @param {string} token pixiv oauth token 8 | * @returns {Promise>} 9 | */ 10 | module.exports = async function getIllusts(user_id, token) { 11 | return await got({ 12 | method: 'get', 13 | url: 'https://app-api.pixiv.net/v1/user/illusts', 14 | headers: { 15 | ...maskHeader, 16 | Authorization: 'Bearer ' + token, 17 | }, 18 | params: { 19 | user_id: user_id, 20 | filter: 'for_ios', 21 | type: 'illust', 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/getRanking.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | const assert = require('assert'); 4 | 5 | const allowMode = ['day', 'week', 'month', 'day_male', 'day_female', 'week_original', 'week_rookie', 'day_r18', 'day_male_r18', 'day_female_r18', 'week_r18', 'week_r18g']; 6 | 7 | /** 8 | * 获取某天的排行榜 9 | * @param {string} mode 模式 10 | * @param {Date} date 日期 11 | * @param {string} token pixiv oauth token 12 | * @returns {Promise>} 13 | */ 14 | module.exports = async function getRanking(mode, date, token) { 15 | assert(allowMode.includes(mode), 'Mode not allow.'); 16 | return await got({ 17 | method: 'get', 18 | url: 'https://app-api.pixiv.net/v1/illust/ranking', 19 | headers: { 20 | ...maskHeader, 21 | Authorization: 'Bearer ' + token, 22 | }, 23 | params: { 24 | mode: mode, 25 | filter: 'for_ios', 26 | ...(date && { 27 | date: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`, 28 | }), 29 | }, 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/getUserDetail.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | 4 | /** 5 | * pixiv 用户 6 | * @typedef {{user: {id: number, name: string, account: string, profile_image_urls: any, comment: string}, profile: any} userDetail 7 | */ 8 | 9 | /** 10 | * 获取用户信息 11 | * 12 | * @param {string} user_id 目标用户id 13 | * @param {string} token pixiv oauth token 14 | * @returns {Promise>} 15 | */ 16 | module.exports = async function getUserDetail(user_id, token) { 17 | return await got({ 18 | method: 'get', 19 | url: 'https://app-api.pixiv.net/v1/user/detail', 20 | headers: { 21 | ...maskHeader, 22 | Authorization: 'Bearer ' + token, 23 | }, 24 | params: { 25 | user_id: user_id, 26 | }, 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/searchIllust.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | 4 | /** 5 | * 按时间排序搜索内容 6 | * @param {string} keyword 关键词 7 | * @param {string} token pixiv oauth token 8 | * @returns {Promise>} 9 | */ 10 | module.exports = async function searchIllust(keyword, token) { 11 | return await got({ 12 | method: 'get', 13 | url: 'https://app-api.pixiv.net/v1/search/illust', 14 | headers: { 15 | ...maskHeader, 16 | Authorization: 'Bearer ' + token, 17 | }, 18 | params: { 19 | word: keyword, 20 | search_target: 'partial_match_for_tags', 21 | sort: 'date_desc', 22 | filter: 'for_ios', 23 | }, 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/pixiv/api/searchPopularIllust.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const maskHeader = require('../constants').maskHeader; 3 | 4 | /** 5 | * 按热门排序搜索内容 6 | * @param {string} keyword 关键词 7 | * @param {string} token pixiv oauth token 8 | * @returns {Promise>} 9 | */ 10 | module.exports = async function searchPopularIllust(keyword, token) { 11 | return await got({ 12 | method: 'get', 13 | url: 'https://app-api.pixiv.net/v1/search/popular-preview/illust', 14 | headers: { 15 | ...maskHeader, 16 | Authorization: 'Bearer ' + token, 17 | }, 18 | params: { 19 | word: keyword, 20 | search_target: 'partial_match_for_tags', 21 | filter: 'for_ios', 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/pixiv/constants.js: -------------------------------------------------------------------------------- 1 | exports.modules = { 2 | maskHeader: { 3 | 'App-OS': 'ios', 4 | 'App-OS-Version': '10.3.1', 5 | 'App-Version': '6.7.1', 6 | 'User-Agent': 'PixivIOSApp/6.7.1 (iOS 10.3.1; iPhone8,1)', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /lib/routes/polimi/news.js: -------------------------------------------------------------------------------- 1 | const buildData = require('@/utils/common-config'); 2 | 3 | module.exports = async (ctx) => { 4 | let link = `https://www.polimi.it/tutte-le-news/`; 5 | if (ctx.params.language === 'en') { 6 | link = `https://www.polimi.it/en/all-news/`; 7 | } 8 | ctx.state.data = await buildData({ 9 | link, 10 | url: link, 11 | title: `Polimi News`, 12 | params: { 13 | homeLink: 'https://www.polimi.it', 14 | }, 15 | item: { 16 | item: '.container .no', 17 | title: `$('h3').text()`, 18 | link: `'%homeLink%' + $('p span a').attr('href')`, 19 | description: `$('p').text() + '

' + 'Full Article { 4 | const bookid = ctx.params.bookid; 5 | const link = `http://www.queshu.com/book/${bookid}`; 6 | const host = `http://www.queshu.com`; 7 | ctx.state.data = await buildData({ 8 | link, 9 | url: link, 10 | title: `%title%`, 11 | allowEmpty: true, 12 | params: { 13 | title: `$('#book_left > #h1').text().trim() + ' - 单品活动信息 - 缺书网'`, 14 | host, 15 | }, 16 | item: { 17 | item: '.stacked.right_state a', 18 | title: `$('.right_item .bodycol_258').text()`, 19 | link: `'%host%' + $('.right_item').parent().attr('href')`, 20 | }, 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/queshu/sale.js: -------------------------------------------------------------------------------- 1 | const buildData = require('@/utils/common-config'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = `http://www.queshu.com/sale/`; 5 | const host = `http://www.queshu.com`; 6 | ctx.state.data = await buildData({ 7 | link, 8 | url: link, 9 | title: `%title%`, 10 | params: { 11 | title: '图书促销 - 缺书网', 12 | host, 13 | }, 14 | item: { 15 | item: '#tb_sale tr', 16 | title: `$('.news_sale_detail .sale_btn').text() + ':' + $('.news_sale_title a').text()`, 17 | link: `'%host%' + $('.news_sale_title a').attr('href')`, 18 | description: `$('.news_sale_detail .sale_btn').text() + ':' + $('.news_sale_title a').text() 19 | + '
' 20 | + $('.news_sale_detail .sale_time_end').first().text() 21 | + '
' 22 | + '发布时间:' + $('.news_sale_detail .sale_time_end.inline_right').text() 23 | 24 | `, 25 | pubDate: `date($('.news_sale_detail .sale_time_end.inline_right').text())`, 26 | }, 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/sans/summit_archive.js: -------------------------------------------------------------------------------- 1 | // const got = require('@/utils/got'); 2 | // const cheerio = require('cheerio'); 3 | const buildData = require('@/utils/common-config'); 4 | 5 | module.exports = async (ctx) => { 6 | const link = `https://www.sans.org/cyber-security-summit/archives/`; 7 | const host = `https://www.sans.org`; 8 | ctx.state.data = await buildData({ 9 | link, 10 | url: link, 11 | title: `%title%`, 12 | params: { 13 | title: 'SANS Institute: Summit Archives', 14 | host, 15 | link, 16 | }, 17 | item: { 18 | item: 'li.bottom-buffer', 19 | title: `$('li.bottom-buffer strong').text()`, 20 | link: `'%link%'`, 21 | description: `$('li.bottom-buffer ul').html()`, 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/sf/sffq-announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const data = (await got({ 5 | method: 'post', 6 | url: 'https://qiao.sf-express.com/menu/getListNews.pub', 7 | json: true, 8 | data: {}, 9 | })).data; 10 | 11 | ctx.state.data = { 12 | title: '顺丰丰桥开放平台-公告', 13 | link: 'https://qiao.sf-express.com/pages/news/index.html', 14 | item: data.latesnewsList.map((item) => ({ 15 | title: item.title, 16 | description: '', 17 | pubDate: '', 18 | link: `https://qiao.sf-express.com/pages/news/index.html?id=${item.id}`, 19 | })), 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/sogou/doodles.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'http://help.sogou.com/logo/doodle_logo_list.html', 7 | }); 8 | 9 | const data = response.data.split(/\r\n/).slice(1); 10 | 11 | ctx.state.data = { 12 | title: '搜狗特色LOGO', 13 | link: 'http://help.sogou.com/logo/', 14 | item: data.map((item) => { 15 | item = item.split(','); 16 | 17 | return { 18 | title: `${item[2]}-${item[5]}`, 19 | description: ``, 20 | pubDate: new Date(item[5]).toUTCString(), 21 | link: item[7], 22 | guid: item[4], 23 | }; 24 | }), 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/routes/soundcloud/tracks.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const user = ctx.params.user; 7 | 8 | const response = await got({ 9 | method: 'get', 10 | url: `https://soundcloud.com/${user}/tracks`, 11 | headers: { 12 | Referer: `https://soundcloud.com/${user}/tracks`, 13 | }, 14 | }); 15 | 16 | const data = response.data; 17 | 18 | const $ = cheerio.load(data, { xmlMode: true }); 19 | const list = $('body article.audible').get(); 20 | const result = await util.ProcessFeed(list, ctx.cache); 21 | 22 | ctx.state.data = { 23 | title: $('title').text(), 24 | link: `https://soundcloud.com/${user}/tracks`, 25 | description: $('meta[name="description"]').attr('content') || $('title').text(), 26 | item: result, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/sspai/series.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: `https://sspai.com/api/v1/series/page/get?limit=5&sort=weight&offset=0&released_at=0`, 7 | headers: { 8 | Host: 'sspai.com', 9 | }, 10 | }); 11 | const data = response.data.data; 12 | 13 | ctx.state.data = { 14 | title: '少数派 -- 最新上架付费专栏', 15 | link: 'https://sspai.com/series', 16 | description: '少数派 -- 最新上架付费专栏', 17 | item: data.map((item) => ({ 18 | title: item.title, 19 | description: item.summary, 20 | link: `https://sspai.com/series/${item.id}`, 21 | author: item.nickname, 22 | })), 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/sspai/shortcutsGallery.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const { 5 | data: { data: categories }, 6 | } = await got.get('https://shortcuts.sspai.com/api/v1/user/workflow/all/get'); 7 | 8 | const items = []; 9 | 10 | for (const category of categories) { 11 | for (const shortcut of category.data || []) { 12 | items.push({ 13 | title: shortcut.name, 14 | description: `作者:
${shortcut.author_id}
${decodeURIComponent((shortcut.description || '').replace(/\+/g, '%20'))}`, 15 | pubDate: new Date(shortcut.utime * 1000).toUTCString(), 16 | guid: shortcut.id, 17 | link: shortcut.url, 18 | }); 19 | } 20 | } 21 | 22 | ctx.state.data = { 23 | title: 'Shortcuts Gallery - 少数派', 24 | link: 'https://shortcuts.sspai.com/#/main/workflow', 25 | description: 'Shortcuts Gallery - 少数派', 26 | item: items, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/routes/steam/news.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { appids } = ctx.params; 6 | const { data: html } = await got.get(`https://store.steampowered.com/news/`, { 7 | params: { appids }, 8 | }); 9 | const $ = cheerio.load(html); 10 | 11 | ctx.state.data = { 12 | title: $('.pageheader').text(), 13 | link: `https://store.steampowered.com/news/?appids=${appids}`, 14 | item: $('#news div[id]') 15 | .toArray() 16 | .map((a) => { 17 | const $el = $(a); 18 | return { 19 | title: $el.find('.posttitle').text(), 20 | link: $el.find('.posttitle a').attr('href'), 21 | author: $el.find('.feed').text(), 22 | description: $el.find('.body').html(), 23 | }; 24 | }) 25 | .filter((it) => it.title), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/tanwu/products.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://api.tanwuapp.com/iOS/2.0.0/products?rentType=0&pageSize=1000&pageNum=1&clientType=web:h5', 7 | }); 8 | 9 | const data = response.data.details.allProductList.data; 10 | 11 | ctx.state.data = { 12 | title: '探物', 13 | link: 'https://m.tanwuapp.com', 14 | description: '探物是一个科技数码产品租赁平台,旨在把市面上最新、最酷、最实用的科技产品带去给用户,传播科技便捷生活、共享丰富生活的价值理念。', 15 | item: data.map(({ productName, productImageSrc, productRent, rentUnit, productId, appraiseAvgScore }) => ({ 16 | title: productName, 17 | link: `https://m.tanwuapp.com/?source=mobile#/productDetails?productId=${productId}`, 18 | description: ` 19 |
20 | ${productName}

21 | 价格: ¥${productRent} / ${rentUnit}
22 | 评分: ${appraiseAvgScore} 23 | `, 24 | guid: productId, 25 | })), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/tencent/qcloud/mlvb/changelog.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const url = 'https://cloud.tencent.com/document/product/454/7878'; 6 | 7 | const res = await got({ 8 | method: 'get', 9 | url: url, 10 | }); 11 | const $ = cheerio.load(res.data); 12 | 13 | const resultItem = []; 14 | $('.J-mainDetail #docArticleContent h3').each(function() { 15 | const item = {}; 16 | item.title = $(this).text(); 17 | item.description = $(this) 18 | .nextUntil('h3') 19 | .text(); 20 | item.link = url; 21 | item.guid = $(this).text(); 22 | resultItem.push(item); 23 | }); 24 | 25 | ctx.state.data = { 26 | title: '腾讯移动直播 SDK 更新日志', 27 | link: url, 28 | item: resultItem, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/tencent/video/playlist.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { id } = ctx.params; 6 | const link = `https://v.qq.com/detail/${id[0]}/${id}.html`; 7 | const page = await got.get(link); 8 | const $ = cheerio.load(page.data); 9 | const episodes = await got.get(`https://s.video.qq.com/get_playsource?id=${id}&plat=2&type=4&data_type=2&video_type=3&range=1&plname=qq&otype=json&num_mod_cnt=20&callback=a&_t=${Date.now()}`); 10 | const playList = JSON.parse(episodes.data.slice(2, -1)).PlaylistItem.videoPlayList; 11 | const items = playList.map((video) => ({ 12 | title: video.title, 13 | link: video.playUrl, 14 | description: ` 15 | 16 | `, 17 | })); 18 | 19 | ctx.state.data = { 20 | title: $('title').text(), 21 | link, 22 | description: $('meta[name=description]').attr('content'), 23 | item: items, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/tencent/wechat/announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { data: htmlString } = await got({ 6 | method: 'get', 7 | url: 'https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncementlist&lang=zh_CN', 8 | }); 9 | 10 | const $ = cheerio.load(htmlString); 11 | const announceList = []; 12 | 13 | $('.mp_news_list > .mp_news_item').each(function() { 14 | const $item = $(this); 15 | const $link = $item.find('a'); 16 | const time = $item.find('.read_more').text(); 17 | const title = $item.find('strong').text(); 18 | 19 | announceList.push({ 20 | title: `${time} ${title}`, 21 | link: `https://mp.weixin.qq.com${$link.attr('href')}`, 22 | description: title, 23 | pubDate: new Date(time).toUTCString(), 24 | }); 25 | }); 26 | 27 | ctx.state.data = { 28 | title: '微信公众平台-系统公告栏目', 29 | link: 'https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncementlist&lang=zh_CN&token=', 30 | item: announceList, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/tencent/wechat/ershcimi.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { id } = ctx.params; 6 | const url = `https://www.ershicimi.com/a/${id}`; 7 | const response = await got(url); 8 | const $ = cheerio.load(response.data); 9 | const name = $('#wrapper > .yheader > .info-wrap .name').text(); 10 | const description = $('#wrapper .right > .Card .Profile-sideColumnItemValue').text(); 11 | const items = $('.weui_media_box') 12 | .map((_, ele) => { 13 | const $item = cheerio.load(ele); 14 | return { 15 | title: $item('.weui_media_title a').text(), 16 | link: $item('.weui_media_title a').attr('href'), 17 | pubDate: new Date($item('.weui_media_extra_info').attr('title')).toUTCString(), 18 | }; 19 | }) 20 | .get(); 21 | ctx.state.data = { 22 | title: `微信公众号 - ${name}`, 23 | link: url, 24 | description, 25 | item: items, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/testerhome/newest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const response = await got({ 6 | method: 'get', 7 | url: 'https://testerhome.com/topics/last', 8 | }); 9 | 10 | const $ = cheerio.load(response.data); 11 | const resultItem = $('.item-list .topic') 12 | .map((index, elem) => { 13 | elem = $(elem); 14 | const $link = elem.find('.title a'); 15 | const title = $link.attr('title'); 16 | 17 | return { 18 | title, 19 | link: `https://testerhome.com${$link.attr('href')}`, 20 | description: title, 21 | }; 22 | }) 23 | .get(); 24 | 25 | ctx.state.data = { 26 | title: 'TesterHome-最新发布', 27 | link: 'https://testerhome.com/topics/last', 28 | description: 'TesterHome软件测试社区,人气最旺的软件测试技术门户,提供软件测试社区交流,测试沙龙。', 29 | item: resultItem, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/the-economist/gre-vocabulary.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://gre.economist.com/api/gre-vocabulary.json', 7 | }); 8 | 9 | const wordInfo = response.data[0]; 10 | const time = wordInfo.publishedDate; 11 | 12 | ctx.state.data = { 13 | title: 'The Economist GRE Vocabulary', 14 | link: 'https://gre.economist.com/gre-vocabulary', 15 | item: [ 16 | { 17 | title: `${wordInfo.word} - ${time}`, 18 | description: ` 19 |
20 | Source: ${wordInfo.sourceTitle} 21 | `, 22 | pubDate: new Date(time).toUTCString(), 23 | link: wordInfo.url, 24 | }, 25 | ], 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/thepaper/channel.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const { id } = ctx.params; 5 | const link = `https://m.thepaper.cn/list_${id}`; 6 | const items = await utils.ProcessFeed(link, ctx); 7 | 8 | ctx.state.data = { 9 | title: `澎湃新闻频道 - ${id}`, 10 | link, 11 | item: items, 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /lib/routes/thepaper/featured.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = 'https://m.thepaper.cn/'; 5 | const items = await utils.ProcessFeed(link, ctx); 6 | 7 | ctx.state.data = { 8 | title: '澎湃新闻 - 首页头条', 9 | link, 10 | item: items, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/routes/tingshuitz/xian.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const iconv = require('iconv-lite'); 4 | 5 | module.exports = async (ctx) => { 6 | const url = 'http://www.xazls.com/tsgg/index.htm'; 7 | const response = await got.get(url, { 8 | responseType: 'buffer', 9 | }); 10 | 11 | const data = response.data; 12 | const $ = cheerio.load(iconv.decode(data, 'gb2312')); 13 | const list = $('#body > div.M > div.AboutUsDetail > ul > li'); 14 | 15 | ctx.state.data = { 16 | title: $('title').text() || '停水通知 - 西安市自来水有限公司', 17 | link: 'http://www.xazls.com/tsgg/index.htm', 18 | item: list 19 | .map((_, el) => { 20 | const item = $(el); 21 | 22 | const a = item.find('a'); 23 | return { 24 | title: a.text().trim(), 25 | description: item.text().trim(), 26 | link: 'http://www.xazls.com' + a.attr('href'), 27 | }; 28 | }) 29 | .get(), 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /lib/routes/titsguru/category.js: -------------------------------------------------------------------------------- 1 | const { getPage, normalizeKeyword } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { type } = ctx.params; 5 | 6 | ctx.state.data = await getPage(`https://tits-guru.com/category/${normalizeKeyword(type)}/date`); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/routes/titsguru/daily.js: -------------------------------------------------------------------------------- 1 | const { createHandler } = require('./util'); 2 | 3 | module.exports = createHandler('https://tits-guru.com/thebest/perDay'); 4 | -------------------------------------------------------------------------------- /lib/routes/titsguru/home.js: -------------------------------------------------------------------------------- 1 | const { createHandler } = require('./util'); 2 | 3 | module.exports = createHandler('https://tits-guru.com/'); 4 | -------------------------------------------------------------------------------- /lib/routes/titsguru/model.js: -------------------------------------------------------------------------------- 1 | const { getPage, normalizeKeyword } = require('./util'); 2 | 3 | module.exports = async (ctx) => { 4 | const { name } = ctx.params; 5 | 6 | const formalName = normalizeKeyword(name) 7 | .split('-') 8 | .map((word) => `${word[0].toUpperCase()}${word.substr(1)}`) 9 | .join(' '); 10 | 11 | ctx.state.data = { 12 | ...(await getPage(`https://tits-guru.com/boobs-model/${normalizeKeyword(name)}/date`)), 13 | title: `TitsGuru - ${formalName}`, 14 | description: `TitsGuru - ${formalName}`, 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /lib/routes/tophub/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id; 6 | 7 | const link = `https://tophub.today/n/${id}`; 8 | const response = await got.get(link); 9 | const $ = cheerio.load(response.data); 10 | 11 | const title = $('div.Xc-ec-L.b-L') 12 | .text() 13 | .trim(); 14 | 15 | const out = $('div.Zd-p-Sc > div:nth-child(1) tr') 16 | .map(function() { 17 | const info = { 18 | title: $(this) 19 | .find('td.al a') 20 | .text(), 21 | link: $(this) 22 | .find('td.al a') 23 | .attr('href'), 24 | }; 25 | return info; 26 | }) 27 | .get(); 28 | 29 | ctx.state.data = { 30 | title: title, 31 | link: link, 32 | item: out, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/twitter/followings.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | const config = require('@/config'); 3 | const T = {}; 4 | const Twit = require('twit'); 5 | 6 | module.exports = async (ctx) => { 7 | const id = ctx.params.id; 8 | const cookie = config.twitter.tokens[id]; 9 | if (!cookie) { 10 | throw Error(`lacking twitter token for user ${id}`); 11 | } else if (!T[id]) { 12 | const token = cookie.split(','); 13 | T[id] = new Twit({ 14 | consumer_key: token[0], 15 | consumer_secret: token[1], 16 | access_token: token[2], 17 | access_token_secret: token[3], 18 | }); 19 | } 20 | 21 | const result = await T[id].get('statuses/home_timeline', { 22 | tweet_mode: 'extended', 23 | count: 100, 24 | }); 25 | const data = result.data; 26 | 27 | ctx.state.data = { 28 | title: `${id} 的 Twitter 关注时间线`, 29 | link: `https://twitter.com/${id}/`, 30 | item: utils.ProcessFeed( 31 | { 32 | data, 33 | }, 34 | true 35 | ), 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /lib/routes/twitter/likes.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | const config = require('@/config'); 3 | 4 | module.exports = async (ctx) => { 5 | if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) { 6 | throw 'Twitter RSS is disabled due to the lack of relevant config'; 7 | } 8 | const id = ctx.params.id; 9 | const result = await utils.getTwit().get('favorites/list', { 10 | screen_name: id, 11 | tweet_mode: 'extended', 12 | }); 13 | const data = result.data; 14 | 15 | ctx.state.data = { 16 | title: `Twitter Likes - ${id}`, 17 | link: `https://twitter.com/${id}/likes`, 18 | item: utils.ProcessFeed({ 19 | data, 20 | }), 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/routes/twitter/list.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | const config = require('@/config'); 3 | 4 | module.exports = async (ctx) => { 5 | if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) { 6 | throw 'Twitter RSS is disabled due to the lack of relevant config'; 7 | } 8 | const { id, name } = ctx.params; 9 | const result = await utils.getTwit().get('lists/statuses', { 10 | owner_screen_name: id, 11 | slug: name, 12 | tweet_mode: 'extended', 13 | }); 14 | const data = result.data; 15 | 16 | ctx.state.data = { 17 | title: `Twitter List - ${id}/${name}`, 18 | link: `https://twitter.com/${id}/lists/${name}`, 19 | item: utils.ProcessFeed({ 20 | data, 21 | }), 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/twitter/user.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | const config = require('@/config'); 3 | 4 | module.exports = async (ctx) => { 5 | if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) { 6 | throw 'Twitter RSS is disabled due to the lack of relevant config'; 7 | } 8 | 9 | const id = ctx.params.id; 10 | const result = await utils.getTwit().get('statuses/user_timeline', { 11 | screen_name: id, 12 | tweet_mode: 'extended', 13 | }); 14 | const data = result.data; 15 | const userInfo = data[0].user; 16 | const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https; 17 | 18 | ctx.state.data = { 19 | title: `${userInfo.name} 的 Twitter`, 20 | link: `https://twitter.com/${id}/`, 21 | image: profileImageUrl, 22 | description: userInfo.description, 23 | item: utils.ProcessFeed({ 24 | data, 25 | }), 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/routes/universities/bit/cs/cs.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'http://cs.bit.edu.cn/tzgg', 9 | }); 10 | 11 | const $ = cheerio.load(response.data); 12 | 13 | const list = $('.box_list01 li') 14 | .slice(0, 10) 15 | .get(); 16 | 17 | const result = await util.ProcessFeed(list, ctx.cache); 18 | 19 | ctx.state.data = { 20 | title: $('title').text(), 21 | link: 'http://cs.bit.edu.cn/tzgg', 22 | description: $('meta[name="description"]').attr('content'), 23 | item: result, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/universities/bit/jwc/jwc.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'http://jwc.bit.edu.cn/tzgg', 9 | }); 10 | 11 | const $ = cheerio.load(response.data); 12 | 13 | const list = $('.crules div') 14 | .slice(0, 10) 15 | .get(); 16 | 17 | const result = await util.ProcessFeed(list, ctx.cache); 18 | 19 | ctx.state.data = { 20 | title: $('title').text(), 21 | link: 'http://jwc.bit.edu.cn/tzgg', 22 | description: '北京理工大学教务部', 23 | item: result, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/routes/universities/cqu/news/jzyg.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'http://news.cqu.edu.cn/newsv2/info-24.html', 9 | }); 10 | 11 | const data = response.data; 12 | const $ = cheerio.load(data); 13 | const list = $('.item').get(); 14 | 15 | const result = await util.ProcessFeed(list, ctx.cache); 16 | 17 | ctx.state.data = { 18 | title: '重庆大学新闻网-讲座预告', 19 | link: 'http://news.cqu.edu.cn/newsv2/info-24.html', 20 | description: '重庆大学新闻网-讲座预告', 21 | item: result, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/universities/cuc/yz.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const url = require('url'); 4 | const iconv = require('iconv-lite'); 5 | 6 | const host = 'http://yz.cuc.edu.cn/'; 7 | 8 | /* 研究生招生网通知公告*/ 9 | 10 | module.exports = async (ctx) => { 11 | const response = await got({ 12 | method: 'get', 13 | url: host, 14 | responseType: 'buffer', 15 | }); 16 | 17 | const responseHtml = iconv.decode(response.data, 'gbk'); 18 | const $ = cheerio.load(responseHtml); 19 | 20 | const notice = $('#notice_area').children('.notice_block_top'); 21 | const content = notice.children('.notice_content1').children(); 22 | 23 | const items = content 24 | .map((_, elem) => { 25 | const a = $('a', elem); 26 | return { 27 | link: url.resolve(host, a.attr('href')), 28 | title: a.text(), 29 | }; 30 | }) 31 | .get(); 32 | 33 | ctx.state.data = { 34 | title: $('title').text(), 35 | link: host, 36 | description: '中国传媒大学研究生招生网 通知公告', 37 | item: items, 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /lib/routes/universities/gdou/jwc/jwtz.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | const iconv = require('iconv-lite'); 5 | 6 | module.exports = async (ctx) => { 7 | const response = await got({ 8 | method: 'get', 9 | url: 'http://www3.gdou.edu.cn/jwc/', 10 | responseType: 'buffer', 11 | }); 12 | 13 | // HTML-buffer转为gb2312 14 | const data = iconv.decode(response.data, 'gb2312'); 15 | const $ = cheerio.load(data); 16 | 17 | const list = $('.bor03.cont li') 18 | .slice(0, 10) 19 | .get(); 20 | 21 | const result = await util.ProcessFeed(list, ctx.cache); 22 | 23 | ctx.state.data = { 24 | title: $('title').text(), 25 | link: 'http://www3.gdou.edu.cn/jwc/Item/list.asp?id=1224', 26 | description: '教务通知', 27 | item: result, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/universities/njust/cwc/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const url = require('url'); 4 | 5 | const host = 'http://cwc.njust.edu.cn/'; 6 | 7 | const map = new Map([[1, { title: '南京理工大学财务处 -- 新闻及通知', id: 'wp_news_w2' }], [2, { title: '南京理工大学财务处 -- 办事指南', id: 'wp_news_w3' }]]); 8 | 9 | module.exports = async (ctx) => { 10 | const type = Number.parseInt(ctx.params.type); 11 | const response = await got.get(host); 12 | 13 | const $ = cheerio.load(response.data); 14 | 15 | const id = map.get(type).id; 16 | 17 | const items = $(`#${id} tr tr`) 18 | .slice(0, 10) 19 | .map((_, elem) => { 20 | const a = $('td:first-child > a', elem); 21 | return { 22 | link: url.resolve(host, a.attr('href')), 23 | title: a.attr('title'), 24 | pubDate: new Date($('td:nth-child(2)', elem).text()).toUTCString(), 25 | }; 26 | }) 27 | .get(); 28 | 29 | ctx.state.data = { 30 | link: host, 31 | title: map.get(type).title, 32 | item: items, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/universities/scnu/library.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const res = await got({ 6 | method: 'get', 7 | url: 'https://lib.scnu.edu.cn/news/zuixingonggao', 8 | headers: { 9 | Referer: 'https://lib.scnu.edu.cn', 10 | }, 11 | }); 12 | const $ = cheerio.load(res.data); 13 | const list = $('.article-list') 14 | .find('li') 15 | .slice(0, 10); 16 | 17 | ctx.state.data = { 18 | title: $('title').text(), 19 | link: 'https://lib.scnu.edu.cn/news/zuixingonggao', 20 | description: '华南师范大学图书馆 - 通知公告', 21 | item: 22 | list && 23 | list 24 | .map((index, item) => { 25 | item = $(item); 26 | return { 27 | title: item.find('a').text(), 28 | pubDate: new Date(item.find('.clock').text()).toUTCString(), 29 | link: item.find('a').attr('href'), 30 | }; 31 | }) 32 | .get(), 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/universities/sjtu/gs/tzgg.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const type = ctx.params.type; 7 | let url = ''; 8 | if (type) { 9 | url = `http://www.gs.sjtu.edu.cn/index/tzgg/${type}.htm`; 10 | } else { 11 | url = 'http://www.gs.sjtu.edu.cn/index/tzgg.htm'; 12 | } 13 | 14 | const response = await got({ 15 | method: 'get', 16 | url: url, 17 | }); 18 | 19 | const data = response.data; 20 | 21 | const $ = cheerio.load(data); 22 | const list = $('.comm li') 23 | .slice(0, 10) 24 | .get(); 25 | 26 | const result = await util.ProcessFeed(list, ctx.cache, url); 27 | 28 | ctx.state.data = { 29 | title: $('title').text(), 30 | link: url, 31 | item: result, 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/routes/universities/sjtu/seiee/academic.js: -------------------------------------------------------------------------------- 1 | const workerFactory = require('./utils'); 2 | 3 | module.exports = workerFactory( 4 | () => ({ 5 | title: '上海交通大学电子信息与电气工程学院本科教务办 -- 学术动态', 6 | local: `seiee/list/683-1-20.htm`, 7 | author: '上海交通大学电子信息与电气工程学院本科教务办', 8 | }), 9 | ($) => 10 | $('.list_box_5 li') 11 | .slice(0, 10) 12 | .map((i, e) => ({ 13 | date: $(e) 14 | .children('span') 15 | .text() 16 | .slice(1, -1), 17 | title: $(e) 18 | .children('a') 19 | .text() 20 | .slice(2), 21 | link: $(e) 22 | .children('a') 23 | .attr('href'), 24 | })) 25 | .get() 26 | ); 27 | -------------------------------------------------------------------------------- /lib/routes/universities/tju/sse/_article.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); // get web content 2 | const cheerio = require('cheerio'); // html parser 3 | 4 | const domain = 'http://sse.tongji.edu.cn'; 5 | 6 | module.exports = async function get_article(url) { 7 | if (/^\/.*$/.test(url)) { 8 | url = domain + url; 9 | } 10 | const response = await got({ 11 | method: 'get', 12 | url: url, 13 | }); 14 | const data = response.data; 15 | 16 | const $ = cheerio.load(data); 17 | const title = $('div.view-title').text(); 18 | const pub_date_raw = $('div.view-info').text(); 19 | const pub_date_parse = /\d{2,4}-\d{1,2}-\d{1,2}/.exec(pub_date_raw); 20 | const pub_date = new Date(pub_date_parse[0]).toUTCString(); 21 | const content = $('div.view-cnt') 22 | .html() 23 | .replace(//g, '-->') 25 | .replace(/href="\//g, 'href="' + domain + '/'); 26 | 27 | const item = { 28 | title: title, 29 | pubDate: pub_date, 30 | link: url, 31 | description: content, 32 | }; 33 | return item; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/universities/zjgsu/tzgg/scripts.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const util = require('./utils'); 4 | 5 | module.exports = async (ctx) => { 6 | const response = await got({ 7 | method: 'get', 8 | url: 'http://news.zjgsu.edu.cn/18/', 9 | }); 10 | 11 | const data = response.data; 12 | const $ = cheerio.load(data); 13 | const list = $('ul.list-1 li').get(); 14 | 15 | const result = await util.ProcessFeed(list, ctx.cache); 16 | 17 | ctx.state.data = { 18 | title: '浙江工商大学新闻网-通知公告', 19 | link: 'http://news.zjgsu.edu.cn/18/', 20 | description: '浙江工商大学新闻网-通知公告', 21 | item: result, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/v2ex/topics.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const type = ctx.params.type; 5 | 6 | const response = await got({ 7 | method: 'get', 8 | url: `https://www.v2ex.com/api/topics/${type}.json`, 9 | }); 10 | 11 | const data = response.data; 12 | 13 | let title; 14 | if (type === 'hot') { 15 | title = '最热主题'; 16 | } else if (type === 'latest') { 17 | title = '最新主题'; 18 | } 19 | 20 | ctx.state.data = { 21 | title: `V2EX-${title}`, 22 | link: 'https://www.v2ex.com/', 23 | description: `V2EX-${title}`, 24 | item: data.map((item) => ({ 25 | title: item.title, 26 | description: `${item.member.username}: ${item.content_rendered}`, 27 | content: { text: item.content, html: item.content_rendered }, 28 | pubDate: new Date(item.created * 1000).toUTCString(), 29 | guid: item.id, 30 | link: item.url, 31 | author: item.member.username, 32 | })), 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/routes/vgtime/release.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: `http://app02.vgtime.com:8080/vgtime-app/api/v2/game/last/sales`, 7 | }); 8 | const data = response.data.data.gameList; 9 | 10 | ctx.state.data = { 11 | title: `游戏时光游戏发售表`, 12 | link: `https://www.vgtime.com/game/release.jhtml`, 13 | item: data.map((item) => ({ 14 | title: item.title, 15 | description: `平台:${item.platformsText};${item.introduction}`, 16 | pubDate: item.publishDate, 17 | link: item.shareUrl, 18 | })), 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/routes/vocus/publication.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { ProcessFeed } = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const { id } = ctx.params; 6 | const link = `https://vocus.cc/${id}/home`; 7 | const configs = { headers: { Referer: link } }; 8 | 9 | const { _id, title, abstract } = (await got.get(`https://api.sosreader.com/api/publication/${id}`, configs)).data; 10 | const { articles } = (await got.get(`https://api.sosreader.com/api/articles?publicationId=${_id}&status=2&num=10&page=1`, configs)).data; 11 | const items = await ProcessFeed(articles, `https://vocus.cc/${id}`, ctx.cache); 12 | 13 | ctx.state.data = { 14 | title: `${title} - 方格子`, 15 | link, 16 | description: abstract, 17 | item: items, 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/routes/vocus/user.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { ProcessFeed } = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const id = ctx.params.id; 6 | const link = `https://vocus.cc/user/@${id}`; 7 | 8 | const { _id, fullname, intro } = (await got({ 9 | method: 'get', 10 | url: `https://api.sosreader.com/api/users/${id}`, 11 | headers: { 12 | Referer: link, 13 | }, 14 | })).data; 15 | 16 | const { articles } = (await got({ 17 | method: 'get', 18 | url: `https://api.sosreader.com/api/articles?userId=${_id}&num=10&status=2&sort=lastPublishAt`, 19 | headers: { 20 | Referer: link, 21 | }, 22 | })).data; 23 | 24 | const items = await ProcessFeed(articles, link, ctx.cache); 25 | 26 | ctx.state.data = { 27 | title: `${fullname}的个人文章 - 方格子`, 28 | link: link, 29 | description: intro, 30 | item: items, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/vocus/utils.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | const load = async (link, configs) => ({ 4 | description: (await got.get(link, configs)).data.article.content, 5 | }); 6 | 7 | const ProcessFeed = async (list, host, caches) => { 8 | const configs = { headers: { Referer: host } }; 9 | 10 | return await Promise.all( 11 | list.map(async (item) => { 12 | const itemUrl = `https://api.sosreader.com/api/article/${item._id}`; 13 | 14 | const single = { 15 | title: item.title, 16 | author: item.user.fullname, 17 | pubDate: new Date(item.updatedAt).toUTCString(), 18 | link: `${host}/${item._id}`, 19 | }; 20 | 21 | const other = await caches.tryGet(itemUrl, async () => await load(itemUrl, configs)); 22 | return Promise.resolve(Object.assign({}, single, other)); 23 | }) 24 | ); 25 | }; 26 | 27 | module.exports = { 28 | ProcessFeed, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/routes/weatheralarm/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const date = require('@/utils/date'); 4 | 5 | module.exports = async (ctx) => { 6 | const alarmInfoURL = 'http://www.nmc.cn/f/alarm.html'; 7 | const html = (await got.get(alarmInfoURL)).data; 8 | const $ = cheerio.load(html); 9 | const alarmElements = $('.alarmlist > div:not(.pagination)').toArray(); 10 | 11 | ctx.state.data = { 12 | title: '中央气象台全国气象预警', 13 | link: alarmInfoURL, 14 | item: alarmElements.map((el) => { 15 | const $el = $(el); 16 | const $aEl = $el.find('a'); 17 | return { 18 | title: $aEl.text(), 19 | link: `http://www.nmc.cn${$aEl.attr('href')}`, 20 | pubDate: date($el.find('.date').text(), 8), 21 | }; 22 | }), 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /lib/routes/wechat-open/community/pay-announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { data: htmlString } = await got({ 6 | method: 'get', 7 | url: 'https://developers.weixin.qq.com/community/pay/list/2', 8 | }); 9 | const $ = cheerio.load(htmlString); 10 | const announceList = []; 11 | $('#article_frame > div > ul > li').each(function() { 12 | const $item = $(this); 13 | const $link = $item.find('a').attr('href'); 14 | const time = $item.find('div > em').text(); 15 | const title = $item.find('h2 > meta').attr('content'); 16 | 17 | announceList.push({ 18 | title: title, 19 | link: `https://developers.weixin.qq.com${$link}`, 20 | description: title, 21 | pubDate: time, 22 | }); 23 | }); 24 | 25 | ctx.state.data = { 26 | title: '微信开放社区-微信支付公告', 27 | link: 'https://developers.weixin.qq.com/community/pay/list/2', 28 | item: announceList, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/wechat-open/community/xcx-announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { data: htmlString } = await got({ 6 | method: 'get', 7 | url: 'https://developers.weixin.qq.com/community/develop/list/2', 8 | }); 9 | const $ = cheerio.load(htmlString); 10 | const announceList = []; 11 | $('#article_frame > div > ul > li').each(function() { 12 | const $item = $(this); 13 | const $link = $item.find('a').attr('href'); 14 | const time = $item.find('div > em').text(); 15 | const title = $item.find('h2 > meta').attr('content'); 16 | 17 | announceList.push({ 18 | title: title, 19 | link: `https://developers.weixin.qq.com${$link}`, 20 | description: title, 21 | pubDate: time, 22 | }); 23 | }); 24 | 25 | ctx.state.data = { 26 | title: '微信开放社区-小程序公告', 27 | link: 'https://developers.weixin.qq.com/community/develop/list/2', 28 | item: announceList, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/wechat-open/community/xyx-announce.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const { data: htmlString } = await got({ 6 | method: 'get', 7 | url: 'https://developers.weixin.qq.com/community/minigame/list/2', 8 | }); 9 | const $ = cheerio.load(htmlString); 10 | const announceList = []; 11 | $('#article_frame > div > ul > li').each(function() { 12 | const $item = $(this); 13 | const $link = $item.find('a').attr('href'); 14 | const time = $item.find('div > em').text(); 15 | const title = $item.find('h2 > meta').attr('content'); 16 | 17 | announceList.push({ 18 | title: title, 19 | link: `https://developers.weixin.qq.com${$link}`, 20 | description: title, 21 | pubDate: time, 22 | }); 23 | }); 24 | 25 | ctx.state.data = { 26 | title: '微信开放社区-小游戏公告', 27 | link: 'https://developers.weixin.qq.com/community/minigame/list/2', 28 | item: announceList, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/wegene/column.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const type = ctx.params.type; 5 | let name; 6 | if (type === 'all') { 7 | name = '全部项目'; 8 | } else { 9 | name = '专业版'; 10 | } 11 | const category = ctx.params.category; 12 | 13 | const link = 'https://www.wegene.com/crowdsourcing'; 14 | const api = `https://www.wegene.com/crowdsourcing/ajax/get_list/?&htmlspecialchars_decode=true&type=${type}&page=1&limit=10&category=${category}&sort=NEW`; 15 | 16 | const response = await got.get(api); 17 | const data = response.data.rsm.list; 18 | 19 | ctx.state.data = { 20 | title: `${name}的${category}最近更新-WeGene`, 21 | link: link, 22 | item: data.map((item) => ({ 23 | title: item.title, 24 | link: `https://www.wegene.com/crowdsourcing/details/${item.id}`, 25 | date: new Date(item.add_time * 1000).toUTCString(), 26 | author: item.user.user_name, 27 | description: item.description, 28 | })), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/wegene/newest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const link = 'https://www.wegene.com/'; 5 | const api = 'https://www.wegene.com/contents/ajax/update/get_list/?htmlspecialchars_decode=true&page=1'; 6 | 7 | const response = await got.get(api); 8 | const data = response.data.rsm; 9 | 10 | ctx.state.data = { 11 | title: '最近更新-WeGene', 12 | link: link, 13 | item: data.map((item) => ({ 14 | title: item.title, 15 | description: item.description, 16 | pubDate: new Date(item.add_time * 1000).toUTCString(), 17 | link: item.url, 18 | })), 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/routes/weidian/goods.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = String(ctx.params.id); 5 | 6 | const link = `https://weidian.com/?userid=${id}`; 7 | const api_link = `https://thor.weidian.com/ares/shop.getItemsInShop/1.0?param={"shopId":"${id}","tabId":3,"sortOrder":"desc","limit":20,"page":1}`; 8 | 9 | const response = await got({ 10 | method: 'get', 11 | url: api_link, 12 | headers: { 13 | Referer: link, 14 | }, 15 | }); 16 | const goods = response.data.result.shopItems.items; 17 | 18 | const out = goods.map((good) => { 19 | const item = { 20 | title: good.itemName, 21 | description: `价格:${parseInt(good.itemPrice) / 100}
`, 22 | link: `https://weidian.com/item.html?itemID=${good.itemId}`, 23 | }; 24 | return item; 25 | }); 26 | 27 | ctx.state.data = { 28 | title: `${id} 商铺上新`, 29 | link: link, 30 | item: out, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/wenku8/chapter.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | const iconv = require('iconv-lite'); 4 | 5 | module.exports = async (ctx) => { 6 | const id = ctx.params.id; 7 | const index = parseInt(id / 1000); 8 | 9 | const response = await got({ 10 | method: 'get', 11 | url: `https://www.wenku8.net/novel/${index}/${id}/index.htm`, 12 | responseType: 'buffer', 13 | }); 14 | 15 | const responseHtml = iconv.decode(response.data, 'gbk'); 16 | 17 | const $ = cheerio.load(responseHtml); 18 | 19 | const name = $('#title').text(); 20 | 21 | const chapter_item = []; 22 | 23 | $('.ccss>a').each(function() { 24 | chapter_item.push({ 25 | title: $(this).text(), 26 | link: `https://www.wenku8.net/novel/${index}/${id}/` + $(this).attr('href'), 27 | }); 28 | }); 29 | 30 | ctx.state.data = { 31 | title: `轻小说文库 ${name}`, 32 | link: `https://www.wenku8.net/book/${id}.htm`, 33 | item: chapter_item, 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /lib/routes/wikihow/index.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const cheerio = require('cheerio'); 3 | 4 | module.exports = async (ctx) => { 5 | const link = `https://zh.wikihow.com/${encodeURIComponent('首页')}`; 6 | const res = await got(link); 7 | const $ = cheerio.load(res.data); 8 | 9 | const item = $('#fa_container td.image_map') 10 | .map((_, ele) => { 11 | const $item = cheerio.load(ele); 12 | const title = '如何' + $item('.text span').text(); 13 | const link = $item('.thumbnail > a').attr('href'); 14 | const thumbnail = $item('.thumbnail img').attr('data-src'); 15 | return { 16 | title, 17 | description: ` 18 | ${title}
19 | 20 | `, 21 | link, 22 | }; 23 | }) 24 | .get(); 25 | 26 | ctx.state.data = { 27 | title: 'wikiHow - 首页', 28 | description: 'wikiHow - 首页', 29 | link, 30 | item, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/wikipedia/utils.js: -------------------------------------------------------------------------------- 1 | const ProcessLink = ($, lang) => { 2 | lang = !lang ? 'en' : lang; 3 | 4 | $.find('a').each((i, e) => { 5 | if (e.attribs.href.startsWith('/wiki/')) { 6 | e.attribs.href = `https://${lang}.wikipedia.org${e.attribs.href}`; 7 | } 8 | }); 9 | 10 | // img links 11 | $.find('img').each((i, e) => { 12 | if (e.attribs.src.startsWith('//upload.wikimedia.org/')) { 13 | e.attribs.src = `https:${e.attribs.src}`; 14 | e.attribs.srcset = ''; 15 | } 16 | }); 17 | 18 | return $.html(); 19 | }; 20 | 21 | module.exports = { 22 | ProcessLink, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/routes/xiachufang/popular.js: -------------------------------------------------------------------------------- 1 | const { generatePopularData } = require('./utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await generatePopularData(ctx.params.timeframe); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/routes/xiachufang/user/cooked.js: -------------------------------------------------------------------------------- 1 | const { generateUserData } = require('../utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await generateUserData({ 5 | id: ctx.params.id, 6 | path: 'cooked', 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/routes/xiachufang/user/created.js: -------------------------------------------------------------------------------- 1 | const { generateUserData } = require('../utils'); 2 | 3 | module.exports = async (ctx) => { 4 | ctx.state.data = await generateUserData({ 5 | id: ctx.params.id, 6 | path: 'created', 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/routes/xiaoheihe/discount.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://api.xiaoheihe.cn/game/web/all_recommend/games/?show_type=discount&limit=20&offset=0&os_type=web&version=999.0.0', 7 | }); 8 | const data = response.data.result.list; 9 | 10 | ctx.state.data = { 11 | title: '小黑盒 Steam 热门折扣', 12 | link: 'https://xiaoheihe.cn/games/index', 13 | item: data.map((item) => ({ 14 | title: (item.price.discount === item.price.lowest_discount ? '[史低] ' : '') + item.name + (item.name_en ? ' / ' + item.name_en : ''), 15 | description: `
原价¥${item.heybox_price.original_coin / 1000},现价¥${item.heybox_price.cost_coin / 1000},折扣力度 ${ 16 | item.heybox_price.discount 17 | }%,${item.price.deadline_date}
评分:${item.score}
简介:${item.about_the_game}`, 18 | link: `https://store.steampowered.com/app/${item.appid}/?l=zh&cc=cn`, 19 | })), 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/routes/xueqiu/favorite.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = ctx.params.id; 5 | 6 | const res1 = await got({ 7 | method: 'get', 8 | url: 'https://xueqiu.com/', 9 | }); 10 | const token = res1.headers['set-cookie'].find((s) => s.startsWith('xq_a_token=')).split(';')[0]; 11 | 12 | const res2 = await got({ 13 | method: 'get', 14 | url: 'https://xueqiu.com/favorites.json', 15 | params: { 16 | userid: id, 17 | }, 18 | headers: { 19 | Cookie: token, 20 | Referer: `https://xueqiu.com/u/${id}`, 21 | }, 22 | }); 23 | const data = res2.data.list; 24 | 25 | ctx.state.data = { 26 | title: `ID: ${id} 的雪球收藏动态`, 27 | link: `https://xueqiu.com/u/${id}`, 28 | description: `ID: ${id} 的雪球收藏动态`, 29 | item: data.map((item) => ({ 30 | title: item.title, 31 | description: item.description, 32 | pubDate: new Date(item.created_at).toUTCString(), 33 | link: `https://xueqiu.com${item.target}`, 34 | })), 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /lib/routes/yicai/brief.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const baseUrl = 'https://www.yicai.com'; 5 | const url = `${baseUrl}/api/ajax/getbrieflist`; 6 | const query = { 7 | pagesize: 30, 8 | type: 0, 9 | page: 0, 10 | }; 11 | const response = await got(url, { 12 | query, 13 | }); 14 | 15 | const resList = response.data; 16 | const item = resList.map((obj) => { 17 | const { datekey, istop, url, newcontent, hm, indexTitle } = obj; 18 | const dateStr = `${datekey.replace('.', '-')} ${hm}`; 19 | return { 20 | title: indexTitle.length === newcontent.length ? indexTitle : `${indexTitle}...`, 21 | description: [istop ? '置顶:' : '', newcontent].filter((str) => !!str).join('
'), 22 | pubDate: new Date(dateStr).toUTCString(), 23 | link: `${baseUrl}${url}`, 24 | }; 25 | }); 26 | 27 | ctx.state.data = { 28 | title: `第一财经 - 直播区`, 29 | description: `第一财经 - 直播区`, 30 | link: `${baseUrl}/brief`, 31 | item, 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/routes/youzan/goods.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const id = String(ctx.params.id); 5 | 6 | const link = `https://h5.youzan.com/wscshop/feature/goods/all?kdt_id=${id}`; 7 | const api_link = `https://h5.youzan.com/wscshop/showcase/goods/allGoods.json?pageSize=20&page=1&offlineId=0&order=&order_by=createdTime&kdt_id=${id}`; 8 | 9 | const response = await got({ 10 | method: 'get', 11 | url: api_link, 12 | headers: { 13 | Referer: link, 14 | }, 15 | }); 16 | const goods = response.data.data.list; 17 | 18 | const out = goods.map((good) => { 19 | const item = { 20 | title: good.title, 21 | description: `价格:${good.price}
`, 22 | link: `https://detail.youzan.com/show/goods?alias=${good.alias}`, 23 | }; 24 | return item; 25 | }); 26 | 27 | ctx.state.data = { 28 | title: `${id} 商铺上新`, 29 | link: link, 30 | item: out, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/routes/zhihu/bookstore/newest.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | 3 | module.exports = async (ctx) => { 4 | const response = await got({ 5 | method: 'get', 6 | url: 'https://api.zhihu.com/books/features/new', 7 | }); 8 | 9 | const data = response.data.data; 10 | 11 | ctx.state.data = { 12 | title: '知乎书店-新书抢鲜', 13 | link: 'https://www.zhihu.com/pub/features/new', 14 | item: data.map((item) => { 15 | const authors = item.authors.map((author) => author.name).join('、'); 16 | 17 | return { 18 | title: item.title, 19 | link: item.url, 20 | description: ` 21 |
22 | ${item.title}
23 | 作者: ${authors}

24 | ${item.description}

25 | 价格: ${item.promotion.price / 100}元 26 | `, 27 | }; 28 | }), 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/routes/zhihu/pin/daily.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { generateData } = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const { 6 | data: { data }, 7 | } = await got({ 8 | method: 'get', 9 | url: 'https://api.zhihu.com/pins/special/972884951192113152/moments?order_by=newest&reverse_order=0&limit=20', 10 | }); 11 | 12 | ctx.state.data = { 13 | title: '知乎想法-24小时新闻汇总', 14 | link: 'https://www.zhihu.com/pin/special/972884951192113152', 15 | description: '汇集每天的社会大事、行业资讯,让你用最简单的方式获得想法里的新闻', 16 | item: generateData(data), 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/routes/zhihu/pin/hotlist.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { generateData } = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const { 6 | data: { data }, 7 | } = await got({ 8 | method: 'get', 9 | url: 'https://api.zhihu.com/pins/hot_list?reverse_order=0', 10 | }); 11 | 12 | ctx.state.data = { 13 | title: '知乎想法热榜', 14 | link: 'https://www.zhihu.com/', 15 | description: '整点更新', 16 | item: generateData(data), 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/routes/zhihu/pin/people.js: -------------------------------------------------------------------------------- 1 | const got = require('@/utils/got'); 2 | const { generateData } = require('./utils'); 3 | 4 | module.exports = async (ctx) => { 5 | const { id } = ctx.params; 6 | 7 | const { 8 | data: { data }, 9 | } = await got({ 10 | method: 'get', 11 | url: `https://www.zhihu.com/api/v4/members/${id}/pins?offset=0&limit=20&includes=data%5B*%5D.upvoted_followees%2Cadmin_closed_comment`, 12 | }); 13 | 14 | ctx.state.data = { 15 | title: `${data[0].author.name}的知乎想法`, 16 | link: `https://www.zhihu.com/people/${id}/pins`, 17 | item: generateData(data), 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/routes/zimuzu/resource.js: -------------------------------------------------------------------------------- 1 | const parser = require('@/utils/rss-parser'); 2 | 3 | module.exports = async (ctx) => { 4 | const { id = 0 } = ctx.params; 5 | const feed = await parser.parseURL(`http://rss.rrys.tv/rss/feed/${id}`); 6 | feed.items.map((item) => { 7 | item.link = null; 8 | item.enclosure_url = item.magnet; 9 | item.enclosure_type = 'application/x-bittorrent'; 10 | return item; 11 | }); 12 | 13 | ctx.state.data = { 14 | title: feed.title, 15 | link: feed.link, 16 | description: feed.description, 17 | item: feed.items, 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/utils/common-utils.js: -------------------------------------------------------------------------------- 1 | // convert a string into title case 2 | const toTitleCase = (str) => 3 | str 4 | .toLowerCase() 5 | .split(' ') 6 | .map((word) => word.replace(word[0], word[0].toUpperCase())) 7 | .join(' '); 8 | 9 | module.exports = { 10 | toTitleCase, 11 | }; 12 | -------------------------------------------------------------------------------- /lib/utils/logger.js: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | const config = require('@/config'); 3 | 4 | const logger = winston.createLogger({ 5 | level: config.loggerLevel, 6 | format: winston.format.json(), 7 | transports: [ 8 | // 9 | // - Write to all logs with level `info` and below to `combined.log` 10 | // - Write all logs error (and below) to `error.log`. 11 | // 12 | new winston.transports.File({ 13 | filename: 'logs/error.log', 14 | level: 'error', 15 | }), 16 | new winston.transports.File({ filename: 'logs/combined.log' }), 17 | ], 18 | }); 19 | 20 | // 21 | // If we're not in production then log to the `console` with the format: 22 | // `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` 23 | // 24 | logger.add( 25 | new winston.transports.Console({ 26 | format: winston.format.combine(winston.format.colorize(), winston.format.simple()), 27 | silent: process.env.NODE_ENV === 'test', 28 | }) 29 | ); 30 | 31 | module.exports = logger; 32 | -------------------------------------------------------------------------------- /lib/utils/md5.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | module.exports = function md5(date) { 4 | return crypto 5 | .createHash('md5') 6 | .update(date) 7 | .digest('hex'); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/utils/puppeteer.js: -------------------------------------------------------------------------------- 1 | const config = require('@/config'); 2 | const puppeteer = require('puppeteer'); 3 | 4 | const options = { 5 | args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-infobars', '--window-position=0,0', '--ignore-certifcate-errors', '--ignore-certifcate-errors-spki-list', `--user-agent=${config.ua}`], 6 | headless: true, 7 | ignoreHTTPSErrors: true, 8 | userDataDir: './tmp', 9 | }; 10 | 11 | module.exports = async () => { 12 | let browser; 13 | if (config.puppeteerWSEndpoint) { 14 | browser = await puppeteer.connect({ 15 | browserWSEndpoint: config.puppeteerWSEndpoint, 16 | }); 17 | } else { 18 | browser = await puppeteer.launch(options); 19 | } 20 | setTimeout(async () => { 21 | if ((await browser.process()).exitCode === null) { 22 | browser.close(); 23 | } 24 | }, 10000); 25 | 26 | return browser; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/utils/rss-parser.js: -------------------------------------------------------------------------------- 1 | const config = require('@/config'); 2 | const Parser = require('rss-parser'); 3 | 4 | const parser = new Parser({ 5 | customFields: { 6 | item: ['magnet'], 7 | }, 8 | headers: { 9 | 'User-Agent': config.ua, 10 | }, 11 | }); 12 | 13 | module.exports = parser; 14 | -------------------------------------------------------------------------------- /lib/utils/wait.js: -------------------------------------------------------------------------------- 1 | module.exports = function wait(ms) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, ms); 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Remsh/RSSHub/2ca197b622d453e9cca3207a4615e3dc5801a3e1/logs/.gitkeep -------------------------------------------------------------------------------- /process.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [ 3 | { 4 | "name": "rsshub", 5 | "script": "lib/index.js", 6 | "instances": "max", 7 | "exec_mode": "cluster", 8 | "env": { 9 | "NODE_ENV": "production" 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "prHourlyLimit": 0, 6 | "separateMajorMinor": false 7 | } 8 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | jest: true 3 | -------------------------------------------------------------------------------- /test/middleware/error.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | const { server } = require('../../lib/index'); 3 | const request = supertest(server); 4 | 5 | afterAll(() => { 6 | server.close(); 7 | }); 8 | 9 | describe('error', () => { 10 | it(`error`, async () => { 11 | const response = await request.get('/test/0'); 12 | expect(response.status).toBe(404); 13 | expect(response.text).toMatch(/RSSHub 发生了一些意外:
Error: Error test/);
14 |     });
15 | });
16 | 


--------------------------------------------------------------------------------
/test/middleware/header.js:
--------------------------------------------------------------------------------
 1 | process.env.NODE_NAME = 'mock';
 2 | 
 3 | const supertest = require('supertest');
 4 | const { server } = require('../../lib/index');
 5 | const request = supertest(server);
 6 | const config = require('../../lib/config');
 7 | 
 8 | afterAll(() => {
 9 |     server.close();
10 | });
11 | 
12 | describe('header', () => {
13 |     it(`header`, async () => {
14 |         const response = await request.get('/test/1');
15 |         expect(response.headers['access-control-allow-origin']).toBe('127.0.0.1:1200');
16 |         expect(response.headers['access-control-allow-methods']).toBe('GET');
17 |         expect(response.headers['content-type']).toBe('application/xml; charset=utf-8');
18 |         expect(response.headers['cache-control']).toBe(`public, max-age=${config.cache.routeExpire}`);
19 |         expect(response.headers['last-modified']).toBe(response.text.match(/(.*)<\/lastBuildDate>/)[1]);
20 |         expect(response.headers['rsshub-node']).toBe('mock');
21 |     });
22 | });
23 | 


--------------------------------------------------------------------------------
/test/utils/common-utils.js:
--------------------------------------------------------------------------------
1 | const utils = require('../../lib/utils/common-utils');
2 | 
3 | describe('common-utils', () => {
4 |     it('toTitleCase', async () => {
5 |         expect(utils.toTitleCase('RSSHub IS AS aweSOme aS henry')).toBe('Rsshub Is As Awesome As Henry');
6 |     });
7 | });
8 | 


--------------------------------------------------------------------------------
/test/utils/md5.js:
--------------------------------------------------------------------------------
1 | const md5 = require('../../lib/utils/md5');
2 | 
3 | describe('md5', () => {
4 |     it('md5 RSSHub', async () => {
5 |         expect(md5('RSSHub')).toBe('3187d745ec5983413e4f0dce3900d92d');
6 |     });
7 | });
8 | 


--------------------------------------------------------------------------------
/test/utils/puppeteer.js:
--------------------------------------------------------------------------------
 1 | const puppeteer = require('../../lib/utils/puppeteer');
 2 | const wait = require('../../lib/utils/wait');
 3 | 
 4 | describe('puppeteer', () => {
 5 |     it('puppeteer run', async () => {
 6 |         const browser = await puppeteer();
 7 |         const page = await browser.newPage();
 8 |         await page.goto('https://www.google.com', {
 9 |             waitUntil: 'domcontentloaded',
10 |         });
11 | 
12 |         // eslint-disable-next-line no-undef
13 |         const html = await page.evaluate(() => document.body.innerHTML);
14 |         expect(html.length).toBeGreaterThan(0);
15 | 
16 |         expect((await browser.process()).exitCode).toBe(null);
17 |         await wait(10 * 1000);
18 |         expect((await browser.process()).exitCode).toBe(0);
19 |     }, 20000);
20 | });
21 | 


--------------------------------------------------------------------------------
/test/utils/rss-parser.js:
--------------------------------------------------------------------------------
 1 | const parser = require('../../lib/utils/rss-parser');
 2 | const config = require('../../lib/config');
 3 | const nock = require('nock');
 4 | 
 5 | describe('got', () => {
 6 |     it('headers', async () => {
 7 |         nock('http://rsshub.test')
 8 |             .get('/test')
 9 |             .reply(function() {
10 |                 expect(this.req.headers['user-agent']).toBe(config.ua);
11 |                 return [200, ''];
12 |             });
13 | 
14 |         await parser.parseURL('http://rsshub.test/test');
15 |     });
16 | });
17 | 


--------------------------------------------------------------------------------
/test/utils/wait.js:
--------------------------------------------------------------------------------
 1 | const wait = require('../../lib/utils/wait');
 2 | 
 3 | describe('wait', () => {
 4 |     it('wait 0.1 second', async () => {
 5 |         const startDate = new Date();
 6 | 
 7 |         await wait(0.1 * 1000);
 8 | 
 9 |         const endDate = new Date();
10 |         expect(endDate - startDate).toBeGreaterThan(90);
11 |         expect(endDate - startDate).toBeLessThan(110);
12 |     });
13 | });
14 | 


--------------------------------------------------------------------------------