feat: bark notification #1699

This commit is contained in:
Seefs
2025-09-01 15:57:23 +08:00
parent 5cfc133413
commit 247e029159
6 changed files with 187 additions and 3 deletions

View File

@@ -535,8 +535,27 @@ func checkAndSendQuotaNotify(relayInfo *relaycommon.RelayInfo, quota int, preCon
if quotaTooLow {
prompt := "您的额度即将用尽"
topUpLink := fmt.Sprintf("%s/topup", setting.ServerAddress)
content := "{{value}},当前剩余额度为 {{value}},为了不影响您的使用,请及时充值。<br/>充值链接:<a href='{{value}}'>{{value}}</a>"
err := NotifyUser(relayInfo.UserId, relayInfo.UserEmail, relayInfo.UserSetting, dto.NewNotify(dto.NotifyTypeQuotaExceed, prompt, content, []interface{}{prompt, logger.FormatQuota(relayInfo.UserQuota), topUpLink, topUpLink}))
// 根据通知方式生成不同的内容格式
var content string
var values []interface{}
notifyType := userSetting.NotifyType
if notifyType == "" {
notifyType = dto.NotifyTypeEmail
}
if notifyType == dto.NotifyTypeBark {
// Bark推送使用简短文本不支持HTML
content = "{{value}},剩余额度:{{value}},请及时充值"
values = []interface{}{prompt, logger.FormatQuota(relayInfo.UserQuota)}
} else {
// 默认内容格式适用于Email和Webhook
content = "{{value}},当前剩余额度为 {{value}},为了不影响您的使用,请及时充值。<br/>充值链接:<a href='{{value}}'>{{value}}</a>"
values = []interface{}{prompt, logger.FormatQuota(relayInfo.UserQuota), topUpLink, topUpLink}
}
err := NotifyUser(relayInfo.UserId, relayInfo.UserEmail, relayInfo.UserSetting, dto.NewNotify(dto.NotifyTypeQuotaExceed, prompt, content, values))
if err != nil {
common.SysError(fmt.Sprintf("failed to send quota notify to user %d: %s", relayInfo.UserId, err.Error()))
}

View File

@@ -2,9 +2,12 @@ package service
import (
"fmt"
"net/http"
"net/url"
"one-api/common"
"one-api/dto"
"one-api/model"
"one-api/setting"
"strings"
)
@@ -51,6 +54,13 @@ func NotifyUser(userId int, userEmail string, userSetting dto.UserSetting, data
// 获取 webhook secret
webhookSecret := userSetting.WebhookSecret
return SendWebhookNotify(webhookURLStr, webhookSecret, data)
case dto.NotifyTypeBark:
barkURL := userSetting.BarkUrl
if barkURL == "" {
common.SysLog(fmt.Sprintf("user %d has no bark url, skip sending bark", userId))
return nil
}
return sendBarkNotify(barkURL, data)
}
return nil
}
@@ -64,3 +74,67 @@ func sendEmailNotify(userEmail string, data dto.Notify) error {
}
return common.SendEmail(data.Title, userEmail, content)
}
func sendBarkNotify(barkURL string, data dto.Notify) error {
// 处理占位符
content := data.Content
for _, value := range data.Values {
content = strings.Replace(content, dto.ContentValueParam, fmt.Sprintf("%v", value), 1)
}
// 替换模板变量
finalURL := strings.ReplaceAll(barkURL, "{{title}}", url.QueryEscape(data.Title))
finalURL = strings.ReplaceAll(finalURL, "{{content}}", url.QueryEscape(content))
// 发送GET请求到Bark
var req *http.Request
var resp *http.Response
var err error
if setting.EnableWorker() {
// 使用worker发送请求
workerReq := &WorkerRequest{
URL: finalURL,
Key: setting.WorkerValidKey,
Method: http.MethodGet,
Headers: map[string]string{
"User-Agent": "OneAPI-Bark-Notify/1.0",
},
}
resp, err = DoWorkerRequest(workerReq)
if err != nil {
return fmt.Errorf("failed to send bark request through worker: %v", err)
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("bark request failed with status code: %d", resp.StatusCode)
}
} else {
// 直接发送请求
req, err = http.NewRequest(http.MethodGet, finalURL, nil)
if err != nil {
return fmt.Errorf("failed to create bark request: %v", err)
}
// 设置User-Agent
req.Header.Set("User-Agent", "OneAPI-Bark-Notify/1.0")
// 发送请求
client := GetHttpClient()
resp, err = client.Do(req)
if err != nil {
return fmt.Errorf("failed to send bark request: %v", err)
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("bark request failed with status code: %d", resp.StatusCode)
}
}
return nil
}