diff --git a/apps/groupAdmin/groupAdmin.js b/apps/groupAdmin/groupAdmin.js index d472585..ad58795 100644 --- a/apps/groupAdmin/groupAdmin.js +++ b/apps/groupAdmin/groupAdmin.js @@ -12,9 +12,7 @@ const TimeUnitReg = Object.keys(Time_unit).join("|") const noactivereg = new RegExp(`^#(查看|清理|确认清理|获取)(${Numreg})个?(${TimeUnitReg})没发言的人(第(${Numreg})页)?$`) /** 我要自闭正则 */ const Autisticreg = new RegExp(`^#?我要(自闭|禅定)(${Numreg})?个?(${TimeUnitReg})?$`, "i") -/** 获取定时任务 */ -const redisTask = await Ga.getRedisMuteTask() || false - +Ga.loadRedisMuteTask() export class GroupAdmin extends plugin { constructor() { super({ @@ -100,7 +98,6 @@ export class GroupAdmin extends plugin { } ] }) - this.task = redisTask } get Bot() { diff --git a/components/Config.js b/components/Config.js index 6de0217..576ea84 100644 --- a/components/Config.js +++ b/components/Config.js @@ -3,9 +3,6 @@ import chokidar from "chokidar" import fs from "node:fs" import YamlReader from "./YamlReader.js" import cfg from "../../../lib/config/config.js" -import loader from "../../../lib/plugins/loader.js" -import _ from "lodash" -import moment from "moment" const Path = process.cwd() const Plugin_Name = "yenai-plugin" @@ -227,24 +224,6 @@ class Config { } } - async change_picApi() { - let tmp = {} - - logger.debug("[Yenai-Plugin]api接口修改,重载fun.js") - tmp = await import(`../apps/fun.js?${moment().format("x")}`) - - _.forEach(tmp, (p) => { - /* eslint-disable new-cap */ - let plugin = new p() - for (let i in loader.priority) { - if (loader.priority[i].key == Plugin_Name && loader.priority[i].name == "椰奶娱乐") { - loader.priority[i].class = p - loader.priority[i].priority = plugin.priority - } - } - }) - } - async change_pixiv() { let pixiv = (await import("../model/index.js")).Pixiv let PixivApi = (await import("../model/Pixiv/api.js")).default diff --git a/config.default.jsonc b/config.default.jsonc deleted file mode 100644 index a2f273d..0000000 --- a/config.default.jsonc +++ /dev/null @@ -1,246 +0,0 @@ -{ - // 是否自动按照 config.default.json 来更新 config.json,可以自动向 config.json 添加新增的配置项以及同步说明注释, - // 但 config.json 中你自己写的注释以及不在 config.default.json 中的配置项都会被丢弃,第一次启用前建议先备份一下你的 config.json - "autoUpdateConfig": false, - // 陌生人点赞 - "strangers_love": false, - "sese": false, - "sesepro": false, - // 渲染精度 - "renderScale": 100, - // 通知相关设置 - "notice": { - "notificationsAll": false, // 全部通知 - "deltime": 600, // 删除缓存 - "default": { - // 群通知类 - "groupMessage": false, // 群消息 - "grouptemporaryMessage": false, //群临时消息 - "groupRecall": false, // 群撤回 - "groupInviteRequest": false, // 群邀请 - "groupAdminChange": false, // 群管理变动 - "groupNumberChange": false, // 群聊列表变动 - "groupMemberNumberChange": false, // 群成员变动 - "addGroupApplication": false, // 加群申请 - // 好友通知类 - "privateMessage": false, // 好友消息 - "PrivateRecall": false, // 好友撤回 - "friendRequest": false, // 好友申请 - "friendNumberChange": false, // 好友列表变动 - // 其他通知 - "flashPhoto": false, // 闪照 - "botBeenBanned": false, // 禁言 - "input": false // 对方正在输入 - }, - // 群单独设置 - "123456": { - "groupMessage": false, - "grouptemporaryMessage": false - }, - "456789": { - "groupMessage": false, - "grouptemporaryMessage": false - } - }, - // 代理椰奶绝大部分请求 - "proxy": { - // 代理网址 仅支持http - "proxyAddress": "http: //127.0.0.1:7890", - // #开启或关闭使用代理 - "switchProxy": false, - // #代理黑名单,可以使用*和?通配符 - "blacklist": [ - "*.baidu.com" - ], - // #代理白名单,在全局关闭的情况下可用 - "whitelist": [ - "*.google.com" - ] - }, - // 哔咔设置 - "bika": { - // 是否允许私聊使用 - "allowPM": true, - // 每名用户每日使用次数限制 0则无限制 - "limit": 30, - // 哔咔图片直连,直接使用哔咔原图发送不使用图片反代,国内则需使用代理 - "bikaDirectConnection": false, - // 哔咔图片反代 - "bikaImageProxy": "s3.go2778.com/static", - // 哔咔图片质量,可选值 ['low', 'medium', 'high', 'original'] 质量依次从低到高 - "imageQuality": "medium" - }, - // 群管配置 - "groupAdmin": { - // 白名单QQ - "whiteQQ": [], - // 白名单禁言自动解禁 - "noBan": false, - "vote": { - // # 投票禁言开关 - "VoteBan": true, - // # 投票踢人开关 - "VoteKick": false, - // # 投票禁言超时时间 单位:秒 - "outTime": 180, - // # 投票最低所需票数 不建议太低 - "minNum": 4, - // # 投票成功禁言时间 单位:秒 - "BanTime": 3600, - // # 是否允许管理员一票否决 - "veto": true, - // # 是否允许投票禁言管理员(Bot为群主的情况下) - "voteAdmin": false - }, - // 收到进群事件向群里发送通知 - "groupAddNot": { - // 开启的群聊 - "openGroup": [], - "msg": "有一个加群通知,管理员快去看看吧~" - }, - "groupverify": { - // 开启的群聊 - "openGroup": [], - // 如需要分群,请按照下列格式添加 <以群号为字段> 的成功提示即可,0 字段代表默认回复 - "SuccessMsgs": { - "254974507": "✅ 验证成功,欢迎入群,群里有非常多的大佬给你透哦~~", - "0": "✅ 验证成功,欢迎入群" - }, - // 答案验证模式,可选:精确、模糊 - "mode": "精确", - // 最多允许尝试次数 - "times": 7, - // 是否开启最后一分钟提醒(仅在超时时长大于等于 120 秒时有效,true 开启,false 关闭) - "remindAtLastMinute": true, - // 超时时长(秒),建议至少一分钟(60 秒) - "time": 300, - // 随机算式的数字的随机范围 - "range": { - "min": 10, - "max": 100 - }, - // 收到进群事件后延迟多少秒再发送验证信息(秒) - "DelayTime": 2 - } - }, - "state": { - // 将椰奶状态作为默认状态 - "setDefaultState": false, - // 网络测试 - // show: 是否显示 true - 显示 - false - 不显示 pro - 只有pro显示 - // 测试网址列表 - // list: - // name: 显示名称 - // url: 要访问的网址 - // useProxy: 是否使用插件配置中的代理访问 - // timeout: 测试超时时间 - "psTestSites": { - "show": "pro", - "list": [ - { - "name": "Baidu", - "url": "https://baidu.com" - }, - { - "name": "Google", - "url": "https://google.com" - }, - { - "name": "Google(proxy)", - "url": "https://google.com", - "useProxy": true - } - ], - "timeout": 5000 - }, - // 监控任务 - "statusTask": true, - // 如果出现内存异常的情况可将此配置项开启,如果打开后报错请将监控任务关闭 - "statusPowerShellStart": false, - // 关闭node的那个圈圈 - "closedNodeInfo": true, - // 关闭图表 - "closedChart": false, - // 是否显示FastFetch 参数: true, false, pro ,default - // true - 开启 - // false - 关闭 - // pro - 只有状态pro显示 - // default - win默认只有pro显示,liunx等其他正常也会显示 - "showFastFetch": "default", - // 样式 - "style": { - // 背景图片api,设置为false则一直使用默认背景 - // CF跳板(会损失一点访问时间,但后期API地址可以自动更新) - // http: //dir.dengfenglai.icu - // 默认地址(直连,但是不确定是否会GG) - // https: //t.mwm.moe/mp/ - "backdrop": "http://dir.dengfenglai.icu", - // 当api请求失败时使用的默认背景图,请放置在resources/state/img/bg目录下 - // random - 随机选择state/img/bg目录里的一张图片 - // default_bg.jpg 可直接指定bg目录里一张图片,注意请带上后缀名 - "backdropDefault": "random", - // Bot昵称的颜色 - "BotNameColor": "#000", - // Bot昵称渐变色 设置了该值BotNameColor值将无效 如要使用BotNameColor请将该值设为none或false - // 该值请参考https: //developer.mozilla.org/zh-CN/docs/Web/CSS/gradient/linear-gradient - // xxdeg 为渐变角度 后为渐变颜色 - "BotNameColorGradient": "none", - // 主硬件进度条和磁盘进度条的颜色 - "progressBar": { - // 严重 - "highColor": "#d73403", - // 警告 - "mediumColor": "#ffa500", - // 正常 - "lowColor": "#84A0DF" - } - } - }, - // pixiv相关配置 - "pixiv": { - // 是否允许私聊使用 - "allowPM": true, - // pixiv图片直连,国内需配合代理使用 - "pixivDirectConnection": false, - // pixiv图片反代,开启直连后反代服务则无效 - "pixivImageProxy": "i.pixiv.re", - // 每名用户每日次数限制(0 则无限制) - "limit": 30, - // Pixiv 登录凭证刷新令牌 (Refresh Token) - // 获取方法请参考: https: //github.com/mixmoe/HibiAPI/issues/53 - "refresh_token": null, - // 返回语言, 会影响标签的翻译 - "language": "zh-cn" - }, - "picSearch": { - // 是否只有主人能用 - "isMasterUse": false, - // 是否允许私聊使用 - "allowPM": true, - // 每名用户每日搜索次数限制 - "limit": 30, - // SauceNAO搜图apikey 请在 https://saucenao.com/user.php?page=search-api 进行获取 - "SauceNAOApiKey": null, - // SauceNAO搜图相似度低于这个百分比将被认定为相似度过低 - "SauceNAOMinSim": 60, - // saucanao 得到 NSFW 结果时隐藏缩略图,可选 0~3,严格程度依次增加 - // 0-不隐藏,1-隐藏明确为 NSFW 的缩略图,2-隐藏明确和可能为 NSFW 的缩略图,3-只显示明确为非 NSFW 的缩略图 - "hideImgWhenSaucenaoNSFW": 0, - // 绕过 Cloudflare Challenge 所使用的 TLS 版本,建议可选值:["TLSv1.1", "TLSv1.2"] - "cfTLSVersion": "TLSv1.1", - // 是否使用 Puppeteer 请求 ascii2d 以绕过 cf js challenge - "ascii2dUsePuppeteer": false, - // ascii2d搜图返回结果的最大数量 - "ascii2dResultMaxQuantity": 3, - // 隐藏所有搜索结果的缩略图 - "hideImg": false, - // whatanime 得到 R18 结果时隐藏结果缩略图 - "hideImgWhenWhatanimeR18": false, - // whatanime 发送预览视频,R18 结果不会发送 - "whatanimeSendVideo": false, - // 是否在 saucenao 相似度过低时自动使用 ascii2d - "useAscii2dWhenLowAcc": true, - // 是否在 saucenao 搜索失败时自动使用 ascii2d - "useAscii2dWhenFailed": true - } -} \ No newline at end of file diff --git a/model/GroupAdmin.js b/model/GroupAdmin.js index 133fb6d..21d92ce 100644 --- a/model/GroupAdmin.js +++ b/model/GroupAdmin.js @@ -5,7 +5,6 @@ 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 { QQApi } from "./index.js" import { Time_unit, ROLE_MAP } from "../constants/other.js" @@ -14,8 +13,8 @@ import schedule from "node-schedule" // 无管理文案 const ROLE_ERROR = "❎ 该命令需要管理员权限" - -export default class { +let _task = [] +export default class GroupAdmin { constructor(e) { this.e = e this.Bot = e.bot ?? Bot @@ -269,14 +268,16 @@ segment.image(`https://q1.qlogo.cn/g?b=qq&s=100&nk=${item.user_id}`), * @returns {Promise} - 返回操作结果。如果设置成功,则返回 true;否则,返回 false。 */ async setMuteTask(group, cron, type) { - let name = `椰奶群定时${type ? "禁言" : "解禁"}${group}` - if (loader.task.find(item => item.name == name)) return false let redisTask = JSON.parse(await redis.get(this.MuteTaskKey)) || [] + if (_task.find(i => i.group === group && i.type === type)) return false + const bot = this.Bot let task = { cron, - name, + group, + type, + bot, fnc: () => { - this.Bot.pickGroup(group).muteAll(type) + bot.pickGroup(group).muteAll(type) }, job: schedule.scheduleJob(cron, async() => { try { @@ -293,25 +294,40 @@ segment.image(`https://q1.qlogo.cn/g?b=qq&s=100&nk=${item.user_id}`), } }) } - loader.task.push(task) + _task.push(task) redisTask.push({ cron, group, type, botId: this.Bot.uin }) redis.set(this.MuteTaskKey, JSON.stringify(redisTask)) return true } - /** - * @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}`, + static async loadRedisMuteTask() { + const data = JSON.parse(await redis.get("yenai:MuteTasks")) + if (!data) return false + data.forEach((i) => { + const { cron, group, type, botId } = i + const task = { + cron, + group, + type, fnc: () => { - (Bot[item.botId] ?? Bot).pickGroup(item.group).muteAll(item.type) - } + (Bot[botId] ?? Bot).pickGroup(group).muteAll(type) + }, + job: schedule.scheduleJob(cron, async() => { + try { + if (task.log == true) { + logger.mark(`开始定时任务:${task.name}`) + } + await task.fnc() + if (task.log == true) { + logger.mark(`定时任务完成:${task.name}`) + } + } catch (err) { + logger.error(`定时任务报错:${task.name}`) + logger.error(err) + } + }) } + _task.push(task) }) } @@ -323,42 +339,54 @@ segment.image(`https://q1.qlogo.cn/g?b=qq&s=100&nk=${item.user_id}`), */ async delMuteTask(group, type) { let redisTask = JSON.parse(await redis.get(this.MuteTaskKey)) || [] - const name = `椰奶群定时${type ? "禁言" : "解禁"}${group}` // 终止任务 - const task = loader.task.find(i => i.name === name) + const task = _task.find(i => i.group == group && i.type == type) task?.job?.cancel() - - loader.task = loader.task.filter(item => item.name !== name) - redisTask = redisTask.filter(item => item.group !== group && item.type !== type) + const f = i => !(i.group == group && i.type == type) + _task = _task.filter(f) + redisTask = redisTask.filter(f) redis.set(this.MuteTaskKey, JSON.stringify(redisTask)) return true } /** 获取定时任务 */ getMuteTask() { - let RegEx = /椰奶群定时(禁言|解禁)(\d+)/ - let taskList = _.cloneDeep(loader.task) - let MuteList = taskList.filter(item => /椰奶群定时禁言\d+/.test(item.name)) - let noMuteList = taskList.filter(item => /椰奶群定时解禁\d+/.test(item.name)) - noMuteList.forEach(noitem => { - let index = MuteList.findIndex(item => noitem.name.match(RegEx)[2] == item.name.match(RegEx)[2]) - if (index !== -1) { - MuteList[index].nocron = noitem.cron + let taskList = _.cloneDeep(_task) + console.log(taskList) + const taskGroups = new Map() + for (const item of taskList) { + if (item.type) { + if (!taskGroups.has(item.group)) { + taskGroups.set(item.group, { ...item }) + } else { + const muteItem = taskGroups.get(item.group) + muteItem.cron = item.cron + } } else { - noitem.nocron = noitem.cron - delete noitem.cron - MuteList.push(noitem) + if (!taskGroups.has(item.group)) { + const data = { ...item } + data.nocron = data.cron + delete data.cron + taskGroups.set(item.group, { ...item }) + } else { + const muteItem = taskGroups.get(item.group) + muteItem.nocron = item.cron + } } - }) - return MuteList.map(item => { - let analysis = item.name.match(RegEx) - 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}"` : "" - ] - }) + } + console.log(taskGroups) + const result = [] + for (const [ group, item ] of taskGroups) { + const imageSegment = segment.image(`https://p.qlogo.cn/gh/${group}/${group}/100`) + result.push([ + imageSegment, + `\n群号:${group}`, + item.cron ? `\n禁言时间:"${item.cron}"` : "", + item.nocron ? `\n解禁时间:"${item.nocron}"` : "" + ]) + } + + return result } /** diff --git a/model/State/OtherInfo.js b/model/State/OtherInfo.js index 4c7c39e..65c9ea1 100644 --- a/model/State/OtherInfo.js +++ b/model/State/OtherInfo.js @@ -2,11 +2,15 @@ import fs from "fs" import _ from "lodash" import os from "os" import path from "path" -import loader from "../../../../lib/plugins/loader.js" import { Version } from "../../components/index.js" import { formatDuration } from "../../tools/index.js" import { osInfo, si } from "./utils.js" +let loader = null +try { + loader = (await import("../../../../lib/plugins/loader.js")).default +} catch { +} export default function otherInfo(e) { let otherInfo = [] // 其他信息 @@ -48,9 +52,12 @@ function getPluginNum(e) { ?.filter(item => item.endsWith(".js")) ?.length const pluginsStr = `${plugins ?? 0} plugins | ${js ?? 0} js` - const { priority, task } = loader - const loaderStr = `${priority?.length} fnc | ${task?.length} task` - return e.isPro ? `${pluginsStr} | ${loaderStr}` : pluginsStr + if (loader && e.isPro) { + const { priority, task } = loader + const loaderStr = `${priority?.length} fnc | ${task?.length} task` + return `${pluginsStr} | ${loaderStr}` + } + return pluginsStr } export async function getCopyright() {