Skip to content

Commit

Permalink
feat: 定时任务支持设置工作日 (#65)
Browse files Browse the repository at this point in the history
* feat: 定时任务支持设置工作日

* feat: 补充节假日数据
  • Loading branch information
yqchilde authored Mar 12, 2023
1 parent 0100219 commit 9c7cd69
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 0 deletions.
3 changes: 3 additions & 0 deletions plugins/manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* 插件名:cronjob
* 插件描述:定时任务
* 权限:多数配置限配置的管理员使用,不区分私聊和群聊中使用
* 说明:节假日数据来自[holiday-cn](https://github.com/NateScarlet/holiday-cn)
* 指令:
* 提醒类任务指令(具体正则请查看代码):
* [x] `设置每月[]号[]的提醒任务`,例如:设置每月8号10:00:00的提醒任务
Expand All @@ -21,6 +22,7 @@
* [x] `设置每隔[]的提醒任务`,例如:设置每隔1小时的提醒任务
* [x] `设置[]的提醒任务`,例如:设置2023-01-01 15:00:00的提醒任务
* [x] `设置表达式[]的提醒任务`,例如:设置表达式(*/10 * * * * *)的提醒任务
* [x] `设置工作日[]的提醒任务`,例如:设置工作日10:00:00的提醒任务
* [x] `删除全部提醒任务`
* 插件类任务指令(具体正则请查看代码):
* [x] `设置每月[]号[]的插件任务`,例如:设置每月8号10:00:00的插件任务
Expand All @@ -29,6 +31,7 @@
* [x] `设置每隔[]的插件任务`,例如:设置每隔1小时的插件任务
* [x] `设置[]的插件任务`,例如:设置2023-01-01 15:00:00的插件任务
* [x] `设置表达式[]的插件任务`,例如:设置表达式(*/10 * * * * *)的插件任务
* [x] `设置工作日[]的插件任务`,例如:设置工作日10:00:00的插件任务
* [x] `删除全部提醒任务`
* 其他指令:
* [x] `列出所有任务`
Expand Down
73 changes: 73 additions & 0 deletions plugins/manager/cronjob.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package manager

import (
"embed"
"fmt"
"regexp"
"time"

"github.com/tidwall/gjson"

"github.com/yqchilde/wxbot/engine/control"
"github.com/yqchilde/wxbot/engine/pkg/log"
"github.com/yqchilde/wxbot/engine/robot"
)

//go:embed data
var holidayData embed.FS

const (
JobTypeRemind = "remind" // 提醒类任务
JobTypePlugin = "plugin" // 插件类任务
Expand Down Expand Up @@ -37,6 +44,7 @@ func registerCronjob() {
"* 设置每隔[]的提醒任务 -> 例如:设置每隔1小时的提醒任务\n" +
"* 设置[]的提醒任务 -> 例如:设置2023-01-01 15:00:00的提醒任务\n" +
"* 设置表达式[]的提醒任务 -> 例如:设置表达式(*/10 * * * * *)的提醒任务\n" +
"* 设置工作日[]的提醒任务 -> 例如:设置工作日10:00:00的提醒任务\n" +
"* 删除全部提醒任务\n\n" +
"插件类任务指令:\n" +
"* 设置每月[]号[]的插件任务 -> 例如:设置每月8号10:00:00的插件任务\n" +
Expand All @@ -45,6 +53,7 @@ func registerCronjob() {
"* 设置每隔[]的插件任务 -> 例如:设置每隔1小时的插件任务\n" +
"* 设置[]的插件任务 -> 例如:设置2023-01-01 15:00:00的插件任务\n" +
"* 设置表达式[]的插件任务 -> 例如:设置表达式(*/10 * * * * *)的插件任务\n" +
"* 设置工作日[]的插件任务 -> 例如:设置工作日10:00:00的插件任务\n" +
"* 删除全部插件任务\n\n" +
"其他指令:\n" +
"* 列出所有任务\n" +
Expand Down Expand Up @@ -120,6 +129,38 @@ func registerCronjob() {
log.Errorf("恢复表达式提醒任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}

// 恢复工作日提醒任务
if matched := regexp.MustCompile(RegexOfRemindWorkDay).FindStringSubmatch(cronJob.Desc); matched != nil {
if _, err := AddCronjobOfEveryDay(ctx, cronJob.Tag, matched, func() {
data, err := holidayData.ReadFile(fmt.Sprintf("data/holiday_%d.json", time.Now().Year()))
if err != nil {
log.Errorf("获取节假日数据失败: %v", err)
ctx.ReplyText("很抱歉今天提醒您,由于节假日数据获取失败了,我无法确定今天是否是工作日")
return
}
var now = time.Now().Local()
var isWorkDay, isHoliday bool
gjson.GetBytes(data, "days").ForEach(func(key, val gjson.Result) bool {
if val.Get("date").String() == now.Format("2006-01-02") {
isWorkDay = !val.Get("isOffDay").Bool()
isHoliday = val.Get("isOffDay").Bool()
return false
}
return true
})
if !isHoliday && !isWorkDay {
if now.Weekday() != time.Saturday && now.Weekday() != time.Sunday {
isWorkDay = true
}
}
if isWorkDay {
ctx.SendText(cronJob.WxId, cronJob.Remind)
}
}); err != nil {
log.Errorf("恢复表达式提醒任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}
case JobTypePlugin:
// 恢复每月的插件任务
if matched := regexp.MustCompile(RegexOfPluginEveryMonth).FindStringSubmatch(cronJob.Desc); matched != nil {
Expand Down Expand Up @@ -174,6 +215,38 @@ func registerCronjob() {
log.Errorf("恢复表达式插件任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}

// 恢复工作日插件任务
if matched := regexp.MustCompile(RegexOfPluginWorkDay).FindStringSubmatch(cronJob.Desc); matched != nil {
if _, err := AddCronjobOfEveryDay(ctx, cronJob.Tag, matched, func() {
data, err := holidayData.ReadFile(fmt.Sprintf("data/holiday_%d.json", time.Now().Year()))
if err != nil {
log.Errorf("获取节假日数据失败: %v", err)
ctx.SendText(cronJob.WxId, "很抱歉今天提醒您,由于节假日数据获取失败了,我无法确定今天是否是工作日")
return
}
var now = time.Now().Local()
var isWorkDay, isHoliday bool
gjson.GetBytes(data, "days").ForEach(func(key, val gjson.Result) bool {
if val.Get("date").String() == now.Format("2006-01-02") {
isWorkDay = !val.Get("isOffDay").Bool()
isHoliday = val.Get("isOffDay").Bool()
return false
}
return true
})
if !isHoliday && !isWorkDay {
if now.Weekday() != time.Saturday && now.Weekday() != time.Sunday {
isWorkDay = true
}
}
if isWorkDay {
ctx.SendTextAndPushEvent(cronJob.WxId, cronJob.Remind)
}
}); err != nil {
log.Errorf("恢复表达式提醒任务失败: jobId: %d, error: %v", cronJob.Id, err)
}
}
}
}
job.StartAsync()
Expand Down
72 changes: 72 additions & 0 deletions plugins/manager/cronjob_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"strconv"
"time"

"github.com/tidwall/gjson"

"github.com/yqchilde/wxbot/engine/control"
"github.com/yqchilde/wxbot/engine/pkg/log"
"github.com/yqchilde/wxbot/engine/pkg/mid"
Expand All @@ -18,6 +20,7 @@ const (
RegexOfPluginInterval = `^设置每隔(\d+)(s|秒|m|分|分钟|h|时|d|小时)的插件任务`
RegexOfPluginSpecifyTime = `^设置((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])的插件任务`
RegexOfPluginExpression = `^设置表达式\((((\*(/\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+)?)\)的插件任务`
RegexOfPluginWorkDay = `^设置工作日(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的插件任务$`
)

// SetPluginCommand 设置插件类任务指令
Expand Down Expand Up @@ -286,6 +289,75 @@ func SetPluginCommand(engine *control.Engine) {
}
})

// 设置工作日的插件任务
// 设置工作日10:00:00的插件任务
engine.OnRegex(RegexOfPluginWorkDay, 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()
ctx.ReplyText("请问要执行的插件指令是什么呢?")
for {
select {
case <-time.After(20 * time.Second):
ctx.ReplyTextAndAt("操作时间太久了,请重新设置")
return
case ctx := <-recv:
jobId := mid.UniqueId()
jobTag := strconv.Itoa(int(jobId))
remind := ctx.MessageString()

// 设置定时任务
if _, err := AddCronjobOfEveryDay(ctx, jobTag, matched, func() {
data, err := holidayData.ReadFile(fmt.Sprintf("data/holiday_%d.json", time.Now().Year()))
if err != nil {
log.Errorf("获取节假日数据失败: %v", err)
ctx.ReplyText("很抱歉今天提醒您,由于节假日数据获取失败了,我无法确定今天是否是工作日")
return
}
var now = time.Now().Local()
var isWorkDay, isHoliday bool
gjson.GetBytes(data, "days").ForEach(func(key, val gjson.Result) bool {
if val.Get("date").String() == now.Format("2006-01-02") {
isWorkDay = !val.Get("isOffDay").Bool()
isHoliday = val.Get("isOffDay").Bool()
return false
}
return true
})
if !isHoliday && !isWorkDay {
if now.Weekday() != time.Saturday && now.Weekday() != time.Sunday {
isWorkDay = true
}
}
if isWorkDay {
ctx.ReplyTextAndAt(remind)
}
}); 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,
WxId: ctx.Event.FromUniqueID,
WxName: ctx.Event.FromUniqueName,
Remind: remind,
}).Error; err != nil {
ctx.ReplyTextAndAt(fmt.Errorf("设置失败: %v", err).Error())
return
}
ctx.ReplyTextAndAt(fmt.Sprintf("已为您%s: %s", jobDesc, remind))
job.StartAsync()
return
}
}
})

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

"github.com/tidwall/gjson"

"github.com/yqchilde/wxbot/engine/control"
"github.com/yqchilde/wxbot/engine/pkg/log"
"github.com/yqchilde/wxbot/engine/pkg/mid"
Expand All @@ -18,6 +20,7 @@ const (
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+)?)\)的提醒任务$`
RegexOfRemindWorkDay = `^设置工作日(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])的提醒任务$`
)

// SetRemindCommand 设置提醒类任务指令
Expand Down Expand Up @@ -286,6 +289,75 @@ func SetRemindCommand(engine *control.Engine) {
}
})

// 设置工作日的提醒任务
// 设置工作日10:00:00的提醒任务
engine.OnRegex(RegexOfRemindWorkDay, 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()
ctx.ReplyText("请问需要提醒什么呢?")
for {
select {
case <-time.After(20 * time.Second):
ctx.ReplyTextAndAt("操作时间太久了,请重新设置")
return
case ctx := <-recv:
jobId := mid.UniqueId()
jobTag := strconv.Itoa(int(jobId))
remind := ctx.MessageString()

// 设置定时任务
if _, err := AddCronjobOfEveryDay(ctx, jobTag, matched, func() {
data, err := holidayData.ReadFile(fmt.Sprintf("data/holiday_%d.json", time.Now().Year()))
if err != nil {
log.Errorf("获取节假日数据失败: %v", err)
ctx.ReplyText("很抱歉今天提醒您,由于节假日数据获取失败了,我无法确定今天是否是工作日")
return
}
var now = time.Now().Local()
var isWorkDay, isHoliday bool
gjson.GetBytes(data, "days").ForEach(func(key, val gjson.Result) bool {
if val.Get("date").String() == now.Format("2006-01-02") {
isWorkDay = !val.Get("isOffDay").Bool()
isHoliday = val.Get("isOffDay").Bool()
return false
}
return true
})
if !isHoliday && !isWorkDay {
if now.Weekday() != time.Saturday && now.Weekday() != time.Sunday {
isWorkDay = true
}
}
if isWorkDay {
ctx.ReplyTextAndAt(remind)
}
}); err != nil {
ctx.ReplyTextAndAt(fmt.Errorf("设置失败: %v", err).Error())
return
}

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

// 删除所有提醒任务
engine.OnFullMatchGroup([]string{"删除全部提醒任务", "删除所有提醒任务"}, robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
var jobTags []string
Expand Down
Loading

0 comments on commit 9c7cd69

Please sign in to comment.