diff --git a/docs/en/new-media.md b/docs/en/new-media.md
index 97dfca98f8..bce3147394 100644
--- a/docs/en/new-media.md
+++ b/docs/en/new-media.md
@@ -13,7 +13,7 @@ pageClass: routes
Supported sub-sites:
| 9To5Mac | 9To5Google | 9To5Toys |
| ------- | ---------- | -------- |
-| Mac | Google | Toys |
+| Mac | Google | Toys |
@@ -152,6 +152,32 @@ Compared to the official one, this feed:
+## Soomal
+
+### 话题
+
+
+
+- Available languages:
+
+| Simplified Chinese | Traditional Chinese | English |
+| ------------------ | ------------------- | ------- |
+| zh | zh_tw | en |
+
+- Available topics by locale:
+
+| Languages | | | | | |
+| ------------------- | -------- | ----- | -------- | -------- | -------- |
+| Simplified Chinese | 最新文章 | 科普 | 测评报告 | 发烧入门 | 摄影入门 | 古典音乐入门 |
+| Traditional Chinese | 最新文章 | 科普 | 測評報告 | 發燒入門 | 攝影入門 | 古典音樂入門 |
+| English | Phone | Audio | Album | Review |
+
+- Soomal offers official RSS subscriptions
+ - Soomal website:[http://www.soomal.com/doc/101.rss.xml](http://www.soomal.com/doc/101.rss.xml)
+ - Soomal forum and comments:[http://www.soomal.com/bbs/101.rss.xml](http://www.soomal.com/bbs/101.rss.xml)
+
+
+
## The Verge
diff --git a/docs/new-media.md b/docs/new-media.md
index 59dbc377f8..617a2c23fd 100644
--- a/docs/new-media.md
+++ b/docs/new-media.md
@@ -316,6 +316,32 @@ pageClass: routes
+## Soomal
+
+### 话题
+
+
+
+- 可选语言:
+
+| 简体中文 | 正体中文 | 英语 |
+| -------- | -------- | ---- |
+| zh | zh_tw | en |
+
+- 可选话题(按语言分类):
+
+| 语言 | | | | | | |
+| -------- | -------- | ----- | -------- | -------- | -------- | ------------ |
+| 简体中文 | 最新文章 | 科普 | 测评报告 | 发烧入门 | 摄影入门 | 古典音乐入门 |
+| 正体中文 | 最新文章 | 科普 | 測評報告 | 發燒入門 | 攝影入門 | 古典音樂入門 |
+| 英语 | Phone | Audio | Album | Review | | |
+
+- Soomal 提供官方 RSS 订阅
+ - Soomal 网站更新:
+ - Soomal 论坛与留言系统的更新:
+
+
+
## The Verge
### The Verge
diff --git a/lib/router.js b/lib/router.js
index 8384e717f3..1485669e45 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -3088,6 +3088,9 @@ router.get('/souyun/today', require('./routes/souyun/today'));
// 生物谷
router.get('/bioon/latest', require('./routes/bioon/latest'));
+// soomal
+router.get('/soomal/topics/:category/:language?', require('./routes/soomal/topics'));
+
// NASA
router.get('/nasa/apod', require('./routes/nasa/apod'));
router.get('/nasa/apod-ncku', require('./routes/nasa/apod-ncku'));
diff --git a/lib/routes/soomal/topics.js b/lib/routes/soomal/topics.js
new file mode 100644
index 0000000000..48785583c7
--- /dev/null
+++ b/lib/routes/soomal/topics.js
@@ -0,0 +1,130 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const url = require('url');
+const iconv = require('iconv-lite');
+
+module.exports = async (ctx) => {
+ const category = ctx.params.category;
+ const lang = ctx.params.language;
+
+ let index_url;
+ let character_set;
+ switch (lang) {
+ case 'zh_tw':
+ index_url = 'http://www.soomal.com/doc/index201000_0001_00.htm';
+ character_set = 'big5';
+ break;
+ case 'en':
+ index_url = 'http://eng.soomal.com/edoc/index101000_0001_00.htm';
+ character_set = 'ISO-8859-5';
+ break;
+ default:
+ index_url = 'http://www.soomal.com/doc/index101000_0001_00.htm';
+ character_set = 'gbk';
+ break;
+ }
+
+ let category_list;
+ let url_list;
+
+ const category_url = await ctx.cache.tryGet([lang, category], async () => {
+ const front_page = await got.get(index_url, {
+ responseType: 'buffer',
+ });
+
+ const front_data = iconv.decode(front_page.data, character_set);
+
+ const $ = cheerio.load(front_data);
+ if (lang !== 'en') {
+ const container_list = $('div.container.cf').slice(0, 2);
+ category_list = container_list
+ .find('ul.ul-reset')
+ .find('h3')
+ .children()
+ .get()
+ .map((item) => $(item).text());
+ url_list = container_list
+ .find('ul.ul-reset')
+ .find('h3')
+ .children()
+ .get()
+ .map((item) => $(item).attr('href'));
+ } else {
+ category_list = $('div.menu')
+ .find('a')
+ .get()
+ .map((item) => $(item).text());
+ url_list = $('div.menu')
+ .find('a')
+ .get()
+ .map((item) => $(item).attr('href'));
+ }
+
+ return url.resolve(index_url, url_list[category_list.indexOf(category)]);
+ });
+
+ const response = await got.get(category_url, {
+ responseType: 'buffer',
+ });
+
+ const data = iconv.decode(response.data, character_set);
+ const $ = cheerio.load(data);
+
+ const list = $('div.item')
+ .find('div.title')
+ .children('a')
+ .get()
+ .map((item) => url.resolve(category_url, $(item).attr('href')));
+
+ const out = await Promise.all(
+ list.map(async (link) => {
+ const [title, author, pubDate, description] = await ctx.cache.tryGet(link, async () => {
+ const response = await got.get(link, {
+ responseType: 'buffer',
+ });
+
+ const data = iconv.decode(response.data, character_set);
+ const $ = cheerio.load(data);
+
+ let [title, author, pubDate, description] = '';
+ if (lang !== 'en') {
+ title = $('div.titlebox').find('div.title').text();
+ author = $('div.titlebox').find('div.info').children('a').text();
+ pubDate = Date.parse(
+ $('div.titlebox')
+ .find('div.info')
+ .text()
+ .match(/\d{4}([.\-/ ])\d{2}\1\d{2}\s\d{2}[:]\d{2}[:]\d{2}/)[0]
+ );
+
+ $('div.toolbar,div.grade').remove();
+ description = $('div.Doc').html();
+ } else {
+ title = $('div.titlebox').find('div.title').text();
+ author = $('div.titlebox').find('div.infobox').children('a').text();
+ pubDate = new Date();
+
+ $('div.grade').remove();
+ description = $('div.textbox').html();
+ }
+
+ return [title, author, pubDate, description];
+ });
+
+ const item = {
+ title: title,
+ description: description,
+ pubDate: pubDate,
+ link: link,
+ author: author,
+ };
+ return Promise.resolve(item);
+ })
+ );
+
+ ctx.state.data = {
+ title: 'Soomal.com - ' + category,
+ link: category_url,
+ item: out,
+ };
+};