diff --git a/docs/en/finance.md b/docs/en/finance.md
index 2ad64dbac..ad0321b7a 100644
--- a/docs/en/finance.md
+++ b/docs/en/finance.md
@@ -53,6 +53,12 @@ pageClass: routes
+## Futubull
+
+### Headlines
+
+
+
## FX Markets
### Channel
diff --git a/docs/finance.md b/docs/finance.md
index 159313757..ea835c1af 100644
--- a/docs/finance.md
+++ b/docs/finance.md
@@ -276,7 +276,7 @@ TokenInsight 官方亦有提供 RSS,可参考
+
## 格隆汇
diff --git a/lib/router.js b/lib/router.js
index bac7a8344..e497f3329 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -2882,7 +2882,7 @@ router.get('/gov/chinatax/latest', lazyloadRouteHandler('./routes/gov/chinatax/l
router.get('/lizhi/user/:id', lazyloadRouteHandler('./routes/lizhi/user'));
// 富途牛牛
-router.get('/futunn/highlights', lazyloadRouteHandler('./routes/futunn/highlights'));
+// router.get('/futunn/highlights', lazyloadRouteHandler('./routes/futunn/highlights'));
// 即刻 migrated to v2
// router.get('/jike/topic/:id', lazyloadRouteHandler('./routes/jike/topic'));
diff --git a/lib/routes/futunn/highlights.js b/lib/routes/futunn/highlights.js
deleted file mode 100644
index e2d94f0c1..000000000
--- a/lib/routes/futunn/highlights.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const timezone = require('@/utils/timezone');
-const { parseDate } = require('@/utils/parse-date');
-
-module.exports = async (ctx) => {
- const link = `https://news.futunn.com/main`;
-
- const response = await got.get(link);
- const $ = cheerio.load(response.data);
-
- const contents = $('script:not([src]):contains("window._params")');
- const params = contents.html().trim().slice(17, -1);
-
- const newslist = JSON.parse(params).preList;
-
- const out = await Promise.all(
- newslist
- .filter((info) => {
- const url = new URL(info.url);
- if (url.hostname !== 'news.futunn.com') {
- // ignore videos.futunn.com
- return false;
- } else if (url.searchParams.get('src') !== '3') {
- // ignore src=12 redirecting to mp.weixin.qq.com
- return false;
- }
- return true;
- })
- .map((info) =>
- ctx.cache.tryGet(info.url, async () => {
- const response = await got({
- url: info.url,
- method: 'get',
- headers: {
- Referer: link,
- },
- });
-
- const $ = cheerio.load(response.data);
- const description = $('div.newsDetailBox div.main div.inner').html();
- const date = $('div.timeBar').children().remove().end().text().trim();
-
- info.link = info.url;
- info.description = description;
- info.pubDate = timezone(parseDate(date, 'YYYY/MM/DD HH:mm'), +8);
-
- return info;
- })
- )
- );
-
- ctx.state.data = {
- title: `富途牛牛要闻`,
- link,
- item: out,
- };
-};
diff --git a/lib/v2/futunn/main.js b/lib/v2/futunn/main.js
new file mode 100644
index 000000000..0f5027cfc
--- /dev/null
+++ b/lib/v2/futunn/main.js
@@ -0,0 +1,67 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 50;
+
+ const rootUrl = 'https://news.futunn.com';
+ const currentUrl = `${rootUrl}/main`;
+ const apiUrl = `${rootUrl}/news-site-api/main/get-market-list?size=${limit}`;
+
+ const response = await got({
+ method: 'get',
+ url: apiUrl,
+ });
+
+ let items = response.data.data.list.map((item) => ({
+ title: item.title,
+ link: item.url.split('?')[0],
+ author: item.source,
+ pubDate: parseDate(item.timestamp * 1000),
+ description: art(path.join(__dirname, 'templates/description.art'), {
+ abs: item.abs,
+ pic: item.pic,
+ }),
+ }));
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ if (/news\.futunn\.com/.test(item.link)) {
+ const detailResponse = await got({
+ method: 'get',
+ url: item.link,
+ });
+
+ const content = cheerio.load(detailResponse.data);
+
+ content('.futu-news-time-stamp').remove();
+ content('.nnstock').each(function () {
+ content(this).replaceWith(`${content(this).text().replace(/\$/g, '')}`);
+ });
+
+ item.description = content('.origin_content').html();
+ item.category = [
+ ...content('.news__from-topic__title')
+ .toArray()
+ .map((a) => content(a).text()),
+ ...content('#relatedStockWeb .stock-name')
+ .toArray()
+ .map((s) => content(s).text().trim()),
+ ];
+ }
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: 'Futubull - Headlines',
+ link: currentUrl,
+ item: items,
+ };
+};
diff --git a/lib/v2/futunn/maintainer.js b/lib/v2/futunn/maintainer.js
new file mode 100644
index 000000000..48d0f0d04
--- /dev/null
+++ b/lib/v2/futunn/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/main': ['Wsine', 'nczitzk'],
+};
diff --git a/lib/v2/futunn/radar.js b/lib/v2/futunn/radar.js
new file mode 100644
index 000000000..b8602d715
--- /dev/null
+++ b/lib/v2/futunn/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'futunn.com': {
+ _name: '富途牛牛',
+ news: [
+ {
+ title: '要闻',
+ docs: 'https://docs.rsshub.app/finance.html#fu-tu-niu-niu',
+ source: ['/main', '/'],
+ target: '/futunn/main',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/futunn/router.js b/lib/v2/futunn/router.js
new file mode 100644
index 000000000..dd338c277
--- /dev/null
+++ b/lib/v2/futunn/router.js
@@ -0,0 +1,5 @@
+module.exports = function (router) {
+ router.get('/highlights', require('./main'));
+ router.get('/main', require('./main'));
+ router.get('/', require('./main'));
+};
diff --git a/lib/v2/futunn/templates/description.art b/lib/v2/futunn/templates/description.art
new file mode 100644
index 000000000..c9cfd7c14
--- /dev/null
+++ b/lib/v2/futunn/templates/description.art
@@ -0,0 +1,6 @@
+{{ if pic }}
+
+{{ /if }}
+{{ if abs }}
+
{{ abs }}
+{{ /if }}
\ No newline at end of file