diff --git a/apps/groupAdmin/voteBan.js b/apps/groupAdmin/GroupVoteBan.js similarity index 98% rename from apps/groupAdmin/voteBan.js rename to apps/groupAdmin/GroupVoteBan.js index 98481d3..971d19a 100644 --- a/apps/groupAdmin/voteBan.js +++ b/apps/groupAdmin/GroupVoteBan.js @@ -9,10 +9,10 @@ import md5 from "md5" let Vote = {} let time = 180 // 投票超时时间 单位秒 -export class NewGroupVerify extends plugin { +export class GroupVoteBan extends plugin { constructor() { super({ - name: "椰奶投票禁言", + name: "椰奶群管-投票禁言", dsc: "投票禁言某人", event: "message.group", priority: 5000, diff --git a/apps/groupAdmin/groupAdmin.js b/apps/groupAdmin/groupAdmin.js index 99eb984..a87f6d3 100644 --- a/apps/groupAdmin/groupAdmin.js +++ b/apps/groupAdmin/groupAdmin.js @@ -1,12 +1,9 @@ import _ from "lodash" -import moment from "moment" import { Config } from "../../components/index.js" import { Time_unit } from "../../constants/other.js" -import { GroupAdmin as Ga, GroupBannedWords, QQApi, common, puppeteer } from "../../model/index.js" +import { GroupAdmin as Ga, GroupBannedWords, common } from "../../model/index.js" import { cronValidate, translateChinaNum } from "../../tools/index.js" -// API请求错误文案 -const API_ERROR = "❎ 出错辣,请稍后重试" // 正则 const Numreg = "[零一壹二两三四五六七八九十百千万亿\\d]+" const TimeUnitReg = Object.keys(Time_unit).join("|") @@ -20,7 +17,7 @@ const redisTask = await Ga.getRedisMuteTask() || false export class GroupAdmin extends plugin { constructor() { super({ - name: "椰奶群管", + name: "椰奶群管-基础", event: "message.group", priority: 500, rule: [ @@ -44,18 +41,6 @@ export class GroupAdmin extends plugin { reg: "^#(设置|取消)管理(\\d+)?$", fnc: "SetAdmin" }, - { - reg: "^#发群公告", - fnc: "AddAnnounce" - }, - { - reg: "^#删群公告(\\d+)$", - fnc: "DelAnnounce" - }, - { - reg: "^#查群公告$", - fnc: "GetAnnounce" - }, { reg: "^#(修改|设置)头衔", fnc: "adminsetTitle" @@ -64,22 +49,6 @@ export class GroupAdmin extends plugin { reg: "^#申请头衔", fnc: "SetGroupSpecialTitle" }, - { - reg: "^#(查)?(幸运)?字符(列表)?$", - fnc: "qun_luckylist" - }, - { - reg: "^#抽(幸运)?字符$", - fnc: "qun_lucky" - }, - { - reg: "^#替换(幸运)?字符(\\d+)$", - fnc: "qun_luckyuse" - }, - { - reg: "^#(开启|关闭)(幸运)?字符$", - fnc: "qun_luckyset" - }, { reg: "^#(获取|查看)?禁言列表$", fnc: "Mutelist" @@ -116,26 +85,6 @@ export class GroupAdmin extends plugin { reg: "^#(查看|获取)?群?发言(榜单|排行)((7|七)天)?", fnc: "SpeakRank" }, - { - reg: "^#?(谁|哪个吊毛|哪个屌毛|哪个叼毛)是龙王$", - fnc: "dragonKing" - }, - { - reg: "^#群星级$", - fnc: "Group_xj" - }, - { - reg: "^#群数据((7|七)天)?$", - fnc: "groupData" - }, - { - reg: "^#今日打卡$", - fnc: "DaySigned" - }, - { - reg: "^#((今|昨|前|明|后)天|\\d{4}-\\d{1,2}-\\d{1,2})谁生日$", - fnc: "groupBirthday" - }, { reg: "^#?(开启|关闭)加群通知$", fnc: "handleGroupAdd" @@ -144,14 +93,6 @@ export class GroupAdmin extends plugin { reg: "^#?(加|设|移)精$", fnc: "essenceMessage" }, - { - reg: "^#?群管(加|删)白(名单)?", - fnc: "whiteQQ" - }, - { - reg: "^#?(开启|关闭)白名单(自动)?解禁", - fnc: "noBan" - }, { reg: Autisticreg, // 我要自闭 fnc: "Autistic" @@ -235,44 +176,6 @@ export class GroupAdmin extends plugin { type ? e.reply(`✅ 已经把「${name}」设置为管理啦!!`) : e.reply(`✅ 已取消「${name}」的管理`) } - // 发群公告 - async AddAnnounce(e) { - if (!common.checkPermission(e, "admin", "admin")) { return true } - // 获取发送的内容 - let msg = e.msg.replace(/#|发群公告/g, "").trim() - if (!msg) return e.reply("❎ 公告不能为空") - - let result = await new QQApi(e).setAnnounce(e.group_id, msg) - - if (!result) return e.reply(API_ERROR) - if (result.ec != 0) { - e.reply("❎ 发送失败\n" + JSON.stringify(result, null, "\t")) - } - } - - // 查群公告 - async GetAnnounce(e) { - let res = await new QQApi(e).getAnnouncelist(e.group_id) - if (!res) return e.reply(API_ERROR) - return e.reply(res) - } - - // 删群公告 - async DelAnnounce(e) { - if (!common.checkPermission(e, "admin", "admin")) { return true } - let msg = e.msg.replace(/#|删群公告/, "").trim() - if (!msg) return e.reply("❎ 序号不可为空") - - let result = await new QQApi(e).delAnnounce(e.group_id, msg) - if (!result) return e.reply(API_ERROR) - - if (result.ec == 0) { - e.reply(`✅ 已删除「${result.text}」`) - } else { - e.reply("❎ 删除失败\n" + JSON.stringify(result, null, "\t")) - } - } - // 修改头衔 async adminsetTitle(e) { if (!common.checkPermission(e, "master", "owner")) return @@ -313,58 +216,6 @@ export class GroupAdmin extends plugin { e.reply(`✅ 已将你的头衔更换为「${Title}」`, true) } - // 字符列表 - async qun_luckylist(e) { - let data = await new QQApi(e).luckylist(e.group_id) - if (!data) return e.reply(API_ERROR) - if (data.retcode != 0) return e.reply("❎ 获取数据失败\n" + JSON.stringify(data)) - - let msg = data.data.word_list.map((item, index) => { - let { wording, word_id, word_desc } = item.word_info - return `${word_id}:${wording}\n寓意:${word_desc}` - }).join("\n") - e.reply(msg) - } - - // 抽幸运字符 - async qun_lucky(e) { - let res = await new QQApi(e).drawLucky(e.group_id) - - if (!res) return e.reply(API_ERROR) - if (res.retcode == 11004) return e.reply("❎ 今天已经抽过了,明天再来抽取吧") - if (res.retcode != 0) return e.reply("❎ 错误\n" + JSON.stringify(res.data)) - - if (res.data.word_info) { - let { wording, word_desc } = res.data.word_info.word_info - e.reply(`恭喜您抽中了${wording}\n寓意为:${word_desc}`) - } else { - e.reply("恭喜您抽了中了个寂寞") - } - } - - // 替换幸运字符 - async qun_luckyuse(e) { - if (!common.checkPermission(e, "admin", "admin")) { return true } - let id = e.msg.replace(/#|替换(幸运)?字符/g, "") - let res = await new QQApi(e).equipLucky(e.group_id, id) - - if (!res) return e.reply(API_ERROR) - if (res.retcode != 0) return e.reply("❎替换失败\n" + JSON.stringify(res)) - e.reply("✅ OK") - } - - // 开启或关闭群字符 - async qun_luckyset(e) { - if (!common.checkPermission(e, "admin", "admin")) { return true } - - let res = await new QQApi(e).swichLucky(e.group_id, /开启/.test(e.msg)) - if (!res) return e.reply(API_ERROR) - - if (res.retcode == 11111) return e.reply("❎ 重复开启或关闭") - if (res.retcode != 0) return e.reply("❎ 错误\n" + JSON.stringify(res)) - e.reply("✅ OK") - } - // 获取禁言列表 async Mutelist(e) { new Ga(e).getMuteList(e.group_id, true) @@ -519,163 +370,6 @@ export class GroupAdmin extends plugin { : e.reply(`❎ 该群定时${type ? "禁言" : "解禁"}已存在不可重复设置`) } - // 谁是龙王 - async dragonKing(e) { - // 浏览器截图 - let screenshot = await puppeteer.Webpage({ - url: `https://qun.qq.com/interactive/honorlist?gc=${e.group_id}&type=1&_wv=3&_wwv=129`, - headers: { Cookie: this.Bot.cookies["qun.qq.com"] }, - font: true - }) - if (screenshot) return e.reply(screenshot) - // 数据版 - let res = await new QQApi(e).dragon(e.group_id) - if (!res) return e.reply(API_ERROR) - e.reply([ - `本群龙王:${res.nick}`, - segment.image(`https://q1.qlogo.cn/g?b=qq&s=100&nk=${res.uin}`), - `蝉联天数:${res.avatar_size}` - ]) - } - - /** - * 群星级 - * @param e - */ - async Group_xj(e) { - let screenshot = await puppeteer.Webpage({ - url: `https://qqweb.qq.com/m/business/qunlevel/index.html?gc=${e.group_id}&from=0&_wv=1027`, - cookie: common.getck("qun.qq.com", this.Bot, true), - emulate: "QQTheme", - font: true - }) - if (screenshot) return e.reply(screenshot) - // 出错后发送数据 - let result = await new QQApi(e).getCreditLevelInfo(e.group_id) - if (!result) return e.reply(API_ERROR) - if (result.ec != 0) return e.reply("❎ 查询错误\n" + JSON.stringify(result)) - let { uiGroupLevel, group_name, group_uin } = result.info - let str = "⭐" - str = str.repeat(uiGroupLevel) - e.reply([ - `群名:${group_name}\n`, - `群号:${group_uin}\n`, - `群星级:${str}` - ]) - } - - // 群发言榜单 - async SpeakRank(e) { - if (!common.checkPermission(e, "all", "admin")) return - - // 图片截图 - let screenshot = await puppeteer.Webpage({ - url: `https://qun.qq.com/m/qun/activedata/speaking.html?gc=${e.group_id}&time=${/(7|七)天/.test(e.msg) ? 1 : 0}`, - headers: { Cookie: this.Bot.cookies["qun.qq.com"] }, - font: true - }) - if (screenshot) return e.reply(screenshot) - // 出错后发送文字数据 - let res = await new QQApi(e).SpeakRank(e.group_id, /(7|七)天/.test(e.msg)) - if (!res) return e.reply(API_ERROR) - if (res.retcode != 0) return e.reply("❎ 未知错误\n" + JSON.stringify(res)) - let msg = _.take(res.data.speakRank.map((item, index) => - `${index + 1}:${item.nickname}-${item.uin}\n连续活跃${item.active}天:发言${item.msgCount}次` - ), 10).join("\n") - e.reply(msg) - } - - // 今日打卡 - async DaySigned(e) { - // 浏览器截图 - let screenshot = await puppeteer.Webpage({ - url: `https://qun.qq.com/v2/signin/list?gc=${e.group_id}`, - emulate: "iPhone 6", - cookie: common.getck("qun.qq.com", this.Bot, true), - font: true - }) - if (screenshot) return e.reply(screenshot) - // 出错后使用接口 - let res = await new QQApi(e).signInToday(e.group_id) - if (!res) return e.reply(API_ERROR) - if (res.retCode != 0) return e.reply("❎ 未知错误\n" + JSON.stringify(res)) - - let list = res.response.page[0] - if (list.total == 0) return e.reply("❎ 今天还没有人打卡") - // 发送消息 - let msg = list.infos.map((item, index) => `${index + 1}:${item.uidGroupNick}-${item.uid}\n打卡时间:${moment(item.signedTimeStamp * 1000).format("YYYY-MM-DD HH:mm:ss")}`).join("\n") - e.reply(msg) - } - - // 查看某天谁生日 - async groupBirthday(e) { - let date = e.msg.match(/^#?(今天|昨天|明天|后天|\d{4}-\d{1,2}-\d{1,2})谁生日$/)[1] - if (date == "昨天") { - date = moment().subtract(1, "days").format("YYYY-MM-DD") - } else if (date == "前天") { - date = moment().subtract(2, "days").format("YYYY-MM-DD") - } else if (date == "明天") { - date = moment().add(1, "days").format("YYYY-MM-DD") - } else if (date == "后天") { - date = moment().add(2, "days").format("YYYY-MM-DD") - } else if (date == "今天") { - date = moment().format("YYYY-MM-DD") - } - e.reply( - await puppeteer.Webpage({ - url: `https://qun.qq.com/qqweb/m/qun/calendar/detail.html?_wv=1031&_bid=2340&src=3&gc=${e.group_id}&type=2&date=${date}`, - cookie: common.getck("qun.qq.com", this.Bot, true), - emulate: "iPhone 6", - font: true - }) - ) - } - - // 群数据 - async groupData(e) { - if (!common.checkPermission(e, "all", "admin")) return - - // 浏览器截图 - let screenshot = await puppeteer.Webpage({ - url: `https://qun.qq.com/m/qun/activedata/active.html?_wv=3&_wwv=128&gc=${e.group_id}&src=2`, - cookie: common.getck("qun.qq.com", this.Bot, true), - click: /(7|七)天/.test(e.msg) - ? [ - { - selector: "#app > div.tabbar > div.tabbar__time > div.tabbar__time__date", - time: 500 - }, - { - selector: "#app > div.tabbar > div.tabbar__date-selector > div > div:nth-child(3)", - time: 1000 - } - ] - : false, - font: true - }) - if (screenshot) return e.reply(screenshot) - // 数据 - let res = await new QQApi(e).groupData(e.group_id, /(7|七)天/.test(e.msg)) - if (!res) return e.reply(API_ERROR) - if (res.retcode != 0) return e.reply(res.msg || JSON.stringify(res)) - let { groupInfo, activeData, msgInfo, joinData, exitData, applyData } = res.data - e.reply( - [ - `${groupInfo.groupName}(${groupInfo.groupCode})${/(7|七)天/.test(e.msg) ? "七天" : "昨天"}的群数据\n`, - "------------消息条数---------\n", - `消息条数:${msgInfo.total}\n`, - "------------活跃人数---------\n", - `活跃人数:${activeData.activeData}\n`, - `总人数:${activeData.groupMember}\n`, - `活跃比例:${activeData.ratio}%\n`, - "-----------加退群人数--------\n", - `申请人数:${joinData.total}\n`, - `入群人数:${applyData.total}\n`, - `退群人数:${exitData.total}\n` - ] - ) - } - /** * 开启或关闭加群通知 * @param e @@ -724,38 +418,4 @@ export class GroupAdmin extends plugin { await e.group.muteMember(e.user_id, TabooTime * Company) e.reply("那我就不手下留情了~", true) } - - /** - * 加白名单 - */ - async whiteQQ() { - if (!common.checkPermission(this.e, "master")) return - - let type = /加/.test(this.e.msg) ? "add" : "del" - let qq = this.e.at || (this.e.msg.match(/\d+/)?.[0] || "") - qq = Number(qq) || String(qq) - - if (!qq) return this.reply("❎ 请艾特或输入需要加白的QQ") - - const { whiteQQ } = Config.groupAdmin - const isWhite = whiteQQ.includes(qq) - - if (isWhite && type === "add") return this.reply("❎ 此人已在群管白名单内") - if (!isWhite && type === "del") return this.reply("❎ 此人未在群管白名单中") - - Config.modifyarr("groupAdmin", "whiteQQ", qq, type) - this.reply(`✅ 已${type === "add" ? "加入" : "删除"}${qq}到群管白名单`) - } - - async noBan() { - if (!common.checkPermission(this.e, "master")) return - let type = !!/开启/.test(this.e.msg) - - const { noBan } = Config.groupAdmin - if (noBan && type) return this.reply("❎ 白名单自动解禁已处于开启状态") - if (!noBan && !type) return this.reply("❎ 白名单自动解禁已处于关闭状态") - - Config.modify("groupAdmin", "noBan", type) - this.reply(`✅ 已${type ? "开启" : "关闭"}白名单自动解禁`) - } } diff --git a/apps/groupAdmin/groupAdminOther.js b/apps/groupAdmin/groupAdminOther.js new file mode 100644 index 0000000..e816b64 --- /dev/null +++ b/apps/groupAdmin/groupAdminOther.js @@ -0,0 +1,193 @@ +import moment from "moment" +import _ from "lodash" +import { QQApi, common, puppeteer } from "../../model/index.js" +// API请求错误文案 +const API_ERROR = "❎ 出错辣,请稍后重试" +export class GroupAdminOther extends plugin { + constructor() { + super({ + name: "椰奶群管-其他", + event: "message.group", + priority: 500, + rule: [ + { + reg: "^#?(谁|哪个吊毛|哪个屌毛|哪个叼毛)是龙王$", + fnc: "dragonKing" + }, + { + reg: "^#群星级$", + fnc: "Group_xj" + }, + { + reg: "^#群数据((7|七)天)?$", + fnc: "groupData" + }, + { + reg: "^#今日打卡$", + fnc: "DaySigned" + }, + { + reg: "^#((今|昨|前|明|后)天|\\d{4}-\\d{1,2}-\\d{1,2})谁生日$", + fnc: "groupBirthday" + } + ] + }) + } + + // 谁是龙王 + async dragonKing(e) { + // 浏览器截图 + let screenshot = await puppeteer.Webpage({ + url: `https://qun.qq.com/interactive/honorlist?gc=${e.group_id}&type=1&_wv=3&_wwv=129`, + headers: { Cookie: this.Bot.cookies["qun.qq.com"] }, + font: true + }) + if (screenshot) return e.reply(screenshot) + // 数据版 + let res = await new QQApi(e).dragon(e.group_id) + if (!res) return e.reply(API_ERROR) + e.reply([ + `本群龙王:${res.nick}`, + segment.image(`https://q1.qlogo.cn/g?b=qq&s=100&nk=${res.uin}`), + `蝉联天数:${res.avatar_size}` + ]) + } + + /** + * 群星级 + * @param e + */ + async Group_xj(e) { + let screenshot = await puppeteer.Webpage({ + url: `https://qqweb.qq.com/m/business/qunlevel/index.html?gc=${e.group_id}&from=0&_wv=1027`, + cookie: common.getck("qun.qq.com", this.Bot, true), + emulate: "QQTheme", + font: true + }) + if (screenshot) return e.reply(screenshot) + // 出错后发送数据 + let result = await new QQApi(e).getCreditLevelInfo(e.group_id) + if (!result) return e.reply(API_ERROR) + if (result.ec != 0) return e.reply("❎ 查询错误\n" + JSON.stringify(result)) + let { uiGroupLevel, group_name, group_uin } = result.info + let str = "⭐" + str = str.repeat(uiGroupLevel) + e.reply([ + `群名:${group_name}\n`, + `群号:${group_uin}\n`, + `群星级:${str}` + ]) + } + + // 群发言榜单 + async SpeakRank(e) { + if (!common.checkPermission(e, "all", "admin")) return + + // 图片截图 + let screenshot = await puppeteer.Webpage({ + url: `https://qun.qq.com/m/qun/activedata/speaking.html?gc=${e.group_id}&time=${/(7|七)天/.test(e.msg) ? 1 : 0}`, + headers: { Cookie: this.Bot.cookies["qun.qq.com"] }, + font: true + }) + if (screenshot) return e.reply(screenshot) + // 出错后发送文字数据 + let res = await new QQApi(e).SpeakRank(e.group_id, /(7|七)天/.test(e.msg)) + if (!res) return e.reply(API_ERROR) + if (res.retcode != 0) return e.reply("❎ 未知错误\n" + JSON.stringify(res)) + let msg = _.take(res.data.speakRank.map((item, index) => + `${index + 1}:${item.nickname}-${item.uin}\n连续活跃${item.active}天:发言${item.msgCount}次` + ), 10).join("\n") + e.reply(msg) + } + + // 今日打卡 + async DaySigned(e) { + // 浏览器截图 + let screenshot = await puppeteer.Webpage({ + url: `https://qun.qq.com/v2/signin/list?gc=${e.group_id}`, + emulate: "iPhone 6", + cookie: common.getck("qun.qq.com", this.Bot, true), + font: true + }) + if (screenshot) return e.reply(screenshot) + // 出错后使用接口 + let res = await new QQApi(e).signInToday(e.group_id) + if (!res) return e.reply(API_ERROR) + if (res.retCode != 0) return e.reply("❎ 未知错误\n" + JSON.stringify(res)) + + let list = res.response.page[0] + if (list.total == 0) return e.reply("❎ 今天还没有人打卡") + // 发送消息 + let msg = list.infos.map((item, index) => `${index + 1}:${item.uidGroupNick}-${item.uid}\n打卡时间:${moment(item.signedTimeStamp * 1000).format("YYYY-MM-DD HH:mm:ss")}`).join("\n") + e.reply(msg) + } + + // 查看某天谁生日 + async groupBirthday(e) { + let date = e.msg.match(/^#?(今天|昨天|明天|后天|\d{4}-\d{1,2}-\d{1,2})谁生日$/)[1] + if (date == "昨天") { + date = moment().subtract(1, "days").format("YYYY-MM-DD") + } else if (date == "前天") { + date = moment().subtract(2, "days").format("YYYY-MM-DD") + } else if (date == "明天") { + date = moment().add(1, "days").format("YYYY-MM-DD") + } else if (date == "后天") { + date = moment().add(2, "days").format("YYYY-MM-DD") + } else if (date == "今天") { + date = moment().format("YYYY-MM-DD") + } + e.reply( + await puppeteer.Webpage({ + url: `https://qun.qq.com/qqweb/m/qun/calendar/detail.html?_wv=1031&_bid=2340&src=3&gc=${e.group_id}&type=2&date=${date}`, + cookie: common.getck("qun.qq.com", this.Bot, true), + emulate: "iPhone 6", + font: true + }) + ) + } + + // 群数据 + async groupData(e) { + if (!common.checkPermission(e, "all", "admin")) return + + // 浏览器截图 + let screenshot = await puppeteer.Webpage({ + url: `https://qun.qq.com/m/qun/activedata/active.html?_wv=3&_wwv=128&gc=${e.group_id}&src=2`, + cookie: common.getck("qun.qq.com", this.Bot, true), + click: /(7|七)天/.test(e.msg) + ? [ + { + selector: "#app > div.tabbar > div.tabbar__time > div.tabbar__time__date", + time: 500 + }, + { + selector: "#app > div.tabbar > div.tabbar__date-selector > div > div:nth-child(3)", + time: 1000 + } + ] + : false, + font: true + }) + if (screenshot) return e.reply(screenshot) + // 数据 + let res = await new QQApi(e).groupData(e.group_id, /(7|七)天/.test(e.msg)) + if (!res) return e.reply(API_ERROR) + if (res.retcode != 0) return e.reply(res.msg || JSON.stringify(res)) + let { groupInfo, activeData, msgInfo, joinData, exitData, applyData } = res.data + e.reply( + [ + `${groupInfo.groupName}(${groupInfo.groupCode})${/(7|七)天/.test(e.msg) ? "七天" : "昨天"}的群数据\n`, + "------------消息条数---------\n", + `消息条数:${msgInfo.total}\n`, + "------------活跃人数---------\n", + `活跃人数:${activeData.activeData}\n`, + `总人数:${activeData.groupMember}\n`, + `活跃比例:${activeData.ratio}%\n`, + "-----------加退群人数--------\n", + `申请人数:${joinData.total}\n`, + `入群人数:${applyData.total}\n`, + `退群人数:${exitData.total}\n` + ] + ) + } +} diff --git a/apps/groupAdmin/groupAnnounce.js b/apps/groupAdmin/groupAnnounce.js new file mode 100644 index 0000000..98fc6d7 --- /dev/null +++ b/apps/groupAdmin/groupAnnounce.js @@ -0,0 +1,63 @@ +import { QQApi, common } from "../../model/index.js" +// API请求错误文案 +const API_ERROR = "❎ 出错辣,请稍后重试" +export class GroupAnnounce extends plugin { + constructor() { + super({ + name: "椰奶群管-群公告", + event: "message.group", + priority: 500, + rule: [ + { + reg: "^#发群公告", + fnc: "AddAnnounce" + }, + { + reg: "^#删群公告(\\d+)$", + fnc: "DelAnnounce" + }, + { + reg: "^#查群公告$", + fnc: "GetAnnounce" + } + ] + }) + } + + // 发群公告 + async AddAnnounce(e) { + if (!common.checkPermission(e, "admin", "admin")) { return true } + // 获取发送的内容 + let msg = e.msg.replace(/#|发群公告/g, "").trim() + if (!msg) return e.reply("❎ 公告不能为空") + + let result = await new QQApi(e).setAnnounce(e.group_id, msg) + + if (!result) return e.reply(API_ERROR) + if (result.ec != 0) { + e.reply("❎ 发送失败\n" + JSON.stringify(result, null, "\t")) + } + } + + // 查群公告 + async GetAnnounce(e) { + let res = await new QQApi(e).getAnnouncelist(e.group_id) + if (!res) return e.reply(API_ERROR) + return e.reply(res) + } + + // 删群公告 + async DelAnnounce(e) { + if (!common.checkPermission(e, "admin", "admin")) { return true } + let msg = e.msg.replace(/#|删群公告/g, "").trim() + if (!msg) return e.reply("❎ 序号不可为空") + let result = await new QQApi(e).delAnnounce(e.group_id, msg) + if (!result) return e.reply(API_ERROR) + + if (result.ec == 0) { + e.reply(`✅ 已删除「${result.text}」`) + } else { + e.reply("❎ 删除失败\n" + JSON.stringify(result, null, "\t")) + } + } +} diff --git a/apps/groupAdmin/groupBannedWords.js b/apps/groupAdmin/groupBannedWords.js index 01cbf63..e12e518 100644 --- a/apps/groupAdmin/groupBannedWords.js +++ b/apps/groupAdmin/groupBannedWords.js @@ -1,11 +1,11 @@ -import { common, GroupBannedWords } from "../../model/index.js" +import { common, GroupBannedWords as groupBannedWords } from "../../model/index.js" import { Config } from "../../components/index.js" import _ from "lodash" -export class NewGroupBannedWords extends plugin { +export class GroupBannedWords extends plugin { constructor() { super({ - name: "椰奶群违禁词", + name: "椰奶群管-违禁词", event: "message.group", priority: 1, rule: [ @@ -47,21 +47,21 @@ export class NewGroupBannedWords extends plugin { if (!e.message || e.isMaster || e.member?.is_owner || e.member?.is_admin || isWhite) { return false } - const groupBannedWords = GroupBannedWords.initTextArr(e.group_id) - if (_.isEmpty(groupBannedWords)) { + const bannedWords = groupBannedWords.initTextArr(e.group_id) + if (_.isEmpty(bannedWords)) { return false } const KeyWord = e.raw_message.trim() const trimmedKeyWord = this.#trimAlias(KeyWord) let data = null - for (const [ k, v ] of groupBannedWords) { + for (const [ k, v ] of bannedWords) { if (k.test(trimmedKeyWord)) { data = v break } } if (!data) return false - const muteTime = GroupBannedWords.getMuteTime(e.group_id) + const muteTime = groupBannedWords.getMuteTime(e.group_id) const punishments = { 1: () => e.member.kick(), 2: () => this.#mute(muteTime), @@ -84,7 +84,7 @@ export class NewGroupBannedWords extends plugin { } if (punishments[data.penaltyType]) { punishments[data.penaltyType]() - const keyWordTran = await GroupBannedWords.keyWordTran(data.rawItem) + const keyWordTran = await groupBannedWords.keyWordTran(data.rawItem) const senderCard = e.sender.card || e.sender.nickname const wordNum = keyWordTran.length - 2 const replaceWord = "*".repeat(wordNum < 0 ? 0 : wordNum) @@ -139,7 +139,7 @@ export class NewGroupBannedWords extends plugin { } } try { - let res = GroupBannedWords.addBannedWords( + let res = groupBannedWords.addBannedWords( e.group_id, word[3].trim(), word[1], word[2], e.user_id ) e.reply([ @@ -160,7 +160,7 @@ export class NewGroupBannedWords extends plugin { word = word.replace(/#?删除违禁词/, "").trim() if (!word) return e.reply("需要删除的屏蔽词为空") try { - let msg = await GroupBannedWords.delBannedWords(e.group_id, word) + let msg = await groupBannedWords.delBannedWords(e.group_id, word) e.reply([ "✅ 成功删除:", msg ]) } catch (error) { common.handleException(e, error) @@ -172,7 +172,7 @@ export class NewGroupBannedWords extends plugin { word = word.replace(/#?查看违禁词/, "").trim() if (!word) return e.reply("需要查询的屏蔽词为空") try { - const { words, matchType, penaltyType, addedBy, date } = GroupBannedWords.queryBannedWords(e.group_id, word) + const { words, matchType, penaltyType, addedBy, date } = groupBannedWords.queryBannedWords(e.group_id, word) e.reply([ "✅ 查询屏蔽词\n", "屏蔽词:", @@ -188,19 +188,19 @@ export class NewGroupBannedWords extends plugin { } async list(e) { - const groupBannedWords = GroupBannedWords.initTextArr(e.group_id) - if (_.isEmpty(groupBannedWords)) { + const bannedWords = groupBannedWords.initTextArr(e.group_id) + if (_.isEmpty(bannedWords)) { return e.reply("❎ 没有违禁词") } let isRaw = /(原始)|(raw)/.test(e.msg) const msg = [] - for (const [ , v ] of groupBannedWords) { + for (const [ , v ] of bannedWords) { const { matchType, penaltyType, addedBy, date, rawItem } = v msg.push([ "屏蔽词:", - isRaw ? rawItem : await GroupBannedWords.keyWordTran(rawItem), - `\n匹配模式:${GroupBannedWords.matchTypeMap[matchType]}\n`, - `处理方式:${GroupBannedWords.penaltyTypeMap[penaltyType]}\n`, + isRaw ? rawItem : await bannedWords.keyWordTran(rawItem), + `\n匹配模式:${bannedWords.matchTypeMap[matchType]}\n`, + `处理方式:${bannedWords.penaltyTypeMap[penaltyType]}\n`, `添加人:${addedBy ?? "未知"}\n`, `添加时间:${date ?? "未知"}` ]) @@ -211,14 +211,14 @@ export class NewGroupBannedWords extends plugin { async muteTime(e) { if (!common.checkPermission(e, "admin", "admin")) return false let time = e.msg.match(/\d+/)[0] - GroupBannedWords.setMuteTime(e.group_id, time) + groupBannedWords.setMuteTime(e.group_id, time) e.reply(`✅ 群${e.group_id}违禁词禁言时间已设置为${time}s`) } // 增删查头衔屏蔽词 async ProhibitedTitle(e) { // 获取现有的头衔屏蔽词 - let shieldingWords = GroupBannedWords.getTitleBannedWords(e.group_id) + let shieldingWords = groupBannedWords.getTitleBannedWords(e.group_id) // 判断是否需要查看头衔屏蔽词 if (/查看/.test(e.msg)) { // 返回已有的头衔屏蔽词列表 @@ -249,7 +249,7 @@ export class NewGroupBannedWords extends plugin { if (isAddition) { // 添加新的屏蔽词 if (!_.isEmpty(newWords)) { - GroupBannedWords.addTitleBannedWords(e.group_id, newWords) + groupBannedWords.addTitleBannedWords(e.group_id, newWords) e.reply(`✅ 成功添加:${newWords.join(",")}`) } // 提示已有的屏蔽词 @@ -259,7 +259,7 @@ export class NewGroupBannedWords extends plugin { } else { // 删除已有的屏蔽词 if (!_.isEmpty(existingWords)) { - GroupBannedWords.delTitleBannedWords(e.group_id, existingWords) + groupBannedWords.delTitleBannedWords(e.group_id, existingWords) e.reply(`✅ 成功删除:${existingWords.join(",")}`) } // 提示不在屏蔽词中的词 @@ -272,7 +272,7 @@ export class NewGroupBannedWords extends plugin { // 修改头衔匹配模式 async ProhibitedTitlePattern(e) { if (!common.checkPermission(e, "admin", "admin")) return false - let res = GroupBannedWords.setTitleFilterModeChange(e.group_id) + let res = groupBannedWords.setTitleFilterModeChange(e.group_id) e.reply(`✅ 已修改匹配模式为${res ? "精确" : "模糊"}匹配`) } } diff --git a/apps/groupAdmin/groupLuckyword.js b/apps/groupAdmin/groupLuckyword.js new file mode 100644 index 0000000..fbf245c --- /dev/null +++ b/apps/groupAdmin/groupLuckyword.js @@ -0,0 +1,82 @@ +import { QQApi, common } from "../../model/index.js" +// API请求错误文案 +const API_ERROR = "❎ 出错辣,请稍后重试" +export class GroupLuckyword extends plugin { + constructor() { + super({ + name: "椰奶群管-幸运字符", + event: "message.group", + priority: 500, + rule: [ + { + reg: "^#(查)?(幸运)?字符(列表)?$", + fnc: "qun_luckylist" + }, + { + reg: "^#抽(幸运)?字符$", + fnc: "qun_lucky" + }, + { + reg: "^#替换(幸运)?字符(\\d+)$", + fnc: "qun_luckyuse" + }, + { + reg: "^#(开启|关闭)(幸运)?字符$", + fnc: "qun_luckyset" + } + ] + }) + } + + // 字符列表 + async qun_luckylist(e) { + let data = await new QQApi(e).luckylist(e.group_id) + if (!data) return e.reply(API_ERROR) + if (data.retcode != 0) return e.reply("❎ 获取数据失败\n" + JSON.stringify(data)) + + let msg = data.data.word_list.map((item, index) => { + let { wording, word_id, word_desc } = item.word_info + return `${word_id}:${wording}\n寓意:${word_desc}` + }).join("\n") + e.reply(msg) + } + + // 抽幸运字符 + async qun_lucky(e) { + let res = await new QQApi(e).drawLucky(e.group_id) + + if (!res) return e.reply(API_ERROR) + if (res.retcode == 11004) return e.reply("❎ 今天已经抽过了,明天再来抽取吧") + if (res.retcode != 0) return e.reply("❎ 错误\n" + JSON.stringify(res.data)) + + if (res.data.word_info) { + let { wording, word_desc } = res.data.word_info.word_info + e.reply(`恭喜您抽中了${wording}\n寓意为:${word_desc}`) + } else { + e.reply("恭喜您抽了中了个寂寞") + } + } + + // 替换幸运字符 + async qun_luckyuse(e) { + if (!common.checkPermission(e, "admin", "admin")) { return true } + let id = e.msg.replace(/#|替换(幸运)?字符/g, "") + let res = await new QQApi(e).equipLucky(e.group_id, id) + + if (!res) return e.reply(API_ERROR) + if (res.retcode != 0) return e.reply("❎替换失败\n" + JSON.stringify(res)) + e.reply("✅ OK") + } + + // 开启或关闭群字符 + async qun_luckyset(e) { + if (!common.checkPermission(e, "admin", "admin")) { return true } + + let res = await new QQApi(e).swichLucky(e.group_id, /开启/.test(e.msg)) + if (!res) return e.reply(API_ERROR) + + if (res.retcode == 11111) return e.reply("❎ 重复开启或关闭") + if (res.retcode != 0) return e.reply("❎ 错误\n" + JSON.stringify(res)) + e.reply("✅ OK") + } +} diff --git a/apps/groupAdmin/groupVerify.js b/apps/groupAdmin/groupVerify.js index f4c38e8..77a1e74 100644 --- a/apps/groupAdmin/groupVerify.js +++ b/apps/groupAdmin/groupVerify.js @@ -5,11 +5,10 @@ import { sleep } from "../../tools/index.js" // 全局 let temp = {} const ops = [ "+", "-" ] -export class NewGroupVerify extends plugin { +export class GroupVerify extends plugin { constructor() { super({ - name: "椰奶入群验证", - dsc: "重新验证和绕过验证", + name: "椰奶群管-入群验证", event: "message.group", priority: 5, rule: [ diff --git a/apps/groupAdmin/groupWhiteListCtrl.js b/apps/groupAdmin/groupWhiteListCtrl.js new file mode 100644 index 0000000..084c680 --- /dev/null +++ b/apps/groupAdmin/groupWhiteListCtrl.js @@ -0,0 +1,56 @@ +import { common } from "../../model/index.js" +import { Config } from "../../components/index.js" + +export class groupWhiteListCtrl extends plugin { + constructor() { + super({ + name: "椰奶群管-白名单", + event: "message.group", + priority: 500, + rule: [ + { + reg: "^#?群管(加|删)白(名单)?", + fnc: "whiteQQ" + }, + { + reg: "^#?(开启|关闭)白名单(自动)?解禁", + fnc: "noBan" + } + ] + }) + } + + /** + * 加白名单 + */ + async whiteQQ() { + if (!common.checkPermission(this.e, "master")) return + + let type = /加/.test(this.e.msg) ? "add" : "del" + let qq = this.e.at || (this.e.msg.match(/\d+/)?.[0] || "") + qq = Number(qq) || String(qq) + + if (!qq) return this.reply("❎ 请艾特或输入需要加白的QQ") + + const { whiteQQ } = Config.groupAdmin + const isWhite = whiteQQ.includes(qq) + + if (isWhite && type === "add") return this.reply("❎ 此人已在群管白名单内") + if (!isWhite && type === "del") return this.reply("❎ 此人未在群管白名单中") + + Config.modifyarr("groupAdmin", "whiteQQ", qq, type) + this.reply(`✅ 已${type === "add" ? "加入" : "删除"}${qq}到群管白名单`) + } + + async noBan() { + if (!common.checkPermission(this.e, "master")) return + let type = !!/开启/.test(this.e.msg) + + const { noBan } = Config.groupAdmin + if (noBan && type) return this.reply("❎ 白名单自动解禁已处于开启状态") + if (!noBan && !type) return this.reply("❎ 白名单自动解禁已处于关闭状态") + + Config.modify("groupAdmin", "noBan", type) + this.reply(`✅ 已${type ? "开启" : "关闭"}白名单自动解禁`) + } +} diff --git a/model/GroupBannedWords.js b/model/GroupBannedWords.js index 50b8dc9..e0f0ecd 100644 --- a/model/GroupBannedWords.js +++ b/model/GroupBannedWords.js @@ -175,4 +175,4 @@ export default new class { Data.writeJSON(`${groupId}.json`, data, this.root) this.groupTitleCach.delete(groupId) } -} \ No newline at end of file +}() diff --git a/model/State/RAM.js b/model/State/RAM.js index ddbeb78..001af84 100644 --- a/model/State/RAM.js +++ b/model/State/RAM.js @@ -3,23 +3,22 @@ import os from "os" /** 获取当前内存占用 */ export default function getMemUsage() { - // 内存使用率 - let MemUsage = (1 - os.freemem() / os.totalmem()).toFixed(2) - // 空闲内存 - let freemem = getFileSize(os.freemem()) - // 总共内存 - let totalmem = getFileSize(os.totalmem()) - // 使用内存 - let Usingmemory = getFileSize((os.totalmem() - os.freemem())) + const freeMemory = os.freemem() + const totalMemory = os.totalmem() + + const memoryUsagePercentage = (1 - freeMemory / totalMemory).toFixed(2) + const freeMem = getFileSize(freeMemory) + const totalMem = getFileSize(totalMemory) + const usingMemory = getFileSize(totalMemory - freeMemory) return { - ...Circle(MemUsage), - inner: Math.round(MemUsage * 100) + "%", + ...Circle(memoryUsagePercentage), + inner: `${Math.round(memoryUsagePercentage * 100)}%`, title: "RAM", info: [ - `总共 ${totalmem}`, - `已用 ${Usingmemory}`, - `空闲 ${freemem}` + `总共 ${totalMem}`, + `已用 ${usingMemory}`, + `空闲 ${freeMem}` ] } } diff --git a/model/State/utils.js b/model/State/utils.js index 196c290..8b2543f 100644 --- a/model/State/utils.js +++ b/model/State/utils.js @@ -20,21 +20,21 @@ export function addData(arr, data, maxLen = 60) { /** * 将文件大小从字节转化为可读性更好的格式,例如B、KB、MB、GB、TB。 * @param {number} size - 带转化的字节数。 - * @param {boolean} [isByte] - 如果为 true,则最终的文件大小显示保留 B 的后缀. - * @param {boolean} [isSuffix] - 如果为 true,则在所得到的大小后面加上 kb、mb、gb、tb 等后缀. + * @param {boolean} [showByte] - 如果为 true,则最终的文件大小显示保留 B 的后缀. + * @param {boolean} [showSuffix] - 如果为 true,则在所得到的大小后面加上 kb、mb、gb、tb 等后缀. * @returns {string} 文件大小格式转换后的字符串. */ -export function getFileSize(size, isByte = true, isSuffix = true) { // 把字节转换成正常文件大小 +export function getFileSize(size, showByte = true, showSuffix = true) { // 把字节转换成正常文件大小 if (size == null || size == undefined) return 0 let num = 1024.00 // byte - if (isByte && size < num) { + if (showByte && size < num) { return size.toFixed(2) + "B" } if (size < Math.pow(num, 2)) { - return (size / num).toFixed(2) + `K${isSuffix ? "b" : ""}` + return (size / num).toFixed(2) + `K${showSuffix ? "b" : ""}` } // kb if (size < Math.pow(num, 3)) { - return (size / Math.pow(num, 2)).toFixed(2) + `M${isSuffix ? "b" : ""}` + return (size / Math.pow(num, 2)).toFixed(2) + `M${showSuffix ? "b" : ""}` } // M if (size < Math.pow(num, 4)) { return (size / Math.pow(num, 3)).toFixed(2) + "G" diff --git a/tools/formatDuration.js b/tools/formatDuration.js index f4a0873..f70d33b 100644 --- a/tools/formatDuration.js +++ b/tools/formatDuration.js @@ -25,47 +25,75 @@ * // 输出: "1h 1m 5s" */ export default function formatDuration(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 = { + const timeObj = computeTimeObject(time, repair) + if (typeof format === "function") { + return format(timeObj) + } + + if (format === "default") { + return formatDefault(timeObj) + } + + if (typeof format === "string") { + return formatTemplate(format, timeObj) + } + + return timeObj +} +// 默认格式化逻辑拆分到单独的函数,提高代码可维护性 +function formatDefault(timeObj) { + const { day, hour, minute, second } = timeObj + let result = "" + + if (day > 0) { + result += `${day}天` + } + if (hour > 0) { + result += `${hour}小时` + } + if (minute > 0) { + result += `${minute}分` + } + if (second > 0) { + result += `${second}秒` + } + + return result +} + +// 字符串模板格式化逻辑拆分到单独的函数 +function formatTemplate(format, timeObj) { + const replaceRegexes = [ + { pattern: /dd/g, value: timeObj.day }, + { pattern: /hh/g, value: timeObj.hour }, + { pattern: /mm/g, value: timeObj.minute }, + { pattern: /ss/g, value: timeObj.second } + ] + + // 优化字符串替换逻辑 + for (const { pattern, value } of replaceRegexes) { + format = format.replace(pattern, value) + } + + return format +} + +/** + * 计算并返回表示时间的对象。 + * @param {number} time - 要计算的时间(以秒为单位)。 + * @param {boolean} [repair] - 修复小时、分钟和秒的显示格式的可选参数。如果设置为true,并且小时、分钟或秒小于10,则在值前面添加零。 + * @returns {{day: string, hour: string, minute: string, second: string}} - 包含天、小时、分钟和秒的时间对象。 + */ +function computeTimeObject(time, repair = true) { + const second = parseInt(time % 60, 10) + const minute = Math.floor(time / 60) + const hour = Math.floor(minute / 60) + const day = Math.floor(time / (24 * 60 * 60)) + + return { day, hour: repair && hour < 10 ? `0${hour}` : hour, minute: repair && minute < 10 ? `0${minute}` : minute, second: repair && second < 10 ? `0${second}` : second } - if (typeof format === "function") { - return format(timeObj) - } - - 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 - } - - return timeObj }