From 189eab8a0a1e9e87fd694f8939f128a32fba1489 Mon Sep 17 00:00:00 2001 From: mei Date: Mon, 6 Apr 2026 10:10:31 +0800 Subject: [PATCH] feat(checker): checker --- api/account/account.go | 6 ++--- api/user/tool.go | 4 +++ api/user/user.go | 18 ++++++++++--- data.db | Bin 49152 -> 49152 bytes drivers/bilibili/bilibili.go | 16 +++++++++-- drivers/bilibili/model.go | 2 +- drivers/bilibili/tool.go | 13 ++++++++- handler/checker/checker.go | 51 +++++++++++++++++++++++++++++++++++ handler/reminder/reminder.go | 28 +++++++++++++++++++ handler/runner/runner.go | 48 +++++++++++++++++++++++++++++++++ main.go | 1 + model/timer.go | 1 + model/user.go | 5 ++++ 13 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 handler/checker/checker.go create mode 100644 handler/reminder/reminder.go create mode 100644 handler/runner/runner.go diff --git a/api/account/account.go b/api/account/account.go index 5f520f3..bdf0119 100644 --- a/api/account/account.go +++ b/api/account/account.go @@ -73,7 +73,6 @@ func HandleCheckDeleteAccount(c *gin.Context) { c.JSON(401, gin.H{"code": 401, "data": "获取用户信息失败"}) return } - accountID, err := strconv.Atoi(c.Param("accountID")) if err != nil { c.JSON(400, gin.H{"code": 400, "data": "账号ID格式错误"}) @@ -96,19 +95,18 @@ func HandleDeleteAccount(c *gin.Context) { c.JSON(401, gin.H{"code": 401, "data": "获取用户信息失败"}) return } - accountID, err := strconv.Atoi(c.Param("accountID")) if err != nil { c.JSON(400, gin.H{"code": 400, "data": "账号ID格式错误"}) return } + // 删除相关规则 rules, err := getRulesByAccountID(uint(accountID), userInfo.ID) if err != nil { c.JSON(500, gin.H{"code": 500, "data": "获取相关规则失败"}) return } - for _, oneRule := range rules { err = rule.DeleteRuleByID(oneRule.ID, userInfo.ID) if err != nil { @@ -117,12 +115,12 @@ func HandleDeleteAccount(c *gin.Context) { } } + // 删除账号 gormDB, err := db.GetGormDB() if err != nil { c.JSON(500, gin.H{"code": 500, "data": "服务器内部错误"}) return } - result := gormDB.Where("id = ? AND uid = ?", accountID, userInfo.ID).Delete(&model.Account{}) if result.Error != nil { c.JSON(500, gin.H{"code": 500, "data": "删除账号失败"}) diff --git a/api/user/tool.go b/api/user/tool.go index d9d10a7..6ae6688 100644 --- a/api/user/tool.go +++ b/api/user/tool.go @@ -1,6 +1,8 @@ package user import ( + "time" + "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "github.com/ssdomei232/goodBaby/handler/db" @@ -34,6 +36,8 @@ func createUser(u *model.User) error { } u.Password = hashedPassword + u.CreateAt = time.Now().Unix() + result := gormDB.Create(u) return result.Error } diff --git a/api/user/user.go b/api/user/user.go index 146f6af..fbb7225 100644 --- a/api/user/user.go +++ b/api/user/user.go @@ -9,7 +9,7 @@ import ( // 处理注册请求,配置文件中可设置禁用注册 func HandleRegistry(c *gin.Context) { - var user model.User + var userRegistryRequest model.UserRegistryReuest var err error config, err := configs.GetConfig() @@ -23,12 +23,17 @@ func HandleRegistry(c *gin.Context) { return } - err = c.BindJSON(&user) + err = c.BindJSON(&userRegistryRequest) if err != nil { c.JSON(400, gin.H{"code": 400, "data": "输入错误"}) return } + user := model.User{ + Username: userRegistryRequest.Username, + Password: userRegistryRequest.Password, + } + if err = user.IsValid(); err != nil { c.JSON(400, gin.H{"code": 400, "data": err.Error()}) return @@ -53,15 +58,20 @@ func HandleRegistry(c *gin.Context) { // 处理登录请求 func HandleLogin(c *gin.Context) { - var user model.User + var userLoginRequest model.UserRegistryReuest var err error - err = c.BindJSON(&user) + err = c.BindJSON(&userLoginRequest) if err != nil { c.JSON(400, gin.H{"code": 400, "data": "输入错误"}) return } + user := model.User{ + Username: userLoginRequest.Username, + Password: userLoginRequest.Password, + } + if err = user.IsValid(); err != nil { c.JSON(400, gin.H{"code": 400, "data": err.Error()}) } diff --git a/data.db b/data.db index 7848f5c4c06172cc9faac1b54a838a9e35cefb89..9eb321b12b6406ceb4927f55c1a6b44935a08ad2 100644 GIT binary patch delta 248 zcmZo@U~Xt&o**sA!@$4*!W=-%1jH&6b&QpH81$+NdAaW}aB@Cp;5*OD%yWtB63=qp zyWAT&pYv4l#PGyyoS4cf)0iQ_E-op_*sNWWn3R)Rl9`)YR16`RCja2>6HzKk&CSe9 zi3f?r7pErY=cN=+uHw<%e2I&Tk%e~`!@P+TXR&EZvWeSEPF~9+G5G)=A0zYT;lIMb|C0Y3{}ujw{QEW=@*m>o n;b3PL=O`@(I)Rg8^8;kNeKwq%~Rea%Jl;8jWr2t3$ delta 212 zcmZo@U~Xt&o**sA&A`CG1H{Zg%ml;=6LpN0xf%4T3VC^VF>rESV&FT^%gl3$>k`j$ z?mK*oc<*v=g); E08_R&yZ`_I diff --git a/drivers/bilibili/bilibili.go b/drivers/bilibili/bilibili.go index 39384c2..733fa6e 100644 --- a/drivers/bilibili/bilibili.go +++ b/drivers/bilibili/bilibili.go @@ -8,12 +8,13 @@ import ( "github.com/CuteReimu/bilibili/v2" "github.com/cenkalti/backoff/v5" "github.com/ssdomei232/goodBaby/configs" + "github.com/ssdomei232/goodBaby/model" ) // 在Bilibili动态发送消息 // // 暂时没有处理 429 和 403 的区别对待 -func SendBiliDynamicMsg(biliClient *bilibili.Client, msg string) { +func SendBiliDynamicMsg(rule *model.Rule) { config, err := configs.GetConfig() if err != nil { log.Printf("获取配置失败: %v", err) @@ -21,12 +22,23 @@ func SendBiliDynamicMsg(biliClient *bilibili.Client, msg string) { } var timeout time.Duration = time.Duration(config.TimeoutDurationHours) * time.Hour + biliClient, err := getBiliClient(rule) + if err != nil { + log.Printf("获取B站客户端失败: %v", err) + return + } + biliDyncmicConfig, err := getBiliDynamicConfig(rule) + if err != nil { + log.Printf("获取B站动态配置失败: %v", err) + return + } + var dynamicParams bilibili.CreateDynamicParam dynamicParams = bilibili.CreateDynamicParam{ DynamicId: 0, Type: 4, Rid: 0, - Content: msg, + Content: biliDyncmicConfig.Msg, } ctx, cancel := context.WithTimeout(context.Background(), timeout) diff --git a/drivers/bilibili/model.go b/drivers/bilibili/model.go index 531ea48..5589a6f 100644 --- a/drivers/bilibili/model.go +++ b/drivers/bilibili/model.go @@ -4,6 +4,6 @@ type BiliAccount struct { RawCookies string `json:"raw_cookies"` } -type BiliDynamicRule struct { +type BiliDynamicConfig struct { Msg string `json:"msg"` } diff --git a/drivers/bilibili/tool.go b/drivers/bilibili/tool.go index de42877..70bff62 100644 --- a/drivers/bilibili/tool.go +++ b/drivers/bilibili/tool.go @@ -9,7 +9,7 @@ import ( ) // 获取 Bilibili Client -func GetBiliClient(rule *model.Rule) (*bilibili.Client, error) { +func getBiliClient(rule *model.Rule) (*bilibili.Client, error) { client := bilibili.New() gormDB, err := db.GetGormDB() if err != nil { @@ -34,3 +34,14 @@ func GetBiliClient(rule *model.Rule) (*bilibili.Client, error) { return client, nil } + +// 获取 Bilibili Dynamic Config +func getBiliDynamicConfig(rule *model.Rule) (*BiliDynamicConfig, error) { + var biliDynamicConfig BiliDynamicConfig + err := json.Unmarshal([]byte(rule.ConfigJson), &biliDynamicConfig) + if err != nil { + return nil, err + } + + return &biliDynamicConfig, nil +} diff --git a/handler/checker/checker.go b/handler/checker/checker.go new file mode 100644 index 0000000..b540aa6 --- /dev/null +++ b/handler/checker/checker.go @@ -0,0 +1,51 @@ +package checker + +import ( + "log" + "time" + + "github.com/ssdomei232/goodBaby/handler/db" + "github.com/ssdomei232/goodBaby/handler/reminder" + "github.com/ssdomei232/goodBaby/handler/runner" + "github.com/ssdomei232/goodBaby/model" +) + +// 定时检查所有 timers 是否需要被触发 +// +// 同时向即将被触发的 timers 发送通知 +func CheckTimers() { + gormDB, err := db.GetGormDB() + if err != nil { + log.Printf("failed to get gorm db: %v", err) + return + } + + // 1. 获取所有 timers + var timers []model.Timer + if err := gormDB.Find(&timers).Error; err != nil { + log.Printf("failed to get timers: %v", err) + return + } + + // 2. 检查每个 timer 是否需要被触发 + for _, timer := range timers { + // 计算下次触发时间 + nextSignTime := timer.LastSign + timer.SignDerationSeconds + + // 计算提醒时间 + remindTime := nextSignTime - timer.RemindTimeSeconds + + // 获取当前时间 + currentTime := time.Now().Unix() + + if currentTime >= remindTime && currentTime < nextSignTime { + // 发送通知 + reminder.Reminder(&timer) + } + + if currentTime >= nextSignTime { + // 触发 timer + runner.Runner(&timer) + } + } +} diff --git a/handler/reminder/reminder.go b/handler/reminder/reminder.go new file mode 100644 index 0000000..b95b764 --- /dev/null +++ b/handler/reminder/reminder.go @@ -0,0 +1,28 @@ +package reminder + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/dingtalk" + "github.com/ssdomei232/goodBaby/handler/db" + "github.com/ssdomei232/goodBaby/model" +) + +// reminder 负责在 checker 检查到 timer 即将被触发时,向用户发送提醒 +func Reminder(timer *model.Timer) { + gormDB, err := db.GetGormDB() + if err != nil { + log.Printf("failed to get gorm db: %v", err) + return + } + + // 1. 获取 timer 对应的 user + var user model.User + if err := gormDB.Where("id = ?", timer.UID).First(&user).Error; err != nil { + log.Printf("failed to get user: %v", err) + return + } + + // 2. 向用户发送提醒 + dingtalk.SendDingTalkMsg(&user, "您的 goodbaby timer 即将被触发", "您的 goodbaby timer 即将被触发") +} diff --git a/handler/runner/runner.go b/handler/runner/runner.go new file mode 100644 index 0000000..c39b5a6 --- /dev/null +++ b/handler/runner/runner.go @@ -0,0 +1,48 @@ +package runner + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/bilibili" + "github.com/ssdomei232/goodBaby/drivers/email" + "github.com/ssdomei232/goodBaby/drivers/github" + "github.com/ssdomei232/goodBaby/drivers/onebot" + "github.com/ssdomei232/goodBaby/handler/db" + "github.com/ssdomei232/goodBaby/model" +) + +// runner 执行需要被执行的 Rule +func Runner(timer *model.Timer) { + gormDB, err := db.GetGormDB() + if err != nil { + log.Printf("failed to get gorm db: %v", err) + return + } + + // 1. 获取所有需要执行的 Rule + var rules []model.Rule + if err := gormDB.Where("timer_id = ?", timer.ID).Find(&rules).Error; err != nil { + log.Printf("failed to get rules: %v", err) + return + } + + // 2. 执行每个 Rule + for _, rule := range rules { + // 执行 rule + go executeRule(&rule) + } +} + +// super switch +func executeRule(rule *model.Rule) { + switch rule.Type { + case "bilibili-dynamic": + bilibili.SendBiliDynamicMsg(rule) + case "email": + email.SendMail(rule) + case "github-make-repo-public": + github.MakeRepositoryPublic(rule) + case "onebot": + onebot.SendOneBotMsg(rule) + } +} diff --git a/main.go b/main.go index 5a82dd3..c6cac32 100644 --- a/main.go +++ b/main.go @@ -34,6 +34,7 @@ func main() { authorized.GET("/accounts", account.HandleGetAllAccounts) authorized.POST("/accounts", account.HandleAddAccount) authorized.GET("/accounts/:accountID/check", account.HandleCheckDeleteAccount) + authorized.DELETE("/accounts/:accountID", account.HandleDeleteAccount) } r.Run(":8088") diff --git a/model/timer.go b/model/timer.go index cd2d338..7658187 100644 --- a/model/timer.go +++ b/model/timer.go @@ -6,4 +6,5 @@ type Timer struct { Name string `json:"name"` SignDerationSeconds int64 `json:"sign_deration_seconds"` LastSign int64 `json:"last_sign"` + RemindTimeSeconds int64 `json:"remind_time_seconds"` // 提前多少秒提醒 } diff --git a/model/user.go b/model/user.go index e54ed32..cd8a54d 100644 --- a/model/user.go +++ b/model/user.go @@ -6,6 +6,11 @@ import ( "github.com/ssdomei232/goodBaby/handler/db" ) +type UserRegistryReuest struct { + Username string `json:"username"` + Password string `json:"password"` +} + type User struct { ID uint `json:"id" gorm:"primaryKey"` CreateAt int64 `json:"create_at"`