This commit is contained in:
🌌
2024-04-02 20:02:49 +08:00
parent e6989268fc
commit 319e65965c
30 changed files with 163 additions and 376 deletions

View File

@@ -14,7 +14,8 @@ module.exports = {
redis: true,
logger: true,
plugin: true,
segment: true
segment: true,
ReplyError: true
},
rules: {
'eqeqeq': ['off'],

View File

@@ -1,3 +1,7 @@
# 1.4.1
* 删除匿名相关功能
# 1.4.0
* 增加开发依赖`eslint` `eslint-config-standard` `eslint-plugin-import` `eslint-plugin-jsdoc` `eslint-plugin-promise` `husky` `lint-staged`
* 使用`husky``lint-staged`进行提交时eslint检测和自动格式化

View File

@@ -280,13 +280,12 @@ Tip具体可使用 **#椰奶群管帮助** 查看
1. 功能仅限内部交流与小范围使用请勿将Yunzai-Bot及Yenai-Plugin用于任何以盈利为目的的场景.
2. 图片与其他素材均来自于网络,仅供交流学习使用,如有侵权请联系,会立即删除.
3. 本插件为防止滥用,内置部分开发者权限,包括但不限于`更新``设置`的权限,通常情况下不会被使用,如介意请勿使用本插件.
## 联系方式 <img src="https://media.giphy.com/media/VgCDAzcKvsR6OM0uWg/giphy.gif" width="50">
🐧746659424
💬:(暂无)
💬:914247840
❤️:[打赏](https://yenai.trss.me/donate.html)

View File

@@ -13,7 +13,6 @@ const OtherCfgType = {
const SeSeCfgType = {
涩涩: 'sese',
涩涩pro: 'sesepro',
匿名: 'anonymous',
代理: {
name: 'proxy',
key: 'switchProxy'
@@ -181,12 +180,11 @@ export class Admin extends plugin {
async SeSe_Settings (e) {
let set = setu.getSeSeConfig(e)
let { proxy, pixiv, bika } = Config
let { sese, sesepro, anonymous } = Config.getGroup(e.group_id)
let { sese: _sese, sesepro: _sesepro, anonymous: _anonymous } = Config.getConfig('group')[e.group_id] ?? {}
let { sese, sesepro } = Config.getGroup(e.group_id)
let { sese: _sese, sesepro: _sesepro } = Config.getConfig('group')[e.group_id] ?? {}
let data = {
sese: getStatus(sese, _sese),
sesepro: getStatus(sesepro, _sesepro),
anonymous: getStatus(anonymous, _anonymous),
r18: getStatus(set.r18),
cd: Number(set.cd),
recall: set.recall ? set.recall : '无',

View File

@@ -44,10 +44,6 @@ export class GroupAdmin extends plugin {
reg: '^#(设置|取消)管理(\\d+)?$',
fnc: 'SetAdmin'
},
{
reg: '^#(允许|禁止|开启|关闭)匿名$',
fnc: 'AllowAnony'
},
{
reg: '^#发群公告',
fnc: 'AddAnnounce'
@@ -248,15 +244,6 @@ export class GroupAdmin extends plugin {
type ? e.reply(`已经把「${name}」设置为管理啦!!`) : e.reply(`${name}」的管理已经被我吃掉啦~`)
}
// 匿名
async AllowAnony (e) {
if (!common.checkPermission(e, 'admin', 'admin')) { return true }
let type = /(允许|开启)匿名/.test(e.msg)
let res = await e.group.allowAnony(type)
if (!res) return e.reply('❎ 未知错误', true)
type ? e.reply('已把匿名开启了哦,可以藏起来了~') : e.reply('已关闭匿名,小贼们不准藏了~')
}
// 发群公告
async AddAnnounce (e) {
if (!common.checkPermission(e, 'admin', 'admin')) { return true }

View File

@@ -109,11 +109,7 @@ export class NewGroupBannedWords extends plugin {
*/
#mute (time) {
const e = this.e
if (e.anonymous) {
e.group.muteAnony(e.anonymous.flag, time)
} else {
e.member.mute(time)
}
e.member.mute(time)
}
/**

View File

@@ -195,7 +195,7 @@ export class NewPixiv extends plugin {
if (ispro && !await this._Authentication(e, 'sesepro', false)) return
await Pixiv.pximg(ispro)
.then(res => ispro ? common.recallSendForwardMsg(e, [res]) : common.recallsendMsg(e, res, false, { anony: true }))
.then(res => ispro ? common.recallSendForwardMsg(e, [res]) : common.recallsendMsg(e, res, false))
.catch(err => common.handleException(e, err))
}

View File

@@ -1,19 +1,5 @@
import plugin from '../../../lib/plugins/plugin.js'
import { createRequire } from 'module'
import _ from 'lodash'
import { Restart } from '../../other/restart.js'
import common from '../lib/common/common.js'
const require = createRequire(import.meta.url)
const { exec, execSync } = require('child_process')
// 是否在更新中
let uping = false
/**
* 处理插件更新
*/
export class Update extends plugin {
import { update as Update } from '../../other/update.js'
export class YenaiUpdate extends plugin {
constructor () {
super({
name: '椰奶更新插件',
@@ -28,221 +14,10 @@ export class Update extends plugin {
})
}
/**
* rule - 更新椰奶插件
* @param e
*/
async update (e) {
if (!common.checkPermission(e, 'master')) return true
/** 检查是否正在更新中 */
if (uping) {
await this.reply('已有命令更新中..请勿重复操作')
return
}
/** 检查git安装 */
if (!(await this.checkGit())) return
const isForce = this.e.msg.includes('强制')
/** 执行更新 */
await this.runUpdate(isForce)
/** 是否需要重启 */
if (this.isUp) {
// await this.reply("更新完毕,请重启云崽后生效")
setTimeout(() => this.restart(), 2000)
}
}
restart () {
new Restart(this.e).restart()
}
/**
* 椰奶插件更新函数
* @param {boolean} isForce 是否为强制更新
*/
async runUpdate (isForce) {
const _path = './plugins/yenai-plugin/'
let command = `git -C ${_path} pull --no-rebase`
if (isForce) {
command = `git -C ${_path} reset --hard origin && ${command}`
this.e.reply('正在执行强制更新操作,请稍等')
} else {
this.e.reply('正在执行更新操作,请稍等')
}
/** 获取上次提交的commitId用于获取日志时判断新增的更新日志 */
this.oldCommitId = await this.getcommitId('yenai-plugin')
uping = true
let ret = await this.execSync(command)
uping = false
if (ret.error) {
logger.mark(`${this.e.logFnc} 更新失败:椰奶插件`)
this.gitErr(ret.error, ret.stdout)
return false
}
/** 获取插件提交的最新时间 */
let time = await this.getTime('yenai-plugin')
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(ret.stdout)) {
await this.reply(`椰奶插件已经是最新版本\n最后更新时间:${time}`)
} else {
await this.reply(`椰奶插件\n最后更新时间:${time}`)
this.isUp = true
/** 获取椰奶组件的更新日志 */
let log = await this.getLog('yenai-plugin')
await this.reply(log)
}
logger.mark(`${this.e.logFnc} 最后更新时间:${time}`)
return true
}
/**
* 获取椰奶插件的更新日志
* @param {string} plugin 插件名称
*/
async getLog (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -20 --oneline --pretty=format:"%h||[%cd] %s" --date=format:"%m-%d %H:%M"`
let logAll
try {
logAll = await execSync(cm, { encoding: 'utf-8' })
} catch (error) {
logger.error(error.toString())
this.reply(error.toString())
}
if (!logAll) return false
logAll = logAll.split('\n')
let log = []
for (let str of logAll) {
str = str.split('||')
if (str[0] == this.oldCommitId) break
if (str[1].includes('Merge branch')) continue
log.push(str[1])
}
let line = log.length
log = log.join('\n\n')
if (log.length <= 0) return ''
let end = ''
end =
'更多详细信息请前往gitee查看\nhttps://gitee.com/yeyang52/yenai-plugin/blob/master/CHANGELOG.md'
let forwardMsg = [
`椰奶插件更新日志,共${line}`, log, end
]
log = await common.getforwardMsg(this.e, forwardMsg, {
shouldSendMsg: false
})
return log
}
/**
* 获取上次提交的commitId
* @param {string} plugin 插件名称
*/
async getcommitId (plugin = '') {
let cm = `git -C ./plugins/${plugin}/ rev-parse --short HEAD`
let commitId = await execSync(cm, { encoding: 'utf-8' })
commitId = _.trim(commitId)
return commitId
}
/**
* 获取本次更新插件的最后一次提交时间
* @param {string} plugin 插件名称
*/
async getTime (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -1 --oneline --pretty=format:"%cd" --date=format:"%m-%d %H:%M"`
let time = ''
try {
time = await execSync(cm, { encoding: 'utf-8' })
time = _.trim(time)
} catch (error) {
logger.error(error.toString())
time = '获取时间失败'
}
return time
}
/**
* 处理更新失败的相关函数
* @param {string} err
* @param {string} stdout
*/
async gitErr (err, stdout) {
let msg = '更新失败!'
let errMsg = err.toString()
stdout = stdout.toString()
if (errMsg.includes('Timed out')) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接超时:${remote}`)
return
}
if (/Failed to connect|unable to access/g.test(errMsg)) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接失败:${remote}`)
return
}
if (errMsg.includes('be overwritten by merge')) {
await this.reply(
msg +
`存在冲突:\n${errMsg}\n` +
'请解决冲突后再更新,或者执行#强制更新,放弃本地修改'
)
return
}
if (stdout.includes('CONFLICT')) {
await this.reply([
msg + '存在冲突\n',
errMsg,
stdout,
'\n请解决冲突后再更新或者执行#强制更新,放弃本地修改'
])
return
}
await this.reply([errMsg, stdout])
}
/**
* 异步执行git相关命令
* @param {string} cmd git命令
*/
async execSync (cmd) {
return new Promise((resolve, reject) => {
exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
resolve({ error, stdout, stderr })
})
})
}
/**
* 检查git是否安装
*/
async checkGit () {
let ret = await execSync('git --version', { encoding: 'utf-8' })
if (!ret || !ret.includes('git version')) {
await this.reply('请先安装git')
return false
}
return true
async update (e = this.e) {
e.msg = `#${e.msg.includes('强制') ? '强制' : ''}更新yenai-plugin`
const up = new Update(e)
up.e = e
return up.update()
}
}

View File

@@ -17,5 +17,5 @@ statusTask: true
#如果出现内存异常的情况可将此配置项开启,如果打开后报错请将监控任务关闭
statusPowerShellStart: false
#背景图片api 默认https://api.suyanw.cn/api/comic2
backdrop: "https://api.suyanw.cn/api/comic2"
#背景图片api 默认 https://api.sretna.cn/layout/pe.php
backdrop: "https://api.sretna.cn/layout/pe.php"

View File

@@ -24,4 +24,3 @@ state: false #状态
deltime: 600 #删除缓存
sesepro: false #涩涩增强
renderScale: 100 #渲染精度
anonymous: false #匿名

View File

@@ -15,6 +15,12 @@ if (!global.segment) {
}
}
global.ReplyError = class ReplyError extends Error {
constructor (message) {
super(message)
this.name = 'ReplyError'
}
}
// 加载监听事件
const eventsPath = './plugins/yenai-plugin/apps/events'
const events = fs.readdirSync(eventsPath)

9
lib/common/error.js Normal file
View File

@@ -0,0 +1,9 @@
// 直接回复错误类型
global.ReplyError = class ReplyError extends Error {
constructor (message) {
super(message)
this.name = 'ReplyError'
}
}
throw new ReplyError('a')

View File

@@ -33,7 +33,6 @@ export default class {
* @param {boolean} [options.isxml] - 处理卡片
* @param {boolean} [options.xmlTitle] - XML 标题
* @param {boolean} [options.oneMsg] - 用于只有一条消息,不用再转成二维数组
* @param {boolean | import("icqq").Anonymous} [options.anony] - 匿名消息若为true则发送匿名消息
* @param {boolean} [options.shouldSendMsg] - 是否直接发送消息true为直接发送否则返回需要发送的消息
* @returns {Promise<import("icqq").MessageRet|import("icqq").XmlElem|import("icqq").JsonElem>} 消息发送结果的Promise对象
*/
@@ -44,11 +43,10 @@ export default class {
isxml,
xmlTitle,
oneMsg,
anony,
shouldSendMsg = true
} = {}) {
let forwardMsg = []
if (_.isEmpty(message)) throw Error('[Yenai-Plugin][sendforwardMsg][Error]发送的转发消息不能为空')
if (_.isEmpty(message)) throw new ReplyError('[Yenai-Plugin][sendforwardMsg][Error]发送的转发消息不能为空')
let add = (msg) => forwardMsg.push(
{
message: msg,
@@ -84,7 +82,6 @@ export default class {
}
if (shouldSendMsg) {
let msgRes = await this.reply(e, forwardMsg, false, {
anony,
fkmsg,
recallMsg
})
@@ -103,15 +100,13 @@ export default class {
* @param {object} data 其他参数
* @param {number} data.recallMsg 撤回时间
* @param {boolean} data.fkmsg 风控消息
* @param {boolean | import("icqq").Anonymous} data.anony 匿名消息
* @param {boolean | number} data.at 是否艾特该成员
* @returns {Promise<import("icqq").MessageRet>} 返回发送消息后的结果对象
*/
async reply (e, msg, quote, {
recallMsg = 0,
fkmsg = '',
at = false,
anony
at = false
} = {}) {
if (at && e.isGroup) {
let text = ''
@@ -136,15 +131,7 @@ export default class {
let msgRes = null
// 发送消息
if (e.isGroup) {
// 判断是否开启匿名
if (anony) {
let getAnonyInfo = await e.group.getAnonyInfo()
if (!getAnonyInfo.enable) {
e.reply('[警告]该群未开启匿名,请启用匿名再使用匿名功能')
anony = false
}
}
msgRes = await e.group.sendMsg(msg, quote ? e : undefined, anony)
msgRes = await e.group.sendMsg(msg, quote ? e : undefined)
} else {
msgRes = await e.reply(msg, quote)
if (!msgRes) await e.reply(fkmsg || '消息发送失败,可能被风控')
@@ -167,17 +154,42 @@ export default class {
* @param {object} data 其他参数
* @param {number} data.recallMsg 撤回时间
* @param {boolean} data.fkmsg 风控消息
* @param {boolean | import("icqq").Anonymous} data.anony 匿名消息
* @returns {Promise<import("icqq").MessageRet>}
*/
async recallsendMsg (e, msg, quote, data = {}) {
let recallMsg = setu.getRecallTime(e.group_id)
let anony = Config.getGroup(e.group_id).anonymous
let msgRes = this.reply(e, msg, quote, {
recallMsg,
anony,
...data
})
return msgRes
}
/**
* 转发消息并根据权限撤回
* @async
* @param {object} e - 反馈的对象
* @param {string | object} msg - 要发送的消息字符串或对象
* @param {object} [data] - 附加的数据对象
* @param {number} [data.recallMsg] - 消息撤回时间
* @param {object} [data.info] - 附加消息信息
* @param {string} [data.info.nickname] - 用户昵称
* @param {number} [data.info.user_id] - 用户ID
* @param {boolean} [data.isxml] - 是否特殊处理转发消息
* @param {string} [data.xmlTitle] - XML 标题
* @returns {Promise<any>} - Promise 对象,返回函数 `getforwardMsg()` 的返回值
*/
async recallSendForwardMsg (e, msg, data = {}) {
let recalltime = setu.getRecallTime(e.group_id)
return await this.getforwardMsg(e, msg, {
recallMsg: recalltime,
info: {
nickname: '🐔🏀',
user_id: 2854196306
},
isxml: true,
xmlTitle: e.logFnc + e.msg,
...data
})
}
}

View File

@@ -14,6 +14,13 @@ class HTTPResponseError extends Error {
}
}
class RequestError extends Error {
constructor (message) {
super(message)
this.name = 'RequestError'
}
}
const checkStatus = response => {
if (response.ok) {
// response.status >= 200 && response.status < 300
@@ -65,8 +72,8 @@ export default new class {
return res
} catch (err) {
logger.error(err)
throw Error(
`Request Get Error${err.message.match(/reason:(.*)/i) || err.message}`
throw RequestError(
`Get Error${err.message.match(/reason:(.*)/)?.[1] || err.message}`
)
}
}
@@ -120,8 +127,8 @@ export default new class {
return res
} catch (err) {
logger.error(err)
throw Error(
`Request Post Errorreason${err.message.match(/reason:(.*)/)[1]}`
throw RequestError(
`Post Errorreason${err.message.match(/reason:(.*)/)?.[1] || err.message}`
)
}
}

View File

@@ -47,10 +47,10 @@ export default new (class {
.then(res => res.json())
.catch(err => {
logger.error(err)
throw Error(`bika search Error${err.message.match(/reason:(.*)/i) || err.message}`)
throw new ReplyError(`bika search Error${err.message.match(/reason:(.*)/i) || err.message}`)
})
let { docs, total, page: pg, pages } = res.data.comics
if (total == 0) throw Error(`未找到作品,换个${type.alias[0]}试试吧`)
if (total == 0) throw new ReplyError(`未找到作品,换个${type.alias[0]}试试吧`)
this.searchCaching = docs
let msg = [
`共找到${total}个关于「${keyword}${type.alias[0]}的作品`,
@@ -87,9 +87,9 @@ export default new (class {
.then((res) => res.json())
.catch(err => {
logger.error(err)
throw Error(`bika comicPage Error${err.message.match(/reason:(.*)/i) || err.message}`)
throw new ReplyError(`bika comicPage Error${err.message.match(/reason:(.*)/i) || err.message}`)
})
if (res.error) throw Error(res.message)
if (res.error) throw new ReplyError(res.message)
this.idNext = {
id, page, order
}
@@ -103,9 +103,9 @@ export default new (class {
}
async viewComicPage (num) {
if (!this.searchCaching) throw Error('请先搜索后再使用此命令')
if (!this.searchCaching) throw new ReplyError('请先搜索后再使用此命令')
let id = this.searchCaching[num]._id
if (!id) throw Error('未获取到目标作品请使用id进行查看')
if (!id) throw new ReplyError('未获取到目标作品请使用id进行查看')
return this.comicPage(id)
}
@@ -117,7 +117,7 @@ export default new (class {
* @throws {Error} - 如果未找到上一个id则抛出异常
*/
async next (type = 'comicPage') {
if (!this.idNext) throw Error('未找到上一个id')
if (!this.idNext) throw new ReplyError('未找到上一个id')
let { id, page, order } = this.idNext
if (type == 'chapter') {
order++
@@ -140,9 +140,9 @@ export default new (class {
.then((res) => res.json())
.catch(err => {
logger.error(err)
throw Error(`bika categories Error${err.message.match(/reason:(.*)/i) || err.message}`)
throw new ReplyError(`bika categories Error${err.message.match(/reason:(.*)/i) || err.message}`)
})
if (res.error) throw Error(res.message)
if (res.error) throw new ReplyError(res.message)
res = res.data.categories.filter(item => !item.isWeb)
await redis.set(key, JSON.stringify(res), { EX: 43200 })
}
@@ -167,9 +167,9 @@ export default new (class {
.then((res) => res.json())
.catch(err => {
logger.error(err)
throw Error(`bika comicDetail Error${err.message.match(/reason:(.*)/i) || err.message}`)
throw new ReplyError(`bika comicDetail Error${err.message.match(/reason:(.*)/i) || err.message}`)
})
if (res.error) throw Error(res.message)
if (res.error) throw new ReplyError(res.message)
let {
_id, title, description, author, chineseTeam, categories, tags, pagesCount, epsCount, finished, totalLikes, totalViews, totalComments, thumb
} = res.data.comic

View File

@@ -27,7 +27,7 @@ export default new class {
) {
let data = Data.readJSON(`${groupId}.json`, this.root)
if (!data.bannedWords) data.bannedWords = {}
if (data.bannedWords[words]) throw Error(`❎ 违禁词${words}已存在`)
if (data.bannedWords[words]) throw new ReplyError(`❎ 违禁词${words}已存在`)
// 翻转对象
let matchTypeMapMirr = _.invert(this.matchTypeMap)
let penaltyTypeMapMirr = _.invert(this.penaltyTypeMap)
@@ -48,7 +48,7 @@ export default new class {
delBannedWords (groupId, words) {
let data = Data.readJSON(`${groupId}.json`, this.root)
if (!data.bannedWords[words]) throw Error(`❎ 违禁词${words}不存在`)
if (!data.bannedWords[words]) throw new ReplyError(`❎ 违禁词${words}不存在`)
delete data.bannedWords[words]
this.dataCach.delete(groupId)
Data.writeJSON(`${groupId}.json`, data, this.root)
@@ -57,7 +57,7 @@ export default new class {
queryBannedWords (groupId, words) {
let data = Data.readJSON(`${groupId}.json`, this.root)
if (!data.bannedWords[words]) throw Error(`❎ 违禁词${words}不存在`)
if (!data.bannedWords[words]) throw new ReplyError(`❎ 违禁词${words}不存在`)
let { matchType, penaltyType } = data.bannedWords[words]
return {
...data.bannedWords[words],

View File

@@ -15,7 +15,7 @@ async function importCheerio () {
try {
cheerio = await import('cheerio')
} catch (e) {
throw Error('未检测到依赖cheerio请安装后再使用Ascii2D搜图安装命令pnpm add cheerio -w 或 pnpm install -P')
throw new ReplyError('未检测到依赖cheerio请安装后再使用Ascii2D搜图安装命令pnpm add cheerio -w 或 pnpm install -P')
}
}
@@ -28,14 +28,14 @@ export default async function doSearch (url) {
const { ascii2dUsePuppeteer, ascii2dResultMaxQuantity } = Config.picSearch
const callApi = ascii2dUsePuppeteer ? callAscii2dUrlApiWithPuppeteer : callAscii2dUrlApi
let ret = await callApi(url)
if (!ret) throw Error('Ascii2D搜图请求失败')
if (!ret) throw new ReplyError('Ascii2D搜图请求失败')
const colorURL = ret.url
if (!colorURL.includes('/color/')) {
const $ = cheerio.load(ret.data, { decodeEntities: false })
logger.error('[error] ascii2d url:', colorURL)
logger.debug(ret.data)
let isCloudflare = ret.data.includes('cloudflare') ? '绕过Cloudflare盾失败' : false
throw Error(`Ascii2D搜索失败错误原因${isCloudflare || $('.container > .row > div:first-child > p').text().trim()}`)
throw new ReplyError(`Ascii2D搜索失败错误原因${isCloudflare || $('.container > .row > div:first-child > p').text().trim()}`)
}
const bovwURL = colorURL.replace('/color/', '/bovw/')
let bovwDetail = await (ascii2dUsePuppeteer ? getAscii2dWithPuppeteer(bovwURL) : request.cfGet(bovwURL))
@@ -47,7 +47,7 @@ export default async function doSearch (url) {
}
let colorData = (await parse(ret.data)).slice(0, ascii2dResultMaxQuantity)
let bovwData = (await parse(bovwDetail.data)).slice(0, ascii2dResultMaxQuantity)
if (_.isEmpty(colorData)) throw Error('Ascii2D数据获取失败')
if (_.isEmpty(colorData)) throw new ReplyError('Ascii2D数据获取失败')
let mapfun = item => [
Config.picSearch.hideImg ? '' : segment.image(item.image),
`${item.info}\n`,
@@ -72,7 +72,7 @@ const callAscii2dUrlApi = async (imgUrl) => {
let res = await request.cfGet(`${domain}/search/url/${imgUrl}`).catch(
err => {
if (err.stack?.includes('legacy sigalg disallowed or unsupported')) {
throw Error(`Error Tls版本过低 请尝试将配置文件的cfTLSVersion字段改为TLS1.2\n详情请参考https://www.yenai.ren/faq.html#openssl-%E9%94%99%E8%AF%AF\n错误信息:${err.stack}`)
throw new ReplyError(`Error Tls版本过低 请尝试将配置文件的cfTLSVersion字段改为TLS1.2\n详情请参考https://www.yenai.ren/faq.html#openssl-%E9%94%99%E8%AF%AF\n错误信息:${err.stack}`)
} else {
throw err
}

View File

@@ -10,9 +10,9 @@ import Ascii2D from './ascii2d.js'
export default async function doSearch (url) {
let res = await getSearchResult(url)
logger.debug('SauceNAO result:', res)
if (res.header.status != 0) throw Error('SauceNAO搜图错误信息' + res.header.message?.replace(/<.*?>/g, ''))
if (res.header.status != 0) throw new ReplyError('SauceNAO搜图错误信息' + res.header.message?.replace(/<.*?>/g, ''))
let format = sagiri(res)
if (_.isEmpty(format)) throw Error('SauceNAO搜图无数据')
if (_.isEmpty(format)) throw new ReplyError('SauceNAO搜图无数据')
let msgMap = async item => [
`SauceNAO (${item.similarity}%)\n`,
@@ -61,7 +61,7 @@ export default async function doSearch (url) {
async function getSearchResult (imgURL, db = 999) {
logger.debug(`saucenao [${imgURL}]}`)
let api_key = Config.picSearch.SauceNAOApiKey
if (!api_key) throw Error('未配置SauceNAOApiKey无法使用SauceNAO搜图请在 https://saucenao.com/user.php?page=search-api 进行获取,请用指令:#设置SauceNAOapiKey <apikey> 进行添加')
if (!api_key) throw new ReplyError('未配置SauceNAOApiKey无法使用SauceNAO搜图请在 https://saucenao.com/user.php?page=search-api 进行获取,请用指令:#设置SauceNAOapiKey <apikey> 进行添加')
return await request.get('https://saucenao.com/search.php', {
params: {
api_key,
@@ -75,7 +75,7 @@ async function getSearchResult (imgURL, db = 999) {
timeout: 60000
}).then(res => {
if (res.status === 429) {
throw Error('SauceNAO搜图 搜索次数已达单位时间上限,请稍候再试')
throw new ReplyError('SauceNAO搜图 搜索次数已达单位时间上限,请稍候再试')
} else {
return res.json()
}

View File

@@ -9,7 +9,7 @@ import _ from 'lodash'
*/
export default async function doSearch (imgURL) {
let result = await getSearchResult(imgURL)
if (result.error) throw Error(result.error)
if (result.error) throw new ReplyError(result.error)
let {
result: [{
@@ -21,7 +21,7 @@ export default async function doSearch (imgURL) {
// image // 预览图片
}]
} = result
if (_.isEmpty(result)) throw Error('未获取到相关信息')
if (_.isEmpty(result)) throw new ReplyError('未获取到相关信息')
similarity = (similarity * 100).toFixed(2) // 相似度
const time = (() => {
const s = Math.floor(from)

View File

@@ -19,7 +19,7 @@ export default new class Pixiv {
if (!this.PixivClient?.auth) {
await this.PixivClient.login()
}
if (!this.PixivClient.auth?.user) throw Error('❎ 未获取到登录信息')
if (!this.PixivClient.auth?.user) throw new ReplyError('❎ 未获取到登录信息')
const { profile_image_urls: { px_170x170 }, id, name, account, mail_address, is_premium, x_restrict } = this.PixivClient.auth.user
return [
await this._requestPixivImg(px_170x170),
@@ -67,7 +67,7 @@ export default new class Pixiv {
} else {
res = await request.get(`${this.domain}/illust`, { params }).then(res => res.json())
}
if (res.error) throw Error(res.error?.user_message || '无法获取数据')
if (res.error) throw new ReplyError(res.error?.user_message || '无法获取数据')
let illust = this._format(res.illust)
let { id, title, user, tags, total_bookmarks, total_view, url, create_date, x_restrict, illust_ai_type } = illust
let msg = [
@@ -90,7 +90,7 @@ export default new class Pixiv {
} else {
linkmsg.push(`https://pixiv.re/${id}.jpg`)
}
throw Error(linkmsg.join('\n'))
throw new ReplyError(linkmsg.join('\n'))
}
let img = await Promise.all(url.map(async item => await this._requestPixivImg(item)))
return { msg, img }
@@ -114,13 +114,13 @@ export default new class Pixiv {
// r18处理
if (r18) {
let R18 = this.ranktype[mode].r18
if (!R18) throw Error('该排行没有不适合所有年龄段的分类哦~')
if (!R18) throw new ReplyError('该排行没有不适合所有年龄段的分类哦~')
type = R18.type
pageSizeAll = R18.total
}
// 总页数
let pageAll = Math.ceil(pageSizeAll / 30)
if (page > pageAll) throw Error('哪有那么多图片给你辣(•̀へ •́ ╮ )')
if (page > pageAll) throw new ReplyError('哪有那么多图片给你辣(•̀へ •́ ╮ )')
if (!date) date = moment().subtract(moment().utcOffset(9).hour() >= 12 ? 1 : 2, 'days').format('YYYY-MM-DD')
@@ -135,8 +135,8 @@ export default new class Pixiv {
} else {
res = await request.get(`${this.domain}/rank`, { params }).then(res => res.json())
}
if (res.error) throw Error(res.error.message)
if (_.isEmpty(res.illusts)) throw Error('暂无数据,请等待榜单更新哦(。-ω-)zzz')
if (res.error) throw new ReplyError(res.error.message)
if (_.isEmpty(res.illusts)) throw new ReplyError('暂无数据,请等待榜单更新哦(。-ω-)zzz')
let illusts = await Promise.all(res.illusts.map(async (item, index) => {
let list = this._format(item)
@@ -186,11 +186,11 @@ export default new class Pixiv {
offset: (page - 1) * 30
}
let res = await request.get(api, { params }).then(res => res.json())
if (res.data.count == 0) throw Error('呜呜呜,人家没有找到相关的插画(ó﹏ò。)')
if (res.data.count == 0) throw new ReplyError('呜呜呜,人家没有找到相关的插画(ó﹏ò。)')
let pageall = Math.ceil(res.data.count / 30)
if (page > pageall) throw Error('啊啊啊淫家给不了你那么多辣d(ŐдŐ๑)')
if (page > pageall) throw new ReplyError('啊啊啊淫家给不了你那么多辣d(ŐдŐ๑)')
let list = [
`当前为第${page}页,共${pageall}页,本页共${res.data.rows.length}张,总共${res.data.count}`
@@ -230,8 +230,8 @@ export default new class Pixiv {
} else {
res = await request.get(`${this.domain}/search`, { params }).then(res => res.json())
}
if (res.error) throw Error(res.error.message)
if (_.isEmpty(res.illusts)) throw Error('宝~没有数据了哦(๑>︶<)و')
if (res.error) throw new ReplyError(res.error.message)
if (_.isEmpty(res.illusts)) throw new ReplyError('宝~没有数据了哦(๑>︶<)و')
let sortIllusts = _.orderBy(res.illusts, 'total_bookmarks', 'desc')
let illusts = []
let filterNum = 0
@@ -252,7 +252,7 @@ export default new class Pixiv {
await this._requestPixivImg(image_urls.large)
])
}
if (_.isEmpty(illusts)) throw Error('该页全为涩涩内容已全部过滤(#/。\#)')
if (_.isEmpty(illusts)) throw new ReplyError('该页全为涩涩内容已全部过滤(#/。\#)')
return [
`本页共${NowNum}${filterNum ? `,过滤${filterNum}` : ''}\n可尝试使用 "#tagpro搜图${tag}${page - 0 + 1}页" 翻页\n无数据则代表无下一页`,
@@ -272,7 +272,7 @@ export default new class Pixiv {
res = await fetch(`${this.domain}/tags`).then(res => res.json())
}
if (!res.trend_tags) throw Error('呜呜呜,没有获取到数据(๑ १д१)')
if (!res.trend_tags) throw new ReplyError('呜呜呜,没有获取到数据(๑ १д१)')
let list = []
for (let i of res.trend_tags) {
@@ -310,7 +310,7 @@ export default new class Pixiv {
}
}).then(res => res.json())
}
if (_.isEmpty(wordlist.user_previews)) throw Error('呜呜呜人家没有找到这个淫d(ŐдŐ๑)')
if (_.isEmpty(wordlist.user_previews)) throw new ReplyError('呜呜呜人家没有找到这个淫d(ŐдŐ๑)')
keyword = wordlist.user_previews[0].user.id
}
const params = {
@@ -324,9 +324,9 @@ export default new class Pixiv {
res = await request.get(`${this.domain}/member_illust`, { params }).then(res => res.json())
}
if (res.error) throw Error(res.error.message)
if (res.error) throw new ReplyError(res.error.message)
// 没有作品直接返回信息
if (_.isEmpty(res.illusts)) throw Error(page >= 2 ? '这一页没有作品辣(>人<;)' : 'Σ(っ °Д °;)っ这个淫居然没有作品')
if (_.isEmpty(res.illusts)) throw new ReplyError(page >= 2 ? '这一页没有作品辣(>人<;)' : 'Σ(っ °Д °;)っ这个淫居然没有作品')
let illusts = []
let filter = 0
@@ -346,7 +346,7 @@ export default new class Pixiv {
await this._requestPixivImg(url[0])
])
}
if (_.isEmpty(illusts)) throw Error('该页全为涩涩内容已全部过滤(#/。\#)')
if (_.isEmpty(illusts)) throw new ReplyError('该页全为涩涩内容已全部过滤(#/。\#)')
let { id: uid, name, profile_image_urls } = res.user
return [
[
@@ -378,8 +378,8 @@ export default new class Pixiv {
} else {
user = await request.get(`${this.domain}/search_user`, { params }).then(res => res.json())
}
if (user.error) throw Error(user.error.message)
if (_.isEmpty(user.user_previews)) throw Error('呜呜呜人家没有找到这个淫d(ŐдŐ๑)')
if (user.error) throw new ReplyError(user.error.message)
if (_.isEmpty(user.user_previews)) throw new ReplyError('呜呜呜人家没有找到这个淫d(ŐдŐ๑)')
let msg = await Promise.all(user.user_previews.slice(0, 10).map(async (item, index) => {
let { id, name, profile_image_urls } = item.user
@@ -410,7 +410,7 @@ export default new class Pixiv {
async vilipixRandomImg (limit) {
let api = `https://www.vilipix.com/api/v1/picture/recommand?limit=${limit}&offset=${_.random(1, 700)}`
let res = await request.get(api).then(res => res.json())
if (!res.data || !res.data.rows) throw Error('呜呜呜,没拿到瑟瑟的图片(˃ ⌑ ˂ഃ )')
if (!res.data || !res.data.rows) throw new ReplyError('呜呜呜,没拿到瑟瑟的图片(˃ ⌑ ˂ഃ )')
return res.data.rows.map(item => {
let { picture_id, title, regular_url, tags, like_total } = item
return [
@@ -437,8 +437,8 @@ export default new class Pixiv {
} else {
res = await request.get(`${this.domain}/related`, { params }).then(res => res.json())
}
if (res.error) throw Error(res.error.user_message)
if (_.isEmpty(res.illusts)) throw Error('呃...没有数据(•ิ_•ิ)')
if (res.error) throw new ReplyError(res.error.user_message)
if (_.isEmpty(res.illusts)) throw new ReplyError('呃...没有数据(•ิ_•ิ)')
let illusts = []
let filter = 0
@@ -458,7 +458,7 @@ export default new class Pixiv {
await this._requestPixivImg(image_urls.large)
])
}
if (_.isEmpty(illusts)) throw Error('啊啊啊!!!居然全是瑟瑟哒不给你看(*/ω\*)')
if (_.isEmpty(illusts)) throw new ReplyError('啊啊啊!!!居然全是瑟瑟哒不给你看(*/ω\*)')
return [
`Pid:${pid}的相关作品,共${res.illusts.length}${filter ? `,过滤${filter}` : ''}`,

View File

@@ -15,9 +15,9 @@ export default class PixivApi {
async login () {
if (!this.refresh_token) {
throw Error('[Yenai][Pixiv] 未配置refresh_token刷新令牌')
throw new ReplyError('[Yenai][Pixiv] 未配置refresh_token刷新令牌')
}
const response = login(this.refresh_token)
const response = await login(this.refresh_token)
this.access_token = response.access_token
this.refresh_token = response.refresh_token
this.auth = response
@@ -30,7 +30,7 @@ export default class PixivApi {
} catch (error) {
if (this._once) {
this._once = false
throw error
throw new ReplyError()
}
await this.login()
this._once = true

View File

@@ -19,8 +19,8 @@ export const headers = {
export async function login (refresh_token) {
const local_time = moment().format()
const headers = {
...this.headers,
let _headers = {
...headers,
'X-Client-Time': local_time,
'X-Client-Hash': md5(`${local_time}${HASH_SECRET}`)
}
@@ -32,9 +32,9 @@ export async function login (refresh_token) {
}
const { response, error } = await request.post('https://oauth.secure.pixiv.net/auth/token', {
data,
headers
headers: _headers
}).then(res => res.json())
if (error) throw Error(`[Yenai][Pixiv]login Error Response: ${error}`)
if (error) throw new ReplyError(`[Yenai][Pixiv]login Error Response: ${error}`)
if (response.access_token) {
const { id, name, account } = this.auth.user
logger.info(`[Yenai][Pixiv]login ${logger.yellow(`${name}(${id}) @${account}`)} ${logger.green('success')}`)

View File

@@ -1,6 +1,5 @@
import { formatDuration } from '../../tools/index.js'
import { Version, Plugin_Name } from '../../components/index.js'
import moment from 'moment'
import os from 'os'
import { status } from '../../constants/other.js'
import { createRequire } from 'module'
@@ -10,7 +9,6 @@ export default async function getBotState (botList) {
const defaultAvatar = `../../../../../plugins/${Plugin_Name}/resources/state/img/default_avatar.jpg`
const BotName = Version.name
const systime = formatDuration(os.uptime(), 'dd天hh小时mm分', false)
const calendar = moment().format('YYYY-MM-DD HH:mm:ss')
const dataPromises = botList.map(async (i) => {
const bot = Bot[i]
@@ -40,10 +38,10 @@ export default async function getBotState (botList) {
<div class="header">
<h1>${nickname}</h1>
<hr noshade>
<p> ${BotName} 已运行 ${runTime}</p>
<p>${onlineStatus}(${platform}) | ${botVersion}</p>
<p>收${recv || 0} | 发${sent || 0} | 图片${screenshot || 0} | 好友${friendQuantity} | 群${groupQuantity} | 群员${groupMemberQuantity}</p>
<p>${BotName} 已运行 ${runTime} | 系统运行 ${systime}</p>
<p>${calendar} | Node.js ${process.version} | ${process.platform} ${process.arch}</p>
<p>Node.js ${process.version} | ${process.platform} ${process.arch} | 系统运行 ${systime}</p>
</div>
</div>
</div>`

View File

@@ -19,7 +19,7 @@ export async function pandadiu (type = 'cos', 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('未找到结果')
if (!href) throw new ReplyError('未找到结果')
logger.debug('[Yenai-Plugin][acg]图片详情页:' + domain + href)
const details = await request.get(domain + href).then(res => res.text())
const $ = cheerio.load(details)
@@ -50,7 +50,7 @@ export async function mengdui (keywords, isSearch) {
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())
if (!href) throw new ReplyError($('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))
@@ -119,7 +119,7 @@ export async function coser () {
(item) => item.attribs.href
)
)
if (!href) throw Error('未知错误')
if (!href) throw new ReplyError('未知错误')
logger.debug('[Yenai-Plugin][coser]图片详情页:' + domain + href)
const imgPage = await request.get(domain + href).then(res => res.text())
const $1 = cheerio.load(imgPage)

View File

@@ -21,7 +21,7 @@ export default async function thumbUp (e) {
/** 判断是否为好友 */
let isFriend = await (e.bot ?? Bot).fl.get(userId)
let allowLikeByStrangers = Config.whole.Strangers_love
if (!isFriend && !allowLikeByStrangers) return e.reply(`不加好友不${_do}🙄`, true)
if (!isFriend && !allowLikeByStrangers) { return (e.message?.[0]?.text == '#全部赞我') ? false : e.reply(`不加好友不${_do}🙄`, true) }
/** 执行点赞 */
let n = 0
let failsMsg = `今天已经${_do}过了,还搁这讨${_do}呢!!!`
@@ -60,14 +60,16 @@ export default async function thumbUp (e) {
segment.image((await memes[successFn](avatar)) ||
_.sample(successImgs) + userId)
]
: [
: (e.message?.[0]?.text == '#全部赞我')
? []
: [
`\n${failsMsg}`,
segment.image((await memes.crawl(avatar)) ||
_.sample(faildsImgs) + userId)
]
]
/** 回复 */
e.reply(msg, true, { at: userId })
if (msg.length) { return e.reply(msg, true, { at: userId }) }
}
class ThumbUpApi {
@@ -114,7 +116,7 @@ class ThumbUpApi {
async origThumbUp (uid, times) {
const friend = this.Bot.pickFriend(uid)
if (!friend?.thumbUp) throw Error('当前协议端不支持点赞,详情查看\nhttps://gitee.com/TimeRainStarSky/Yunzai')
if (!friend?.thumbUp) throw new ReplyError('当前协议端不支持点赞,详情查看\nhttps://gitee.com/TimeRainStarSky/Yunzai')
const res = { ...await friend.thumbUp(times) }
if (res.retcode && !res.code) { res.code = res.retcode }
if (res.message && !res.msg) { res.msg = res.message }

View File

@@ -9,7 +9,7 @@ export async function _importDependency () {
if (cheerio) return cheerio
cheerio = await import('cheerio')
.catch(() => {
throw Error('未检测到依赖cheerio请安装后再使用该功能安装命令pnpm add cheerio -w 或 pnpm install -P')
throw new ReplyError('未检测到依赖cheerio请安装后再使用该功能安装命令pnpm add cheerio -w 或 pnpm install -P')
})
return cheerio
}

View File

@@ -54,7 +54,7 @@ export default new class setu {
excludeAI
}
let result = await request.post(api, { data: parans }).then(res => res.json())
if (_.isEmpty(result.data)) throw Error('没有找到相关的tag')
if (_.isEmpty(result.data)) throw new ReplyError('没有找到相关的tag')
// 消息
return await Promise.all(result.data.map(async item => {
let { pid, title, tags, author, r18, urls, aiType } = item

View File

@@ -124,14 +124,6 @@
</div>
<div class="cfg-desc">可选值:低质量,中等质量,高质量,原图</div>
</li>
<li class="cfg-li">
<div class="cfg-line">
匿名
<span class="cfg-hint">#椰奶设置匿名 + 开启/关闭</span>
{{@anonymous}}
</div>
<div class="cfg-desc">群聊使用匿名发送</div>
</li>
</ul>
</div>
{{/block}}

View File

@@ -7,7 +7,7 @@
:root {
--high-color: #d73403;
--medium-color: #ffa500;
--low-color: #90ee90;
--low-color: #87CEEB;
}
.container {
@@ -35,7 +35,7 @@ body {
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border-radius: 10px;
border-radius: 27px;
border: 1px solid rgba(255, 255, 255, 0.18);
font-weight: 700;
}
@@ -120,7 +120,7 @@ hr {
.cpu {
width: 100px;
height: 100px;
background: #eee;
background: rgba(238, 238, 238, 0.6);
position: relative;
border-radius: 50%;
}
@@ -169,7 +169,7 @@ hr {
transform: translate(-50%, -50%);
color: #999;
font-size: 20px;
background: white;
background: rgba(255, 255, 255, 0.5);
}
.memory li {
@@ -186,11 +186,13 @@ hr {
margin: 0 5px;
position: relative;
border-radius: 5px;
overflow: hidden;
}
.memory .progress .current {
background: #90ee90;
height: 25px;
border-radius: 5px;
border-radius: 0 10px 10px 0;
}
.memory .progress .word {
position: absolute;

View File

@@ -39,16 +39,6 @@ export default function formatDuration (time, format, repair = true) {
return format(timeObj)
}
if (typeof format === 'string') {
format = format
.replace(/dd/g, day)
.replace(/hh/g, timeObj.hour)
.replace(/mm/g, timeObj.minute)
.replace(/ss/g, timeObj.second)
return format
}
if (format == 'default') {
let result = ''
@@ -67,5 +57,15 @@ export default function formatDuration (time, format, repair = true) {
return result
}
if (typeof format === 'string') {
format = format
.replace(/dd/g, day)
.replace(/hh/g, timeObj.hour)
.replace(/mm/g, timeObj.minute)
.replace(/ss/g, timeObj.second)
return format
}
return timeObj
}