优化 timer 资源消耗

This commit is contained in:
fumiama 2021-10-26 15:05:32 +08:00
parent 21ff752322
commit 4dc8e5619c
2 changed files with 134 additions and 87 deletions

View File

@ -23,6 +23,7 @@ import (
const ( const (
datapath = "data/manager/" datapath = "data/manager/"
confile = datapath + "config.pb" confile = datapath + "config.pb"
timerfile = datapath + "timers.pb"
hint = "====群管====\n" + hint = "====群管====\n" +
"- 禁言@QQ 1分钟\n" + "- 禁言@QQ 1分钟\n" +
"- 解除禁言 @QQ\n" + "- 解除禁言 @QQ\n" +
@ -51,10 +52,12 @@ const (
var ( var (
config Config config Config
limit = rate.NewManager(time.Minute*5, 2) limit = rate.NewManager(time.Minute*5, 2)
clock timer.Clock
) )
func init() { // 插件主体 func init() { // 插件主体
loadConfig() loadConfig()
clock = timer.NewClock(timerfile)
// 菜单 // 菜单
zero.OnFullMatch("群管系统", zero.AdminPermission).SetBlock(true).FirstPriority(). zero.OnFullMatch("群管系统", zero.AdminPermission).SetBlock(true).FirstPriority().
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
@ -250,10 +253,10 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 { if ctx.Event.GroupID > 0 {
dateStrs := ctx.State["regex_matched"].([]string) dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimeStamp(dateStrs, false) ts := timer.GetFilledTimer(dateStrs, false)
ts.Grpid = uint64(ctx.Event.GroupID) ts.Grpid = uint64(ctx.Event.GroupID)
if ts.Enable { if ts.Enable {
go timer.RegisterTimer(ts, true) go clock.RegisterTimer(ts, true)
ctx.SendChain(message.Text("记住了~")) ctx.SendChain(message.Text("记住了~"))
} else { } else {
ctx.SendChain(message.Text("参数非法!")) ctx.SendChain(message.Text("参数非法!"))
@ -265,14 +268,11 @@ func init() { // 插件主体
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 { if ctx.Event.GroupID > 0 {
dateStrs := ctx.State["regex_matched"].([]string) dateStrs := ctx.State["regex_matched"].([]string)
ts := timer.GetFilledTimeStamp(dateStrs, true) ts := timer.GetFilledTimer(dateStrs, true)
ts.Grpid = uint64(ctx.Event.GroupID) ts.Grpid = uint64(ctx.Event.GroupID)
ti := timer.GetTimerInfo(ts) ti := ts.GetTimerInfo()
t, ok := (*timer.Timers)[ti] ok := clock.CancelTimer(ti)
if ok { if ok {
t.Enable = false
delete(*timer.Timers, ti) // 避免重复取消
_ = timer.SaveTimers()
ctx.SendChain(message.Text("取消成功~")) ctx.SendChain(message.Text("取消成功~"))
} else { } else {
ctx.SendChain(message.Text("没有这个定时器哦~")) ctx.SendChain(message.Text("没有这个定时器哦~"))
@ -283,7 +283,7 @@ func init() { // 插件主体
zero.OnFullMatch("列出所有提醒", zero.AdminPermission).SetBlock(true).SetPriority(40). zero.OnFullMatch("列出所有提醒", zero.AdminPermission).SetBlock(true).SetPriority(40).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
if ctx.Event.GroupID > 0 { 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))))
} }
}) })
// 随机点名 // 随机点名

View File

@ -4,33 +4,32 @@ package timer
import ( import (
"fmt" "fmt"
"io" "io"
"math/rand"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"unicode" "unicode"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot" zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
) )
type ( type Clock struct {
TimeStamp = Timer
)
const (
// 定时器存储位置
datapath = "data/manager/" // 数据目录
pbfile = datapath + "timers.pb"
)
var (
// 记录每个定时器以便取消 // 记录每个定时器以便取消
timersmap TimersMap timersmap TimersMap
// 定时器map // 定时器map
Timers *(map[string]*Timer) timers *(map[string]*Timer)
// 读写锁
timersmu sync.RWMutex
// 定时器存储位置
pbfile *string
}
var (
// @全体成员 // @全体成员
atall = message.MessageSegment{ atall = message.MessageSegment{
Type: "at", Type: "at",
@ -40,21 +39,70 @@ var (
} }
) )
func init() { func NewClock(pbfile string) (c Clock) {
go func() { c.loadTimers(pbfile)
time.Sleep(time.Second + time.Millisecond*time.Duration(rand.Intn(1000))) c.timers = &c.timersmap.Timers
err := os.MkdirAll(datapath, 0755) c.pbfile = &pbfile
if err != nil { return
panic(err)
}
loadTimers()
Timers = &timersmap.Timers
}()
} }
func judgeHM(ts *TimeStamp) { func nextDistance(nextTime int32, nowTime int, smallUnit, largeUnit time.Duration) (d time.Duration, overflow bool) {
if ts.Hour < 0 || ts.Hour == int32(time.Now().Hour()) { d = time.Duration(int(nextTime)-nowTime) * smallUnit
if ts.Minute < 0 || ts.Minute == int32(time.Now().Minute()) { 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 { zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
ctx.Event = new(zero.Event) ctx.Event = new(zero.Event)
ctx.Event.GroupID = int64(ts.Grpid) ctx.Event.GroupID = int64(ts.Grpid)
@ -69,103 +117,102 @@ func judgeHM(ts *TimeStamp) {
} }
} }
// RegisterTimer 注册计时器 // CancelTimer 取消计时器
func RegisterTimer(ts *TimeStamp, save bool) { func (c *Clock) CancelTimer(key string) bool {
key := GetTimerInfo(ts) t, ok := (*c.timers)[key]
if Timers != nil { if ok {
t, ok := (*Timers)[key]
if t != ts && ok { // 避免重复注册定时器
t.Enable = false t.Enable = false
c.timersmu.Lock()
delete(*c.timers, key) // 避免重复取消
c.timersmu.Unlock()
_ = c.SaveTimers()
} }
(*Timers)[key] = ts return ok
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)
}
} }
// SaveTimers 保存当前计时器 // SaveTimers 保存当前计时器
func SaveTimers() error { func (c *Clock) SaveTimers() error {
data, err := timersmap.Marshal() c.timersmu.RLock()
if err != nil { data, err := c.timersmap.Marshal()
return err c.timersmu.RUnlock()
} else if _, err := os.Stat(datapath); err == nil || os.IsExist(err) { if err == nil {
f, err1 := os.OpenFile(pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) 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 { if err1 != nil {
return err1 return err1
} else { } else {
defer f.Close()
_, err2 := f.Write(data) _, err2 := f.Write(data)
f.Close()
return err2 return err2
} }
} else {
return nil
} }
return err
} }
// ListTimers 列出本群所有计时器 // ListTimers 列出本群所有计时器
func ListTimers(grpID uint64) []string { func (c *Clock) ListTimers(grpID uint64) []string {
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高 // 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
if Timers != nil { if c.timers != nil {
g := strconv.FormatUint(grpID, 10) g := strconv.FormatUint(grpID, 10)
keys := make([]string, 0, len(*Timers)) c.timersmu.RLock()
for k := range *Timers { keys := make([]string, 0, len(*c.timers))
for k := range *c.timers {
if strings.Contains(k, g) { if strings.Contains(k, g) {
start := strings.Index(k, "]") 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 return keys
} else { } else {
return nil return nil
} }
} }
func loadTimers() { func (c *Clock) GetTimer(key string) (t *Timer, ok bool) {
if _, err := os.Stat(pbfile); err == nil || os.IsExist(err) { 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) f, err := os.Open(pbfile)
if err == nil { if err == nil {
data, err1 := io.ReadAll(f) data, err1 := io.ReadAll(f)
if err1 == nil { if err1 == nil {
if len(data) > 0 { if len(data) > 0 {
timersmap.Unmarshal(data) c.timersmap.Unmarshal(data)
for _, t := range timersmap.Timers { for _, t := range c.timersmap.Timers {
go RegisterTimer(t, false) go c.RegisterTimer(t, false)
} }
return return
} }
} }
} }
} }
timersmap.Timers = make(map[string]*Timer) c.timersmap.Timers = make(map[string]*Timer)
} }
// GetTimerInfo 获得标准化定时字符串 // 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) return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", ts.Grpid, ts.Month, ts.Day, ts.Week, ts.Hour, ts.Minute)
} }
// GetFilledTimeStamp 获得填充好的ts // GetFilledTimer 获得填充好的ts
func GetFilledTimeStamp(dateStrs []string, matchDateOnly bool) *TimeStamp { func GetFilledTimer(dateStrs []string, matchDateOnly bool) *Timer {
monthStr := []rune(dateStrs[1]) monthStr := []rune(dateStrs[1])
dayWeekStr := []rune(dateStrs[2]) dayWeekStr := []rune(dateStrs[2])
hourStr := []rune(dateStrs[3]) hourStr := []rune(dateStrs[3])
minuteStr := []rune(dateStrs[4]) minuteStr := []rune(dateStrs[4])
var ts TimeStamp var ts Timer
ts.Month = chineseNum2Int(monthStr) ts.Month = chineseNum2Int(monthStr)
if (ts.Month != -1 && ts.Month <= 0) || ts.Month > 12 { // 月份非法 if (ts.Month != -1 && ts.Month <= 0) || ts.Month > 12 { // 月份非法
log.Println("[群管]月份非法!") log.Println("[群管]月份非法!")