From e6864373bc49be6abf22dc3060218cd6571ea1ca Mon Sep 17 00:00:00 2001 From: duorameng <2997944583@qq.com> Date: Wed, 15 Apr 2026 17:09:45 +0800 Subject: [PATCH] chore: udpate bark push #83 --- internal/sdk/message/bark.go | 162 ++++++++++++++++++------ internal/sdk/messenger/channels/bark.go | 6 +- web/src/views/notify/Notify.vue | 4 + 3 files changed, 131 insertions(+), 41 deletions(-) diff --git a/internal/sdk/message/bark.go b/internal/sdk/message/bark.go index 3f835c2..3817d0c 100644 --- a/internal/sdk/message/bark.go +++ b/internal/sdk/message/bark.go @@ -6,10 +6,16 @@ import ( "crypto/cipher" "encoding/base64" "encoding/json" + "errors" "fmt" "io" + "net" "net/http" + "net/url" "strings" + "time" + + "golang.org/x/net/proxy" ) type barkResponse struct { @@ -18,22 +24,27 @@ type barkResponse struct { } type Bark struct { - PushKey string - Archive string - Group string - Sound string - Icon string - Level string - URL string - Key string - IV string - Server string + PushKey string + Archive string + Group string + Sound string + Icon string + Level string + URL string + Key string + IV string + Server string + Badge string + Copy string + AutoCopy string + ProxyURL string // 可选的代理地址 } func (b *Bark) Request(title, content string) ([]byte, error) { data := map[string]interface{}{ - "title": title, - "body": content, + "device_key": b.PushKey, + "title": title, + "body": content, } if b.Archive != "" { data["isArchive"] = b.Archive @@ -53,32 +64,53 @@ func (b *Bark) Request(title, content string) ([]byte, error) { if b.URL != "" { data["url"] = b.URL } + if b.Badge != "" { + data["badge"] = b.Badge + } + if b.Copy != "" { + data["copy"] = b.Copy + } + if b.AutoCopy != "" { + data["autoCopy"] = b.AutoCopy + } + + server := b.Server + if server == "" { + server = "https://api.day.app" + } + server = strings.TrimSuffix(server, "/") + apiURL := server + "/push" + + // If PushKey is a full URL, we might be using an old-style custom URL + if strings.HasPrefix(b.PushKey, "http") { + apiURL = b.PushKey + } var postData interface{} - url := b.getURL() - if b.Key != "" && b.IV != "" { - // Use encryption - jsonData, err := json.Marshal(data) + // Encrypted Request + // 1. Prepare the full notification payload (without device_key, as specified for encryption) + encryptData := make(map[string]interface{}) + for k, v := range data { + if k != "device_key" { + encryptData[k] = v + } + } + + jsonData, err := json.Marshal(encryptData) if err != nil { return nil, err } + ciphertext, err := b.encryptPayload(string(jsonData)) if err != nil { return nil, fmt.Errorf("encryption failed: %v", err) } + postData = map[string]interface{}{ "ciphertext": ciphertext, + "iv": b.IV, "device_key": b.PushKey, - "sound": b.Sound, - } - // When using encryption, use the push endpoint if PushKey is just a key - if !strings.HasPrefix(b.PushKey, "http") { - server := b.Server - if server == "" { - server = "https://api.day.app" - } - url = strings.TrimSuffix(server, "/") + "/push" } } else { // Normal request @@ -90,7 +122,9 @@ func (b *Bark) Request(title, content string) ([]byte, error) { return nil, err } - resp, err := http.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(jsonData)) + // 使用带超时的客户端 + client := b.getHTTPClient() + resp, err := client.Post(apiURL, "application/json;charset=utf-8", bytes.NewBuffer(jsonData)) if err != nil { return nil, err } @@ -104,28 +138,19 @@ func (b *Bark) Request(title, content string) ([]byte, error) { var r barkResponse err = json.Unmarshal(body, &r) if err != nil { + // If not JSON, return the raw body as it might be a simple success message from some servers + if resp.StatusCode == 200 { + return body, nil + } return body, err } - if r.Code != 200 { + if r.Code != 200 && resp.StatusCode != 200 { return body, fmt.Errorf("bark response error: %s", string(body)) } return body, nil } -func (b *Bark) getURL() string { - pushKey := b.PushKey - if strings.HasPrefix(pushKey, "http") { - return pushKey - } - server := b.Server - if server == "" { - server = "https://api.day.app" - } - server = strings.TrimSuffix(server, "/") - return fmt.Sprintf("%s/%s", server, pushKey) -} - func (b *Bark) encryptPayload(payload string) (string, error) { key := []byte(b.Key) iv := []byte(b.IV) @@ -148,3 +173,60 @@ func (b *Bark) pkcs7Pad(data []byte, blockSize int) []byte { padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padtext...) } + +// getHTTPClient 获取 HTTP 客户端(含超时和代理) +func (b *Bark) getHTTPClient() *http.Client { + client := &http.Client{ + Timeout: 20 * time.Second, + } + + if b.ProxyURL != "" { + proxyURL, err := url.Parse(b.ProxyURL) + if err == nil { + if strings.HasPrefix(strings.ToLower(b.ProxyURL), "socks5://") { + dialer, err := b.createSOCKS5Dialer(proxyURL) + if err == nil { + client.Transport = &http.Transport{ + DialContext: dialer.DialContext, + } + } + } else { + client.Transport = &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + } + } + } + } + + return client +} + +// createSOCKS5Dialer 创建 SOCKS5 拨号器 +func (b *Bark) createSOCKS5Dialer(proxyURL *url.URL) (proxy.ContextDialer, error) { + host := proxyURL.Host + var auth *proxy.Auth + if proxyURL.User != nil { + password, _ := proxyURL.User.Password() + auth = &proxy.Auth{ + User: proxyURL.User.Username(), + Password: password, + } + } + + baseDialer := &net.Dialer{ + Timeout: 20 * time.Second, + KeepAlive: 20 * time.Second, + } + + dialer, err := proxy.SOCKS5("tcp", host, auth, baseDialer) + if err != nil { + return nil, err + } + + contextDialer, ok := dialer.(proxy.ContextDialer) + if !ok { + return nil, errors.New("failed to convert to ContextDialer") + } + + return contextDialer, nil +} diff --git a/internal/sdk/messenger/channels/bark.go b/internal/sdk/messenger/channels/bark.go index e5c9100..b1d1f50 100644 --- a/internal/sdk/messenger/channels/bark.go +++ b/internal/sdk/messenger/channels/bark.go @@ -24,7 +24,11 @@ func (c *BarkChannel) Send(config ChannelConfig, msg *Message) (*Result, error) URL: config.GetString("url"), Key: config.GetString("key"), IV: config.GetString("iv"), - Server: config.GetString("server"), + Server: config.GetString("server"), + Badge: config.GetString("badge"), + Copy: config.GetString("copy"), + AutoCopy: config.GetString("auto_copy"), + ProxyURL: config.GetString("proxy_url"), } res, err := cli.Request(msg.Title, msg.Text) diff --git a/web/src/views/notify/Notify.vue b/web/src/views/notify/Notify.vue index ab71bf8..1909298 100644 --- a/web/src/views/notify/Notify.vue +++ b/web/src/views/notify/Notify.vue @@ -51,11 +51,15 @@ const channelConfigFields: Record