From 73dd6cc4eba00b186096240b6dfdd1a1ff19ce83 Mon Sep 17 00:00:00 2001 From: duorameng <2997944583@qq.com> Date: Tue, 14 Apr 2026 22:01:23 +0800 Subject: [PATCH] feat: add vocechat push #82 --- internal/sdk/message/vocechat.go | 62 +++++++++++++++++++ internal/sdk/messenger/channels/types.go | 1 + internal/sdk/messenger/channels/vocechat.go | 32 ++++++++++ internal/sdk/messenger/messenger.go | 2 + internal/services/notification_service.go | 1 + web/src/views/notify/Notify.vue | 7 +++ .../views/notify/components/ChannelDialog.vue | 7 ++- 7 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 internal/sdk/message/vocechat.go create mode 100644 internal/sdk/messenger/channels/vocechat.go diff --git a/internal/sdk/message/vocechat.go b/internal/sdk/message/vocechat.go new file mode 100644 index 0000000..1b6f4ad --- /dev/null +++ b/internal/sdk/message/vocechat.go @@ -0,0 +1,62 @@ +package message + +import ( + "bytes" + "fmt" + "io" + "net/http" + "strings" +) + +type VoceChat struct { + Server string + APIKey string + TargetType string // "user" or "group" + TargetID string +} + +func (v *VoceChat) Request(title, content string) ([]byte, error) { + if v.Server == "" || v.APIKey == "" || v.TargetID == "" { + return nil, fmt.Errorf("vocechat config missing: server, api_key and target_id are required") + } + + server := strings.TrimSuffix(v.Server, "/") + endpoint := "send_to_user" + if v.TargetType == "group" { + endpoint = "send_to_group" + } + + url := fmt.Sprintf("%s/api/bot/%s/%s", server, endpoint, v.TargetID) + + // Use text/plain for now as requested + body := content + if title != "" { + body = fmt.Sprintf("%s\n\n%s", title, content) + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(body))) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "text/plain") + req.Header.Set("x-api-key", v.APIKey) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return respBody, fmt.Errorf("vocechat response error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/internal/sdk/messenger/channels/types.go b/internal/sdk/messenger/channels/types.go index 1d67ee1..ab41464 100644 --- a/internal/sdk/messenger/channels/types.go +++ b/internal/sdk/messenger/channels/types.go @@ -73,4 +73,5 @@ const ( ChannelNtfy = "Ntfy" ChannelGotify = "Gotify" ChannelPushPlus = "PushPlus" + ChannelVoceChat = "VoceChat" ) diff --git a/internal/sdk/messenger/channels/vocechat.go b/internal/sdk/messenger/channels/vocechat.go new file mode 100644 index 0000000..d005b32 --- /dev/null +++ b/internal/sdk/messenger/channels/vocechat.go @@ -0,0 +1,32 @@ +package channels + +import "github.com/engigu/baihu-panel/internal/sdk/message" + +type VoceChatChannel struct{ *BaseChannel } + +func NewVoceChatChannel() Channel { + return &VoceChatChannel{NewBaseChannel(ChannelVoceChat, []string{FormatTypeMarkdown})} +} + +func (c *VoceChatChannel) Send(config ChannelConfig, msg *Message) (*Result, error) { + server := config.GetString("server") + apiKey := config.GetString("api_key") + targetID := config.GetString("target_id") + + if server == "" || apiKey == "" || targetID == "" { + return SendError("vocechat config missing: server, api_key and target_id are required"), nil + } + + cli := message.VoceChat{ + Server: server, + APIKey: apiKey, + TargetType: config.GetString("target_type"), // defaults to "user" in SDK if not matched differently + TargetID: targetID, + } + + res, err := cli.Request(msg.Title, msg.Text) + if err != nil { + return ErrorResult(string(res), err), nil + } + return SuccessResult(string(res)), nil +} diff --git a/internal/sdk/messenger/messenger.go b/internal/sdk/messenger/messenger.go index 46d55de..d85883a 100644 --- a/internal/sdk/messenger/messenger.go +++ b/internal/sdk/messenger/messenger.go @@ -48,6 +48,7 @@ const ( ChannelNtfy = channels.ChannelNtfy ChannelGotify = channels.ChannelGotify ChannelPushPlus = channels.ChannelPushPlus + ChannelVoceChat = channels.ChannelVoceChat ) // 重导出辅助函数 @@ -80,6 +81,7 @@ func init() { RegisterChannel(ChannelWeChatOFAccount, func() Channel { return channels.NewWeChatOFAccountChannel() }) RegisterChannel(ChannelAliyunSMS, func() Channel { return channels.NewAliyunSMSChannel() }) RegisterChannel(ChannelPushPlus, func() Channel { return channels.NewPushPlusChannel() }) + RegisterChannel(ChannelVoceChat, func() Channel { return channels.NewVoceChatChannel() }) } // RegisterChannel 注册自定义渠道(可用于扩展) diff --git a/internal/services/notification_service.go b/internal/services/notification_service.go index 55e5aa7..aeb4e71 100644 --- a/internal/services/notification_service.go +++ b/internal/services/notification_service.go @@ -55,6 +55,7 @@ var SupportedChannelTypes = []map[string]string{ // {"type": messenger.ChannelWeChatOFAccount, "label": "微信公众号"}, {"type": messenger.ChannelAliyunSMS, "label": "阿里云短信"}, {"type": messenger.ChannelPushPlus, "label": "PushPlus"}, + {"type": messenger.ChannelVoceChat, "label": "VoceChat"}, } // SupportedEvents 支持的事件类型 diff --git a/web/src/views/notify/Notify.vue b/web/src/views/notify/Notify.vue index 3b68822..ab71bf8 100644 --- a/web/src/views/notify/Notify.vue +++ b/web/src/views/notify/Notify.vue @@ -117,6 +117,13 @@ const channelConfigFields: Record
+
+ {{ field.placeholder || '' }} +