feat(route): add juejin news (#7969)

* feat(route): add juejin news

* feat(route): add juejin news

* fix(route): juejin news pubDate

* fix: fix utility

* refactor: migrate to v2

* fix: typo

Co-authored-by: TonyRL <TonyRL@users.noreply.github.com>
This commit is contained in:
CancerGary 2022-05-20 01:55:18 +08:00 committed by GitHub
parent 8a8dde7432
commit 950bfa61ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 163 additions and 89 deletions

View File

@ -151,22 +151,6 @@
},
'algocasts.io': { _name: 'AlgoCasts', '.': [{ title: '视频更新', docs: 'https://docs.rsshub.app/programming.html#algocasts', source: '/episodes', target: '/algocasts' }] },
'soulapp.cn': { _name: 'Soul', '.': [{ title: '瞬间更新', docs: 'https://docs.rsshub.app/social-media.html#soul' }] },
'juejin.cn': {
_name: '掘金',
'.': [
{ title: '标签', docs: 'https://docs.rsshub.app/programming.html#jue-jin-biao-qian', source: '/tag/:tag', target: '/juejin/tag/:tag' },
{ title: '小册', docs: 'https://docs.rsshub.app/programming.html#jue-jin-xiao-ce', source: '/books', target: '/juejin/books' },
{
title: '沸点',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-fei-dian',
source: ['/pins/:type', '/pins/topic/:type'],
target: (params) => (params.type !== 'recommended' ? '/juejin/pins/:type' : '/juejin/pins'),
},
{ title: '专栏', docs: 'https://docs.rsshub.app/programming.html#jue-jin-zhuan-lan', source: ['/user/:id', '/user/:id/posts'], target: '/juejin/posts/:id' },
{ title: '收藏集', docs: 'https://docs.rsshub.app/programming.html#jue-jin-shou-cang-ji', source: ['/user/:id', '/user/:id/collections'], target: '/juejin/collections/:id' },
{ title: '单个收藏夹', docs: 'https://docs.rsshub.app/programming.html#jue-jin-dan-ge-shou-cang-jia', source: '/collection/:collectionId', target: '/juejin/collection/:collectionId' },
],
},
'anime1.me': {
_name: 'Anime1',
'.': [

View File

@ -701,7 +701,7 @@ GitHub 官方也提供了一些 RSS:
### 分类
<Route author="DIYgod" example="/juejin/category/frontend" path="/juejin/category/:category" :paramsDesc="['分类名']">
<Route author="DIYgod" example="/juejin/category/frontend" path="/juejin/category/:category" :paramsDesc="['分类名']" radar="1" rssbud="1">
| 后端 | 前端 | Android | iOS | 人工智能 | 开发工具 | 代码人生 | 阅读 |
| ------- | -------- | ------- | --- | ---- | ------- | ------ | ------- |
@ -711,11 +711,11 @@ GitHub 官方也提供了一些 RSS:
### 标签
<Route author="isheng5" example="/juejin/tag/架构" path="/juejin/tag/:tag" :paramsDesc="['标签名, 可在标签 URL 中找到']"/>
<Route author="isheng5" example="/juejin/tag/架构" path="/juejin/tag/:tag" :paramsDesc="['标签名, 可在标签 URL 中找到']" radar="1" rssbud="1"/>
### 热门
<Route author="moaix" example="/juejin/trending/ios/monthly" path="/juejin/trending/:category/:type" :paramsDesc="['分类名', '类型']">
<Route author="moaix" example="/juejin/trending/ios/monthly" path="/juejin/trending/:category/:type" :paramsDesc="['分类名', '类型']" radar="1" rssbud="1">
| category | 标签 |
| -------- | ------- |
@ -741,13 +741,13 @@ GitHub 官方也提供了一些 RSS:
### 小册
<Route author="xyqfer" example="/juejin/books" path="/juejin/books"/>
<Route author="xyqfer" example="/juejin/books" path="/juejin/books" radar="1" rssbud="1"/>
> 掘金小册需要付费订阅RSS 仅做更新提醒,不含付费内容.
### 沸点
<Route author="xyqfer laampui" example="/juejin/pins/6824710202487472141" :paramsDesc="['默认为 recommend见下表']" path="/juejin/pins/:type?">
<Route author="xyqfer laampui" example="/juejin/pins/6824710202487472141" :paramsDesc="['默认为 recommend见下表']" path="/juejin/pins/:type?" radar="1" rssbud="1">
| 推荐 | 热门 | 上班摸鱼 | 内推招聘 | 一图胜千言 | 今天学到了 | 每天一道算法题 | 开发工具推荐 | 树洞一下 |
| --------- | --- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
@ -755,21 +755,29 @@ GitHub 官方也提供了一些 RSS:
</Route>
### 专栏
### 用户专栏
<Route author="Maecenas" example="/juejin/posts/3051900006845944" path="/juejin/posts/:id" :paramsDesc="['用户 id, 可在用户页 URL 中找到']" radar="1" rssbud="1"/>
<Route author="Maecenas" example="/juejin/posts/3051900006845944" path="/juejin/posts/:id" :paramsDesc="['用户 id, 可在用户页 URL 中找到']" radar="1" rssbud="1" radar="1" rssbud="1"/>
### 收藏集
<Route author="isQ" example="/juejin/collections/1697301682482439" path="/juejin/collections/:userId" :paramsDesc="['用户唯一标志符, 在浏览器地址栏URL中能够找到']"/>
<Route author="isQ" example="/juejin/collections/1697301682482439" path="/juejin/collections/:userId" :paramsDesc="['用户唯一标志符, 在浏览器地址栏URL中能够找到']" radar="1" rssbud="1"/>
### 单个收藏夹
<Route author="isQ" example="/juejin/collection/6845243180586123271" path="/juejin/collection/:collectionId" :paramsDesc="['收藏夹唯一标志符, 在浏览器地址栏URL中能够找到']"/>
<Route author="isQ" example="/juejin/collection/6845243180586123271" path="/juejin/collection/:collectionId" :paramsDesc="['收藏夹唯一标志符, 在浏览器地址栏URL中能够找到']" radar="1" rssbud="1"/>
### 分享
<Route author="qiwihui" example="/juejin/shares/56852b2460b2a099cdc1d133" path="/juejin/shares/:userId" :paramsDesc="['用户 id, 可在用户页 URL 中找到']"/>
<Route author="qiwihui" example="/juejin/shares/56852b2460b2a099cdc1d133" path="/juejin/shares/:userId" :paramsDesc="['用户 id, 可在用户页 URL 中找到']" radar="1" rssbud="1"/>
### 专栏
<Route author="xiangzy1" example="/juejin/column/6960559453037199391" path="/juejin/column/:id" :paramsDesc="['专栏 id, 可在专栏页 URL 中找到']" radar="1" rssbud="1"/>
### 资讯
<Route author="cancergary" example="/juejin/news/739332228916791" path="/juejin/news/:userId" :paramsDesc="['用户 id, 可在用户页 URL 中找到']" radar="1" rssbud="1"/>
## 开源中国

View File

@ -207,47 +207,6 @@ module.exports = {
},
],
},
'juejin.cn': {
_name: '掘金',
'.': [
{
title: '标签',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-biao-qian',
source: '/tag/:tag',
target: '/juejin/tag/:tag',
},
{
title: '小册',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-xiao-ce',
source: '/books',
target: '/juejin/books',
},
{
title: '沸点',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-fei-dian',
source: ['/pins/:type', '/pins/topic/:type'],
target: (params) => (params.type !== 'recommended' ? '/juejin/pins/:type' : '/juejin/pins'),
},
{
title: '专栏',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-zhuan-lan',
source: ['/user/:id', '/user/:id/posts'],
target: '/juejin/posts/:id',
},
{
title: '收藏集',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-shou-cang-ji',
source: ['/user/:id', '/user/:id/collections'],
target: '/juejin/collections/:id',
},
{
title: '单个收藏夹',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-dan-ge-shou-cang-jia',
source: '/collection/:collectionId',
target: '/juejin/collection/:collectionId',
},
],
},
'anime1.me': {
_name: 'Anime1',
'.': [

View File

@ -68,17 +68,18 @@ router.get('/ncm/artist/:id', lazyloadRouteHandler('./routes/ncm/artist'));
router.get('/ncm/djradio/:id', lazyloadRouteHandler('./routes/ncm/djradio'));
router.get('/ncm/user/playrecords/:uid/:type?', lazyloadRouteHandler('./routes/ncm/userplayrecords'));
// 掘金
router.get('/juejin/category/:category', lazyloadRouteHandler('./routes/juejin/category'));
router.get('/juejin/tag/:tag', lazyloadRouteHandler('./routes/juejin/tag'));
router.get('/juejin/trending/:category/:type', lazyloadRouteHandler('./routes/juejin/trending'));
router.get('/juejin/books', lazyloadRouteHandler('./routes/juejin/books'));
router.get('/juejin/pins/:type?', lazyloadRouteHandler('./routes/juejin/pins'));
router.get('/juejin/posts/:id', lazyloadRouteHandler('./routes/juejin/posts'));
router.get('/juejin/collections/:userId', lazyloadRouteHandler('./routes/juejin/favorites'));
router.get('/juejin/collection/:collectionId', lazyloadRouteHandler('./routes/juejin/collection'));
router.get('/juejin/shares/:userId', lazyloadRouteHandler('./routes/juejin/shares'));
router.get('/juejin/column/:id', lazyloadRouteHandler('./routes/juejin/column'));
// 掘金 migrated to v2
// router.get('/juejin/category/:category', lazyloadRouteHandler('./routes/juejin/category'));
// router.get('/juejin/tag/:tag', lazyloadRouteHandler('./routes/juejin/tag'));
// router.get('/juejin/trending/:category/:type', lazyloadRouteHandler('./routes/juejin/trending'));
// router.get('/juejin/books', lazyloadRouteHandler('./routes/juejin/books'));
// router.get('/juejin/pins/:type?', lazyloadRouteHandler('./routes/juejin/pins'));
// router.get('/juejin/posts/:id', lazyloadRouteHandler('./routes/juejin/posts'));
// router.get('/juejin/news/:id', lazyloadRouteHandler('./routes/juejin/news'));
// router.get('/juejin/collections/:userId', lazyloadRouteHandler('./routes/juejin/favorites'));
// router.get('/juejin/collection/:collectionId', lazyloadRouteHandler('./routes/juejin/collection'));
// router.get('/juejin/shares/:userId', lazyloadRouteHandler('./routes/juejin/shares'));
// router.get('/juejin/column/:id', lazyloadRouteHandler('./routes/juejin/column'));
// 自如
router.get('/ziroom/room/:city/:iswhole/:room/:keyword', lazyloadRouteHandler('./routes/ziroom/room'));

View File

@ -1,4 +1,5 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const response = await got({
@ -17,7 +18,7 @@ module.exports = async (ctx) => {
${base_info.summary}<br>
<strong>价格:</strong> ${base_info.price / 100}
`,
pubDate: new Date(base_info.ctime * 1000).toUTCString(),
pubDate: parseDate(base_info.ctime * 1000),
guid: base_info.booklet_id,
}));

View File

@ -26,7 +26,7 @@ module.exports = async (ctx) => {
let originalData = [];
if (response.data.data) {
originalData = response.data.data.slice(0, 5);
originalData = response.data.data;
}
const resultItems = await util.ProcessFeed(originalData, ctx.cache);

View File

@ -0,0 +1,13 @@
module.exports = {
'/books': ['xyqfer'],
'/category/:category': ['DIYgod'],
'/collection/:collectionId': ['isQ'],
'/collections/:userId': ['isQ'],
'/column/:id': ['xiangzy1'],
'/news/:id': ['CancerGary'],
'/pins/:type?': ['xyqfer', 'laampui'],
'/posts/:id': ['Maecenas'],
'/shares/:userId': ['qiwihui'],
'/tag/:tag': ['isheng5'],
'/trending/:category/:type': ['moaix'],
};

25
lib/v2/juejin/news.js Normal file
View File

@ -0,0 +1,25 @@
const got = require('@/utils/got');
const util = require('./utils');
module.exports = async (ctx) => {
const { id } = ctx.params;
const response = await got({
method: 'post',
url: 'https://api.juejin.cn/content_api/v1/news/list_user',
json: {
user_id: id,
sort_type: 2,
},
});
const { data } = response.data;
const username = data[0] && data[0].author_user_info && data[0].author_user_info.user_name;
const resultItems = await util.ProcessFeed(data, ctx.cache);
ctx.state.data = {
title: `掘金资讯-${username}`,
link: `https://juejin.cn/user/${id}/news`,
description: `掘金资讯-${username}`,
item: resultItems,
};
};

View File

@ -1,7 +1,8 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const type = ctx.params.type || 'recommend';
const type = ctx.params.type ?? 'recommend';
const title = {
recommend: '推荐',
hot: '热门',
@ -35,7 +36,7 @@ module.exports = async (ctx) => {
const title = content;
const guid = item.msg_id;
const link = `https://juejin.cn/pin/${guid}`;
const pubDate = new Date(parseInt(item.msg_Info.ctime) * 1000).toUTCString();
const pubDate = parseDate(item.msg_Info.ctime * 1000);
const author = item.author_user_info.user_name;
const imgs = item.msg_Info.pic_list.reduce((imgs, item) => {
imgs += `

61
lib/v2/juejin/radar.js Normal file
View File

@ -0,0 +1,61 @@
module.exports = {
'juejin.cn': {
_name: '掘金',
'.': [
{
title: '标签',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-biao-qian',
source: '/tag/:tag',
target: '/juejin/tag/:tag',
},
{
title: '小册',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-xiao-ce',
source: '/books',
target: '/juejin/books',
},
{
title: '沸点',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-fei-dian',
source: ['/pins/:type', '/pins/topic/:type'],
target: (params) => (params.type !== 'recommended' ? '/juejin/pins/:type' : '/juejin/pins'),
},
{
title: '用户专栏',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-zhuan-lan',
source: ['/user/:id', '/user/:id/posts'],
target: '/juejin/posts/:id',
},
{
title: '收藏集',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-shou-cang-ji',
source: ['/user/:id', '/user/:id/collections'],
target: '/juejin/collections/:id',
},
{
title: '单个收藏夹',
docs: 'https://docs.rsshub.app/programming.html#jue-jin-dan-ge-shou-cang-jia',
source: '/collection/:collectionId',
target: '/juejin/collection/:collectionId',
},
{
title: '分享',
docs: 'https://docs.rsshub.app/programming.html#jue-jin',
source: ['/user/:userId', '/user/:userId/shares'],
target: '/juejin/shares/:userId',
},
{
title: '专栏',
docs: 'https://docs.rsshub.app/programming.html#jue-jin',
source: '/column/:id',
target: '/juejin/column/:id',
},
{
title: '资讯',
docs: 'https://docs.rsshub.app/programming.html#jue-jin',
source: ['/user/:id', '/user/:id/news'],
target: '/juejin/news/:userId',
},
],
},
};

13
lib/v2/juejin/router.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = (router) => {
router.get('/books', require('./books'));
router.get('/category/:category', require('./category'));
router.get('/collection/:collectionId', require('./collection'));
router.get('/collections/:userId', require('./favorites'));
router.get('/column/:id', require('./column'));
router.get('/news/:id', require('./news'));
router.get('/pins/:type?', require('./pins'));
router.get('/posts/:id', require('./posts'));
router.get('/shares/:userId', require('./shares'));
router.get('/tag/:tag', require('./tag'));
router.get('/trending/:category/:type', require('./trending'));
};

View File

@ -1,6 +1,6 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const timezone = require('@/utils/timezone');
const md = require('markdown-it')({
html: true,
});
@ -15,21 +15,29 @@ async function load(id) {
});
let description;
if (response.data.data) {
description = md.render(response.data.data.article_info.mark_content);
description = md.render(response.data.data.article_info.mark_content) || response.data.data.article_info.content;
}
return { description };
}
const loadNews = async (link) => {
const response = await got(link);
const $ = cheerio.load(response.data);
$('h1.title, .main-box .message').remove();
return { description: $('.main-box .article').html() };
};
const ProcessFeed = (list, caches) =>
Promise.all(
list.map(async (item) => {
const pubDate = timezone(parseDate(parseInt(item.article_info.ctime) * 1000), +8);
const link = `https://juejin.cn/post/${item.article_id}`;
const isArticle = !!item.article_info;
const pubDate = parseDate((isArticle ? item.article_info.ctime : item.content_info.ctime) * 1000);
const link = `https://juejin.cn${isArticle ? '/post/' + item.article_id : '/news/' + item.content_id}`;
// 列表上提取到的信息
const single = {
title: item.article_info.title,
description: (item.article_info.brief_content || '无描述').replace(/[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]/g, ''),
title: isArticle ? item.article_info.title : item.content_info.title,
description: ((isArticle ? item.article_info.brief_content : item.content_info.brief) || '无描述').replace(/[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]/g, ''),
pubDate,
author: item.author_user_info.user_name,
link,
@ -37,7 +45,7 @@ const ProcessFeed = (list, caches) =>
// 使用tryGet方法从缓存获取内容。
// 当缓存中无法获取到链接内容的时候则使用load方法加载文章内容。
const other = await caches.tryGet(link, () => load(item.article_id));
const other = await caches.tryGet(link, () => (isArticle ? load(item.article_id) : loadNews(link)));
// 合并解析后的结果集作为该篇文章最终的输出结果
return { ...single, ...other };
})