Revert "Merge branch 'DIYgod:master' into master"

This reverts commit 102cb48db6, reversing
changes made to ccac4be518.
This commit is contained in:
evnydd0sf
2022-10-04 15:41:40 +08:00
parent a08cf32799
commit c8841910b8
81 changed files with 776 additions and 2660 deletions
+3 -10
View File
@@ -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 -1
View File
@@ -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?' }] },
-6
View File
@@ -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
### 当前日期发售的新产品
-2
View File
@@ -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.
-58
View File
@@ -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
-29
View File
@@ -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>
## 中国人民银行
### 沟通交流
-12
View File
@@ -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
View File
@@ -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
View File
@@ -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
### 分类
+6 -77
View File
@@ -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 迁移说明
-20
View File
@@ -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
View File
@@ -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'));
+38
View File
@@ -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;
})
)
),
};
};
+61
View File
@@ -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,
};
};
+88
View File
@@ -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,
};
};
+8 -8
View File
@@ -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
View File
@@ -1,4 +1,3 @@
module.exports = {
'/:category?': ['nczitzk'],
'/topic/:id': ['nczitzk'],
};
-6
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
module.exports = function (router) {
router.get(/([\w-/]+)?/, require('./index'));
router.get('/:category?', require('./index'));
};
-87
View File
@@ -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,
};
};
-5
View File
@@ -1,5 +0,0 @@
module.exports = {
'/:language?/:category?': ['nczitzk'],
'/:language?/tag/:id': ['nczitzk'],
'/:language?/rss': ['nczitzk'],
};
-62
View File
@@ -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',
},
],
},
};
-3
View File
@@ -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 }}
+1 -1
View File
@@ -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('');
+7 -30
View File
@@ -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);
}
+8 -26
View File
@@ -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) => {
+3 -9
View File
@@ -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;
-60
View File
@@ -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',
};
};
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
'/book/:id/:coverOnly?/:quality?': ['TonyRL'],
};
-13
View File
@@ -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',
},
],
},
};
-3
View File
@@ -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 }}
-96
View File
@@ -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,
};
-54
View File
@@ -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,
};
};
+9 -33
View File
@@ -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,
-1
View File
@@ -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'],
-11
View File
@@ -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: [
-1
View File
@@ -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'));
+2 -2
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
module.exports = {
'/:category?': ['nczitzk'],
'/': ['nczitzk', 'bigfei'],
'/': ['nczitzk'],
};
+2 -14
View File
@@ -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;
}
-3
View File
@@ -1,3 +0,0 @@
<video poster="{{ videoPoster }}" controls>
<source src="{{ videoSrc }}" type="video/mp4">
</video>
-96
View File
@@ -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}&timestamp=${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
View File
@@ -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}`,
-2
View File
@@ -1,5 +1,3 @@
module.exports = {
'/album/:id': ['nczitzk'],
'/zhibo/:id': ['nczitzk'],
'/:id': ['kt286', 'nczitzk'],
};
+3 -15
View File
@@ -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')}`,
},
],
},
};
+50
View File
@@ -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,
};
};
-2
View File
@@ -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'));
};
+4 -7
View File
@@ -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>
-70
View File
@@ -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}&timestamp=${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,
};
};
-53
View File
@@ -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,
};
};
-53
View File
@@ -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,
};
};
-2
View File
@@ -1,8 +1,6 @@
module.exports = {
'/bgw/:code': ['CYTMWIA'],
'/cjxy/:code': ['RiverTwilight'],
'/dean/:code': ['CYTMWIA'],
'/dxy/:code': ['CYTMWIA'],
'/is/:code': ['RiverTwilight'],
'/scs/:code': ['CYTMWIA'],
};
-12
View File
@@ -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: '',
},
],
},
};
-2
View File
@@ -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'));
};
-90
View File
@@ -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,
};
};
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
'/': ['HenryQW'],
};
-13
View File
@@ -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',
},
],
},
};
-3
View File
@@ -1,3 +0,0 @@
module.exports = (router) => {
router.get('/', require('./index'));
};
-67
View File
@@ -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,
};
};
-51
View File
@@ -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,
};
};
-2
View File
@@ -1,5 +1,3 @@
module.exports = {
'/global/:category?': ['nczitzk'],
'/global/tag/:tag?': ['emdoe', 'nczitzk'],
'/news/breakingnews/:id': ['miles170'],
};
+1 -15
View File
@@ -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?',
},
],
},
};
-2
View File
@@ -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'));
};
-1
View File
@@ -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'],
};
-6
View File
@@ -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',
-1
View File
@@ -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'));
};
-65
View File
@@ -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,
};
};
+1 -2
View File
@@ -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,
+7 -8
View File
@@ -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
-155
View File
@@ -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;
-118
View File
@@ -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,
};
};
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
'/:id?/:category?': ['nczitzk'],
};
-17
View File
@@ -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}` : ''}`;
},
},
],
},
};
-3
View File
@@ -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
View File
@@ -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",
+380 -401
View File
File diff suppressed because it is too large Load Diff