mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-23 01:36:36 +08:00
✨ 优化 timer 资源消耗
This commit is contained in:
parent
21ff752322
commit
4dc8e5619c
@ -21,9 +21,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
datapath = "data/manager/"
|
datapath = "data/manager/"
|
||||||
confile = datapath + "config.pb"
|
confile = datapath + "config.pb"
|
||||||
hint = "====群管====\n" +
|
timerfile = datapath + "timers.pb"
|
||||||
|
hint = "====群管====\n" +
|
||||||
"- 禁言@QQ 1分钟\n" +
|
"- 禁言@QQ 1分钟\n" +
|
||||||
"- 解除禁言 @QQ\n" +
|
"- 解除禁言 @QQ\n" +
|
||||||
"- 我要自闭 1分钟\n" +
|
"- 我要自闭 1分钟\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))))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 随机点名
|
// 随机点名
|
||||||
|
|||||||
@ -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]
|
t.Enable = false
|
||||||
if t != ts && ok { // 避免重复注册定时器
|
c.timersmu.Lock()
|
||||||
t.Enable = false
|
delete(*c.timers, key) // 避免重复取消
|
||||||
}
|
c.timersmu.Unlock()
|
||||||
(*Timers)[key] = ts
|
_ = c.SaveTimers()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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("[群管]月份非法!")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user