mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-24 23:12:34 +08:00
Revert "Merge branch 'DIYgod:master' into master"
This reverts commit102cb48db6, reversing changes made toccac4be518.
This commit is contained in:
+3
-10
@@ -2,11 +2,13 @@ FROM node:16-bullseye as dep-builder
|
||||
# Here we use the non-slim image to provide build-time deps (compilers and python), thus no need to install later.
|
||||
# This effectively speeds up qemu-based cross-build.
|
||||
|
||||
# no longer needed
|
||||
#RUN ln -sf /bin/bash /bin/sh
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# place ARG statement before RUN statement which need it to avoid cache miss
|
||||
ARG USE_CHINA_NPM_REGISTRY=0
|
||||
ARG TARGETPLATFORM
|
||||
RUN \
|
||||
set -ex && \
|
||||
if [ "$USE_CHINA_NPM_REGISTRY" = 1 ]; then \
|
||||
@@ -18,15 +20,6 @@ RUN \
|
||||
COPY ./yarn.lock /app/
|
||||
COPY ./package.json /app/
|
||||
|
||||
# required for building canvas in arm64 and arm/v7 (introduce in #10954)
|
||||
RUN \
|
||||
set -ex && \
|
||||
if [ "$TARGETPLATFORM" != "linux/amd64" ]; then \
|
||||
apt-get update && \
|
||||
apt-get install -yq --no-install-recommends \
|
||||
libpango1.0-dev ; \
|
||||
fi;
|
||||
|
||||
# lazy install Chromium to avoid cache miss, only install production dependencies to minimize the image size
|
||||
RUN \
|
||||
set -ex && \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
({
|
||||
// '8world.com': { _name: '8视界', '.': [{ title: '分类', docs: 'https://docs.rsshub.app/new-media.html#_8-shi-jie-fen-lei', source: ['/:category', '/'], target: '/8world/:category?' }] },
|
||||
'8world.com': { _name: '8视界', '.': [{ title: '分类', docs: 'https://docs.rsshub.app/new-media.html#_8-shi-jie-fen-lei', source: ['/:category', '/'], target: '/8world/:category?' }] },
|
||||
'aamacau.com': { _name: '論盡媒體 AllAboutMacau Media', '.': [{ title: '话题', docs: 'https://docs.rsshub.app/new-media.html#lun-jin-mei-ti-allaboutmacau-media-hua-ti', source: ['/'], target: '/:category?/:id?' }] },
|
||||
'eprice.com.tw': { _name: 'ePrice', '.': [{ title: 'ePrice 比價王', docs: 'https://docs.rsshub.app/new-media.html#eprice', source: ['/'], target: '/:region?' }] },
|
||||
'eprice.com.hk': { _name: 'ePrice', '.': [{ title: 'ePrice 香港', docs: 'https://docs.rsshub.app/new-media.html#eprice', source: ['/'], target: '/:region?' }] },
|
||||
|
||||
@@ -246,12 +246,6 @@ pageClass: routes
|
||||
|
||||
见 [#bilibili](/social-media.html#bilibili)
|
||||
|
||||
## CCC 創作集
|
||||
|
||||
### 漫畫
|
||||
|
||||
<Route author="TonyRL" example="/creative-comic/book/117" path="/creative-comic/book/:id/:coverOnly?/:quality?" :paramsDesc="['漫畫 ID,可在 URL 中找到', '僅獲取封面,非 `true` 時將獲取**全部**頁面,預設 `true`', '閱讀品質,標準畫質 `1`,高畫質 `2`,預設 `1`']" radar="1" rssbud="1"/>
|
||||
|
||||
## DLsite
|
||||
|
||||
### 当前日期发售的新产品
|
||||
|
||||
@@ -762,8 +762,6 @@ Compared to the official one, this feed:
|
||||
|
||||
## The Verge
|
||||
|
||||
### The Verge
|
||||
|
||||
<RouteEn author="HenryQW" example="/verge" path="/verge">
|
||||
|
||||
Provides a better reading experience (full text articles) over the official one.
|
||||
|
||||
@@ -56,64 +56,6 @@ The following are some of the supported Channel and Topic ids.
|
||||
|
||||
</RouteEn>
|
||||
|
||||
## Aljazeera
|
||||
|
||||
### News
|
||||
|
||||
<RouteEn author="nczitzk" example="/aljazeera/english/news" path="/aljazeera/:language?/:category?" :paramsDesc="['Language, see below, arbric by default, as Arbric', 'Category, can be found in URL, homepage by default']">
|
||||
|
||||
Language
|
||||
|
||||
| Arbric | Chinese | English |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip Tip
|
||||
|
||||
If you subscribe to [Al Jazeera English - Economy](https://www.aljazeera.com/economy), whose language is `english` and whose path is `economy`, you can get the route as [`/aljazeera/english/economy`](https://rsshub.app/aljazeera/english/economy)
|
||||
|
||||
If you subscribe to [Al Jazeera Chinese - Political](https://chinese.aljazeera.net/news/political) with language `chinese` and path `news/political`, you can get the route as [`/aljazeera/chinese/news/political`](https://rsshub.app/aljazeera/chinese/news/political)
|
||||
|
||||
:::
|
||||
|
||||
</RouteEn>
|
||||
|
||||
### Tag
|
||||
|
||||
<RouteEn author="nczitzk" example="/aljazeera/english/tag/science-and-technology" path="/aljazeera/:language?/tag/:id" :paramsDesc="['Language, see below, arbric by default, as Arbric', 'Tag id, can be found in URL']">
|
||||
|
||||
Language
|
||||
|
||||
| Arbric | Chinese | English |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip Tip
|
||||
|
||||
If you subscribe to [Al Jazeera English - Science and Technology](https://www.aljazeera.com/tag/science-and-technology), whose language is `english` and whose path is `science-and-technology`, you can get the route as [`/aljazeera/english/tag/science-and-technology`](https://rsshub.app/aljazeera/english/tag/science-and-technology)
|
||||
|
||||
:::
|
||||
|
||||
</RouteEn>
|
||||
|
||||
### Official RSS
|
||||
|
||||
<RouteEn author="nczitzk" example="/aljazeera/english/rss" path="/aljazeera/:language?/rss" :paramsDesc="['Language, see below, arbric by default, as Arbric']">
|
||||
|
||||
Language
|
||||
|
||||
| Arbric | Chinese | English |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip Tip
|
||||
|
||||
There is no RSS source for Al Jazeera Chinese, returning homepage content by default
|
||||
|
||||
:::
|
||||
|
||||
</RouteEn>
|
||||
|
||||
## AP News
|
||||
|
||||
### Topics
|
||||
|
||||
@@ -525,10 +525,6 @@ TokenInsight 官方亦有提供 RSS,可参考 <https://api.tokeninsight.com/re
|
||||
|
||||
## 雪球
|
||||
|
||||
### 今日话题
|
||||
|
||||
<Route author="nczitzk" example="/xueqiu/today" path="/xueqiu/today"/>
|
||||
|
||||
### 用户动态
|
||||
|
||||
<Route author="imlonghao" example="/xueqiu/user/8152922548" path="/xueqiu/user/:id/:type?" :paramsDesc="['用户 id, 可在用户主页 URL 中找到', '动态的类型, 不填则默认全部']">
|
||||
@@ -631,31 +627,6 @@ TokenInsight 官方亦有提供 RSS,可参考 <https://api.tokeninsight.com/re
|
||||
|
||||
</Route>
|
||||
|
||||
## 智通财经网
|
||||
|
||||
### 推荐
|
||||
|
||||
<Route author="nczitzk" example="/zhitongcaijing" path="/zhitongcaijing/:id?/:category?" :paramsDesc="['栏目 id,可在对应栏目页 URL 中找到,默认为 recommend,即推荐', '分类 id,可在对应栏目子分类页 URL 中找到,默认为全部']">
|
||||
|
||||
| id | 栏目 |
|
||||
| ------------ | --- |
|
||||
| recommend | 推荐 |
|
||||
| hkstock | 港股 |
|
||||
| meigu | 美股 |
|
||||
| agu | 沪深 |
|
||||
| ct | 创投 |
|
||||
| esg | ESG |
|
||||
| aqs | 券商 |
|
||||
| ajj | 基金 |
|
||||
| focus | 要闻 |
|
||||
| announcement | 公告 |
|
||||
| research | 研究 |
|
||||
| shares | 新股 |
|
||||
| bazaar | 市场 |
|
||||
| company | 公司 |
|
||||
|
||||
</Route>
|
||||
|
||||
## 中国人民银行
|
||||
|
||||
### 沟通交流
|
||||
|
||||
@@ -44,18 +44,6 @@ pageClass: routes
|
||||
|
||||
<Route author="Fatpandac" example="/ssm/news" path="/ssm/news"/>
|
||||
|
||||
## 北京市保障房中心有限公司
|
||||
|
||||
### 北京市共有产权住房租赁服务平台
|
||||
|
||||
<Route author="bigfei" example="/gov/beijing/bphc/announcement" path="/gov/beijing/bphc/:caty" :paramsDesc="['类别']">
|
||||
|
||||
| 通知公告 | 项目介绍 |
|
||||
| :----------: | :-----: |
|
||||
| announcement | project |
|
||||
|
||||
</Route>
|
||||
|
||||
## 北京市教育委员会
|
||||
|
||||
### 通知公告
|
||||
|
||||
+2
-48
@@ -1656,55 +1656,9 @@ Tiny Tiny RSS 会给所有 iframe 元素添加 `sandbox="allow-scripts"` 属性
|
||||
|
||||
## 云听
|
||||
|
||||
### 专辑
|
||||
### 电台节目
|
||||
|
||||
<Route author="nczitzk" example="/radio/album/15682090498666" path="/radio/album/:id" :paramsDesc="['专辑 id,可在对应专辑页面的 URL 中找到']" supportPodcast="1">
|
||||
|
||||
如果订阅 [中国相声榜](https://www.radio.cn/pc-portal/sanji/detail.html?columnId=15682090498666),其 URL 为 <https://www.radio.cn/pc-portal/sanji/detail.html?columnId=15682090498666>,可以得到 `columnId` 为 `15682090498666`
|
||||
|
||||
所以对应路由为 [`/radio/album/15682090498666`](https://rsshub.app/radio/album/15682090498666)
|
||||
|
||||
::: tip 提示
|
||||
|
||||
部分专辑不适用该路由,此时可以尝试 [节目](#yun-ting-jie-mu) 路由
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### 节目
|
||||
|
||||
<Route author="kt286 nczitzk" example="/radio/1552135" path="/radio/:id" :paramsDesc="['专辑 id,可在对应专辑页面的 URL 中找到']" supportPodcast="1">
|
||||
|
||||
如果订阅 [共和国追梦人](http://www.radio.cn/pc-portal/sanji/detail.html?columnId=1552135),其 URL 为 <https://www.radio.cn/pc-portal/sanji/detail.html?columnId=1552135>,可以得到 `columnId` 为 `1552135`
|
||||
|
||||
所以对应路由为 [`/radio/1552135`](https://rsshub.app/radio/1552135)
|
||||
|
||||
::: tip 提示
|
||||
|
||||
该路由仅适用于更新时间较早的电台节目,如 [共和国追梦人](http://www.radio.cn/pc-portal/sanji/detail.html?columnId=1552135)
|
||||
|
||||
与适用于 [专辑](#yun-ting-zhuan-ji) 路由的专辑其 `columnId` 长度相比,它们的 `columnId` 长度较短
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### 直播
|
||||
|
||||
<Route author="nczitzk" example="/radio/zhibo/1395528" path="/radio/zhibo/:id" :paramsDesc="['直播 id,可在对应点播页面的 URL 中找到']" supportPodcast="1">
|
||||
|
||||
如果订阅 [新闻和报纸摘要](http://www.radio.cn/pc-portal/sanji/zhibo\_2.html?name=1395528),其 URL 为 <http://www.radio.cn/pc-portal/sanji/zhibo_2.html?name=1395528>,可以得到 `name` 为 `1395528`
|
||||
|
||||
所以对应路由为 [`/radio/zhibo/1395528`](https://rsshub.app/radio/zhibo/1395528)
|
||||
|
||||
::: tip 提示
|
||||
|
||||
查看更多电台直播节目,可前往 [电台直播](http://www.radio.cn/pc-portal/erji/radioStation.html)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
<Route author="kt286 nczitzk" example="/radio/1552135" path="/radio/:id" :paramsDesc="['节目ID,可在对应专辑页面的 URL 中找到']" supportPodcast="1"/>
|
||||
|
||||
## 中国高清网
|
||||
|
||||
|
||||
+10
-15
@@ -100,25 +100,14 @@ pageClass: routes
|
||||
|
||||
### 分类
|
||||
|
||||
<Route author="nczitzk" example="/8world" path="/8world/:category?" :paramsDesc="['分类 id,见下表,默认为即时 REALTIME']">
|
||||
<Route author="nczitzk" example="/8world" path="/8world/:category?" :paramsDesc="['分类,见下表,默认为即时']">
|
||||
|
||||
| 分类 | id |
|
||||
| ------------------- | -------------- |
|
||||
| 即时 REALTIME | realtime |
|
||||
| 新加坡 SINGAPORE | singapore |
|
||||
| 东南亚 SOUTH-EAST ASIA | southeast-asia |
|
||||
| 中港台 GREATER CHINA | greater-china |
|
||||
| 国际 WORLD | world |
|
||||
| 财经 FINANCE | finance |
|
||||
| 体育 SPORTS | sports |
|
||||
| 社团 COMMUNITY | community |
|
||||
| 即时 | 新加坡 | 东南亚 | 中港台 | 国际 | 财经 | 体育 |
|
||||
| -------- | --------- | -------------- | ------------- | ----- | ------- | ------ |
|
||||
| realtime | singapore | southeast-asia | greater-china | world | finance | sports |
|
||||
|
||||
</Route>
|
||||
|
||||
### 标签
|
||||
|
||||
<Route author="nczitzk" example="/8world/topic/xianggang-3" path="/8world/topic/:id" :paramsDesc="['标签 id,可在对应标签页中找到']" />
|
||||
|
||||
## 9To5
|
||||
|
||||
### 9To5 分站
|
||||
@@ -151,6 +140,12 @@ pageClass: routes
|
||||
|
||||
</Route>
|
||||
|
||||
## Aljazeera 半岛网
|
||||
|
||||
### 新闻
|
||||
|
||||
<Route author="nczitzk" example="/aljazeera/news" path="/aljazeera/news"/>
|
||||
|
||||
## AppleInsider
|
||||
|
||||
### 分类
|
||||
|
||||
@@ -62,64 +62,6 @@ pageClass: routes
|
||||
|
||||
</Route>
|
||||
|
||||
## Aljazeera 半岛电视台
|
||||
|
||||
### 新闻
|
||||
|
||||
<Route author="nczitzk" example="/aljazeera/chinese/news" path="/aljazeera/:language?/:category?" :paramsDesc="['语言,见下表,默认为 arbric,即阿拉伯文', '分类,可在对应新闻分类页 URL 中找到,默认为空,即首页']">
|
||||
|
||||
语言
|
||||
|
||||
| 阿拉伯文 | 中文 | 英文 |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip 提示
|
||||
|
||||
若订阅 [Al Jazeera English - Economy](https://www.aljazeera.com/economy),其语言为 `english`,路径为 `economy`,可得到其路由为 [`/aljazeera/english/economy`](https://rsshub.app/aljazeera/english/economy)
|
||||
|
||||
若订阅 [半岛电视台中文频道 - 政治](https://chinese.aljazeera.net/news/political),其语言为 `chinese`,路径为 `news/political`,可得到其路由为 [`/aljazeera/chinese/news/political`](https://rsshub.app/aljazeera/chinese/news/political)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### 标签
|
||||
|
||||
<Route author="nczitzk" example="/aljazeera/english/tag/science-and-technology" path="/aljazeera/:language?/tag/:id" :paramsDesc="['语言,见下表,默认为 arbric,即阿拉伯文', '标签 id,可在对应标签页 URL 中找到']">
|
||||
|
||||
语言
|
||||
|
||||
| 阿拉伯文 | 中文 | 英文 |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip 提示
|
||||
|
||||
若订阅 [Al Jazeera English - Science and Technology](https://www.aljazeera.com/tag/science-and-technology),其语言为 `english`,标签 id 为 `science-and-technology`,可得到其路由为 [`/aljazeera/english/tag/science-and-technology`](https://rsshub.app/aljazeera/english/tag/science-and-technology)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### 官方 RSS
|
||||
|
||||
<Route author="nczitzk" example="/aljazeera/english/rss" path="/aljazeera/:language?/rss" :paramsDesc="['语言,见下表,默认为 arbric,即阿拉伯文']">
|
||||
|
||||
语言
|
||||
|
||||
| 阿拉伯文 | 中文 | 英文 |
|
||||
| ------ | ------- | ------- |
|
||||
| arbric | chinese | english |
|
||||
|
||||
::: tip 提示
|
||||
|
||||
半岛电视台中文频道无对应的 RSS,故默认返回首页内容
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
## AP News
|
||||
|
||||
### 话题
|
||||
@@ -502,6 +444,12 @@ Solidot 提供的 feed:
|
||||
|
||||
</Route>
|
||||
|
||||
## UDN
|
||||
|
||||
### 轉角國際
|
||||
|
||||
<Route author="emdoe" example="/udn/global/鏡頭背後" path="/udn/global/:tid" :paramsDesc="['標籤名稱,請在轉角國際首頁獲取;如果選擇輸入 `newest` 則輸出最新文章']">
|
||||
|
||||
## Voice of America (VOA)
|
||||
|
||||
透過提取全文,以獲得更好的閱讀體驗
|
||||
@@ -1414,25 +1362,6 @@ category 对应的关键词有
|
||||
|
||||
</Route>
|
||||
|
||||
### 轉角國際 - 首頁
|
||||
|
||||
<Route author="emdoe nczitzk" example="/udn/global" path="/udn/global/:category?" :paramsDesc="['分类,见下表,默认为首頁']">
|
||||
|
||||
| 首頁 | 最新文章 | 熱門文章 |
|
||||
| -- | ---- | ---- |
|
||||
| | new | hot |
|
||||
|
||||
</Route>
|
||||
|
||||
### 轉角國際 - 標籤
|
||||
|
||||
<Route author="nczitzk" example="/udn/global/tag/過去24小時" path="/udn/global/tag/:tag?" :paramsDesc="['标签,可在对应标签页 URL 中找到']">
|
||||
|
||||
| 過去 24 小時 | 鏡頭背後 | 深度專欄 | 重磅廣播 |
|
||||
| -------- | ---- | ---- | ---- |
|
||||
|
||||
</Route>
|
||||
|
||||
## 路透社
|
||||
|
||||
::: warning 迁移说明
|
||||
|
||||
@@ -2991,26 +2991,6 @@ jsjxy.hbut.edu.cn 证书链不全,自建 RSSHub 可设置环境变量 NODE_TLS
|
||||
|
||||
</Route>
|
||||
|
||||
### 财经学院
|
||||
|
||||
<Route author="RiverTwilight" example="/swpu/cjxy/xyxw" path="/swpu/cjxy/:code" :paramsDesc="['栏目代码']">
|
||||
|
||||
| 栏目 | 学院新闻 | 学院通知 |
|
||||
| -- | ---- | ---- |
|
||||
| 代码 | xyxw | xytz |
|
||||
|
||||
</Route>
|
||||
|
||||
### 信息学院
|
||||
|
||||
<Route author="RiverTwilight" example="/swpu/is/xyxw" path="/swpu/is/:code" :paramsDesc="['栏目代码']">
|
||||
|
||||
| 栏目 | 学院新闻 | 通知公告 | 教育教学 | 学生工作 | 招生就业 |
|
||||
| -- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 代码 | xyxw | tzgg | jyjx | xsgz | zsjy |
|
||||
|
||||
</Route>
|
||||
|
||||
## 信阳师范学院
|
||||
|
||||
### 高等教育自学考试办公室
|
||||
|
||||
+3
-3
@@ -878,7 +878,7 @@ router.get('/bbc/:site?/:channel?', lazyloadRouteHandler('./routes/bbc/index'));
|
||||
// router.get('/ft/:language/:channel?', lazyloadRouteHandler('./routes/ft/channel'));
|
||||
|
||||
// The Verge
|
||||
// router.get('/verge', lazyloadRouteHandler('./routes/verge/index'));
|
||||
router.get('/verge', lazyloadRouteHandler('./routes/verge/index'));
|
||||
|
||||
// 看雪
|
||||
router.get('/pediy/topic/:category?/:type?', lazyloadRouteHandler('./routes/pediy/topic'));
|
||||
@@ -1035,7 +1035,7 @@ router.get('/anime1/anime/:time/:name', lazyloadRouteHandler('./routes/anime1/an
|
||||
router.get('/anime1/search/:keyword', lazyloadRouteHandler('./routes/anime1/search'));
|
||||
|
||||
// Global UDN
|
||||
// router.get('/udn/global/:tid', lazyloadRouteHandler('./routes/udn/global'));
|
||||
router.get('/udn/global/:tid', lazyloadRouteHandler('./routes/udn/global'));
|
||||
|
||||
// gitea
|
||||
router.get('/gitea/blog', lazyloadRouteHandler('./routes/gitea/blog'));
|
||||
@@ -2807,7 +2807,7 @@ router.get('/manhuadui/manhua/:name/:serial?', lazyloadRouteHandler('./routes/ma
|
||||
router.get('/fzdm/manhua/:id', lazyloadRouteHandler('./routes/fzdm/manhua'));
|
||||
|
||||
// Aljazeera 半岛网
|
||||
// router.get('/aljazeera/news', lazyloadRouteHandler('./routes/aljazeera/news'));
|
||||
router.get('/aljazeera/news', lazyloadRouteHandler('./routes/aljazeera/news'));
|
||||
|
||||
// CFD indices dividend adjustment
|
||||
router.get('/cfd/gbp_div', lazyloadRouteHandler('./routes/cfd/gbp_div'));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
const url = require('url');
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const link = `https://chinese.aljazeera.net/news`;
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: link,
|
||||
});
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const list = $('h3 a')
|
||||
.map((_, item) => {
|
||||
item = $(item);
|
||||
return {
|
||||
title: item.text(),
|
||||
link: url.resolve(`https://chinese.aljazeera.net`, item.attr('href')),
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
ctx.state.data = {
|
||||
title: `Aljazeera半岛网 - 新闻`,
|
||||
link,
|
||||
item: await Promise.all(
|
||||
list.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({ method: 'get', url: item.link });
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
item.pubDate = new Date(detailResponse.data.match(/"datePublished": "(.*?)",/)[1]).toUTCString();
|
||||
item.description = content('div.wysiwyg').html();
|
||||
return item;
|
||||
})
|
||||
)
|
||||
),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
const cheerio = require('cheerio');
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let filter, url, tag;
|
||||
if (ctx.params.tid !== 'newest') {
|
||||
url = `https://global.udn.com/search/tagging/1020/${ctx.params.tid}`;
|
||||
filter = 'li.contentli';
|
||||
tag = ctx.params.tid;
|
||||
} else {
|
||||
url = `https://global.udn.com/global_vision/index`;
|
||||
filter = `.news_cards > ul > li`;
|
||||
tag = '最新文章';
|
||||
}
|
||||
const res = await got.get(url);
|
||||
const $ = cheerio.load(res.data);
|
||||
const list = $(filter).get();
|
||||
|
||||
const out = await Promise.all(
|
||||
list.map(async (item) => {
|
||||
const $ = cheerio.load(item);
|
||||
let title;
|
||||
let address = $('a').attr('href');
|
||||
if (ctx.params.tid === 'newest') {
|
||||
address = `https://global.udn.com` + address;
|
||||
title = $('h3').text();
|
||||
} else {
|
||||
title = $('h2').text();
|
||||
}
|
||||
const time = $('span.date').text();
|
||||
const author = $('span.author').text();
|
||||
const cache = await ctx.cache.get(address);
|
||||
if (cache) {
|
||||
return Promise.resolve(JSON.parse(cache));
|
||||
}
|
||||
const res = await got.get(address);
|
||||
const capture = cheerio.load(res.data);
|
||||
capture('div.social_bar').remove();
|
||||
capture('div.area').remove();
|
||||
capture('.story_art_title').remove();
|
||||
capture('.story_bady_info_author').remove();
|
||||
capture('.photo_pop').remove();
|
||||
const contents = capture('div.story_body_content').html();
|
||||
const single = {
|
||||
title,
|
||||
author,
|
||||
description: contents,
|
||||
link: address,
|
||||
guid: address,
|
||||
pubDate: new Date(time).toUTCString(),
|
||||
};
|
||||
ctx.cache.set(address, JSON.stringify(single));
|
||||
return Promise.resolve(single);
|
||||
})
|
||||
);
|
||||
ctx.state.data = {
|
||||
title: `轉角國際 | ${tag}`,
|
||||
link: url,
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
const got = require('@/utils/got');
|
||||
const parser = require('@/utils/rss-parser');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const feed = await parser.parseURL('https://www.theverge.com/rss/index.xml');
|
||||
|
||||
const items = await Promise.all(
|
||||
feed.items.slice(0, 10).map(async (item) => {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const content = $('div.c-entry-content');
|
||||
|
||||
// 处理封面图片
|
||||
|
||||
const cover = $('meta[property="og:image"]');
|
||||
|
||||
if (cover.length > 0) {
|
||||
$(`<img src=${cover[0].attribs.content}>`).insertBefore(content[0].childNodes[0]);
|
||||
}
|
||||
|
||||
// 处理封面视频
|
||||
$('div.l-col__main > div.c-video-embed, div.c-entry-hero > div.c-video-embed').each((i, e) => {
|
||||
const src = `https://volume.vox-cdn.com/embed/${e.attribs['data-volume-uuid']}?autoplay=false`;
|
||||
|
||||
$(`<iframe src="${src}" style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" allowfullscreen scrolling="no"></iframe>`).insertBefore(content[0].childNodes[0]);
|
||||
});
|
||||
|
||||
// 处理封面视频
|
||||
$('div.l-col__main > div.c-video-embed--media iframe').each((i, e) => {
|
||||
$(e).insertBefore(content[0].childNodes[0]);
|
||||
});
|
||||
|
||||
// 处理文章图片
|
||||
content.find('figure.e-image').each((i, e) => {
|
||||
let src, caption;
|
||||
|
||||
// 处理 jpeg, png
|
||||
if ($(e).find('picture > source').length > 0) {
|
||||
src = $(e)
|
||||
.find('picture > img')[0]
|
||||
.attribs.srcset.match(/(?<=320w,).*?(?=520w)/g)[0]
|
||||
.trim();
|
||||
} else if ($(e).find('img.c-dynamic-image').length > 0) {
|
||||
// 处理 gif
|
||||
src = $(e).find('span.e-image__image')[0].attribs['data-original'];
|
||||
}
|
||||
|
||||
// 处理 caption
|
||||
if ($(e).find('span.e-image__meta').length > 0) {
|
||||
caption = $(e).find('span.e-image__meta').text();
|
||||
}
|
||||
|
||||
const figure = `<figure><img src=${src}>${caption ? `<br><figcaption>${caption}</figcaption>` : ''}</figure>`;
|
||||
|
||||
$(figure).insertBefore(e);
|
||||
|
||||
$(e).remove();
|
||||
});
|
||||
|
||||
// 移除无用 DOM
|
||||
content.find('aside').each((i, e) => {
|
||||
$(e).remove();
|
||||
});
|
||||
|
||||
const single = {
|
||||
title: item.title,
|
||||
description: content.html(),
|
||||
author: item.author,
|
||||
pubDate: item.pubDate,
|
||||
link: item.link,
|
||||
};
|
||||
return Promise.resolve(single);
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: feed.title,
|
||||
link: feed.link,
|
||||
description: feed.description,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -3,10 +3,10 @@ const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const path = ctx.path === '/' ? '/realtime' : ctx.path;
|
||||
const category = ctx.params.category ?? 'realtime';
|
||||
|
||||
const rootUrl = 'https://www.8world.com';
|
||||
const currentUrl = `${rootUrl}${path}`;
|
||||
const currentUrl = `${rootUrl}/${category}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
@@ -15,19 +15,19 @@ module.exports = async (ctx) => {
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
let items = $('div[data-column="Two-Third"] .article-title .article-link')
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
const list = $('div[data-column="Two-Third"] .article-title .article-link')
|
||||
.map((_, item) => {
|
||||
item = $(item);
|
||||
|
||||
return {
|
||||
title: item.text(),
|
||||
link: `${rootUrl}${item.attr('href')}`,
|
||||
};
|
||||
});
|
||||
})
|
||||
.get();
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
const items = await Promise.all(
|
||||
list.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
'/:category?': ['nczitzk'],
|
||||
'/topic/:id': ['nczitzk'],
|
||||
};
|
||||
|
||||
@@ -8,12 +8,6 @@ module.exports = {
|
||||
source: ['/:category', '/'],
|
||||
target: '/8world/:category?',
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
docs: 'https://docs.rsshub.app/new-media.html#_8-shi-jie-biao-qian',
|
||||
source: ['/topic/:id', '/'],
|
||||
target: '/8world/topic/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = function (router) {
|
||||
router.get(/([\w-/]+)?/, require('./index'));
|
||||
router.get('/:category?', require('./index'));
|
||||
};
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
const languages = {
|
||||
arabic: {
|
||||
rootUrl: 'https://www.aljazeera.net',
|
||||
rssUrl: 'rss',
|
||||
},
|
||||
chinese: {
|
||||
rootUrl: 'https://chinese.aljazeera.net',
|
||||
rssUrl: undefined,
|
||||
},
|
||||
english: {
|
||||
rootUrl: 'https://www.aljazeera.com',
|
||||
rssUrl: 'xml/rss/all.xml',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const params = ctx.path === '/' ? ['arabic'] : ctx.path.replace(/^\//, '').split('/');
|
||||
|
||||
if (!languages.hasOwnProperty(params[0])) {
|
||||
params.unshift('arabic');
|
||||
}
|
||||
|
||||
const language = params.shift();
|
||||
const isRSS = params.length === 1 && params.at(-1) === 'rss' && languages[language].rssUrl;
|
||||
|
||||
const rootUrl = languages[language].rootUrl;
|
||||
const currentUrl = `${rootUrl}/${isRSS ? languages[language].rssUrl : params.join('/')}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: currentUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
let items = isRSS
|
||||
? response.data.match(new RegExp('<link>' + rootUrl + '/(.*?)</link>', 'g')).map((item) => ({
|
||||
link: item.match(/<link>(.*?)<\/link>/)[1],
|
||||
}))
|
||||
: $('.u-clickable-card__link')
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
item = $(item);
|
||||
|
||||
return {
|
||||
link: `${rootUrl}${item.attr('href')}`,
|
||||
};
|
||||
});
|
||||
|
||||
items = await Promise.all(
|
||||
items.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50).map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
content('.more-on').parent().remove();
|
||||
content('.responsive-image img').removeAttr('srcset');
|
||||
|
||||
item.title = content('h1').first().text();
|
||||
item.author = content('.author').text();
|
||||
item.pubDate = parseDate(detailResponse.data.match(/"datePublished": "(.*?)",/)[1]);
|
||||
item.description = art(path.join(__dirname, 'templates/description.art'), {
|
||||
image: content('.article-featured-image').html(),
|
||||
description: content('.wysiwyg').html(),
|
||||
});
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: $('title').first().text(),
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
'/:language?/:category?': ['nczitzk'],
|
||||
'/:language?/tag/:id': ['nczitzk'],
|
||||
'/:language?/rss': ['nczitzk'],
|
||||
};
|
||||
@@ -1,62 +0,0 @@
|
||||
module.exports = {
|
||||
'aljazeera.com': {
|
||||
_name: 'Aljazeera 半岛电视台',
|
||||
'.': [
|
||||
{
|
||||
title: 'News',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-xin-wen',
|
||||
source: ['/:category', '/'],
|
||||
target: '/aljazeera/english/:category',
|
||||
},
|
||||
{
|
||||
title: 'Tag',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-biao-qian',
|
||||
source: ['/tag/:id', '/'],
|
||||
target: '/aljazeera/english/tag/:id',
|
||||
},
|
||||
{
|
||||
title: 'Official RSS',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-guan-fang-rss',
|
||||
source: ['/xml/rss/all.xml', '/'],
|
||||
target: '/aljazeera/english/rss',
|
||||
},
|
||||
],
|
||||
},
|
||||
'aljazeera.net': {
|
||||
_name: 'Aljazeera 半岛电视台',
|
||||
'.': [
|
||||
{
|
||||
title: 'News',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-xin-wen',
|
||||
source: ['/:category', '/'],
|
||||
target: '/aljazeera/arbric/:category',
|
||||
},
|
||||
{
|
||||
title: 'Tag',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-biao-qian',
|
||||
source: ['/tag/:id', '/'],
|
||||
target: '/aljazeera/arbric/tag/:id',
|
||||
},
|
||||
{
|
||||
title: 'Official RSS',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-guan-fang-rss',
|
||||
source: ['/rss', '/'],
|
||||
target: '/aljazeera/arbric/rss',
|
||||
},
|
||||
],
|
||||
chinese: [
|
||||
{
|
||||
title: '新闻',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-xin-wen',
|
||||
source: ['/:category', '/'],
|
||||
target: '/aljazeera/chinese/:category',
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
docs: 'https://docs.rsshub.app/traditional-media.html#aljazeera-ban-dao-dian-shi-tai-biao-qian',
|
||||
source: ['/tag/:id', '/'],
|
||||
target: '/aljazeera/chinese/tag/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function (router) {
|
||||
router.get(/([\w/-]+)?/, require('./index'));
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
{{ if image }}
|
||||
<figure>
|
||||
{{@ image }}
|
||||
</figure>
|
||||
{{ /if }}
|
||||
{{ if description }}
|
||||
{{@ description }}
|
||||
{{ /if }}
|
||||
@@ -39,7 +39,7 @@ module.exports = async (ctx) => {
|
||||
.toArray()
|
||||
.map((element) => {
|
||||
const item = $(element);
|
||||
return `<img src="${item.attr('bpic')}">`;
|
||||
return `<img src="${item.attr('bpic')?.split('?tbpicau')[0]}">`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
|
||||
@@ -1,53 +1,30 @@
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = {
|
||||
getCookie: (ctx) => {
|
||||
const key = 'bili-cookie';
|
||||
return ctx.cache.tryGet(key, async () => {
|
||||
const response = await got(`https://www.bilibili.com/`);
|
||||
return response.headers['set-cookie'];
|
||||
});
|
||||
},
|
||||
getUsernameFromUID: (ctx, uid) => {
|
||||
const key = 'bili-username-from-uid-' + uid;
|
||||
return ctx.cache.tryGet(key, async () => {
|
||||
const cookie = await module.exports.getCookie(ctx);
|
||||
await got(`https://space.bilibili.com/${uid}/`, {
|
||||
const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/acc/info?mid=${uid}&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: 'https://www.bilibili.com/',
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/acc/info?mid=${uid}&token=&platform=web&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
Cookie: cookie,
|
||||
Referer: `https://space.bilibili.com/${uid}`,
|
||||
},
|
||||
});
|
||||
return nameResponse.data.name;
|
||||
});
|
||||
},
|
||||
getUsernameAndFaceFromUID: async (ctx, uid) => {
|
||||
const cookie = await module.exports.getCookie(ctx);
|
||||
const nameKey = 'bili-username-from-uid-' + uid;
|
||||
const faceKey = 'bili-userface-from-uid-' + uid;
|
||||
let name = await ctx.cache.get(nameKey);
|
||||
let face = await ctx.cache.get(faceKey);
|
||||
if (!name || !face) {
|
||||
await got(`https://space.bilibili.com/${uid}/`, {
|
||||
if (!name) {
|
||||
const nameResponse = await got(`https://api.bilibili.com/x/space/acc/info?mid=${uid}&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://www.bilibili.com/`,
|
||||
Cookie: cookie,
|
||||
Referer: `https://space.bilibili.com/${uid}`,
|
||||
},
|
||||
});
|
||||
const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/acc/info?mid=${uid}&token=&platform=web&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
name = nameResponse.data.name;
|
||||
face = nameResponse.data.face;
|
||||
name = nameResponse.data.data.name;
|
||||
face = nameResponse.data.data.face;
|
||||
ctx.cache.set(nameKey, name);
|
||||
ctx.cache.set(faceKey, face);
|
||||
}
|
||||
|
||||
@@ -6,45 +6,27 @@ const { parseDate } = require('@/utils/parse-date');
|
||||
module.exports = async (ctx) => {
|
||||
const uid = ctx.params.uid;
|
||||
const disableEmbed = ctx.params.disableEmbed;
|
||||
const cookie = await cache.getCookie(ctx);
|
||||
const [name, face] = await cache.getUsernameAndFaceFromUID(ctx, uid);
|
||||
|
||||
await got(`https://space.bilibili.com/${uid}/video?tid=0&page=1&keyword=&order=pubdate`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
const response = await got(`https://api.bilibili.com/x/space/arc/search?mid=${uid}&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/video?tid=0&page=1&keyword=&order=pubdate`,
|
||||
Cookie: cookie,
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
},
|
||||
});
|
||||
|
||||
const vlist = [...response.data.data.list.vlist];
|
||||
const pageTotal = Math.ceil(response.data.data.page.count / response.data.data.page.ps);
|
||||
|
||||
const getPage = async (pageId) => {
|
||||
await got(`https://space.bilibili.com/${uid}/video?tid=0&page=${pageId}&keyword=&order=pubdate`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
return await got(`https://api.bilibili.com/x/space/arc/search?mid=${uid}&ps=30&tid=0&pn=${pageId}&keyword=&order=pubdate&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/video?tid=0&page=${pageId}&keyword=&order=pubdate`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
|
||||
if (pageTotal > 1) {
|
||||
for (let i = 2; i <= pageTotal; i++) {
|
||||
promises.push(getPage(i));
|
||||
promises.push(
|
||||
got(`https://api.bilibili.com/x/space/arc/search?mid=${uid}&ps=30&tid=0&pn=${i}&keyword=&order=pubdate&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
const rets = await Promise.all(promises);
|
||||
rets.forEach((ret) => {
|
||||
|
||||
@@ -5,19 +5,13 @@ const utils = require('./utils');
|
||||
module.exports = async (ctx) => {
|
||||
const uid = ctx.params.uid;
|
||||
const disableEmbed = ctx.params.disableEmbed;
|
||||
const cookie = await cache.getCookie(ctx);
|
||||
const [name, face] = await cache.getUsernameAndFaceFromUID(ctx, uid);
|
||||
|
||||
await got(`https://space.bilibili.com/${uid}/video?tid=0&page=1&keyword=&order=pubdate`, {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: `https://api.bilibili.com/x/space/arc/search?mid=${uid}&ps=10&tid=0&pn=1&order=pubdate&jsonp=jsonp`,
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
const response = await got(`https://api.bilibili.com/x/space/arc/search?mid=${uid}&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp`, {
|
||||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/video?tid=0&page=1&keyword=&order=pubdate`,
|
||||
Cookie: cookie,
|
||||
},
|
||||
});
|
||||
const data = response.data;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
const { getUuid, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, decrypt, getRealKey, siteHost } = require('./utils');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { id, coverOnly = 'true', quality = '1' } = ctx.params;
|
||||
const uuid = await getUuid(ctx.cache.tryGet);
|
||||
let { data: book } = await getBook(id, uuid);
|
||||
let { data: chapters } = await getChapters(id, uuid);
|
||||
book = book.data;
|
||||
chapters = chapters.data;
|
||||
|
||||
const items = await Promise.all(
|
||||
chapters.chapters.map(async (c) => {
|
||||
let pages;
|
||||
if (coverOnly !== 'true' && coverOnly !== '1') {
|
||||
let { data: chapter } = await getChapter(c.id, uuid);
|
||||
chapter = chapter.data;
|
||||
|
||||
if (chapter.chapter.free_day === null || chapter.chapter.free_day === 0) {
|
||||
pages = await Promise.all(
|
||||
chapter.chapter.proportion.map(async (p) => {
|
||||
let { data: imgKey } = await getImgKey(p.id, uuid);
|
||||
imgKey = imgKey.data.key;
|
||||
|
||||
const realKey = getRealKey(imgKey);
|
||||
const encrypted = await getImgEncrypted(p.id, quality);
|
||||
|
||||
return ctx.cache.tryGet(`${siteHost}/fs/chapter_content/encrypt/${p.id}/${quality}`, () => decrypt(encrypted, realKey));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: c.vol_name,
|
||||
description: art(path.join(__dirname, 'templates/chapter.art'), {
|
||||
chapter: c,
|
||||
pages,
|
||||
cover: c.image1,
|
||||
}),
|
||||
pubDate: parseDate(c.online_at),
|
||||
updated: parseDate(c.updated_at),
|
||||
link: `https://www.creative-comic.tw/reader_comic/${c.id}`,
|
||||
author: book.author.map((author) => author.name).join(', '),
|
||||
category: book.tags.map((tag) => tag.name),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${book.name} | CCC創作集`,
|
||||
description: `${book.brief} ${book.description}`,
|
||||
link: book.share_link,
|
||||
image: book.image1,
|
||||
item: items,
|
||||
language: 'zh-hant',
|
||||
};
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
'/book/:id/:coverOnly?/:quality?': ['TonyRL'],
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = {
|
||||
'creative-comic.tw': {
|
||||
_name: 'CCC 創作集',
|
||||
'.': [
|
||||
{
|
||||
title: '漫畫',
|
||||
docs: 'https://docs.rsshub.app/anime.html#ccc-chuang-zuo-ji',
|
||||
source: ['/book/:id/*'],
|
||||
target: '/creative-comic/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (router) => {
|
||||
router.get('/book/:id/:coverOnly?/:quality?', require('./book'));
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
{{ if chapter }}
|
||||
<p>{{ chapter.name }}</p>
|
||||
{{ /if }}
|
||||
{{ if pages }}
|
||||
{{ each pages page }}
|
||||
<br><img src="{{ page }}">
|
||||
{{ /each }}
|
||||
{{ /if }}
|
||||
{{ if cover }}
|
||||
<img src="{{ cover }}">
|
||||
{{ /if }}
|
||||
@@ -1,96 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
const apiHost = 'https://api.creative-comic.tw';
|
||||
const siteHost = 'https://www.creative-comic.tw';
|
||||
const device = 'web_desktop';
|
||||
const DEFAULT_TOKEN = 'freeforccc2020reading';
|
||||
|
||||
const getBook = (bookId, uuid) =>
|
||||
got(`${apiHost}/book/${bookId}/info`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getChapter = (id, uuid) =>
|
||||
got(`${apiHost}/book/chapter/${id}`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getChapters = (bookId, uuid) =>
|
||||
got(`${apiHost}/book/${bookId}/chapter`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getImgEncrypted = async (pageId, quality) => {
|
||||
const { data: res } = await got(`${siteHost}/fs/chapter_content/encrypt/${pageId}/${quality}`, {
|
||||
headers: {
|
||||
device,
|
||||
},
|
||||
responseType: 'buffer',
|
||||
});
|
||||
return Buffer.from(res).toString('base64');
|
||||
};
|
||||
|
||||
const getImgKey = (pageId, uuid) =>
|
||||
got(`${apiHost}/book/chapter/image/${pageId}`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getUuid = (tryGet) =>
|
||||
tryGet('creative-comic:uuid', async () => {
|
||||
const { data } = await got(`${apiHost}/guest`, {
|
||||
headers: {
|
||||
device,
|
||||
},
|
||||
});
|
||||
return data.data;
|
||||
});
|
||||
|
||||
const decrypt = (encrypted, secrets) =>
|
||||
CryptoJS.AES.decrypt(encrypted, CryptoJS.enc.Hex.parse(secrets.key), {
|
||||
iv: CryptoJS.enc.Hex.parse(secrets.iv),
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
|
||||
const token2Key = (token) => {
|
||||
const t = CryptoJS.SHA512(token).toString();
|
||||
return {
|
||||
key: t.substring(0, 64),
|
||||
iv: t.substring(30, 62), // t.substr(30, 32)
|
||||
};
|
||||
};
|
||||
|
||||
const getRealKey = (imgKey, token = DEFAULT_TOKEN) => {
|
||||
const secrets = token2Key(token);
|
||||
const key = decrypt(imgKey, secrets);
|
||||
const realKey = key.split(':');
|
||||
return {
|
||||
key: realKey[0],
|
||||
iv: realKey[1],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getBook,
|
||||
getChapter,
|
||||
getChapters,
|
||||
getImgEncrypted,
|
||||
getImgKey,
|
||||
getUuid,
|
||||
decrypt,
|
||||
token2Key,
|
||||
getRealKey,
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const timezone = require('@/utils/timezone');
|
||||
|
||||
const mapping = {
|
||||
'/project': {
|
||||
title: '项目介绍',
|
||||
list: '/front/index/project/page',
|
||||
detail: '/front/index/project',
|
||||
link: '/#/communityDetail?id=',
|
||||
},
|
||||
'/announcement': {
|
||||
title: '通知公告',
|
||||
list: '/front/announcement/page',
|
||||
detail: '/front/announcement/',
|
||||
link: '/#/announcementList/detail?id=',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const rootUrl = 'http://gycp.bphc.com.cn:21000';
|
||||
const defaultPath = 'announcement';
|
||||
|
||||
const pathname = ctx.path.replace(/(^\/beijing\/bphc|\/$)/g, '');
|
||||
const key = pathname === '' ? defaultPath : pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
|
||||
const obj = mapping[key];
|
||||
const currentUrl = `${rootUrl}${obj.list}`;
|
||||
|
||||
const listResp = await got(currentUrl).json();
|
||||
const list = listResp.data?.records ?? [];
|
||||
const items = await Promise.all(
|
||||
list.map((item) => {
|
||||
const detail = `${rootUrl}${obj.detail}/${item.id}`;
|
||||
return ctx.cache.tryGet(detail, async () => {
|
||||
const detailResponse = await got(detail).json();
|
||||
const description = (detailResponse.data?.content || detailResponse.data?.introduction) ?? '';
|
||||
const single = {
|
||||
title: item.title || item.fullName,
|
||||
author: description.match(/来源:(.*?)</)?.[1].trim() ?? item.operator,
|
||||
link: `${rootUrl}${obj.link}${item.id}`,
|
||||
description,
|
||||
pubDate: timezone(parseDate(item.createTime), +8),
|
||||
};
|
||||
return single;
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: obj.title,
|
||||
link: rootUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -3,31 +3,24 @@ const { parseDate } = require('@/utils/parse-date');
|
||||
const got = require('@/utils/got');
|
||||
const timezone = require('@/utils/timezone');
|
||||
|
||||
const { CookieJar, Cookie } = require('tough-cookie');
|
||||
const { CookieJar } = require('tough-cookie');
|
||||
const cookieJar = new CookieJar();
|
||||
|
||||
const owner = '中央纪委国家监委网站';
|
||||
const rootUrl = 'https://www.ccdi.gov.cn';
|
||||
const regex = /(?<key>[A-Z_]+)=(?<value>(?:.*?(?=; max-age)|[a-fA-F0-9]+))/gm;
|
||||
const regex = /([A-Z_]+=(?:.*?(?=; max-age)|[a-fA-F0-9]+))/gm;
|
||||
|
||||
const parseCookie = async (body) => {
|
||||
let m;
|
||||
const cookies = [];
|
||||
while ((m = regex.exec(body)) !== null) {
|
||||
// This is necessary to avoid infinite loops with zero-width matches
|
||||
if (m.index === regex.lastIndex) {
|
||||
regex.lastIndex++;
|
||||
}
|
||||
const { key, value } = m.groups;
|
||||
cookies.push(new Cookie({ key, value }));
|
||||
const cookies = body.match(regex);
|
||||
if (cookies) {
|
||||
await Promise.all(cookies.map((c) => cookieJar.setCookie(c, rootUrl)));
|
||||
}
|
||||
await Promise.all(cookies.map((c) => cookieJar.setCookie(c, rootUrl)));
|
||||
};
|
||||
|
||||
const parseNewsList = async (url, selector, ctx) => {
|
||||
const response = await got(url, { cookieJar });
|
||||
const data = response.data;
|
||||
await parseCookie(data);
|
||||
parseCookie(data);
|
||||
|
||||
const $ = cheerio.load(data);
|
||||
const list = $(selector)
|
||||
@@ -47,27 +40,11 @@ const parseNewsList = async (url, selector, ctx) => {
|
||||
return { list, title };
|
||||
};
|
||||
|
||||
const changeTrCookie = async () => {
|
||||
const cookies = await cookieJar.getCookies(rootUrl);
|
||||
const c = cookies.find((c) => c.key === 'HOY_TR');
|
||||
if (c) {
|
||||
const value = c.value;
|
||||
const tr_array = value.split(',');
|
||||
const csr = tr_array[0];
|
||||
const cnv = tr_array[1].split('');
|
||||
const otr = tr_array[2].split('');
|
||||
otr[0] = csr.charAt(parseInt(cnv[0], 16));
|
||||
const nc = new Cookie({ key: 'HOY_TR', value: csr + ',' + cnv.join('') + ',' + otr.join('') + ',0' });
|
||||
await cookieJar.setCookie(nc, rootUrl);
|
||||
}
|
||||
};
|
||||
|
||||
const parseArticle = async (item, ctx) => {
|
||||
await changeTrCookie();
|
||||
return await ctx.cache.tryGet(item.link, async () => {
|
||||
const parseArticle = async (item, ctx) =>
|
||||
await ctx.cache.tryGet(item.link, async () => {
|
||||
const response = await got(item.link, { cookieJar });
|
||||
const data = response.data;
|
||||
await parseCookie(data);
|
||||
parseCookie(data);
|
||||
|
||||
const $ = cheerio.load(data);
|
||||
const title = $('.daty, .source-box').text().trim();
|
||||
@@ -85,7 +62,6 @@ const parseArticle = async (item, ctx) => {
|
||||
item.description = $('.content, .bom-box').html();
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
rootUrl,
|
||||
|
||||
@@ -37,7 +37,6 @@ module.exports = {
|
||||
'/stats/:path+': ['bigfei'],
|
||||
// province
|
||||
'/anhui/kjt/:path?': ['nczitzk'],
|
||||
'/beijing/bphc/:channel': ['bigfei'],
|
||||
'/beijing/jw/tzgg': ['nczitzk'],
|
||||
'/beijing/kw/:channel': ['Fatpandac'],
|
||||
'/guangdong/tqyb/tfxtq': ['Fatpandac'],
|
||||
|
||||
@@ -297,17 +297,6 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
'bphc.com.cn': {
|
||||
_name: '北京保障房中心有限公司',
|
||||
gycpt: [
|
||||
{
|
||||
title: '北京市共有产权住房租赁服务平台',
|
||||
docs: 'https://docs.rsshub.app/government.html#bei-jing-shi-bao-zhang-fang-zhong-xin-you-xian-gong-si',
|
||||
source: ['/*'],
|
||||
target: (params, url) => `/gov/bphc/${new URL(url).href.match(/gycpt\.bphc\.com\.cn\/(.*)/)[1]}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
'ccdi.gov.cn': {
|
||||
_name: '中央纪委国家监委',
|
||||
www: [
|
||||
|
||||
@@ -29,7 +29,6 @@ module.exports = function (router) {
|
||||
router.get(/stats(\/[\w/-]+)?/, require('./stats'));
|
||||
// province
|
||||
router.get(/anhui\/kjt\/([\w\d/-]+)?/, require('./anhui/kjt'));
|
||||
router.get(/beijing\/bphc(\/[\w/-]+)?/, require('./beijing/bphc'));
|
||||
router.get('/beijing/jw/tzgg', require('./beijing/jw/tzgg'));
|
||||
router.get('/beijing/kw/:channel', require('./beijing/kw/index'));
|
||||
router.get('/guangdong/tqyb/tfxtq', require('./guangdong/tqyb/tfxtq'));
|
||||
|
||||
@@ -25,13 +25,13 @@ module.exports = async (ctx) => {
|
||||
const item = {};
|
||||
|
||||
item.guid = thing.attr('id');
|
||||
item.title = thing.find('.titleline').children('a').text();
|
||||
item.title = thing.find('.titlelink').text();
|
||||
item.category = thing.find('.sitestr').text();
|
||||
item.author = thing.next().find('.hnuser').text();
|
||||
item.pubDate = parseDate(thing.find('.age').attr('title') ?? thing.next().find('.age').attr('title'));
|
||||
|
||||
item.link = `${rootUrl}/item?id=${item.guid}`;
|
||||
item.origin = thing.find('.titleline').children('a').attr('href');
|
||||
item.origin = thing.find('.titlelink').attr('href');
|
||||
item.onStory = thing.find('.onstory').text().substring(2);
|
||||
|
||||
item.comments = thing.next().find('a').last().text().split(' comment')[0];
|
||||
|
||||
+51
-17
@@ -1,30 +1,64 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const parser = require('@/utils/rss-parser');
|
||||
const timezone = require('@/utils/timezone');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const rootUrl = 'http://i.jandan.net';
|
||||
const feed = await parser.parseURL(`${rootUrl}/feed/`);
|
||||
const items = await Promise.all(
|
||||
feed.items.map((item) =>
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
let items = $('.post')
|
||||
.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 15)
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
item = $(item);
|
||||
|
||||
const a = item.find('.thetitle a');
|
||||
|
||||
return {
|
||||
title: a.text(),
|
||||
link: a.attr('href'),
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
summary: item.find('.indexs').text(),
|
||||
image: item
|
||||
.find('img')
|
||||
.attr('data-original')
|
||||
.replace(/!square/, ''),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const response = await got(item.link);
|
||||
const $ = cheerio.load(response.data);
|
||||
$('.wechat-hide').prev().nextAll().remove();
|
||||
const single = {
|
||||
title: item.title,
|
||||
description: $('.entry').html(),
|
||||
pubDate: item.pubDate,
|
||||
link: item.link,
|
||||
author: item['dc:creator'],
|
||||
category: item.categories,
|
||||
};
|
||||
return single;
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
content('.wechat-hide').prev().nextAll().remove();
|
||||
|
||||
item.description += content('.entry').html();
|
||||
item.author = content('.postinfo strong').text();
|
||||
item.pubDate = timezone(parseDate(detailResponse.data.match(/"pubDate": "(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})"/)[1]), +8);
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: '煎蛋',
|
||||
title: $('title').text(),
|
||||
link: rootUrl,
|
||||
item: items,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
'/:category?': ['nczitzk'],
|
||||
'/': ['nczitzk', 'bigfei'],
|
||||
'/': ['nczitzk'],
|
||||
};
|
||||
|
||||
@@ -48,20 +48,8 @@ module.exports = async (ctx) => {
|
||||
const $ = cheerio.load(data);
|
||||
// Instagram 允许最多 10 条图片/视频任意混合于一条 post,picuki 在所有情况下都会将它(们)置于 .single-photo 内
|
||||
let html = '';
|
||||
$('.single-photo img').each((_, item) => {
|
||||
html += $(item).toString();
|
||||
});
|
||||
$('.single-photo video').each((_, item) => {
|
||||
item = $(item);
|
||||
let videoSrc = item.attr('src');
|
||||
if (videoSrc === undefined) {
|
||||
videoSrc = item.children().attr('src');
|
||||
}
|
||||
const videoPoster = item.attr('poster');
|
||||
html += art(path.join(__dirname, 'templates/video.art'), {
|
||||
videoSrc,
|
||||
videoPoster,
|
||||
});
|
||||
$('.single-photo img,video').each((_, item) => {
|
||||
html += $(item).toString(); // 可能是视频,也可能是图片,格式都比较友好,这里直接添加
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<video poster="{{ videoPoster }}" controls>
|
||||
<source src="{{ videoSrc }}" type="video/mp4">
|
||||
</video>
|
||||
@@ -1,96 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const KEY = 'f0fc4c668392f9f9a447e48584c214ee';
|
||||
|
||||
const id = ctx.params.id;
|
||||
const size = ctx.query.limit ?? '100';
|
||||
|
||||
const params = `albumId=${id}&pageNo=0&pageSize=${size}`;
|
||||
|
||||
const rootUrl = 'https://www.radio.cn';
|
||||
const apiRootUrl = 'https://ytmsout.radio.cn';
|
||||
const detailRootUrl = 'https://ytapi.radio.cn';
|
||||
|
||||
const iconUrl = `${rootUrl}/pc-portal/image/icon_32.jpg`;
|
||||
const currentUrl = `${rootUrl}/pc-portal/sanji/detail.html?columnId=${id}`;
|
||||
const apiUrl = `${apiRootUrl}/web/appSingle/pageByAlbum?${params}`;
|
||||
const detailUrl = `${detailRootUrl}/ytsrv/srv/wifimusicbox/demand/detail`;
|
||||
|
||||
const detailResponse = await got({
|
||||
method: 'post',
|
||||
url: detailUrl,
|
||||
form: {
|
||||
pageIndex: '0',
|
||||
sortType: '1',
|
||||
mobileId: '',
|
||||
providerCode: '25010',
|
||||
pid: id,
|
||||
paySongFlag: '1',
|
||||
richText: '1',
|
||||
h5flag: '1',
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
version: '4.0.0',
|
||||
providerCode: '25010',
|
||||
equipmentSource: 'WEB',
|
||||
},
|
||||
});
|
||||
|
||||
const data = detailResponse.data;
|
||||
|
||||
const timestamp = new Date().getTime();
|
||||
const sign = CryptoJS.MD5(`${params}×tamp=${timestamp}&key=${KEY}`).toString().toUpperCase();
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
headers: {
|
||||
sign,
|
||||
timestamp,
|
||||
'Content-Type': 'application/json',
|
||||
equipmentId: '0000',
|
||||
platformCode: 'WEB',
|
||||
},
|
||||
});
|
||||
|
||||
const items = response.data.data.data.map((item) => {
|
||||
let enclosure_url = item.playUrlHigh ?? item.playUrlLow;
|
||||
enclosure_url = /\.m3u8$/.test(enclosure_url) ? item.downloadUrl : enclosure_url;
|
||||
|
||||
const enclosure_type = `audio/${enclosure_url.match(/\.(\w+)$/)[1]}`;
|
||||
|
||||
return {
|
||||
guid: item.id,
|
||||
title: item.name,
|
||||
link: enclosure_url,
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
description: item.des,
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
}),
|
||||
author: data.anchorpersons,
|
||||
pubDate: parseDate(item.publishTime),
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
enclosure_length: item.fileSize,
|
||||
itunes_duration: item.duration,
|
||||
itunes_item_image: iconUrl,
|
||||
};
|
||||
});
|
||||
|
||||
ctx.state.data = {
|
||||
title: `云听 - ${data.columnName}`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
image: iconUrl,
|
||||
itunes_author: data.anchorpersons,
|
||||
description: data.descriptions ?? data.descriptionSimple,
|
||||
itunes_category: data.atypeInfo.map((c) => c.categoryName).join(),
|
||||
};
|
||||
};
|
||||
+18
-25
@@ -5,44 +5,37 @@ const path = require('path');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
const size = ctx.query.limit ?? '100';
|
||||
|
||||
const rootUrl = 'https://www.radio.cn';
|
||||
const apiRootUrl = 'http://tacc.radio.cn';
|
||||
const apiUrl = `${apiRootUrl}/pcpages/odchannelpages?od_id=${id}&start=1&rows=${size}`;
|
||||
const rootUrl = 'http://www.radio.cn';
|
||||
const currentUrl = `${rootUrl}/pc-portal/sanji/detail.html?columnId=${id}`;
|
||||
|
||||
const apiRootUrl = 'http://tacc.radio.cn';
|
||||
const apiUrl = `${apiRootUrl}/pcpages/odchannelpages?od_id=${id}&start=1&rows=50`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
if (/^\(.*\)$/.test(response.data)) {
|
||||
response.data = JSON.parse(response.data.match(/^\((.*)\)$/)[1]);
|
||||
response.data = JSON.parse(response.data)[0];
|
||||
}
|
||||
|
||||
const data = response.data.data;
|
||||
|
||||
const items = data.program.map((item) => {
|
||||
const enclosure_url = item.streams[0].url;
|
||||
const enclosure_type = `audio/${enclosure_url.match(/\.(\w+)$/)[1]}`;
|
||||
|
||||
return {
|
||||
guid: item.id,
|
||||
title: item.name,
|
||||
link: item.streams[0].url,
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
description: item.description,
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
}),
|
||||
pubDate: parseDate(item.onlinetime),
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
itunes_duration: item.duration,
|
||||
itunes_item_image: data.odchannel.imageUrl[0].url,
|
||||
};
|
||||
});
|
||||
const items = data.program.map((item) => ({
|
||||
guid: item.id,
|
||||
title: item.name,
|
||||
link: item.streams[0].url,
|
||||
description: `${item.description}${art(path.join(__dirname, 'templates/description.art'), {
|
||||
streams: item.streams,
|
||||
})}`,
|
||||
pubDate: parseDate(item.onlinetime),
|
||||
enclosure_url: item.streams[0].url,
|
||||
enclosure_type: 'audio/x-m4a',
|
||||
itunes_duration: item.duration,
|
||||
itunes_item_image: data.odchannel.imageUrl[0].url,
|
||||
}));
|
||||
|
||||
ctx.state.data = {
|
||||
title: `云听 - ${data.odchannel.name}`,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
'/album/:id': ['nczitzk'],
|
||||
'/zhibo/:id': ['nczitzk'],
|
||||
'/:id': ['kt286', 'nczitzk'],
|
||||
};
|
||||
|
||||
+3
-15
@@ -3,23 +3,11 @@ module.exports = {
|
||||
_name: '云听',
|
||||
'.': [
|
||||
{
|
||||
title: '专辑',
|
||||
docs: 'https://docs.rsshub.app/multimedia.html#yun-ting-zhuan-ji',
|
||||
source: ['/pc-portal/sanji/detail.html', '/share/albumDetail', '/'],
|
||||
target: (params, url) => `/radio/album/${new URL(url).searchParams.get('columnId')}`,
|
||||
},
|
||||
{
|
||||
title: '节目',
|
||||
docs: 'https://docs.rsshub.app/multimedia.html#yun-ting-jie-mu',
|
||||
source: ['/pc-portal/sanji/detail.html', '/share/albumDetail', '/'],
|
||||
title: '电台节目',
|
||||
docs: 'https://docs.rsshub.app/multimedia.html#yun-ting-dian-tai-jie-mu',
|
||||
source: ['/pc-portal/sanji/detail.html', '/'],
|
||||
target: (params, url) => `/radio/${new URL(url).searchParams.get('columnId')}`,
|
||||
},
|
||||
{
|
||||
title: '直播',
|
||||
docs: 'https://docs.rsshub.app/multimedia.html#yun-ting-zhi-bo',
|
||||
source: ['/pc-portal/sanji/zhibo_2.html', '/'],
|
||||
target: (params, url) => `/radio/zhibo/${new URL(url).searchParams.get('name')}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
const got = require('@/utils/got');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const channelname = ctx.params.channelname; // 专辑id
|
||||
const name = ctx.params.name; // 专辑id
|
||||
|
||||
const api = `http://tacc.radio.cn/pcpages/searchs/livehistory?channelname=${channelname}&name=${name}&start=1&rows=20`;
|
||||
const response = await got.get(api);
|
||||
|
||||
const playList = response.data.passprogram;
|
||||
let title = '';
|
||||
let itunes_category = '';
|
||||
let items = [];
|
||||
if (playList && playList.length > 0) {
|
||||
const first = playList[0];
|
||||
title = `${first.channel_name} ${first.name}`;
|
||||
itunes_category = first.channel_name;
|
||||
|
||||
items = playList.map((item) => {
|
||||
const single = {
|
||||
title: `${item.name} ${item.broadcast_date}`,
|
||||
link: 'http://' + item.stream_domain1 + item.stream_url1,
|
||||
description: `
|
||||
<audio controls="controls">
|
||||
<source src="http://${item.stream_domain1}${item.stream_url1}" type="audio/x-m4a">
|
||||
<source src="http://${item.stream_domain2}${item.stream_url2}" type="audio/x-m4a">
|
||||
<source src="http://${item.stream_domain3}${item.stream_url3}" type="audio/x-m4a">
|
||||
</audio>
|
||||
`,
|
||||
pubDate: item.update_time,
|
||||
guid: item.id,
|
||||
itunes_item_image: 'http://www.radio.cn/pc-portal/image/icon_32.jpg',
|
||||
enclosure_url: 'http://' + item.stream_domain1 + item.stream_url1,
|
||||
itunes_duration: item.resource_length,
|
||||
enclosure_type: 'audio/x-m4a',
|
||||
};
|
||||
return single;
|
||||
});
|
||||
}
|
||||
|
||||
ctx.state.data = {
|
||||
title,
|
||||
link: `http://www.radio.cn/pc-portal/sanji/zhibo_2.html?channelname=${channelname}&name=${name}&title=radio`,
|
||||
description: title,
|
||||
image: 'http://www.radio.cn/pc-portal/image/icon_32.jpg',
|
||||
itunes_author: 'radio.cn',
|
||||
itunes_category,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
module.exports = function (router) {
|
||||
router.get('/album/:id', require('./album'));
|
||||
router.get('/zhibo/:id', require('./zhibo'));
|
||||
router.get('/:id', require('./index'));
|
||||
};
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
{{ if description }}
|
||||
<p>{{ description }}</p>
|
||||
{{ /if }}
|
||||
{{ if enclosure_url && enclosure_type }}
|
||||
<audio controls="controls">
|
||||
<source src="{{ enclosure_url }}" type="{{ enclosure_type }}">
|
||||
</audio>
|
||||
{{ /if }}
|
||||
{{each streams stream}}
|
||||
<source src="{{ stream.url }}" type="audio/x-m4a">
|
||||
{{/each}}
|
||||
</audio>
|
||||
@@ -1,70 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const KEY = 'f0fc4c668392f9f9a447e48584c214ee';
|
||||
|
||||
const id = ctx.params.id;
|
||||
const size = ctx.query.limit ?? '100';
|
||||
|
||||
const params = `columnId=${id}&pageNo=0&pageSize=${size}`;
|
||||
|
||||
const rootUrl = 'https://www.radio.cn';
|
||||
const apiRootUrl = 'https://ytmsout.radio.cn';
|
||||
|
||||
const iconUrl = `${rootUrl}/pc-portal/image/icon_32.jpg`;
|
||||
const currentUrl = `${rootUrl}/pc-portal/sanji/zhibo_2.html?name=${id}`;
|
||||
const apiUrl = `${apiRootUrl}/web/appProgram/pageByColumn?${params}`;
|
||||
|
||||
const timestamp = new Date().getTime();
|
||||
const sign = CryptoJS.MD5(`${params}×tamp=${timestamp}&key=${KEY}`).toString().toUpperCase();
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
headers: {
|
||||
sign,
|
||||
timestamp,
|
||||
'Content-Type': 'application/json',
|
||||
equipmentId: '0000',
|
||||
platformCode: 'WEB',
|
||||
},
|
||||
});
|
||||
|
||||
const data = response.data.data.data;
|
||||
|
||||
const items = data.map((item) => {
|
||||
let enclosure_url = item.playUrlHigh ?? item.playUrlLow;
|
||||
enclosure_url = /\.m3u8$/.test(enclosure_url) ? item.downloadUrl : enclosure_url;
|
||||
|
||||
const enclosure_type = `audio/${enclosure_url.match(/\.(\w+)$/)[1]}`;
|
||||
|
||||
return {
|
||||
guid: item.id,
|
||||
title: item.name,
|
||||
link: enclosure_url,
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
description: item.des,
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
}),
|
||||
pubDate: parseDate(item.startTime),
|
||||
enclosure_url,
|
||||
enclosure_type,
|
||||
itunes_duration: item.durationStr,
|
||||
itunes_item_image: iconUrl,
|
||||
};
|
||||
});
|
||||
|
||||
ctx.state.data = {
|
||||
title: `云听 - ${data[0].name}`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
image: iconUrl,
|
||||
itunes_author: 'radio.cn',
|
||||
description: data[0].des,
|
||||
};
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const cheerio = require('cheerio');
|
||||
const got = require('@/utils/got');
|
||||
const timezone = require('@/utils/timezone');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const url = `https://www.swpu.edu.cn/nccjxy/xydt/${ctx.params.code}.htm`;
|
||||
|
||||
const res = await got(url);
|
||||
const $ = cheerio.load(res.data);
|
||||
|
||||
let title = $('title').text();
|
||||
title = title.substring(0, title.indexOf('-'));
|
||||
|
||||
const items = $('.main_conRCb > ul > li')
|
||||
.toArray()
|
||||
.map((elem) => ({
|
||||
title: $('a[href]', elem).text().trim(),
|
||||
pubDate: timezone(parseDate($('span', elem).text(), 'YYYY年MM月DD日'), +8),
|
||||
link: `https://www.swpu.edu.cn/nccjxy/${$('a[href]', elem).attr('href').split('../')[1]}`,
|
||||
}));
|
||||
|
||||
const out = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const res = await got(item.link);
|
||||
const $ = cheerio.load(res.data);
|
||||
if ($('title').text().startsWith('系统提示')) {
|
||||
item.author = '系统';
|
||||
item.description = '无权访问';
|
||||
} else {
|
||||
item.author = '财经学院';
|
||||
item.description = $('.v_news_content').html();
|
||||
for (const elem of $('.v_news_content p')) {
|
||||
if ($(elem).css('text-align') === 'right') {
|
||||
item.author = $(elem).text();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `西南石油大学财经学院 ${title}`,
|
||||
link: url,
|
||||
description: `西南石油大学财经学院 ${title}`,
|
||||
language: 'zh-CN',
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const cheerio = require('cheerio');
|
||||
const got = require('@/utils/got');
|
||||
const timezone = require('@/utils/timezone');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const url = `https://www.swpu.edu.cn/is/xydt/${ctx.params.code}.htm`;
|
||||
|
||||
const res = await got(url);
|
||||
const $ = cheerio.load(res.data);
|
||||
|
||||
let title = $('title').text();
|
||||
title = title.substring(0, title.indexOf('-'));
|
||||
|
||||
const items = $('tr[height="20"]')
|
||||
.toArray()
|
||||
.map((elem) => ({
|
||||
title: $('a[title]', elem).text().trim(),
|
||||
pubDate: timezone(parseDate($('td:eq(1)', elem).text(), 'YYYY年MM月DD日'), +8),
|
||||
link: `https://www.swpu.edu.cn/is/${$('a[href]', elem).attr('href').split('../')[1]}`,
|
||||
}));
|
||||
|
||||
const out = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const res = await got(item.link);
|
||||
const $ = cheerio.load(res.data);
|
||||
if ($('title').text().startsWith('系统提示')) {
|
||||
item.author = '系统';
|
||||
item.description = '无权访问';
|
||||
} else {
|
||||
item.author = '学院';
|
||||
item.description = $('.v_news_content').html();
|
||||
for (const elem of $('.v_news_content p')) {
|
||||
if ($(elem).css('text-align') === 'right') {
|
||||
item.author = $(elem).text();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `西南石油大学信息学院 ${title}`,
|
||||
link: url,
|
||||
description: `西南石油大学信息学院 ${title}`,
|
||||
language: 'zh-CN',
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
module.exports = {
|
||||
'/bgw/:code': ['CYTMWIA'],
|
||||
'/cjxy/:code': ['RiverTwilight'],
|
||||
'/dean/:code': ['CYTMWIA'],
|
||||
'/dxy/:code': ['CYTMWIA'],
|
||||
'/is/:code': ['RiverTwilight'],
|
||||
'/scs/:code': ['CYTMWIA'],
|
||||
};
|
||||
|
||||
@@ -26,18 +26,6 @@ module.exports = {
|
||||
source: ['/'],
|
||||
target: '',
|
||||
},
|
||||
{
|
||||
title: '信息学院',
|
||||
docs: 'https://docs.rsshub.app/university.html#xi-nan-shi-you-da-xue',
|
||||
source: ['/'],
|
||||
target: '',
|
||||
},
|
||||
{
|
||||
title: '财经学院',
|
||||
docs: 'https://docs.rsshub.app/university.html#xi-nan-shi-you-da-xue',
|
||||
source: ['/'],
|
||||
target: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module.exports = function (router) {
|
||||
router.get('/bgw/:code', require('./bgw'));
|
||||
router.get('/cjxy/:code', require('./cjxy'));
|
||||
router.get('/dean/:code', require('./dean'));
|
||||
router.get('/dxy/:code', require('./dxy'));
|
||||
router.get('/is/:code', require('./is'));
|
||||
router.get('/scs/:code', require('./scs'));
|
||||
};
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const parser = require('@/utils/rss-parser');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const feed = await parser.parseURL('https://www.theverge.com/rss/index.xml');
|
||||
|
||||
const items = await Promise.all(
|
||||
feed.items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const response = await got(item.link);
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const content = $('#content');
|
||||
const body = $('.duet--article--article-body-component-container');
|
||||
|
||||
// 处理封面图片
|
||||
|
||||
const cover = $('meta[property="og:image"]');
|
||||
|
||||
if (cover.length > 0) {
|
||||
$(`<img src=${cover[0].attribs.content}>`).insertBefore(body[0].childNodes[0]);
|
||||
}
|
||||
|
||||
// 处理封面视频
|
||||
$('div.l-col__main > div.c-video-embed, div.c-entry-hero > div.c-video-embed').each((i, e) => {
|
||||
const src = `https://volume.vox-cdn.com/embed/${e.attribs['data-volume-uuid']}?autoplay=false`;
|
||||
|
||||
$(`<iframe src="${src}" style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" allowfullscreen scrolling="no"></iframe>`).insertBefore(body[0].childNodes[0]);
|
||||
});
|
||||
|
||||
// 处理封面视频
|
||||
$('div.l-col__main > div.c-video-embed--media iframe').each((i, e) => {
|
||||
$(e).insertBefore(body[0].childNodes[0]);
|
||||
});
|
||||
|
||||
// 处理文章图片
|
||||
content.find('figure.e-image').each((i, e) => {
|
||||
let src, caption;
|
||||
|
||||
// 处理 jpeg, png
|
||||
if ($(e).find('picture > source').length > 0) {
|
||||
src = $(e)
|
||||
.find('picture > img')[0]
|
||||
.attribs.srcset.match(/(?<=320w,).*?(?=520w)/g)[0]
|
||||
.trim();
|
||||
} else if ($(e).find('img.c-dynamic-image').length > 0) {
|
||||
// 处理 gif
|
||||
src = $(e).find('span.e-image__image')[0].attribs['data-original'];
|
||||
}
|
||||
|
||||
// 处理 caption
|
||||
if ($(e).find('span.e-image__meta').length > 0) {
|
||||
caption = $(e).find('span.e-image__meta').text();
|
||||
}
|
||||
|
||||
const figure = `<figure><img src=${src}>${caption ? `<br><figcaption>${caption}</figcaption>` : ''}</figure>`;
|
||||
|
||||
$(figure).insertBefore(e);
|
||||
|
||||
$(e).remove();
|
||||
});
|
||||
|
||||
const lede = $('.duet--article--lede h2:first');
|
||||
if (lede[0]) {
|
||||
lede.insertBefore(body[0].childNodes[0]);
|
||||
}
|
||||
|
||||
// 移除无用 DOM
|
||||
content.find('.duet--article--comments-join-the-conversation').remove();
|
||||
content.find('.duet--recirculation--related-list').remove();
|
||||
delete item.content;
|
||||
delete item.contentSnippet;
|
||||
delete item.isoDate;
|
||||
|
||||
item.description = body.html();
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: feed.title,
|
||||
link: feed.link,
|
||||
description: feed.description,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
'/': ['HenryQW'],
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = {
|
||||
'theverge.com': {
|
||||
_name: 'The Verge',
|
||||
'.': [
|
||||
{
|
||||
title: 'The Verge',
|
||||
docs: 'https://docs.rsshub.app/new-media.html#the-verge',
|
||||
source: ['/rss/index.xml', '/'],
|
||||
target: '/theverge',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = (router) => {
|
||||
router.get('/', require('./index'));
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const timezone = require('@/utils/timezone');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const category = ctx.params.category ?? '';
|
||||
|
||||
const start = category === 'hot' ? 6 : 0;
|
||||
const end = category === 'new' ? 6 : 12;
|
||||
|
||||
const rootUrl = 'https://global.udn.com';
|
||||
const currentUrl = `${rootUrl}/global_vision/index${category ? `/${category}` : ''}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: currentUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
$('.topic').remove();
|
||||
|
||||
let items = $('.news_cards ul li a')
|
||||
.toArray()
|
||||
.slice(start, end)
|
||||
.concat(category === '' ? $('.last24, h2').find('a').toArray() : [])
|
||||
.map((item) => {
|
||||
item = $(item);
|
||||
|
||||
const link = item.attr('href');
|
||||
|
||||
return {
|
||||
title: item.find('h3').text() || item.text(),
|
||||
link: /^http/.test(link) ? link : `${rootUrl}${item.attr('href')}`,
|
||||
};
|
||||
});
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
content('#story_art_title, #story_bady_info, #story_also').remove();
|
||||
content('.social_bar, .photo_pop, .only_mobile, .area').remove();
|
||||
|
||||
item.description = content('#tags').prev().html();
|
||||
item.author = content('#story_author_name').text();
|
||||
item.pubDate = timezone(parseDate(content('meta[name="date"]').attr('content')), +8);
|
||||
item.category = content('meta[name="news_keywords"]').attr('content').split(',');
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: $('title').text(),
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,51 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const tag = ctx.params.tag ?? '過去24小時';
|
||||
|
||||
const rootUrl = 'https://global.udn.com';
|
||||
const currentUrl = `${rootUrl}/search/tagging/1020/${tag}`;
|
||||
const apiUrl = `${rootUrl}/search/ajax_tag/1020/${tag}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
let items = response.data.articles.map((item) => ({
|
||||
title: item.TITLE,
|
||||
author: item.AUTHOR,
|
||||
pubDate: parseDate(item.TIMESTAMP * 1000),
|
||||
category: item.TAG.map((t) => t.tag),
|
||||
link: `${rootUrl}/global_vision/story/${item.CATE_ID}/${item.ART_ID}`,
|
||||
}));
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
content('#story_art_title, #story_bady_info, #story_also').remove();
|
||||
content('.social_bar, .photo_pop, .only_mobile, .area').remove();
|
||||
|
||||
item.author = content('#story_author_name').text();
|
||||
item.description = content('#tags').prev().html();
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `轉角國際 udn Global - ${tag}`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
'/global/:category?': ['nczitzk'],
|
||||
'/global/tag/:tag?': ['emdoe', 'nczitzk'],
|
||||
'/news/breakingnews/:id': ['miles170'],
|
||||
};
|
||||
|
||||
+1
-15
@@ -5,23 +5,9 @@ module.exports = {
|
||||
{
|
||||
title: '即時新聞',
|
||||
docs: 'https://docs.rsshub.app/new-media.html#lian-he-xin-wen-wang-ji-shi-xin-wen',
|
||||
source: ['/news/breaknews/1/:id', '/'],
|
||||
source: '/news/breaknews/1/:id',
|
||||
target: '/udn/news/breakingnews/:id',
|
||||
},
|
||||
],
|
||||
global: [
|
||||
{
|
||||
title: '轉角國際 - 首頁',
|
||||
docs: 'https://docs.rsshub.app/new-media.html#lian-he-xin-wen-wang-zhuan-jiao-guo-ji-shou-ye',
|
||||
source: ['/global_vision/index/:category', '/'],
|
||||
target: '/udn/global/:category?',
|
||||
},
|
||||
{
|
||||
title: '轉角國際 - 標籤',
|
||||
docs: 'https://docs.rsshub.app/new-media.html#lian-he-xin-wen-wang-zhuan-jiao-guo-ji-biao-qian',
|
||||
source: ['/search/tagging/1020/:tag', '/'],
|
||||
target: '/udn/global/tag/:tag?',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
module.exports = (router) => {
|
||||
router.get('/global/tag/:tag?', require('./global/tag'));
|
||||
router.get('/global/:category?', require('./global/index'));
|
||||
router.get('/news/breakingnews/:id', require('./breaking-news'));
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ module.exports = {
|
||||
'/snb/:id': ['ZhishanZhang'],
|
||||
'/stock_comments/:id/:titleLength?': ['zytomorrow'],
|
||||
'/stock_info/:id/:type?': ['YuYang'],
|
||||
'/today': ['nczitzk'],
|
||||
'/user/:id/:type?': ['imlonghao'],
|
||||
'/user_stock/:id': ['hillerliao'],
|
||||
};
|
||||
|
||||
@@ -13,12 +13,6 @@ module.exports = {
|
||||
'xueqiu.com': {
|
||||
_name: '雪球',
|
||||
'.': [
|
||||
{
|
||||
title: '今日话题',
|
||||
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
|
||||
source: ['/today'],
|
||||
target: '/xueqiu/today',
|
||||
},
|
||||
{
|
||||
title: '用户动态',
|
||||
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
|
||||
|
||||
@@ -6,7 +6,6 @@ module.exports = (router) => {
|
||||
router.get('/snb/:id', require('./snb'));
|
||||
router.get('/stock_comments/:id/:titleLength?', require('./stock_comments'));
|
||||
router.get('/stock_info/:id/:type?', require('./stock_info'));
|
||||
router.get('/today', require('./today'));
|
||||
router.get('/user/:id/:type?', require('./user'));
|
||||
router.get('/user_stock/:id', require('./user_stock'));
|
||||
};
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const size = ctx.query.limit ?? '20';
|
||||
|
||||
const rootUrl = 'https://xueqiu.com';
|
||||
const currentUrl = `${rootUrl}/today`;
|
||||
const apiUrl = `${rootUrl}/statuses/hot/listV2.json?since_id=-1&size=${size}`;
|
||||
|
||||
const firstResponse = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
const token = firstResponse.headers['set-cookie'].join(',').match(/(xq_a_token=.*?;)/)[1];
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
headers: {
|
||||
Referer: rootUrl,
|
||||
Cookie: token,
|
||||
},
|
||||
});
|
||||
|
||||
let items = response.data.items.map((item) => {
|
||||
item = item.original_status;
|
||||
|
||||
return {
|
||||
title: item.title || item.rawTitle || item.description.replace(/<(.*?)>/g, ''),
|
||||
link: `${rootUrl}${item.target}`,
|
||||
pubDate: parseDate(item.created_at),
|
||||
author: item.user.screen_name,
|
||||
};
|
||||
});
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
headers: {
|
||||
Referer: rootUrl,
|
||||
Cookie: token,
|
||||
},
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
item.description = content('.article__bd__detail').html();
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: '今日话题 - 雪球',
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -30,7 +30,6 @@ module.exports = async (ctx) => {
|
||||
|
||||
return {
|
||||
id: isBio ? null : item.id,
|
||||
guid: isBio ? `bio:${item.url}` : `news:${item.title}${item.date}`,
|
||||
title: isBio ? `${randomEmoji} ${item.url}` : item.title,
|
||||
category: item.category ?? 'Achievement',
|
||||
date: isBio ? item.url : item.date,
|
||||
@@ -59,7 +58,7 @@ module.exports = async (ctx) => {
|
||||
// the article publish time
|
||||
pubDate: parseDate(i.date),
|
||||
// guid
|
||||
guid: i.guid,
|
||||
guid: i.title + i.date,
|
||||
// the article link
|
||||
link: i.id ? `${officialUrl}/${i.id}` : officialUrl,
|
||||
category: i.category,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint-disable no-console */
|
||||
const got = require('@/utils/got');
|
||||
const utils = require('./utils');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const g_encrypt = require('./execlib/g_encrypt_3');
|
||||
const zhihu_hash = require('./execlib/hash.js');
|
||||
const md5 = require('@/utils/md5');
|
||||
const g_encrypt = require('./execlib/g_encrypt');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
@@ -34,16 +33,16 @@ module.exports = async (ctx) => {
|
||||
const cookie = `d_c0=${cookie_mes}`;
|
||||
|
||||
// second: get real data from zhihu
|
||||
const apiPath = `/api/v3/moments/${id}/activities?limit=7&desktop=true`;
|
||||
const apiPath = `/api/v4/web_moments/${id}/activities?limit=7&desktop=true`;
|
||||
|
||||
// calculate x-zse-96, refer to https://github.com/srx-2000/spider_collection/issues/18
|
||||
const f = `101_3_3.0+${apiPath}+${cookie_mes}`;
|
||||
const xzse96 = '2.0_' + g_encrypt(zhihu_hash(f));
|
||||
const _header = { cookie, 'x-zse-96': xzse96, 'x-app-za': 'OS=Web', 'x-zse-93': '101_3_3.0' };
|
||||
const f = `101_3_2.0+${apiPath}+${cookie_mes}`;
|
||||
const xzse96 = '2.0_' + g_encrypt(md5(f));
|
||||
const _header = { cookie, 'x-zse-96': xzse96, 'x-app-za': 'OS=Web', 'x-zse-93': '101_3_2.0' };
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: `https://www.zhihu.com/api/v3/moments/${id}/activities?limit=7&desktop=true`,
|
||||
url: `https://www.zhihu.com/api/v4/web_moments/${id}/activities?limit=7&desktop=true`,
|
||||
headers: {
|
||||
...utils.header,
|
||||
..._header,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,155 +0,0 @@
|
||||
function o(e, t) {
|
||||
const n = (65535 & e) + (65535 & t);
|
||||
return (((e >> 16) + (t >> 16) + (n >> 16)) << 16) | (65535 & n);
|
||||
}
|
||||
|
||||
function a(e, t, n, r, i, a) {
|
||||
let c, u;
|
||||
return o(((c = o(o(t, e), o(r, a))) << (u = i)) | (c >>> (32 - u)), n);
|
||||
}
|
||||
|
||||
function c(e, t, n, r, i, o, c) {
|
||||
return a((t & n) | (~t & r), e, t, i, o, c);
|
||||
}
|
||||
|
||||
function u(e, t, n, r, i, o, c) {
|
||||
return a((t & r) | (n & ~r), e, t, i, o, c);
|
||||
}
|
||||
|
||||
function s(e, t, n, r, i, o, c) {
|
||||
return a(t ^ n ^ r, e, t, i, o, c);
|
||||
}
|
||||
|
||||
function l(e, t, n, r, i, o, c) {
|
||||
return a(n ^ (t | ~r), e, t, i, o, c);
|
||||
}
|
||||
|
||||
function d(e, t) {
|
||||
let n, r, i, a, d;
|
||||
(e[t >> 5] |= 128 << t % 32), (e[14 + (((t + 64) >>> 9) << 4)] = t);
|
||||
let f = 1732584193,
|
||||
p = -271733879,
|
||||
h = -1732584194,
|
||||
b = 271733878;
|
||||
for (n = 0; n < e.length; n += 16) {
|
||||
(r = f),
|
||||
(i = p),
|
||||
(a = h),
|
||||
(d = b),
|
||||
(f = c(f, p, h, b, e[n], 7, -680876936)),
|
||||
(b = c(b, f, p, h, e[n + 1], 12, -389564586)),
|
||||
(h = c(h, b, f, p, e[n + 2], 17, 606105819)),
|
||||
(p = c(p, h, b, f, e[n + 3], 22, -1044525330)),
|
||||
(f = c(f, p, h, b, e[n + 4], 7, -176418897)),
|
||||
(b = c(b, f, p, h, e[n + 5], 12, 1200080426)),
|
||||
(h = c(h, b, f, p, e[n + 6], 17, -1473231341)),
|
||||
(p = c(p, h, b, f, e[n + 7], 22, -45705983)),
|
||||
(f = c(f, p, h, b, e[n + 8], 7, 1770035416)),
|
||||
(b = c(b, f, p, h, e[n + 9], 12, -1958414417)),
|
||||
(h = c(h, b, f, p, e[n + 10], 17, -42063)),
|
||||
(p = c(p, h, b, f, e[n + 11], 22, -1990404162)),
|
||||
(f = c(f, p, h, b, e[n + 12], 7, 1804603682)),
|
||||
(b = c(b, f, p, h, e[n + 13], 12, -40341101)),
|
||||
(h = c(h, b, f, p, e[n + 14], 17, -1502002290)),
|
||||
(f = u(f, (p = c(p, h, b, f, e[n + 15], 22, 1236535329)), h, b, e[n + 1], 5, -165796510)),
|
||||
(b = u(b, f, p, h, e[n + 6], 9, -1069501632)),
|
||||
(h = u(h, b, f, p, e[n + 11], 14, 643717713)),
|
||||
(p = u(p, h, b, f, e[n], 20, -373897302)),
|
||||
(f = u(f, p, h, b, e[n + 5], 5, -701558691)),
|
||||
(b = u(b, f, p, h, e[n + 10], 9, 38016083)),
|
||||
(h = u(h, b, f, p, e[n + 15], 14, -660478335)),
|
||||
(p = u(p, h, b, f, e[n + 4], 20, -405537848)),
|
||||
(f = u(f, p, h, b, e[n + 9], 5, 568446438)),
|
||||
(b = u(b, f, p, h, e[n + 14], 9, -1019803690)),
|
||||
(h = u(h, b, f, p, e[n + 3], 14, -187363961)),
|
||||
(p = u(p, h, b, f, e[n + 8], 20, 1163531501)),
|
||||
(f = u(f, p, h, b, e[n + 13], 5, -1444681467)),
|
||||
(b = u(b, f, p, h, e[n + 2], 9, -51403784)),
|
||||
(h = u(h, b, f, p, e[n + 7], 14, 1735328473)),
|
||||
(f = s(f, (p = u(p, h, b, f, e[n + 12], 20, -1926607734)), h, b, e[n + 5], 4, -378558)),
|
||||
(b = s(b, f, p, h, e[n + 8], 11, -2022574463)),
|
||||
(h = s(h, b, f, p, e[n + 11], 16, 1839030562)),
|
||||
(p = s(p, h, b, f, e[n + 14], 23, -35309556)),
|
||||
(f = s(f, p, h, b, e[n + 1], 4, -1530992060)),
|
||||
(b = s(b, f, p, h, e[n + 4], 11, 1272893353)),
|
||||
(h = s(h, b, f, p, e[n + 7], 16, -155497632)),
|
||||
(p = s(p, h, b, f, e[n + 10], 23, -1094730640)),
|
||||
(f = s(f, p, h, b, e[n + 13], 4, 681279174)),
|
||||
(b = s(b, f, p, h, e[n], 11, -358537222)),
|
||||
(h = s(h, b, f, p, e[n + 3], 16, -722521979)),
|
||||
(p = s(p, h, b, f, e[n + 6], 23, 76029189)),
|
||||
(f = s(f, p, h, b, e[n + 9], 4, -640364487)),
|
||||
(b = s(b, f, p, h, e[n + 12], 11, -421815835)),
|
||||
(h = s(h, b, f, p, e[n + 15], 16, 530742520)),
|
||||
(f = l(f, (p = s(p, h, b, f, e[n + 2], 23, -995338651)), h, b, e[n], 6, -198630844)),
|
||||
(b = l(b, f, p, h, e[n + 7], 10, 1126891415)),
|
||||
(h = l(h, b, f, p, e[n + 14], 15, -1416354905)),
|
||||
(p = l(p, h, b, f, e[n + 5], 21, -57434055)),
|
||||
(f = l(f, p, h, b, e[n + 12], 6, 1700485571)),
|
||||
(b = l(b, f, p, h, e[n + 3], 10, -1894986606)),
|
||||
(h = l(h, b, f, p, e[n + 10], 15, -1051523)),
|
||||
(p = l(p, h, b, f, e[n + 1], 21, -2054922799)),
|
||||
(f = l(f, p, h, b, e[n + 8], 6, 1873313359)),
|
||||
(b = l(b, f, p, h, e[n + 15], 10, -30611744)),
|
||||
(h = l(h, b, f, p, e[n + 6], 15, -1560198380)),
|
||||
(p = l(p, h, b, f, e[n + 13], 21, 1309151649)),
|
||||
(f = l(f, p, h, b, e[n + 4], 6, -145523070)),
|
||||
(b = l(b, f, p, h, e[n + 11], 10, -1120210379)),
|
||||
(h = l(h, b, f, p, e[n + 2], 15, 718787259)),
|
||||
(p = l(p, h, b, f, e[n + 9], 21, -343485551)),
|
||||
(f = o(f, r)),
|
||||
(p = o(p, i)),
|
||||
(h = o(h, a)),
|
||||
(b = o(b, d));
|
||||
}
|
||||
return [f, p, h, b];
|
||||
}
|
||||
|
||||
function f(e) {
|
||||
let t,
|
||||
n = '';
|
||||
const r = 32 * e.length;
|
||||
for (t = 0; t < r; t += 8) {
|
||||
n += String.fromCharCode((e[t >> 5] >>> t % 32) & 255);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
function p(e) {
|
||||
let t;
|
||||
const n = [];
|
||||
for (n[(e.length >> 2) - 1] = void 0, t = 0; t < n.length; t += 1) {
|
||||
n[t] = 0;
|
||||
}
|
||||
const r = 8 * e.length;
|
||||
for (t = 0; t < r; t += 8) {
|
||||
n[t >> 5] |= (255 & e.charCodeAt(t / 8)) << t % 32;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
function h(e) {
|
||||
let t,
|
||||
n,
|
||||
r = '';
|
||||
for (n = 0; n < e.length; n += 1) {
|
||||
(t = e.charCodeAt(n)), (r += '0123456789abcdef'.charAt((t >>> 4) & 15) + '0123456789abcdef'.charAt(15 & t));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function b(e) {
|
||||
return unescape(encodeURIComponent(e));
|
||||
}
|
||||
|
||||
function v(e) {
|
||||
return (function (e) {
|
||||
return f(d(p(e), 8 * e.length));
|
||||
})(b(e));
|
||||
}
|
||||
|
||||
function A(e) {
|
||||
return h(v(e));
|
||||
}
|
||||
|
||||
module.exports = A;
|
||||
@@ -1,118 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
const ids = {
|
||||
recommend: {
|
||||
url: 'content/recommend',
|
||||
title: '推荐',
|
||||
},
|
||||
hkstock: {
|
||||
url: 'content/hkstock',
|
||||
title: '港股',
|
||||
},
|
||||
meigu: {
|
||||
url: 'content/meigu',
|
||||
title: '美股',
|
||||
},
|
||||
agu: {
|
||||
url: 'content/agu',
|
||||
title: '沪深',
|
||||
},
|
||||
ct: {
|
||||
url: 'content/ct',
|
||||
title: '创投',
|
||||
},
|
||||
esg: {
|
||||
url: 'content/esg',
|
||||
title: 'ESG',
|
||||
},
|
||||
aqs: {
|
||||
url: 'content/aqs',
|
||||
title: '券商',
|
||||
},
|
||||
ajj: {
|
||||
url: 'content/ajj',
|
||||
title: '基金',
|
||||
},
|
||||
focus: {
|
||||
url: 'focus',
|
||||
title: '要闻',
|
||||
},
|
||||
announcement: {
|
||||
url: 'announcement',
|
||||
title: '公告',
|
||||
},
|
||||
research: {
|
||||
url: 'research',
|
||||
title: '研究',
|
||||
},
|
||||
shares: {
|
||||
url: 'shares',
|
||||
title: '新股',
|
||||
},
|
||||
bazaar: {
|
||||
url: 'bazaar',
|
||||
title: '市场',
|
||||
},
|
||||
company: {
|
||||
url: 'company',
|
||||
title: '公司',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id ?? 'recommend';
|
||||
const category = ctx.params.category ?? '';
|
||||
|
||||
const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 20;
|
||||
|
||||
const rootUrl = 'https://www.zhitongcaijing.com';
|
||||
const currentUrl = `${rootUrl}/${ids[id].url}.html${category === '' ? '' : `?category_key=${category}`}`;
|
||||
const apiUrl = `${rootUrl}/${ids[id].url}.html?data_type=1&page=1${category === '' ? '' : `&category_key=${category}`}`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
let items = response.data.data.slice(0, limit).map((item) => ({
|
||||
title: item.title,
|
||||
link: `${rootUrl}${item.url}`,
|
||||
description: item.digest,
|
||||
author: item.author_info.author_name,
|
||||
pubDate: parseDate((item.create_time ?? parseInt(item.original_time)) * 1000),
|
||||
category: (item.keywords?.split(',') ?? []).concat([item.category_name ?? item.type_tag]),
|
||||
}));
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
|
||||
content('#subscribe-vip-box').remove();
|
||||
content('#news-content').remove();
|
||||
|
||||
item.description = art(path.join(__dirname, 'templates/description.art'), {
|
||||
digest: content('.digetst-box').html() || content('.telegram-origin-contentn').html(),
|
||||
description: content('.news-body-content').html(),
|
||||
});
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `智通财经 - ${ids[id].title}`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
'/:id?/:category?': ['nczitzk'],
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
'zhitongcaijing.com': {
|
||||
_name: '智通财经',
|
||||
'.': [
|
||||
{
|
||||
title: '资讯',
|
||||
docs: 'https://docs.rsshub.app/finance.html#zhi-tong-cai-jing-zi-xun',
|
||||
source: ['/:category', '/'],
|
||||
target: (params, url) => {
|
||||
const id = new URL(url).toString().match(/\/(\w+)\.html/)[1];
|
||||
const category = new URL(url).searchParams.get('category_key');
|
||||
return `/zhitongcaijing/${id}${category ? `/${category}` : ''}`;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function (router) {
|
||||
router.get('/:id?/:category?', require('./index'));
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
{{ if digest }}
|
||||
{{@ digest }}
|
||||
{{ /if }}
|
||||
{{ if description }}
|
||||
{{@ description }}
|
||||
{{ /if }}
|
||||
+7
-8
@@ -54,8 +54,8 @@
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-yml": "1.2.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"jest": "29.1.2",
|
||||
"meilisearch": "0.28.0",
|
||||
"jest": "29.0.3",
|
||||
"meilisearch": "0.27.0",
|
||||
"mockdate": "3.0.5",
|
||||
"nock": "13.2.9",
|
||||
"nodemon": "2.0.20",
|
||||
@@ -83,18 +83,17 @@
|
||||
"dependencies": {
|
||||
"@koa/router": "12.0.0",
|
||||
"@postlight/parser": "2.2.2",
|
||||
"@sentry/node": "7.14.1",
|
||||
"@sentry/node": "7.13.0",
|
||||
"aes-js": "3.1.2",
|
||||
"art-template": "4.13.2",
|
||||
"bbcodejs": "0.0.4",
|
||||
"canvas": "2.10.1",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"chrono-node": "2.4.1",
|
||||
"city-timezones": "1.2.1",
|
||||
"crypto-js": "4.1.1",
|
||||
"currency-symbol-map": "5.1.0",
|
||||
"dayjs": "1.11.5",
|
||||
"dotenv": "16.0.3",
|
||||
"dotenv": "16.0.2",
|
||||
"emailjs-imap-client": "3.1.0",
|
||||
"entities": "3.0.1",
|
||||
"etag": "1.8.1",
|
||||
@@ -108,7 +107,7 @@
|
||||
"ioredis": "5.2.3",
|
||||
"ip-regex": "4.3.0",
|
||||
"is-localhost-ip": "2.0.0",
|
||||
"jsdom": "20.0.1",
|
||||
"jsdom": "20.0.0",
|
||||
"json-bigint": "1.0.0",
|
||||
"json5": "2.2.1",
|
||||
"koa": "2.13.4",
|
||||
@@ -123,12 +122,12 @@
|
||||
"parse-torrent": "9.1.5",
|
||||
"pidusage": "3.0.0",
|
||||
"plist": "3.0.6",
|
||||
"proxy-chain": "2.1.0",
|
||||
"proxy-chain": "2.0.7",
|
||||
"puppeteer": "18.0.5",
|
||||
"puppeteer-extra": "3.3.4",
|
||||
"puppeteer-extra-plugin-stealth": "2.11.1",
|
||||
"query-string": "7.1.1",
|
||||
"rand-user-agent": "1.0.84",
|
||||
"rand-user-agent": "1.0.83",
|
||||
"re2": "1.17.7",
|
||||
"require-all": "3.0.0",
|
||||
"rss-parser": "3.12.0",
|
||||
|
||||
Reference in New Issue
Block a user