Merge remote-tracking branch 'DIYgod/master' into dev
This commit is contained in:
commit
47778c187d
|
|
@ -1,3 +1,4 @@
|
|||
comment: off
|
||||
coverage:
|
||||
status:
|
||||
patch:
|
||||
|
|
|
|||
|
|
@ -297,6 +297,8 @@ RSSHub 提供下列 API 接口:
|
|||
|
||||
<route name="视频评论" author="Qixingchen" example="/bilibili/video/reply/21669336" path="/bilibili/video/reply/:aid" :paramsDesc="['可在视频页 URL 中找到']"/>
|
||||
|
||||
<route name="视频弹幕" author="Qixingchen" example="/bilibili/video/danmaku/21669336/1" path="/bilibili/video/danmaku/:aid/:pid?" :paramsDesc="['视频AV号,可在视频页 URL 中找到','分P号,不填默认为1']"/>
|
||||
|
||||
<route name="link 公告" author="Qixingchen" example="/bilibili/link/news/live" path="/bilibili/link/news/:product" :paramsDesc="['公告分类, 包括 直播:live 小视频:vc 相簿:wh']"/>
|
||||
|
||||
#### 直播开播 <Author uid="Qixingchen"/>
|
||||
|
|
@ -497,6 +499,8 @@ RSSHub 提供下列 API 接口:
|
|||
|
||||
</route>
|
||||
|
||||
<route name="豆列" author="LogicJake" example="/douban/doulist/37716774" path="douban/doulist/:id" :paramsDesc="['豆列id']"/>
|
||||
|
||||
### Disqus
|
||||
|
||||
<route name="评论" author="DIYgod" example="/disqus/posts/diygod-me" path="/disqus/posts/:forum" :paramsDesc="['网站的 disqus name']"/>
|
||||
|
|
@ -2778,23 +2782,23 @@ board 和 build 可在[这里](http://api.ineal.me/tss/status)查看
|
|||
|
||||
### JavBus
|
||||
|
||||
<route name="首页" author="MegrezZhu" example="/javbus/home" path="/javbus/home" />
|
||||
<route name="首页" author="MegrezZhu CoderTonyChan" example="/javbus/home" path="/javbus/home"/>
|
||||
|
||||
<route name="分类" author="MegrezZhu" example="/javbus/genre/7g" path="/javbus/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.com/genre)的链接']" />
|
||||
<route name="分类" author="MegrezZhu CoderTonyChan" example="/javbus/genre/7g" path="/javbus/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.com/genre)的链接']" />
|
||||
|
||||
<route name="演员" author="MegrezZhu" example="/javbus/star/2jv" path="/javbus/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.com/actresses)的链接']" />
|
||||
<route name="演员" author="MegrezZhu CoderTonyChan" example="/javbus/star/2jv" path="/javbus/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.com/actresses)的链接']" />
|
||||
|
||||
<route name="首页/步兵" author="MegrezZhu" example="/javbus/uncensored/home" path="/javbus/uncensored/home" />
|
||||
<route name="首页/步兵" author="MegrezZhu CoderTonyChan" example="/javbus/uncensored/home" path="/javbus/uncensored/home"/>
|
||||
|
||||
<route name="分类/步兵" author="MegrezZhu" example="/javbus/uncensored/genre/1bc" path="/javbus/uncensored/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.com/uncensored/genre)的链接']" />
|
||||
<route name="分类/步兵" author="MegrezZhu CoderTonyChan" example="/javbus/uncensored/genre/1bc" path="/javbus/uncensored/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.com/uncensored/genre)的链接']" />
|
||||
|
||||
<route name="演员/步兵" author="MegrezZhu" example="/javbus/uncensored/star/b5b" path="/javbus/uncensored/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.com/uncensored/actresses)的链接']" />
|
||||
<route name="演员/步兵" author="MegrezZhu CoderTonyChan" example="/javbus/uncensored/star/b5b" path="/javbus/uncensored/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.com/uncensored/actresses)的链接']" />
|
||||
|
||||
<route name="首页/欧陆风云" author="MegrezZhu" example="/javbus/western/home" path="/javbus/western/home" />
|
||||
<route name="首页/欧陆风云" author="MegrezZhu CoderTonyChan" example="/javbus/western/home" path="/javbus/western/home"/>
|
||||
|
||||
<route name="分类/欧陆风云" author="MegrezZhu" example="/javbus/western/genre/86" path="/javbus/western/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.work/genre)的链接']" />
|
||||
<route name="分类/欧陆风云" author="MegrezZhu CoderTonyChan" example="/javbus/western/genre/86" path="/javbus/western/genre/:gid" :paramsDesc="['分类id,详见[网站里](https://www.javbus.work/genre)的链接']" />
|
||||
|
||||
<route name="演员/欧陆风云" author="MegrezZhu" example="/javbus/western/star/4hv" path="/javbus/western/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.work/actresses)的链接']" />
|
||||
<route name="演员/欧陆风云" author="MegrezZhu CoderTonyChan" example="/javbus/western/star/4hv" path="/javbus/western/star/:sid" :paramsDesc="['演员id,详见[网站里](https://www.javbus.work/actresses)的链接']" />
|
||||
|
||||
### 虎嗅
|
||||
|
||||
|
|
@ -2802,6 +2806,8 @@ board 和 build 可在[这里](http://api.ineal.me/tss/status)查看
|
|||
|
||||
<route name="搜索" author="xyqfer" example="/huxiu/search/%E8%99%8E%E5%97%85%E6%97%A9%E6%8A%A5" path="/huxiu/search/:keyword" :paramsDesc="['关键字']" />
|
||||
|
||||
<route name="作者" author="HenryQW" example="/huxiu/author/29318" path="/huxiu/author/:id" :paramsDesc="['用户 id']" />
|
||||
|
||||
### 扇贝
|
||||
|
||||
<route name="打卡" author="DIYgod" example="/shanbay/checkin/ddwej" path="/shanbay/checkin/:id" :paramsDesc="['用户 id']" />
|
||||
|
|
@ -2833,3 +2839,14 @@ board 和 build 可在[这里](http://api.ineal.me/tss/status)查看
|
|||
### 移动支付网
|
||||
|
||||
<route name="新闻" author="LogicJake" example="/mpaypass/news" path="/mpaypass/news"/>
|
||||
|
||||
### 新浪科技
|
||||
|
||||
<route name="科学探索" author="LogicJake" example="/sina/discovery/zx" path="/sina/discovery/:type" :paramsDesc="['订阅分区类型']">
|
||||
|
||||
分类:
|
||||
| zx | twhk | dwzw | zrdl | lskg | smyx | shbk | kjqy |
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 最新 | 天文航空 | 动物植物 | 自然地理 | 历史考古 | 生命医学 | 生活百科 | 科技前沿 |
|
||||
|
||||
</route>
|
||||
|
|
|
|||
|
|
@ -37,29 +37,29 @@ function responseHandler() {
|
|||
ctx.body = { status, data, message };
|
||||
};
|
||||
|
||||
ctx.res.fail = ({ statusCode, code, data = null, message = null }) => {
|
||||
const status = -1;
|
||||
// ctx.res.fail = ({ statusCode, code, data = null, message = null }) => {
|
||||
// const status = -1;
|
||||
|
||||
if (!!statusCode && (statusCode >= 400 && statusCode < 500)) {
|
||||
ctx.status = statusCode;
|
||||
} else if (!(ctx.status >= 400 && ctx.status < 500)) {
|
||||
ctx.status = statusCodes.BAD_REQUEST;
|
||||
}
|
||||
// if (!!statusCode && (statusCode >= 400 && statusCode < 500)) {
|
||||
// ctx.status = statusCode;
|
||||
// } else if (!(ctx.status >= 400 && ctx.status < 500)) {
|
||||
// ctx.status = statusCodes.BAD_REQUEST;
|
||||
// }
|
||||
|
||||
ctx.body = { status, code, data, message };
|
||||
};
|
||||
// ctx.body = { status, code, data, message };
|
||||
// };
|
||||
|
||||
ctx.res.error = ({ statusCode, code, data = null, message = null }) => {
|
||||
const status = -2;
|
||||
// ctx.res.error = ({ statusCode, code, data = null, message = null }) => {
|
||||
// const status = -2;
|
||||
|
||||
if (!!statusCode && (statusCode >= 500 && statusCode < 600)) {
|
||||
ctx.status = statusCode;
|
||||
} else if (!(ctx.status >= 500 && ctx.status < 600)) {
|
||||
ctx.status = statusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
// if (!!statusCode && (statusCode >= 500 && statusCode < 600)) {
|
||||
// ctx.status = statusCode;
|
||||
// } else if (!(ctx.status >= 500 && ctx.status < 600)) {
|
||||
// ctx.status = statusCodes.INTERNAL_SERVER_ERROR;
|
||||
// }
|
||||
|
||||
ctx.body = { status, code, data, message };
|
||||
};
|
||||
// ctx.body = { status, code, data, message };
|
||||
// };
|
||||
|
||||
ctx.res.ok = (params = {}) => {
|
||||
ctx.res.success({
|
||||
|
|
@ -68,20 +68,6 @@ function responseHandler() {
|
|||
});
|
||||
};
|
||||
|
||||
ctx.res.created = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.CREATED,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.accepted = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
statusCode: statusCodes.ACCEPTED,
|
||||
});
|
||||
};
|
||||
|
||||
ctx.res.noContent = (params = {}) => {
|
||||
ctx.res.success({
|
||||
...params,
|
||||
|
|
@ -89,75 +75,76 @@ function responseHandler() {
|
|||
});
|
||||
};
|
||||
|
||||
ctx.res.badRequest = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.BAD_REQUEST,
|
||||
});
|
||||
};
|
||||
// ctx.res.badRequest = (params = {}) => {
|
||||
// ctx.res.fail({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.BAD_REQUEST,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.forbidden = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.FORBIDDEN,
|
||||
});
|
||||
};
|
||||
// ctx.res.forbidden = (params = {}) => {
|
||||
// ctx.res.fail({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.FORBIDDEN,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.notFound = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.NOT_FOUND,
|
||||
});
|
||||
};
|
||||
// ctx.res.notFound = (params = {}) => {
|
||||
// ctx.res.fail({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.NOT_FOUND,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.requestTimeout = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.REQUEST_TIMEOUT,
|
||||
});
|
||||
};
|
||||
// ctx.res.requestTimeout = (params = {}) => {
|
||||
// ctx.res.fail({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.REQUEST_TIMEOUT,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.unprocessableEntity = (params = {}) => {
|
||||
ctx.res.fail({
|
||||
...params,
|
||||
statusCode: statusCodes.UNPROCESSABLE_ENTITY,
|
||||
});
|
||||
};
|
||||
// ctx.res.unprocessableEntity = (params = {}) => {
|
||||
// ctx.res.fail({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.UNPROCESSABLE_ENTITY,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.internalServerError = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.INTERNAL_SERVER_ERROR,
|
||||
});
|
||||
};
|
||||
// ctx.res.internalServerError = (params = {}) => {
|
||||
// ctx.res.error({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.INTERNAL_SERVER_ERROR,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.notImplemented = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.NOT_IMPLEMENTED,
|
||||
});
|
||||
};
|
||||
// ctx.res.notImplemented = (params = {}) => {
|
||||
// ctx.res.error({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.NOT_IMPLEMENTED,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.badGateway = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.BAD_GATEWAY,
|
||||
});
|
||||
};
|
||||
// ctx.res.badGateway = (params = {}) => {
|
||||
// ctx.res.error({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.BAD_GATEWAY,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.serviceUnavailable = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.SERVICE_UNAVAILABLE,
|
||||
});
|
||||
};
|
||||
// ctx.res.serviceUnavailable = (params = {}) => {
|
||||
// ctx.res.error({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.SERVICE_UNAVAILABLE,
|
||||
// });
|
||||
// };
|
||||
|
||||
// ctx.res.gatewayTimeOut = (params = {}) => {
|
||||
// ctx.res.error({
|
||||
// ...params,
|
||||
// statusCode: statusCodes.GATEWAY_TIME_OUT,
|
||||
// });
|
||||
// };
|
||||
|
||||
ctx.res.gatewayTimeOut = (params = {}) => {
|
||||
ctx.res.error({
|
||||
...params,
|
||||
statusCode: statusCodes.GATEWAY_TIME_OUT,
|
||||
});
|
||||
};
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
const pathToRegExp = require('path-to-regexp');
|
||||
const readall = require('readall');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const paired = (route, path) => {
|
||||
const options = {
|
||||
|
|
@ -11,24 +9,6 @@ const paired = (route, path) => {
|
|||
return pathToRegExp(route, [], options).exec(path);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const read = (stream) =>
|
||||
new Promise((resolve, reject) => {
|
||||
readall(stream, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const md5 = (str) =>
|
||||
crypto
|
||||
.createHash('md5')
|
||||
.update(str)
|
||||
.digest('hex');
|
||||
|
||||
const validityCheck = (routes, exclude, path) => {
|
||||
let match = false;
|
||||
let routeExpire = false;
|
||||
|
|
@ -59,7 +39,5 @@ const validityCheck = (routes, exclude, path) => {
|
|||
|
||||
module.exports = {
|
||||
paired,
|
||||
read,
|
||||
md5,
|
||||
validityCheck,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,16 +9,11 @@ module.exports = async (ctx, next) => {
|
|||
ctx.debug.ips[ip] = 0;
|
||||
}
|
||||
ctx.debug.ips[ip]++;
|
||||
|
||||
if (ctx.request.path !== '/') {
|
||||
ctx.debug.request++;
|
||||
}
|
||||
ctx.debug.request++;
|
||||
|
||||
await next();
|
||||
|
||||
if (ctx.request.path !== '/') {
|
||||
if (ctx.response.get('X-Koa-Redis-Cache') || ctx.response.get('X-Koa-Memory-Cache')) {
|
||||
ctx.debug.hitCache++;
|
||||
}
|
||||
if (ctx.response.get('X-Koa-Redis-Cache') || ctx.response.get('X-Koa-Memory-Cache')) {
|
||||
ctx.debug.hitCache++;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
const lru = require('lru-cache');
|
||||
const common = require('./cache-common');
|
||||
const md5 = require('../utils/md5');
|
||||
|
||||
module.exports = function(options = {}) {
|
||||
const {
|
||||
|
|
@ -55,7 +56,7 @@ module.exports = function(options = {}) {
|
|||
return async function cache(ctx, next) {
|
||||
const { url, path } = ctx.request;
|
||||
const resolvedPrefix = typeof prefix === 'function' ? prefix.call(ctx, ctx) : prefix;
|
||||
const key = resolvedPrefix + common.md5(ignoreQuery ? path : url);
|
||||
const key = resolvedPrefix + md5(ignoreQuery ? path : url);
|
||||
const tkey = key + ':type';
|
||||
|
||||
const validityCheck = common.validityCheck(routes, exclude, path);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
const wrapper = require('co-redis');
|
||||
const Redis = require('redis');
|
||||
const common = require('./cache-common');
|
||||
const md5 = require('../utils/md5');
|
||||
|
||||
module.exports = function(options = {}) {
|
||||
let redisAvailable = false;
|
||||
|
|
@ -68,7 +69,7 @@ module.exports = function(options = {}) {
|
|||
return async function cache(ctx, next) {
|
||||
const { url, path } = ctx.request;
|
||||
const resolvedPrefix = typeof prefix === 'function' ? prefix.call(ctx, ctx) : prefix;
|
||||
const key = resolvedPrefix + common.md5(ignoreQuery ? path : url);
|
||||
const key = resolvedPrefix + md5(ignoreQuery ? path : url);
|
||||
const tkey = key + ':type';
|
||||
|
||||
const validityCheck = common.validityCheck(routes, exclude, path);
|
||||
|
|
|
|||
|
|
@ -87,23 +87,23 @@ router.get('/', async (ctx) => {
|
|||
});
|
||||
|
||||
router.get('/test/:id', (ctx) => {
|
||||
if (ctx.params.id === '0') {
|
||||
throw Error('Error test');
|
||||
}
|
||||
const item = [];
|
||||
for (let i = 1; i < 6; i++) {
|
||||
item.push({
|
||||
title: `Title${i}`,
|
||||
description: `Item${i}`,
|
||||
pubDate: new Date(`2018-4-${i}`).toUTCString(),
|
||||
link: `https://github.com/DIYgod/RSSHub/issues/${i}`,
|
||||
author: `DIYgod${i}`,
|
||||
});
|
||||
}
|
||||
ctx.state.data = {
|
||||
title: `Test ${ctx.params.id}`,
|
||||
link: 'https://github.com/DIYgod/RSSHub',
|
||||
item: [
|
||||
{
|
||||
title: 'Title1',
|
||||
description: 'Item1',
|
||||
pubDate: new Date('2018-4-2').toUTCString(),
|
||||
link: 'https://github.com/DIYgod/RSSHub/issues/1',
|
||||
},
|
||||
{
|
||||
title: 'Title2',
|
||||
description: 'Item2',
|
||||
pubDate: new Date('2018-4-10').toUTCString(),
|
||||
link: 'https://github.com/DIYgod/RSSHub/issues/2',
|
||||
},
|
||||
],
|
||||
item: item,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -123,6 +123,7 @@ router.get('/bilibili/partion/ranking/:tid/:days?', require('./routes/bilibili/p
|
|||
router.get('/bilibili/bangumi/:seasonid', require('./routes/bilibili/bangumi')); // 弃用
|
||||
router.get('/bilibili/bangumi/media/:mediaid', require('./routes/bilibili/bangumi'));
|
||||
router.get('/bilibili/video/reply/:aid', require('./routes/bilibili/reply'));
|
||||
router.get('/bilibili/video/danmaku/:aid/:pid?', require('./routes/bilibili/danmaku'));
|
||||
router.get('/bilibili/link/news/:product', require('./routes/bilibili/linkNews'));
|
||||
router.get('/bilibili/live/room/:roomID', require('./routes/bilibili/liveRoom'));
|
||||
router.get('/bilibili/live/search/:key/:order', require('./routes/bilibili/liveSearch'));
|
||||
|
|
@ -226,6 +227,7 @@ router.get('/douban/event/hot/:locationId', require('./routes/douban/event/hot')
|
|||
router.get('/douban/commercialpress/latest', require('./routes/douban/commercialpress/latest'));
|
||||
router.get('/douban/bookstore', require('./routes/douban/bookstore'));
|
||||
router.get('/douban/book/rank/:type', require('./routes/douban/book/rank'));
|
||||
router.get('/douban/doulist/:id', require('./routes/douban/doulist'));
|
||||
|
||||
// 煎蛋
|
||||
router.get('/jandan/:sub_model', require('./routes/jandan/pic'));
|
||||
|
|
@ -956,6 +958,7 @@ router.get('/zimuzu/resource/:id?', require('./routes/zimuzu/resource'));
|
|||
// 虎嗅
|
||||
router.get('/huxiu/tag/:id', require('./routes/huxiu/tag'));
|
||||
router.get('/huxiu/search/:keyword', require('./routes/huxiu/search'));
|
||||
router.get('/huxiu/author/:id', require('./routes/huxiu/author'));
|
||||
|
||||
// Steam
|
||||
router.get('/steam/search/:params', require('./routes/steam/search'));
|
||||
|
|
@ -1013,4 +1016,7 @@ router.get('/fitchratings/site/:type', require('./routes/fitchratings/site'));
|
|||
// 移动支付
|
||||
router.get('/mpaypass/news', require('./routes/mpaypass/news'));
|
||||
|
||||
// 新浪科技探索
|
||||
router.get('/sina/discovery/:type', require('./routes/sina/discovery'));
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -102,4 +102,24 @@ module.exports = {
|
|||
}
|
||||
return name;
|
||||
},
|
||||
getCidFromAid: async (ctx, aid, pid) => {
|
||||
const key = `bili-Cid-from-Aid-${aid}-${pid}`;
|
||||
let cid = await ctx.cache.get(key);
|
||||
if (!cid) {
|
||||
const cidResponse = await axios({
|
||||
method: 'get',
|
||||
url: `https://api.bilibili.com/x/web-interface/view?aid=${aid}`,
|
||||
headers: {
|
||||
Referer: `https://www.bilibili.com/video/av${aid}`,
|
||||
},
|
||||
});
|
||||
if (cidResponse && cidResponse.data && cidResponse.data.data && cidResponse.data.data.pages && cidResponse.data.data.pages.length >= pid) {
|
||||
cid = cidResponse.data.data.pages[pid - 1].cid;
|
||||
}
|
||||
if (!cid) {
|
||||
ctx.cache.set(key, cid, 24 * 60 * 60);
|
||||
}
|
||||
}
|
||||
return cid;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
const cheerio = require('cheerio');
|
||||
const cache = require('./cache');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const aid = ctx.params.aid;
|
||||
const pid = Number(ctx.params.pid || 1);
|
||||
const limit = 50;
|
||||
const cid = await cache.getCidFromAid(ctx, aid, pid);
|
||||
|
||||
const videoName = await cache.getVideoNameFromAid(ctx, aid);
|
||||
|
||||
const danmakuResponse = await fetch(`https://comment.bilibili.com/${cid}.xml`);
|
||||
const danmakuText = await danmakuResponse.text();
|
||||
let danmakuList = [];
|
||||
const $ = cheerio.load(danmakuText, { xmlMode: true });
|
||||
$('d').each((index, item) => {
|
||||
danmakuList.push({ p: $(item).attr('p'), text: $(item).text() });
|
||||
});
|
||||
|
||||
danmakuList = danmakuList.reverse().slice(0, limit);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${videoName} 的 弹幕动态`,
|
||||
link: `https://www.bilibili.com/video/av${aid}`,
|
||||
description: `${videoName} 的 弹幕动态`,
|
||||
item: danmakuList.map((item) => ({
|
||||
title: item.text,
|
||||
description: item.text,
|
||||
pubDate: new Date(item.p.split(',')[4] * 1000).toUTCString(),
|
||||
guid: `${cid}-${item.p.split(',')[4]}-${item.p.split(',')[7]}`,
|
||||
link: `https://www.bilibili.com/video/av${aid}`,
|
||||
})),
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const cheerio = require('cheerio');
|
||||
const url = require('url');
|
||||
|
||||
const host = 'https://www.douban.com/doulist/';
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
|
||||
const link = url.resolve(host, id);
|
||||
const response = await axios.get(link);
|
||||
const $ = cheerio.load(response.data);
|
||||
|
||||
const title = $('#content h1')
|
||||
.text()
|
||||
.trim();
|
||||
const description = $('div.doulist-about')
|
||||
.text()
|
||||
.trim();
|
||||
const out = $('div.doulist-item')
|
||||
.slice(0, 10)
|
||||
.map(function() {
|
||||
const title = $(this)
|
||||
.find('div.bd.doulist-note div.title a')
|
||||
.text()
|
||||
.trim();
|
||||
const link = $(this)
|
||||
.find('div.bd.doulist-note div.title a')
|
||||
.attr('href');
|
||||
const date = $(this)
|
||||
.find('div.ft div.actions time span')
|
||||
.attr('title');
|
||||
|
||||
const description = $(this)
|
||||
.find('div.bd.doulist-note div.abstract')
|
||||
.text()
|
||||
.trim();
|
||||
const single = {
|
||||
title: title,
|
||||
link: link,
|
||||
description: description,
|
||||
pubDate: new Date(date).toUTCString(),
|
||||
};
|
||||
return single;
|
||||
})
|
||||
.get();
|
||||
|
||||
ctx.state.data = {
|
||||
title: title,
|
||||
link: link,
|
||||
description: description,
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const cheerio = require('cheerio');
|
||||
const url = require('url');
|
||||
const date = require('../../utils/date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { id } = ctx.params;
|
||||
const link = `https://www.huxiu.com/member/${id}.html`;
|
||||
const host = 'https://www.huxiu.com';
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url: link,
|
||||
headers: {
|
||||
Referer: link,
|
||||
},
|
||||
});
|
||||
|
||||
const $ = cheerio.load(response.data);
|
||||
const author = $('.user-name')
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const list = $('.message-box > .mod-art > a')
|
||||
.slice(0, 10)
|
||||
.get()
|
||||
.map((e) => $(e).attr('href'));
|
||||
|
||||
const items = await Promise.all(
|
||||
list.map(async (e) => {
|
||||
const itemUrl = url.resolve(host, e);
|
||||
|
||||
const single = await ctx.cache.tryGet(
|
||||
itemUrl,
|
||||
async () => {
|
||||
const response = await axios.get(itemUrl);
|
||||
const $ = cheerio.load(response.data);
|
||||
$('.neirong-shouquan, .neirong-shouquan-public').remove();
|
||||
return {
|
||||
title: $('.t-h1')
|
||||
.text()
|
||||
.trim(),
|
||||
link: itemUrl,
|
||||
description: $('.article-img-box').html() + $('.article-content-wrap').html(),
|
||||
pubDate: date($('.article-time').text(), 8),
|
||||
author,
|
||||
};
|
||||
},
|
||||
2 * 24 * 60 * 60
|
||||
);
|
||||
|
||||
return Promise.resolve(single);
|
||||
})
|
||||
);
|
||||
const authorInfo = `虎嗅网 - ${author}`;
|
||||
ctx.state.data = {
|
||||
title: authorInfo,
|
||||
link,
|
||||
description: authorInfo,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
|
|
@ -3,5 +3,5 @@ const { getPage } = require('./util');
|
|||
module.exports = async (ctx) => {
|
||||
const { gid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/genre/${gid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/genre/${gid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ const { getPage } = require('./util');
|
|||
module.exports = async (ctx) => {
|
||||
const { sid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/star/${sid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/star/${sid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,5 @@ const { getPage } = require('../util');
|
|||
|
||||
module.exports = async (ctx) => {
|
||||
const { gid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/uncensored/genre/${gid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/uncensored/genre/${gid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ const { getPage } = require('../util');
|
|||
module.exports = async (ctx) => {
|
||||
const { sid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/uncensored/star/${sid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.com/uncensored/star/${sid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const { load } = require('cheerio');
|
||||
|
||||
exports.getPage = async (url) => {
|
||||
const { data } = await axios.get(url);
|
||||
exports.getPage = async (url, ctx) => {
|
||||
const { data } = await axios(url);
|
||||
const $ = load(data);
|
||||
|
||||
const pageTitle = `JavBus - ${$('head > title')
|
||||
|
|
@ -13,15 +13,15 @@ exports.getPage = async (url) => {
|
|||
title: pageTitle,
|
||||
link: url,
|
||||
description: pageTitle,
|
||||
item: parseItems($),
|
||||
item: await Promise.all(parseItems($, ctx)),
|
||||
};
|
||||
};
|
||||
|
||||
exports.createHandler = (url) => async (ctx) => {
|
||||
ctx.state.data = await exports.getPage(url);
|
||||
ctx.state.data = await exports.getPage(url, ctx);
|
||||
};
|
||||
|
||||
const parseItems = ($) =>
|
||||
const parseItems = ($, ctx) =>
|
||||
$('.movie-box')
|
||||
.map((_, ele) => ({
|
||||
title: $(ele)
|
||||
|
|
@ -41,15 +41,108 @@ const parseItems = ($) =>
|
|||
.text(),
|
||||
}))
|
||||
.toArray()
|
||||
.map(render);
|
||||
|
||||
const render = ({ title, thumb, link, pubDate, aid }) => ({
|
||||
title,
|
||||
link,
|
||||
pubDate,
|
||||
description: `
|
||||
<h1>${aid} - ${title}</h1>
|
||||
.map(async ({ title, thumb, link, pubDate, aid }) => {
|
||||
const detail = await getDetail(link, ctx);
|
||||
title = `${aid} ${title}${detail.filmTime}`;
|
||||
if (detail.screenData) {
|
||||
detail.imgHTML = detail.screenData.map((url) => `<img referrerpolicy="no-referrer" src="${url}"><br />`);
|
||||
}
|
||||
return {
|
||||
title,
|
||||
link,
|
||||
pubDate,
|
||||
description: `
|
||||
<h3>${title}</h3>
|
||||
<br />
|
||||
<img referrerpolicy="no-referrer" src="${thumb.replace(/\/thumbs?\//, '/cover/').replace('.jpg', '_b.jpg')}" />
|
||||
${detail.filmTime ? `<h3>发售日期 : ${detail.filmTime}</h3>` : ''}
|
||||
${detail.actresses ? `<h3>女优 : ${detail.actresses}</h3>` : ''}
|
||||
${detail.filmEstabName ? `<h3>發行商 : ${detail.filmEstabName}</h3>` : ''}
|
||||
${detail.filmMakerbName ? `<h3>製作商 : ${detail.filmMakerbName}</h3>` : ''}
|
||||
${detail.seriesName ? `<h3>系列 : ${detail.seriesName}</h3>` : ''}
|
||||
${detail.screenData ? `<h3>影片截图 : </h3>${detail.imgHTML.join('')}` : ''}
|
||||
`.trim(),
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
const getDetail = async (link, ctx) => {
|
||||
const cache = await ctx.cache.get(link);
|
||||
if (cache) {
|
||||
return JSON.parse(cache);
|
||||
} else {
|
||||
const resp = await axios(link);
|
||||
const detailPage = resp.data;
|
||||
// 演员
|
||||
const actressReg = /<a class="avatar-box"[\s\S]*?<\/a>/g;
|
||||
let match = detailPage.match(actressReg);
|
||||
let actresses = [];
|
||||
if (match) {
|
||||
match.map((i) => {
|
||||
const name = /<span>(.*?)<\/span>/.exec(i)[1];
|
||||
actresses = actresses.concat(name);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
actresses = null; // []
|
||||
}
|
||||
// 影片详情
|
||||
// const filmCover = /<a class="bigImage" href="(.*?)"/.exec(detailPage)[1];
|
||||
// const filmName = /<a class="bigImage" href="(.*?)" title="(.*?)"/.exec(detailPage)[2];
|
||||
let filmTime = /<span class="header">發行日期:<\/span>([\s\S]*?)<\/p>/.exec(detailPage);
|
||||
if (filmTime) {
|
||||
filmTime = filmTime[1];
|
||||
} else {
|
||||
filmTime = null;
|
||||
}
|
||||
|
||||
let filmLast = /<span class="header">長度:<\/span>([\s\S]*?)<\/p>/.exec(detailPage);
|
||||
if (filmLast) {
|
||||
const lastmins = /\d+/.exec(filmLast[1]);
|
||||
const hours = Math.floor(lastmins / 60);
|
||||
let mins = lastmins % 60;
|
||||
mins = mins === '0' ? '00' : mins;
|
||||
filmLast = hours + ':' + mins;
|
||||
} else {
|
||||
filmLast = null;
|
||||
}
|
||||
let filmEstabName = /<span class="header">發行商:[\s\S]*?"(.*?)">(.*?)<\/a>/.exec(detailPage);
|
||||
if (filmEstabName) {
|
||||
filmEstabName = filmEstabName[2];
|
||||
} else {
|
||||
filmEstabName = null;
|
||||
}
|
||||
|
||||
let filmMakerbName = /<span class="header">製作商:[\s\S]*?"(.*?)">(.*?)<\/a>/.exec(detailPage);
|
||||
if (filmMakerbName) {
|
||||
filmMakerbName = filmMakerbName[2];
|
||||
} else {
|
||||
filmMakerbName = null;
|
||||
}
|
||||
let seriesName = /<span class="header">系列:[\s\S]*?"(.*?)">(.*?)<\/a>/.exec(detailPage);
|
||||
if (seriesName) {
|
||||
seriesName = seriesName[2];
|
||||
} else {
|
||||
seriesName = null;
|
||||
}
|
||||
let directorName = /<span class="header">導演:[\s\S]*?"(.*?)">(.*?)<\/a>/.exec(detailPage);
|
||||
if (directorName) {
|
||||
directorName = directorName[2];
|
||||
} else {
|
||||
directorName = null;
|
||||
}
|
||||
|
||||
const code = /<span class="header">識別碼:[\s\S]*?">([\s\S]*?)<\/span>/.exec(detailPage)[1];
|
||||
// 影片截图
|
||||
const regScreenshot = /<a class="sample-box" href="(.*?)"[\s\S]*?<img src="(.*?)">/g;
|
||||
match = detailPage.match(regScreenshot);
|
||||
let screenData = [];
|
||||
if (match) {
|
||||
screenData = match.map((i) => /<a class="sample-box" href="(.*?)"[\s\S]*?<img src="(.*?)">/g.exec(i)[1]);
|
||||
} else {
|
||||
screenData = null;
|
||||
}
|
||||
const detail = { actresses, filmTime, filmLast, filmEstabName, filmMakerbName, seriesName, directorName, code, screenData };
|
||||
ctx.cache.set(link, JSON.stringify(detail), 24 * 60 * 60);
|
||||
return detail;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ const { getPage } = require('../util');
|
|||
module.exports = async (ctx) => {
|
||||
const { gid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.work/genre/${gid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.work/genre/${gid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ const { getPage } = require('../util');
|
|||
module.exports = async (ctx) => {
|
||||
const { sid } = ctx.params;
|
||||
|
||||
ctx.state.data = await getPage(`https://www.javbus.work/star/${sid}`);
|
||||
ctx.state.data = await getPage(`https://www.javbus.work/star/${sid}`, ctx);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const common = require('./common');
|
||||
const cheerio = require('cheerio');
|
||||
const dayjs = require('dayjs');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
|
|
@ -32,4 +34,34 @@ module.exports = async (ctx) => {
|
|||
image: topic.squarePicture.picUrl || topic.squarePicture.middlePicUrl || topic.squarePicture.thumbnailUrl,
|
||||
item: common.topicDataHanding(data),
|
||||
};
|
||||
if (id === '553870e8e4b0cafb0a1bef68' || id === '55963702e4b0d84d2c30ce6f') {
|
||||
const promises = ctx.state.data.item.map(async (one) => {
|
||||
const item = { ...one };
|
||||
const regResult = /https:\/\/www.okjike.com\/medium\/[a-zA-Z0-9]*/.exec(item.description);
|
||||
if (regResult) {
|
||||
const newsUrl = regResult[0];
|
||||
const cache = ctx.cache.get(newsUrl);
|
||||
if (cache) {
|
||||
item.description = cache;
|
||||
} else {
|
||||
const { data } = await axios.get(newsUrl);
|
||||
const $ = cheerio.load(data);
|
||||
const upper = $('ul.main > li.item');
|
||||
const links = upper.find('a').map((_, ele) => $(ele).attr('href'));
|
||||
const texts = upper.find('span.text').map((_, ele) => $(ele).text());
|
||||
let description = '';
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
description += `${i + 1}、<a href="${links[i]}">${texts[i]}</a><br/>`;
|
||||
}
|
||||
if (description) {
|
||||
item.description = description;
|
||||
ctx.cache.set(newsUrl, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
item.title = `${topic.content} ${dayjs(new Date(one.pubDate)).format('MM月DD日')}`;
|
||||
return item;
|
||||
});
|
||||
ctx.state.data.item = await Promise.all(promises);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,32 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const dayjs = require('dayjs');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const category = ctx.params.category;
|
||||
|
||||
let title;
|
||||
let link;
|
||||
let path = category;
|
||||
switch (category) {
|
||||
case 'topic':
|
||||
title = '热门话题';
|
||||
link = 'https://readhub.cn/topics';
|
||||
break;
|
||||
case 'news':
|
||||
title = '科技动态';
|
||||
link = 'https://readhub.cn/news';
|
||||
break;
|
||||
case 'technews':
|
||||
title = '开发者资讯';
|
||||
link = 'https://readhub.cn/tech';
|
||||
break;
|
||||
case 'blockchain':
|
||||
title = '区块链快讯';
|
||||
link = 'https://readhub.cn/blockchain';
|
||||
break;
|
||||
case 'daily':
|
||||
title = '每日早报';
|
||||
link = 'https://readhub.cn/daily';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -29,22 +36,63 @@ module.exports = async (ctx) => {
|
|||
path = 'topic/daily';
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
const {
|
||||
data: { data },
|
||||
} = await axios({
|
||||
method: 'get',
|
||||
url: `https://api.readhub.cn/${path}`,
|
||||
});
|
||||
|
||||
const data = response.data;
|
||||
const promises = data.map(async (news) => {
|
||||
const id = news.id;
|
||||
if (category === 'topic' || category === 'daily') {
|
||||
const link = `https://readhub.cn/topic/${id}`;
|
||||
const cache = ctx.cache.get(link);
|
||||
if (cache) {
|
||||
return cache;
|
||||
}
|
||||
const { data } = await axios.get(`https://api.readhub.cn/topic/${id}`);
|
||||
let description = data.summary;
|
||||
if (data.newsArray) {
|
||||
description += '<br/><br/>媒体报道:';
|
||||
for (const one of data.newsArray) {
|
||||
description += `<br/>${dayjs(new Date(one.publishDate)).format('YY-MM-DD')} ${one.siteName}: <a href='${one.mobileUrl || one.url}'>${one.title}</a>`;
|
||||
}
|
||||
}
|
||||
if (data.timeline && data.timeline.topics) {
|
||||
let type = '相关事件';
|
||||
if (data.timeline.commonEntities && data.timeline.commonEntities.length > 0) {
|
||||
type = '事件追踪';
|
||||
}
|
||||
description += `<br/><br/>${type}:`;
|
||||
for (const one of data.timeline.topics) {
|
||||
description += `<br/>${dayjs(new Date(one.createdAt)).format('YY-MM-DD')} <a href='https://readhub.cn/topic/${one.id}'>${one.title.trim()}</a>`;
|
||||
}
|
||||
}
|
||||
const item = {
|
||||
title: data.title,
|
||||
description: description.replace(new RegExp('\n', 'g'), '<br/>'),
|
||||
pubDate: data.publishDate,
|
||||
guid: id,
|
||||
link,
|
||||
};
|
||||
ctx.cache.set(link, description);
|
||||
return item;
|
||||
} else {
|
||||
const description = news.summaryAuto || news.summary || news.title;
|
||||
return {
|
||||
title: news.title,
|
||||
description: description.replace(new RegExp('\n', 'g'), '<br/>'),
|
||||
pubDate: news.publishDate,
|
||||
guid: id,
|
||||
link: news.url || news.mobileUrl,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ctx.state.data = {
|
||||
title: `Readhub-${title}`,
|
||||
link: 'https://readhub.cn',
|
||||
item: data.data.map((item) => ({
|
||||
title: item.title,
|
||||
pubDate: new Date(item.publishDate || Date.now()).toUTCString(),
|
||||
description: item.summary || item.title,
|
||||
guid: `/${category}/${item.id}`,
|
||||
link: category === 'daily' ? `https://readhub.cn/topic/${item.id}` : item.url || item.newsArray[0].url,
|
||||
})),
|
||||
link,
|
||||
item: await Promise.all(promises),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
const axios = require('../../utils/axios');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
const link = 'https://tech.sina.com.cn/discovery/';
|
||||
const api = 'https://feed.sina.com.cn/api/roll/get?pageid=207&num=10&versionNumber=1.2.4&page=1&encode=utf-8&lid=';
|
||||
|
||||
const map = new Map([
|
||||
['zx', { title: '最新', id: '1795' }],
|
||||
['twhk', { title: '天文航空', id: '1796' }],
|
||||
['dwzw', { title: '动物植物', id: '1797' }],
|
||||
['zrdl', { title: '自然地理', id: '1798' }],
|
||||
['lskg', { title: '历史考古', id: '1799' }],
|
||||
['smyx', { title: '生命医学', id: '1800' }],
|
||||
['shbk', { title: '生活百科', id: '1801' }],
|
||||
['kjqy', { title: '科技前沿', id: '1802' }],
|
||||
]);
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const type = ctx.params.type;
|
||||
const id = map.get(type).id;
|
||||
const title = map.get(type).title;
|
||||
|
||||
const api_url = api + id;
|
||||
const response = await axios.get(api_url);
|
||||
const list = response.data.result.data;
|
||||
|
||||
const out = await Promise.all(
|
||||
list.map(async (data) => {
|
||||
const title = data.title;
|
||||
const date = data.intime * 1000;
|
||||
const itemUrl = data.url;
|
||||
|
||||
const response = await axios.get(itemUrl);
|
||||
const $ = cheerio.load(response.data);
|
||||
const description = $('.article').html();
|
||||
|
||||
const single = {
|
||||
title: title,
|
||||
link: itemUrl,
|
||||
description: description,
|
||||
pubDate: new Date(date).toUTCString(),
|
||||
};
|
||||
return Promise.resolve(single);
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${title}-新浪科技科学探索`,
|
||||
link: link,
|
||||
item: out,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
const axios = require('../../../utils/axios');
|
||||
const cheerio = require('cheerio');
|
||||
// const timezone = require('../../utils/timezone');
|
||||
const date = require('../../../utils/date');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { id } = ctx.params;
|
||||
|
|
@ -24,7 +24,7 @@ module.exports = async (ctx) => {
|
|||
link: `https://wemp.app${$(e)
|
||||
.find('.post-item__title')
|
||||
.attr('href')}`,
|
||||
pubDate: timezone(
|
||||
pubDate: date(
|
||||
`${year} ${$(e)
|
||||
.find('.post-item__date')
|
||||
.text()
|
||||
|
|
@ -34,11 +34,6 @@ module.exports = async (ctx) => {
|
|||
author,
|
||||
}));
|
||||
|
||||
// to be replaced
|
||||
function timezone(html, timezone) {
|
||||
return new Date(new Date(html).getTime() - 60 * 60 * 1000 * (timezone + new Date().getTimezoneOffset() / 60)).toUTCString();
|
||||
}
|
||||
|
||||
ctx.state.data = {
|
||||
title: `微信公众号 - ${author}`,
|
||||
link: `https://wemp.app/accounts/${id}/`,
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -10,7 +10,8 @@
|
|||
"docs:build": "vuepress build docs",
|
||||
"format": "eslint \"**/*.js\" --fix && prettier \"**/*.{js,json,md}\" --write",
|
||||
"lint": "eslint \"**/*.js\" && prettier-check \"**/*.{js,json,md}\"",
|
||||
"test": "npm run lint && NODE_ENV=test jest --coverage --runInBand --forceExit"
|
||||
"test": "npm run lint && NODE_ENV=test jest --coverage --runInBand --forceExit",
|
||||
"jest": "NODE_ENV=test jest --runInBand --forceExit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -34,7 +35,7 @@
|
|||
"eslint-config-prettier": "4.0.0",
|
||||
"eslint-plugin-prettier": "3.0.1",
|
||||
"jest": "24.0.0",
|
||||
"lint-staged": "8.1.0",
|
||||
"lint-staged": "8.1.1",
|
||||
"mockdate": "2.0.2",
|
||||
"nodemon": "1.18.9",
|
||||
"prettier": "1.16.1",
|
||||
|
|
@ -53,7 +54,7 @@
|
|||
"dayjs": "^1.7.7",
|
||||
"form-data": "^2.3.2",
|
||||
"git-rev-sync": "1.12.0",
|
||||
"googleapis": "36.0.0",
|
||||
"googleapis": "37.1.0",
|
||||
"he": "^1.1.1",
|
||||
"iconv-lite": "0.4.24",
|
||||
"imgur": "^0.3.1",
|
||||
|
|
@ -66,18 +67,18 @@
|
|||
"lru-cache": "5.1.1",
|
||||
"luxon": "^1.7.1",
|
||||
"markdown-it": "^8.4.2",
|
||||
"node-fetch": "^2.3.0",
|
||||
"path-to-regexp": "3.0.0",
|
||||
"pidusage": "^2.0.17",
|
||||
"plist": "^3.0.1",
|
||||
"puppeteer": "^1.10.0",
|
||||
"raven": "^2.6.4",
|
||||
"readall": "1.1.0",
|
||||
"redis": "2.8.0",
|
||||
"rss-parser": "3.6.2",
|
||||
"sharp": "^0.21.0",
|
||||
"socks-proxy-agent": "^4.0.1",
|
||||
"twit": "2.2.11",
|
||||
"winston": "3.2.0"
|
||||
"winston": "3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
|
|
|
|||
|
|
@ -12,26 +12,46 @@ async function checkBlock(response) {
|
|||
}
|
||||
|
||||
afterEach(() => {
|
||||
process.env.BLACKLIST = undefined;
|
||||
delete process.env.BLACKLIST;
|
||||
delete process.env.WHITELIST;
|
||||
jest.resetModules();
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('access-control', () => {
|
||||
it(`blacklist`, async () => {
|
||||
process.env.BLACKLIST = '/test/1,/test/2,233.233.233.233';
|
||||
process.env.BLACKLIST = '/test/1,233.233.233.233';
|
||||
server = require('../../lib/index').server;
|
||||
const request = supertest(server);
|
||||
|
||||
const response1 = await request.get('/test/1');
|
||||
checkBlock(response1);
|
||||
const response11 = await request.get('/test/1');
|
||||
checkBlock(response11);
|
||||
|
||||
const response2 = await request.get('/test/2');
|
||||
checkBlock(response2);
|
||||
const response12 = await request.get('/test/1').set('X-Forwarded-For', '233.233.233.233');
|
||||
checkBlock(response12);
|
||||
|
||||
const response31 = await request.get('/test/3');
|
||||
expect(response31.status).toBe(200);
|
||||
const response21 = await request.get('/test/2');
|
||||
expect(response21.status).toBe(200);
|
||||
|
||||
const response32 = await request.get('/test/3').set('X-Forwarded-For', '233.233.233.233');
|
||||
checkBlock(response32);
|
||||
const response22 = await request.get('/test/2').set('X-Forwarded-For', '233.233.233.233');
|
||||
checkBlock(response22);
|
||||
});
|
||||
|
||||
it(`whitelist`, async () => {
|
||||
process.env.WHITELIST = '/test/1,233.233.233.233';
|
||||
server = require('../../lib/index').server;
|
||||
const request = supertest(server);
|
||||
|
||||
const response11 = await request.get('/test/1');
|
||||
expect(response11.status).toBe(200);
|
||||
|
||||
const response12 = await request.get('/test/1').set('X-Forwarded-For', '233.233.233.233');
|
||||
expect(response12.status).toBe(200);
|
||||
|
||||
const response21 = await request.get('/test/2');
|
||||
checkBlock(response21);
|
||||
|
||||
const response22 = await request.get('/test/2').set('X-Forwarded-For', '233.233.233.233');
|
||||
expect(response22.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
const supertest = require('supertest');
|
||||
const { server } = require('../../lib/index');
|
||||
const request = supertest(server);
|
||||
const cheerio = require('cheerio');
|
||||
let gitHash;
|
||||
try {
|
||||
gitHash = require('git-rev-sync').short();
|
||||
} catch (e) {
|
||||
gitHash = (process.env.HEROKU_SLUG_COMMIT && process.env.HEROKU_SLUG_COMMIT.slice(0, 7)) || 'unknown';
|
||||
}
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('debug', () => {
|
||||
it(`debug`, async () => {
|
||||
await request.get('/test/1').set('X-Forwarded-For', '233.233.233.233');
|
||||
await request.get('/test/1').set('X-Forwarded-For', '233.233.233.233');
|
||||
await request.get('/test/1').set('X-Forwarded-For', '233.233.233.234');
|
||||
await request.get('/test/2').set('X-Forwarded-For', '233.233.233.233');
|
||||
await request.get('/test/2').set('X-Forwarded-For', '233.233.233.234');
|
||||
|
||||
const response = await request.get('/').set('X-Forwarded-For', '233.233.233.233');
|
||||
|
||||
const $ = cheerio.load(response.text);
|
||||
$('.debug-item').each((index, item) => {
|
||||
const key = $(item)
|
||||
.find('.debug-key')
|
||||
.html()
|
||||
.trim();
|
||||
const value = $(item)
|
||||
.find('.debug-value')
|
||||
.html()
|
||||
.trim();
|
||||
switch (key) {
|
||||
case 'git hash:':
|
||||
expect(value).toBe(gitHash);
|
||||
break;
|
||||
case '请求数:':
|
||||
expect(value).toBe('6');
|
||||
break;
|
||||
case '热门路由:':
|
||||
expect(value).toBe(`3 /test/1<br>2 /test/2<br>1 /<br>`);
|
||||
break;
|
||||
case '热门IP:':
|
||||
expect(value).toBe(`4 233.233.233.233<br>2 233.233.233.234<br>`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
const supertest = require('supertest');
|
||||
const { server } = require('../../lib/index');
|
||||
const request = supertest(server);
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
it(`error`, async () => {
|
||||
const response = await request.get('/test/0');
|
||||
expect(response.text).toMatch(/RSSHub 发生了一些意外: <pre>Error: Error test/);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
const supertest = require('supertest');
|
||||
const { server } = require('../../lib/index');
|
||||
const request = supertest(server);
|
||||
const Parser = require('rss-parser');
|
||||
const parser = new Parser();
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('filter', () => {
|
||||
it(`filter`, async () => {
|
||||
const response = await request.get('/test/1?filter=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(2);
|
||||
expect(parsed.items[0].title).toBe('Title4');
|
||||
expect(parsed.items[1].title).toBe('Title5');
|
||||
});
|
||||
|
||||
it(`filter_title`, async () => {
|
||||
const response = await request.get('/test/1?filter_title=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(1);
|
||||
expect(parsed.items[0].title).toBe('Title5');
|
||||
});
|
||||
|
||||
it(`filter_description`, async () => {
|
||||
const response = await request.get('/test/1?filter_description=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(1);
|
||||
expect(parsed.items[0].title).toBe('Title4');
|
||||
});
|
||||
|
||||
it(`filter_author`, async () => {
|
||||
const response = await request.get('/test/1?filter_author=DIYgod4|DIYgod5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(2);
|
||||
expect(parsed.items[0].title).toBe('Title4');
|
||||
expect(parsed.items[1].title).toBe('Title5');
|
||||
});
|
||||
|
||||
it(`filterout`, async () => {
|
||||
const response = await request.get('/test/1?filterout=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(3);
|
||||
expect(parsed.items[0].title).toBe('Title1');
|
||||
expect(parsed.items[1].title).toBe('Title2');
|
||||
expect(parsed.items[2].title).toBe('Title3');
|
||||
});
|
||||
|
||||
it(`filterout_title`, async () => {
|
||||
const response = await request.get('/test/1?filterout_title=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(4);
|
||||
expect(parsed.items[0].title).toBe('Title1');
|
||||
expect(parsed.items[1].title).toBe('Title2');
|
||||
expect(parsed.items[2].title).toBe('Title3');
|
||||
expect(parsed.items[3].title).toBe('Title4');
|
||||
});
|
||||
|
||||
it(`filterout_description`, async () => {
|
||||
const response = await request.get('/test/1?filterout_description=Item4|Title5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(4);
|
||||
expect(parsed.items[0].title).toBe('Title1');
|
||||
expect(parsed.items[1].title).toBe('Title2');
|
||||
expect(parsed.items[2].title).toBe('Title3');
|
||||
expect(parsed.items[3].title).toBe('Title5');
|
||||
});
|
||||
|
||||
it(`filterout_author`, async () => {
|
||||
const response = await request.get('/test/1?filterout_author=DIYgod4|DIYgod5');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(3);
|
||||
expect(parsed.items[0].title).toBe('Title1');
|
||||
expect(parsed.items[1].title).toBe('Title2');
|
||||
expect(parsed.items[2].title).toBe('Title3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('limit', () => {
|
||||
it(`limit`, async () => {
|
||||
const response = await request.get('/test/1?limit=3');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items.length).toBe(3);
|
||||
expect(parsed.items[0].title).toBe('Title1');
|
||||
expect(parsed.items[1].title).toBe('Title2');
|
||||
expect(parsed.items[2].title).toBe('Title3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tgiv', () => {
|
||||
it(`tgiv`, async () => {
|
||||
const response = await request.get('/test/1?tgiv=test');
|
||||
const parsed = await parser.parseString(response.text);
|
||||
expect(parsed.items[0].link).toBe(`https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F1&rhash=test`);
|
||||
expect(parsed.items[1].link).toBe(`https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F2&rhash=test`);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue