✏️ drop pb in timer

This commit is contained in:
fumiama
2021-12-16 12:51:41 +08:00
parent b871573dc3
commit a363623df9
11 changed files with 258 additions and 121 deletions

View File

@@ -1,6 +1,8 @@
package timer
import (
"crypto/md5"
"encoding/binary"
"fmt"
"strconv"
"strings"
@@ -8,28 +10,37 @@ import (
"unicode"
"github.com/sirupsen/logrus"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
)
// GetTimerInfo 获得标准化定时字符串
func (ts *Timer) GetTimerInfo(grp int64) string {
func (ts *Timer) GetTimerInfo() string {
if ts.Cron != "" {
return fmt.Sprintf("[%d]%s", grp, ts.Cron)
return fmt.Sprintf("[%d]%s", ts.GrpId, ts.Cron)
}
return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", grp, 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())
}
// GetTimerInfo 获得标准化 ID
func (ts *Timer) GetTimerID() uint32 {
key := ts.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 int64) *Timer {
func GetFilledCronTimer(croncmd string, alert string, img string, botqq, gid int64) *Timer {
var ts Timer
ts.Alert = alert
ts.Cron = croncmd
ts.Url = img
ts.Selfid = botqq
ts.GrpId = gid
return &ts
}
// GetFilledTimer 获得填充好的ts
func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Timer {
monthStr := []rune(dateStrs[1])
dayWeekStr := []rune(dateStrs[2])
hourStr := []rune(dateStrs[3])
@@ -43,7 +54,8 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
}
ts.SetMonth(mon)
lenOfDW := len(dayWeekStr)
if lenOfDW == 4 { // 包括末尾的"日"
switch {
case lenOfDW == 4: // 包括末尾的"日"
dayWeekStr = []rune{dayWeekStr[0], dayWeekStr[2]} // 去除中间的十
d := chineseNum2Int(dayWeekStr)
if (d != -1 && d <= 0) || d > 31 { // 日期非法
@@ -51,7 +63,7 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
return &ts
}
ts.SetDay(d)
} else if dayWeekStr[lenOfDW-1] == rune('日') { // xx日
case dayWeekStr[lenOfDW-1] == rune('日'): // xx日
dayWeekStr = dayWeekStr[:lenOfDW-1]
d := chineseNum2Int(dayWeekStr)
if (d != -1 && d <= 0) || d > 31 { // 日期非法
@@ -59,9 +71,9 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
return &ts
}
ts.SetDay(d)
} else if dayWeekStr[0] == rune('每') { // 每周
case dayWeekStr[0] == rune('每'): // 每周
ts.SetWeek(-1)
} else { // 周x
default: // 周x
w := chineseNum2Int(dayWeekStr[1:])
if w == 7 { // 周天是0
w = 0
@@ -105,6 +117,7 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
ts.SetEn(true)
}
ts.Selfid = botqq
ts.GrpId = grp
return &ts
}
@@ -116,13 +129,14 @@ func chineseNum2Int(rs []rune) int {
if unicode.IsDigit(rs[0]) { // 默认可能存在的第二位也为int
r, _ = strconv.Atoi(string(rs))
} else {
if rs[0] == mai {
switch {
case rs[0] == mai:
if l == 2 {
r = -chineseChar2Int(rs[1])
}
} else if l == 1 {
case l == 1:
r = chineseChar2Int(rs[0])
} else {
default:
ten := chineseChar2Int(rs[0])
if ten != 10 {
ten *= 10

View File

@@ -56,11 +56,12 @@ func (ts *Timer) nextWakeTime() (date time.Time) {
} else {
stable |= 0x8
}
if d < 0 {
switch {
case d < 0:
d = date.Day()
} else if d > 0 {
case d > 0:
stable |= 0x4
} else {
default:
d = date.Day()
if w >= 0 {
stable |= 0x2
@@ -148,14 +149,14 @@ func (ts *Timer) nextWakeTime() (date time.Time) {
return date
}
func (ts *Timer) judgeHM(grp int64) {
func (ts *Timer) judgeHM() {
if ts.Hour() < 0 || ts.Hour() == time.Now().Hour() {
if ts.Minute() < 0 || ts.Minute() == time.Now().Minute() {
if ts.Selfid != 0 {
ts.sendmsg(grp, zero.GetBot(ts.Selfid))
ts.sendmsg(ts.GrpId, zero.GetBot(ts.Selfid))
} else {
zero.RangeBot(func(id int64, ctx *zero.Ctx) (_ bool) {
ts.sendmsg(grp, ctx)
ts.sendmsg(ts.GrpId, ctx)
return
})
}

View File

@@ -0,0 +1,26 @@
package timer
import (
"strconv"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
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"`
}
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
}

View File

@@ -2,34 +2,28 @@
package timer
import (
"io"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/RomiChan/protobuf/proto"
"github.com/fumiama/cron"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
)
type Clock struct {
// 记录每个定时器以便取消
timersmap TimersMap
// 定时器map
timers *(map[string]*Timer)
db *sql.Sqlite
timers *(map[uint32]*Timer)
timersmu sync.RWMutex
// 定时器存储位置
pbfile *string
// cron 定时器
cron *cron.Cron
// entries key <-> cron
entries map[string]cron.EntryID
entries map[uint32]cron.EntryID
entmu sync.Mutex
}
@@ -43,26 +37,27 @@ var (
}
)
func NewClock(pbfile string) (c Clock) {
c.loadTimers(pbfile)
c.timers = &c.timersmap.Timers
c.pbfile = &pbfile
func NewClock(dbfile string) (c Clock) {
c.loadTimers(dbfile)
c.cron = cron.New()
c.entries = make(map[string]cron.EntryID)
c.entries = make(map[uint32]cron.EntryID)
c.cron.Start()
return
}
// RegisterTimer 注册计时器
func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
key := ts.GetTimerInfo(grp)
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)
}
c.timersmu.Lock()
(*c.timers)[key] = ts
c.timersmu.Unlock()
logrus.Println("[群管]注册计时器", key)
if ts.Cron != "" {
var ctx *zero.Ctx
@@ -75,33 +70,33 @@ func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
return false
})
}
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(grp, ctx) })
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 {
c.SaveTimers()
err = c.AddTimer(ts)
}
return true
return err == nil
}
ts.Alert = err.Error()
} else {
if save {
c.SaveTimers()
_ = c.AddTimer(ts)
}
for ts.En() {
nextdate := ts.nextWakeTime()
sleepsec := time.Until(nextdate)
logrus.Printf("[群管]计时器%s将睡眠%ds", key, sleepsec/time.Second)
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(grp)
ts.judgeHM()
} else if ts.Day() == 0 {
if ts.Week() < 0 || ts.Week() == time.Now().Weekday() {
ts.judgeHM(grp)
ts.judgeHM()
}
}
}
@@ -112,8 +107,8 @@ func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
}
// CancelTimer 取消计时器
func (c *Clock) CancelTimer(key string) bool {
t, ok := (*c.timers)[key]
func (c *Clock) CancelTimer(key uint32) bool {
t, ok := c.GetTimer(key)
if ok {
if t.Cron != "" {
c.entmu.Lock()
@@ -126,41 +121,22 @@ func (c *Clock) CancelTimer(key string) bool {
}
c.timersmu.Lock()
delete(*c.timers, key) // 避免重复取消
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key)))
c.timersmu.Unlock()
_ = c.SaveTimers()
return e == nil
}
return ok
}
// SaveTimers 保存当前计时器
func (c *Clock) SaveTimers() error {
c.timersmu.RLock()
data, err := proto.Marshal(&c.timersmap)
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 {
_, err2 := f.Write(data)
f.Close()
return err2
}
}
return err
return false
}
// ListTimers 列出本群所有计时器
func (c *Clock) ListTimers(grpID int64) []string {
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
if c.timers != nil {
g := strconv.FormatInt(grpID, 10)
c.timersmu.RLock()
keys := make([]string, 0, len(*c.timers))
for k := range *c.timers {
if strings.Contains(k, g) {
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周", "月周天")
@@ -176,35 +152,32 @@ func (c *Clock) ListTimers(grpID int64) []string {
}
}
func (c *Clock) GetTimer(key string) (t *Timer, ok bool) {
func (c *Clock) GetTimer(key uint32) (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)
func (c *Clock) AddTimer(t *Timer) (err error) {
c.timersmu.Lock()
(*c.timers)[t.Id] = t
err = c.db.Insert("timer", t)
c.timersmu.Unlock()
return
}
func (c *Clock) loadTimers(dbfile string) {
if file.IsExist(dbfile) {
c.db.DBPath = dbfile
err := c.db.Create("timer", &Timer{})
if err == nil {
data, err := io.ReadAll(f)
if err == nil {
if len(data) > 0 {
err = proto.Unmarshal(data, &c.timersmap)
if err == nil {
for str, t := range c.timersmap.Timers {
grp, err := strconv.ParseInt(str[1:strings.Index(str, "]")], 10, 64)
if err == nil {
go c.RegisterTimer(t, grp, false)
}
}
return
}
logrus.Errorln("[群管]读取定时器文件失败将在下一次保存时覆盖原文件。err:", err)
logrus.Errorln("[群管]如不希望被覆盖请运行源码plugin_manager/timers/migrate下的程序将timers.pb刷新为新版")
}
}
var t Timer
c.db.FindFor("timer", &t, "", func() error {
tescape := t
go c.RegisterTimer(&tescape, false)
return nil
})
}
}
c.timersmap.Timers = make(map[string]*Timer)
}

View File

@@ -1,13 +0,0 @@
package timer
type Timer struct {
Alert string `protobuf:"bytes,1,opt"`
Cron string `protobuf:"bytes,2,opt"`
En1Month4Day5Week3Hour5Min6 int32 `protobuf:"varint,4,opt"`
Selfid int64 `protobuf:"varint,8,opt"`
Url string `protobuf:"bytes,16,opt"`
}
type TimersMap struct {
Timers map[string]*Timer `protobuf:"bytes,1,rep" protobuf_key:"bytes,1,opt" protobuf_val:"bytes,2,opt"`
}

View File

@@ -2,10 +2,12 @@ package timer
import "time"
// En isEnabled 1bit
func (m *Timer) En() (en bool) {
return m.En1Month4Day5Week3Hour5Min6&0x800000 != 0
}
// Month 4bits
func (m *Timer) Month() (mon time.Month) {
mon = time.Month((m.En1Month4Day5Week3Hour5Min6 & 0x780000) >> 19)
if mon == 0b1111 {
@@ -14,6 +16,7 @@ func (m *Timer) Month() (mon time.Month) {
return
}
// Day 5bits
func (m *Timer) Day() (d int) {
d = int((m.En1Month4Day5Week3Hour5Min6 & 0x07c000) >> 14)
if d == 0b11111 {
@@ -22,6 +25,7 @@ func (m *Timer) Day() (d int) {
return
}
// Week 3bits
func (m *Timer) Week() (w time.Weekday) {
w = time.Weekday((m.En1Month4Day5Week3Hour5Min6 & 0x003800) >> 11)
if w == 0b111 {
@@ -30,6 +34,7 @@ func (m *Timer) Week() (w time.Weekday) {
return
}
// Hour 5bits
func (m *Timer) Hour() (h int) {
h = int((m.En1Month4Day5Week3Hour5Min6 & 0x0007c0) >> 6)
if h == 0b11111 {
@@ -38,6 +43,7 @@ func (m *Timer) Hour() (h int) {
return
}
// Minute 6bits
func (m *Timer) Minute() (min int) {
min = int(m.En1Month4Day5Week3Hour5Min6 & 0x00003f)
if min == 0b111111 {
@@ -46,6 +52,7 @@ func (m *Timer) Minute() (min int) {
return
}
// SetEn ...
func (m *Timer) SetEn(en bool) {
if en {
m.En1Month4Day5Week3Hour5Min6 |= 0x800000
@@ -54,22 +61,27 @@ func (m *Timer) SetEn(en bool) {
}
}
// SetMonth ...
func (m *Timer) SetMonth(mon time.Month) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(mon) << 19) & 0x780000) | (m.En1Month4Day5Week3Hour5Min6 & 0x87ffff)
}
// SetDay ...
func (m *Timer) SetDay(d int) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(d) << 14) & 0x07c000) | (m.En1Month4Day5Week3Hour5Min6 & 0xf83fff)
}
// SetWeek ...
func (m *Timer) SetWeek(w time.Weekday) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(w) << 11) & 0x003800) | (m.En1Month4Day5Week3Hour5Min6 & 0xffc7ff)
}
// SetHour ...
func (m *Timer) SetHour(h int) {
m.En1Month4Day5Week3Hour5Min6 = ((int32(h) << 6) & 0x0007c0) | (m.En1Month4Day5Week3Hour5Min6 & 0xfff83f)
}
// SetMinute ...
func (m *Timer) SetMinute(min int) {
m.En1Month4Day5Week3Hour5Min6 = (int32(min) & 0x00003f) | (m.En1Month4Day5Week3Hour5Min6 & 0xffffc0)
}