diff --git a/docs/picture.md b/docs/picture.md
index 0352fd8ede..193edec984 100644
--- a/docs/picture.md
+++ b/docs/picture.md
@@ -117,6 +117,31 @@ pageClass: routes
+## MM 范
+
+### 分类
+
+
+
+| 最新 | 热门 | 校花 | 森系 | 清纯 | 童颜 | 嫩模 | 少女 |
+| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
+
+
+
+### 标签
+
+
+
+### 集合
+
+
+
+| 清纯唯美 | 摄影私房 | 明星写真 | 三次元 | 异域美景 | 性感妖姬 | 游戏主题 | 美女壁纸 |
+| -------- | -------- | -------- | ------ | -------- | -------- | -------- | -------- |
+| 1 | 2 | 4 | 5 | 6 | 7 | 9 | 11 |
+
+
+
## NASA 每日天文图片
### NASA
diff --git a/lib/router.js b/lib/router.js
index 74b3fc3f5e..6eb2ce9bcb 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -3257,8 +3257,14 @@ router.get('/appsales/:caty?/:time?', require('./routes/appsales/index'));
// CGTN
router.get('/cgtn/top', require('./routes/cgtn/top'));
+// MM范
+router.get('/95mm/tab/:tab?', require('./routes/95mm/tab'));
+router.get('/95mm/tag/:tag', require('./routes/95mm/tag'));
+router.get('/95mm/category/:category', require('./routes/95mm/category'));
+
// 中国工程科技知识中心
router.get('/cktest/app/:ctgroup?/:domain?', require('./routes/cktest/app'));
+
// 妈咪帮
router.get('/mamibuy/:caty?/:age?/:sort?', require('./routes/mamibuy/index'));
diff --git a/lib/routes/95mm/category.js b/lib/routes/95mm/category.js
new file mode 100644
index 0000000000..cf164d180d
--- /dev/null
+++ b/lib/routes/95mm/category.js
@@ -0,0 +1,18 @@
+const utils = require('./utils');
+
+module.exports = async (ctx) => {
+ const categories = {
+ 1: '清纯唯美',
+ 2: '摄影私房',
+ 4: '明星写真',
+ 5: '三次元',
+ 6: '异域美景',
+ 7: '性感妖姬',
+ 9: '游戏主题',
+ 11: '美女壁纸',
+ };
+
+ const rootUrl = `https://www.95mm.net/category-${ctx.params.category}/list-1/index.html?page=1`;
+
+ ctx.state.data = await utils(ctx, categories[ctx.params.category], rootUrl);
+};
diff --git a/lib/routes/95mm/tab.js b/lib/routes/95mm/tab.js
new file mode 100644
index 0000000000..619de6dbd7
--- /dev/null
+++ b/lib/routes/95mm/tab.js
@@ -0,0 +1,9 @@
+const utils = require('./utils');
+
+module.exports = async (ctx) => {
+ ctx.params.tab = ctx.params.tab || '最新';
+
+ const rootUrl = `https://www.95mm.net/home-ajax/index.html?tabcid=${ctx.params.tab}&page=1`;
+
+ ctx.state.data = await utils(ctx, ctx.params.tab, rootUrl);
+};
diff --git a/lib/routes/95mm/tag.js b/lib/routes/95mm/tag.js
new file mode 100644
index 0000000000..0176aaac76
--- /dev/null
+++ b/lib/routes/95mm/tag.js
@@ -0,0 +1,7 @@
+const utils = require('./utils');
+
+module.exports = async (ctx) => {
+ const rootUrl = `https://www.95mm.net/tag-${ctx.params.tag}/page-1/index.html`;
+
+ ctx.state.data = await utils(ctx, ctx.params.tag, rootUrl);
+};
diff --git a/lib/routes/95mm/utils.js b/lib/routes/95mm/utils.js
new file mode 100644
index 0000000000..f5a8786d41
--- /dev/null
+++ b/lib/routes/95mm/utils.js
@@ -0,0 +1,52 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+
+module.exports = async (ctx, title, rootUrl) => {
+ const response = await got({
+ method: 'get',
+ url: rootUrl,
+ header: {
+ Referer: 'https://www.95mm.net',
+ },
+ });
+
+ const $ = cheerio.load(response.data);
+
+ const list = $('div.list-body')
+ .map((_, item) => {
+ item = $(item);
+ const a = item.find('a');
+ return {
+ title: a.text(),
+ link: a.attr('href'),
+ };
+ })
+ .get();
+
+ const items = await Promise.all(
+ list.map(
+ async (item) =>
+ await ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got({
+ method: 'get',
+ url: item.link,
+ });
+ const images = detailResponse.data.match(/src": '(.*?)',"width/g);
+
+ item.description = '';
+
+ for (const i of images) {
+ item.description += `
`;
+ }
+
+ return item;
+ })
+ )
+ );
+
+ return {
+ title: `${title} - MM范`,
+ link: rootUrl,
+ item: items,
+ };
+};