Skip to content

Commit

Permalink
fix(OnCronjob)!: 移除oncronjob方法,通过添加定时提醒实现,之前的任务需要重新根据定时插件的文档设置 (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
yqchilde authored Mar 9, 2023
1 parent 7869549 commit d7c5934
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 153 deletions.
1 change: 0 additions & 1 deletion engine/control/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ type Options struct {
DataFolder string // 数据文件夹
OnEnable func(ctx *robot.Ctx) // 自定义启用插件后执行的操作
OnDisable func(ctx *robot.Ctx) // 自定义禁用插件后执行的操作
OnCronjob func(ctx *robot.Ctx) // 自定义定时任务,用于被定时调用
}

// PluginConfig 插件配置表
Expand Down
16 changes: 0 additions & 16 deletions engine/control/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,3 @@ func Register(service string, o *Options) *Engine {
log.Printf("插件[%s]已注册, 优先级: %d", service, priority)
return newEngine(service, o)
}

// GetOptionsOnCronjob 获取定时任务插件控制器配置
func GetOptionsOnCronjob() map[string]*Control {
var (
services = managers.LookupAll()
servicesClone = make(map[string]*Control)
)

for i := range services {
if services[i].Options.OnCronjob == nil {
continue
}
servicesClone[services[i].Service] = services[i]
}
return servicesClone
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.1
github.com/gin-gonic/gin v1.9.0
github.com/glebarez/sqlite v1.7.0
github.com/go-co-op/gocron v1.18.0
github.com/go-co-op/gocron v1.18.1
github.com/imroc/req/v3 v3.32.0
github.com/sashabaranov/go-openai v1.4.2
github.com/sirupsen/logrus v1.9.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ github.com/glebarez/sqlite v1.7.0 h1:A7Xj/KN2Lvie4Z4rrgQHY8MsbebX3NyWsL3n2i82MVI
github.com/glebarez/sqlite v1.7.0/go.mod h1:PkeevrRlF/1BhQBCnzcMWzgrIk7IOop+qS2jUYLfHhk=
github.com/go-co-op/gocron v1.18.0 h1:SxTyJ5xnSN4byCq7b10LmmszFdxQlSQJod8s3gbnXxA=
github.com/go-co-op/gocron v1.18.0/go.mod h1:sD/a0Aadtw5CpflUJ/lpP9Vfdk979Wl1Sg33HPHg0FY=
github.com/go-co-op/gocron v1.18.1 h1:erHHbIIav46xAV54lnyKKjrKLP+2RgjuDsbwGamBEvI=
github.com/go-co-op/gocron v1.18.1/go.mod h1:UqVyvM90I1q/R1qGEX6cBORI6WArLuEgYlbncLMvzRM=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
Expand Down Expand Up @@ -256,6 +258,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
Expand Down
112 changes: 7 additions & 105 deletions plugins/manager/cronjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package manager
import (
"fmt"
"regexp"
"runtime/debug"
"strconv"
"time"

Expand All @@ -16,25 +15,23 @@ import (
const (
JobTypeRemind = "remind" // 提醒类任务
JobTypeFunc = "func" // 函数类任务
JobTypePlugin = "plugin" // 插件类任务

RegexOfRemindEveryMonth = `^设置每月(0?[1-9]|[12][0-9]|3[01])号(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的提醒$`
RegexOfRemindEveryWeek = `^设置每周(一|二|三|四|五|六|七|日)(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的提醒$`
RegexOfRemindEveryDay = `^设置每天(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的提醒$`
RegexOfRemindInterval = `^设置每隔(\d+)(s|秒|m|分|分钟|h|时|d|小时)的提醒$`
RegexOfRemindSpecifyTime = `^设置((20[2-9][0-9]|2100)-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\s([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的提醒$`
RegexOfRemindExpression = `^设置表达式\((((\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?)\s+(\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?\s+(\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?\s+(\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?\s+(\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?\s+(\*(/\d+)?|((\d+(-\d+)?)(,\d+(-\d+)?)*))(/\d+)?)\)的提醒$`
RegexOfPluginEveryDay = `^设置每天(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])执行插件$`
)

type CronJob struct {
Id int64 `gorm:"primary_key"`
Tag string `gorm:"column:tag"`
Type string `gorm:"column:type"`
Desc string `gorm:"column:desc"`
GroupId string `gorm:"column:group_id"`
Remind string `gorm:"column:remind"`
Service string `gorm:"column:service"`
Id int64 `gorm:"primary_key"` // 任务ID
Tag string `gorm:"column:tag"` // 任务标签
Type string `gorm:"column:type"` // 任务类型
Desc string `gorm:"column:desc"` // 任务描述
GroupId string `gorm:"column:group_id"` // 群ID
Remind string `gorm:"column:remind"` // 提醒内容
Service string `gorm:"column:service"` // 插件服务名
}

func registerCronjob() {
Expand All @@ -50,7 +47,6 @@ func registerCronjob() {
"* 设置每隔[]的提醒 -> 例如:设置每隔1小时的提醒\n" +
"* 设置[]的提醒 -> 例如:设置2023-01-01 15:00:00的提醒\n" +
"* 设置表达式[]的提醒 -> 例如:设置表达式(*/10 * * * * *)的提醒\n" +
"* 设置每天[]执行插件 -> 例如:设置每天08:00:00执行插件\n" +
"* 列出所有任务\n" +
"* 删除任务 [任务ID]\n" +
"* 删除全部任务\n" +
Expand All @@ -68,7 +64,6 @@ func registerCronjob() {
return
}
ctx := robot.GetCtx()
options := control.GetOptionsOnCronjob()
for i := range cronJobs {
cronJob := cronJobs[i]
switch cronJob.Type {
Expand Down Expand Up @@ -126,23 +121,6 @@ func registerCronjob() {
log.Errorf("恢复表达式提醒任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}
case JobTypePlugin:
// 恢复每天的插件任务
if matched := regexp.MustCompile(RegexOfPluginEveryDay).FindStringSubmatch(cronJob.Desc); matched != nil {
if _, err := AddPluginOfEveryDay(ctx, cronJob.Tag, matched, func() {
defer func() {
if err := recover(); err != nil {
log.Errorf("执行插件任务失败: %v", string(debug.Stack()))
}
}()
if s, ok := options[cronJob.Service]; ok {
ctx.Event = &robot.Event{FromUniqueID: cronJob.GroupId}
s.Options.OnCronjob(ctx)
}
}); err != nil {
log.Errorf("恢复每天插件任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}
}
}
job.StartAsync()
Expand Down Expand Up @@ -406,60 +384,6 @@ func registerCronjob() {
}
})

// 设置每天的执行插件任务
// 设置每天08:00:00执行插件
engine.OnRegex(RegexOfPluginEveryDay, robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
matched := ctx.State["regex_matched"].([]string)
jobDesc := ctx.MessageString()
recv, cancel := ctx.EventChannel(ctx.CheckUserSession()).Repeat()
defer cancel()
options := control.GetOptionsOnCronjob()
msg := "请问需要设置哪个插件呢?\n"
for i := range options {
msg += options[i].Service + "\n"
}
ctx.ReplyText(msg)
for {
select {
case <-time.After(20 * time.Second):
ctx.ReplyTextAndAt("操作时间太久了,请重新设置")
return
case ctx := <-recv:
s, ok := options[ctx.MessageString()]
if !ok {
ctx.ReplyTextAndAt("没有这个插件服务,请重新设置")
continue
}

jobId := mid.UniqueId()
jobTag := strconv.Itoa(int(jobId))
service := ctx.MessageString()

// 设置定时任务
if _, err := AddPluginOfEveryDay(ctx, jobTag, matched, func() { s.Options.OnCronjob(ctx) }); err != nil {
ctx.ReplyTextAndAt(fmt.Errorf("设置失败: %v", err).Error())
return
}

// 存起来便于服务启动恢复
if err := db.Orm.Table("cronjob").Create(&CronJob{
Id: jobId,
Tag: jobTag,
Type: JobTypePlugin,
Desc: jobDesc,
GroupId: ctx.Event.FromUniqueID,
Service: service,
}).Error; err != nil {
ctx.ReplyTextAndAt(fmt.Errorf("设置失败: %v", err).Error())
return
}
ctx.ReplyTextAndAt(fmt.Sprintf("已为您%s: %s", jobDesc, service))
job.StartAsync()
return
}
}
})

// 列出当前所有定时任务
engine.OnFullMatch("列出所有任务").SetBlock(true).Handle(func(ctx *robot.Ctx) {
var cronJobs []CronJob
Expand All @@ -472,8 +396,6 @@ func registerCronjob() {
switch cronJobs[i].Type {
case JobTypeRemind:
jobInfo += fmt.Sprintf("任务ID: %d\n任务类型: %s\n任务描述: %s\n任务内容: %s\n\n", cronJobs[i].Id, cronJobs[i].Type, cronJobs[i].Desc, cronJobs[i].Remind)
case JobTypePlugin:
jobInfo += fmt.Sprintf("任务ID: %d\n任务类型: %s\n任务描述: %s\n任务内容: %s\n\n", cronJobs[i].Id, cronJobs[i].Type, cronJobs[i].Desc, cronJobs[i].Service)
}
}
if len(cronJobs) == 0 {
Expand Down Expand Up @@ -524,26 +446,6 @@ func registerCronjob() {
}
})

// 删除所有插件任务
engine.OnFullMatchGroup([]string{"删除全部插件任务", "删除所有插件任务"}, robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
var jobTags []string
if err := db.Orm.Table("cronjob").Where("group_id = ? AND type = ?", ctx.Event.FromUniqueID, JobTypePlugin).Pluck("tag", &jobTags).Error; err != nil {
log.Errorf("[CronJob] 删除全部插件任务失败: %v", err)
ctx.ReplyTextAndAt("删除全部插件任务失败")
return
}
if err := db.Orm.Table("cronjob").Where("group_id = ? AND type = ?", ctx.Event.FromUniqueID, JobTypePlugin).Delete(&CronJob{}).Error; err != nil {
log.Errorf("[CronJob] 删除全部插件任务失败: %v", err)
ctx.ReplyTextAndAt("删除全部插件任务失败")
return
}
if err := job.RemoveByTagsAny(jobTags...); err != nil {
log.Errorf("[CronJob] 删除全部插件任务失败: %v", err)
} else {
ctx.ReplyTextAndAt("已删除全部插件任务")
}
})

// 删除全部任务
engine.OnFullMatchGroup([]string{"删除全部任务", "删除所有任务"}, robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
var jobTags []string
Expand Down
11 changes: 6 additions & 5 deletions plugins/manager/cronjob_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (

var job = gocron.NewScheduler(time.Local)

func init() {
// 设置最大并发任务数,防止并发太大对账号有影响
// SetMaxConcurrentJobs函数可能有重复的可能,待观察,https://github.com/go-co-op/gocron/blob/main/executor.go#L16
job.SetMaxConcurrentJobs(10, gocron.WaitMode)
}

// AddRemindOfEveryMonth 添加每月提醒
func AddRemindOfEveryMonth(ctx *robot.Ctx, jobTag string, matched []string, f func()) (*gocron.Job, error) {
timeSplit := strings.Split(matched[2], ":")
Expand Down Expand Up @@ -83,8 +89,3 @@ func AddRemindForSpecifyTime(ctx *robot.Ctx, jobTag string, matched []string, f
func AddRemindForExpression(ctx *robot.Ctx, jobTag string, matched []string, f func()) (*gocron.Job, error) {
return job.CronWithSeconds(matched[1]).Tag(jobTag).Do(func() { f() })
}

// AddPluginOfEveryDay 添加每天执行的插件
func AddPluginOfEveryDay(ctx *robot.Ctx, jobTag string, matched []string, f func()) (*gocron.Job, error) {
return job.Every(1).Day().At(matched[1]).Tag(jobTag).Do(func() { f() })
}
27 changes: 2 additions & 25 deletions plugins/zaobao/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package zaobao
import (
"fmt"
"path/filepath"
"sync"
"time"

"github.com/yqchilde/wxbot/engine/control"
Expand All @@ -14,10 +13,8 @@ import (
)

var (
db sqlite.DB
zaoBao ZaoBao
cronjobMutex sync.Mutex
waitSendImage sync.Map
db sqlite.DB
zaoBao ZaoBao
)

type ZaoBao struct {
Expand All @@ -41,12 +38,6 @@ func init() {
// todo 停止将定时任务从定时任务列表移除
ctx.ReplyText("禁用成功")
},
OnCronjob: func(ctx *robot.Ctx) {
wxId := ctx.Event.FromUniqueID
cronjobMutex.Lock()
defer cronjobMutex.Unlock()
waitSendImage.Store(wxId, ctx)
},
})

if err := sqlite.Open(engine.GetDataFolder()+"/zaobao.db", &db); err != nil {
Expand Down Expand Up @@ -105,18 +96,6 @@ func pollingTask() {
<-timer.C
timer.Stop()

// 任务
doSendImage := func(imgCache string) {
waitSendImage.Range(func(key, val interface{}) bool {
ctx := val.(*robot.Ctx)
ctx.SendImage(key.(string), "local://"+imgCache)
waitSendImage.Delete(key)
// 有时候连续发图片会有问题,所以延迟10s
time.Sleep(10 * time.Second)
return true
})
}

// 轮询任务
ticker := time.NewTicker(10 * time.Minute)
for range ticker.C {
Expand All @@ -136,8 +115,6 @@ func pollingTask() {
if err := flushZaoBao(zaoBao.Token, imgCache); err != nil {
continue
}
doSendImage(imgCache)
}
doSendImage(imgCache)
}
}

0 comments on commit d7c5934

Please sign in to comment.