feat: v2(Incomplete,very early version)

This commit is contained in:
mei
2026-04-05 10:36:22 +08:00
parent 2ab9e060eb
commit 6190a8853a
41 changed files with 1131 additions and 766 deletions
+30
View File
@@ -0,0 +1,30 @@
package bilibili
import (
"log"
"os"
"github.com/CuteReimu/bilibili/v2"
)
// Qrcode login
func HandleLoginWithQRCode(biliClient *bilibili.Client) {
qrCode, err := biliClient.GetQRCode()
if err != nil {
log.Printf("获取二维码失败: %v", err)
return
}
buf, _ := qrCode.Encode()
os.WriteFile("tmp/qrcode.png", buf, 0644)
qrCode.Print()
result, err := biliClient.LoginWithQRCode(bilibili.LoginWithQRCodeParam{
QrcodeKey: qrCode.QrcodeKey,
})
if err != nil || result.Code != 0 {
log.Printf("登录失败: %v", err)
return
}
log.Println("登录成功")
}
+44
View File
@@ -0,0 +1,44 @@
package bilibili
import (
"context"
"log"
"time"
"github.com/CuteReimu/bilibili/v2"
"github.com/cenkalti/backoff/v5"
"github.com/ssdomei232/goodBaby/configs"
)
// 在Bilibili动态发送消息
//
// 暂时没有处理 429 和 403 的区别对待
func SendBiliDynamicMsg(biliClient *bilibili.Client, msg string) {
config, err := configs.GetConfig()
if err != nil {
log.Printf("获取配置失败: %v", err)
return
}
var timeout time.Duration = time.Duration(config.TimeoutDurationHours) * time.Hour
var dynamicParams bilibili.CreateDynamicParam
dynamicParams = bilibili.CreateDynamicParam{
DynamicId: 0,
Type: 4,
Rid: 0,
Content: msg,
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
operation := func() (string, error) {
_, err := biliClient.CreateDynamic(dynamicParams)
return "", err
}
_, err = backoff.Retry(ctx, operation, backoff.WithBackOff(backoff.NewExponentialBackOff()))
if err != nil {
log.Printf("发送B站动态失败: %v", err)
}
}
+9
View File
@@ -0,0 +1,9 @@
package bilibili
type BiliAccount struct {
CookiesString string `json:"cookies_string"`
}
type BiliDynamicRule struct {
Msg string `json:"msg"`
}
+134
View File
@@ -0,0 +1,134 @@
package bilibili
import (
"encoding/json"
"log"
"net/http"
"strings"
"time"
"github.com/CuteReimu/bilibili/v2"
"github.com/ssdomei232/goodBaby/handler/db"
"github.com/ssdomei232/goodBaby/model"
)
// 检查cookie是否过期
func isCookiesExpired(cookiesString string) bool {
// 从cookie字符串中提取过期时间
lines := strings.SplitSeq(cookiesString, "\n")
for line := range lines {
if strings.Contains(line, "Expires=") {
// 提取过期时间字符串
expireStr := ""
parts := strings.SplitSeq(line, ";")
for part := range parts {
part = strings.TrimSpace(part)
if after, ok := strings.CutPrefix(part, "Expires="); ok {
expireStr = after
break
}
}
if expireStr != "" {
// 解析过期时间
expireTime, err := http.ParseTime(expireStr)
if err != nil {
log.Printf("解析过期时间失败: %v", err)
return true // 如果解析失败,cookie已过期
}
if time.Now().After(expireTime) {
return true
}
}
}
}
return false
}
// 检查cookie是否即将过期(提前1天)
func isCookiesExpiringSoon(cookiesString string) bool {
lines := strings.SplitSeq(cookiesString, "\n")
for line := range lines {
if strings.Contains(line, "Expires=") {
// 提取过期时间字符串
expireStr := ""
parts := strings.SplitSeq(line, ";")
for part := range parts {
part = strings.TrimSpace(part)
if after, ok := strings.CutPrefix(part, "Expires="); ok {
expireStr = after
break
}
}
if expireStr != "" {
// 解析过期时间
expireTime, err := http.ParseTime(expireStr)
if err != nil {
log.Printf("解析过期时间失败: %v", err)
return true // 如果解析失败,认为需要重新登录
}
// 检查是否即将过期(提前1天)
if time.Now().Add(24 * time.Hour).After(expireTime) {
return true // 即将过期
}
}
}
}
return false
}
// 触发登录请求
func triggerLoginRequest(biliClient *bilibili.Client) {
// 生成新的二维码
qrCode, err := biliClient.GetQRCode()
if err != nil {
log.Printf("获取二维码失败: %v", err)
return
}
// 在命令行打印二维码
log.Println("需要重新登录哔哩哔哩")
qrCode.Print()
// 通过Dingtalk发送登录请求
// title := "B站登录请求 - Cookie即将过期"
// msg := "您的B站cookie即将过期,请尽快重启摇篮系统进行登陆,请尽快完成登录操作。"
// 发送Dingtalk通知
// if config.DingtalkBot.AccessToken != "" && config.DingtalkBot.Secret != "" {
// SendDingTalkMsg(title, msg, config.DingtalkBot.AccessToken, config.DingtalkBot.Secret)
// }
}
// 获取 Bilibili Client
func GetBiliClient(rule *model.Rule) (*bilibili.Client, error) {
client := bilibili.New()
gormDB, err := db.GetGormDB()
if err != nil {
return nil, err
}
// 1. 通过 account_id 获取到对应的 account 配置
var biliAccount model.Account
result := gormDB.Where("id = ?", rule.AccountID).First(&biliAccount)
if result.Error != nil {
return nil, result.Error
}
// 2. 通过 account 配置中的 config 字段获取到 CookiesString
var biliAccountConfig BiliAccount
err = json.Unmarshal([]byte(biliAccount.Config), &biliAccountConfig)
if err != nil {
return nil, err
}
client.SetCookiesString(biliAccountConfig.CookiesString)
return client, nil
}
+5
View File
@@ -0,0 +1,5 @@
package dingtalk
import "github.com/ssdomei232/goodBaby/model"
func SendDingTalkMsg(user *model.User, title string, msg string) {}
+6
View File
@@ -0,0 +1,6 @@
package dingtalk
type DingTalkConfig struct {
AccessToken string `json:"access_token"`
Secret string `json:"secret"`
}
+20
View File
@@ -0,0 +1,20 @@
package dingtalk
import (
"encoding/json"
"github.com/ssdomei232/goodBaby/model"
)
func getDingTalkConfigFromUser(user *model.User) *DingTalkConfig {
if user.DingTalkConfig == nil {
return nil
}
var config DingTalkConfig
err := json.Unmarshal([]byte(*user.DingTalkConfig), &config)
if err != nil {
return nil
}
return &config
}
+20
View File
@@ -0,0 +1,20 @@
package email
import (
"log"
"github.com/ssdomei232/goodBaby/model"
)
// 向邮件列表发送消息
func SendMail(rule *model.Rule) {
emailRule, err := GetEmailRuleFromRule(rule)
if err != nil {
log.Printf("获取邮件规则配置失败: %v", err)
return
}
for _, destinations := range emailRule.Destinations {
go sendMailMsgWithRetry(rule, destinations) // 使用 goroutine 发送邮件,避免指数退避阻塞其他地址
}
}
+15
View File
@@ -0,0 +1,15 @@
package email
type EmailAccountConfig struct {
SMTPServer string `json:"smtp_server"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
TestDestination string `json:"test_destination"` // 用于测试邮件发送的地址
}
type EmailRule struct {
Msg string `json:"msg"`
Title string `json:"title"`
Destinations []string `json:"destinations"` // 邮件地址列表
}
+116
View File
@@ -0,0 +1,116 @@
package email
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/cenkalti/backoff/v5"
"github.com/ssdomei232/goodBaby/configs"
"github.com/ssdomei232/goodBaby/handler/db"
"github.com/ssdomei232/goodBaby/model"
"github.com/wneessen/go-mail"
)
// 从 Rule 中获取 EmailAccount 配置
func GetEmailAccountFromRule(rule *model.Rule) (*EmailAccountConfig, error) {
gormDB, err := db.GetGormDB()
if err != nil {
return nil, err
}
// 1. 通过 account_id 获取到对应的 account 配置
var emailAccount model.Account
result := gormDB.Where("id = ?", rule.AccountID).First(&emailAccount)
if result.Error != nil {
return nil, result.Error
}
// 2. 通过 account 配置中的 config 字段获取到 EmailAccount
var emailAccountConfig EmailAccountConfig
err = json.Unmarshal([]byte(emailAccount.Config), &emailAccountConfig)
if err != nil {
return nil, err
}
return &emailAccountConfig, nil
}
// 从 Rule 中获取 EmailRule 配置
func GetEmailRuleFromRule(rule *model.Rule) (*EmailRule, error) {
var emailRule EmailRule
err := json.Unmarshal([]byte(rule.ConfigJson), &emailRule)
if err != nil {
return nil, err
}
return &emailRule, nil
}
func sendMailMsgWithRetry(rule *model.Rule, address string) {
config, err := configs.GetConfig()
if err != nil {
log.Printf("获取配置失败: %v", err)
return
}
var timeout time.Duration = time.Duration(config.TimeoutDurationHours) * time.Hour
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
operation := func() (string, error) {
err := sendMailMsg(address, rule)
return "", err
}
_, err = backoff.Retry(ctx, operation, backoff.WithBackOff(backoff.NewExponentialBackOff()))
if err != nil {
log.Printf("发送邮件失败: %v", err)
}
}
func sendMailMsg(address string, rule *model.Rule) error {
emailAccountConfig, err := GetEmailAccountFromRule(rule)
if err != nil {
log.Printf("获取邮件账户配置失败: %v", err)
return fmt.Errorf("获取邮件账户配置失败: %v", err)
}
emailRule, err := GetEmailRuleFromRule(rule)
if err != nil {
log.Printf("获取邮件规则配置失败: %v", err)
return fmt.Errorf("获取邮件规则配置失败: %v", err)
}
client, err := mail.NewClient(
emailAccountConfig.SMTPServer,
mail.WithPort(emailAccountConfig.Port),
mail.WithSSL(),
mail.WithUsername(emailAccountConfig.Username),
mail.WithPassword(emailAccountConfig.Password),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
)
if err != nil {
return fmt.Errorf("创建邮件客户端失败: %v", err)
}
// 创建邮件
message := mail.NewMsg()
if err := message.From(emailAccountConfig.Username); err != nil {
return fmt.Errorf("设置发件人失败: %v", err)
}
if err := message.To(address); err != nil {
return fmt.Errorf("设置收件人失败: %v", err)
}
message.Subject(emailRule.Title)
message.SetBodyString(mail.TypeTextPlain, emailRule.Msg)
// 发送邮件
if err := client.DialAndSend(message); err != nil {
return fmt.Errorf("发送邮件失败: %v", err)
}
return nil
}
+42
View File
@@ -0,0 +1,42 @@
package github
import (
"context"
"log"
"time"
"github.com/cenkalti/backoff/v5"
"github.com/ssdomei232/goodBaby/configs"
"github.com/ssdomei232/goodBaby/model"
)
// 将repos设置为public
func MakeRepositoryPublic(rule *model.Rule) {
reposConfig, account := GetGithubReposAndAccountFromRule(rule)
if reposConfig == nil || account == nil {
log.Printf("无法获取 GitHub 配置或账户信息")
return
}
for _, repo := range reposConfig.Repos {
config, err := configs.GetConfig()
if err != nil {
log.Printf("获取配置失败: %v", err)
return
}
var timeout time.Duration = time.Duration(config.TimeoutDurationHours) * time.Hour
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
operation := func() (string, error) {
err := SetRepositoryPublic(account.Token, account.Owner, repo)
return "", err
}
_, err = backoff.Retry(ctx, operation, backoff.WithBackOff(backoff.NewExponentialBackOff()))
if err != nil {
log.Printf("将仓库 %s 设置为 public 失败: %v", repo, err)
}
}
}
+10
View File
@@ -0,0 +1,10 @@
package github
type GithubReposConfig struct {
Repos []string `yaml:"repos"`
}
type GithubAccount struct {
Token string
Owner string
}
+57
View File
@@ -0,0 +1,57 @@
package github
import (
"context"
"encoding/json"
"log"
"github.com/google/go-github/v84/github"
"github.com/ssdomei232/goodBaby/handler/db"
"github.com/ssdomei232/goodBaby/model"
)
// 从 rule 中获取 GithubReposConfig 和 GithubAccount
func GetGithubReposAndAccountFromRule(rule *model.Rule) (*GithubReposConfig, *GithubAccount) {
var githubReposConfig GithubReposConfig
var githubAccount GithubAccount
// 获取 GithubReposConfig
err := json.Unmarshal([]byte(rule.ConfigJson), &githubReposConfig)
if err != nil {
log.Printf("获取 GitHub 配置失败: %v", err)
return nil, nil
}
// 获取 GithubAccount
gormDB, err := db.GetGormDB()
if err != nil {
log.Printf("获取 Gorm DB 失败: %v", err)
return nil, nil
}
err = gormDB.Where("id = ?", rule.AccountID).First(&githubAccount).Error
if err != nil {
log.Printf("获取 GitHub 账户失败: %v", err)
return nil, nil
}
return &githubReposConfig, &githubAccount
}
// 将仓库设置为 public
func SetRepositoryPublic(token, owner, repo string) error {
ctx := context.Background()
// 1. 初始化客户端
client := github.NewClient(nil).WithAuthToken(token)
// 2. 准备修改的参数
opts := &github.Repository{
Visibility: github.Ptr("public"),
}
// 3. 执行更新操作
_, _, err := client.Repositories.Edit(ctx, owner, repo, opts)
return err
}
+11
View File
@@ -0,0 +1,11 @@
package onebot
type OneBotConfig struct {
SendGroups []int `json:"send_groups"`
Msg string `json:"msg"`
}
type OneBotAccount struct {
URL string `json:"url"` // 例如 "http://localhost:5700"
Token string `json:"token"` // 例如 "your_token_here"
}
+42
View File
@@ -0,0 +1,42 @@
package onebot
import (
"context"
"log"
"time"
"github.com/cenkalti/backoff/v5"
"github.com/ssdomei232/goodBaby/configs"
"github.com/ssdomei232/goodBaby/model"
)
func SendOneBotMsg(rule *model.Rule) {
oneBotConfig := getOneBotConfigFromRule(rule)
oneBotAccount, err := getOneBotAccountFromRule(rule)
if err != nil {
log.Printf("获取OneBot账户失败: %v", err)
return
}
for _, groupID := range oneBotConfig.SendGroups {
config, err := configs.GetConfig()
if err != nil {
log.Printf("获取配置失败: %v", err)
return
}
var timeout time.Duration = time.Duration(config.TimeoutDurationHours) * time.Hour
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
operation := func() (string, error) {
err := sendOneBotMsg(oneBotAccount, groupID, oneBotConfig.Msg)
return "", err
}
_, err = backoff.Retry(ctx, operation, backoff.WithBackOff(backoff.NewExponentialBackOff()))
if err != nil {
log.Printf("发送OneBot消息失败: %v", err)
}
}
}
+47
View File
@@ -0,0 +1,47 @@
package onebot
import (
"encoding/json"
"strconv"
"github.com/ssdomei232/goodBaby/handler/db"
"github.com/ssdomei232/goodBaby/model"
napcat "github.com/ssdomei232/napcat-http-go-sdk"
)
// 从 Rule 中获取 OneBotConfig
func getOneBotConfigFromRule(rule *model.Rule) *OneBotConfig {
var oneBotConfig OneBotConfig
err := json.Unmarshal([]byte(rule.ConfigJson), &oneBotConfig)
if err != nil {
return nil
}
return &oneBotConfig
}
// 从 Rule 中获取 OneBotAccount
func getOneBotAccountFromRule(rule *model.Rule) (*OneBotAccount, error) {
gormDB, err := db.GetGormDB()
if err != nil {
return nil, err
}
var oneBotAccountEntry model.Account
gormDB.Where("id = ?", rule.AccountID).First(&oneBotAccountEntry)
var oneBotAccount OneBotAccount
err = json.Unmarshal([]byte(oneBotAccountEntry.Config), &oneBotAccount)
if err != nil {
return nil, err
}
return &oneBotAccount, nil
}
// 发送一条消息到OneBot
func sendOneBotMsg(account *OneBotAccount, groupID int, msg string) error {
client := napcat.NewClient(account.Token, account.URL)
err := client.SendGroupMsg(strconv.Itoa(groupID), msg)
return err
}