From 933558521062191e854c45a25bc3cbc11350fcc7 Mon Sep 17 00:00:00 2001
From: Lei Zhang
Date: Tue, 29 Aug 2023 20:54:06 +0800
Subject: [PATCH] fix: V2EX Route Exclude Duplicate Posts (#13144)
* update: V2EX Route Exclude Duplicate Posts
* use ctx.query.limit
* refactor: migrate to v2
---------
Co-authored-by: pull[bot] <39814207+pull[bot]@users.noreply.github.com>
---
lib/router.js | 6 ++---
lib/routes/v2ex/post.js | 39 -------------------------------
lib/v2/v2ex/maintainer.js | 5 ++++
lib/v2/v2ex/post.js | 35 +++++++++++++++++++++++++++
lib/v2/v2ex/radar.js | 35 +++++++++++++++++++++++++++
lib/v2/v2ex/router.js | 5 ++++
lib/{routes => v2}/v2ex/tab.js | 38 ++++++++++++++----------------
lib/{routes => v2}/v2ex/topics.js | 2 +-
8 files changed, 101 insertions(+), 64 deletions(-)
delete mode 100644 lib/routes/v2ex/post.js
create mode 100644 lib/v2/v2ex/maintainer.js
create mode 100644 lib/v2/v2ex/post.js
create mode 100644 lib/v2/v2ex/radar.js
create mode 100644 lib/v2/v2ex/router.js
rename lib/{routes => v2}/v2ex/tab.js (62%)
rename lib/{routes => v2}/v2ex/topics.js (96%)
diff --git a/lib/router.js b/lib/router.js
index 8f859a2b69d801..e61ec26abce1bc 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -77,9 +77,9 @@ router.get('/huya/live/:id', lazyloadRouteHandler('./routes/huya/live'));
router.get('/showroom/room/:id', lazyloadRouteHandler('./routes/showroom/room'));
// v2ex
-router.get('/v2ex/topics/:type', lazyloadRouteHandler('./routes/v2ex/topics'));
-router.get('/v2ex/post/:postid', lazyloadRouteHandler('./routes/v2ex/post'));
-router.get('/v2ex/tab/:tabid', lazyloadRouteHandler('./routes/v2ex/tab'));
+// router.get('/v2ex/topics/:type', lazyloadRouteHandler('./routes/v2ex/topics'));
+// router.get('/v2ex/post/:postid', lazyloadRouteHandler('./routes/v2ex/post'));
+// router.get('/v2ex/tab/:tabid', lazyloadRouteHandler('./routes/v2ex/tab'));
// f-droid
router.get('/fdroid/apprelease/:app', lazyloadRouteHandler('./routes/fdroid/apprelease'));
diff --git a/lib/routes/v2ex/post.js b/lib/routes/v2ex/post.js
deleted file mode 100644
index 5b4d26c4d723af..00000000000000
--- a/lib/routes/v2ex/post.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const { parseDate } = require('@/utils/parse-date');
-
-module.exports = async (ctx) => {
- const postid = ctx.params.postid;
- const pageUrl = `https://www.v2ex.com/t/${postid}`;
-
- const response = await got({
- method: 'get',
- url: pageUrl,
- });
-
- const $ = cheerio.load(response.data);
- const list = $('[id^="r_"]').get();
-
- ctx.state.data = {
- title: `V2EX-${$('.header h1').text()}`,
- link: pageUrl,
- description: $('.topic_content').text(),
- item: list
- .map((item) => {
- const post = $(item);
- const reply_content = post.find('.reply_content').first();
- const no = post.find('.no').first();
-
- return {
- title: `#${no.text()} ${reply_content.text()}`,
- description: reply_content.html(),
- guid: post.attr('id'),
- link: `${pageUrl}#${post.attr('id')}`,
- author: post.find('.dark').first().text(),
- pubDate: parseDate(post.find('.ago').attr('title')),
- };
- })
- .reverse(),
- allowEmpty: true,
- };
-};
diff --git a/lib/v2/v2ex/maintainer.js b/lib/v2/v2ex/maintainer.js
new file mode 100644
index 00000000000000..01c685653ac57d
--- /dev/null
+++ b/lib/v2/v2ex/maintainer.js
@@ -0,0 +1,5 @@
+module.exports = {
+ '/post/:postid': ['kt286'],
+ '/tab/:tabid': ['liyefox'],
+ '/topics/:type': ['WhiteWorld'],
+};
diff --git a/lib/v2/v2ex/post.js b/lib/v2/v2ex/post.js
new file mode 100644
index 00000000000000..b933692cfbf113
--- /dev/null
+++ b/lib/v2/v2ex/post.js
@@ -0,0 +1,35 @@
+const got = require('@/utils/got');
+const { parseDate } = require('@/utils/parse-date');
+
+module.exports = async (ctx) => {
+ const { postid } = ctx.params;
+ const pageUrl = `https://www.v2ex.com/t/${postid}`;
+
+ const { data: topicResponse } = await got('https://www.v2ex.com/api/topics/show.json', {
+ searchParams: {
+ id: postid,
+ },
+ });
+
+ const { data: replies } = await got('https://www.v2ex.com/api/replies/show.json', {
+ searchParams: {
+ topic_id: postid,
+ },
+ });
+
+ const topic = topicResponse[0];
+
+ ctx.state.data = {
+ title: `V2EX-${topic.title}`,
+ link: pageUrl,
+ description: topic.content,
+ item: replies.map((item, index) => ({
+ title: `#${index + 1} ${item.content}`,
+ description: item.content_rendered,
+ link: `${pageUrl}#r_${item.id}`,
+ author: item.member.username,
+ pubDate: parseDate(item.created, 'X'),
+ })),
+ allowEmpty: true,
+ };
+};
diff --git a/lib/v2/v2ex/radar.js b/lib/v2/v2ex/radar.js
new file mode 100644
index 00000000000000..f3a9d43bbfcdf6
--- /dev/null
+++ b/lib/v2/v2ex/radar.js
@@ -0,0 +1,35 @@
+module.exports = {
+ 'v2ex.com': {
+ _name: 'V2EX',
+ '.': [
+ {
+ title: '最热 / 最新主题',
+ docs: 'https://docs.rsshub.app/routes/v2ex',
+ source: ['/'],
+ target: (_, url) => {
+ const { searchParams } = new URL(url);
+ if (searchParams.get('tab') === 'all' || searchParams.get('tab') === 'hot') {
+ return `/v2ex/topics/${searchParams.get('tab')?.replace('all', 'latest')}`;
+ }
+ },
+ },
+ {
+ title: '帖子',
+ docs: 'https://docs.rsshub.app/routes/v2ex',
+ source: ['/t/:postid'],
+ target: '/v2ex/post/:postid',
+ },
+ {
+ title: '标签',
+ docs: 'https://docs.rsshub.app/routes/v2ex',
+ source: ['/'],
+ target: (_, url) => {
+ const { searchParams } = new URL(url);
+ if (searchParams.get('tab') && searchParams.get('tab') !== 'all' && searchParams.get('tab') !== 'hot') {
+ return `/v2ex/tab/${searchParams.get('tab')}`;
+ }
+ },
+ },
+ ],
+ },
+};
diff --git a/lib/v2/v2ex/router.js b/lib/v2/v2ex/router.js
new file mode 100644
index 00000000000000..c17828c761aec2
--- /dev/null
+++ b/lib/v2/v2ex/router.js
@@ -0,0 +1,5 @@
+module.exports = (router) => {
+ router.get('/post/:postid', require('./post'));
+ router.get('/tab/:tabid', require('./tab'));
+ router.get('/topics/:type', require('./topics'));
+};
diff --git a/lib/routes/v2ex/tab.js b/lib/v2/v2ex/tab.js
similarity index 62%
rename from lib/routes/v2ex/tab.js
rename to lib/v2/v2ex/tab.js
index 140eca922d4750..71e141dc32eabe 100644
--- a/lib/routes/v2ex/tab.js
+++ b/lib/v2/v2ex/tab.js
@@ -13,24 +13,21 @@ module.exports = async (ctx) => {
const $ = cheerio.load(response.data);
const links = $('span.item_title > a')
- .map((i, link) => `${host}${$(link).attr('href')}`)
- .slice(0, 10)
- .get();
+ .toArray()
+ .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 10)
+ .map((link) => `${host}${$(link).attr('href').replace(/#.*$/, '')}`);
+
const items = await Promise.all(
- links.map(async (pageUrl) => {
- const cacheKey = `v2ex-${pageUrl}`;
- const cacheValue = await ctx.cache.get(cacheKey);
- let post = {};
- if (cacheValue) {
- post = cacheValue;
- } else {
+ links.map((link) =>
+ ctx.cache.tryGet(`v2ex-${link}`, async () => {
const response = await got({
method: 'get',
- url: pageUrl,
+ url: link,
});
+
const $ = cheerio.load(response.data);
- const list = $('[id^="r_"]').get();
- const reply_content = list
+ const list = $('[id^="r_"]').toArray();
+ const replyContent = list
.map((item) => {
const post = $(item);
const content = post.find('.reply_content').html();
@@ -39,18 +36,17 @@ module.exports = async (ctx) => {
return `#${no}: ${author}
${content}
`;
})
.join('');
- post = {
+
+ return {
title: $('.header h1').text(),
- link: pageUrl,
- guid: pageUrl,
- description: $('div.topic_content').html() + `${reply_content}
`,
+ link,
+ description: `${$('div.topic_content').html()}${replyContent}
`,
author: $('div.header > small > a').text(),
};
- ctx.cache.set(cacheKey, post);
- }
- return Promise.resolve(post);
- })
+ })
+ )
);
+
ctx.state.data = {
title: `V2EX-${tabid}`,
link: pageUrl,
diff --git a/lib/routes/v2ex/topics.js b/lib/v2/v2ex/topics.js
similarity index 96%
rename from lib/routes/v2ex/topics.js
rename to lib/v2/v2ex/topics.js
index db3097d9c15531..28062030b8d85f 100644
--- a/lib/routes/v2ex/topics.js
+++ b/lib/v2/v2ex/topics.js
@@ -2,7 +2,7 @@ const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
- const type = ctx.params.type;
+ const { type } = ctx.params;
const { data } = await got(`https://www.v2ex.com/api/topics/${type}.json`);