diff --git a/docs/other.md b/docs/other.md index ff0d3aecb5..8893105f19 100644 --- a/docs/other.md +++ b/docs/other.md @@ -729,6 +729,36 @@ type 为 all 时,category 参数不支持 cost 和 free +## 品玩 + +### 实时要闻 + + + +### 话题动态 + + + +内容类型 + +| 最新 | 最热 | +| ---- | ---- | +| 1 | 2 | + + + +### 用户 + + + +内容类型 + +| 文章 | 动态 | +| ------- | ----- | +| article | state | + + + ## 且听风吟福利 ### 分类 diff --git a/lib/router.js b/lib/router.js index 407107f8b7..867ecfbdbf 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1618,6 +1618,11 @@ router.get('/mofcom/article/:suffix', require('./routes/mofcom/article')); // 字幕库 router.get('/zimuku/:type?', require('./routes/zimuku/index')); +// 品玩 +router.get('/pingwest/status', require('./routes/pingwest/status')); +router.get('/pingwest/tag/:tag/:type', require('./routes/pingwest/tag')); +router.get('/pingwest/user/:uid/:type?', require('./routes/pingwest/user')); + // Hanime router.get('/hanime/video', require('./routes/hanime/video')); diff --git a/lib/routes/hpoi/info.js b/lib/routes/hpoi/info.js index 8d9b066be5..a3bf9a1f3f 100644 --- a/lib/routes/hpoi/info.js +++ b/lib/routes/hpoi/info.js @@ -38,6 +38,7 @@ module.exports = async (ctx) => { return { title: infoTitle, link, + category: infoType, description: [`类型:${typeName}`, infoTitle, `更新内容: ${infoType}`, ``].join('
'), }; }) diff --git a/lib/routes/pingwest/status.js b/lib/routes/pingwest/status.js new file mode 100644 index 0000000000..db4a978347 --- /dev/null +++ b/lib/routes/pingwest/status.js @@ -0,0 +1,43 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const baseUrl = 'https://www.pingwest.com'; + const url = `${baseUrl}/api/state/list`; + const response = await got(url, { + query: { + page: 1, + }, + headers: { + Referer: baseUrl, + }, + }); + const $ = cheerio.load(response.data.data.list); + const items = $('section.item') + .map((_, ele) => { + const timestamp = ele.attribs['data-t']; + const $item = cheerio.load(ele); + const rightNode = $item('.news-info'); + const tag = rightNode.find('.item-tag-list').text(); + const title = rightNode.find('.title').text(); + const link = rightNode + .find('a') + .last() + .attr('href'); + const description = rightNode.text(); + return { + title: title || tag, + link: link.startsWith('http') ? link : `https:${link}`, + description, + pubDate: new Date(timestamp * 1000).toUTCString(), + }; + }) + .get(); + + ctx.state.data = { + title: '品玩 - 实时要闻', + description: '品玩 - 实时要闻', + link: `${baseUrl}/status`, + item: items, + }; +}; diff --git a/lib/routes/pingwest/tag.js b/lib/routes/pingwest/tag.js new file mode 100644 index 0000000000..c29747df63 --- /dev/null +++ b/lib/routes/pingwest/tag.js @@ -0,0 +1,50 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const utils = require('./utils'); + +module.exports = async (ctx) => { + const { tag, type } = ctx.params; + const baseUrl = 'https://www.pingwest.com'; + const tagUrl = `${baseUrl}/tag/${tag}`; + const tagId = await ctx.cache.tryGet(`pingwest-tag-${tag}`, async () => { + const res = await got(encodeURI(tagUrl), { + headers: { + Referer: baseUrl, + }, + }); + const $ = cheerio.load(res.data); + const tagId = $('.tag-detail').attr('data-id'); + return tagId; + }); + const url = `${baseUrl}/api/tag_article_list`; + const response = await got(url, { + query: { + page: 1, + id: tagId, + type, + }, + headers: { + Referer: baseUrl, + }, + }); + const $ = cheerio.load(response.data.data.list); + const items = $('.item') + .map((_, ele) => { + const className = ele.attribs.class; + if (className.includes('state')) { + return utils.statusListParser(cheerio.load(ele))[0]; + } + if (className.includes('wire')) { + return utils.wireListParser(cheerio.load(ele))[0]; + } + return utils.articleListParser(cheerio.load(ele))[0]; + }) + .get(); + + ctx.state.data = { + title: `品玩 - ${tag}`, + description: `品玩 - ${tag}`, + link: tagUrl, + item: items, + }; +}; diff --git a/lib/routes/pingwest/user.js b/lib/routes/pingwest/user.js new file mode 100644 index 0000000000..81204ddd38 --- /dev/null +++ b/lib/routes/pingwest/user.js @@ -0,0 +1,55 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const utils = require('./utils'); + +module.exports = async (ctx) => { + const { uid, type = 'article' } = ctx.params; + const baseUrl = 'https://www.pingwest.com'; + const aimUrl = `${baseUrl}/user/${uid}/${type}`; + const { userName, realUid } = await ctx.cache.tryGet(`pingwest-user-info-${uid}`, async () => { + const res = await got(aimUrl, { + headers: { + Referer: baseUrl, + }, + }); + const $ = cheerio.load(res.data); + const userInfoNode = $('#J_userId'); + return { + userName: userInfoNode.text(), + realUid: userInfoNode.attr('data-user-id'), + }; + }); + const url = `${baseUrl}/api/user_data`; + const response = await got(url, { + query: { + page: 1, + user_id: realUid, + tab: type, + }, + headers: { + Referer: baseUrl, + }, + }); + const $ = cheerio.load(response.data.data.list); + + let item = []; + switch (type) { + case 'article': + item = utils.articleListParser($); + break; + case 'state': + item = utils.statusListParser($); + break; + } + + const typeToLabel = { + article: '文章', + state: '动态', + }; + ctx.state.data = { + title: `品玩 - ${userName} - ${typeToLabel[type]}`, + description: `品玩 - ${userName} - ${typeToLabel[type]}`, + link: aimUrl, + item, + }; +}; diff --git a/lib/routes/pingwest/utils.js b/lib/routes/pingwest/utils.js new file mode 100644 index 0000000000..730d3f0e16 --- /dev/null +++ b/lib/routes/pingwest/utils.js @@ -0,0 +1,79 @@ +const cheerio = require('cheerio'); + +const statusListParser = ($) => { + const items = $('.item') + .map((_, ele) => { + const timestamp = ele.attribs.pt; + const $item = cheerio.load(ele); + const mainNode = $item('.news-detail'); + const imgsStr = mainNode + .find('img') + .map((_, ele) => ``) + .get() + .join('
'); + const link = mainNode + .find('.content a') + .first() + .attr('href'); + const content = mainNode + .text() + .trim() + .replace(/展开全文$/, ''); + return { + title: content, + link: link.startsWith('http') ? link : `https:${link}`, + description: [content, imgsStr].filter((s) => !!s).join('
'), + pubDate: new Date(timestamp * 1000).toUTCString(), + }; + }) + .get(); + return items; +}; + +const articleListParser = ($) => { + const items = $('.item') + .map((_, ele) => { + const $item = cheerio.load(ele); + const timestamp = ele.attribs.pt; + const imgUrl = $item('.news-img img').attr('src'); + const titleNode = $item('.title').first(); + const title = titleNode.text(); + const link = titleNode.find('a').attr('href'); + const description = $item('.desc').text(); + const author = $item('.author a').text(); + return { + title, + link: link.startsWith('http') ? link : `https:${link}`, + description: [description, ``].join('
'), + author, + pubDate: new Date(timestamp * 1000).toUTCString(), + }; + }) + .get(); + return items; +}; + +const wireListParser = ($) => { + const items = $('.item') + .map((_, ele) => { + const $item = cheerio.load(ele); + const timestamp = ele.attribs.pt; + const titleNode = $item('.text').first(); + const title = titleNode.text(); + const link = titleNode.find('a').attr('href'); + return { + title, + link: link.startsWith('http') ? link : `https:${link}`, + description: title, + pubDate: new Date(timestamp * 1000).toUTCString(), + }; + }) + .get(); + return items; +}; + +module.exports = { + articleListParser, + statusListParser, + wireListParser, +};