diff --git a/docs/new-media.md b/docs/new-media.md
index 9091ea25ae..a7b30a0e44 100644
--- a/docs/new-media.md
+++ b/docs/new-media.md
@@ -994,6 +994,34 @@ IPFS 网关有可能失效,那时候换成其他网关。
+## PeoPo 公民新聞
+
+### 新聞分類
+
+
+
+| 分類 | ID |
+| ---- | --- |
+| 社會關懷 | 159 |
+| 生態環保 | 113 |
+| 文化古蹟 | 143 |
+| 社區改造 | 160 |
+| 教育學習 | 161 |
+| 農業 | 163 |
+| 生活休閒 | 162 |
+| 媒體觀察 | 164 |
+| 運動科技 | 165 |
+| 政治經濟 | 166 |
+| 北台灣 | 223 |
+| 中台灣 | 224 |
+| 南台灣 | 225 |
+| 東台灣 | 226 |
+| 校園中心 | 167 |
+| 原住民族 | 227 |
+| 天然災害 | 168 |
+
+
+
## PMCAFF
### 今日推荐 / 精选
diff --git a/lib/v2/peopo/maintainer.js b/lib/v2/peopo/maintainer.js
new file mode 100644
index 0000000000..ca7bdb6180
--- /dev/null
+++ b/lib/v2/peopo/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/topic/:topic?': ['TonyRL'],
+};
diff --git a/lib/v2/peopo/radar.js b/lib/v2/peopo/radar.js
new file mode 100644
index 0000000000..239c3665ae
--- /dev/null
+++ b/lib/v2/peopo/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'peopo.org': {
+ _name: 'PeoPo 公民新聞',
+ '.': [
+ {
+ title: '新聞分類',
+ docs: 'https://docs.rsshub.app/new-media.html#peopo-gong-min-xin-wen',
+ source: '/topic/:topicId',
+ target: '/peopo/topic/:topicId',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/peopo/router.js b/lib/v2/peopo/router.js
new file mode 100644
index 0000000000..3871a1cf45
--- /dev/null
+++ b/lib/v2/peopo/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/topic/:topicId?', require('./topic'));
+};
diff --git a/lib/v2/peopo/topic.js b/lib/v2/peopo/topic.js
new file mode 100644
index 0000000000..9bc17ae0df
--- /dev/null
+++ b/lib/v2/peopo/topic.js
@@ -0,0 +1,45 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const timezone = require('@/utils/timezone');
+const baseUrl = 'https://www.peopo.org';
+
+module.exports = async (ctx) => {
+ const { topicId = '159' } = ctx.params;
+ const url = `${baseUrl}/topic/${topicId}`;
+ const response = await got(url);
+ const $ = cheerio.load(response.data);
+ const list = $('.view-list-title')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.find('a').text(),
+ link: new URL(item.find('a').attr('href'), baseUrl).href,
+ };
+ });
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ const $ = cheerio.load(response.data);
+
+ item.author = $('#user-info h3').text();
+ item.category = $('#node-terms .inline li')
+ .toArray()
+ .map((item) => $(item).find('a').text());
+ item.pubDate = timezone(parseDate($('.submitted span').text()), +8);
+ item.description = ($('.field-name-field-video-id .field-items').text() ? $('.field-name-field-video-id input').attr('value') : '') + $('.post_text_s .field-items').html();
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: $('head title').text(),
+ link: url,
+ item: items,
+ };
+};