mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-24 23:12:34 +08:00
Merge branch 'master' into booth-pm
This commit is contained in:
@@ -11,3 +11,42 @@ Close #
|
||||
To simplify the testing workflow, please include the complete route, with all required and optional parameters, otherwise your pull request will be closed.
|
||||
|
||||
-->
|
||||
|
||||
## 新RSS检查列表 / New RSS Script Checklist
|
||||
|
||||
<!--
|
||||
|
||||
Please go over the checklist below before PR: this improve your PR pass rate.
|
||||
|
||||
Reference: https://docs.rsshub.app/en/joinus/
|
||||
|
||||
请在提交PR前检查以下事项: 这可以提升PR通过率
|
||||
|
||||
参考: https://docs.rsshub.app/joinus
|
||||
|
||||
-->
|
||||
|
||||
- [ ] 这是在提交一个新的RSS吗? Is this a new RSS Script?
|
||||
- **如果不是, 请留空本列表**. **LEAVE BLANK** if it is not a submitting new RSS Script
|
||||
- [ ] 是否提供了文档? Documentation provided?
|
||||
- [ ] 是否提供了英文文档? EN Documentation provided?
|
||||
- [ ] 是否支持全文获取? Is this RSS Script support fulltext?
|
||||
- [ ] 如果全文获取中需要访问文章链接, 是否使用了缓存? If fulltext requires to fetch detail pages, is cache used in the process?
|
||||
- [缓存说明](https://docs.rsshub.app/joinus/#ti-jiao-xin-de-rsshub-gui-ze-bian-xie-jiao-ben-shi-yong-huan-cun) | [How to use cache](https://docs.rsshub.app/joinus/#ti-jiao-xin-de-rsshub-gui-ze-bian-xie-jiao-ben-shi-yong-huan-cun)
|
||||
- [ ] 目标是否有明显的反爬/频率限制? Is there any sign of anti-bot or rate limit?
|
||||
- [ ] 如果有, 是否有对应的措施? (延长缓存时间, 写文档说明, etc.) If yes, do your code reflect this sign? (e.g. write documentations, use long cache time)
|
||||
- [ ] 是否引入的新的包? Any new package introduced?
|
||||
- 如果有, 请说明原因. If yes, please state your reason
|
||||
- [ ] 是否使用了`Puppeteer`? Make use of `Puppeteer`?
|
||||
- 如果有, 请说明原因. If yes, please state your reason
|
||||
|
||||
|
||||
## 说明 / Note
|
||||
|
||||
<!--
|
||||
|
||||
Please state your reason/note here
|
||||
|
||||
请在这里描述你的原因或留下其他相关的说明
|
||||
|
||||
-->
|
||||
|
||||
@@ -2500,4 +2500,18 @@
|
||||
},
|
||||
],
|
||||
},
|
||||
'scboy.com': {
|
||||
_name: 'scboy 论坛',
|
||||
www: [
|
||||
{
|
||||
title: '帖子',
|
||||
docs: 'https://docs.rsshub.app/bbs.html#scboy',
|
||||
source: '',
|
||||
target: (params, url) => {
|
||||
const id = url.includes('thread') ? url.split('-')[1].split('.')[0] : '';
|
||||
return id ? `/scboy/thread/${id}` : '';
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
+29
@@ -180,6 +180,17 @@ pageClass: routes
|
||||
|
||||
</Route>
|
||||
|
||||
## SCBOY 论坛
|
||||
|
||||
### 帖子
|
||||
|
||||
<Route author="totorowechat" example="/scboy/thread/188673" path="/scboy/thread/:tid" :paramsDesc="['帖子 tid']" radar="1">
|
||||
|
||||
帖子网址如果为 <https://www.scboy.com/?thread-188673.htm> 那么帖子 tid 就是 `1789863`。
|
||||
|
||||
访问水区需要添加环境变量 `SCBOY_BBS_TOKEN`, 详情见部署页面的配置模块。 `SCBOY_BBS_TOKEN`在 cookies 的`bbs_token`中。
|
||||
</Route>
|
||||
|
||||
## V2EX
|
||||
|
||||
### 最热 / 最新主题
|
||||
@@ -395,6 +406,24 @@ pageClass: routes
|
||||
|
||||
</Route>
|
||||
|
||||
## 品葱
|
||||
|
||||
### 发现
|
||||
|
||||
<Route author="zphw" example="/pincong/category/1/new" path="/pincong/category/:category?/:sort?" :paramsDesc="['分类,与官网分类 URL `category-` 后的数字对应,默认为全部', '排序方式,参数可见下表,默认为推荐']" anticrawler="1" />
|
||||
|
||||
| 最新 | 推荐 | 热门 |
|
||||
| ---- | --------- | ---- |
|
||||
| new | recommend | hot |
|
||||
|
||||
### 精选
|
||||
|
||||
<Route author="zphw" example="/pincong/hot" path="/pincong/hot/:category?" :paramsDesc="['分类,与官网分类 URL `category-` 后的数字对应,默认为全部']" anticrawler="1" />
|
||||
|
||||
### 话题
|
||||
|
||||
<Route author="zphw" example="/pincong/topic/美国" path="/pincong/topic/:topic?" :paramsDesc="['话题,可在官网获取']" anticrawler="1" />
|
||||
|
||||
## 三星盖乐世社区
|
||||
|
||||
### 最新帖子
|
||||
|
||||
@@ -104,6 +104,35 @@ $ docker run -d --name rsshub -p 1200:1200 -e CACHE_EXPIRE=3600 -e GITHUB_ACCESS
|
||||
|
||||
To configure more options please refer to [Configuration](#configuration).
|
||||
|
||||
# Ansible Deployment
|
||||
|
||||
This Ansible playbook includes RSSHub, Redis, browserless (uses Docker) and Caddy 2
|
||||
|
||||
Currently only support Ubuntu 20.04
|
||||
|
||||
Requires sudo privilege and virtualization capability (Docker will be automatically installed)
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ansible
|
||||
git clone https://github.com/DIYgod/RSSHub.git ~/RSSHub
|
||||
cd ~/RSSHub/scripts/ansible
|
||||
sudo ansible-playbook rsshub.yaml
|
||||
# When prompt to enter a domain name, enter the domain name that this machine/VM will use
|
||||
# For example, if your users use https://rsshub.exmaple.com to access your RSSHub instance, enter rsshub.exmaple.com (remove the https://)
|
||||
```
|
||||
|
||||
### Update
|
||||
|
||||
```bash
|
||||
cd ~/RSSHub/scripts/ansible
|
||||
sudo ansible-playbook rsshub.yaml
|
||||
# When prompt to enter a domain name, enter the domain name that this machine/VM will use
|
||||
# For example, if your users use https://rsshub.exmaple.com to access your RSSHub instance, enter rsshub.exmaple.com (remove the https://)
|
||||
```
|
||||
|
||||
## Manual Deployment
|
||||
|
||||
The most direct way to deploy `RSSHub`, you can follow the steps below to deploy`RSSHub` on your computer, server or anywhere.
|
||||
|
||||
@@ -67,6 +67,10 @@ Compared to the official one, the RSS feed generated by RSSHub not only has more
|
||||
|
||||
## CGTN
|
||||
|
||||
### Opinions
|
||||
|
||||
<RouteEn author="nczitzk" example="/cgtn/opinions" path="/cgtn/opinions"/>
|
||||
|
||||
### Most Read & Most Share
|
||||
|
||||
<RouteEn author="nczitzk" example="/cgtn/most/read/day" path="/cgtn/most/:type?/:time?" :paramsDesc="['Type, `read` as most read, `share` as most share, `read` by default', 'Time range, `all` as all the time, `day` as today, `week` as this week, `month` as this month, `year` as this year, `all` by default']"/>
|
||||
|
||||
@@ -42,6 +42,10 @@ pageClass: routes
|
||||
|
||||
<RouteEn author="FHYunCai" example="/bing" path="/bing" radar="1" rssbud="1"/>
|
||||
|
||||
## ComicsKingdom Comic Strips
|
||||
|
||||
<RouteEn author="stjohnjohnson" example="/comicskingdom/baby-blues" path="/comicskingdom/:strip" :paramsDesc="['URL path of the strip on comicskingdom.com']" />
|
||||
|
||||
## DailyArt
|
||||
|
||||
<RouteEn author="zphw" example="/dailyart/en" path="/dailyart/:language?" :paramsDesc="['Support en, es, fr, de, it, zh, jp, etc. English by default.']" />
|
||||
@@ -50,6 +54,10 @@ pageClass: routes
|
||||
|
||||
<RouteEn name="Daily Strip" author="Maecenas" example="/dilbert/strip" path="/dilbert/strip"/>
|
||||
|
||||
## GoComics Comic Strips
|
||||
|
||||
<RouteEn author="stjohnjohnson" example="/gocomics/foxtrot" path="/gocomics/:strip" :paramsDesc="['URL path of the strip on gocomics.com']" />
|
||||
|
||||
## Google Doodles
|
||||
|
||||
### Update
|
||||
|
||||
@@ -164,6 +164,12 @@ The owner of the official image fills in the library, for example: https://rsshu
|
||||
|
||||
<RouteEn author="hoilc" example="/edge/addon/gangkeiaobmjcjokiofpkfpcobpbmnln" path="/edge/addon/:crxid" :paramsDesc="['Addon id, can be found in addon url']"/>
|
||||
|
||||
## Microsoft Store
|
||||
|
||||
### Updates
|
||||
|
||||
<Route author="hellodword" example="/microsoft-store/updates/9WZDNCRFHVN5/CN" path="/microsoft-store/updates/:productid/:market?" :paramsDesc="['`Share` - `Copy Link` in the Store', '`CN` as default']" />
|
||||
|
||||
## Minecraft
|
||||
|
||||
Refer to [#minecraft](/en/game.html#minecraft)
|
||||
|
||||
@@ -20,6 +20,10 @@ Site
|
||||
|
||||
## AP News
|
||||
|
||||
### Top Stories
|
||||
|
||||
<RouteEn author="zphw" example="/apnews" path="/apnews" />
|
||||
|
||||
### Topics
|
||||
|
||||
<RouteEn author="zoenglinghou" example="/apnews/topics/apf-topnews" path="/apnews/topics/:topic" :paramsDesc="['Topic name,can be found in URL. For example: the topic name of AP Top News [https://apnews.com/apf-topnews](https://apnews.com/apf-topnews) is `apf-topnews`']" radar="1" rssbud="1"/>
|
||||
@@ -149,6 +153,18 @@ Generates full-text feeds that the official feed doesn't provide.
|
||||
|
||||
<RouteEn author="oppliate" example="/phoronix/news_topic/Intel" path="/phoronix/:page/:queryOrItem?" :paramsDesc="['Page name', 'For `category` it corresponds to `item`, for other pages it\'s `q`. You may find available parameters from their navigator links. E.g. to subscribe to the category page `https://www.phoronix.com/scan.php?page=category&item=Computers`, fill in the path `/phoronix/category/Computers`']" />
|
||||
|
||||
## Radio Free Asia (RFA)
|
||||
|
||||
<RouteEn author="zphw" example="/rfa/english" path="/rfa/:language?/:channel?/:subChannel?" :paramsDesc="['language, English by default', 'channel', 'subchannel, where applicable']" />
|
||||
|
||||
Delivers a better experience by supporting parameter specification.
|
||||
|
||||
Parameters can be obtained from the official website, for instance:
|
||||
|
||||
`https://www.rfa.org/cantonese/news` corresponds to `/rfa/cantonese/news`
|
||||
|
||||
`https://www.rfa.org/cantonese/news/htm` corresponds to `/rfa/cantonese/news/htm`
|
||||
|
||||
## Reuters
|
||||
|
||||
### Channel
|
||||
|
||||
@@ -185,6 +185,18 @@ pageClass: routes
|
||||
|
||||
<Route author="Yoge-Code" example="/gov/cnca/zxtz" path="/gov/cnca/zxtz"/>
|
||||
|
||||
## 中国农工民主党
|
||||
|
||||
### 新闻中心
|
||||
|
||||
<Route author="nczitzk" example="/ngd" path="/ngd/:slug?" :paramsDesc="['见下文']">
|
||||
|
||||
将目标栏目的网址拆解为 `http://www.ngd.org.cn/` 和后面的字段,去掉 `.htm` 后,把后面的字段中的 `/` 替换为 `-`,即为该路由的 slug
|
||||
|
||||
如:(要闻动态)[http://www.ngd.org.cn/xwzx/ywdt/index.htm] 的网址在 `http://www.ngd.org.cn/` 后的字段是 `xwzx/ywdt/index.htm`,则对应的 slug 为 `xwzx-ywdt-index`,对应的路由即为 `/ngd/xwzx-ywdt-index`
|
||||
|
||||
</Route>
|
||||
|
||||
## 中国人大网
|
||||
|
||||
<Route author="233yeee" example="/npc/c183" path="/npc/:caty" :paramsDesc="['分类名,支持形如`http://www.npc.gov.cn/npc/*/list.shtml`的网站,传入 npc 之后的参数']">
|
||||
|
||||
@@ -106,6 +106,35 @@ $ docker run -d --name rsshub -p 1200:1200 -e CACHE_EXPIRE=3600 -e GITHUB_ACCESS
|
||||
|
||||
更多配置项请看 [#配置](#pei-zhi)
|
||||
|
||||
## Ansible 部署
|
||||
|
||||
这个 Ansible playbook 包括了 RSSHub, Redis, browserless (依赖 Docker) 以及 Caddy 2
|
||||
|
||||
目前只支持 Ubuntu 20.04
|
||||
|
||||
需要 sudo 权限和虚拟化能力(Docker 将会被自动安装)
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ansible
|
||||
git clone https://github.com/DIYgod/RSSHub.git ~/RSSHub
|
||||
cd ~/RSSHub/scripts/ansible
|
||||
sudo ansible-playbook rsshub.yaml
|
||||
# 当提示输入 domain name 的时候,输入该主机所使用的域名
|
||||
# 举例:如果您的 RSSHub 用户使用 https://rsshub.exmaple.com 访问您的 RSSHub 实例,输入 rsshub.exmaple.com(去掉 https://)
|
||||
```
|
||||
|
||||
### 更新
|
||||
|
||||
```bash
|
||||
cd ~/RSSHub/scripts/ansible
|
||||
sudo ansible-playbook rsshub.yaml
|
||||
# 当提示输入 domain name 的时候,输入该主机所使用的域名
|
||||
# 举例:如果您的 RSSHub 用户使用 https://rsshub.exmaple.com 访问您的 RSSHub 实例,输入 rsshub.exmaple.com(去掉 https://)
|
||||
```
|
||||
|
||||
## 手动部署
|
||||
|
||||
部署 `RSSHub` 最直接的方式,您可以按照以下步骤将 `RSSHub` 部署在您的电脑、服务器或者其他任何地方
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ pageClass: routes
|
||||
|
||||
### 直播分区
|
||||
|
||||
<Route author="Qixingchen" example="/bilibili/live/area/143/online" path="/bilibili/live/area/:areaID/:order" :paramsDesc="['分区 ID 分区增删较多, 可通过 [分区列表](https://api.live.bilibili.com/room/v1/Area/getList) 查询', '排序方式, live_time 开播时间, online 人气']">
|
||||
<Route author="Qixingchen" example="/bilibili/live/area/207/online" path="/bilibili/live/area/:areaID/:order" :paramsDesc="['分区 ID 分区增删较多, 可通过 [分区列表](https://api.live.bilibili.com/room/v1/Area/getList) 查询', '排序方式, live_time 开播时间, online 人气']">
|
||||
|
||||
::: warning 注意
|
||||
|
||||
|
||||
@@ -122,6 +122,10 @@ pageClass: routes
|
||||
|
||||
## CGTN
|
||||
|
||||
### Opinions
|
||||
|
||||
<Route author="nczitzk" example="/cgtn/opinions" path="/cgtn/opinions"/>
|
||||
|
||||
### Most Read & Most Share
|
||||
|
||||
<Route author="nczitzk" example="/cgtn/most/read/day" path="/cgtn/most/:type?/:time?" :paramsDesc="['类型,`read` 指最多阅读,`share` 指最多分享,默认为 `read`', '时间,`all` 指所有时间,`day` 指今天,`week` 指本周,`month` 指本月,`year` 指今年,默认为 `all`']"/>
|
||||
@@ -2028,6 +2032,9 @@ column 为 third 时可选的 category:
|
||||
|
||||
<Route author="nczitzk" example="/interesting-sky/recent-interesting" path="/interesting-sky/recent-interesting"/>
|
||||
|
||||
## 鱼塘热榜
|
||||
|
||||
<Route author="TheresaQWQ" example="/mofish/2" path="/mofish/:id" :paramsDesc="['分类id,可以在 https://api.tophub.fun/GetAllType 获取']" />
|
||||
## 遠見
|
||||
|
||||
<Route author="laampui" example="/gvm/index/health" path="/gvm/index/:category?" :paramsDesc="['見下表, 默認爲 newest']">
|
||||
|
||||
@@ -212,6 +212,12 @@ pageClass: routes
|
||||
|
||||
<Route author="hoilc" example="/edge/addon/gangkeiaobmjcjokiofpkfpcobpbmnln" path="/edge/addon/:crxid" :paramsDesc="['扩展 id, 可在扩展页 URL 中找到']" />
|
||||
|
||||
## Microsoft Store
|
||||
|
||||
### Updates
|
||||
|
||||
<Route author="hellodword" example="/microsoft-store/updates/9WZDNCRFHVN5/CN" path="/microsoft-store/updates/:productid/:market?" :paramsDesc="['在 Store 中点击 `分享` - `复制链接` 即可获得', '默认为 `CN`']" />
|
||||
|
||||
## Minecraft
|
||||
|
||||
见 [#minecraft](/game.html#minecraft)
|
||||
@@ -310,6 +316,16 @@ pageClass: routes
|
||||
|
||||
<Route author="nczitzk" example="/sesame/release_notes" path="/sesame/release_notes"/>
|
||||
|
||||
## simpread
|
||||
|
||||
### 消息通知
|
||||
|
||||
<Route author="zytomorrow" example="/simpread/notice" path="/simpread/notice"/>
|
||||
|
||||
### 更新日志
|
||||
|
||||
<Route author="zytomorrow" example="/simpread/changelog" path="/simpread/changelog"/>
|
||||
|
||||
## sketch.com
|
||||
|
||||
### beta 更新
|
||||
|
||||
@@ -172,6 +172,18 @@ For instance, in <https://www.leboncoin.fr/recherche/?**category=10&locations=Pa
|
||||
|
||||
<Route author="sanmmm" example="/liwushuo/index" path="/liwushuo/index"/>
|
||||
|
||||
## 麦当劳
|
||||
|
||||
### 麦当劳活动资讯
|
||||
|
||||
<Route author="huyyi" example="/mcdonalds/sales+event" path="/mcdonalds/:category" :paramsDesc="['分类名(可用+连接多个分类)']">
|
||||
|
||||
| 全部分类 | 社会责任 | 人员品牌 | 产品故事 | 优惠 | 品牌文化 | 活动速报 |
|
||||
| --------- | -------------- | -------- | -------- | ----- | -------- | -------- |
|
||||
| news_list | responsibility | brand | product | sales | culture | event |
|
||||
|
||||
</Route>
|
||||
|
||||
## 缺书网
|
||||
|
||||
### 促销
|
||||
|
||||
@@ -26,6 +26,10 @@ pageClass: routes
|
||||
|
||||
## AP News
|
||||
|
||||
### 首页头条
|
||||
|
||||
<Route author="zphw" example="/apnews" path="/apnews" />
|
||||
|
||||
### 话题
|
||||
|
||||
<Route author="zoenglinghou" example="/apnews/topics/apf-topnews" path="/apnews/topics/:topic" :paramsDesc="['话题名称,可在 URL 中找到,例如 AP Top News [https://apnews.com/apf-topnews](https://apnews.com/apf-topnews) 的话题为 `apf-topnews`']" radar="1" rssbud="1"/>
|
||||
@@ -181,6 +185,23 @@ pageClass: routes
|
||||
|
||||
<Route author="oppliate" example="/phoronix/news_topic/Intel" path="/phoronix/:page/:queryOrItem?" :paramsDesc="['页面', '对 `category` 页面是分类项目 `item`,对其它页面是主题 `q`,可以在网站顶部导航栏各项目链接里找出。如 `https://www.phoronix.com/scan.php?page=category&item=Computers` 对应 `/phoronix/category/Computers`']" />
|
||||
|
||||
## RTHK 傳媒透視
|
||||
|
||||
<Route author="tpnonthealps" example="/mediadigest/latest" path="/mediadigest/:range" :paramsDesc="['时间范围']">
|
||||
|
||||
细则:
|
||||
|
||||
- `:range` 时间范围参数
|
||||
(可为 `latest` 或 `四位数字的年份`)
|
||||
|
||||
- `latest`: 最新的 50 篇文章
|
||||
- `2020`: 2020 年的所有文章
|
||||
|
||||
- 全文输出转换为简体字: `?opencc=t2s`
|
||||
(`opencc` 是 RSSHub 的通用参数,详情请参阅 [「中文简繁体转换」](https://docs.rsshub.app/parameter.html#zhong-wen-jian-fan-ti-zhuan-huan))
|
||||
|
||||
</Route>
|
||||
|
||||
## Solidot
|
||||
|
||||
### 最新消息
|
||||
@@ -965,3 +986,15 @@ category 对应的关键词有
|
||||
### 九江新闻
|
||||
|
||||
<Route author="jjlzg" example="/fjnews/jjnews" path="/fjnews/jjnews"/>
|
||||
|
||||
## 自由亚洲电台
|
||||
|
||||
<Route author="zphw" example="/rfa/mandarin" path="/rfa/:language?/:channel?/:subChannel?" :paramsDesc="['语言,默认 English', '频道', '子频道(如存在)']" />
|
||||
|
||||
通过指定频道参数,提供比官方源更佳的阅读体验。
|
||||
|
||||
参数均可在官网获取,如:
|
||||
|
||||
`https://www.rfa.org/cantonese/news` 对应 `/rfa/cantonese/news`
|
||||
|
||||
`https://www.rfa.org/cantonese/news/htm` 对应 `/rfa/cantonese/news/htm`
|
||||
|
||||
@@ -185,6 +185,9 @@ const calculateValue = () => {
|
||||
username: envs.DIDA365_USERNAME,
|
||||
password: envs.DIDA365_PASSWORD,
|
||||
},
|
||||
scboy: {
|
||||
token: envs.SCBOY_BBS_TOKEN,
|
||||
},
|
||||
};
|
||||
};
|
||||
calculateValue();
|
||||
|
||||
@@ -2491,6 +2491,7 @@ router.get('/gbcc/trust', require('./routes/gbcc/trust'));
|
||||
|
||||
// Associated Press
|
||||
router.get('/apnews/topics/:topic', require('./routes/apnews/topics'));
|
||||
router.get('/apnews', require('./routes/apnews/index'));
|
||||
|
||||
// CBC
|
||||
router.get('/cbc/topics/:topic?', require('./routes/cbc/topics'));
|
||||
@@ -2853,6 +2854,9 @@ router.get('/xposed/module/:mod', require('./routes/xposed/module'));
|
||||
// Microsoft Edge
|
||||
router.get('/edge/addon/:crxid', require('./routes/edge/addon'));
|
||||
|
||||
// Microsoft Store
|
||||
router.get('/microsoft-store/updates/:productid/:market?', require('./routes/microsoft-store/updates'));
|
||||
|
||||
// 上海立信会计金融学院
|
||||
router.get('/slu/tzgg/:id', require('./routes/universities/slu/tzgg'));
|
||||
router.get('/slu/jwc/:id', require('./routes/universities/slu/jwc'));
|
||||
@@ -3339,6 +3343,7 @@ router.get('/fulinian/:caty?', require('./routes/fulinian/index'));
|
||||
|
||||
// CGTN
|
||||
router.get('/cgtn/most/:type?/:time?', require('./routes/cgtn/most'));
|
||||
router.get('/cgtn/opinions', require('./routes/cgtn/opinions'));
|
||||
|
||||
// AppSales
|
||||
router.get('/appsales/:caty?/:time?', require('./routes/appsales/index'));
|
||||
@@ -3769,6 +3774,9 @@ router.get('/sciencenet/blog/:type?/:time?/:sort?', require('./routes/sciencenet
|
||||
// DailyArt
|
||||
router.get('/dailyart/:language?', require('./routes/dailyart/index'));
|
||||
|
||||
// SCBOY
|
||||
router.get('/scboy/thread/:tid', require('./routes/scboy/thread'));
|
||||
|
||||
// 猿料
|
||||
router.get('/yuanliao/:tag?/:sort?', require('./routes/yuanliao/index'));
|
||||
|
||||
@@ -3781,6 +3789,37 @@ router.get('/nace/blog/:sort?', require('./routes/nace/blog'));
|
||||
// Caixin Latest
|
||||
router.get('/caixin/latest', require('./routes/caixin/latest'));
|
||||
|
||||
// 鱼塘热榜
|
||||
router.get('/mofish/:id', require('./routes/mofish/index'));
|
||||
|
||||
// Mcdonalds
|
||||
router.get('/mcdonalds/:category', require('./routes/mcdonalds/news'));
|
||||
|
||||
// Pincong 品葱
|
||||
router.get('/pincong/category/:category?/:sort?', require('./routes/pincong/index'));
|
||||
router.get('/pincong/hot/:category?', require('./routes/pincong/hot'));
|
||||
router.get('/pincong/topic/:topic', require('./routes/pincong/topic'));
|
||||
|
||||
// GoComics
|
||||
router.get('/gocomics/:name', require('./routes/gocomics/index'));
|
||||
|
||||
// Comics Kingdom
|
||||
router.get('/comicskingdom/:name', require('./routes/comicskingdom/index'));
|
||||
|
||||
// Media Digest
|
||||
router.get('/mediadigest/:range/:category?', require('./routes/mediadigest/category'));
|
||||
|
||||
// 中国农工民主党
|
||||
router.get('/ngd/:slug?', require('./routes/gov/ngd/index'));
|
||||
|
||||
// SimpRead-消息通知
|
||||
router.get('/simpread/notice', require('./routes/simpread/notice'));
|
||||
// SimpRead-更新日志
|
||||
router.get('/simpread/changelog', require('./routes/simpread/changelog'));
|
||||
|
||||
// Radio Free Asia
|
||||
router.get('/rfa/:language?/:channel?/:subChannel?', require('./routes/rfa/index'));
|
||||
|
||||
// booth.pm
|
||||
router.get('/booth.pm/shop/:subdomain', require('./routes/booth-pm/shop'));
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
const cheerio = require('cheerio');
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const response = await got.get('https://apnews.com/');
|
||||
const $ = cheerio.load(response.data);
|
||||
const list = [];
|
||||
|
||||
// main story component
|
||||
const main = $('div[data-key=main-story]').first();
|
||||
|
||||
// headline news
|
||||
const headline = {};
|
||||
headline.title = $(main).find('a[data-key=card-headline] h1').first().text();
|
||||
headline.link = 'https://apnews.com' + $(main).find('a[data-key=card-headline]').first().attr('href');
|
||||
list.push(headline);
|
||||
|
||||
// related stories
|
||||
$(main)
|
||||
.find('a[data-key=related-story-link]')
|
||||
.each(function (_, e) {
|
||||
const item = {};
|
||||
item.title = $(e).find('div[data-key=related-story-headline]').first().text();
|
||||
item.link = 'https://apnews.com' + $(e).attr('href');
|
||||
list.push(item);
|
||||
});
|
||||
|
||||
const result = await Promise.all(
|
||||
list.map(
|
||||
async (item) =>
|
||||
await ctx.cache.tryGet(item.link, async () => {
|
||||
const content = await got.get(item.link);
|
||||
|
||||
const description = cheerio.load(content.data);
|
||||
const metadata = JSON.parse(description('script[type="application/ld+json"]').html());
|
||||
const featureImageURL = metadata.image;
|
||||
|
||||
item.description = `<img src="${featureImageURL}">`;
|
||||
item.description += description('div[class=Article]')
|
||||
.html()
|
||||
.replace(/ADVERTISEMENT/g, '');
|
||||
item.pubDate = new Date(metadata.datePublished).toISOString();
|
||||
item.author = metadata.author[0];
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: 'Associated Press News',
|
||||
link: 'https://apnews.com/',
|
||||
item: result,
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const response = await got({
|
||||
@@ -12,15 +13,33 @@ module.exports = async (ctx) => {
|
||||
|
||||
const data = response.data.data.list;
|
||||
|
||||
const items = await Promise.all(
|
||||
data.map(async (item) => {
|
||||
const link = item.web_url;
|
||||
const summary = `<blockquote><p>${item.summary}</p></blockquote><img src="${item.pics}">`;
|
||||
|
||||
const fullText = await ctx.cache.tryGet(link, async () => {
|
||||
const result = await got.get(link);
|
||||
|
||||
const $ = cheerio.load(result.data);
|
||||
|
||||
return $('#Main_Content_Val').html();
|
||||
});
|
||||
|
||||
return {
|
||||
title: item.title,
|
||||
description: fullText ? summary + fullText : summary,
|
||||
link: link,
|
||||
pubDate: new Date(item.time * 1000),
|
||||
author: item.author_name,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `财新网 - 首页`,
|
||||
link: `http://www.caixin.com/`,
|
||||
description: '财新网 - 首页',
|
||||
item: data.map((item) => ({
|
||||
title: item.title,
|
||||
description: `<p>${item.summary}</p><img src="${item.pics}">`,
|
||||
link: item.web_url,
|
||||
pubDate: new Date(item.time * 1000),
|
||||
})),
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const rootUrl = `https://www.cgtn.com/opinions`;
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
$('.cg-pic').parent().remove();
|
||||
|
||||
const list = $(`.cg-title h4`)
|
||||
.slice(0, 15)
|
||||
.map((_, item) => {
|
||||
item = $(item);
|
||||
const a = item.find('a');
|
||||
return {
|
||||
title: a.text(),
|
||||
link: a.attr('href'),
|
||||
pubDate: new Date(parseInt(a.attr('data-time'))).toUTCString(),
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
const items = await Promise.all(
|
||||
list.map(
|
||||
async (item) =>
|
||||
await ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
item.author = content('.news-author-name').text();
|
||||
item.description = content('#cmsMainContent').html();
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: 'CGTN - Opinions',
|
||||
link: rootUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const baseURL = 'https://www.comicskingdom.com';
|
||||
const name = ctx.params.name;
|
||||
const url = `${baseURL}/${name}/archive`;
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: url,
|
||||
});
|
||||
|
||||
const data = response.data;
|
||||
const $ = cheerio.load(data);
|
||||
|
||||
// Determine Comic and Author from main page
|
||||
const comic = $('title').text().replace('Comics Kingdom - ', '').trim();
|
||||
const author = $('div.author p').text();
|
||||
|
||||
// Find the links for all non-archived items
|
||||
const links = $('div.archive-tile[data-is-blocked=false]')
|
||||
.map((i, el) => $(el).find('a[data-prem="Comic Tile"]').first().attr('href'))
|
||||
.get()
|
||||
.map((url) => `${baseURL}${url}`);
|
||||
|
||||
if (links.length === 0) {
|
||||
throw `Comic Not Found - ${name}`;
|
||||
}
|
||||
const items = await Promise.all(
|
||||
links.map(
|
||||
async (link) =>
|
||||
await ctx.cache.tryGet(link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: link,
|
||||
});
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
const title = content('title').text();
|
||||
const image = content('meta[property="og:image"]').attr('content');
|
||||
const description = `<img src="${image}" />`;
|
||||
// Pull the date out of the URL
|
||||
const pubDate = new Date(link.split('/').slice(-3).join('/')).toUTCString();
|
||||
|
||||
return {
|
||||
title: title,
|
||||
author: author,
|
||||
category: 'comic',
|
||||
description: description,
|
||||
pubDate: pubDate,
|
||||
link: link,
|
||||
};
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${comic} - Comics Kingdom`,
|
||||
link: url,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,19 +1,32 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: 'https://api.douban.com/v2/movie/coming_soon?apikey=0df993c66c0c636e29ecbb5344252a4a',
|
||||
url: 'https://movie.douban.com/cinema/later/beijing/',
|
||||
});
|
||||
const movieList = response.data.subjects;
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const item = $('#showing-soon .item')
|
||||
.map((index, ele) => {
|
||||
const description = $(ele).html();
|
||||
const name = $('h3', ele).text().trim();
|
||||
const date = $('ul li', ele).eq(0).text().trim();
|
||||
const type = $('ul li', ele).eq(1).text().trim();
|
||||
const link = $('a.thumb', ele).attr('href');
|
||||
|
||||
return {
|
||||
title: `${date} - 《${name}》 - ${type}`,
|
||||
link,
|
||||
description,
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
ctx.state.data = {
|
||||
title: '即将上映的电影',
|
||||
link: 'https://movie.douban.com/cinema/later/',
|
||||
item: movieList.map((item) => ({
|
||||
title: item.title,
|
||||
description: `标题:${item.title}<br> 影片类型:${item.genres.join(' | ')} <br>评分:${item.rating.stars === '00' ? '无' : item.rating.average} <br/> <img src="${item.images.large}">`,
|
||||
link: item.alt,
|
||||
})),
|
||||
item,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const baseURL = 'https://www.gocomics.com';
|
||||
const name = ctx.params.name;
|
||||
const limit = ctx.query.limit || 5;
|
||||
const url = `${baseURL}/${name}/`;
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: url,
|
||||
});
|
||||
|
||||
const data = response.data;
|
||||
const $ = cheerio.load(data);
|
||||
|
||||
// Determine Comic and Author from main page
|
||||
const comic = $('.media-heading').eq(0).text();
|
||||
const author = $('.media-subheading').eq(0).text().replace('By ', '');
|
||||
|
||||
// Load previous comic URL
|
||||
const items = [];
|
||||
let previous = $('.gc-deck--cta-0 a').attr('href');
|
||||
|
||||
if (!previous) {
|
||||
throw `Comic Not Found - ${name}`;
|
||||
}
|
||||
|
||||
while (items.length < limit) {
|
||||
const link = `${baseURL}${previous}`;
|
||||
/* eslint-disable no-await-in-loop */
|
||||
const item = await ctx.cache.tryGet(link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: link,
|
||||
});
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
const title = content('h1.m-0').eq(0).text();
|
||||
const image = content('.comic.container').eq(0).attr('data-image');
|
||||
const description = `<img src="${image}" />`;
|
||||
// Pull the date out of the URL
|
||||
const pubDate = new Date(link.split('/').slice(-3).join('/')).toUTCString();
|
||||
const previous = content('.js-previous-comic').eq(0).attr('href');
|
||||
|
||||
return {
|
||||
title: title,
|
||||
author: author,
|
||||
category: 'comic',
|
||||
description: description,
|
||||
pubDate: pubDate,
|
||||
link: link,
|
||||
previous: previous,
|
||||
};
|
||||
});
|
||||
items.push(item);
|
||||
previous = item.previous;
|
||||
}
|
||||
ctx.state.data = {
|
||||
title: `${comic} - GoComics`,
|
||||
link: url,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const slug = ctx.params.slug || 'xwzx-ywdt-index';
|
||||
|
||||
const rootUrl = 'http://www.ngd.org.cn';
|
||||
const currentUrl = `${rootUrl}/${slug.replace(/-/g, '/')}.htm`;
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: currentUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const list = $('.gp-ellipsis a')
|
||||
.slice(0, 15)
|
||||
.map((_, item) => {
|
||||
item = $(item);
|
||||
return {
|
||||
title: item.text(),
|
||||
link: `${currentUrl.replace('/index.htm', '')}/${item.attr('href')}`,
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
const items = await Promise.all(
|
||||
list.map(
|
||||
async (item) =>
|
||||
await ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
const info = content('.articleAuthor').text().split('|');
|
||||
|
||||
item.author = info[0].replace('来源:', '');
|
||||
item.description = content('.gp-article').html();
|
||||
item.pubDate = new Date(info[info.length - 1].replace('发布时间:', '')).toUTCString();
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: $('title').text(),
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -6,19 +6,12 @@ module.exports = async (ctx) => {
|
||||
url: 'http://idaily-cdn.idailycdn.com/api/list/v3/iphone/zh-hans?page=1&ver=iphone',
|
||||
});
|
||||
|
||||
const data = response.data;
|
||||
|
||||
let dataToday = data.filter((item) => item.pubdate_timestamp * 1000 >= new Date().getTime() - 86400000);
|
||||
|
||||
// leverage yesterday's items if today's not published yet
|
||||
if (dataToday.length === 0) {
|
||||
dataToday = data.filter((item) => item.pubdate_timestamp * 1000 >= new Date().getTime() - 86400000 * 2);
|
||||
}
|
||||
const data = response.data.filter((item) => item.ui_sets && item.ui_sets.caption_subtitle).slice(0, 15);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `iDaily 每日环球视野`,
|
||||
description: 'iDaily 每日环球视野',
|
||||
item: dataToday.map((item) => ({
|
||||
item: data.map((item) => ({
|
||||
title: item.ui_sets.caption_subtitle,
|
||||
description: `<img src='${item.cover_landscape}'><br>${item.content}`,
|
||||
pubDate: new Date(item.pubdate_timestamp * 1000).toUTCString(),
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const category_param = ctx.params.category || 'news_list';
|
||||
const categories = category_param.split('+');
|
||||
const baseUrl = 'https://www.mcdonalds.com.cn/news/';
|
||||
|
||||
const get_news_list = async (cates) =>
|
||||
await Promise.all(
|
||||
cates.map(async (cate) => {
|
||||
const response = await got.get(baseUrl + cate);
|
||||
const $ = cheerio.load(response.data);
|
||||
// console.log($('title').text());
|
||||
const news = $('.news_list .box-container > div')
|
||||
.slice(0, 10)
|
||||
.map((idx, item) => {
|
||||
item = $(item);
|
||||
return item.find('a[target]').attr('href');
|
||||
})
|
||||
.get();
|
||||
// console.log(news);
|
||||
return Promise.resolve(news);
|
||||
})
|
||||
);
|
||||
const all_news = [].concat.apply([], await get_news_list(categories));
|
||||
|
||||
const out = await Promise.all(
|
||||
all_news.map(async (news_url) => {
|
||||
const news_detail = await ctx.cache.tryGet(news_url, async () => {
|
||||
const result = await got.get(news_url);
|
||||
const $ = cheerio.load(result.data);
|
||||
|
||||
// console.log($('h3 ~ time').text(), news_url);
|
||||
|
||||
return {
|
||||
title: $('h3').text(),
|
||||
link: news_url,
|
||||
// author: author,
|
||||
pubDate: new Date($('h3 ~ time').text()).toUTCString(),
|
||||
description: $('.cmsPage').html(),
|
||||
};
|
||||
});
|
||||
return Promise.resolve(news_detail);
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: '麦当劳资讯',
|
||||
link: baseUrl,
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
async function getArticle(ctx, list) {
|
||||
const now = list.map(
|
||||
async (line) =>
|
||||
await ctx.cache.tryGet(line, async () => {
|
||||
const a_r = await got.get(`https://app3.rthk.hk/mediadigest/${line}`);
|
||||
const $ = cheerio.load(a_r.data);
|
||||
// title
|
||||
const h1 = $('h1.story-title').text();
|
||||
// author
|
||||
const author_list = $('div.story-author');
|
||||
const authors = author_list.map((_index, author) => $(author).text());
|
||||
const author = authors.map((_index, author) => `<b>${author}</b><br>`);
|
||||
const s_author = author.toArray().join('');
|
||||
const author_block = `<blockquote><p>${s_author}</p></blockquote>`;
|
||||
// date
|
||||
const date = $('div.story-calendar').text();
|
||||
// desc
|
||||
const desc = `${$(author_block)}${$('div.story-content').html()}`;
|
||||
|
||||
return {
|
||||
title: h1,
|
||||
description: desc,
|
||||
pubDate: new Date(`${date}T09:00:00+0800`).toUTCString(),
|
||||
link: line,
|
||||
};
|
||||
})
|
||||
);
|
||||
// 執行一個切片 (20 個文章 URL) 的任務並將結果資料推至 rss 値
|
||||
return await Promise.all(now);
|
||||
}
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const range = ctx.params.range || 'latest';
|
||||
const category = ctx.params.category || 'all';
|
||||
|
||||
// for range validation
|
||||
const num = /^[1-9][0-9]{3}$/;
|
||||
const current_year = new Date().getFullYear();
|
||||
// placeholders
|
||||
let title = '傳媒透視';
|
||||
let rss = [
|
||||
{
|
||||
description: '<p>Invalid <code>:range</code> input.</p><p>所輸入<code>:range</code>參數有誤。</p><p>所输入<code>:range</code>参数有误。</p>',
|
||||
},
|
||||
];
|
||||
// cid
|
||||
const cids = [1, 2];
|
||||
for (let i = 4; i < 28; ++i) {
|
||||
cids.push(i);
|
||||
}
|
||||
|
||||
// latest (50 articles):
|
||||
// if 'all' (latest 50 articles of the site);
|
||||
// else if 'cid' (deprecated) (latest 50 articles of a specific category);
|
||||
// else 'error'.
|
||||
if (range === 'latest') {
|
||||
if (category === 'all') {
|
||||
// 獲取全站文章 URL
|
||||
const urls = cids.map((cid) => `https://app3.rthk.hk/mediadigest/category.php?cid=${cid}`);
|
||||
const list_allt = await Promise.all(
|
||||
urls.map(async (url) => {
|
||||
const response = await got.get(url);
|
||||
const $ = cheerio.load(response.data);
|
||||
const list = $('div.category-line').map((_index, line) => $(line).find('a').first().attr('href'));
|
||||
return Promise.resolve(list.toArray());
|
||||
})
|
||||
);
|
||||
const list_all = [...new Set(list_allt.flat())]; // removed duplicates and flatten
|
||||
// 時序排列並抽取最新 50 項
|
||||
const list = list_all
|
||||
.sort((a, b) => {
|
||||
const aid_a = a.match(/aid=(\d+)/)[1];
|
||||
const aid_b = b.match(/aid=(\d+)/)[1];
|
||||
// reverse
|
||||
return aid_b - aid_a;
|
||||
})
|
||||
.slice(0, 50);
|
||||
// getArticle(ctx, list);
|
||||
rss = await getArticle(ctx, list);
|
||||
} else if (cids.includes(parseInt(category))) {
|
||||
// 獲取特定 category 文章目錄
|
||||
const response = await got.get(`https://app3.rthk.hk/mediadigest/category.php?cid=${category}`);
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const list = $('div.category-line')
|
||||
.map((_index, line) => $(line).find('a').first().attr('href'))
|
||||
.toArray();
|
||||
rss = await getArticle(ctx, list.slice(0, 50));
|
||||
}
|
||||
}
|
||||
// year (specific year range):
|
||||
// if 'all' (latest 200 articles in a specific year range);
|
||||
// else 'error'.
|
||||
else if (num.test(range)) {
|
||||
const range_num = parseInt(range);
|
||||
if (category === 'all' && range_num >= 1970 && range_num <= current_year) {
|
||||
// 獲取全站文章 URL
|
||||
const urls = cids.map((cid) => `https://app3.rthk.hk/mediadigest/category.php?cid=${cid}`);
|
||||
const list_all = await Promise.all(
|
||||
// 每個任務是篩選出一個文章目錄裏需要抓取的文章 URL,以供後續「獲取全文」
|
||||
urls.map(async (url) => {
|
||||
const response = await got.get(url);
|
||||
const $ = cheerio.load(response.data);
|
||||
// 對應文章 URL 與年份
|
||||
// Ref: https://cythilya.github.io/2016/03/13/jquery-map-grep/
|
||||
let list = $('div.category-line')
|
||||
.map((_index, line) => $(line).find('a').first().attr('href'))
|
||||
.toArray();
|
||||
const year = $('div.category-line div.pull-right')
|
||||
.map((_index, date) => new Date($(date).text()).getFullYear())
|
||||
.toArray();
|
||||
list = list.filter((_url, i) => year[i] === range_num);
|
||||
// 篩出 list (需要抓取的文章 URL 並組成數列)
|
||||
return Promise.resolve(list);
|
||||
})
|
||||
);
|
||||
const list = [...new Set(list_all.flat())]; // removed duplicates and flatten
|
||||
rss = await getArticle(ctx, list.slice(0, 200));
|
||||
title = `傳媒透視 - ${range}`;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.state.data = {
|
||||
title: title,
|
||||
link: 'https://app3.rthk.hk/mediadigest/index.php',
|
||||
item: rss,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { market = 'CN', productid } = ctx.params;
|
||||
|
||||
const { data } = await got({
|
||||
method: 'get',
|
||||
url: `https://displaycatalog.mp.microsoft.com/v7.0/products/${productid}/?fieldsTemplate=&market=${market}&languages=en`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'MS-CV': `${Array(16)
|
||||
.join()
|
||||
.split(',')
|
||||
.map(function () {
|
||||
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.charAt(Math.floor(Math.random() * 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.length));
|
||||
})
|
||||
.join('')}.1`,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${data.Product.LocalizedProperties[0].ProductTitle} - Microsoft Store Updates`,
|
||||
link: `https://www.microsoft.com/store/productId/${productid}`,
|
||||
item: [
|
||||
{
|
||||
title: data.Product.DisplaySkuAvailabilities[0].Sku.Properties.Packages[0].PackageFullName,
|
||||
pubDate: new Date(data.Product.DisplaySkuAvailabilities[0].Sku.LastModifiedDate),
|
||||
link: `https://www.microsoft.com/store/productId/${productid}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
const page = ctx.query.page || 0;
|
||||
|
||||
const url = `https://api.tophub.fun/v2/GetAllInfoGzip?id=${id}&page=${page}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: url,
|
||||
});
|
||||
|
||||
const data = response.data.Data.data;
|
||||
|
||||
const title = `鱼塘热榜`;
|
||||
|
||||
ctx.state.data = {
|
||||
title: title,
|
||||
link: `https://mo.fish/`,
|
||||
description: title,
|
||||
item: data.map((item) => ({
|
||||
title: item.Title,
|
||||
pubDate: new Date(item.releaseTime).toUTCString(),
|
||||
link: item.Url,
|
||||
guid: item.id,
|
||||
})),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let url = 'https://pincong.rocks/hot/list/';
|
||||
|
||||
url += ctx.params.category ? 'category-' + ctx.params.category : 'category-0';
|
||||
|
||||
// use Puppeteer due to the obstacle by cloudflare challenge
|
||||
const browser = await require('@/utils/puppeteer')();
|
||||
const page = await browser.newPage();
|
||||
await page.goto(url);
|
||||
const html = await page.evaluate(
|
||||
// eslint-disable-next-line no-undef
|
||||
() => document.documentElement.innerHTML
|
||||
);
|
||||
|
||||
browser.close();
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
const list = $('div.aw-item');
|
||||
|
||||
ctx.state.data = {
|
||||
title: '品葱 - 精选',
|
||||
link: 'https://pincong.rocks/hot/',
|
||||
item: list
|
||||
.map((_, item) => ({
|
||||
title: $(item).find('h2 a').text().trim(),
|
||||
description: $(item).find('div.markitup-box').html(),
|
||||
link: 'https://pincong.rocks' + $(item).find('div.mod-head h2 a').attr('href'),
|
||||
}))
|
||||
.get(),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let url = 'https://pincong.rocks/';
|
||||
|
||||
const sortMap = {
|
||||
new: 'sort_type-new',
|
||||
recommend: 'recommend-1',
|
||||
hot: 'sort_type-hot__day2',
|
||||
};
|
||||
|
||||
url += (ctx.params.sort && sortMap[ctx.params.sort]) || 'recommend-1';
|
||||
url += ctx.params.category ? '__category-' + ctx.params.category : '';
|
||||
|
||||
// use Puppeteer due to the obstacle by cloudflare challenge
|
||||
const browser = await require('@/utils/puppeteer')();
|
||||
const page = await browser.newPage();
|
||||
await page.goto(url);
|
||||
const html = await page.evaluate(
|
||||
// eslint-disable-next-line no-undef
|
||||
() => document.querySelector('div.aw-common-list').innerHTML
|
||||
);
|
||||
|
||||
browser.close();
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
const list = $('div.aw-item');
|
||||
|
||||
ctx.state.data = {
|
||||
title: '品葱 - 发现',
|
||||
link: url,
|
||||
item: list
|
||||
.map((_, item) => ({
|
||||
title: $(item).find('h4 a').text().trim(),
|
||||
link: 'https://pincong.rocks' + $(item).find('h4 a').attr('href'),
|
||||
pubDate: new Date($(item).attr('data-timestamp') * 1000).toISOString(),
|
||||
}))
|
||||
.get(),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const url = 'https://pincong.rocks/topic/' + ctx.params.topic;
|
||||
|
||||
// use Puppeteer due to the obstacle by cloudflare challenge
|
||||
const browser = await require('@/utils/puppeteer')();
|
||||
const page = await browser.newPage();
|
||||
await page.goto(url);
|
||||
const html = await page.evaluate(
|
||||
() =>
|
||||
// eslint-disable-next-line no-undef
|
||||
(document.querySelector('div.aw-common-list') && document.querySelector('div.aw-common-list').innerHTML) || ''
|
||||
);
|
||||
|
||||
browser.close();
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
const list = $('div.aw-item');
|
||||
|
||||
ctx.state.data = {
|
||||
title: `品葱 - ${ctx.params.topic}`,
|
||||
link: url,
|
||||
item: list
|
||||
.map((_, item) => ({
|
||||
title: $(item).find('h4 a').text().trim(),
|
||||
link: 'https://pincong.rocks' + $(item).find('h4 a').attr('href'),
|
||||
pubDate: new Date($(item).attr('data-timestamp') * 1000).toISOString(),
|
||||
}))
|
||||
.get(),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
const cheerio = require('cheerio');
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let url = 'https://www.rfa.org/' + (ctx.params.language || 'english');
|
||||
|
||||
if (ctx.params.channel) {
|
||||
url += '/' + ctx.params.channel;
|
||||
}
|
||||
if (ctx.params.subChannel) {
|
||||
url += '/' + ctx.params.subChannel;
|
||||
}
|
||||
|
||||
const response = await got.get(url);
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const selectors = ['div[id=topstorywidefulltease]', 'div.two_featured', 'div.three_featured', 'div.single_column_teaser', 'div.sectionteaser', 'div.specialwrap'];
|
||||
const list = [];
|
||||
selectors.forEach(function (selector) {
|
||||
$(selector).each(function (_, e) {
|
||||
const item = {};
|
||||
item.title = $(e).find('h2 a span').first().text();
|
||||
item.link = $(e).find('h2 a').first().attr('href');
|
||||
list.push(item);
|
||||
});
|
||||
});
|
||||
|
||||
const result = await Promise.all(
|
||||
list.map(
|
||||
async (item) =>
|
||||
await ctx.cache.tryGet(item.link, async () => {
|
||||
const content = await got.get(item.link);
|
||||
|
||||
const description = cheerio.load(content.data);
|
||||
item.description = description('div[id=storytext]').html();
|
||||
item.pubDate = new Date(description('span[id=story_date]').text()).toUTCString();
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: 'RFA',
|
||||
link: 'https://www.rfa.org/',
|
||||
item: result,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const date = require('@/utils/date');
|
||||
const config = require('@/config').value;
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const tid = ctx.params.tid;
|
||||
const link = `https://www.scboy.com/?thread-${tid}.htm`;
|
||||
let cookieString = 'postlist_orderby=desc';
|
||||
if (config.scboy.token) {
|
||||
cookieString = `postlist_orderby=desc; bbs_token=${config.scboy.token}`;
|
||||
}
|
||||
|
||||
const res = await got.get({
|
||||
method: 'get',
|
||||
url: link,
|
||||
responseType: 'buffer',
|
||||
headers: {
|
||||
Cookie: cookieString,
|
||||
},
|
||||
});
|
||||
|
||||
const $ = cheerio.load(res.data);
|
||||
const title = $('h4 > span:nth-of-type(1)').text();
|
||||
|
||||
const list = $('li.media.post');
|
||||
const count = [];
|
||||
|
||||
for (let i = 0; i < Math.min(list.length, 30); i++) {
|
||||
count.push(i);
|
||||
}
|
||||
|
||||
const resultItems = await Promise.all(
|
||||
count.map(async (i) => {
|
||||
const each = $(list[i]);
|
||||
const floor = each.find('span.floor-parent').text();
|
||||
const item = {
|
||||
title: `${title} #${floor ? floor : ' 热门回复'}`,
|
||||
link: `https://www.scboy.com/?thread-${tid}`,
|
||||
description: each.find('div.message.mt-1.break-all > div:nth-of-type(1)').html(),
|
||||
author: each.find('username').text(),
|
||||
pubDate: date(each.find('.date').text()),
|
||||
};
|
||||
return Promise.resolve(item);
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: title,
|
||||
link: `https://www.scboy.com/?thread-${tid}.htm`,
|
||||
item: resultItems,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const url = 'http://ksria.com/simpread/changelog.html';
|
||||
const response = await got.get(url);
|
||||
const data = response.data;
|
||||
const $ = cheerio.load(data);
|
||||
ctx.state.data = {
|
||||
title: 'SimpRead 更新日志',
|
||||
link: 'https://simpread.pro/changelog.html',
|
||||
description: $('body > div.container.changelog > div.desc').html(),
|
||||
item: $('.version')
|
||||
.map((index, item) => {
|
||||
const year = $(item).find('.year').html();
|
||||
const month_day = $(item).find('.day').html();
|
||||
let version = '';
|
||||
let detail = '';
|
||||
// 版本名称处理
|
||||
version = $(item).find('.num > a').clone().children().remove().end().text();
|
||||
// detail处理
|
||||
detail = $(item).find('.details');
|
||||
if (version === '') {
|
||||
version = $(item).find('.num').clone().children().remove().end().text();
|
||||
}
|
||||
let version_type = $(item).find('.num > a > i').attr('class');
|
||||
// 部分结构不一致处理
|
||||
if (version_type === undefined) {
|
||||
version_type = $(item).find('.num > i').attr('class');
|
||||
}
|
||||
if (version_type.indexOf('chrome') !== -1) {
|
||||
version_type = 'Chrome';
|
||||
}
|
||||
if (version_type.indexOf('apple') !== -1) {
|
||||
version_type = 'Safari';
|
||||
}
|
||||
if (version_type.indexOf('code') !== -1) {
|
||||
version_type = 'UserScript';
|
||||
}
|
||||
if (version_type.indexOf('firefox') !== -1) {
|
||||
version_type = 'Firefox';
|
||||
}
|
||||
|
||||
// detail
|
||||
const text_color = {
|
||||
important: '#9c27b0',
|
||||
add: '#4caf50',
|
||||
change: '#ffc107',
|
||||
fix: '#f44336',
|
||||
complete: '#03a9f4',
|
||||
};
|
||||
$(detail)
|
||||
.find('li')
|
||||
.map((index, item) => {
|
||||
let span_class = $(item).find('span').attr('class');
|
||||
const text = $(item).find('span').html();
|
||||
$(item).find('span').remove();
|
||||
if (span_class !== undefined) {
|
||||
span_class = span_class.split(' ');
|
||||
if (span_class[1] === 'empty') {
|
||||
$(item).wrap('<ul></ul>');
|
||||
} else {
|
||||
$(item).prepend(`<b style='color:${text_color[span_class[1]]}'>${text}: </b>`);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
return {
|
||||
description: $(detail).html(),
|
||||
link: 'https://simpread.pro/changelog.html',
|
||||
pubDate: `${year}-${month_day.replace('.', '-')} 00:00:00 GMT`,
|
||||
title: `${version_type}${version}`,
|
||||
author: 'SimpRead',
|
||||
};
|
||||
})
|
||||
.get(),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const got = require('@/utils/got');
|
||||
const md = require('markdown-it')();
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const url = 'https://static.simp.red/notice';
|
||||
const response = await got.get(url);
|
||||
const data = response.data.notice;
|
||||
ctx.state.data = {
|
||||
title: 'SimpRead 消息通知',
|
||||
link: 'https://simpread.pro/changelog.html',
|
||||
description: 'SimpRead 消息通知',
|
||||
item: data.map((item) => ({
|
||||
description: md.render(item.content),
|
||||
link: 'https://simpread.pro/changelog.html',
|
||||
pubDate: item.date,
|
||||
title: `${item.category.name}-${item.title}`,
|
||||
author: 'SimpRead',
|
||||
})),
|
||||
};
|
||||
};
|
||||
@@ -6,6 +6,9 @@ module.exports = async (ctx) => {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: 'http://cs.bit.edu.cn/tzgg',
|
||||
https: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
@@ -5,7 +5,11 @@ const url = require('url');
|
||||
// 专门定义一个function用于加载文章内容
|
||||
async function load(link) {
|
||||
// 异步请求文章
|
||||
const response = await got.get(link);
|
||||
const response = await got.get(link, {
|
||||
https: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
// 加载文章内容
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ module.exports = async (ctx) => {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: 'http://jwc.bit.edu.cn/tzgg',
|
||||
https: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
@@ -5,7 +5,11 @@ const url = require('url');
|
||||
// 专门定义一个function用于加载文章内容
|
||||
async function load(link) {
|
||||
// 异步请求文章
|
||||
const response = await got.get(link);
|
||||
const response = await got.get(link, {
|
||||
https: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
// 加载文章内容
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ module.exports = async (ctx) => {
|
||||
const data = response.data;
|
||||
const $ = cheerio.load(data);
|
||||
|
||||
const $list = $('div#wp_news_w3 a').slice(0, 10).get();
|
||||
const $list = $('div#wp_news_w3 a').get();
|
||||
|
||||
const resultItem = await Promise.all(
|
||||
$list.map(async (item) => {
|
||||
const title = $(item).attr('title');
|
||||
const href = $(item).attr('href');
|
||||
const detail_url = 'http://www.cpu.edu.cn' + href;
|
||||
const detail_url = href.startsWith('/') ? `http://www.cpu.edu.cn${href}` : href;
|
||||
const single = {
|
||||
title: title,
|
||||
link: detail_url,
|
||||
@@ -37,7 +37,7 @@ module.exports = async (ctx) => {
|
||||
{
|
||||
const detail_data = detail.data;
|
||||
const $ = cheerio.load(detail_data);
|
||||
single.description = $('table[bgcolor="#FFFFFF"]').html();
|
||||
single.description = $('table[bgcolor="#FFFFFF"]').html() || $('table.nyxx').html() || $('div.inner div.article').html();
|
||||
}
|
||||
return Promise.resolve(single);
|
||||
})
|
||||
|
||||
@@ -38,6 +38,7 @@ module.exports = async (ctx) => {
|
||||
// 存放到缓存区
|
||||
ctx.cache.set(cacheKey, content);
|
||||
news.description = content;
|
||||
news.link = `https://api.xiaoheihe.cn/maxnews/app/share/detail/${newsId}`;
|
||||
}
|
||||
|
||||
return Promise.resolve(news);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,ansible,vagrant
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,ansible,vagrant
|
||||
|
||||
### Ansible ###
|
||||
*.retry
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Vagrant ###
|
||||
# General
|
||||
.vagrant/
|
||||
|
||||
# Log files (if you are creating logs in debug mode, uncomment this)
|
||||
# *.log
|
||||
|
||||
### Vagrant Patch ###
|
||||
*.box
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,ansible,vagrant
|
||||
|
||||
# vagrant logs
|
||||
*.log
|
||||
@@ -0,0 +1,20 @@
|
||||
# Readme
|
||||
|
||||
Ansible playbook to deploy [RSSHub](https://github.com/DIYgod/RSSHub) on bare-metal with Redis, browserless and Caddy 2
|
||||
|
||||
Requires sudo permission
|
||||
|
||||
## Usage
|
||||
On `Ubuntu 20.04`, [install ansible](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-ansible-on-ubuntu-20-04), then
|
||||
|
||||
```bash
|
||||
sudo ansible-playbook rsshub.yaml
|
||||
```
|
||||
|
||||
## Development
|
||||
Install `vagrant`, then
|
||||
|
||||
```bash
|
||||
./try.sh
|
||||
ansible-playbook rsshub.yaml
|
||||
```
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "generic/ubuntu2004"
|
||||
config.vm.synced_folder ".", "/vagrant", type: "rsync", rsync__exclude: ".git/"
|
||||
config.ssh.extra_args = ["-t", "cd /vagrant; bash --login"]
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
{{ domain_name }}
|
||||
|
||||
reverse_proxy localhost:1200
|
||||
@@ -0,0 +1,3 @@
|
||||
NODE_ENV=production
|
||||
CACHE_TYPE=redis
|
||||
PUPPETEER_WS_ENDPOINT=ws://localhost:3000
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=RSSHub is an open source, easy to use, and extensible RSS feed aggregator
|
||||
|
||||
[Service]
|
||||
User=rsshub
|
||||
WorkingDirectory=/home/rsshub/app
|
||||
ExecStart=yarn start
|
||||
EnvironmentFile=/home/rsshub/app/.env
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,130 @@
|
||||
-
|
||||
name: Install RSSHub
|
||||
hosts: localhost
|
||||
become: true
|
||||
vars_prompt:
|
||||
-
|
||||
name: domain_name
|
||||
prompt: What is the domain name (without www, e.g. rsshub.example.com)? Use "http://localhost" for development in Vagrant VM.
|
||||
private: no
|
||||
tasks:
|
||||
-
|
||||
name: Check OS
|
||||
fail:
|
||||
msg: This playbook can only be run on Ubuntu 20.04 at this moment
|
||||
when: ansible_distribution != 'Ubuntu' or ansible_distribution_version !='20.04'
|
||||
-
|
||||
name: Install GPG keys for repos
|
||||
apt_key:
|
||||
url: '{{ item }}'
|
||||
state: present
|
||||
with_items:
|
||||
- https://deb.nodesource.com/gpgkey/nodesource.gpg.key
|
||||
- https://dl.yarnpkg.com/debian/pubkey.gpg
|
||||
- https://download.docker.com/linux/ubuntu/gpg
|
||||
- https://dl.cloudsmith.io/public/caddy/stable/cfg/gpg/gpg.155B6D79CA56EA34.key
|
||||
-
|
||||
name: Install repos
|
||||
apt_repository:
|
||||
repo: '{{ item }}'
|
||||
state: present
|
||||
update_cache: yes
|
||||
with_items:
|
||||
- deb https://deb.nodesource.com/node_12.x focal main
|
||||
- deb https://dl.yarnpkg.com/debian/ stable main
|
||||
- deb https://download.docker.com/linux/ubuntu focal stable
|
||||
- deb https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main
|
||||
-
|
||||
name: Install prerequisites
|
||||
apt:
|
||||
name:
|
||||
- nodejs
|
||||
- yarn
|
||||
- build-essential
|
||||
- python-is-python2
|
||||
- redis-server
|
||||
- docker-ce
|
||||
- python3-pip
|
||||
- virtualenv
|
||||
- python3-setuptools
|
||||
- caddy
|
||||
state: present
|
||||
update_cache: yes
|
||||
-
|
||||
name: Install python module for docker
|
||||
pip:
|
||||
name: docker
|
||||
-
|
||||
name: Pull docker image for browserless
|
||||
docker_image:
|
||||
name: browserless/chrome
|
||||
source: pull
|
||||
-
|
||||
name: Start redis
|
||||
systemd:
|
||||
state: restarted
|
||||
enabled: yes
|
||||
name: redis
|
||||
daemon_reload: yes
|
||||
-
|
||||
name: Copy caddy configuration
|
||||
template:
|
||||
src: rsshub.Caddyfile
|
||||
dest: /etc/caddy/Caddyfile
|
||||
-
|
||||
name: Start caddy
|
||||
systemd:
|
||||
state: restarted
|
||||
enabled: yes
|
||||
name: caddy
|
||||
daemon_reload: yes
|
||||
-
|
||||
name: Create and start browserless container
|
||||
docker_container:
|
||||
name: browserless
|
||||
image: browserless/chrome
|
||||
state: started
|
||||
restart_policy: always
|
||||
published_ports:
|
||||
- "3000:3000"
|
||||
-
|
||||
name: Create the user
|
||||
user:
|
||||
name: rsshub
|
||||
create_home: true
|
||||
shell: /bin/bash
|
||||
-
|
||||
name: Clone the repo
|
||||
git:
|
||||
repo: https://github.com/DIYgod/RSSHub.git
|
||||
dest: /home/rsshub/app
|
||||
update: yes
|
||||
-
|
||||
name: Install repo dependencies
|
||||
command: yarn install --production
|
||||
args:
|
||||
chdir: /home/rsshub/app
|
||||
-
|
||||
name: Copy configuration
|
||||
copy:
|
||||
src: rsshub.env
|
||||
dest: /home/rsshub/app/.env
|
||||
-
|
||||
name: Own repo to the user
|
||||
file:
|
||||
path: /home/rsshub/app
|
||||
owner: rsshub
|
||||
group: rsshub
|
||||
recurse: yes
|
||||
-
|
||||
name: Install the systemd unit
|
||||
copy:
|
||||
src: rsshub.service
|
||||
dest: /etc/systemd/system/rsshub.service
|
||||
-
|
||||
name: Start the systemd service
|
||||
systemd:
|
||||
state: restarted
|
||||
enabled: yes
|
||||
name: rsshub
|
||||
daemon_reload: yes
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
vagrant rsync
|
||||
vagrant ssh
|
||||
Reference in New Issue
Block a user