diff --git a/docs/README.md b/docs/README.md
index bcec671cc..1aa531ff6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -974,6 +974,11 @@ GitHub 官方也提供了一些 RSS:
+### Tits Guru
+
+
+
+
### nHentai
@@ -1007,6 +1012,10 @@ GitHub 官方也提供了一些 RSS:
+### 看漫画
+
+
+
### Anime1
diff --git a/router.js b/router.js
index e4a083775..bd4bb6c13 100644
--- a/router.js
+++ b/router.js
@@ -790,6 +790,13 @@ router.get('/eeo/:category?', require('./routes/eeo/index'));
// 腾讯视频
router.get('/tencentvideo/playlist/:id', require('./routes/tencent/video/playlist'));
+// 看漫画
+router.get('/manhuagui/comic/:id', require('./routes/manhuagui/comic'));
+
+// Tits Guru
+router.get('/tits-guru/home', require('./routes/titsguru/home'));
+router.get('/tits-guru/daily', require('./routes/titsguru/daily'));
+
// typora
router.get('/typora/changelog', require('./routes/typora/changelog'));
diff --git a/routes/manhuagui/comic.js b/routes/manhuagui/comic.js
new file mode 100644
index 000000000..e6eb999ac
--- /dev/null
+++ b/routes/manhuagui/comic.js
@@ -0,0 +1,42 @@
+const { resolve } = require('url');
+const cheerio = require('cheerio');
+const axios = require('../../utils/axios');
+
+const getChapters = ($) =>
+ $('#chapter-list-0 > ul li')
+ .map((_, ele) => {
+ const a = $(ele).children('a');
+ return {
+ link: resolve('https://www.manhuagui.com/', a.attr('href')),
+ title: a.attr('title'),
+ num: a.find('i').text(),
+ };
+ })
+ .toArray();
+
+module.exports = async (ctx) => {
+ const { id } = ctx.params;
+
+ const { data } = await axios.get(`https://www.manhuagui.com/comic/${id}/`);
+ const $ = cheerio.load(data);
+
+ const bookTitle = $('.book-title > h1').text();
+ const bookIntro = $('#intro-all').text();
+ const coverImgSrc = $('.book-cover img').attr('src');
+ const chapters = getChapters($);
+
+ const genResult = (chapter) => ({
+ link: chapter.link,
+ title: chapter.title,
+ description: `
+ ${chapter.num}
+
+ `.trim(),
+ });
+ ctx.state.data = {
+ title: `看漫画 - ${bookTitle}`,
+ link: `https://www.manhuagui.com/comic/${id}/`,
+ description: bookIntro,
+ item: chapters.map(genResult),
+ };
+};
diff --git a/routes/titsguru/daily.js b/routes/titsguru/daily.js
new file mode 100644
index 000000000..8f29346a4
--- /dev/null
+++ b/routes/titsguru/daily.js
@@ -0,0 +1,21 @@
+const cheerio = require('cheerio');
+const axios = require('../../utils/axios');
+const { mapDetail } = require('./util');
+
+module.exports = async (ctx) => {
+ const { data } = await axios.get('https://tits-guru.com/thebest/perDay', {
+ headers: { Accept: '' },
+ });
+ const $ = cheerio.load(data);
+
+ const items = $('.post-row')
+ .map((_, ele) => mapDetail($(ele)))
+ .toArray();
+
+ ctx.state.data = {
+ title: 'TitsGuru - Babe of The Day',
+ link: 'https://tits-guru.com/thebest/perDay',
+ description: 'TitsGuru - Babe of The Day',
+ item: items,
+ };
+};
diff --git a/routes/titsguru/home.js b/routes/titsguru/home.js
new file mode 100644
index 000000000..778504fa5
--- /dev/null
+++ b/routes/titsguru/home.js
@@ -0,0 +1,21 @@
+const cheerio = require('cheerio');
+const axios = require('../../utils/axios');
+const { mapDetail } = require('./util');
+
+module.exports = async (ctx) => {
+ const { data } = await axios.get('https://tits-guru.com', {
+ headers: { Accept: '' },
+ });
+ const $ = cheerio.load(data);
+
+ const items = $('.post-row')
+ .map((_, ele) => mapDetail($(ele)))
+ .toArray();
+
+ ctx.state.data = {
+ title: 'TitsGuru',
+ link: 'https://tits-guru.com/',
+ description: 'TitsGuru',
+ item: items,
+ };
+};
diff --git a/routes/titsguru/util.js b/routes/titsguru/util.js
new file mode 100644
index 000000000..730b111e4
--- /dev/null
+++ b/routes/titsguru/util.js
@@ -0,0 +1,42 @@
+const { resolve } = require('url');
+const day = require('dayjs');
+
+exports.mapDetail = (ele) => {
+ const link = resolve('https://tits-guru.com', ele.find('.img-link').attr('href'));
+ const title = ele.find('.img-link > img').attr('title');
+ const image = ele
+ .find('.horizontal-socials .socials-share-pinterest > span')
+ .attr('data-src')
+ .match(/media=(.+)&url=/)[1];
+ const dateStr = ele.find('.post-time').text();
+ return {
+ link,
+ title,
+ pubDate: exports.parseDate(dateStr.trim()).toUTCString(),
+ description: `
+
+ `.trim(),
+ };
+};
+
+exports.parseDate = (str) => {
+ // 网页给的是一种很诡异的零时区表示
+ const [, _dayStr, timeStr] = str.match(/^(.+) (.+)$/);
+ const current = day().add(new Date().getTimezoneOffset(), 'minute'); // offset date
+ let dayStr;
+ if (_dayStr === 'Today') {
+ dayStr = current.format('YYYY-MM-DD');
+ } else if (_dayStr === 'Yesterday') {
+ dayStr = current.subtract(1, 'day').format('YYYY-MM-DD');
+ } else if (_dayStr.match(/[a-z]+/)) {
+ // eg. '06 november'
+ dayStr = day(`${_dayStr} UTC`)
+ .set('year', day().year())
+ .format('YYYY-MM-DD');
+ } else {
+ // eg. '16.03.2014'
+ const [, dd, mm, yy] = _dayStr.match(/(\d+)\.(\d+)\.(\d+)/);
+ dayStr = `${yy}-${mm}-${dd}`;
+ }
+ return new Date(`${dayStr} ${timeStr} UTC`);
+};