From dae0b254eb004e0fb5e1e6c215fa436db9a186e2 Mon Sep 17 00:00:00 2001 From: Hengyu <76626546+5upernova-heng@users.noreply.github.com> Date: Tue, 7 Mar 2023 04:05:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(route):=20add=20cls=20hot=20articles=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A2=E8=81=94=E7=A4=BE=E7=83=AD=E9=97=A8?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E6=8E=92=E8=A1=8C=E6=A6=9C=20(#12044)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat/add cls hot articles * rename router to one word * Apply suggestions from code review * import crypto * add heading 3 * refactor: migrate to v2 --------- --- docs/finance.md | 20 ++++--- lib/router.js | 4 +- lib/routes/cls/depth.js | 63 ---------------------- lib/v2/cls/depth.js | 82 +++++++++++++++++++++++++++++ lib/v2/cls/hot.js | 45 ++++++++++++++++ lib/v2/cls/maintainer.js | 5 ++ lib/v2/cls/radar.js | 25 +++++++++ lib/v2/cls/router.js | 5 ++ lib/{routes => v2}/cls/telegraph.js | 24 +++++++-- lib/v2/cls/templates/depth.art | 10 ++++ lib/v2/cls/templates/telegraph.art | 10 ++++ lib/v2/cls/utils.js | 15 ++++++ 12 files changed, 232 insertions(+), 76 deletions(-) delete mode 100644 lib/routes/cls/depth.js create mode 100644 lib/v2/cls/depth.js create mode 100644 lib/v2/cls/hot.js create mode 100644 lib/v2/cls/maintainer.js create mode 100644 lib/v2/cls/radar.js create mode 100644 lib/v2/cls/router.js rename lib/{routes => v2}/cls/telegraph.js (50%) create mode 100644 lib/v2/cls/templates/depth.art create mode 100644 lib/v2/cls/templates/telegraph.art create mode 100644 lib/v2/cls/utils.js diff --git a/docs/finance.md b/docs/finance.md index df2c7ba690..2a09ea7c20 100644 --- a/docs/finance.md +++ b/docs/finance.md @@ -224,24 +224,28 @@ TokenInsight 官方亦有提供 RSS,可参考 + -| 看盘 | 公告 | 解读 | 加红 | 推送 | 提醒 | 基金 | -| ----- | ------------ | ------- | --- | ----- | ------ | ---- | -| watch | announcement | explain | red | jpush | remind | fund | +| 看盘 | 公司 | 解读 | 加红 | 推送 | 提醒 | 基金 | 港股 | +| ----- | ------------ | ------- | --- | ----- | ------ | ---- | -- | +| watch | announcement | explain | red | jpush | remind | fund | hk | ### 深度 - + -| 要闻 | 股市 | 环球 | 公司 | 地产 | 券商 | 金融 | 汽车 | 科创版 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | -| 1000 | 1003 | 1007 | 1005 | 1006 | 1118 | 1032 | 1119 | 1111 | +| 头条 | 股市 | 港股 | 环球 | 公司 | 券商 | 基金 | 地产 | 金融 | 汽车 | 科创版 | 品见 | 期货 | 投教 | +| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | +| 1000 | 1003 | 1135 | 1007 | 1005 | 1118 | 1110 | 1006 | 1032 | 1119 | 1111 | 1160 | 1124 | 1176 | +### 热门文章排行榜 + + + ## 第一财经杂志 ### 首页 diff --git a/lib/router.js b/lib/router.js index 233514aa85..4c7705f35b 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2862,8 +2862,8 @@ router.get('/gov/caict/caictgd', lazyloadRouteHandler('./routes/gov/caict/caictg router.get('/cs/news/:caty', lazyloadRouteHandler('./routes/cs/news')); // 财联社 -router.get('/cls/depth/:category?', lazyloadRouteHandler('./routes/cls/depth')); -router.get('/cls/telegraph/:category?', lazyloadRouteHandler('./routes/cls/telegraph')); +// router.get('/cls/depth/:category?', lazyloadRouteHandler('./routes/cls/depth')); +// router.get('/cls/telegraph/:category?', lazyloadRouteHandler('./routes/cls/telegraph')); // hentai-cosplays router.get('/hentai-cosplays/:type?/:name?', lazyloadRouteHandler('./routes/hentai-cosplays/hentai-cosplays')); diff --git a/lib/routes/cls/depth.js b/lib/routes/cls/depth.js deleted file mode 100644 index 75c0ce0a9a..0000000000 --- a/lib/routes/cls/depth.js +++ /dev/null @@ -1,63 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -const config = { - 1000: '要闻', - 1003: '股市', - 1007: '环球', - 1005: '公司', - 1006: '地产', - 1118: '券商', - 1032: '金融', - 1119: '汽车', - 1111: '科创版', -}; - -module.exports = async (ctx) => { - ctx.params.category = ctx.params.category || '1000'; - const title = config[ctx.params.category]; - if (!title) { - throw Error('Bad category. See docs'); - } - - const link = `https://www.cls.cn/v3/depth/home/assembled/${ctx.params.category}?app=CailianpressWeb&os=web&sv=7.2.2&sign=6ac194f4b3b39d45631708474e058b73`; - const response = await got({ - method: 'get', - url: link, - }); - - let list = response.data.data.depth_list.map((item) => ({ - title: item.title || item.brief, - link: `https://www.cls.cn/detail/${item.id}`, - pubDate: new Date(item.ctime * 1000).toUTCString(), - })); - - list = list.concat( - response.data.data.top_article.map((item) => ({ - title: item.title || item.brief, - link: `https://www.cls.cn/detail/${item.id}`, - pubDate: new Date(item.ctime * 1000).toUTCString(), - })) - ); - - const items = 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.description = content('div.detail-content').html(); - return item; - }) - ) - ); - - ctx.state.data = { - title: `财联社 - ${title}`, - link: 'https://www.cls.cn/telegraph', - item: items, - }; -}; diff --git a/lib/v2/cls/depth.js b/lib/v2/cls/depth.js new file mode 100644 index 0000000000..018a54de0f --- /dev/null +++ b/lib/v2/cls/depth.js @@ -0,0 +1,82 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { app, os, sv, getSignedSearchParams } = require('./utils'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const config = { + 1000: '头条', + 1003: '股市', + 1135: '港股', + 1007: '环球', + 1005: '公司', + 1118: '券商', + 1110: '基金', + 1006: '地产', + 1032: '金融', + 1119: '汽车', + 1111: '科创版', + 1160: '品见', + 1124: '期货', + 1176: '投教', +}; + +module.exports = async (ctx) => { + const category = ctx.params.category || '1000'; + const title = config[category]; + if (!title) { + throw Error('Bad category. See docs'); + } + const searchParams = getSignedSearchParams({ + app, + os, + sv, + }); + const baseUrl = 'https://www.cls.cn'; + const link = `${baseUrl}/v3/depth/home/assembled/${category}`; + const response = await got({ + method: 'get', + url: link, + searchParams, + }); + + let list = + response.data.data.depth_list?.map((item) => ({ + title: item.title || item.brief, + link: `${baseUrl}/detail/${item.id}`, + pubDate: new Date(item.ctime * 1000).toUTCString(), + })) || []; + + list = list.concat( + response.data.data.top_article.map((item) => ({ + title: item.title || item.brief, + link: `${baseUrl}/detail/${item.id}`, + pubDate: new Date(item.ctime * 1000).toUTCString(), + })) + ); + + const items = 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); + const nextData = JSON.parse(content('script#__NEXT_DATA__').text()); + const articleDetail = nextData.props.initialState.detail.articleDetail; + + item.description = art(path.join(__dirname, 'templates/depth.art'), { articleDetail }); + item.author = articleDetail.author?.name; + + return item; + }) + ) + ); + + ctx.state.data = { + title: `财联社 - ${title}`, + link: `${baseUrl}/depth?id=${category}`, + item: items, + }; +}; diff --git a/lib/v2/cls/hot.js b/lib/v2/cls/hot.js new file mode 100644 index 0000000000..af36223646 --- /dev/null +++ b/lib/v2/cls/hot.js @@ -0,0 +1,45 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { app, os, sv, getSignedSearchParams } = require('./utils'); + +async function getDescription(link, ctx) { + const description = await ctx.cache.tryGet(link, async () => { + const detailResponse = await got({ + method: 'get', + url: link, + }); + const content = cheerio.load(detailResponse.data); + + return content('div.detail-content').html(); + }); + return description; +} + +module.exports = async (ctx) => { + const searchParams = getSignedSearchParams({ + app, + os, + sv, + }); + + const link = 'https://www.cls.cn/v2/article/hot/list'; + const response = await got({ + method: 'get', + url: link, + searchParams, + }); + const items = await Promise.all( + response.data.data.map(async (item) => ({ + title: item.title || item.brief, + link: `https://www.cls.cn/detail/${item.id}`, + pubDate: new Date(item.ctime * 1000).toUTCString(), + description: await getDescription(`https://www.cls.cn/detail/${item.id}`, ctx), + })) + ); + + ctx.state.data = { + title: '财联社 - 热门文章排行榜', + link: 'https://www.cls.cn/', + item: items, + }; +}; diff --git a/lib/v2/cls/maintainer.js b/lib/v2/cls/maintainer.js new file mode 100644 index 0000000000..4c7acb9d89 --- /dev/null +++ b/lib/v2/cls/maintainer.js @@ -0,0 +1,5 @@ +module.exports = { + '/depth/:category?': ['nczitzk'], + '/hot': ['5upernova-heng'], + '/telegraph/:category?': ['nczitzk'], +}; diff --git a/lib/v2/cls/radar.js b/lib/v2/cls/radar.js new file mode 100644 index 0000000000..0ba4cb0d66 --- /dev/null +++ b/lib/v2/cls/radar.js @@ -0,0 +1,25 @@ +module.exports = { + 'cls.cn': { + _name: '财联社', + '.': [ + { + title: '电报', + docs: 'https://docs.rsshub.app/finance.html#cai-lian-she', + sources: ['/telegraph', '/'], + target: '/cls/telegraph', + }, + { + title: '深度', + docs: 'https://docs.rsshub.app/finance.html#cai-lian-she', + sources: ['/depth'], + target: (_, url) => `/cls/depth/${new URL(url).searchParams.get('id')}`, + }, + { + title: '热门文章排行榜', + docs: 'https://docs.rsshub.app/finance.html#cai-lian-she', + sources: ['/'], + target: '/cls/hot', + }, + ], + }, +}; diff --git a/lib/v2/cls/router.js b/lib/v2/cls/router.js new file mode 100644 index 0000000000..d844c6068d --- /dev/null +++ b/lib/v2/cls/router.js @@ -0,0 +1,5 @@ +module.exports = (router) => { + router.get('/depth/:category?', require('./depth')); + router.get('/hot', require('./hot')); + router.get('/telegraph/:category?', require('./telegraph')); +}; diff --git a/lib/routes/cls/telegraph.js b/lib/v2/cls/telegraph.js similarity index 50% rename from lib/routes/cls/telegraph.js rename to lib/v2/cls/telegraph.js index 8eb32880e4..0c5efcfa9a 100644 --- a/lib/routes/cls/telegraph.js +++ b/lib/v2/cls/telegraph.js @@ -1,29 +1,47 @@ const got = require('@/utils/got'); +const { app, os, sv, getSignedSearchParams } = require('./utils'); +const { art } = require('@/utils/render'); +const path = require('path'); const titles = { watch: '看盘', - announcement: '公告', + announcement: '公司', explain: '解读', red: '加红', jpush: '推送', remind: '提醒', fund: '基金', + hk: '港股', }; module.exports = async (ctx) => { const category = ctx.params.category || ''; + const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 30; - const link = `https://www.cls.cn/nodeapi/updateTelegraphList?app=CailianpressWeb&category=${category}&hasFirstVipArticle=1&rn=30`; + const searchParams = getSignedSearchParams({ + app, + category, + hasFirstVipArticle: 1, + os, + rn: limit, + sv, + }); + let link = 'https://www.cls.cn/nodeapi/updateTelegraphList'; + if (category) { + link = 'https://www.cls.cn/v1/roll/get_roll_list'; + } const response = await got({ method: 'get', url: link, + searchParams, }); const list = response.data.data.roll_data.map((item) => ({ title: item.title || item.content, link: item.shareurl, - description: item.content, + description: art(path.join(__dirname, 'templates/telegraph.art'), { item }), pubDate: new Date(item.ctime * 1000).toUTCString(), + category: item.subjects?.map((s) => s.subject_name), })); ctx.state.data = { diff --git a/lib/v2/cls/templates/depth.art b/lib/v2/cls/templates/depth.art new file mode 100644 index 0000000000..eff8d7070d --- /dev/null +++ b/lib/v2/cls/templates/depth.art @@ -0,0 +1,10 @@ +{{ if articleDetail.images }} + {{ each articleDetail.images i }} + + {{ /each }} +
+{{ /if }} + +{{ if articleDetail.content }} +{{@ articleDetail.content }} +{{ /if }} diff --git a/lib/v2/cls/templates/telegraph.art b/lib/v2/cls/templates/telegraph.art new file mode 100644 index 0000000000..5d40c55dbf --- /dev/null +++ b/lib/v2/cls/templates/telegraph.art @@ -0,0 +1,10 @@ +{{ if item.content }} + {{ item.content }} +{{ /if }} + +{{ if item.images }} +
+ {{ each item.images i }} + + {{ /each }} +{{ /if }} diff --git a/lib/v2/cls/utils.js b/lib/v2/cls/utils.js new file mode 100644 index 0000000000..723b36381a --- /dev/null +++ b/lib/v2/cls/utils.js @@ -0,0 +1,15 @@ +const CryptoJS = require('crypto-js'); + +const getSignedSearchParams = (searchParams) => { + searchParams = new URLSearchParams(searchParams); + searchParams.sort(); + searchParams.append('sign', CryptoJS.MD5(CryptoJS.SHA1(searchParams.toString()).toString()).toString()); + return searchParams; +}; + +module.exports = { + appName: 'CailianpressWeb', + os: 'web', + sv: '7.7.5', + getSignedSearchParams, +};