Files
yenai-plugin/model/api/funApi.js
2023-04-28 16:29:17 +08:00

348 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import _ from 'lodash'
import md5 from 'md5'
import fetch from 'node-fetch'
import request from '../../lib/request/request.js'
import { puppeteer } from '../index.js'
import { xiurenTypeId, youDaoLangType, pandadiuType } from '../../constants/fun.js'
const API_ERROR = '出了点小问题,待会再试试吧'
let cheerio = null
export default new class {
constructor () {
this.xiurenTypeId = xiurenTypeId
}
async _importDependency () {
if (cheerio) return cheerio
cheerio = await import('cheerio')
.catch(() => {
throw Error('未检测到依赖cheerio请安装后再使用该功能安装命令pnpm add cheerio -w 或 pnpm install -P')
})
}
/** 有道翻译 */
async youdao (msg, to = 'auto', from = 'auto') {
if (to != 'auto') to = youDaoLangType.find(item => item.label == to)?.code
if (from != 'auto') from = youDaoLangType.find(item => item.label == from)?.code
if (!to || !from) return `未找到翻译的语种,支持的语言为:\n${youDaoLangType.map(item => item.label).join('')}\n示例:#翻译你好 - 自动翻译\n#日语翻译你好 - 指定翻译为语种\n#中文-日语翻译你好 - 指定原语言翻译为指定语言`
// 翻译结果为空的提示
const RESULT_ERROR = '找不到翻译结果'
// API 请求错误提示
const qs = (obj) => {
let res = ''
for (const [k, v] of Object.entries(obj)) { res += `${k}=${encodeURIComponent(v)}&` }
return res.slice(0, res.length - 1)
}
const appVersion = '5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4750.0'
const payload = {
from,
to,
bv: md5(appVersion),
client: 'fanyideskweb',
doctype: 'json',
version: '2.1',
keyfrom: 'fanyi.web',
action: 'FY_BY_DEFAULT',
smartresult: 'dict'
}
const headers = {
'Host': 'fanyi.youdao.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102',
'Referer': 'https://fanyi.youdao.com/',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'OUTFOX_SEARCH_USER_ID_NCOO=133190305.98519628; OUTFOX_SEARCH_USER_ID="2081065877@10.169.0.102";'
}
const api = 'https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
const key = 'Ygy_4c=r#e#4EX^NUGUc5'
const i = msg // 翻译的内容
const lts = '' + new Date().getTime()
const salt = lts + parseInt(String(10 * Math.random()), 10)
const sign = md5(payload.client + i + salt + key)
const postData = qs(Object.assign({ i, lts, sign, salt }, payload))
try {
let { errorCode, translateResult } = await fetch(api, {
method: 'POST',
body: postData,
headers
}).then(res => res.json()).catch(err => console.error(err))
if (errorCode != 0) return API_ERROR
translateResult = _.flattenDeep(translateResult)?.map(item => item.tgt).join('\n')
if (!translateResult) return RESULT_ERROR
return translateResult
} catch (err) {
logger.error(err)
return API_ERROR
}
}
/** 随机唱歌/唱鸭 */
async randomSinging () {
try {
const api = 'https://m.api.singduck.cn/user-piece/SoQJ9cKu61FJ1Vwc7'
let res = await fetch(api).then(res => res.text())
let JSONdara = JSON.parse(res.match(/<script id="__NEXT_DATA__" type="application\/json" crossorigin="anonymous">(.*?)<\/script>/)[1])
if (!JSONdara) return { error: API_ERROR }
let piece = _.sample(JSONdara.props.pageProps.pieces)
let { songName, lyric, audioUrl } = piece
if (!audioUrl) return { error: '找不到歌曲文件' }
return {
lyrics: `${songName}\n${lyric}`,
audioUrl: decodeURIComponent(audioUrl)
}
} catch (error) {
console.log(error)
return { error: API_ERROR }
}
}
/**
* @description: 黑丝屋
* @param {data.heisiType} type 类型
* @param {Number} page 页数
* @return {*}
*/
async heisiwu (type, page) {
await this._importDependency()
const url = `http://hs.heisiwu.com/${type}/page/${_.random(1, page)}`
const home = await request.get(url).then(res => res.text())
const href = _.sample(_.map(cheerio.load(home)('article > a'), (item) => item.attribs.href))
if (_.isEmpty(href)) throw Error('获取页面失败')
const details = await request.get(href).then(res => res.text())
const $ = cheerio.load(details)
const imgs = _.map($('.alignnone'), (item) => item.attribs.src)
if (_.isEmpty(imgs)) throw Error('获取图片失败')
const title = $('.article-content > p:nth-child(1)').text()
const msg = imgs.map(item => segment.image(item, undefined, undefined,
{
'Referer': 'http://hs.heisiwu.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.46'
})
)
return [title, ..._.take(msg, 30)]
}
async pandadiu (type = 'cos', keywords = '') {
await this._importDependency()
let domain = 'https://www.pandadiu.com'
const { id, page } = pandadiuType[type]
let homeUrl = `${domain}/list-${id}-${_.random(1, page)}.html`
if (keywords) {
homeUrl = `${domain}/index.php?m=search&c=index&a=init&typeid=1&siteid=1&q=${keywords}`
}
logger.debug('[Yenai-Plugin][acg]作品索引页:' + homeUrl)
const home = await request.get(homeUrl).then(res => res.text())
const href = _.sample(_.map(cheerio.load(home)('div.cos-list.clearfix > ul > a, div.cover.mod_imgLight > a'), item => item.attribs.href))
if (!href) throw Error('未找到结果')
logger.debug('[Yenai-Plugin][acg]图片详情页:' + domain + href)
const details = await request.get(domain + href).then(res => res.text())
const $ = cheerio.load(details)
const imgs = _.map($('div.con > p > img'), item =>
segment.image(item.attribs.src.includes('http')
? item.attribs.src
: domain + item.attribs.src
))
const title = $('div.title > h1').text()
return [
title,
..._.take(imgs, 30)
]
}
async mengdui (keywords, isSearch) {
await this._importDependency()
const domain = 'https://b6u8.com'
let href = ''
if (isSearch) {
const mengduipage = JSON.parse(await redis.get('yenai:mengduipage')) || {}
const randomPage = _.random(1, mengduipage[keywords] || 1)
const url = `${domain}/search.php?mdact=community&q=${keywords}&page=${randomPage}`
const home = await request.get(url).then(res => res.text())
const $ = cheerio.load(home)
href = _.sample(_.map($('div.md-wide > ul > li > a'), item => item.attribs.href))
if (!href) throw Error($('div.no-tips > p:nth-of-type(1)').text().trim())
const maxPage = $('div.pagebar.md-flex-wc.mb20 > a:not(:last-child)').length
mengduipage[keywords] = maxPage
await redis.set('yenai:mengduipage', JSON.stringify(mengduipage))
} else {
let random = keywords
if (!random) {
do {
random = _.random(1, 11687)
} while (
_.inRange(random, 7886, 10136)
)
}
href = `${domain}/post/${random}.html`
}
const details = await request.get(href).then(res => res.text())
const $ = cheerio.load(details)
const imgs = _.map($('div.md-text.mb20.f-16 > p > img'), item => segment.image(item.attribs.src))
const title = $('h1').text().trim()
const number = `序号:${href.match(/(\d+).html/)[1]}`
return [title, number, ..._.take(imgs, 30)]
}
async xiuren (type) {
await this._importDependency()
// 可扩展
let handleType = this.xiurenTypeId[type]
let homeUrl = `https://www.lisiku1.com/forum-${handleType.id}-${_.random(1, handleType.maxPage)}.html`
let html = await request.get(homeUrl).then(res => res.text())
let $ = cheerio.load(html)
let href = _.sample(
_.map(
$('#moderate > div > div.kind_show > div > div:nth-child(1) > a'),
(item) => item.attribs.href
)
)
let imgPageUrl = 'https://www.lisiku1.com/' + href
let imgPage = await request.get(imgPageUrl).then((res) =>
res.text()
)
let $1 = cheerio.load(imgPage)
let imgList = _.map(
$1(
'td > img'
), item => segment.image('https://www.lisiku1.com/' + item.attribs.src)
)
return imgList
}
async bgg (keyword) {
await this._importDependency()
let url = 'https://www.gstonegames.com/game/?hot_sort=1&keyword=' + encodeURIComponent(keyword)
const home = await request.get(url).then((res) => res.text())
const $ = cheerio.load(home)
// 获取集石第一个搜索结果的地址
const firstGameLink = $('.goods-list.fl').first().find('a').attr('href')
// 如果搜不到
if (!firstGameLink) {
const screenshot = await puppeteer.Webpage({ url })
return [
'集石搜索不到该游戏:',
`搜索地址:${url}`,
'\n以下是搜索页面的截图',
screenshot
]
}
// 拼出集石详情网址并访问
let href = `https://www.gstonegames.com${firstGameLink}`
logger.info(`集石详情网址:${href}`)
const detailshtml = await request.get(href).then((res) => res.text())
const details$ = cheerio.load(detailshtml)
// 获取游戏类型
const gametype = details$(
'.published:contains("分类信息") .part-left-title + ul li:nth-child(1) a'
)
.text()
.trim()
const gamemode = details$(
'.published:contains("分类信息") .part-left-title + ul li:nth-child(2) a'
)
.text()
.trim()
// const gamemove =details$('.published:contains("分类信息") .part-left-title + ul li:nth-child(3) a').text().trim();
// 获取BGG网址
const bgglink = details$('.published.who.matop15.mabot50')
.eq(2)
.find('a')
.attr('href')
// 如果搜不到
if (!bgglink) {
let url = href
const screenshot = await puppeteer.Webpage({ url })
return [
'集石该游戏页面无BGG信息',
`搜索地址:${url}`,
'\n以下是该游戏集石页面的截图',
screenshot
]
}
// 如果搜到了
logger.info(bgglink)
// 扒集石的数据
const gameName2 = details$('.details-title h2 a').text().trim()
logger.info(`游戏中文名字:${gameName2}`)
// 访问bgg
const bgghtml = await request.get(bgglink).then((res) => res.text())
const bgg$ = cheerio.load(bgghtml)
// 开扒
let scriptdataA = bgg$('script').eq(2).text()
let scriptdata = JSON.parse(
scriptdataA.substring(
scriptdataA.indexOf('GEEK.geekitemPreload = ') + 22,
scriptdataA.indexOf('GEEK.geekitemSettings = ') - 3
)
)
let {
minplayers,
maxplayers,
minplaytime,
maxplaytime,
minage
} = scriptdata.item
let avgweight = scriptdata.item.stats.avgweight.substring(0, 4)
let OverallRank = scriptdata.item.rankinfo[0].rank
const gameName1 = JSON.parse(
bgg$("script[type='application/ld+json']").text()
).name
const gameimg = JSON.parse(
bgg$("script[type='application/ld+json']").text()
).image
logger.info(`游戏英文名字:${gameName1}`)
// 回复
return [
`游戏中文名:${gameName2}\n`,
`游戏英文名:${gameName1}\n`,
`游戏类型:${gametype}\n`,
`游戏模式:${gamemode}\n`,
`BGG地址${bgglink}\n`,
`集石地址:${href}\n`,
`BGG当前总排名${OverallRank}\n`,
`支持游玩人数:${minplayers}-${maxplayers}\n`,
`大概游玩时间:${minplaytime}-${maxplaytime}分钟\n`,
`推荐年龄:${minage}+\n`,
`游戏重度:${avgweight}/5\n`,
segment.image(gameimg)
]
}
async coser () {
await this._importDependency()
const domain = 'https://t2cy.com'
const homeUrl = `${domain}/acg/cos/index_${_.random(1, 30)}.html`
logger.debug('[Yenai-Plugin][coser]作品索引页:' + homeUrl)
const home = await request.get(homeUrl).then(res => res.text())
const $ = cheerio.load(home)
const href = _.sample(
_.map(
$('body > div > div.content.hidden > ul.cy2-coslist.clr > li > div.showImg > a'),
(item) => item.attribs.href
)
)
if (!href) throw Error('未知错误')
logger.debug('[Yenai-Plugin][coser]图片详情页:' + domain + href)
const imgPage = await request.get(domain + href).then(res => res.text())
const $1 = cheerio.load(imgPage)
const imgList = _.map(
$1(
'body > div > div.content.pb20.clr > div.cy_cosCon > div.w.maxImg.tc > p > img'
), item => segment.image(_.includes(item.attribs.src, 'http') ? item.attribs.src : domain + (item.attribs['data-loadsrc'] || item.attribs.src))
)
const title = $1('h1').text().trim()
return [title, ..._.take(imgList, 20)]
}
}()