♻️ 分离fun.js代码

This commit is contained in:
yeyang
2024-03-11 20:18:43 +08:00
parent a4ebfe1717
commit 828c1b1e88
9 changed files with 494 additions and 525 deletions

View File

@@ -2,11 +2,8 @@ import _ from 'lodash'
import fetch from 'node-fetch'
import plugin from '../../../lib/plugins/plugin.js'
import { Config } from '../components/index.js'
import { faildsImgs, heisiType, pandadiuType, successImgs } from '../constants/fun.js'
import { QQApi, common, funApi, memes, uploadRecord } from '../model/index.js'
/** API请求错误文案 */
const API_ERROR = '❎ 出错辣,请稍后重试'
import { heisiType, pandadiuType, xiurenTypeId } from '../constants/fun.js'
import { common, funApi, uploadRecord } from '../model/index.js'
/** 开始执行文案 */
const START_EXECUTION = '椰奶产出中......'
@@ -24,7 +21,7 @@ _.forIn(picApis, (values, key) => {
const apiReg = new RegExp(`(${picApiKeys.join('|')}|^jktj$|^接口统计$)`)
export class Fun extends plugin {
constructor() {
constructor () {
super({
name: '椰奶娱乐',
event: 'message',
@@ -54,10 +51,6 @@ export class Fun extends plugin {
reg: '^#?coser$',
fnc: 'coser'
},
// {
// reg: `^#?来点(${Object.keys(heisiType).join('|')})$`,
// fnc: 'heisiwu'
// },
{
reg: '^#?铃声搜索',
fnc: 'lingsheng'
@@ -79,7 +72,7 @@ export class Fun extends plugin {
fnc: 'acg'
},
{
reg: `^#来点(${Object.keys(funApi.xiurenTypeId).join('|')})$`,
reg: `^#来点(${Object.keys(xiurenTypeId).join('|')})$`,
fnc: 'xiuren'
}
@@ -88,7 +81,7 @@ export class Fun extends plugin {
}
/** 随机唱鸭 */
async Sing(e) {
async Sing (e) {
let data = await funApi.randomSinging()
if (data.error) return e.reply(data.error)
await e.reply(await uploadRecord(data.audioUrl, 0, false))
@@ -96,7 +89,7 @@ export class Fun extends plugin {
}
/** 支付宝语音 */
async ZFB(e) {
async ZFB (e) {
let amount = parseFloat(e.msg.replace(/#|支付宝到账|元|圆/g, '').trim())
if (!/^\d+(\.\d{1,2})?$/.test(amount)) return e.reply('你觉得这河里吗!!', true)
@@ -108,7 +101,7 @@ export class Fun extends plugin {
}
/** 有道翻译 */
async youdao(e) {
async youdao (e) {
const msg = e.msg.match(/#(([\u4e00-\u9fa5]{2,6})-)?([\u4e00-\u9fa5]{2,6})?翻译(.*)/)
// 如果是在群聊中回复,则获取上一条消息作为翻译内容
if (e.source) {
@@ -125,121 +118,12 @@ export class Fun extends plugin {
}
/** 点赞 */
async thumbUp(e) {
if (e.msg.includes(``, ``, ``, ``, ``, ``)) {
this.do = ``
} else {
this.do = ``
}
/** 判断是赞自己还是赞别人 */
if (e.at && e.msg.includes(``, ``, ``, `TA`, `ta`, `Ta`)) {
/** 判断是否为好友 */
let isFriend = await (e.bot ?? Bot).fl.get(e.at)
let allowLikeByStrangers = Config.whole.Strangers_love
if (!isFriend && !allowLikeByStrangers) return e.reply(`不加好友不${this.do}🙄`, true)
/** 执行点赞 */
let n = 0
let failsMsg = `今天已经${this.do}过了,还搁这讨${this.do}呢!!!`
for (let i = 0; i < 10; i++) {
let res = null
try {
res = await new QQApi(e).thumbUp(e.at, 10)
} catch (error) {
logger.error(error)
return common.handleException(e, error)
}
logger.debug(`${e.logFnc}${e.at}点赞`, res)
if (res.code) {
if (res.code == 1) {
failsMsg = `${this.do}失败,请检查是否开启陌生人点赞或添加好友`
} else {
if (this.do == ``) {
failsMsg = res.msg.replace(/点赞/g, '超').replace('给', '超').replace('点', '').replace('个赞', '下')
} else {
failsMsg = res.msg
}
}
break
} else {
n += 10
}
}
let successMsg = `${e.at}${this.do}${n}下哦,记得回我~ ${isFriend ? `` : `(如${this.do}失败请添加好友)`}`
const avatar = `https://q1.qlogo.cn/g?b=qq&s=100&nk=${e.at}`
const successFn = _.sample(['ganyu', 'zan'])
/** 判断点赞是否成功 */
let msg = n > 0
? [
`\n${successMsg}`,
segment.image((await memes[successFn](avatar)) ||
_.sample(successImgs) + e.user_id)
]
: [
`\n${failsMsg}`,
segment.image((await memes.crawl(avatar)) ||
_.sample(faildsImgs) + e.user_id)
]
/** 回复 */
e.reply(msg, true, { at: e.at })
} else if (!e.msg.includes(``, ``, ``, `TA`, `ta`, `Ta`)) {
/** 判断是否为好友 */
let isFriend = await (e.bot ?? Bot).fl.get(e.user_id)
let allowLikeByStrangers = Config.whole.Strangers_love
if (!isFriend && !allowLikeByStrangers) return e.reply(`不加好友不${this.do}🙄`, true)
/** 执行点赞 */
let n = 0
let failsMsg = `今天已经${this.do}过了,还搁这讨${this.do}呢!!!`
for (let i = 0; i < 10; i++) {
let res = null
try {
res = await new QQApi(e).thumbUp(e.user_id, 10)
} catch (error) {
logger.error(error)
return common.handleException(e, error)
}
logger.debug(`${e.logFnc}${e.user_id}点赞`, res)
if (res.code) {
if (res.code == 1) {
failsMsg = `${this.do}失败,请检查是否开启陌生人点赞或添加好友`
} else {
if (this.do == ``) {
failsMsg = res.msg.replace(/点赞/g, '超').replace('给', '超').replace('点', '').replace('个赞', '下')
} else {
failsMsg = res.msg
}
}
break
} else {
n += 10
}
}
let successMsg = `给你${this.do}${n}下哦,记得回我~ ${isFriend ? `` : `(如${this.do}失败请添加好友)`}`
const avatar = `https://q1.qlogo.cn/g?b=qq&s=100&nk=${e.user_id}`
const successFn = _.sample(['ganyu', 'zan'])
/** 判断点赞是否成功 */
let msg = n > 0
? [
`\n${successMsg}`,
segment.image((await memes[successFn](avatar)) ||
_.sample(successImgs) + e.user_id)
]
: [
`\n${failsMsg}`,
segment.image((await memes.crawl(avatar)) ||
_.sample(faildsImgs) + e.user_id)
]
/** 回复 */
e.reply(msg, true, { at: true })
}
async thumbUp (e) {
await funApi.thumbUp(e)
}
// github
async GH(e) {
async GH (e) {
const api = 'https://opengraph.githubassets.com'
let reg = /github.com\/[a-zA-Z0-9-]{1,39}\/[a-zA-Z0-9_-]{1,100}(?:\/(?:pull|issues)\/\d+)?/
@@ -256,7 +140,7 @@ export class Fun extends plugin {
}
// coser
async coser(e) {
async coser (e) {
if (!common.checkSeSePermission(e)) return false
e.reply(START_EXECUTION)
@@ -266,7 +150,7 @@ export class Fun extends plugin {
}
// cos/acg搜索
async acg(e) {
async acg (e) {
if (!common.checkSeSePermission(e)) return false
e.reply(START_EXECUTION)
const reg = new RegExp(`^#(${Object.keys(pandadiuType).join('|')})?acg(.*)$`)
@@ -277,7 +161,7 @@ export class Fun extends plugin {
}
// 黑丝
async heisiwu(e) {
async heisiwu (e) {
if (!common.checkSeSePermission(e, 'sesepro')) return false
e.reply(START_EXECUTION)
@@ -289,7 +173,7 @@ export class Fun extends plugin {
}
// 萌堆
async mengdui(e) {
async mengdui (e) {
if (!common.checkSeSePermission(e, 'sesepro')) return false
// 开始执行
e.reply(START_EXECUTION)
@@ -299,7 +183,7 @@ export class Fun extends plugin {
.catch(err => common.handleException(e, err))
}
async xiuren(e) {
async xiuren (e) {
if (!common.checkSeSePermission(e, 'pro')) return false
// 开始执行
e.reply(START_EXECUTION)
@@ -309,41 +193,24 @@ export class Fun extends plugin {
}
// 铃声多多
async lingsheng(e) {
let msg = e.msg.replace(/#|铃声搜索/g, '')
let num = Math.ceil(Math.random() * 15)
if (num == 0) num = 1
let api = `http://xiaobai.klizi.cn/API/music/lingsheng.php?msg=${msg}&n=${num}`
let res = await fetch(api).then(res => res.json()).catch(err => logger.error(err))
if (!res) return e.reply(API_ERROR)
if (res.title == null && res.author == null) return e.reply('❎ 没有找到相关的歌曲哦~', true)
// async lingsheng (e) {
// let msg = e.msg.replace(/#|铃声搜索/g, '')
// let num = Math.ceil(Math.random() * 15)
// if (num == 0) num = 1
// let api = `http://xiaobai.klizi.cn/API/music/lingsheng.php?msg=${msg}&n=${num}`
// let res = await fetch(api).then(res => res.json()).catch(err => logger.error(err))
// if (!res) return e.reply(API_ERROR)
// if (res.title == null && res.author == null) return e.reply('❎ 没有找到相关的歌曲哦~', true)
await e.reply([
`标题:${res.title}\n`,
`作者:${res.author}`
])
await e.reply(await uploadRecord(res.aac, 0, false))
}
/** 半次元话题 */
async bcyTopic(e) {
let api = 'https://xiaobai.klizi.cn/API/other/bcy_topic.php'
let res = await fetch(api).then(res => res.json()).catch(err => logger.error(err))
if (!res) return e.reply(API_ERROR)
if (res.code != 200) return e.reply('❎ 出错辣' + JSON.stringify(res))
if (_.isEmpty(res.data)) return e.reply('请求错误!无数据,请稍后再试')
let msg = []
for (let i of res.data) {
if (!i.title || _.isEmpty(i.image)) continue
msg.push(i.title)
msg.push(i.image.map(item => segment.image(item)))
}
if (_.isEmpty(msg)) return this.bcyTopic(e)
common.getforwardMsg(e, msg)
}
// await e.reply([
// `标题:${res.title}\n`,
// `作者:${res.author}`
// ])
// await e.reply(await uploadRecord(res.aac, 0, false))
// }
// api大集合
async picture(e) {
async picture (e) {
let { sese, sesepro } = Config.getGroup(e.group_id)
if (!sese && !sesepro && !e.isMaster) return false
let key = 'yenai:apiAggregate:CD'

View File

@@ -428,17 +428,17 @@ export default class {
*/
async thumbUp (uid, times = 1) {
let core = this.Bot.icqq?.core
if (!core) try {
core = (await import('icqq')).core
} catch (error) {
const thumbUp = this.Bot.pickFriend(uid).thumbUp
if (!thumbUp) throw Error('当前协议端不支持点赞,详情查看\nhttps://gitee.com/TimeRainStarSky/Yunzai')
const res = { ...await thumbUp(times) }
if (res.retcode && !res.code)
res.code = res.retcode
if (res.message && !res.msg)
res.msg = res.message
return res
if (!core) {
try {
core = (await import('icqq')).core
} catch (error) {
const thumbUp = this.Bot.pickFriend(uid).thumbUp
if (!thumbUp) throw Error('当前协议端不支持点赞,详情查看\nhttps://gitee.com/TimeRainStarSky/Yunzai')
const res = { ...await thumbUp(times) }
if (res.retcode && !res.code) { res.code = res.retcode }
if (res.message && !res.msg) { res.msg = res.message }
return res
}
}
if (times > 20) { times = 20 }
let ReqFavorite

View File

@@ -1,351 +1,12 @@
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) {
logger.error(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 = bgg$(`meta[property='og:title']`).attr('content');
// 获取游戏图片URL
const gameimgLink = bgg$(`link[rel='preload'][as='image']:eq(1)`).attr('href');
// 游戏图片URL可能有多条这里取第一条
const gameimg = gameimgLink ? gameimgLink : null;
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)]
}
}()
import bgg from './funApi/bgg.js'
import youdao from './funApi/youdao.js'
import * as pageCrawling from './funApi/pageCrawling.js'
import randomSinging from './funApi/randomSinging.js'
import thumbUp from './funApi/thumbUp.js'
export default {
bgg,
youdao,
thumbUp,
randomSinging,
...pageCrawling
}

115
model/api/funApi/bgg.js Normal file
View File

@@ -0,0 +1,115 @@
import { _importDependency } from './utils.js'
import request from '../../../lib/request/request.js'
import { puppeteer } from '../../index.js'
export default async function bgg (keyword) {
let cheerio = await _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 = bgg$('meta[property=\'og:title\']').attr('content')
// 获取游戏图片URL
const gameimgLink = bgg$('link[rel=\'preload\'][as=\'image\']:eq(1)').attr('href')
// 游戏图片URL可能有多条这里取第一条
const gameimg = gameimgLink || null
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)
]
}

View File

@@ -0,0 +1,116 @@
import { pandadiuType, xiurenTypeId } from '../../../constants/fun.js'
import _ from 'lodash'
import { _importDependency } from './utils.js'
import request from '../../../lib/request/request.js'
export async function pandadiu (type = 'cos', keywords = '') {
let cheerio = await _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)
]
}
export async function mengdui (keywords, isSearch) {
let cheerio = await _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)]
}
export async function xiuren (type) {
let cheerio = await _importDependency()
// 可扩展
let handleType = 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
}
export async function coser () {
let cheerio = await _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)]
}

View File

@@ -0,0 +1,22 @@
import { API_ERROR } from './utils.js'
import _ from 'lodash'
/** 随机唱歌/唱鸭 */
export default async function 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) {
logger.error(error)
return { error: API_ERROR }
}
}

116
model/api/funApi/thumbUp.js Normal file
View File

@@ -0,0 +1,116 @@
import _ from 'lodash'
import { QQApi, memes, common } from '../../index.js'
import { Config } from '../../../components/index.js'
import { successImgs, faildsImgs } from '../../../constants/fun.js'
export default async function thumbUp (e) {
let _do = '赞'
if (e.msg.includes('超', '操', '草', '抄', '吵', '炒')) {
_do = '超'
}
/** 判断是赞自己还是赞别人 */
if (e.at && e.msg.includes('他', '她', '它', 'TA', 'ta', 'Ta')) {
/** 判断是否为好友 */
let isFriend = await (e.bot ?? Bot).fl.get(e.at)
let allowLikeByStrangers = Config.whole.Strangers_love
if (!isFriend && !allowLikeByStrangers) return e.reply(`不加好友不${_do}🙄`, true)
/** 执行点赞 */
let n = 0
let failsMsg = `今天已经${_do}过了,还搁这讨${_do}呢!!!`
for (let i = 0; i < 10; i++) {
let res = null
try {
res = await new QQApi(e).thumbUp(e.at, 10)
} catch (error) {
logger.error(error)
return common.handleException(e, error)
}
logger.debug(`${e.logFnc}${e.at}点赞`, res)
if (res.code) {
if (res.code == 1) {
failsMsg = `${_do}失败,请检查是否开启陌生人点赞或添加好友`
} else {
if (_do == '超') {
failsMsg = res.msg.replace(/点赞/g, '超').replace('给', '超').replace('点', '').replace('个赞', '下')
} else {
failsMsg = res.msg
}
}
break
} else {
n += 10
}
}
let successMsg = `${e.at}${_do}${n}下哦,记得回我~ ${isFriend ? '' : `(如${_do}失败请添加好友)`}`
const avatar = `https://q1.qlogo.cn/g?b=qq&s=100&nk=${e.at}`
const successFn = _.sample(['ganyu', 'zan'])
/** 判断点赞是否成功 */
let msg = n > 0
? [
`\n${successMsg}`,
segment.image((await memes[successFn](avatar)) ||
_.sample(successImgs) + e.user_id)
]
: [
`\n${failsMsg}`,
segment.image((await memes.crawl(avatar)) ||
_.sample(faildsImgs) + e.user_id)
]
/** 回复 */
e.reply(msg, true, { at: e.at })
} else if (!e.msg.includes('他', '她', '它', 'TA', 'ta', 'Ta')) {
/** 判断是否为好友 */
let isFriend = await (e.bot ?? Bot).fl.get(e.user_id)
let allowLikeByStrangers = Config.whole.Strangers_love
if (!isFriend && !allowLikeByStrangers) return e.reply(`不加好友不${_do}🙄`, true)
/** 执行点赞 */
let n = 0
let failsMsg = `今天已经${_do}过了,还搁这讨${_do}呢!!!`
for (let i = 0; i < 10; i++) {
let res = null
try {
res = await new QQApi(e).thumbUp(e.user_id, 10)
} catch (error) {
logger.error(error)
return common.handleException(e, error)
}
logger.debug(`${e.logFnc}${e.user_id}点赞`, res)
if (res.code) {
if (res.code == 1) {
failsMsg = `${_do}失败,请检查是否开启陌生人点赞或添加好友`
} else {
if (_do == '超') {
failsMsg = res.msg.replace(/点赞/g, '超').replace('给', '超').replace('点', '').replace('个赞', '下')
} else {
failsMsg = res.msg
}
}
break
} else {
n += 10
}
}
let successMsg = `给你${_do}${n}下哦,记得回我~ ${isFriend ? '' : `(如${_do}失败请添加好友)`}`
const avatar = `https://q1.qlogo.cn/g?b=qq&s=100&nk=${e.user_id}`
const successFn = _.sample(['ganyu', 'zan'])
/** 判断点赞是否成功 */
let msg = n > 0
? [
`\n${successMsg}`,
segment.image((await memes[successFn](avatar)) ||
_.sample(successImgs) + e.user_id)
]
: [
`\n${failsMsg}`,
segment.image((await memes.crawl(avatar)) ||
_.sample(faildsImgs) + e.user_id)
]
/** 回复 */
e.reply(msg, true, { at: true })
}
}

12
model/api/funApi/utils.js Normal file
View File

@@ -0,0 +1,12 @@
export const API_ERROR = '出了点小问题,待会再试试吧'
let cheerio = null
export async function _importDependency () {
if (cheerio) return cheerio
cheerio = await import('cheerio')
.catch(() => {
throw Error('未检测到依赖cheerio请安装后再使用该功能安装命令pnpm add cheerio -w 或 pnpm install -P')
})
return cheerio
}

View File

@@ -0,0 +1,60 @@
import { youDaoLangType } from '../../../constants/fun.js'
import md5 from 'md5'
import { API_ERROR } from './utils.js'
import _ from 'lodash'
/** 有道翻译 */
export default async function 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
}
}