From 4dc8e5619c7849574406c5f28562b5ca71008ef6 Mon Sep 17 00:00:00 2001 From: fumiama Date: Tue, 26 Oct 2021 15:05:32 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96=20timer=20?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=B6=88=E8=80=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin_manager/manager.go | 24 ++--- plugin_manager/timer/timer.go | 197 +++++++++++++++++++++------------- 2 files changed, 134 insertions(+), 87 deletions(-) diff --git a/plugin_manager/manager.go b/plugin_manager/manager.go index fa60522b..f6408e82 100644 --- a/plugin_manager/manager.go +++ b/plugin_manager/manager.go @@ -21,9 +21,10 @@ import ( ) const ( - datapath = "data/manager/" - confile = datapath + "config.pb" - hint = "====群管====\n" + + datapath = "data/manager/" + confile = datapath + "config.pb" + timerfile = datapath + "timers.pb" + hint = "====群管====\n" + "- 禁言@QQ 1分钟\n" + "- 解除禁言 @QQ\n" + "- 我要自闭 1分钟\n" + @@ -51,10 +52,12 @@ const ( var ( config Config limit = rate.NewManager(time.Minute*5, 2) + clock timer.Clock ) func init() { // 插件主体 loadConfig() + clock = timer.NewClock(timerfile) // 菜单 zero.OnFullMatch("群管系统", zero.AdminPermission).SetBlock(true).FirstPriority(). Handle(func(ctx *zero.Ctx) { @@ -250,10 +253,10 @@ func init() { // 插件主体 Handle(func(ctx *zero.Ctx) { if ctx.Event.GroupID > 0 { dateStrs := ctx.State["regex_matched"].([]string) - ts := timer.GetFilledTimeStamp(dateStrs, false) + ts := timer.GetFilledTimer(dateStrs, false) ts.Grpid = uint64(ctx.Event.GroupID) if ts.Enable { - go timer.RegisterTimer(ts, true) + go clock.RegisterTimer(ts, true) ctx.SendChain(message.Text("记住了~")) } else { ctx.SendChain(message.Text("参数非法!")) @@ -265,14 +268,11 @@ func init() { // 插件主体 Handle(func(ctx *zero.Ctx) { if ctx.Event.GroupID > 0 { dateStrs := ctx.State["regex_matched"].([]string) - ts := timer.GetFilledTimeStamp(dateStrs, true) + ts := timer.GetFilledTimer(dateStrs, true) ts.Grpid = uint64(ctx.Event.GroupID) - ti := timer.GetTimerInfo(ts) - t, ok := (*timer.Timers)[ti] + ti := ts.GetTimerInfo() + ok := clock.CancelTimer(ti) if ok { - t.Enable = false - delete(*timer.Timers, ti) // 避免重复取消 - _ = timer.SaveTimers() ctx.SendChain(message.Text("取消成功~")) } else { ctx.SendChain(message.Text("没有这个定时器哦~")) @@ -283,7 +283,7 @@ func init() { // 插件主体 zero.OnFullMatch("列出所有提醒", zero.AdminPermission).SetBlock(true).SetPriority(40). Handle(func(ctx *zero.Ctx) { if ctx.Event.GroupID > 0 { - ctx.SendChain(message.Text(timer.ListTimers(uint64(ctx.Event.GroupID)))) + ctx.SendChain(message.Text(clock.ListTimers(uint64(ctx.Event.GroupID)))) } }) // 随机点名 diff --git a/plugin_manager/timer/timer.go b/plugin_manager/timer/timer.go index aab01fa4..4d8625c1 100644 --- a/plugin_manager/timer/timer.go +++ b/plugin_manager/timer/timer.go @@ -4,33 +4,32 @@ package timer import ( "fmt" "io" - "math/rand" "os" "strconv" "strings" + "sync" "time" "unicode" log "github.com/sirupsen/logrus" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" + + "github.com/FloatTech/ZeroBot-Plugin/utils/file" ) -type ( - TimeStamp = Timer -) - -const ( - // 定时器存储位置 - datapath = "data/manager/" // 数据目录 - pbfile = datapath + "timers.pb" -) - -var ( +type Clock struct { // 记录每个定时器以便取消 timersmap TimersMap // 定时器map - Timers *(map[string]*Timer) + timers *(map[string]*Timer) + // 读写锁 + timersmu sync.RWMutex + // 定时器存储位置 + pbfile *string +} + +var ( // @全体成员 atall = message.MessageSegment{ Type: "at", @@ -40,21 +39,70 @@ var ( } ) -func init() { - go func() { - time.Sleep(time.Second + time.Millisecond*time.Duration(rand.Intn(1000))) - err := os.MkdirAll(datapath, 0755) - if err != nil { - panic(err) - } - loadTimers() - Timers = &timersmap.Timers - }() +func NewClock(pbfile string) (c Clock) { + c.loadTimers(pbfile) + c.timers = &c.timersmap.Timers + c.pbfile = &pbfile + return } -func judgeHM(ts *TimeStamp) { - if ts.Hour < 0 || ts.Hour == int32(time.Now().Hour()) { - if ts.Minute < 0 || ts.Minute == int32(time.Now().Minute()) { +func nextDistance(nextTime int32, nowTime int, smallUnit, largeUnit time.Duration) (d time.Duration, overflow bool) { + d = time.Duration(int(nextTime)-nowTime) * smallUnit + if d <= 0 { + d += largeUnit + overflow = true + } + return +} + +func (ts *Timer) nextDuration() time.Duration { + sleepdur := time.Minute + isThisHour := ts.Hour < 0 || ts.Hour == int32(time.Now().Hour()) + if isThisHour { + isThisMinute := ts.Minute < 0 || ts.Minute == int32(time.Now().Minute()) + if !isThisMinute { + d, over := nextDistance(ts.Minute, time.Now().Minute(), time.Minute, time.Hour) + if !(ts.Hour > 0 && over) { + sleepdur = d + } + } + } else { + d, over := nextDistance(ts.Hour, time.Now().Hour(), time.Hour, time.Hour*24) + if !(ts.Day > 0 && over) { + sleepdur = d + } + } + return sleepdur +} + +// RegisterTimer 注册计时器 +func (c *Clock) RegisterTimer(ts *Timer, save bool) { + key := ts.GetTimerInfo() + if c.timers != nil { + t, ok := c.GetTimer(key) + if t != ts && ok { // 避免重复注册定时器 + t.Enable = false + } + c.timersmu.Lock() + (*c.timers)[key] = ts + c.timersmu.Unlock() + if save { + c.SaveTimers() + } + } + log.Printf("[群管]注册计时器[%t]%s", ts.Enable, key) + for ts.Enable { + var dur time.Duration + isThisMonth := ts.Month < 0 || ts.Month == int32(time.Now().Month()) + if isThisMonth { + isThisDay := ts.Day < 0 || ts.Day == int32(time.Now().Day()) + isThisWeek := ts.Week < 0 || ts.Week == int32(time.Now().Weekday()) + if isThisDay || isThisWeek { + dur = ts.nextDuration() + } + } + time.Sleep(dur) + if ts.Enable { zero.RangeBot(func(id int64, ctx *zero.Ctx) bool { ctx.Event = new(zero.Event) ctx.Event.GroupID = int64(ts.Grpid) @@ -69,103 +117,102 @@ func judgeHM(ts *TimeStamp) { } } -// RegisterTimer 注册计时器 -func RegisterTimer(ts *TimeStamp, save bool) { - key := GetTimerInfo(ts) - if Timers != nil { - t, ok := (*Timers)[key] - if t != ts && ok { // 避免重复注册定时器 - t.Enable = false - } - (*Timers)[key] = ts - if save { - SaveTimers() - } - } - log.Printf("[群管]注册计时器[%t]%s", ts.Enable, key) - for ts.Enable { - if ts.Month < 0 || ts.Month == int32(time.Now().Month()) { - if ts.Day < 0 || ts.Day == int32(time.Now().Day()) { - judgeHM(ts) - } else if ts.Day == 0 { - if ts.Week < 0 || ts.Week == int32(time.Now().Weekday()) { - judgeHM(ts) - } - } - } - time.Sleep(time.Minute) +// CancelTimer 取消计时器 +func (c *Clock) CancelTimer(key string) bool { + t, ok := (*c.timers)[key] + if ok { + t.Enable = false + c.timersmu.Lock() + delete(*c.timers, key) // 避免重复取消 + c.timersmu.Unlock() + _ = c.SaveTimers() } + return ok } // SaveTimers 保存当前计时器 -func SaveTimers() error { - data, err := timersmap.Marshal() - if err != nil { - return err - } else if _, err := os.Stat(datapath); err == nil || os.IsExist(err) { - f, err1 := os.OpenFile(pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) +func (c *Clock) SaveTimers() error { + c.timersmu.RLock() + data, err := c.timersmap.Marshal() + c.timersmu.RUnlock() + if err == nil { + c.timersmu.Lock() + defer c.timersmu.Unlock() + f, err1 := os.OpenFile(*c.pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err1 != nil { return err1 } else { - defer f.Close() _, err2 := f.Write(data) + f.Close() return err2 } - } else { - return nil } + return err } // ListTimers 列出本群所有计时器 -func ListTimers(grpID uint64) []string { +func (c *Clock) ListTimers(grpID uint64) []string { // 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高 - if Timers != nil { + if c.timers != nil { g := strconv.FormatUint(grpID, 10) - keys := make([]string, 0, len(*Timers)) - for k := range *Timers { + c.timersmu.RLock() + keys := make([]string, 0, len(*c.timers)) + for k := range *c.timers { if strings.Contains(k, g) { start := strings.Index(k, "]") - keys = append(keys, strings.ReplaceAll(k[start+1:]+"\n", "-1", "每")) + msg := strings.ReplaceAll(k[start+1:]+"\n", "-1", "每") + msg = strings.ReplaceAll(msg, "日0周", "日的") + msg = strings.ReplaceAll(msg, "周", "的") + msg = strings.ReplaceAll(msg, "月0日", "月周") + keys = append(keys, msg) } } + c.timersmu.RUnlock() return keys } else { return nil } } -func loadTimers() { - if _, err := os.Stat(pbfile); err == nil || os.IsExist(err) { +func (c *Clock) GetTimer(key string) (t *Timer, ok bool) { + c.timersmu.RLock() + t, ok = (*c.timers)[key] + c.timersmu.RUnlock() + return +} + +func (c *Clock) loadTimers(pbfile string) { + if file.IsExist(pbfile) { f, err := os.Open(pbfile) if err == nil { data, err1 := io.ReadAll(f) if err1 == nil { if len(data) > 0 { - timersmap.Unmarshal(data) - for _, t := range timersmap.Timers { - go RegisterTimer(t, false) + c.timersmap.Unmarshal(data) + for _, t := range c.timersmap.Timers { + go c.RegisterTimer(t, false) } return } } } } - timersmap.Timers = make(map[string]*Timer) + c.timersmap.Timers = make(map[string]*Timer) } // GetTimerInfo 获得标准化定时字符串 -func GetTimerInfo(ts *TimeStamp) string { +func (ts *Timer) GetTimerInfo() string { return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", ts.Grpid, ts.Month, ts.Day, ts.Week, ts.Hour, ts.Minute) } -// GetFilledTimeStamp 获得填充好的ts -func GetFilledTimeStamp(dateStrs []string, matchDateOnly bool) *TimeStamp { +// GetFilledTimer 获得填充好的ts +func GetFilledTimer(dateStrs []string, matchDateOnly bool) *Timer { monthStr := []rune(dateStrs[1]) dayWeekStr := []rune(dateStrs[2]) hourStr := []rune(dateStrs[3]) minuteStr := []rune(dateStrs[4]) - var ts TimeStamp + var ts Timer ts.Month = chineseNum2Int(monthStr) if (ts.Month != -1 && ts.Month <= 0) || ts.Month > 12 { // 月份非法 log.Println("[群管]月份非法!")