ZeroBot-Plugin/plugin/manager/manager.go
2025-03-30 21:36:31 +09:00

723 lines
26 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
}