diff --git a/README.md b/README.md index 6a9872b9..b32c24c2 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ zerobot [-h] [-t token] [-u url] [-n nickname] [-p prefix] [-d|w] [-g 监听地 - [x] /服务列表 - [x] /服务详情 - [x] @Bot 插件冲突检测 (会在本群发送一条消息并在约 1s 后撤回以检测其它同类 bot 中已启用的插件并禁用) +- **定时指令触发器** + - [x] 记录在"cron"触发的指令 + - [x] 取消在"cron"触发的指令 - **聊天** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat"` - [x] [BOT名字] - [x] [戳一戳BOT] diff --git a/data b/data index 86a8be98..e084d72e 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 86a8be98b31cf97a9a0e07ceb15f1e4008cc9869 +Subproject commit e084d72e9d6cf73013be2a4c88dc378f1e3a810d diff --git a/go.mod b/go.mod index e28111e4..82ee90aa 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/FloatTech/AnimeAPI v1.3.1-0.20220305143953-376e5d5b6c94 github.com/FloatTech/sqlite v0.2.0 - github.com/FloatTech/zbputils v1.3.1-0.20220304041409-6607dccd2a10 + github.com/FloatTech/zbputils v1.3.1-0.20220306130312-258cc20d69cb github.com/antchfx/htmlquery v1.2.4 github.com/corona10/goimagehash v1.0.3 github.com/fogleman/gg v1.3.0 diff --git a/go.sum b/go.sum index f958d438..ecb28472 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,9 @@ github.com/FloatTech/AnimeAPI v1.3.1-0.20220305143953-376e5d5b6c94/go.mod h1:hA4 github.com/FloatTech/bot-manager v1.0.0/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ= github.com/FloatTech/sqlite v0.2.0 h1:x3uls/hExXH1+bbaNLkvilce6ATtWlDx4IqoxBW/bv8= github.com/FloatTech/sqlite v0.2.0/go.mod h1:xIDWIvpOFl8AXmZm0FC8t3PZjiR6ZutytCpBv2EWCns= -github.com/FloatTech/zbputils v1.3.1-0.20220304041409-6607dccd2a10 h1:akteVs9gqHzPZuX1gvRiT/1HoSGD9DcO/kWrcWG/7p0= github.com/FloatTech/zbputils v1.3.1-0.20220304041409-6607dccd2a10/go.mod h1:ts1Srsgp6iZlvo3K/7Q2NtwQyD1/AzG4EaPxRitR0S4= +github.com/FloatTech/zbputils v1.3.1-0.20220306130312-258cc20d69cb h1:ykdnk1600xRXJtIvm5Xj7MnbWimwY5GR9K4GBM5Ammo= +github.com/FloatTech/zbputils v1.3.1-0.20220306130312-258cc20d69cb/go.mod h1:ts1Srsgp6iZlvo3K/7Q2NtwQyD1/AzG4EaPxRitR0S4= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= diff --git a/main.go b/main.go index 833024fc..4242f167 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/job" // 炉石 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼 @@ -140,6 +141,7 @@ import ( // // // -----------------------以下为内置依赖,勿动------------------------ // "github.com/FloatTech/zbputils/control/order" + "github.com/FloatTech/zbputils/process" "github.com/fumiama/go-registry" "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" @@ -235,7 +237,7 @@ func main() { Handle(func(ctx *zero.Ctx) { ctx.SendChain(message.Text(getKanban())) }) - zero.RunAndBlock( + zero.Run( zero.Config{ NickName: append([]string{*adana}, nicks...), CommandPrefix: *prefix, @@ -245,4 +247,6 @@ func main() { Driver: []zero.Driver{driver.NewWebSocketClient(*url, *token)}, }, ) + process.GlobalInitMutex.Unlock() + select {} } diff --git a/plugin/acgimage/classify.go b/plugin/acgimage/classify.go index 2b8f4696..37ec9b28 100644 --- a/plugin/acgimage/classify.go +++ b/plugin/acgimage/classify.go @@ -155,12 +155,12 @@ func reply(ctx *zero.Ctx, class int, dhash string, comment string) error { } } else { send = func(msg interface{}) int64 { - return int64(ctx.SendGroupForwardMessage(ctx.Event.GroupID, message.Message{ + return ctx.SendGroupForwardMessage(ctx.Event.GroupID, message.Message{ ctxext.FakeSenderForwardNode(ctx, append( msg.(message.Message), message.Text(comment))..., ), - }).Get("message_id").Int()) + }).Get("message_id").Int() } } diff --git a/plugin/diana/data/text.go b/plugin/diana/data/text.go index 7d6ab005..1ba4accd 100644 --- a/plugin/diana/data/text.go +++ b/plugin/diana/data/text.go @@ -7,6 +7,7 @@ import ( sql "github.com/FloatTech/sqlite" binutils "github.com/FloatTech/zbputils/binary" + "github.com/FloatTech/zbputils/control/order" "github.com/FloatTech/zbputils/file" "github.com/sirupsen/logrus" ) @@ -20,6 +21,7 @@ type text struct { // LoadText 加载小作文 func LoadText(dbfile string) { + defer order.DoneOnExit()() _, err := file.GetLazyData(dbfile, false, false) db.DBPath = dbfile if err != nil { diff --git a/plugin/job/main.go b/plugin/job/main.go new file mode 100644 index 00000000..77d03b34 --- /dev/null +++ b/plugin/job/main.go @@ -0,0 +1,143 @@ +// Package job 定时指令触发器 +package job + +import ( + "hash/crc64" + "strconv" + "sync" + "time" + + "github.com/FloatTech/zbputils/binary" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/control/order" + "github.com/FloatTech/zbputils/process" + "github.com/FloatTech/zbputils/vevent" + "github.com/fumiama/cron" + "github.com/sirupsen/logrus" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +var ( + lo map[int64]vevent.Loop + entries map[int64]cron.EntryID // id entryid + mu sync.Mutex +) + +func init() { + en := control.Register("job", order.AcquirePrio(), &control.Options{ + DisableOnDefault: false, + Help: "定时指令触发器\n- 记录在\"cron\"触发的指令\n- 取消在\"cron\"触发的指令", + PrivateDataFolder: "job", + }) + db.DBPath = en.DataFolder() + "job.db" + err := db.Open() + if err != nil { + panic(err) + } + go func() { + process.GlobalInitMutex.Lock() + process.SleepAbout1sTo2s() + lo = make(map[int64]vevent.Loop, len(zero.BotConfig.Driver)) + entries = map[int64]cron.EntryID{} + for _, drv := range zero.BotConfig.Driver { + id := drv.SelfID() + ids := strconv.FormatInt(id, 36) + c := &cmd{} + lo[id] = vevent.NewLoop(id) + err := db.Create(ids, c) + logrus.Infoln("[job]创建表", ids) + if err != nil { + panic(err) + } + db.FindFor(ids, c, "", func() error { + mu.Lock() + defer mu.Unlock() + eid, err := process.CronTab.AddFunc(c.Cron, inject(id, []byte(c.Cmd))) + if err != nil { + return err + } + entries[c.ID] = eid + return nil + }) + } + logrus.Infoln("[job]本地环回初始化完成") + process.GlobalInitMutex.Unlock() + }() + en.OnRegex(`^记录在"(.*)"触发的指令$`, zero.AdminPermission, islonotnil, func(ctx *zero.Ctx) bool { + ctx.SendChain(message.Text("您的下一条指令将被记录,在", ctx.State["regex_matched"].([]string)[1], "时触发")) + select { + case <-time.After(time.Second * 120): + ctx.SendChain(message.Text("指令记录超时")) + return false + case e := <-zero.NewFutureEvent("message", 0, false, zero.CheckUser(ctx.Event.UserID)).Next(): + ctx.State["job_raw_event"] = e.RawEvent.Raw + return true + } + }).SetBlock(true).Handle(func(ctx *zero.Ctx) { + cron := ctx.State["regex_matched"].([]string)[1] + command := ctx.State["job_raw_event"].(string) + c := &cmd{ + ID: idof(cron, command), + Cron: cron, + Cmd: command, + } + err := addcmd(ctx.Event.SelfID, c) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + ctx.SendChain(message.Text("成功!")) + }) + en.OnRegex(`^取消在"(.*)"触发的指令$`, zero.AdminPermission, islonotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) { + cron := ctx.State["regex_matched"].([]string)[1] + err := rmcmd(ctx.Event.SelfID, cron) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + ctx.SendChain(message.Text("成功!")) + }) +} + +func islonotnil(ctx *zero.Ctx) bool { + return len(lo) > 0 +} + +func inject(bot int64, response []byte) func() { + return func() { + lo[bot].Echo(response) + } +} + +func idof(cron, cmd string) int64 { + return int64(crc64.Checksum(binary.StringToBytes(cron+cmd), crc64.MakeTable(crc64.ISO))) +} + +func addcmd(bot int64, c *cmd) error { + mu.Lock() + defer mu.Unlock() + eid, err := process.CronTab.AddFunc(c.Cron, inject(bot, []byte(c.Cmd))) + if err != nil { + return err + } + entries[c.ID] = eid + return db.Insert(strconv.FormatInt(bot, 36), c) +} + +func rmcmd(bot int64, cron string) error { + c := &cmd{} + mu.Lock() + defer mu.Unlock() + bots := strconv.FormatInt(bot, 36) + err := db.FindFor(bots, c, "WHERE cron='"+cron+"'", func() error { + eid, ok := entries[c.ID] + if ok { + process.CronTab.Remove(eid) + delete(entries, c.ID) + } + return nil + }) + db.Del(bots, "WHERE cron='"+cron+"'") + return err +} diff --git a/plugin/job/model.go b/plugin/job/model.go new file mode 100644 index 00000000..2ff06db3 --- /dev/null +++ b/plugin/job/model.go @@ -0,0 +1,11 @@ +package job + +import sql "github.com/FloatTech/sqlite" + +type cmd struct { + ID int64 `db:"id"` + Cron string `db:"cron"` + Cmd string `db:"cmd"` +} + +var db = &sql.Sqlite{} diff --git a/plugin/reborn/load.go b/plugin/reborn/load.go index e9430864..fc63c7d6 100644 --- a/plugin/reborn/load.go +++ b/plugin/reborn/load.go @@ -3,11 +3,13 @@ package reborn import ( "encoding/json" + "github.com/FloatTech/zbputils/control/order" "github.com/FloatTech/zbputils/file" ) // load 加载rate数据 func load(area *rate, jsonfile string) error { + defer order.DoneOnExit()() data, err := file.GetLazyData(jsonfile, true, true) if err != nil { return err diff --git a/plugin/wordle/wordle.go b/plugin/wordle/wordle.go index 3b6b1300..097c60d9 100644 --- a/plugin/wordle/wordle.go +++ b/plugin/wordle/wordle.go @@ -73,6 +73,7 @@ func init() { }), )) go func() { + defer order.DoneOnExit()() for i := 5; i <= 7; i++ { dc, err := file.GetLazyData(fmt.Sprintf("%scet-4_%d.txt", en.DataFolder(), i), true, true) if err != nil {