fix(route):雪球 基金净值数据来源调整为蛋卷基金 (#9489)

* 雪球基金净值更新调整为蛋卷基金

* Update docs/finance.md

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* Update docs/finance.md

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* refactor: migrate to v2

* fix: parseDate

* fix: fund unnecessary String()

* feat: add radar

* fix: stock comments template

* style: add a linebreak

* docs: fund example path
This commit is contained in:
NathanDai
2022-04-08 23:38:33 +08:00
committed by GitHub
co-authored by Tony
parent d09693c9b7
commit 6996efd070
15 changed files with 162 additions and 113 deletions
+6 -2
View File
@@ -289,9 +289,9 @@ pageClass: routes
<Route author="hillerliao" example="/xueqiu/user_stock/1247347556" path="/xueqiu/user_stock/:id" :paramsDesc="['用户 id, 可在用户主页 URL 中找到']"/>
### 基金净值更新
### 蛋卷基金净值更新
<Route author="HenryQW" example="/xueqiu/fund/040008" path="/xueqiu/fund/:id" :paramsDesc="['基金代码, 可在基金主页 URL 中找到. 此路由的数据为场外基金 (`F`开头)']"/>
<Route author="HenryQW NathanDai" example="/xueqiu/fund/040008" path="/xueqiu/fund/:id" :paramsDesc="['基金代码, 可在基金主页 URL 中找到. 此路由的数据为场外基金 (`F`开头)']"/>
### 组合最新调仓信息
@@ -311,6 +311,10 @@ pageClass: routes
<Route author="zytomorrow" example="/xueqiu/stock_comments/SZ002626" path="/xueqiu/stock_comments/:id/:titleLength?" :paramsDesc="['股票代码(需要带上交易所)', '标题长度']"/>
### 热帖
<Route author="hillerliao" example="/xueqiu/hots" path="/xueqiu/hots"/>
## 有知有行
### 有知文章
-4
View File
@@ -1257,10 +1257,6 @@ rule
<Route author="kt286" example="/weibo/oasis/user/1990895721" path="/weibo/oasis/user/:userid" :paramsDesc="['用户 id, 可在用户主页 URL 中找到']" anticrawler="1"/>
### 热帖
<Route author="hillerliao" example="/xueqiu/hots" path="/xueqiu/hots"/>
## 悟空问答
### 用户动态
+9 -9
View File
@@ -377,15 +377,15 @@ router.get('/egsea/flash', lazyloadRouteHandler('./routes/egsea/flash'));
// 选股宝
router.get('/xuangubao/subject/:subject_id', lazyloadRouteHandler('./routes/xuangubao/subject'));
// 雪球
router.get('/xueqiu/user/:id/:type?', lazyloadRouteHandler('./routes/xueqiu/user'));
router.get('/xueqiu/favorite/:id', lazyloadRouteHandler('./routes/xueqiu/favorite'));
router.get('/xueqiu/user_stock/:id', lazyloadRouteHandler('./routes/xueqiu/user_stock'));
router.get('/xueqiu/fund/:id', lazyloadRouteHandler('./routes/xueqiu/fund'));
router.get('/xueqiu/stock_info/:id/:type?', lazyloadRouteHandler('./routes/xueqiu/stock_info'));
router.get('/xueqiu/snb/:id', lazyloadRouteHandler('./routes/xueqiu/snb'));
router.get('/xueqiu/hots', lazyloadRouteHandler('./routes/xueqiu/hots'));
router.get('/xueqiu/stock_comments/:id/:titleLength?', lazyloadRouteHandler('./routes/xueqiu/stock_comments'));
// 雪球 migrated to v2
// router.get('/xueqiu/user/:id/:type?', lazyloadRouteHandler('./routes/xueqiu/user'));
// router.get('/xueqiu/favorite/:id', lazyloadRouteHandler('./routes/xueqiu/favorite'));
// router.get('/xueqiu/user_stock/:id', lazyloadRouteHandler('./routes/xueqiu/user_stock'));
// router.get('/xueqiu/fund/:id', lazyloadRouteHandler('./routes/xueqiu/fund'));
// router.get('/xueqiu/stock_info/:id/:type?', lazyloadRouteHandler('./routes/xueqiu/stock_info'));
// router.get('/xueqiu/snb/:id', lazyloadRouteHandler('./routes/xueqiu/snb'));
// router.get('/xueqiu/hots', lazyloadRouteHandler('./routes/xueqiu/hots'));
// router.get('/xueqiu/stock_comments/:id/:titleLength?', lazyloadRouteHandler('./routes/xueqiu/stock_comments'));
// Greasy Fork
router.get('/greasyfork/:language/:domain?', lazyloadRouteHandler('./routes/greasyfork/scripts'));
-71
View File
@@ -1,71 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const guid = `xueqiu/fund ${ctx.params.id}`;
const cache = await ctx.cache.get(guid);
if (cache) {
return Promise.resolve(JSON.parse(cache));
} else {
const url = `https://xueqiu.com/S/F${ctx.params.id}`;
let fundName = await got({
method: 'get',
url,
headers: {
Referer: 'https://xueqiu.com/',
},
});
const $ = cheerio.load(fundName.data);
fundName = $('.stock-name').text();
const fundNameShort = fundName.slice(0, -10);
const response = await got({
method: 'get',
url: `https://fund.xueqiu.com/dj/open/fund/growth/${ctx.params.id}?day=7`,
headers: {
Referer: url,
},
});
const data = response.data.data.fund_nav_growth.pop();
const yesterday = response.data.data.fund_nav_growth.pop();
let description = `${fundNameShort} <br> 最新净值 ${data.nav} <br> 今日`;
let title = `${fundNameShort} ${data.date.substring(5)} `;
const value = (parseFloat(data.nav) - parseFloat(yesterday.nav)).toFixed(4);
if (value > 0) {
description += '涨幅';
title += `📈 ${data.percentage}%`;
} else if (value < 0) {
description += '跌幅';
title += `📉 ${data.percentage}%`;
} else if (value === '0.0000') {
description += '无波动';
title += '持平';
}
description += ` ${data.percentage}%(¥${value})`;
const single = {
title,
description,
pubDate: new Date().toUTCString(),
guid: `${guid} ${data.date}`,
link: url,
};
ctx.state.data = {
title: fundName,
link: url,
description: `${fundName} 净值更新`,
item: [single],
};
ctx.cache.set(guid, JSON.stringify(ctx.state.data));
}
};
@@ -1,5 +1,6 @@
const got = require('@/utils/got');
const queryString = require('query-string');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id;
@@ -30,7 +31,7 @@ module.exports = async (ctx) => {
item: data.map((item) => ({
title: item.title,
description: item.description,
pubDate: new Date(item.created_at).toUTCString(),
pubDate: parseDate(item.created_at),
link: `https://xueqiu.com${item.target}`,
})),
};
+50
View File
@@ -0,0 +1,50 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const timezone = require('@/utils/timezone');
module.exports = async (ctx) => {
const guid = ctx.params.id;
const appUrl = `https://danjuanapp.com/funding/${guid}`;
const url = `https://danjuanapp.com/djapi/fund/${guid}`;
const response = await got({
method: 'get',
url,
headers: {
Referer: appUrl,
},
});
const fd_full_name = response.data.data.fd_full_name;
const fd_name = response.data.data.fd_name;
const fd_code = response.data.data.fd_code;
const unit_nav = response.data.data.fund_derived.unit_nav;
const nav_grtd = response.data.data.fund_derived.nav_grtd;
const end_date = response.data.data.fund_derived.end_date;
let description = `基金代码 ${fd_code} <br> 今日净值(${end_date}) ¥${unit_nav} `;
let title = fd_full_name;
if (nav_grtd > 0) {
description += `<br> 日涨跌 ${nav_grtd}%`;
title += `📈 ${nav_grtd}%`;
} else if (nav_grtd < 0) {
description += `<br> 日跌跌 ${nav_grtd}%`;
title += `📉 ${nav_grtd}%`;
} else if (nav_grtd === '0.0000') {
description += '无波动';
title += '持平';
}
const single = {
title,
description,
pubDate: timezone(parseDate(end_date), +8),
link: appUrl,
};
ctx.state.data = {
title: fd_name,
link: appUrl,
description,
item: [single],
};
};
@@ -1,5 +1,6 @@
const got = require('@/utils/got');
const queryString = require('query-string');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const res1 = await got({
@@ -35,7 +36,7 @@ module.exports = async (ctx) => {
return {
title: item.title ? item.title : description.replace(/<[^>]+>/g, ''),
description: item.text,
pubDate: new Date(item.created_at).toUTCString(),
pubDate: parseDate(item.created_at),
link: `https://xueqiu.com${item.target}`,
author: item.user.screen_name,
};
+60
View File
@@ -0,0 +1,60 @@
module.exports = {
'danjuanapp.com': {
_name: '雪球',
'.': [
{
title: '蛋卷基金净值更新',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/funding/:id'],
target: '/xueqiu/funding/:id',
},
],
},
'xueqiu.com': {
_name: '雪球',
'.': [
{
title: '用户动态',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/u/:id'],
target: '/xueqiu/user/:id',
},
{
title: '用户收藏动态',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/u/:id'],
target: '/xueqiu/favorite/:id',
},
{
title: '用户自选动态',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/u/:id'],
target: '/xueqiu/user_stock/:id',
},
{
title: '组合最新调仓信息',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/p/:id'],
target: '/xueqiu/snb/:id',
},
{
title: '股票信息',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/S/:id'],
target: '/xueqiu/stock_info/:id',
},
{
title: '股票评论',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/S/:id'],
target: '/xueqiu/stock_comments/:id',
},
{
title: '热帖',
docs: 'https://docs.rsshub.app/finance.html#xue-qiu',
source: ['/'],
target: '/xueqiu/hots',
},
],
},
};
+10
View File
@@ -0,0 +1,10 @@
module.exports = (router) => {
router.get('/favorite/:id', require('./favorite'));
router.get('/fund/:id', require('./fund'));
router.get('/hots', require('./hots'));
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('/user/:id/:type?', require('./user'));
router.get('/user_stock/:id', require('./user_stock'));
};
@@ -1,4 +1,5 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id;
@@ -28,7 +29,7 @@ module.exports = async (ctx) => {
const single = {
title,
description,
pubDate: new Date(time).toUTCString(),
pubDate: parseDate(time),
link: url,
guid: `xueqiu::snb::${id}::${time}`,
};
@@ -1,28 +1,12 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const template = require('art-template');
const { art } = require('@/utils/render');
const path = require('path');
const { parseDate } = require('@/utils/parse-date');
const description_template = `
<a href='https://xueqiu.com/u/{{item.user.id}}'>
<div style="font-weight: bold;">{{item.user.screen_name}}:</div>
</a>
<hr/>
{{item.text}}
<br/>
{{if item.retweeted_status}}
<hr style="border:1 dashed #987cb9" />
{{item.retweeted_status.text}}
{{else}}
<hr/>
{{/if}}
<div style="text-align:right">
----来源于:{{item.source}}
</div>`;
module.exports = async (ctx) => {
const id = ctx.params.id;
const titleLength = ctx.params.titleLength || 30;
const titleLength = ctx.params.titleLength ? parseInt(ctx.params.titleLength) : 30;
const res = await got({
method: 'get',
@@ -49,7 +33,7 @@ module.exports = async (ctx) => {
if (item.quote_cards) {
link = item.quote_cards[0].target_url;
}
const description = template.render(description_template, { item });
const description = art(path.join(__dirname, 'templates/comments_description.art'), { item });
return {
title: item.title !== '' ? item.title : item.text.replace(/<[^>]+>/g, '').slice(0, titleLength),
description,
@@ -1,6 +1,7 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const queryString = require('query-string');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id;
@@ -63,7 +64,7 @@ module.exports = async (ctx) => {
return {
title: item.title !== '' ? item.title : item.description.replace(/<[^>]+>/g, ''),
description: item.description,
pubDate: new Date(item.created_at).toUTCString(),
pubDate: parseDate(item.created_at),
link,
};
}),
@@ -0,0 +1,10 @@
<a href='https://xueqiu.com/u/{{ item.user.id }}'><div style="font-weight: bold;">{{ item.user.screen_name }}:</div></a>
<hr/>
{{@ item.text }}
<br/>
{{ if item.retweeted_status }}
<hr style="border:1 dashed #987cb9" />{{@ item.retweeted_status.text }}
{{ else }}
<hr/>
{{ /if }}
<div style="text-align:right"> ----来源于:{{ item.source }}</div>
@@ -1,5 +1,6 @@
const got = require('@/utils/got');
const queryString = require('query-string');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id;
@@ -44,7 +45,7 @@ module.exports = async (ctx) => {
return {
title: item.title ? item.title : description.replace(/<[^>]+>/g, ''),
description,
pubDate: new Date(item.created_at).toUTCString(),
pubDate: parseDate(item.created_at),
link: `https://xueqiu.com${item.target}`,
};
}),
@@ -1,5 +1,6 @@
const got = require('@/utils/got');
const queryString = require('query-string');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id;
@@ -39,8 +40,8 @@ module.exports = async (ctx) => {
description: `@${screen_name} 的雪球自选动态`,
item: data.map((item) => ({
title: `@${screen_name} 关注了股票 ${item.name}`,
description: `@${screen_name} 在${new Date(item.created).toLocaleString()} 关注了 ${item.name}(${item.exchange}:${item.symbol})。`,
pubDate: new Date(item.created).toUTCString(),
description: `@${screen_name} 在${parseDate(item.created).toLocaleString()} 关注了 ${item.name}(${item.exchange}:${item.symbol})。`,
pubDate: parseDate(item.created),
link: `https://xueqiu.com/s/${item.symbol}`,
})),
};