// Package manager 群管 package manager import ( "fmt" "math/rand" "regexp" "sort" "strconv" "strings" "time" "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" "github.com/FloatTech/floatbox/math" "github.com/FloatTech/floatbox/process" sql "github.com/FloatTech/sqlite" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/ZeroBot-Plugin/plugin/manager/timer" ) const ( hint = "====群管====\n" + "- 禁言@QQ 1分钟\n" + "- 解除禁言 @QQ\n" + "- 我要自闭 1分钟\n" + "- 开启全员禁言\n" + "- 解除全员禁言\n" + "- 升为管理@QQ\n" + "- 取消管理@QQ\n" + "- 修改名片@QQ XXX\n" + "- 修改头衔@QQ XXX\n" + "- 申请头衔 XXX\n" + "- 对信息回复: 撤回\n" + "- 踢出群聊@QQ\n" + "- 退出群聊 1234@bot\n" + "- 群聊转发 1234 XXX\n" + "- 私聊转发 0000 XXX\n" + "- 在MM月dd日的hh点mm分时(用http://url)提醒大家XXX\n" + "- 在MM月[每周 | 周几]的hh点mm分时(用http://url)提醒大家XXX\n" + "- 取消在MM月dd日的hh点mm分的提醒\n" + "- 取消在MM月[每周 | 周几]的hh点mm分的提醒\n" + "- 在\"cron\"时(用[url])提醒大家[xxx]\n" + "- 取消在\"cron\"的提醒\n" + "- 列出所有提醒\n" + "- 翻牌\n" + "- 赞我\n" + "- 对信息回复: 回应表情 [表情]\n" + "- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] [{uid}] [{gid}] [{groupname}]\n" + "- 测试欢迎语\n" + "- 设置告别辞 参数同设置欢迎语\n" + "- 测试告别辞\n" + "- [开启 | 关闭]入群验证\n" + "- 对信息回复: [设置 | 取消]精华\n" + "- 取消精华 [信息ID]\n" + "- /精华列表\n" + "Tips: {at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像 {uid}是被欢迎者QQ号 {gid}是当前群群号 {groupname} 是当前群群名" ) var ( db sql.Sqlite clock timer.Clock ) func init() { // 插件主体 engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, Brief: "群管插件", Help: hint, PrivateDataFolder: "manager", }) go func() { db = sql.New(engine.DataFolder() + "config.db") err := db.Open(time.Hour) if err != nil { panic(err) } clock = timer.NewClock(&db) err = db.Create("welcome", &welcome{}) if err != nil { panic(err) } err = db.Create("member", &member{}) if err != nil { panic(err) } err = db.Create("farewell", &welcome{}) if err != nil { panic(err) } }() // 升为管理 engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupAdmin( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被升为管理的人的qq true, ) nickname := ctx.GetThisGroupMemberInfo( // 被升为管理的人的昵称 math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被升为管理的人的qq false, ).Get("nickname").Str ctx.SendChain(message.Text(nickname + " 升为了管理~")) }) // 取消管理 engine.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupAdmin( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被取消管理的人的qq false, ) nickname := ctx.GetThisGroupMemberInfo( // 被取消管理的人的昵称 math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被取消管理的人的qq false, ).Get("nickname").Str ctx.SendChain(message.Text("残念~ " + nickname + " 暂时失去了管理员的资格")) }) // 踢出群聊 engine.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupKick( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被踢出群聊的人的qq false, ) nickname := ctx.GetThisGroupMemberInfo( // 被踢出群聊的人的昵称 math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被踢出群聊的人的qq false, ).Get("nickname").Str ctx.SendChain(message.Text("残念~ " + nickname + " 被放逐")) }) // 退出群聊 engine.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetGroupLeave( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 要退出的群的群号 true, ) }) // 开启全体禁言 engine.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupWholeBan(true) ctx.SendChain(message.Text("全员自闭开始~")) }) // 解除全员禁言 engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupWholeBan(false) ctx.SendChain(message.Text("全员自闭结束~")) }) // 禁言 engine.OnMessage(zero.NewPattern(nil).Text("^禁言").At().Text("(\\d+)\\s*(.*)").AsRule(), zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { parsed := ctx.State[zero.KeyPattern].([]zero.PatternParsed) duration := math.Str2Int64(parsed[2].Text()[1]) switch parsed[2].Text()[2] { case "分钟": // case "小时": duration *= 60 case "天": duration *= 60 * 24 default: // } if duration >= 43200 { duration = 43199 // qq禁言最大时长为一个月 } ctx.SetThisGroupBan( math.Str2Int64(parsed[1].At()), // 要禁言的人的qq duration*60, // 要禁言的时间(分钟) ) ctx.SendChain(message.Text("小黑屋收留成功~")) }) // 解除禁言 engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SetThisGroupBan( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 要解除禁言的人的qq 0, ) ctx.SendChain(message.Text("小黑屋释放成功~")) }) // 自闭禁言 engine.OnRegex(`^(我要自闭|禅定).*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { duration := math.Str2Int64(ctx.State["regex_matched"].([]string)[2]) switch ctx.State["regex_matched"].([]string)[3] { case "分钟", "min", "mins", "m": break case "小时", "hour", "hours", "h": duration *= 60 case "天", "day", "days", "d": duration *= 60 * 24 default: break } if duration >= 43200 { duration = 43199 // qq禁言最大时长为一个月 } ctx.SetThisGroupBan( ctx.Event.UserID, duration*60, // 要自闭的时间(分钟) ) ctx.SendChain(message.Text("那我就不手下留情了~")) }) // 修改名片 engine.OnRegex(`^修改名片.*?(\d+).+?\s*(.*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { if len(ctx.State["regex_matched"].([]string)[2]) > 60 { ctx.SendChain(message.Text("名字太长啦!")) return } ctx.SetThisGroupCard( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被修改群名片的人 ctx.State["regex_matched"].([]string)[2], // 修改成的群名片 ) ctx.SendChain(message.Text("嗯!已经修改了")) }) // 修改头衔 engine.OnRegex(`^修改头衔.*?(\d+).+?\s*(.*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { sptitle := ctx.State["regex_matched"].([]string)[2] if sptitle == "" { ctx.SendChain(message.Text("头衔不能为空!")) return } else if len(sptitle) > 18 { ctx.SendChain(message.Text("头衔太长啦!")) return } ctx.SetThisGroupSpecialTitle( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被修改群头衔的人 sptitle, // 修改成的群头衔 ) ctx.SendChain(message.Text("嗯!已经修改了")) }) // 申请头衔 engine.OnRegex(`^申请头衔\s*(.*)$`, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { sptitle := ctx.State["regex_matched"].([]string)[1] if sptitle == "" { ctx.SendChain(message.Text("头衔不能为空!")) return } else if len(sptitle) > 18 { ctx.SendChain(message.Text("头衔太长啦!")) return } ctx.SetThisGroupSpecialTitle( ctx.Event.UserID, // 被修改群头衔的人 sptitle, // 修改成的群头衔 ) ctx.SendChain(message.Text("嗯!不错的头衔呢~")) }) // 撤回 // 群聊中直接回复消息结尾带上撤回 // 权限够的话,可以把请求撤回的消息也一并撤回 engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\].*撤回$`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { // 删除需要撤回的消息ID ctx.DeleteMessage(ctx.State["regex_matched"].([]string)[1]) }) // 群聊转发 engine.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { // 对CQ码进行反转义 content := ctx.State["regex_matched"].([]string)[2] content = strings.ReplaceAll(content, "[", "[") content = strings.ReplaceAll(content, "]", "]") ctx.SendGroupMessage( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 需要发送的群 content, // 需要发送的信息 ) ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1])) }) // 私聊转发 engine.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { // 对CQ码进行反转义 content := ctx.State["regex_matched"].([]string)[2] content = strings.ReplaceAll(content, "[", "[") content = strings.ReplaceAll(content, "]", "]") ctx.SendPrivateMessage( math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 需要发送的人的qq content, // 需要发送的信息 ) ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1])) }) // 定时提醒 engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { dateStrs := ctx.State["regex_matched"].([]string) ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, false) if ts.En() { go clock.RegisterTimer(ts, true, false) ctx.SendChain(message.Text("记住了~")) } else { ctx.SendChain(message.Text("参数非法:" + ts.Alert)) } }) // 定时 cron 提醒 engine.OnRegex(`^在"(.*)"时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { dateStrs := ctx.State["regex_matched"].([]string) var url, alert string switch len(dateStrs) { case 4: url = strings.TrimPrefix(dateStrs[2], "用") alert = dateStrs[3] case 3: alert = dateStrs[2] default: ctx.SendChain(message.Text("参数非法!")) return } logrus.Debugln("[manager] cron:", dateStrs[1]) ts := timer.GetFilledCronTimer(dateStrs[1], alert, url, ctx.Event.SelfID, ctx.Event.GroupID) if clock.RegisterTimer(ts, true, false) { ctx.SendChain(message.Text("记住了~")) } else { ctx.SendChain(message.Text("参数非法:" + ts.Alert)) } }) // 取消定时 engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { dateStrs := ctx.State["regex_matched"].([]string) ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, true) ti := ts.GetTimerID() ok := clock.CancelTimer(ti) if ok { ctx.SendChain(message.Text("取消成功~")) } else { ctx.SendChain(message.Text("没有这个定时器哦~")) } }) // 取消 cron 定时 engine.OnRegex(`^取消在"(.*)"的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { dateStrs := ctx.State["regex_matched"].([]string) ts := timer.Timer{Cron: dateStrs[1], GrpID: ctx.Event.GroupID} ti := ts.GetTimerID() ok := clock.CancelTimer(ti) if ok { ctx.SendChain(message.Text("取消成功~")) } else { ctx.SendChain(message.Text("没有这个定时器哦~")) } }) // 列出本群所有定时 engine.OnFullMatch("列出所有提醒", zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { ctx.SendChain(message.Text(clock.ListTimers(ctx.Event.GroupID))) }) // 随机点名 engine.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser). Handle(func(ctx *zero.Ctx) { // 无缓存获取群员列表 temp := ctx.GetThisGroupMemberListNoCache().Array() sort.SliceStable(temp, func(i, j int) bool { return temp[i].Get("last_sent_time").Int() < temp[j].Get("last_sent_time").Int() }) temp = temp[math.Max(0, len(temp)-10):] who := temp[rand.Intn(len(temp))] if who.Get("user_id").Int() == ctx.Event.SelfID { ctx.SendChain(message.Text("幸运儿居然是我自己")) return } if who.Get("user_id").Int() == ctx.Event.UserID { ctx.SendChain(message.Text("哎呀,就是你自己了")) return } nick := who.Get("card").Str if nick == "" { nick = who.Get("nickname").Str } ctx.SendChain( message.Text( nick, " 就是你啦!", ), ) }) // 给好友点赞 engine.OnFullMatch("赞我").SetBlock(true).Limit(ctxext.LimitByUser). Handle(func(ctx *zero.Ctx) { list := ctx.GetFriendList().Array() flag := false for _, v := range list { if ctx.Event.UserID == v.Get("user_id").Int() { flag = true break } } if !flag { // ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("不加好友不给赞!")) return } ctx.SendLike(ctx.Event.UserID, 10) ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("给你赞了10下哦,记得回我~")) }) facere := regexp.MustCompile(`\[CQ:face,id=(\d+)\]`) // 给消息回应表情 engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\].*回应表情\s*(.+)\s*$`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { msgid := ctx.State["regex_matched"].([]string)[1] face := ctx.State["regex_matched"].([]string)[2] if len(face) == 0 { ctx.SendChain(message.Text("ERROR: 表情长度为 0")) return } ids := facere.FindStringSubmatch(face) id := rune(0) if len(ids) == 2 && len(ids[1]) > 0 { idi, err := strconv.Atoi(ids[1]) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } id = rune(idi) } else { x := []rune(face) if len(x) == 0 { ctx.SendChain(message.Text("ERROR: 解析后表情长度为 0")) return } id = x[0] } err := ctx.SetMessageEmojiLike(msgid, id) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } }) // 入群欢迎 engine.OnNotice().SetBlock(false). Handle(func(ctx *zero.Ctx) { if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID { var w welcome err := db.Find("welcome", &w, "WHERE gid = ?", ctx.Event.GroupID) if err == nil { ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) } else { ctx.SendChain(message.Text("欢迎~")) } c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) if ok { enable := c.GetData(ctx.Event.GroupID)&1 == 1 if enable { uid := ctx.Event.UserID a := rand.Intn(100) b := rand.Intn(100) r := a + b ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("考你一道题:%d+%d=?\n如果60秒之内答不上来,%s就要把你踢出去了哦~", a, b, zero.BotConfig.NickName[0]))) // 匹配发送者进行验证 rule := func(ctx *zero.Ctx) bool { for _, elem := range ctx.Event.Message { if elem.Type == "text" { text := strings.ReplaceAll(elem.Data["text"], " ", "") ans, err := strconv.Atoi(text) if err == nil { if ans != r { ctx.SendChain(message.Text("答案不对哦,再想想吧~")) return false } return true } } } return false } next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession(), rule) recv, cancel := next.Repeat() select { case <-time.After(time.Minute): cancel() ctx.SendChain(message.Text("拜拜啦~")) ctx.SetThisGroupKick(uid, false) case <-recv: cancel() ctx.SendChain(message.Text("答对啦~")) } } } } }) // 退群提醒 engine.OnNotice().SetBlock(false). Handle(func(ctx *zero.Ctx) { if ctx.Event.NoticeType == "group_decrease" { var w welcome err := db.Find("farewell", &w, "WHERE gid = ?", ctx.Event.GroupID) if err == nil { collectsend(ctx, message.ParseMessageFromString(welcometocq(ctx, w.Msg))...) } else { userid := ctx.Event.UserID collectsend(ctx, message.Text(ctx.CardOrNickName(userid), "(", userid, ")", "离开了我们...")) } } }) // 设置欢迎语 engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { welcomestring := ctx.State["regex_matched"].([]string)[1] welcomestring = message.UnescapeCQCodeText(welcomestring) w := &welcome{ GrpID: ctx.Event.GroupID, Msg: welcomestring, } err := db.Insert("welcome", w) if err == nil { ctx.SendChain(message.Text("记住啦!")) } else { ctx.SendChain(message.Text("出错啦: ", err)) } }) // 测试欢迎语 engine.OnFullMatch("测试欢迎语", zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { var w welcome err := db.Find("welcome", &w, "WHERE gid = ?", ctx.Event.GroupID) if err == nil { ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) } else { ctx.SendChain(message.Text("欢迎~")) } }) // 设置告别辞 engine.OnRegex(`^设置告别辞([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { farewellstring := ctx.State["regex_matched"].([]string)[1] farewellstring = message.UnescapeCQCodeText(farewellstring) w := &welcome{ GrpID: ctx.Event.GroupID, Msg: farewellstring, } err := db.Insert("farewell", w) if err == nil { ctx.SendChain(message.Text("记住啦!")) } else { ctx.SendChain(message.Text("出错啦: ", err)) } }) // 测试告别辞 engine.OnFullMatch("测试告别辞", zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { var w welcome err := db.Find("farewell", &w, "WHERE gid = ?", ctx.Event.GroupID) if err == nil { ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg))) } else { userid := ctx.Event.UserID ctx.SendChain(message.Text(ctx.CardOrNickName(userid), "(", userid, ")", "离开了我们...")) } }) // 入群后验证开关 engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { option := ctx.State["regex_matched"].([]string)[1] c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) if ok { data := c.GetData(ctx.Event.GroupID) switch option { case "开启", "打开", "启用": data |= 1 case "关闭", "关掉", "禁用": data &= 0x7fffffff_fffffffe default: return } err := c.SetData(ctx.Event.GroupID, data) if err == nil { ctx.SendChain(message.Text("已", option)) return } ctx.SendChain(message.Text("出错啦: ", err)) return } ctx.SendChain(message.Text("找不到服务!")) }) // 加群 gist 验证开关 engine.OnRegex(`^(.*)gist加群自动审批$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true). Handle(func(ctx *zero.Ctx) { option := ctx.State["regex_matched"].([]string)[1] c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) if ok { data := c.GetData(ctx.Event.GroupID) switch option { case "开启", "打开", "启用": data |= 0x10 case "关闭", "关掉", "禁用": data &= 0x7fffffff_fffffffd default: return } err := c.SetData(ctx.Event.GroupID, data) if err == nil { ctx.SendChain(message.Text("已", option)) return } ctx.SendChain(message.Text("出错啦: ", err)) return } ctx.SendChain(message.Text("找不到服务!")) }) // 根据 gist 自动同意加群 // 加群请在github新建一个gist,其文件名为本群群号的字符串的md5(小写),内容为一行,是当前unix时间戳(10分钟内有效)。 // 然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。 engine.On("request/group/add").SetBlock(false).Handle(func(ctx *zero.Ctx) { c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) if ok && c.GetData(ctx.Event.GroupID)&0x10 == 0x10 { // gist 文件名是群号的 ascii 编码的 md5 // gist 内容是当前 uinx 时间戳,在 10 分钟内视为有效 ans := ctx.Event.Comment[strings.Index(ctx.Event.Comment, "答案:")+len("答案:"):] divi := strings.Index(ans, "/") if divi <= 0 { ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, "格式错误!") return } ghun := ans[:divi] hash := ans[divi+1:] logrus.Debugln("[manager]收到加群申请, 用户:", ghun, ", hash:", hash) ok, reason := checkNewUser(ctx.Event.UserID, ctx.Event.GroupID, ghun, hash) if ok { ctx.SetGroupAddRequest(ctx.Event.Flag, "add", true, "") process.SleepAbout1sTo2s() ctx.SetThisGroupCard(ctx.Event.UserID, ghun) } else { ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, reason) } } }) // 设精 engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\][\s\S]*(设置|取消)精华$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { essenceID, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64) option := ctx.State["regex_matched"].([]string)[2] var rsp zero.APIResponse switch option { case "设置": rsp = ctx.SetGroupEssenceMessage(essenceID) case "取消": rsp = ctx.DeleteGroupEssenceMessage(essenceID) } if rsp.RetCode == 0 { ctx.SendChain(message.Text(option, "成功")) } else { ctx.SendChain(message.Text(option, "失败, 信息: ", rsp.Message, "解释: ", rsp.Wording)) } }) engine.OnCommand("精华列表", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { list := ctx.GetThisGroupEssenceMessageList().Array() msg := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text("本群精华列表:"))} n := len(list) if n > 30 { ctx.SendChain(message.Text("精华内容太多,仅显示前30个")) n = 30 } for _, info := range list[:n] { msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(fmt.Sprintf( "信息ID: %d\n发送者昵称: %s\n发送者QQ 号: %d\n消息发送时间: %s\n操作者昵称: %s\n操作者QQ 号: %d\n精华设置时间: %s", info.Get("message_id").Int(), info.Get("sender_nick").String(), info.Get("sender_id").Int(), time.Unix(info.Get("sender_time").Int(), 0).Format("2006/01/02 15:04:05"), info.Get("operator_nick").String(), info.Get("operator_id").Int(), time.Unix(info.Get("operator_time").Int(), 0).Format("2006/01/02 15:04:05"), ))), ) msgData := ctx.GetMessage(info.Get("message_id").Int()).Elements if msgData != nil { msg = append(msg, message.CustomNode(info.Get("sender_nick").String(), info.Get("sender_id").Int(), msgData), ) } else { msg = append(msg, message.CustomNode(info.Get("sender_nick").String(), info.Get("sender_id").Int(), "[error]信息久远,无法获取,如需查看原始内容请在“精华信息”中查看"), ) } } if id := ctx.Send(msg).ID(); id == 0 { ctx.SendChain(message.Text("ERROR: 可能被风控了")) } }) engine.OnPrefix("取消精华", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { essenceID, err := strconv.ParseInt(strings.TrimSpace(ctx.State["args"].(string)), 10, 64) if err != nil { ctx.SendChain(message.Text("ERROR: 请输入正确的设精ID")) return } rsp := ctx.DeleteGroupEssenceMessage(essenceID) if rsp.RetCode == 0 { ctx.SendChain(message.Text("取消成功")) } else { ctx.SendChain(message.Text("取消失败, 信息: ", rsp.Message, "解释: ", rsp.Wording)) } }) } // 传入 ctx 和 welcome格式string 返回cq格式string 使用方法:welcometocq(ctx,w.Msg) func welcometocq(ctx *zero.Ctx, welcome string) string { uid := strconv.FormatInt(ctx.Event.UserID, 10) // 用户id nickname := ctx.CardOrNickName(ctx.Event.UserID) // 用户昵称 at := "[CQ:at,qq=" + uid + "]" // at用户 avatar := "[CQ:image,file=" + "https://q4.qlogo.cn/g?b=qq&nk=" + uid + "&s=640]" // 用户头像 gid := strconv.FormatInt(ctx.Event.GroupID, 10) // 群id groupname := ctx.GetThisGroupInfo(true).Name // 群名 cqstring := strings.ReplaceAll(welcome, "{at}", at) cqstring = strings.ReplaceAll(cqstring, "{nickname}", nickname) cqstring = strings.ReplaceAll(cqstring, "{avatar}", avatar) cqstring = strings.ReplaceAll(cqstring, "{uid}", uid) cqstring = strings.ReplaceAll(cqstring, "{gid}", gid) cqstring = strings.ReplaceAll(cqstring, "{groupname}", groupname) return cqstring }