feat(gateway): 优化gateway相关功能
Quality check / Web UI (push) Successful in 9m19s

This commit is contained in:
mei
2026-09-12 15:32:27 +08:00
parent 0d89b7fe8f
commit 9b656cebee
84 changed files with 2769 additions and 825 deletions
+2 -11
View File
@@ -1,7 +1,6 @@
package bilibili
import (
"encoding/json"
"fmt"
"github.com/ssdomei232/goodBaby/internal/meta"
@@ -15,7 +14,7 @@ func (v *BilibiliAccountConfigValidator) GetType() string {
}
func (v *BilibiliAccountConfigValidator) Validate(config string) error {
cfg, err := parseAccount(config)
cfg, err := ParseAccountConfig(config)
if err != nil {
return err
}
@@ -29,7 +28,7 @@ func (v *BilibiliAccountConfigValidator) Validate(config string) error {
// Test 用 cookie 拉一次账号信息,验证 cookie 是否还有效
func (v *BilibiliAccountConfigValidator) Test(config string) error {
cfg, err := parseAccount(config)
cfg, err := ParseAccountConfig(config)
if err != nil {
return err
}
@@ -64,11 +63,3 @@ func (v *BilibiliAccountConfigValidator) Meta() meta.AccountMeta {
},
}
}
func parseAccount(config string) (*BiliAccount, error) {
var cfg BiliAccount
if err := json.Unmarshal([]byte(config), &cfg); err != nil {
return nil, fmt.Errorf("解析B站账号配置失败: %v", err)
}
return &cfg, nil
}
+6 -16
View File
@@ -6,32 +6,22 @@ import (
"github.com/CuteReimu/bilibili/v2"
"github.com/ssdomei232/goodBaby/internal/retry"
"github.com/ssdomei232/goodBaby/model"
)
// SendBiliDynamicMsg 在 Bilibili 动态发送消息
// SendDynamic 用给定账号发送一条 B 站动态
//
// 暂时没有处理 429 和 403 的区别对待
func SendBiliDynamicMsg(ctx context.Context, rule *model.Rule) error {
biliClient, err := getBiliClient(rule)
if err != nil {
return fmt.Errorf("获取B站客户端失败: %w", err)
}
biliDynamicConfig, err := getBiliDynamicConfig(rule)
if err != nil {
return fmt.Errorf("获取B站动态配置失败: %w", err)
}
dynamicParams := bilibili.CreateDynamicParam{
func SendDynamic(ctx context.Context, account *BiliAccount, config *BiliDynamicConfig) error {
client := newClient(account.RawCookies)
params := bilibili.CreateDynamicParam{
DynamicId: 0,
Type: 4,
Rid: 0,
Content: biliDynamicConfig.Msg,
Content: config.Msg,
}
if err := retry.Do(ctx, func() error {
_, err := biliClient.CreateDynamic(dynamicParams)
_, err := client.CreateDynamic(params)
return err
}); err != nil {
return fmt.Errorf("发送B站动态失败: %w", err)
+38 -44
View File
@@ -2,54 +2,34 @@ package bilibili
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/CuteReimu/bilibili/v2"
"github.com/ssdomei232/goodBaby/internal/retry"
"github.com/ssdomei232/goodBaby/model"
)
// 私信消息类型:1 为文字
// privateMsgTypeText 私信消息类型:1 为文字
const privateMsgTypeText = 1
// SendBiliPrivateMessage 向配置中的每个 UID 发送一条 B 站私信
func SendBiliPrivateMessage(ctx context.Context, rule *model.Rule) error {
client, err := getBiliClient(rule)
if err != nil {
return fmt.Errorf("获取B站客户端失败: %w", err)
}
// SendPrivateMessage 用给定账号向配置中的每个 UID 发送一条 B 站私信
//
// 每个接收者单独重试、互不阻塞,最后汇总失败信息交给上层记录。
func SendPrivateMessage(ctx context.Context, account *BiliAccount, config *BiliPrivateMessageConfig) error {
client := newClient(account.RawCookies)
config, err := getPrivateMessageConfig(rule)
// 发私信需要带上自己的 UID,从账号信息里取
selfUID, err := fetchSelfUID(ctx, client)
if err != nil {
return err
}
// 发私信需要带上自己的 UID,从账号信息里取
account, err := client.GetAccountInformation()
if err != nil {
return fmt.Errorf("获取B站账号信息失败(cookie 可能已失效): %w", err)
}
if account == nil || account.Mid == 0 {
return fmt.Errorf("B站 cookie 无效或已过期")
}
var fails []string
for _, uid := range config.ReceiverUids {
err := retry.Do(ctx, func() error {
_, err := client.SendPrivateMessage(bilibili.SendPrivateMessageParam{
SenderUid: account.Mid,
ReceiverId: int(uid),
ReceiverType: 1,
MsgType: privateMsgTypeText,
Timestamp: int(time.Now().Unix()),
Content: buildTextContent(config.Msg),
})
return err
})
if err != nil {
if err := retry.Do(ctx, func() error {
return sendPrivateMessage(client, selfUID, uid, config.Msg)
}); err != nil {
fails = append(fails, fmt.Sprintf("UID %d: %v", uid, err))
}
}
@@ -61,20 +41,34 @@ func SendBiliPrivateMessage(ctx context.Context, rule *model.Rule) error {
return nil
}
// buildTextContent 文字私信的 content 是一个 JSON 字符串 {"content":"..."}
func buildTextContent(msg string) string {
payload, err := json.Marshal(map[string]string{"content": msg})
if err != nil {
// msg 是普通字符串,序列化不会失败;兜底也返回合法 JSON
return `{"content":""}`
// fetchSelfUID 取当前账号的 UID,私信接口需要它
func fetchSelfUID(ctx context.Context, client *bilibili.Client) (int, error) {
var selfUID int
if err := retry.Do(ctx, func() error {
info, err := client.GetAccountInformation()
if err != nil {
return err
}
if info == nil || info.Mid == 0 {
return retry.Permanent(fmt.Errorf("B站 cookie 无效或已过期"))
}
selfUID = info.Mid
return nil
}); err != nil {
return 0, fmt.Errorf("获取B站账号信息失败: %w", err)
}
return string(payload)
return selfUID, nil
}
func getPrivateMessageConfig(rule *model.Rule) (*BiliPrivateMessageConfig, error) {
var config BiliPrivateMessageConfig
if err := json.Unmarshal([]byte(rule.ConfigJson), &config); err != nil {
return nil, fmt.Errorf("解析B站私信规则配置失败: %w", err)
}
return &config, nil
// sendPrivateMessage 发送一条私信
func sendPrivateMessage(client *bilibili.Client, senderUID int, receiverUID int64, msg string) error {
_, err := client.SendPrivateMessage(bilibili.SendPrivateMessageParam{
SenderUid: senderUID,
ReceiverId: int(receiverUID),
ReceiverType: 1,
MsgType: privateMsgTypeText,
Timestamp: int(time.Now().Unix()),
Content: buildTextContent(msg),
})
return err
}
+13 -13
View File
@@ -1,7 +1,6 @@
package bilibili
import (
"encoding/json"
"fmt"
"github.com/ssdomei232/goodBaby/internal/meta"
@@ -15,9 +14,9 @@ func (v *BilibiliDynamicRuleValidator) GetType() string {
}
func (v *BilibiliDynamicRuleValidator) Validate(configJSON string) error {
var config BiliDynamicConfig
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
return fmt.Errorf("解析B站动态规则配置失败: %v", err)
config, err := ParseDynamicConfig(configJSON)
if err != nil {
return err
}
if config.Msg == "" {
@@ -39,9 +38,9 @@ func (v *BilibiliPrivateMessageRuleValidator) GetType() string {
}
func (v *BilibiliPrivateMessageRuleValidator) Validate(configJSON string) error {
var config BiliPrivateMessageConfig
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
return fmt.Errorf("解析B站私信规则配置失败: %v", err)
config, err := ParsePrivateMessageConfig(configJSON)
if err != nil {
return err
}
if config.Msg == "" {
@@ -77,7 +76,7 @@ func (v *BilibiliPrivateMessageRuleValidator) Meta() meta.RuleMeta {
Placeholder: "2",
Help: "对方空间地址 space.bilibili.com/ 后面的那串数字",
},
{Key: "msg", Label: "私信内容", Type: meta.FieldTextarea, Required: true},
{Key: "msg", Label: "私信内容", Type: meta.FieldTextarea, Required: true, GatewayMessage: true},
},
}
}
@@ -91,11 +90,12 @@ func (v *BilibiliDynamicRuleValidator) Meta() meta.RuleMeta {
AccountType: AccountType,
Fields: []meta.Field{
{
Key: "msg",
Label: "动态内容",
Type: meta.FieldTextarea,
Required: true,
Placeholder: "要发送的动态正文",
Key: "msg",
Label: "动态内容",
Type: meta.FieldTextarea,
Required: true,
GatewayMessage: true,
Placeholder: "要发送的动态正文",
},
},
}
+22 -7
View File
@@ -2,7 +2,6 @@ package bilibili
import (
"context"
"log"
"github.com/ssdomei232/goodBaby/model"
)
@@ -14,10 +13,18 @@ func (e *BilibiliDynamicExecutor) GetType() string {
return RuleTypeDynamic
}
func (e *BilibiliDynamicExecutor) Execute(ctx context.Context, rule *model.Rule) error {
log.Printf("执行B站动态规则: %s (ID: %d)", rule.Name, rule.ID)
func (e *BilibiliDynamicExecutor) Execute(ctx context.Context, task *model.RuleTask) error {
account, err := ParseAccountConfig(task.AccountConfig())
if err != nil {
return err
}
return SendBiliDynamicMsg(ctx, rule)
config, err := ParseDynamicConfig(task.Rule.ConfigJson)
if err != nil {
return err
}
return SendDynamic(ctx, account, config)
}
// BilibiliPrivateMessageExecutor B站私信执行器
@@ -27,8 +34,16 @@ func (e *BilibiliPrivateMessageExecutor) GetType() string {
return RuleTypePrivateMessage
}
func (e *BilibiliPrivateMessageExecutor) Execute(ctx context.Context, rule *model.Rule) error {
log.Printf("执行B站私信规则: %s (ID: %d)", rule.Name, rule.ID)
func (e *BilibiliPrivateMessageExecutor) Execute(ctx context.Context, task *model.RuleTask) error {
account, err := ParseAccountConfig(task.AccountConfig())
if err != nil {
return err
}
return SendBiliPrivateMessage(ctx, rule)
config, err := ParsePrivateMessageConfig(task.Rule.ConfigJson)
if err != nil {
return err
}
return SendPrivateMessage(ctx, account, config)
}
+40 -19
View File
@@ -2,34 +2,55 @@ package bilibili
import (
"encoding/json"
"fmt"
"github.com/CuteReimu/bilibili/v2"
"github.com/ssdomei232/goodBaby/handler/db"
"github.com/ssdomei232/goodBaby/model"
)
// ParseAccountConfig 解析 B 站账号配置
func ParseAccountConfig(config string) (*BiliAccount, error) {
if config == "" {
return nil, fmt.Errorf("解析B站账号配置失败: 规则没有关联账号")
}
var account BiliAccount
if err := json.Unmarshal([]byte(config), &account); err != nil {
return nil, fmt.Errorf("解析B站账号配置失败: %v", err)
}
return &account, nil
}
// ParseDynamicConfig 解析 B 站动态规则配置
func ParseDynamicConfig(configJSON string) (*BiliDynamicConfig, error) {
var config BiliDynamicConfig
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
return nil, fmt.Errorf("解析B站动态规则配置失败: %v", err)
}
return &config, nil
}
// ParsePrivateMessageConfig 解析 B 站私信规则配置
func ParsePrivateMessageConfig(configJSON string) (*BiliPrivateMessageConfig, error) {
var config BiliPrivateMessageConfig
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
return nil, fmt.Errorf("解析B站私信规则配置失败: %v", err)
}
return &config, nil
}
// newClient 按账号配置创建 B 站客户端
func newClient(rawCookies string) *bilibili.Client {
client := bilibili.New()
client.SetRawCookies(rawCookies)
return client
}
// 获取 Bilibili Client
func getBiliClient(rule *model.Rule) (*bilibili.Client, error) {
var accountConfig BiliAccount
if err := db.LoadAccountConfig(rule.AccountID, &accountConfig); err != nil {
return nil, err
// buildTextContent 文字私信的 content 是一个 JSON 字符串 {"content":"..."}
func buildTextContent(msg string) string {
payload, err := json.Marshal(map[string]string{"content": msg})
if err != nil {
// msg 是普通字符串,序列化不会失败;兜底也返回合法 JSON
return `{"content":""}`
}
return newClient(accountConfig.RawCookies), nil
}
// 获取 Bilibili Dynamic Config
func getBiliDynamicConfig(rule *model.Rule) (*BiliDynamicConfig, error) {
var biliDynamicConfig BiliDynamicConfig
if err := json.Unmarshal([]byte(rule.ConfigJson), &biliDynamicConfig); err != nil {
return nil, err
}
return &biliDynamicConfig, nil
return string(payload)
}