mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-12 18:20:27 +00:00
🎨 优化目录结构
This commit is contained in:
44
plugin/manager/gist.go
Normal file
44
plugin/manager/gist.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/zbputils/math"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
)
|
||||
|
||||
// user hash file
|
||||
const gistraw = "https://gist.githubusercontent.com/%s/%s/raw/%s"
|
||||
|
||||
func checkNewUser(qq, gid int64, ghun, hash string) (bool, string) {
|
||||
if db.CanFind("member", "where ghun="+ghun) {
|
||||
return false, "该github用户已入群"
|
||||
}
|
||||
gidsum := md5.Sum(helper.StringToBytes(strconv.FormatInt(gid, 10)))
|
||||
gidhex := hex.EncodeToString(gidsum[:])
|
||||
u := fmt.Sprintf(gistraw, ghun, hash, gidhex)
|
||||
logrus.Debugln("[gist]visit url:", u)
|
||||
data, err := web.GetData(u)
|
||||
if err == nil {
|
||||
logrus.Debugln("[gist]get data:", helper.BytesToString(data))
|
||||
st, err := strconv.ParseInt(helper.BytesToString(data), 10, 64)
|
||||
if err == nil {
|
||||
// 600s 内验证成功
|
||||
ok := math.Abs(int(time.Now().Unix()-st)) < 600
|
||||
if ok {
|
||||
_ = db.Insert("member", &member{QQ: qq, Ghun: ghun})
|
||||
return true, ""
|
||||
}
|
||||
return false, "时间戳超时"
|
||||
}
|
||||
return false, "时间戳格式错误: " + helper.BytesToString(data)
|
||||
}
|
||||
return false, "无法连接到gist: " + err.Error()
|
||||
}
|
||||
12
plugin/manager/manager.db.go
Normal file
12
plugin/manager/manager.db.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package manager
|
||||
|
||||
type welcome struct {
|
||||
GrpID int64 `db:"gid"`
|
||||
Msg string `db:"msg"`
|
||||
}
|
||||
|
||||
type member struct {
|
||||
QQ int64 `db:"qq"`
|
||||
// github username
|
||||
Ghun string `db:"ghun"`
|
||||
}
|
||||
552
plugin/manager/manager.go
Normal file
552
plugin/manager/manager.go
Normal file
@@ -0,0 +1,552 @@
|
||||
// Package manager 群管
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/math"
|
||||
"github.com/FloatTech/zbputils/process"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
|
||||
"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" +
|
||||
"- 踢出群聊@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" +
|
||||
"- 设置欢迎语XXX(可加{at}在欢迎时@对方)\n" +
|
||||
"- 测试欢迎语\n" +
|
||||
"- [开启 | 关闭]入群验证"
|
||||
)
|
||||
|
||||
var (
|
||||
db = &sql.Sqlite{}
|
||||
clock timer.Clock
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("manager", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: hint,
|
||||
PrivateDataFolder: "manager",
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer order.DoneOnExit()()
|
||||
db.DBPath = engine.DataFolder() + "config.db"
|
||||
clock = timer.NewClock(db)
|
||||
err := db.Create("welcome", &welcome{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = db.Create("member", &member{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 升为管理
|
||||
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupAdmin(
|
||||
ctx.Event.GroupID,
|
||||
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被升为管理的人的qq
|
||||
true,
|
||||
)
|
||||
nickname := ctx.GetGroupMemberInfo( // 被升为管理的人的昵称
|
||||
ctx.Event.GroupID,
|
||||
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.SetGroupAdmin(
|
||||
ctx.Event.GroupID,
|
||||
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被取消管理的人的qq
|
||||
false,
|
||||
)
|
||||
nickname := ctx.GetGroupMemberInfo( // 被取消管理的人的昵称
|
||||
ctx.Event.GroupID,
|
||||
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.SetGroupKick(
|
||||
ctx.Event.GroupID,
|
||||
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被踢出群聊的人的qq
|
||||
false,
|
||||
)
|
||||
nickname := ctx.GetGroupMemberInfo( // 被踢出群聊的人的昵称
|
||||
ctx.Event.GroupID,
|
||||
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.SetGroupWholeBan(
|
||||
ctx.Event.GroupID,
|
||||
true,
|
||||
)
|
||||
ctx.SendChain(message.Text("全员自闭开始~"))
|
||||
})
|
||||
// 解除全员禁言
|
||||
engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupWholeBan(
|
||||
ctx.Event.GroupID,
|
||||
false,
|
||||
)
|
||||
ctx.SendChain(message.Text("全员自闭结束~"))
|
||||
})
|
||||
// 禁言
|
||||
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
duration := math.Str2Int64(ctx.State["regex_matched"].([]string)[2])
|
||||
switch ctx.State["regex_matched"].([]string)[3] {
|
||||
case "分钟":
|
||||
//
|
||||
case "小时":
|
||||
duration *= 60
|
||||
case "天":
|
||||
duration *= 60 * 24
|
||||
default:
|
||||
//
|
||||
}
|
||||
if duration >= 43200 {
|
||||
duration = 43199 // qq禁言最大时长为一个月
|
||||
}
|
||||
ctx.SetGroupBan(
|
||||
ctx.Event.GroupID,
|
||||
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 要禁言的人的qq
|
||||
duration*60, // 要禁言的时间(分钟)
|
||||
)
|
||||
ctx.SendChain(message.Text("小黑屋收留成功~"))
|
||||
})
|
||||
// 解除禁言
|
||||
engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupBan(
|
||||
ctx.Event.GroupID,
|
||||
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.SetGroupBan(
|
||||
ctx.Event.GroupID,
|
||||
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.SetGroupCard(
|
||||
ctx.Event.GroupID,
|
||||
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) {
|
||||
if len(ctx.State["regex_matched"].([]string)[1]) > 18 {
|
||||
ctx.SendChain(message.Text("头衔太长啦!"))
|
||||
return
|
||||
}
|
||||
ctx.SetGroupSpecialTitle(
|
||||
ctx.Event.GroupID,
|
||||
math.Str2Int64(ctx.State["regex_matched"].([]string)[1]), // 被修改群头衔的人
|
||||
ctx.State["regex_matched"].([]string)[2], // 修改成的群头衔
|
||||
)
|
||||
ctx.SendChain(message.Text("嗯!已经修改了"))
|
||||
})
|
||||
// 申请头衔
|
||||
engine.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if len(ctx.State["regex_matched"].([]string)[1]) > 18 {
|
||||
ctx.SendChain(message.Text("头衔太长啦!"))
|
||||
return
|
||||
}
|
||||
ctx.SetGroupSpecialTitle(
|
||||
ctx.Event.GroupID,
|
||||
ctx.Event.UserID, // 被修改群头衔的人
|
||||
ctx.State["regex_matched"].([]string)[1], // 修改成的群头衔
|
||||
)
|
||||
ctx.SendChain(message.Text("嗯!不错的头衔呢~"))
|
||||
})
|
||||
// 群聊转发
|
||||
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)
|
||||
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 = 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) {
|
||||
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) {
|
||||
// 无缓存获取群员列表
|
||||
list := ctx.CallAction("get_group_member_list", zero.Params{
|
||||
"group_id": ctx.Event.GroupID,
|
||||
"no_cache": true,
|
||||
}).Data
|
||||
temp := list.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.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 = "+strconv.FormatInt(ctx.Event.GroupID, 10))
|
||||
if err == nil {
|
||||
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(strings.ReplaceAll(w.Msg, "{at}", "[CQ:at,qq="+strconv.FormatInt(ctx.Event.UserID, 10)+"]")))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("欢迎~"))
|
||||
}
|
||||
c, ok := control.Lookup("manager")
|
||||
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, zero.CheckUser(ctx.Event.UserID), rule)
|
||||
recv, cancel := next.Repeat()
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
ctx.SendChain(message.Text("拜拜啦~"))
|
||||
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
|
||||
cancel()
|
||||
case <-recv:
|
||||
cancel()
|
||||
ctx.SendChain(message.Text("答对啦~"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 退群提醒
|
||||
engine.OnNotice().SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.NoticeType == "group_decrease" {
|
||||
userid := ctx.Event.UserID
|
||||
ctx.SendChain(message.Text(ctxext.CardOrNickName(ctx, userid), "(", userid, ")", "离开了我们..."))
|
||||
}
|
||||
})
|
||||
// 设置欢迎语
|
||||
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
w := &welcome{
|
||||
GrpID: ctx.Event.GroupID,
|
||||
Msg: ctx.State["regex_matched"].([]string)[1],
|
||||
}
|
||||
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 = "+strconv.FormatInt(ctx.Event.GroupID, 10))
|
||||
if err == nil {
|
||||
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(strings.ReplaceAll(w.Msg, "{at}", "[CQ:at,qq="+strconv.FormatInt(ctx.Event.UserID, 10)+"]")))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("欢迎~"))
|
||||
}
|
||||
})
|
||||
// 入群后验证开关
|
||||
engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
c, ok := control.Lookup("manager")
|
||||
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 := control.Lookup("manager")
|
||||
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("找不到服务!"))
|
||||
})
|
||||
// 运行 CQ 码
|
||||
engine.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var cmd = ctx.State["regex_matched"].([]string)[1]
|
||||
cmd = strings.ReplaceAll(cmd, "[", "[")
|
||||
cmd = strings.ReplaceAll(cmd, "]", "]")
|
||||
// 可注入,权限为主人
|
||||
ctx.Send(cmd)
|
||||
})
|
||||
// 根据 gist 自动同意加群
|
||||
// 加群请在github新建一个gist,其文件名为本群群号的字符串的md5(小写),内容为一行,是当前unix时间戳(10分钟内有效)。
|
||||
// 然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
|
||||
engine.OnRequest().SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
/*if ctx.Event.RequestType == "friend" {
|
||||
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
|
||||
}*/
|
||||
c, ok := control.Lookup("manager")
|
||||
if ok && c.GetData(ctx.Event.GroupID)&0x10 == 0x10 && ctx.Event.RequestType == "group" && ctx.Event.SubType == "add" {
|
||||
// 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.Infoln("[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.SetGroupCard(ctx.Event.GroupID, ctx.Event.UserID, ghun)
|
||||
} else {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, reason)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
16
plugin/manager/timer/msg.go
Normal file
16
plugin/manager/timer/msg.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func (t *Timer) sendmsg(grp int64, ctx *zero.Ctx) {
|
||||
ctx.Event = new(zero.Event)
|
||||
ctx.Event.GroupID = grp
|
||||
if t.URL == "" {
|
||||
ctx.SendChain(atall, message.Text(t.Alert))
|
||||
} else {
|
||||
ctx.SendChain(atall, message.Text(t.Alert), message.Image(t.URL).Add("cache", "0"))
|
||||
}
|
||||
}
|
||||
166
plugin/manager/timer/parse.go
Normal file
166
plugin/manager/timer/parse.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
// GetTimerInfo 获得标准化定时字符串
|
||||
func (t *Timer) GetTimerInfo() string {
|
||||
if t.Cron != "" {
|
||||
return fmt.Sprintf("[%d]%s", t.GrpID, t.Cron)
|
||||
}
|
||||
return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", t.GrpID, t.Month(), t.Day(), t.Week(), t.Hour(), t.Minute())
|
||||
}
|
||||
|
||||
// GetTimerID 获得标准化 ID
|
||||
func (t *Timer) GetTimerID() uint32 {
|
||||
key := t.GetTimerInfo()
|
||||
m := md5.Sum(helper.StringToBytes(key))
|
||||
return binary.LittleEndian.Uint32(m[:4])
|
||||
}
|
||||
|
||||
// GetFilledCronTimer 获得以cron填充好的ts
|
||||
func GetFilledCronTimer(croncmd string, alert string, img string, botqq, gid int64) *Timer {
|
||||
var t Timer
|
||||
t.Alert = alert
|
||||
t.Cron = croncmd
|
||||
t.URL = img
|
||||
t.SelfID = botqq
|
||||
t.GrpID = gid
|
||||
return &t
|
||||
}
|
||||
|
||||
// GetFilledTimer 获得填充好的ts
|
||||
func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Timer {
|
||||
monthStr := []rune(dateStrs[1])
|
||||
dayWeekStr := []rune(dateStrs[2])
|
||||
hourStr := []rune(dateStrs[3])
|
||||
minuteStr := []rune(dateStrs[4])
|
||||
|
||||
var t Timer
|
||||
mon := time.Month(chineseNum2Int(monthStr))
|
||||
if (mon != -1 && mon <= 0) || mon > 12 { // 月份非法
|
||||
t.Alert = "月份非法!"
|
||||
return &t
|
||||
}
|
||||
t.SetMonth(mon)
|
||||
lenOfDW := len(dayWeekStr)
|
||||
switch {
|
||||
case lenOfDW == 4: // 包括末尾的"日"
|
||||
dayWeekStr = []rune{dayWeekStr[0], dayWeekStr[2]} // 去除中间的十
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
t.Alert = "日期非法1!"
|
||||
return &t
|
||||
}
|
||||
t.SetDay(d)
|
||||
case dayWeekStr[lenOfDW-1] == rune('日'): // xx日
|
||||
dayWeekStr = dayWeekStr[:lenOfDW-1]
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
t.Alert = "日期非法2!"
|
||||
return &t
|
||||
}
|
||||
t.SetDay(d)
|
||||
case dayWeekStr[0] == rune('每'): // 每周
|
||||
t.SetWeek(-1)
|
||||
default: // 周x
|
||||
w := chineseNum2Int(dayWeekStr[1:])
|
||||
if w == 7 { // 周天是0
|
||||
w = 0
|
||||
}
|
||||
if w < 0 || w > 6 { // 星期非法
|
||||
t.Alert = "星期非法!"
|
||||
return &t
|
||||
}
|
||||
t.SetWeek(time.Weekday(w))
|
||||
}
|
||||
if len(hourStr) == 3 {
|
||||
hourStr = []rune{hourStr[0], hourStr[2]} // 去除中间的十
|
||||
}
|
||||
h := chineseNum2Int(hourStr)
|
||||
if h < -1 || h > 23 { // 小时非法
|
||||
t.Alert = "小时非法!"
|
||||
return &t
|
||||
}
|
||||
t.SetHour(h)
|
||||
if len(minuteStr) == 3 {
|
||||
minuteStr = []rune{minuteStr[0], minuteStr[2]} // 去除中间的十
|
||||
}
|
||||
min := chineseNum2Int(minuteStr)
|
||||
if min < -1 || min > 59 { // 分钟非法
|
||||
t.Alert = "分钟非法!"
|
||||
return &t
|
||||
}
|
||||
t.SetMinute(min)
|
||||
if !matchDateOnly {
|
||||
urlStr := dateStrs[5]
|
||||
if urlStr != "" { // 是图片url
|
||||
t.URL = urlStr[3:] // utf-8下用为3字节
|
||||
logrus.Println("[群管]" + t.URL)
|
||||
if !strings.HasPrefix(t.URL, "http") {
|
||||
t.URL = "illegal"
|
||||
logrus.Println("[群管]url非法!")
|
||||
return &t
|
||||
}
|
||||
}
|
||||
t.Alert = dateStrs[6]
|
||||
t.SetEn(true)
|
||||
}
|
||||
t.SelfID = botqq
|
||||
t.GrpID = grp
|
||||
return &t
|
||||
}
|
||||
|
||||
// chineseNum2Int 汉字数字转int,仅支持-10~99,最多两位数,其中"每"解释为-1,"每二"为-2,以此类推
|
||||
func chineseNum2Int(rs []rune) int {
|
||||
r := -1
|
||||
l := len(rs)
|
||||
mai := rune('每')
|
||||
if unicode.IsDigit(rs[0]) { // 默认可能存在的第二位也为int
|
||||
r, _ = strconv.Atoi(string(rs))
|
||||
} else {
|
||||
switch {
|
||||
case rs[0] == mai:
|
||||
if l == 2 {
|
||||
r = -chineseChar2Int(rs[1])
|
||||
}
|
||||
case l == 1:
|
||||
r = chineseChar2Int(rs[0])
|
||||
default:
|
||||
ten := chineseChar2Int(rs[0])
|
||||
if ten != 10 {
|
||||
ten *= 10
|
||||
}
|
||||
ge := chineseChar2Int(rs[1])
|
||||
if ge == 10 {
|
||||
ge = 0
|
||||
}
|
||||
r = ten + ge
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// chineseChar2Int 处理单个字符的映射0~10
|
||||
func chineseChar2Int(c rune) int {
|
||||
if c == rune('日') || c == rune('天') { // 周日/周天
|
||||
return 7
|
||||
}
|
||||
match := []rune("零一二三四五六七八九十")
|
||||
for i, m := range match {
|
||||
if c == m {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
165
plugin/manager/timer/sleep.go
Normal file
165
plugin/manager/timer/sleep.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
func firstWeek(date *time.Time, week time.Weekday) (d time.Time) {
|
||||
d = date.AddDate(0, 0, 1-date.Day())
|
||||
for d.Weekday() != week {
|
||||
d = d.AddDate(0, 0, 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Timer) nextWakeTime() (date time.Time) {
|
||||
date = time.Now()
|
||||
m := t.Month()
|
||||
d := t.Day()
|
||||
h := t.Hour()
|
||||
mn := t.Minute()
|
||||
w := t.Week()
|
||||
var unit time.Duration
|
||||
logrus.Debugln("[timer] unit init:", unit)
|
||||
if mn >= 0 {
|
||||
switch {
|
||||
case h < 0:
|
||||
if unit <= time.Second {
|
||||
unit = time.Hour
|
||||
}
|
||||
case d < 0 || w < 0:
|
||||
if unit <= time.Second {
|
||||
unit = time.Hour * 24
|
||||
}
|
||||
case d == 0 && w >= 0:
|
||||
delta := time.Hour * 24 * time.Duration(int(w)-int(date.Weekday()))
|
||||
if delta < 0 {
|
||||
delta = time.Hour * 24 * 7
|
||||
}
|
||||
unit += delta
|
||||
case m < 0:
|
||||
unit = -1
|
||||
}
|
||||
} else {
|
||||
unit = time.Minute
|
||||
}
|
||||
logrus.Debugln("[timer] unit:", unit)
|
||||
stable := 0
|
||||
if mn < 0 {
|
||||
mn = date.Minute()
|
||||
}
|
||||
if h < 0 {
|
||||
h = date.Hour()
|
||||
} else {
|
||||
stable |= 0x8
|
||||
}
|
||||
switch {
|
||||
case d < 0:
|
||||
d = date.Day()
|
||||
case d > 0:
|
||||
stable |= 0x4
|
||||
default:
|
||||
d = date.Day()
|
||||
if w >= 0 {
|
||||
stable |= 0x2
|
||||
}
|
||||
}
|
||||
if m < 0 {
|
||||
m = date.Month()
|
||||
} else {
|
||||
stable |= 0x1
|
||||
}
|
||||
switch stable {
|
||||
case 0b0101:
|
||||
if t.Day() != time.Now().Day() || t.Month() != time.Now().Month() {
|
||||
h = 0
|
||||
}
|
||||
case 0b1001:
|
||||
if t.Month() != time.Now().Month() {
|
||||
d = 0
|
||||
}
|
||||
case 0b0001:
|
||||
if t.Month() != time.Now().Month() {
|
||||
d = 0
|
||||
h = 0
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] stable:", stable)
|
||||
logrus.Debugln("[timer] m:", m, "d:", d, "h:", h, "mn:", mn, "w:", w)
|
||||
date = time.Date(date.Year(), m, d, h, mn, date.Second(), date.Nanosecond(), date.Location())
|
||||
logrus.Debugln("[timer] date original:", date)
|
||||
if unit > 0 {
|
||||
date = date.Add(unit)
|
||||
}
|
||||
logrus.Debugln("[timer] date after add:", date)
|
||||
if time.Until(date) <= 0 {
|
||||
if t.Month() < 0 {
|
||||
if t.Day() > 0 || (t.Day() == 0 && t.Week() >= 0) {
|
||||
date = date.AddDate(0, 1, 0)
|
||||
} else if t.Day() < 0 || t.Week() < 0 {
|
||||
if t.Hour() > 0 {
|
||||
date = date.AddDate(0, 0, 1)
|
||||
} else if t.Minute() > 0 {
|
||||
date = date.Add(time.Hour)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
date = date.AddDate(1, 0, 0)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after fix:", date)
|
||||
if stable&0x8 != 0 && date.Hour() != h {
|
||||
switch {
|
||||
case stable&0x4 == 0:
|
||||
date = date.AddDate(0, 0, 1).Add(-time.Hour)
|
||||
case stable&0x2 == 0:
|
||||
date = date.AddDate(0, 0, 7).Add(-time.Hour)
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, 0).Add(-time.Hour)
|
||||
default:
|
||||
date = date.AddDate(1, 0, 0).Add(-time.Hour)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after s8:", date)
|
||||
if stable&0x4 != 0 && date.Day() != d {
|
||||
switch {
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, -1)
|
||||
default:
|
||||
date = date.AddDate(1, 0, -1)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after s4:", date)
|
||||
if stable&0x2 != 0 && date.Weekday() != w {
|
||||
switch {
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, 0)
|
||||
default:
|
||||
date = date.AddDate(1, 0, 0)
|
||||
}
|
||||
date = firstWeek(&date, w)
|
||||
}
|
||||
logrus.Debugln("[timer] date after s2:", date)
|
||||
if time.Until(date) <= 0 {
|
||||
date = time.Now().Add(time.Minute)
|
||||
}
|
||||
return date
|
||||
}
|
||||
|
||||
func (t *Timer) judgeHM() {
|
||||
if t.Hour() < 0 || t.Hour() == time.Now().Hour() {
|
||||
if t.Minute() < 0 || t.Minute() == time.Now().Minute() {
|
||||
if t.SelfID != 0 {
|
||||
t.sendmsg(t.GrpID, zero.GetBot(t.SelfID))
|
||||
} else {
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) (_ bool) {
|
||||
t.sendmsg(t.GrpID, ctx)
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
plugin/manager/timer/timer.db.go
Normal file
28
plugin/manager/timer/timer.db.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
// Timer 计时器
|
||||
type Timer struct {
|
||||
ID uint32 `db:"id"`
|
||||
En1Month4Day5Week3Hour5Min6 int32 `db:"emdwhm"`
|
||||
SelfID int64 `db:"sid"`
|
||||
GrpID int64 `db:"gid"`
|
||||
Alert string `db:"alert"`
|
||||
Cron string `db:"cron"`
|
||||
URL string `db:"url"`
|
||||
}
|
||||
|
||||
// InsertInto 插入自身
|
||||
func (t *Timer) InsertInto(db *sql.Sqlite) error {
|
||||
return db.Insert("timer", t)
|
||||
}
|
||||
|
||||
/*
|
||||
func getTimerFrom(db *sql.Sqlite, id uint32) (t Timer, err error) {
|
||||
err = db.Find("timer", &t, "where id = "+strconv.Itoa(int(id)))
|
||||
return
|
||||
}
|
||||
*/
|
||||
194
plugin/manager/timer/timer.go
Normal file
194
plugin/manager/timer/timer.go
Normal file
@@ -0,0 +1,194 @@
|
||||
// Package timer 群管定时器
|
||||
package timer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/fumiama/cron"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
// Clock 时钟
|
||||
type Clock struct {
|
||||
db *sql.Sqlite
|
||||
timers *(map[uint32]*Timer)
|
||||
timersmu sync.RWMutex
|
||||
// cron 定时器
|
||||
cron *cron.Cron
|
||||
// entries key <-> cron
|
||||
entries map[uint32]cron.EntryID
|
||||
entmu sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
// @全体成员
|
||||
atall = message.MessageSegment{
|
||||
Type: "at",
|
||||
Data: map[string]string{
|
||||
"qq": "all",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// NewClock 添加一个新时钟
|
||||
func NewClock(db *sql.Sqlite) (c Clock) {
|
||||
c.cron = cron.New()
|
||||
c.entries = make(map[uint32]cron.EntryID)
|
||||
c.timers = &map[uint32]*Timer{}
|
||||
c.loadTimers(db)
|
||||
c.cron.Start()
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterTimer 注册计时器
|
||||
func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
|
||||
var key uint32
|
||||
if save {
|
||||
key = ts.GetTimerID()
|
||||
ts.ID = key
|
||||
} else {
|
||||
key = ts.ID
|
||||
}
|
||||
t, ok := c.GetTimer(key)
|
||||
if t != ts && ok { // 避免重复注册定时器
|
||||
t.SetEn(false)
|
||||
}
|
||||
logrus.Println("[群管]注册计时器", key)
|
||||
if ts.Cron != "" {
|
||||
var ctx *zero.Ctx
|
||||
if ts.SelfID != 0 {
|
||||
ctx = zero.GetBot(ts.SelfID)
|
||||
} else {
|
||||
zero.RangeBot(func(id int64, c *zero.Ctx) bool {
|
||||
ctx = c
|
||||
ts.SelfID = id
|
||||
return false
|
||||
})
|
||||
}
|
||||
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(ts.GrpID, ctx) })
|
||||
if err == nil {
|
||||
c.entmu.Lock()
|
||||
c.entries[key] = eid
|
||||
c.entmu.Unlock()
|
||||
if save {
|
||||
err = c.AddTimerIntoDB(ts)
|
||||
}
|
||||
if err == nil {
|
||||
err = c.AddTimerIntoMap(ts)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
ts.Alert = err.Error()
|
||||
} else {
|
||||
if save {
|
||||
_ = c.AddTimerIntoDB(ts)
|
||||
}
|
||||
_ = c.AddTimerIntoMap(ts)
|
||||
for ts.En() {
|
||||
nextdate := ts.nextWakeTime()
|
||||
sleepsec := time.Until(nextdate)
|
||||
logrus.Printf("[群管]计时器%08x将睡眠%ds", key, sleepsec/time.Second)
|
||||
time.Sleep(sleepsec)
|
||||
if ts.En() {
|
||||
if ts.Month() < 0 || ts.Month() == time.Now().Month() {
|
||||
if ts.Day() < 0 || ts.Day() == time.Now().Day() {
|
||||
ts.judgeHM()
|
||||
} else if ts.Day() == 0 {
|
||||
if ts.Week() < 0 || ts.Week() == time.Now().Weekday() {
|
||||
ts.judgeHM()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CancelTimer 取消计时器
|
||||
func (c *Clock) CancelTimer(key uint32) bool {
|
||||
t, ok := c.GetTimer(key)
|
||||
if ok {
|
||||
if t.Cron != "" {
|
||||
c.entmu.Lock()
|
||||
e := c.entries[key]
|
||||
c.cron.Remove(e)
|
||||
delete(c.entries, key)
|
||||
c.entmu.Unlock()
|
||||
} else {
|
||||
t.SetEn(false)
|
||||
}
|
||||
c.timersmu.Lock()
|
||||
delete(*c.timers, key) // 避免重复取消
|
||||
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key)))
|
||||
c.timersmu.Unlock()
|
||||
return e == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListTimers 列出本群所有计时器
|
||||
func (c *Clock) ListTimers(grpID int64) []string {
|
||||
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
|
||||
if c.timers != nil {
|
||||
c.timersmu.RLock()
|
||||
keys := make([]string, 0, len(*c.timers))
|
||||
for _, v := range *c.timers {
|
||||
if v.GrpID == grpID {
|
||||
k := v.GetTimerInfo()
|
||||
start := strings.Index(k, "]")
|
||||
msg := strings.ReplaceAll(k[start+1:]+"\n", "-1", "每")
|
||||
msg = strings.ReplaceAll(msg, "月0日0周", "月周天")
|
||||
msg = strings.ReplaceAll(msg, "月0日", "月")
|
||||
msg = strings.ReplaceAll(msg, "日0周", "日")
|
||||
keys = append(keys, msg)
|
||||
}
|
||||
}
|
||||
c.timersmu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTimer 获得定时器
|
||||
func (c *Clock) GetTimer(key uint32) (t *Timer, ok bool) {
|
||||
c.timersmu.RLock()
|
||||
t, ok = (*c.timers)[key]
|
||||
c.timersmu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// AddTimerIntoDB 添加定时器
|
||||
func (c *Clock) AddTimerIntoDB(t *Timer) (err error) {
|
||||
c.timersmu.Lock()
|
||||
err = c.db.Insert("timer", t)
|
||||
c.timersmu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// AddTimerIntoMap 添加定时器到缓存
|
||||
func (c *Clock) AddTimerIntoMap(t *Timer) (err error) {
|
||||
c.timersmu.Lock()
|
||||
(*c.timers)[t.ID] = t
|
||||
c.timersmu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Clock) loadTimers(db *sql.Sqlite) {
|
||||
c.db = db
|
||||
err := c.db.Create("timer", &Timer{})
|
||||
if err == nil {
|
||||
var t Timer
|
||||
_ = c.db.FindFor("timer", &t, "", func() error {
|
||||
tescape := t
|
||||
go c.RegisterTimer(&tescape, false)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
33
plugin/manager/timer/timer_test.go
Normal file
33
plugin/manager/timer/timer_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestNextWakeTime(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
ts := &Timer{}
|
||||
ts.SetMonth(-1)
|
||||
ts.SetWeek(6)
|
||||
ts.SetHour(16)
|
||||
ts.SetMinute(30)
|
||||
t1 := time.Until(ts.nextWakeTime())
|
||||
if t1 < 0 {
|
||||
t.Log(t1)
|
||||
t.Fail()
|
||||
}
|
||||
t.Log(t1)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
func TestClock(t *testing.T) {
|
||||
db := &sql.Sqlite{DBPath: "test.db"}
|
||||
c := NewClock(db)
|
||||
c.AddTimerIntoDB(GetFilledTimer([]string{"", "12", "-1", "12", "0", "", "test"}, 0, 0, false))
|
||||
t.Log(c.ListTimers(0))
|
||||
t.Fail()
|
||||
}
|
||||
87
plugin/manager/timer/wrap.go
Normal file
87
plugin/manager/timer/wrap.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package timer
|
||||
|
||||
import "time"
|
||||
|
||||
// En isEnabled 1bit
|
||||
func (t *Timer) En() (en bool) {
|
||||
return t.En1Month4Day5Week3Hour5Min6&0x800000 != 0
|
||||
}
|
||||
|
||||
// Month 4bits
|
||||
func (t *Timer) Month() (mon time.Month) {
|
||||
mon = time.Month((t.En1Month4Day5Week3Hour5Min6 & 0x780000) >> 19)
|
||||
if mon == 0b1111 {
|
||||
mon = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Day 5bits
|
||||
func (t *Timer) Day() (d int) {
|
||||
d = int((t.En1Month4Day5Week3Hour5Min6 & 0x07c000) >> 14)
|
||||
if d == 0b11111 {
|
||||
d = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Week 3bits
|
||||
func (t *Timer) Week() (w time.Weekday) {
|
||||
w = time.Weekday((t.En1Month4Day5Week3Hour5Min6 & 0x003800) >> 11)
|
||||
if w == 0b111 {
|
||||
w = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Hour 5bits
|
||||
func (t *Timer) Hour() (h int) {
|
||||
h = int((t.En1Month4Day5Week3Hour5Min6 & 0x0007c0) >> 6)
|
||||
if h == 0b11111 {
|
||||
h = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Minute 6bits
|
||||
func (t *Timer) Minute() (min int) {
|
||||
min = int(t.En1Month4Day5Week3Hour5Min6 & 0x00003f)
|
||||
if min == 0b111111 {
|
||||
min = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetEn ...
|
||||
func (t *Timer) SetEn(en bool) {
|
||||
if en {
|
||||
t.En1Month4Day5Week3Hour5Min6 |= 0x800000
|
||||
} else {
|
||||
t.En1Month4Day5Week3Hour5Min6 &= 0x7fffff
|
||||
}
|
||||
}
|
||||
|
||||
// SetMonth ...
|
||||
func (t *Timer) SetMonth(mon time.Month) {
|
||||
t.En1Month4Day5Week3Hour5Min6 = ((int32(mon) << 19) & 0x780000) | (t.En1Month4Day5Week3Hour5Min6 & 0x87ffff)
|
||||
}
|
||||
|
||||
// SetDay ...
|
||||
func (t *Timer) SetDay(d int) {
|
||||
t.En1Month4Day5Week3Hour5Min6 = ((int32(d) << 14) & 0x07c000) | (t.En1Month4Day5Week3Hour5Min6 & 0xf83fff)
|
||||
}
|
||||
|
||||
// SetWeek ...
|
||||
func (t *Timer) SetWeek(w time.Weekday) {
|
||||
t.En1Month4Day5Week3Hour5Min6 = ((int32(w) << 11) & 0x003800) | (t.En1Month4Day5Week3Hour5Min6 & 0xffc7ff)
|
||||
}
|
||||
|
||||
// SetHour ...
|
||||
func (t *Timer) SetHour(h int) {
|
||||
t.En1Month4Day5Week3Hour5Min6 = ((int32(h) << 6) & 0x0007c0) | (t.En1Month4Day5Week3Hour5Min6 & 0xfff83f)
|
||||
}
|
||||
|
||||
// SetMinute ...
|
||||
func (t *Timer) SetMinute(min int) {
|
||||
t.En1Month4Day5Week3Hour5Min6 = (int32(min) & 0x00003f) | (t.En1Month4Day5Week3Hour5Min6 & 0xffffc0)
|
||||
}
|
||||
Reference in New Issue
Block a user