diff --git a/docs/en/programming.md b/docs/en/programming.md
index 224ebe8f14..4a926715c1 100644
--- a/docs/en/programming.md
+++ b/docs/en/programming.md
@@ -205,6 +205,16 @@ For instance, the `/github/topics/framework/l=php&o=desc&s=stars` route will gen
+## Gitpod
+
+### Blog
+
+
+
+### Changelog
+
+
+
## Hacker News
### Section
@@ -237,8 +247,7 @@ Subscribe to the updates (threads and submission) from a paritcular Hacker News
### Hex-Rays News
-
-
+
## Kaggle
diff --git a/docs/programming.md b/docs/programming.md
index 1281ac4f53..123294f0fa 100644
--- a/docs/programming.md
+++ b/docs/programming.md
@@ -283,6 +283,16 @@ GitHub 官方也提供了一些 RSS:
+## Gitpod
+
+### 博客
+
+
+
+### 更新日志
+
+
+
## Go 语言中文网
### 周刊
@@ -803,8 +813,7 @@ GitHub 官方也提供了一些 RSS:
### 开源项目
-
-
+
## 码农网
diff --git a/lib/v2/gitpod/blog.js b/lib/v2/gitpod/blog.js
new file mode 100644
index 0000000000..c4ccf225be
--- /dev/null
+++ b/lib/v2/gitpod/blog.js
@@ -0,0 +1,52 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { rootUrl } = require('./utils');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 10;
+ const response = await got(rootUrl + '/blog');
+ const $ = cheerio.load(response.data);
+ let items = $('div[class^="flex justify-center"]')
+ .slice(0, limit)
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.find('h2').text(),
+ link: rootUrl + item.find('a').attr('href'),
+ pubDate: parseDate(item.find('span[class^=date]').text()),
+ };
+ });
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got(item.link);
+ const content = cheerio.load(detailResponse.data);
+
+ const headerImg = content('img[class^=max-h]');
+ item.description = art(path.join(__dirname, 'templates/description.art'), {
+ img: headerImg.attr('src'),
+ alt: headerImg.attr('alt'),
+ content: content('div[class^=content-blog]').html(),
+ });
+ item.author = content('span.avatars a')
+ .toArray()
+ .map((e) => content(e).text().trim())
+ .join(', ');
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: $('title').text(),
+ link: rootUrl + '/blog',
+ description: $('meta[name="description"]').attr('content'),
+ language: 'en-US',
+ item: items,
+ };
+};
diff --git a/lib/v2/gitpod/changelog.js b/lib/v2/gitpod/changelog.js
new file mode 100644
index 0000000000..087629f74c
--- /dev/null
+++ b/lib/v2/gitpod/changelog.js
@@ -0,0 +1,35 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { rootUrl } = require('./utils');
+
+module.exports = async (ctx) => {
+ const response = await got(rootUrl + '/changelog');
+ const $ = cheerio.load(response.data);
+ const items = $('div[class^=changelog-entry]')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.find('h2').text(),
+ link: rootUrl + item.find('a').attr('href'),
+ description: item.find('div[class^=content-docs]').html(),
+ pubDate: parseDate(item.find('a[class*=mb-xx-small]').text()),
+ author: item
+ .find('span[class^=flex-shrink-0]')
+ .eq(0)
+ .find('img')
+ .toArray()
+ .map((e) => $(e).attr('alt').replace('Avatar of ', ''))
+ .join(', '),
+ };
+ });
+
+ ctx.state.data = {
+ title: $('title').text(),
+ link: rootUrl + '/changelog',
+ description: $('meta[name="description"]').attr('content'),
+ language: 'en-US',
+ item: items,
+ };
+};
diff --git a/lib/v2/gitpod/maintainer.js b/lib/v2/gitpod/maintainer.js
new file mode 100644
index 0000000000..c98fe8f809
--- /dev/null
+++ b/lib/v2/gitpod/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/blog': ['TonyRL'],
+ '/changelog': ['TonyRL'],
+};
diff --git a/lib/v2/gitpod/radar.js b/lib/v2/gitpod/radar.js
new file mode 100644
index 0000000000..bad04ac086
--- /dev/null
+++ b/lib/v2/gitpod/radar.js
@@ -0,0 +1,19 @@
+module.exports = {
+ 'gitpod.io': {
+ _name: 'Gitpod',
+ '.': [
+ {
+ title: '博客',
+ docs: 'https://docs.rsshub.app/programming.html#gitpod',
+ source: ['/blog', '/'],
+ target: '/gitpod/blog',
+ },
+ {
+ title: '更新日志',
+ docs: 'https://docs.rsshub.app/programming.html#gitpod',
+ source: ['/changelog', '/'],
+ target: '/gitpod/changelog',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/gitpod/router.js b/lib/v2/gitpod/router.js
new file mode 100644
index 0000000000..3b418da409
--- /dev/null
+++ b/lib/v2/gitpod/router.js
@@ -0,0 +1,4 @@
+module.exports = (router) => {
+ router.get('/blog', require('./blog'));
+ router.get('/changelog', require('./changelog'));
+};
diff --git a/lib/v2/gitpod/templates/description.art b/lib/v2/gitpod/templates/description.art
new file mode 100644
index 0000000000..c52a110a27
--- /dev/null
+++ b/lib/v2/gitpod/templates/description.art
@@ -0,0 +1,2 @@
+
+{{@ content }}
\ No newline at end of file
diff --git a/lib/v2/gitpod/utils.js b/lib/v2/gitpod/utils.js
new file mode 100644
index 0000000000..05d3dc35f1
--- /dev/null
+++ b/lib/v2/gitpod/utils.js
@@ -0,0 +1,3 @@
+module.exports = {
+ rootUrl: 'https://www.gitpod.io',
+};