diff --git a/docs/programming.md b/docs/programming.md
index eab7cfd92..1281ac4f5 100644
--- a/docs/programming.md
+++ b/docs/programming.md
@@ -497,11 +497,11 @@ GitHub 官方也提供了一些 RSS:
### 频道
-
+
### 用户
-
+
## TesterHome
diff --git a/lib/router.js b/lib/router.js
index ec22d9b7a..72f15dfb6 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -1371,10 +1371,6 @@ router.get('/leetcode/articles', lazyloadRouteHandler('./routes/leetcode/article
router.get('/leetcode/submission/us/:user', lazyloadRouteHandler('./routes/leetcode/check-us'));
router.get('/leetcode/submission/cn/:user', lazyloadRouteHandler('./routes/leetcode/check-cn'));
-// segmentfault
-router.get('/segmentfault/channel/:name', lazyloadRouteHandler('./routes/segmentfault/channel'));
-router.get('/segmentfault/user/:name', lazyloadRouteHandler('./routes/segmentfault/user'));
-
// 虎扑
router.get('/hupu/bxj/:id/:order?', lazyloadRouteHandler('./routes/hupu/bbs'));
router.get('/hupu/bbs/:id/:order?', lazyloadRouteHandler('./routes/hupu/bbs'));
diff --git a/lib/routes/segmentfault/channel.js b/lib/routes/segmentfault/channel.js
deleted file mode 100644
index 75d8ed6be..000000000
--- a/lib/routes/segmentfault/channel.js
+++ /dev/null
@@ -1,90 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const url = require('url');
-const date = require('@/utils/date');
-
-const host = 'https://segmentfault.com';
-
-module.exports = async (ctx) => {
- const name = ctx.params.name;
-
- const link = `https://segmentfault.com/channel/${name}`;
- const response = await got.get(link);
- const $ = cheerio.load(response.data);
-
- const channel_name = $('div.feed-option h5').text();
-
- const list = $('div.news-item.stream__item.clearfix.mt15')
- .slice(0, 10)
- .map(function () {
- const info = {
- link: $(this).find('div.news__item-info.clearfix a').attr('href'),
- title: $(this).find('div.mb5.mt5 h4').text(),
- author: $(this).find('span.author.pr20 a').text(),
- };
- return info;
- })
- .get();
-
- const out = await Promise.all(
- list.map(async (info) => {
- const title = info.title;
- const itemUrl = url.resolve(host, info.link);
- const author = info.author;
-
- const cache = await ctx.cache.get(itemUrl);
- if (cache) {
- return Promise.resolve(JSON.parse(cache));
- }
-
- const response = await got.get(itemUrl);
- const $ = cheerio.load(response.data);
-
- let date_value = '';
- let description = '';
- if (info.link.indexOf('p') !== -1) {
- // 分享
- description =
- $('div.fmt.mb10').html().trim() +
- $('div.content h3')
- .html()
- .replace(/href="\//g, `href="${url.resolve(host, '.')}`)
- .trim();
-
- const date_list = $('div.news__item-info > p > span').text().trim().split(' ');
- date_list.shift();
- let date_v = '';
- if (date_list.length > 1) {
- date_v = date_list.join('');
- } else {
- date_v = date_list[0];
- }
- date_value = date_v.substring(0, date_v.length - 3);
- } else {
- // 文章
- description = $('article.article.fmt.article-content')
- .html()
- .replace(/data-src="/g, 'src="https://segmentfault.com')
- .trim();
- const date_v = $('div.article__authorright > span').text().trim().replace(' ', '');
- date_value = date_v.substring(0, date_v.length - 2);
- }
-
- const single = {
- title,
- link: itemUrl,
- author,
- description,
- pubDate: date(date_value, 8),
- };
- ctx.cache.set(itemUrl, JSON.stringify(single));
- return Promise.resolve(single);
- })
- );
-
- ctx.state.data = {
- title: `${channel_name}——segmentfault`,
- link,
- item: out,
- };
-};
diff --git a/lib/routes/segmentfault/user.js b/lib/routes/segmentfault/user.js
deleted file mode 100644
index c94beeac0..000000000
--- a/lib/routes/segmentfault/user.js
+++ /dev/null
@@ -1,90 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const url = require('url');
-const date = require('@/utils/date');
-
-const host = 'https://segmentfault.com';
-
-module.exports = async (ctx) => {
- const username = ctx.params.name;
-
- const link = `https://segmentfault.com/u/${username}/articles`;
- const response = await got.get(link);
- const $ = cheerio.load(response.data);
-
- const channel_name = $('h2.profile__heading--name').text().trim().split(' ')[0];
-
- const list = $('ul.profile-mine__content li')
- .slice(0, 10)
- .map(function () {
- const info = {
- link: $(this).find('a.profile-mine__content--title').attr('href'),
- title: $(this).find('a.profile-mine__content--title').text(),
- author: channel_name,
- };
- return info;
- })
- .get();
-
- const out = await Promise.all(
- list.map(async (info) => {
- const title = info.title;
- const itemUrl = url.resolve(host, info.link);
- const author = info.author;
-
- const cache = await ctx.cache.get(itemUrl);
- if (cache) {
- return Promise.resolve(JSON.parse(cache));
- }
-
- const response = await got.get(itemUrl);
- const $ = cheerio.load(response.data);
-
- let date_value = '';
- let description = '';
- if (info.link.indexOf('p') !== -1) {
- // 分享
- description =
- $('div.fmt.mb10').html().trim() +
- $('div.content h3')
- .html()
- .replace(/href="\//g, `href="${url.resolve(host, '.')}`)
- .trim();
-
- const date_list = $('div.news__item-info > p > span').text().trim().split(' ');
- date_list.shift();
- let date_v = '';
- if (date_list.length > 1) {
- date_v = date_list.join('');
- } else {
- date_v = date_list[0];
- }
- date_value = date_v.substring(0, date_v.length - 3);
- } else {
- // 文章
- description = $('article.article.fmt.article-content')
- .html()
- .replace(/data-src="/g, 'src="https://segmentfault.com')
- .trim();
- const date_v = $('div.article__authorright > span').text().trim().replace(' ', '');
- date_value = date_v.substring(0, date_v.length - 2);
- }
-
- const single = {
- title,
- link: itemUrl,
- author,
- description,
- pubDate: date(date_value, 8),
- };
- ctx.cache.set(itemUrl, JSON.stringify(single));
- return Promise.resolve(single);
- })
- );
-
- ctx.state.data = {
- title: `${channel_name}-segmentfault`,
- link,
- item: out,
- };
-};
diff --git a/lib/v2/segmentfault/channel.js b/lib/v2/segmentfault/channel.js
new file mode 100644
index 000000000..de62204f1
--- /dev/null
+++ b/lib/v2/segmentfault/channel.js
@@ -0,0 +1,46 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+const host = 'https://segmentfault.com';
+
+module.exports = async (ctx) => {
+ const name = ctx.params.name;
+
+ const link = `${host}/channel/${name}`;
+ const response = await got(link);
+ const $ = cheerio.load(response.data);
+
+ const channel_name = $('#leftNav > a.active').text();
+
+ const list = $('ul.bg-transparent.list-group.list-group-flush > li')
+ .slice(0, 10)
+ .map((_, item) => ({
+ link: new URL($(item).find('div.content > h5 > a').attr('href'), host).href,
+ title: $(item).find('div.content > h5 > a').text(),
+ author: $(item).find('span.name').text(),
+ }))
+ .get();
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got(item.link);
+ const content = cheerio.load(detailResponse.data);
+
+ item.description = content('article')
+ .html()
+ .replace(/data-src="/g, `src="${host}`);
+ item.pubDate = parseDate(content('time').attr('datetime'));
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: `segmentfault - ${channel_name}`,
+ link,
+ item: items,
+ };
+};
diff --git a/lib/v2/segmentfault/maintainer.js b/lib/v2/segmentfault/maintainer.js
new file mode 100644
index 000000000..264c59f75
--- /dev/null
+++ b/lib/v2/segmentfault/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/channel/:name': ['LogicJake', 'Fatpandac'],
+ '/user/:name': ['leyuuu', 'Fatpandac'],
+};
diff --git a/lib/v2/segmentfault/radar.js b/lib/v2/segmentfault/radar.js
new file mode 100644
index 000000000..5c95675b4
--- /dev/null
+++ b/lib/v2/segmentfault/radar.js
@@ -0,0 +1,19 @@
+module.exports = {
+ 'segmentfault.com': {
+ _name: 'SegmentFault',
+ '.': [
+ {
+ title: '频道',
+ docs: 'https://docs.rsshub.app/programming.html#segmentfault',
+ source: ['/channel/:name'],
+ target: '/channel/:name',
+ },
+ {
+ title: '用户',
+ docs: 'https://docs.rsshub.app/programming.html#segmentfault',
+ source: ['/u/:name'],
+ target: '/user/:name',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/segmentfault/router.js b/lib/v2/segmentfault/router.js
new file mode 100644
index 000000000..3990afd99
--- /dev/null
+++ b/lib/v2/segmentfault/router.js
@@ -0,0 +1,4 @@
+module.exports = function (router) {
+ router.get('/channel/:name', require('./channel'));
+ router.get('/user/:name', require('./user'));
+};
diff --git a/lib/v2/segmentfault/user.js b/lib/v2/segmentfault/user.js
new file mode 100644
index 000000000..98fa8b9f0
--- /dev/null
+++ b/lib/v2/segmentfault/user.js
@@ -0,0 +1,43 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+const host = 'https://segmentfault.com';
+
+module.exports = async (ctx) => {
+ const name = ctx.params.name;
+ const apiURL = `https://segmentfault.com/gateway/homepage/${name}/timeline?size=20&offset=`;
+
+ const response = await got(apiURL);
+ const data = response.data.rows;
+
+ const author = data[0].user.name;
+ const list = data.map((item) => ({
+ title: item.title,
+ description: item.excerpt,
+ link: new URL(item.url, host).href,
+ author,
+ }));
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ const content = cheerio.load(response.data);
+
+ item.description = content('article')
+ .html()
+ .replace(/data-src="/g, `src="${host}`);
+ item.pubDate = parseDate(content('time').attr('datetime'));
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: `segmentfault - ${author}`,
+ link: `${host}/u/${name}`,
+ item: items,
+ };
+};