Files
Message-Push-Nest/service/send_way_service/send_way.go
T

502 lines
16 KiB
Go
Raw Normal View History

2023-12-30 17:40:20 +08:00
package send_way_service
import (
"encoding/json"
"errors"
"fmt"
"message-nest/models"
"message-nest/pkg/app"
2025-12-17 21:51:21 +08:00
"message-nest/pkg/constant"
2023-12-30 17:40:20 +08:00
"message-nest/pkg/message"
"strings"
)
type SendWay struct {
ID string
Name string
Type string
CreatedBy string
ModifiedBy string
Auth string
CreatedOn string
PageNum int
PageSize int
}
2025-12-17 21:51:21 +08:00
// WayValidator 渠道验证接口
type WayValidator interface {
Validate(authJson string) (string, interface{})
}
// WayTester 渠道测试接口
type WayTester interface {
Test() (string, string)
}
// 渠道注册表
var (
validatorRegistry = map[string]func() WayValidator{
constant.MessageTypeEmail: func() WayValidator { return &WayDetailEmail{} },
constant.MessageTypeDtalk: func() WayValidator { return &WayDetailDTalk{} },
constant.MessageTypeQyWeiXin: func() WayValidator { return &WayDetailQyWeiXin{} },
constant.MessageTypeWeChatCorpAccount: func() WayValidator { return &WayDetailWeChatCorpAccount{} },
constant.MessageTypeFeishu: func() WayValidator { return &WayDetailFeishu{} },
constant.MessageTypeCustom: func() WayValidator { return &WayDetailCustom{} },
constant.MessageTypeWeChatOFAccount: func() WayValidator { return &WeChatOFAccount{} },
constant.MessageTypeMessageNest: func() WayValidator { return &MessageNest{} },
constant.MessageTypeAliyunSMS: func() WayValidator { return &WayDetailAliyunSMS{} },
constant.MessageTypeTelegram: func() WayValidator { return &WayDetailTelegram{} },
constant.MessageTypeBark: func() WayValidator { return &WayDetailBark{} },
constant.MessageTypePushMe: func() WayValidator { return &WayDetailPushMe{} },
2025-12-17 21:51:21 +08:00
}
testerRegistry = map[string]func(interface{}) WayTester{
constant.MessageTypeEmail: func(m interface{}) WayTester { return m.(*WayDetailEmail) },
constant.MessageTypeDtalk: func(m interface{}) WayTester { return m.(*WayDetailDTalk) },
constant.MessageTypeQyWeiXin: func(m interface{}) WayTester { return m.(*WayDetailQyWeiXin) },
constant.MessageTypeWeChatCorpAccount: func(m interface{}) WayTester { return m.(*WayDetailWeChatCorpAccount) },
constant.MessageTypeFeishu: func(m interface{}) WayTester { return m.(*WayDetailFeishu) },
constant.MessageTypeCustom: func(m interface{}) WayTester { return m.(*WayDetailCustom) },
constant.MessageTypeWeChatOFAccount: func(m interface{}) WayTester { return m.(*WeChatOFAccount) },
constant.MessageTypeMessageNest: func(m interface{}) WayTester { return m.(*MessageNest) },
constant.MessageTypeAliyunSMS: func(m interface{}) WayTester { return m.(*WayDetailAliyunSMS) },
constant.MessageTypeTelegram: func(m interface{}) WayTester { return m.(*WayDetailTelegram) },
constant.MessageTypeBark: func(m interface{}) WayTester { return m.(*WayDetailBark) },
constant.MessageTypePushMe: func(m interface{}) WayTester { return m.(*WayDetailPushMe) },
2025-12-17 21:51:21 +08:00
}
)
2023-12-30 17:40:20 +08:00
// WayDetailEmail 邮箱渠道明细字段
type WayDetailEmail struct {
Server string `validate:"required,max=50" label:"SMTP服务地址"`
Port int `validate:"required,max=65535" label:"SMTP服务端口"`
Account string `validate:"required,email" label:"邮箱账号"`
Passwd string `validate:"required,max=50" label:"邮箱密码"`
}
2025-12-17 21:51:21 +08:00
func (w *WayDetailEmail) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "邮箱auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailEmail) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var emailer message.EmailMessage
emailer.Init(w.Server, 465, w.Account, w.Passwd)
errMsg := emailer.SendTextMessage(w.Account, "test email", testMsg)
return errMsg, ""
}
2023-12-30 17:40:20 +08:00
// WayDetailDTalk 钉钉渠道明细字段
type WayDetailDTalk struct {
2024-01-06 17:44:45 +08:00
AccessToken string `json:"access_token" validate:"required,max=100" label:"钉钉access_token"`
Keys string `json:"keys" validate:"max=200" label:"钉钉关键字"`
Secret string `json:"secret" validate:"max=100" label:"钉钉加签秘钥"`
2023-12-30 17:40:20 +08:00
}
2025-12-17 21:51:21 +08:00
func (w *WayDetailDTalk) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "钉钉auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailDTalk) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.Dtalk{
AccessToken: w.AccessToken,
Secret: w.Secret,
}
res, err := cli.SendMessageText(testMsg + w.Keys)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), string(res)
}
return "", string(res)
}
2024-01-14 20:39:55 +08:00
// WayDetailQyWeiXin 企业微信渠道明细字段
type WayDetailQyWeiXin struct {
AccessToken string `json:"access_token" validate:"required,max=100" label:"企业微信access_token"`
}
2025-12-17 21:51:21 +08:00
func (w *WayDetailQyWeiXin) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "企业微信auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailQyWeiXin) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.QyWeiXin{
AccessToken: w.AccessToken,
}
res, err := cli.SendMessageText(testMsg)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), string(res)
}
return "", string(res)
}
type WayDetailWeChatCorpAccount struct {
CorpID string `json:"corp_id" validate:"required,max=100" label:"企业微信CorpID"`
AgentID int `json:"agent_id" validate:"required,min=1" label:"企业微信AgentID"`
AgentSecret string `json:"agent_secret" validate:"required,max=200" label:"企业微信AgentSecret"`
ProxyURL string `json:"proxy_url" validate:"max=200" label:"代理地址"`
}
func (w *WayDetailWeChatCorpAccount) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "企业微信应用auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
return msg, w
}
func (w *WayDetailWeChatCorpAccount) Test() (string, string) {
cli := message.WeChatCorpAccount{
CorpID: w.CorpID,
AgentID: w.AgentID,
AgentSecret: w.AgentSecret,
ProxyURL: w.ProxyURL,
}
_, err := cli.GetAccessToken()
if err != nil {
return fmt.Sprintf("连接失败:%s", err.Error()), ""
}
return "", "access_token获取成功"
}
2025-12-17 21:51:21 +08:00
// WayDetailFeishu 飞书渠道明细字段
type WayDetailFeishu struct {
AccessToken string `json:"access_token" validate:"required,max=100" label:"飞书access_token"`
Keys string `json:"keys" validate:"max=200" label:"飞书关键字"`
Secret string `json:"secret" validate:"max=100" label:"飞书加签秘钥"`
}
func (w *WayDetailFeishu) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "飞书auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailFeishu) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.Feishu{
AccessToken: w.AccessToken,
Secret: w.Secret,
}
res, err := cli.SendMessageText(testMsg + w.Keys)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), string(res)
}
return "", string(res)
}
2024-01-07 00:25:35 +08:00
// WayDetailCustom 自定义渠道
type WayDetailCustom struct {
Webhook string `json:"webhook" validate:"required,max=200" label:"自定义的webhook地址"`
Body string `json:"body" validate:"max=2000" label:"自定义的请求体"`
}
2025-12-17 21:51:21 +08:00
func (w *WayDetailCustom) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "自定义参数反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailCustom) Test() (string, string) {
return "自定义webhook不用测试运行,请直接添加", ""
}
2024-03-05 14:42:12 +08:00
// WeChatOFAccount 微信公众号
type WeChatOFAccount struct {
AppID string `json:"appID" validate:"required,max=200" label:"微信公众号id"`
APPSecret string `json:"appsecret" validate:"max=2000" label:"微信公众号秘钥"`
TempID string `json:"tempid" validate:"max=2000" label:"模板消息id"`
}
2025-12-17 21:51:21 +08:00
func (w *WeChatOFAccount) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "微信公众号反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WeChatOFAccount) Test() (string, string) {
return "微信公众号模板消息不用测试运行,请直接添加", ""
}
2025-12-14 12:43:09 +08:00
// WayDetailAliyunSMS 阿里云短信渠道明细字段
type WayDetailAliyunSMS struct {
AccessKeyId string `json:"access_key_id" validate:"required,max=100" label:"AccessKeyId"`
AccessKeySecret string `json:"access_key_secret" validate:"required,max=100" label:"AccessKeySecret"`
SignName string `json:"sign_name" validate:"required,max=50" label:"短信签名"`
2026-01-23 11:31:21 +08:00
RegionId string `json:"region_id" validate:"required,max=50" label:"区域ID"`
2025-12-14 12:43:09 +08:00
}
2025-12-17 21:51:21 +08:00
func (w *WayDetailAliyunSMS) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "阿里云短信auth反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *WayDetailAliyunSMS) Test() (string, string) {
return "阿里云短信不用测试运行,请直接添加", ""
}
2025-01-01 17:30:02 +08:00
// MessageNest 自托管消息
type MessageNest struct {
}
2025-12-17 21:51:21 +08:00
func (w *MessageNest) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "自托管消息反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
2026-01-06 21:14:35 +08:00
return msg, w
2025-12-17 21:51:21 +08:00
}
func (w *MessageNest) Test() (string, string) {
return "自托管消息不用测试运行,请直接添加", ""
}
2026-01-14 20:51:58 +08:00
// WayDetailTelegram Telegram机器人渠道明细字段
type WayDetailTelegram struct {
BotToken string `json:"bot_token" validate:"required,max=100" label:"Telegram Bot Token"`
ChatID string `json:"chat_id" validate:"required,max=50" label:"Chat ID"`
ApiHost string `json:"api_host" validate:"max=200" label:"自定义API地址"`
ProxyURL string `json:"proxy_url" validate:"max=200" label:"代理地址"`
}
func (w *WayDetailTelegram) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "Telegram参数反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
return msg, w
}
func (w *WayDetailTelegram) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.Telegram{
BotToken: w.BotToken,
ChatID: w.ChatID,
ApiHost: w.ApiHost,
ProxyURL: w.ProxyURL,
}
res, err := cli.SendMessageText(testMsg)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), string(res)
}
return "", string(res)
}
2026-01-21 19:26:48 +08:00
// WayDetailBark Bark渠道明细字段
type WayDetailBark struct {
PushKey string `json:"push_key" validate:"required,max=200" label:"Bark Push Key"`
Archive string `json:"archive" validate:"max=10" label:"推送是否存档"`
Group string `json:"group" validate:"max=50" label:"推送分组"`
Sound string `json:"sound" validate:"max=50" label:"推送声音"`
Icon string `json:"icon" validate:"max=200" label:"推送图标"`
Level string `json:"level" validate:"max=20" label:"推送时效性"`
URL string `json:"url" validate:"max=200" label:"推送跳转URL"`
2026-01-22 19:16:24 +08:00
Key string `json:"key" validate:"max=100" label:"加密Key"`
IV string `json:"iv" validate:"max=100" label:"加密IV"`
2026-01-21 19:26:48 +08:00
}
func (w *WayDetailBark) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "Bark参数反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
return msg, w
}
func (w *WayDetailBark) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.Bark{
PushKey: w.PushKey,
Archive: w.Archive,
Group: w.Group,
Sound: w.Sound,
Icon: w.Icon,
Level: w.Level,
URL: w.URL,
2026-01-22 19:16:24 +08:00
Key: w.Key,
IV: w.IV,
2026-01-21 19:26:48 +08:00
}
res, err := cli.Request("Test Message", testMsg)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), string(res)
}
return "", string(res)
}
2026-02-04 19:30:34 +08:00
// WayDetailPushMe PushMe渠道明细字段
type WayDetailPushMe struct {
PushKey string `json:"push_key" validate:"required,max=100" label:"PushMe Push Key"`
URL string `json:"url" validate:"max=200" label:"自定义API地址"`
Date string `json:"date" validate:"max=50" label:"日期"`
Type string `json:"type" validate:"max=50" label:"类型"`
}
func (w *WayDetailPushMe) Validate(authJson string) (string, interface{}) {
var empty interface{}
err := json.Unmarshal([]byte(authJson), w)
if err != nil {
return "PushMe参数反序列化失败!", empty
}
_, msg := app.CommonPlaygroundValid(*w)
return msg, w
}
func (w *WayDetailPushMe) Test() (string, string) {
testMsg := "This is a test message from message-nest."
var cli = message.PushMe{
PushKey: w.PushKey,
URL: w.URL,
Date: w.Date,
Type: w.Type,
}
res, err := cli.Request("Test Message", testMsg)
if err != nil {
return fmt.Sprintf("发送失败:%s", err), res
}
return "", res
}
2023-12-30 17:40:20 +08:00
func (sw *SendWay) GetByID() (interface{}, error) {
return models.GetWayByID(sw.ID)
}
2024-01-21 12:35:33 +08:00
func (sw *SendWay) NameIsExist(method string) error {
2024-01-21 00:54:02 +08:00
way, err := models.GetWayByName(sw.Name)
if err != nil {
return err
}
2024-01-21 12:35:33 +08:00
if method == "add" {
if len(way.ID) > 0 {
return errors.New(fmt.Sprintf("已经存在同名的渠道名:%s", way.Name))
}
} else {
// 只允许当前的重名
if len(way.ID) > 0 && way.ID != sw.ID {
return errors.New(fmt.Sprintf("已经存在同名的渠道名:%s", way.Name))
}
}
return nil
}
func (sw *SendWay) Add() error {
err := sw.NameIsExist("add")
if err != nil {
return err
2024-01-21 00:54:02 +08:00
}
2023-12-30 17:40:20 +08:00
return models.AddSendWay(sw.Name, sw.Auth, sw.Type, sw.CreatedBy, sw.ModifiedBy)
}
func (sw *SendWay) Edit() error {
2024-01-21 12:35:33 +08:00
err := sw.NameIsExist("edit")
if err != nil {
return err
}
2023-12-30 17:40:20 +08:00
data := make(map[string]interface{})
data["modified_by"] = sw.ModifiedBy
data["name"] = sw.Name
data["auth"] = sw.Auth
return models.EditSendWay(sw.ID, data)
}
func (sw *SendWay) Delete() error {
tasks := models.FindTaskByWayId(sw.ID)
if len(tasks) > 0 {
var names []string
for _, task := range tasks {
names = append(names, task.Name)
}
2026-01-22 19:16:24 +08:00
return fmt.Errorf("已经存在使用的任务,删除失败!任务名:%s", strings.Join(names, ", "))
2023-12-30 17:40:20 +08:00
}
return models.DeleteMsgWay(sw.ID)
}
2024-04-29 17:31:13 +08:00
func (sw *SendWay) Count() (int64, error) {
2023-12-30 17:40:20 +08:00
return models.GetSendWaysTotal(sw.Name, sw.Type, sw.getMaps())
}
func (sw *SendWay) GetAll() ([]models.SendWays, error) {
tags, err := models.GetSendWays(sw.PageNum, sw.PageSize, sw.Name, sw.Type, sw.getMaps())
if err != nil {
return nil, err
}
return tags, nil
}
func (sw *SendWay) getMaps() map[string]interface{} {
maps := make(map[string]interface{})
return maps
}
2025-12-17 21:51:21 +08:00
// getValidator 根据渠道类型获取对应的验证器
func (sw *SendWay) getValidator() WayValidator {
factory, exists := validatorRegistry[sw.Type]
if !exists {
return nil
}
return factory()
}
2023-12-30 17:40:20 +08:00
// ValidateDiffWay 各种发信渠道具体字段校验
func (sw *SendWay) ValidateDiffWay() (string, interface{}) {
var empty interface{}
2025-12-17 21:51:21 +08:00
validator := sw.getValidator()
if validator == nil {
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), empty
2023-12-30 17:40:20 +08:00
}
2025-12-17 21:51:21 +08:00
return validator.Validate(sw.Auth)
2023-12-30 17:40:20 +08:00
}
// TestSendWay 尝试带发信测试连通性
2024-01-08 23:35:10 +08:00
func (sw *SendWay) TestSendWay(msgObj interface{}) (string, string) {
2025-12-17 21:51:21 +08:00
factory, exists := testerRegistry[sw.Type]
if !exists {
return fmt.Sprintf("未知的发信渠道测试: %s", sw.Type), ""
2023-12-30 17:40:20 +08:00
}
2025-12-17 21:51:21 +08:00
tester := factory(msgObj)
return tester.Test()
2023-12-30 17:40:20 +08:00
}