1.4.5.2
This commit is contained in:
12
EdgeAPI/internal/senders/mediasenders/format.go
Normal file
12
EdgeAPI/internal/senders/mediasenders/format.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
// TextFormat 内容文本格式类型定义
|
||||
type TextFormat = string
|
||||
|
||||
// 常用的内容文本格式
|
||||
const (
|
||||
FormatText = "text"
|
||||
FormatMarkdown = "markdown"
|
||||
)
|
||||
86
EdgeAPI/internal/senders/mediasenders/media_aliyun_sms.go
Normal file
86
EdgeAPI/internal/senders/mediasenders/media_aliyun_sms.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AliyunSmsMedia 阿里云短信
|
||||
type AliyunSmsMedia struct {
|
||||
Sign string `yaml:"sign" json:"sign"` // 签名名称
|
||||
TemplateCode string `yaml:"templateCode" json:"templateCode"` // 模板CODE
|
||||
Variables []*Variable `yaml:"variables" json:"variables"` // 变量
|
||||
AccessKeyId string `yaml:"accessKeyId" json:"accessKeyId"` // AccessKeyId
|
||||
AccessKeySecret string `yaml:"accessKeySecret" json:"accessKeySecret"` // AccessKeySecret
|
||||
}
|
||||
|
||||
// NewAliyunSmsMedia 获取新对象
|
||||
func NewAliyunSmsMedia() *AliyunSmsMedia {
|
||||
return &AliyunSmsMedia{}
|
||||
}
|
||||
|
||||
func (this *AliyunSmsMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
// {"Message":"OK","RequestId":"xxxx","BizId":"xxxx","Code":"OK"}
|
||||
|
||||
client, err := sdk.NewClientWithAccessKey("cn-hangzhou", this.AccessKeyId, this.AccessKeySecret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
request := requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https" // https | http
|
||||
request.Domain = "dysmsapi.aliyuncs.com"
|
||||
request.Version = "2017-05-25"
|
||||
request.ApiName = "SendSms"
|
||||
request.QueryParams["RegionId"] = "cn-hangzhou"
|
||||
request.QueryParams["PhoneNumbers"] = user
|
||||
request.QueryParams["SignName"] = this.Sign
|
||||
request.QueryParams["TemplateCode"] = this.TemplateCode
|
||||
|
||||
varMap := maps.Map{}
|
||||
for _, v := range this.Variables {
|
||||
value := v.Value
|
||||
value = strings.Replace(value, "${MessageUser}", user, -1)
|
||||
value = strings.Replace(value, "${MessageSubject}", subject, -1)
|
||||
value = strings.Replace(value, "${MessageBody}", body, -1)
|
||||
|
||||
// 阿里云的限制参数长度不能超过20:
|
||||
var maxLen = 20
|
||||
if len([]rune(value)) > maxLen {
|
||||
value = string([]rune(value)[:maxLen-4]) + "..."
|
||||
}
|
||||
|
||||
varMap[v.Name] = value
|
||||
}
|
||||
request.QueryParams["TemplateParam"] = stringutil.JSONEncode(varMap)
|
||||
|
||||
response, err := client.ProcessCommonRequest(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := response.GetHttpContentBytes()
|
||||
m := maps.Map{}
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
if m.GetString("Code") == "OK" {
|
||||
return data, nil
|
||||
}
|
||||
return data, errors.New("fail to send sms:" + string(data))
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *AliyunSmsMedia) RequireUser() bool {
|
||||
return true
|
||||
}
|
||||
72
EdgeAPI/internal/senders/mediasenders/media_dingtalk.go
Normal file
72
EdgeAPI/internal/senders/mediasenders/media_dingtalk.go
Normal file
@@ -0,0 +1,72 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DingTalkMedia 钉钉群机器人媒介
|
||||
type DingTalkMedia struct {
|
||||
WebHookURL string `yaml:"webHookURL" json:"webHookURL"`
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewDingTalkMedia() *DingTalkMedia {
|
||||
return &DingTalkMedia{}
|
||||
}
|
||||
|
||||
func (this *DingTalkMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
if len(this.WebHookURL) == 0 {
|
||||
return nil, errors.New("webHook url should not be empty")
|
||||
}
|
||||
|
||||
content := maps.Map{
|
||||
"msgtype": "text",
|
||||
"text": maps.Map{
|
||||
"content": "标题:" + subject + "\n内容:" + body,
|
||||
},
|
||||
}
|
||||
if len(user) > 0 {
|
||||
mobiles := []string{}
|
||||
for _, u := range strings.Split(user, ",") {
|
||||
u = strings.TrimSpace(u)
|
||||
if len(u) > 0 {
|
||||
mobiles = append(mobiles, u)
|
||||
}
|
||||
}
|
||||
|
||||
content["at"] = maps.Map{
|
||||
"atMobiles": mobiles,
|
||||
}
|
||||
}
|
||||
|
||||
reader := bytes.NewBufferString(stringutil.JSONEncode(content))
|
||||
req, err := http.NewRequest(http.MethodPost, this.WebHookURL, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := utils.SharedHttpClient(5 * time.Second)
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
resp, err = io.ReadAll(response.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *DingTalkMedia) RequireUser() bool {
|
||||
return false
|
||||
}
|
||||
279
EdgeAPI/internal/senders/mediasenders/media_email.go
Normal file
279
EdgeAPI/internal/senders/mediasenders/media_email.go
Normal file
@@ -0,0 +1,279 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MailInfo struct {
|
||||
To string
|
||||
Subject string
|
||||
Body string
|
||||
}
|
||||
|
||||
// EmailMedia 邮件媒介
|
||||
type EmailMedia struct {
|
||||
SMTP string `yaml:"smtp" json:"smtp"` // SMTP地址:host:port
|
||||
Username string `yaml:"username" json:"username"` // 用户名
|
||||
Password string `yaml:"password" json:"password"` // 密码
|
||||
From string `yaml:"from" json:"from"` // 发件人
|
||||
FromName string `yaml:"fromName" json:"fromName"` // 发件人名称
|
||||
Protocol string `yaml:"protocol" json:"protocol"` // 协议:tcp/tls
|
||||
}
|
||||
|
||||
// NewEmailMedia 获取新对象
|
||||
func NewEmailMedia() *EmailMedia {
|
||||
return &EmailMedia{}
|
||||
}
|
||||
|
||||
func (this *EmailMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
if len(this.SMTP) == 0 {
|
||||
return nil, errors.New("host address should be specified")
|
||||
}
|
||||
|
||||
// 自动加端口
|
||||
|
||||
if _, _, err := net.SplitHostPort(this.SMTP); err != nil {
|
||||
this.SMTP += ":587"
|
||||
}
|
||||
|
||||
if len(this.From) == 0 {
|
||||
this.From = this.Username
|
||||
}
|
||||
|
||||
var contentType = "Content-Type: text/html; charset=UTF-8"
|
||||
var senderName = this.FromName
|
||||
if len(senderName) == 0 {
|
||||
if len(productName) > 0 {
|
||||
senderName = productName
|
||||
} else {
|
||||
senderName = teaconst.GlobalProductName
|
||||
}
|
||||
subject = "[" + senderName + "]" + subject
|
||||
}
|
||||
|
||||
var msg = []byte("To: " + user + "\r\nFrom: " + strconv.Quote(senderName) + " <" + this.From + ">\r\nSubject: " + "=?utf-8?B?" + base64.StdEncoding.EncodeToString([]byte(subject)) + "?=" + "\r\n" + contentType + "\r\n\r\n" + body)
|
||||
return nil, this.SendMail(this.From, []string{user}, msg)
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *EmailMedia) RequireUser() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SendMail 发送邮件
|
||||
func (this *EmailMedia) SendMail(from string, to []string, message []byte) error {
|
||||
_, err := mail.ParseAddress(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(to) == 0 {
|
||||
return errors.New("recipients should not be empty")
|
||||
}
|
||||
|
||||
for _, to1 := range to {
|
||||
_, err := mail.ParseAddress(to1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
client, err := this.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = client.Quit()
|
||||
_ = client.Close()
|
||||
}()
|
||||
|
||||
// To && From
|
||||
if err := client.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, to1 := range to {
|
||||
if err := client.Rcpt(to1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Data
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendMails 发送邮件
|
||||
func (this *EmailMedia) SendMails(mails []*MailInfo, productName string) error {
|
||||
if len(mails) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := mail.ParseAddress(this.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := this.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = client.Quit()
|
||||
_ = client.Close()
|
||||
}()
|
||||
|
||||
// 发送者
|
||||
var senderName = this.FromName
|
||||
if len(senderName) == 0 {
|
||||
if len(productName) > 0 {
|
||||
senderName = productName
|
||||
} else {
|
||||
senderName = teaconst.GlobalProductName
|
||||
}
|
||||
}
|
||||
|
||||
// 创建邮件
|
||||
for _, mailInfo := range mails {
|
||||
if len(mailInfo.To) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = mail.ParseAddress(mailInfo.To)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.Mail(this.From); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.Rcpt(mailInfo.To); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Data
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte("To: " + mailInfo.To + "\r\nFrom: " + strconv.Quote(senderName) + " <" + this.From + ">\r\nSubject: " + mailInfo.Subject + "\r\n" + "Content-Type: text/html; charset=UTF-8" + "\r\n\r\n" + mailInfo.Body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *EmailMedia) Connect() (*smtp.Client, error) {
|
||||
var serverName = this.SMTP
|
||||
var username = this.Username
|
||||
var password = this.Password
|
||||
|
||||
host, port, _ := net.SplitHostPort(serverName)
|
||||
|
||||
var client *smtp.Client
|
||||
|
||||
// TLS config
|
||||
var tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: host,
|
||||
}
|
||||
|
||||
if this.Protocol == "tcp" || (len(this.Protocol) == 0 && port == "25") { // 25 port: default tcp port
|
||||
conn, err := net.DialTimeout("tcp", serverName, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err = smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if port == "587" { // 587 port: prefer START_TLS
|
||||
conn, err := net.DialTimeout("tcp", serverName, 10*time.Second)
|
||||
if err != nil {
|
||||
conn, err := tls.Dial("tcp", serverName, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err = smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
client, err = smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = client.StartTLS(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
conn, err := tls.Dial("tcp", serverName, tlsConfig)
|
||||
if err != nil {
|
||||
conn, err := net.DialTimeout("tcp", serverName, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err = smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = client.StartTLS(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
client, err = smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 认证
|
||||
var auth = smtp.PlainAuth("", username, password, host)
|
||||
if err := client.Auth(auth); err != nil {
|
||||
_ = client.Quit()
|
||||
_ = client.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
78
EdgeAPI/internal/senders/mediasenders/media_email_test.go
Normal file
78
EdgeAPI/internal/senders/mediasenders/media_email_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders_test
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/senders/mediasenders"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmailMedia_Send(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var media = mediasenders.NewEmailMedia()
|
||||
media.SMTP = "smtp.qq.com:587"
|
||||
media.Username = "19644627@qq.com"
|
||||
media.Password = "123456" // 换成你的邮件密码或者授权码
|
||||
media.From = "19644627@qq.com"
|
||||
//media.FromName = "测试员"
|
||||
//media.FromName = "\"测试员\""
|
||||
//var subject = "This is test subject"
|
||||
var subject = "这是中文标题"
|
||||
_, err := media.Send("iwind.liu@gmail.com", subject, "This is a test body <strong>粗体哦</strong><br/>换行哦", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailMedia_SendMails(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var media = mediasenders.NewEmailMedia()
|
||||
media.SMTP = "smtp.qq.com:587"
|
||||
media.Username = "19644627@qq.com"
|
||||
media.Password = "123456" // 换成你的邮件密码或者授权码
|
||||
media.From = "19644627@qq.com"
|
||||
//media.FromName = "测试员"
|
||||
//media.FromName = "\"测试员\""
|
||||
err := media.SendMails([]*mediasenders.MailInfo{
|
||||
{
|
||||
To: "iwind.liu@gmail.com",
|
||||
Subject: "This is test subject",
|
||||
Body: "This is a test body <strong>粗体哦</strong><br/>换行哦",
|
||||
},
|
||||
{
|
||||
To: "iwind.liu@gmail.com",
|
||||
Subject: "This is test subject 2",
|
||||
Body: "This is a test body 2 <strong>粗体哦</strong><br/>换行哦",
|
||||
},
|
||||
{
|
||||
To: "q@yun4s.cn",
|
||||
Subject: "This is test subject 3",
|
||||
Body: "This is a test body 3 <strong>粗体哦</strong><br/>换行哦",
|
||||
},
|
||||
{
|
||||
To: "root@teaos.cn",
|
||||
Subject: "This is test subject 4",
|
||||
Body: "This is a test body 4 <strong>粗体哦</strong><br/>换行哦",
|
||||
},
|
||||
}, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailMedia_Send_163(t *testing.T) {
|
||||
var media = mediasenders.NewEmailMedia()
|
||||
media.SMTP = "smtp.163.com:465"
|
||||
media.Username = "iwind_php@163.com"
|
||||
media.Password = "123456" // 换成你的邮件密码或者授权码
|
||||
media.From = "iwind_php@163.com"
|
||||
_, err := media.Send("iwind_php@163.com", "This is test subject", "This is a test body <strong>粗体哦</strong><br/>换行哦", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
137
EdgeAPI/internal/senders/mediasenders/media_qy_weixin.go
Normal file
137
EdgeAPI/internal/senders/mediasenders/media_qy_weixin.go
Normal file
@@ -0,0 +1,137 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// QyWeixinMedia 企业微信媒介
|
||||
type QyWeixinMedia struct {
|
||||
CorporateId string `yaml:"corporateId" json:"corporateId"`
|
||||
AgentId string `yaml:"agentId" json:"agentId"`
|
||||
AppSecret string `yaml:"appSecret" json:"appSecret"`
|
||||
TextFormat TextFormat `yaml:"textFormat" json:"textFormat"`
|
||||
}
|
||||
|
||||
// NewQyWeixinMedia 获取新对象
|
||||
func NewQyWeixinMedia() *QyWeixinMedia {
|
||||
return &QyWeixinMedia{}
|
||||
}
|
||||
|
||||
func (this *QyWeixinMedia) Send(user string, subject string, body string, productName string, datetime string) (respData []byte, err error) {
|
||||
// 获取Token
|
||||
u := "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + url.QueryEscape(this.CorporateId) + "&corpsecret=" + url.QueryEscape(this.AppSecret)
|
||||
req1, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := utils.SharedHttpClient(5 * time.Second)
|
||||
resp1, err := client.Do(req1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp1.Body.Close()
|
||||
|
||||
if resp1.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("status code not 200")
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp1.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := maps.Map{}
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
errCode := m.GetInt("errcode")
|
||||
if errCode > 0 {
|
||||
return data, errors.New("error code:" + fmt.Sprintf("%d", errCode))
|
||||
}
|
||||
|
||||
accessToken := m.GetString("access_token")
|
||||
|
||||
if len(user) == 0 {
|
||||
user = "@all"
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
u = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + url.QueryEscape(accessToken)
|
||||
|
||||
msg := maps.Map{
|
||||
"touser": user,
|
||||
"toparty": "",
|
||||
"totag": "",
|
||||
"toall": 0,
|
||||
"agentid": this.AgentId,
|
||||
"safe": 0,
|
||||
}
|
||||
if this.TextFormat == FormatMarkdown {
|
||||
msg["msgtype"] = "markdown"
|
||||
msg["markdown"] = maps.Map{
|
||||
"content": subject + "\n" + body,
|
||||
}
|
||||
} else {
|
||||
msg["msgtype"] = "text"
|
||||
msg["text"] = maps.Map{
|
||||
"content": subject + "\n" + body,
|
||||
}
|
||||
}
|
||||
data, err = json.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, u, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("status not 200")
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m = maps.Map{}
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
errCode = m.GetInt("errcode")
|
||||
if errCode != 0 {
|
||||
return data, errors.New("errcode " + fmt.Sprintf("%d", errCode))
|
||||
}
|
||||
|
||||
invalidUser := m.GetString("invaliduser")
|
||||
if len(invalidUser) > 0 {
|
||||
return data, errors.New("invalid users:" + invalidUser)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *QyWeixinMedia) RequireUser() bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// QyWeixinRobotMedia 企业微信群机器人媒介
|
||||
type QyWeixinRobotMedia struct {
|
||||
WebHookURL string `yaml:"webHookURL" json:"webHookURL"`
|
||||
TextFormat TextFormat `yaml:"textFormat" json:"textFormat"`
|
||||
}
|
||||
|
||||
// NewQyWeixinRobotMedia 获取新对象
|
||||
func NewQyWeixinRobotMedia() *QyWeixinRobotMedia {
|
||||
return &QyWeixinRobotMedia{}
|
||||
}
|
||||
|
||||
func (this *QyWeixinRobotMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
if len(this.WebHookURL) == 0 {
|
||||
return nil, errors.New("webHook url should not be empty")
|
||||
}
|
||||
|
||||
mobiles := []string{}
|
||||
if len(user) > 0 {
|
||||
for _, u := range strings.Split(user, ",") {
|
||||
u = strings.TrimSpace(u)
|
||||
if len(u) > 0 {
|
||||
mobiles = append(mobiles, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var content maps.Map
|
||||
if this.TextFormat == FormatMarkdown { // markdown
|
||||
content = maps.Map{
|
||||
"msgtype": "markdown",
|
||||
"markdown": maps.Map{
|
||||
"content": subject + "\n" + body,
|
||||
"mentioned_mobile_list": mobiles,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
content = maps.Map{
|
||||
"msgtype": "text",
|
||||
"text": maps.Map{
|
||||
"content": subject + "\n" + body,
|
||||
"mentioned_mobile_list": mobiles,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
reader := bytes.NewBufferString(stringutil.JSONEncode(content))
|
||||
req, err := http.NewRequest(http.MethodPost, this.WebHookURL, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := utils.SharedHttpClient(5 * time.Second)
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
resp, err = io.ReadAll(response.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *QyWeixinRobotMedia) RequireUser() bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQyWeixinRobotMedia_Send(t *testing.T) {
|
||||
media := NewQyWeixinRobotMedia()
|
||||
media.WebHookURL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=123456" //需要换成你自己的webhook
|
||||
media.TextFormat = FormatText
|
||||
resp, err := media.Send("", "这是标题", "*这是内容*", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("resp:", string(resp))
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewQyWeixinMedia(t *testing.T) {
|
||||
m := NewQyWeixinMedia()
|
||||
m.CorporateId = "xxx"
|
||||
m.AppSecret = "xxx"
|
||||
m.AgentId = "1000003"
|
||||
resp, err := m.Send("", "标题:报警标题", "内容:报警内容/全员都有", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Log(string(resp))
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(resp))
|
||||
}
|
||||
136
EdgeAPI/internal/senders/mediasenders/media_script.go
Normal file
136
EdgeAPI/internal/senders/mediasenders/media_script.go
Normal file
@@ -0,0 +1,136 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ScriptMedia 脚本媒介
|
||||
type ScriptMedia struct {
|
||||
Path string `yaml:"path" json:"path"`
|
||||
ScriptType string `yaml:"scriptType" json:"scriptType"` // 脚本类型,可以为path, code
|
||||
ScriptLang string `yaml:"scriptLang" json:"scriptLang"` // 脚本语言
|
||||
Script string `yaml:"script" json:"script"` // 脚本代码
|
||||
Cwd string `yaml:"cwd" json:"cwd"`
|
||||
Env []*Variable `yaml:"env" json:"env"`
|
||||
}
|
||||
|
||||
// NewScriptMedia 获取新对象
|
||||
func NewScriptMedia() *ScriptMedia {
|
||||
return &ScriptMedia{}
|
||||
}
|
||||
|
||||
// AddEnv 添加环境变量
|
||||
func (this *ScriptMedia) AddEnv(name, value string) {
|
||||
this.Env = append(this.Env, &Variable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// FormattedScript 格式化脚本
|
||||
func (this *ScriptMedia) FormattedScript() string {
|
||||
script := this.Script
|
||||
script = strings.Replace(script, "\r", "", -1)
|
||||
return script
|
||||
}
|
||||
|
||||
// Generate 保存到本地
|
||||
func (this *ScriptMedia) Generate(id string) (path string, err error) {
|
||||
var tmpDir = os.TempDir()
|
||||
if runtime.GOOS == "windows" {
|
||||
path = tmpDir + Tea.DS + "edge.script." + id + ".bat"
|
||||
} else {
|
||||
path = tmpDir + "/edge.script." + id + ".script"
|
||||
}
|
||||
shFile := files.NewFile(path)
|
||||
if !shFile.Exists() {
|
||||
err = shFile.WriteString(this.FormattedScript())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = shFile.Chmod(0777)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Send 发送
|
||||
func (this *ScriptMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
// 脚本
|
||||
if this.ScriptType == "code" {
|
||||
path, err := this.Generate(stringutil.Rand(16))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
this.Path = path
|
||||
|
||||
defer func() {
|
||||
f := files.NewFile(this.Path)
|
||||
if f.Exists() {
|
||||
err := f.Delete()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if len(this.Path) == 0 {
|
||||
return nil, errors.New("'path' should be specified")
|
||||
}
|
||||
|
||||
cmd := exec.Command(this.Path)
|
||||
|
||||
if len(this.Env) > 0 {
|
||||
for _, env := range this.Env {
|
||||
cmd.Env = append(cmd.Env, env.Name+"="+env.Value)
|
||||
}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, "MessageUser="+user)
|
||||
cmd.Env = append(cmd.Env, "MessageSubject="+subject)
|
||||
cmd.Env = append(cmd.Env, "MessageBody="+body)
|
||||
|
||||
if len(this.Cwd) > 0 {
|
||||
cmd.Dir = this.Cwd
|
||||
}
|
||||
|
||||
stdout := bytes.NewBuffer([]byte{})
|
||||
stderr := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if stderr.Len() > 0 {
|
||||
return stdout.Bytes(), errors.New(stderr.String())
|
||||
}
|
||||
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *ScriptMedia) RequireUser() bool {
|
||||
return false
|
||||
}
|
||||
50
EdgeAPI/internal/senders/mediasenders/media_script_test.go
Normal file
50
EdgeAPI/internal/senders/mediasenders/media_script_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScriptMedia_Send(t *testing.T) {
|
||||
script := `#!/usr/bin/env bash
|
||||
|
||||
echo "subject:${MessageSubject}"
|
||||
echo "body:${MessageBody}"
|
||||
`
|
||||
|
||||
tmp := files.NewFile(Tea.Root + "/web/tmp/media_test.sh")
|
||||
err := tmp.WriteString(script)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = tmp.Chmod(0777)
|
||||
defer func() {
|
||||
_ = tmp.Delete()
|
||||
}()
|
||||
|
||||
media := NewScriptMedia()
|
||||
media.Path = tmp.Path()
|
||||
_, err = media.Send("zhangsan", "this is subject", "this is body", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScriptMedia_Send2(t *testing.T) {
|
||||
media := NewScriptMedia()
|
||||
media.ScriptType = "code"
|
||||
media.Script = `#!/usr/bin/env bash
|
||||
|
||||
echo "subject:${MessageSubject}"
|
||||
echo "body:${MessageBody}"
|
||||
`
|
||||
_, err := media.Send("zhangsan", "this is subject", "this is body", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
75
EdgeAPI/internal/senders/mediasenders/media_telegram.go
Normal file
75
EdgeAPI/internal/senders/mediasenders/media_telegram.go
Normal file
@@ -0,0 +1,75 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TelegramMedia Telegram媒介
|
||||
type TelegramMedia struct {
|
||||
Token string `yaml:"token" json:"token"`
|
||||
ProxyURL string `yaml:"proxyURL" json:"proxyURL"`
|
||||
}
|
||||
|
||||
// NewTelegramMedia 获取新对象
|
||||
func NewTelegramMedia() *TelegramMedia {
|
||||
return &TelegramMedia{}
|
||||
}
|
||||
|
||||
// Send 发送消息
|
||||
func (this *TelegramMedia) Send(user string, subject string, body string, productName string, datetime string) (respBytes []byte, err error) {
|
||||
var proxyFunc func(req *http.Request) (*url.URL, error)
|
||||
if len(this.ProxyURL) > 0 {
|
||||
proxyFunc = func(req *http.Request) (*url.URL, error) {
|
||||
return url.Parse(this.ProxyURL)
|
||||
}
|
||||
}
|
||||
|
||||
var httpClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 4096,
|
||||
MaxIdleConnsPerHost: 32,
|
||||
MaxConnsPerHost: 32,
|
||||
IdleConnTimeout: 2 * time.Minute,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSHandshakeTimeout: 0,
|
||||
Proxy: proxyFunc,
|
||||
},
|
||||
}
|
||||
|
||||
defer httpClient.CloseIdleConnections()
|
||||
|
||||
bot, err := tgbotapi.NewBotAPIWithClient(this.Token, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bot.Client = httpClient
|
||||
|
||||
var text string
|
||||
if len(productName) > 0 {
|
||||
text += productName
|
||||
if len(datetime) > 0 {
|
||||
text += " AT " + datetime
|
||||
}
|
||||
text += "\n----\n"
|
||||
} else if len(datetime) > 0 {
|
||||
text += "AT " + datetime
|
||||
text += "\n----\n"
|
||||
}
|
||||
text += subject + "\n" + body
|
||||
var msg = tgbotapi.NewMessage(types.Int64(user), text)
|
||||
_, err = bot.Send(msg)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *TelegramMedia) RequireUser() bool {
|
||||
return true
|
||||
}
|
||||
21
EdgeAPI/internal/senders/mediasenders/media_telegram_test.go
Normal file
21
EdgeAPI/internal/senders/mediasenders/media_telegram_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package mediasenders_test
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/senders/mediasenders"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTelegramMedia_Send(t *testing.T) {
|
||||
var media = mediasenders.NewTelegramMedia()
|
||||
media.Token = "YOUR BOT TOKEN" // from @BotFather
|
||||
//media.ProxyURL = "sock5://192.168.2.41:7890"
|
||||
_, err := media.Send("YOUR ID" /** from @get_id_bot **/, "test subject", "test body", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
156
EdgeAPI/internal/senders/mediasenders/media_type.go
Normal file
156
EdgeAPI/internal/senders/mediasenders/media_type.go
Normal file
@@ -0,0 +1,156 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// MediaType 通知媒介类型
|
||||
type MediaType = string
|
||||
|
||||
const (
|
||||
MediaTypeEmail MediaType = "email"
|
||||
MediaTypeWebHook MediaType = "webHook"
|
||||
MediaTypeScript MediaType = "script"
|
||||
MediaTypeDingTalk MediaType = "dingTalk"
|
||||
MediaTypeQyWeixin MediaType = "qyWeixin"
|
||||
MediaTypeQyWeixinRobot MediaType = "qyWeixinRobot"
|
||||
MediaTypeAliyunSms MediaType = "aliyunSms"
|
||||
MediaTypeTelegram MediaType = "telegram"
|
||||
)
|
||||
|
||||
type MediaDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code MediaType `json:"code"`
|
||||
SupportsHTML bool `json:"supportsHtml"`
|
||||
Instance MediaInterface `json:"-"`
|
||||
Description string `json:"description"`
|
||||
User string `json:"user"`
|
||||
IsOn bool `json:"isOn"`
|
||||
}
|
||||
|
||||
// AllMediaTypes 所有媒介
|
||||
func AllMediaTypes() []*MediaDefinition {
|
||||
return []*MediaDefinition{
|
||||
{
|
||||
Name: "邮件",
|
||||
Code: MediaTypeEmail,
|
||||
SupportsHTML: true,
|
||||
Instance: new(EmailMedia),
|
||||
Description: "通过邮件发送通知。",
|
||||
User: "接收人邮箱地址。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "WebHook",
|
||||
Code: MediaTypeWebHook,
|
||||
SupportsHTML: false,
|
||||
Instance: new(WebHookMedia),
|
||||
Description: "通过HTTP请求发送通知。",
|
||||
User: "通过${MessageUser}参数传递到URL上。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "脚本",
|
||||
Code: MediaTypeScript,
|
||||
SupportsHTML: false,
|
||||
Instance: new(ScriptMedia),
|
||||
Description: "通过运行脚本发送通知。",
|
||||
User: "可以在脚本中使用${MessageUser}来获取这个标识。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "钉钉群机器人",
|
||||
Code: MediaTypeDingTalk,
|
||||
SupportsHTML: false,
|
||||
Instance: new(DingTalkMedia),
|
||||
Description: "通过钉钉群机器人发送通知消息。",
|
||||
User: "要At(@)的群成员的手机号,多个手机号用英文逗号隔开,也可以为空。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "企业微信应用",
|
||||
Code: MediaTypeQyWeixin,
|
||||
SupportsHTML: false,
|
||||
Instance: new(QyWeixinMedia),
|
||||
Description: "通过企业微信应用发送通知消息。",
|
||||
User: "接收消息的成员的用户账号,多个成员用竖线(|)分隔,如果所有成员使用@all。留空表示所有成员。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "企业微信群机器人",
|
||||
Code: MediaTypeQyWeixinRobot,
|
||||
SupportsHTML: false,
|
||||
Instance: new(QyWeixinRobotMedia),
|
||||
Description: "通过微信群机器人发送通知消息。",
|
||||
User: "要At(@)的群成员的手机号,多个手机号用英文逗号隔开,也可以为空。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "阿里云短信",
|
||||
Code: MediaTypeAliyunSms,
|
||||
SupportsHTML: false,
|
||||
Instance: new(AliyunSmsMedia),
|
||||
Description: "通过<a href=\"https://www.aliyun.com/product/sms\" target=\"_blank\">阿里云短信服务</a>发送短信。",
|
||||
User: "接收消息的手机号。",
|
||||
IsOn: true,
|
||||
},
|
||||
{
|
||||
Name: "Telegram机器人",
|
||||
Code: MediaTypeTelegram,
|
||||
SupportsHTML: false,
|
||||
Instance: new(TelegramMedia),
|
||||
Description: "通过机器人向群或者某个用户发送消息,需要确保所在网络能够访问Telegram API服务。",
|
||||
User: "群或用户的Chat ID,通常是一个数字,可以通过和 @get_id_bot 建立对话并发送任意消息获得。",
|
||||
IsOn: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FindMediaType 查找媒介类型
|
||||
func FindMediaType(mediaType string) *MediaDefinition {
|
||||
for _, m := range AllMediaTypes() {
|
||||
if m.Code == mediaType {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMediaInstance 查找媒介实例
|
||||
func NewMediaInstance(mediaType string, optionsJSON []byte) (MediaInterface, error) {
|
||||
for _, m := range AllMediaTypes() {
|
||||
if m.Code == mediaType {
|
||||
var media = reflect.New(reflect.TypeOf(m.Instance).Elem()).Interface().(MediaInterface)
|
||||
if len(optionsJSON) > 0 {
|
||||
err := json.Unmarshal(optionsJSON, media)
|
||||
if err != nil {
|
||||
return nil, errors.New("decode media options failed: " + err.Error())
|
||||
}
|
||||
}
|
||||
return media, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("can not find media with type '" + mediaType + "'")
|
||||
}
|
||||
|
||||
// FindMediaTypeName 查找媒介类型名称
|
||||
func FindMediaTypeName(mediaType string) string {
|
||||
var m = FindMediaType(mediaType)
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// MediaInterface 媒介接口
|
||||
type MediaInterface interface {
|
||||
// Send 发送
|
||||
Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error)
|
||||
|
||||
// RequireUser 是否可以需要用户标识
|
||||
RequireUser() bool
|
||||
}
|
||||
148
EdgeAPI/internal/senders/mediasenders/media_web_hook.go
Normal file
148
EdgeAPI/internal/senders/mediasenders/media_web_hook.go
Normal file
@@ -0,0 +1,148 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WebHookMedia WebHook媒介
|
||||
type WebHookMedia struct {
|
||||
URL string `yaml:"url" json:"url"` // URL中可以使用${MessageSubject}, ${MessageBody}两个变量
|
||||
Method string `yaml:"method" json:"method"`
|
||||
ContentType string `yaml:"contentType" json:"contentType"` // 内容类型:params|body
|
||||
Headers []*Variable `yaml:"headers" json:"headers"`
|
||||
Params []*Variable `yaml:"params" json:"params"`
|
||||
Body string `yaml:"body" json:"body"`
|
||||
}
|
||||
|
||||
// NewWebHookMedia 获取新对象
|
||||
func NewWebHookMedia() *WebHookMedia {
|
||||
return &WebHookMedia{}
|
||||
}
|
||||
|
||||
// Send 发送
|
||||
func (this *WebHookMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) {
|
||||
if len(this.URL) == 0 {
|
||||
return nil, errors.New("'url' should be specified")
|
||||
}
|
||||
|
||||
var timeout = 10 * time.Second
|
||||
|
||||
if len(this.Method) == 0 {
|
||||
this.Method = http.MethodGet
|
||||
}
|
||||
|
||||
this.URL = strings.Replace(this.URL, "${MessageUser}", url.QueryEscape(user), -1)
|
||||
this.URL = strings.Replace(this.URL, "${MessageSubject}", url.QueryEscape(subject), -1)
|
||||
this.URL = strings.Replace(this.URL, "${MessageBody}", url.QueryEscape(body), -1)
|
||||
|
||||
var req *http.Request
|
||||
if this.Method == http.MethodGet {
|
||||
req, err = http.NewRequest(this.Method, this.URL, nil)
|
||||
} else {
|
||||
var params = url.Values{
|
||||
"MessageUser": []string{user},
|
||||
"MessageSubject": []string{subject},
|
||||
"MessageBody": []string{body},
|
||||
}
|
||||
|
||||
var postBody = ""
|
||||
if this.ContentType == "params" {
|
||||
for _, param := range this.Params {
|
||||
param.Value = strings.Replace(param.Value, "${MessageUser}", user, -1)
|
||||
param.Value = strings.Replace(param.Value, "${MessageSubject}", subject, -1)
|
||||
param.Value = strings.Replace(param.Value, "$MessageBody}", body, -1)
|
||||
params.Add(param.Name, param.Value)
|
||||
}
|
||||
postBody = params.Encode()
|
||||
} else if this.ContentType == "body" {
|
||||
userJSON := stringutil.JSONEncode(user)
|
||||
subjectJSON := stringutil.JSONEncode(subject)
|
||||
bodyJSON := stringutil.JSONEncode(body)
|
||||
if len(userJSON) > 0 {
|
||||
userJSON = userJSON[1 : len(userJSON)-1]
|
||||
}
|
||||
if len(subjectJSON) > 0 {
|
||||
subjectJSON = subjectJSON[1 : len(subjectJSON)-1]
|
||||
}
|
||||
if len(bodyJSON) > 0 {
|
||||
bodyJSON = bodyJSON[1 : len(bodyJSON)-1]
|
||||
}
|
||||
postBody = strings.Replace(this.Body, "${MessageUser}", userJSON, -1)
|
||||
postBody = strings.Replace(postBody, "${MessageSubject}", subjectJSON, -1)
|
||||
postBody = strings.Replace(postBody, "${MessageBody}", bodyJSON, -1)
|
||||
} else {
|
||||
postBody = params.Encode()
|
||||
}
|
||||
|
||||
req, err = http.NewRequest(this.Method, this.URL, strings.NewReader(postBody))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
|
||||
|
||||
if this.Method == http.MethodPost {
|
||||
if this.ContentType == "params" {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
}
|
||||
|
||||
if len(this.Headers) > 0 {
|
||||
for _, h := range this.Headers {
|
||||
req.Header.Set(h.Name, h.Value)
|
||||
}
|
||||
}
|
||||
|
||||
var client = utils.SharedHttpClient(timeout)
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = response.Body.Close()
|
||||
}()
|
||||
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// check response code
|
||||
if response.StatusCode >= 400 {
|
||||
err = errors.New("invalid response code '" + types.String(response.StatusCode) + "'")
|
||||
}
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
// RequireUser 是否需要用户标识
|
||||
func (this *WebHookMedia) RequireUser() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AddHeader 添加Header
|
||||
func (this *WebHookMedia) AddHeader(name string, value string) {
|
||||
this.Headers = append(this.Headers, &Variable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// AddParam 添加参数
|
||||
func (this *WebHookMedia) AddParam(name string, value string) {
|
||||
this.Params = append(this.Params, &Variable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
16
EdgeAPI/internal/senders/mediasenders/media_web_hook_test.go
Normal file
16
EdgeAPI/internal/senders/mediasenders/media_web_hook_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMediaWebHook_Send(t *testing.T) {
|
||||
media := NewWebHookMedia()
|
||||
media.URL = "http://127.0.0.1:9991/webhook?subject=${MessageSubject}&body=${MessageBody}"
|
||||
resp, err := media.Send("zhangsan", "this is subject", "this is body", teaconst.GlobalProductName, timeutil.Format("Y-m-d H:i:s"))
|
||||
t.Log(string(resp), err)
|
||||
}
|
||||
17
EdgeAPI/internal/senders/mediasenders/var.go
Normal file
17
EdgeAPI/internal/senders/mediasenders/var.go
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build plus
|
||||
|
||||
package mediasenders
|
||||
|
||||
// Variable 变量
|
||||
type Variable struct {
|
||||
Name string `yaml:"name" json:"name"` // 变量名
|
||||
Value string `yaml:"value" json:"value"` // 变量值
|
||||
}
|
||||
|
||||
// 创建新变量
|
||||
func NewVariable(name string, value string) *Variable {
|
||||
return &Variable{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
86
EdgeAPI/internal/senders/smssenders/sender_aliyun_sms.go
Normal file
86
EdgeAPI/internal/senders/smssenders/sender_aliyun_sms.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build plus
|
||||
|
||||
package smssenders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// AliyunSMSSender 阿里云短信
|
||||
type AliyunSMSSender struct {
|
||||
params *userconfigs.SMSSenderAliyunSMSParams
|
||||
}
|
||||
|
||||
// NewAliyunSMSSender 获取新对象
|
||||
func NewAliyunSMSSender() *AliyunSMSSender {
|
||||
return &AliyunSMSSender{}
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *AliyunSMSSender) Init(params any) error {
|
||||
if params == nil {
|
||||
return errors.New("'params' should not be nil")
|
||||
}
|
||||
var ok bool
|
||||
this.params, ok = params.(*userconfigs.SMSSenderAliyunSMSParams)
|
||||
if !ok {
|
||||
return errors.New("invalid params type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 发送短信
|
||||
func (this *AliyunSMSSender) Send(mobile string, body string, code string) (resp string, isOk bool, err error) {
|
||||
// {"Message":"OK","RequestId":"xxxx","BizId":"xxxx","Code":"OK"}
|
||||
|
||||
client, err := sdk.NewClientWithAccessKey("cn-hangzhou", this.params.AccessKeyId, this.params.AccessKeySecret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var request = requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https" // https | http
|
||||
request.Domain = "dysmsapi.aliyuncs.com"
|
||||
request.Version = "2017-05-25"
|
||||
request.ApiName = "SendSms"
|
||||
request.QueryParams["RegionId"] = "cn-hangzhou"
|
||||
request.QueryParams["PhoneNumbers"] = mobile // TODO 支持海外手机号?
|
||||
request.QueryParams["SignName"] = this.params.Sign
|
||||
request.QueryParams["TemplateCode"] = this.params.TemplateCode
|
||||
|
||||
var codeVarName = this.params.CodeVarName
|
||||
if len(codeVarName) == 0 {
|
||||
codeVarName = "code"
|
||||
}
|
||||
|
||||
templateParamJSON, err := json.Marshal(map[string]string{
|
||||
codeVarName: code,
|
||||
})
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
request.QueryParams["TemplateParam"] = string(templateParamJSON)
|
||||
|
||||
response, err := client.ProcessCommonRequest(request)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
var data = response.GetHttpContentBytes()
|
||||
m := maps.Map{}
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return string(data), false, err
|
||||
}
|
||||
|
||||
if m.GetString("Code") == "OK" {
|
||||
return "", true, nil
|
||||
}
|
||||
return string(data), false, errors.New("fail to send sms:" + string(data))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package smssenders_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/senders/smssenders"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAliyunSMSSender_Send(t *testing.T) {
|
||||
var sender = smssenders.NewAliyunSMSSender()
|
||||
err := sender.Init(&userconfigs.SMSSenderAliyunSMSParams{
|
||||
Sign: "SIGN",
|
||||
TemplateCode: "TEMPLATE CODE",
|
||||
CodeVarName: "code1",
|
||||
AccessKeyId: "ACCESS KEY ID",
|
||||
AccessKeySecret: "ACCESS KEY SECRET",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, err = sender.Send("13888888888", "", "1234")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
12
EdgeAPI/internal/senders/smssenders/sender_interface.go
Normal file
12
EdgeAPI/internal/senders/smssenders/sender_interface.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package smssenders
|
||||
|
||||
type SenderInterface interface {
|
||||
// Init 初始化
|
||||
Init(params any) error
|
||||
|
||||
// Send 发送短信
|
||||
Send(mobile string, body string, code string) (resp string, isOk bool, err error)
|
||||
}
|
||||
69
EdgeAPI/internal/senders/smssenders/sender_tencent_sms.go
Normal file
69
EdgeAPI/internal/senders/smssenders/sender_tencent_sms.go
Normal file
@@ -0,0 +1,69 @@
|
||||
//go:build plus
|
||||
|
||||
package smssenders
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111" // 引入sms
|
||||
)
|
||||
|
||||
// 相关文档:https://cloud.tencent.com/document/product/382/43199
|
||||
|
||||
// TencentSMSSender 腾讯云短信
|
||||
type TencentSMSSender struct {
|
||||
params *userconfigs.SMSSenderTencentSMSParams
|
||||
}
|
||||
|
||||
// NewTencentSMSSender 获取新对象
|
||||
func NewTencentSMSSender() *TencentSMSSender {
|
||||
return &TencentSMSSender{}
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (this *TencentSMSSender) Init(params any) error {
|
||||
if params == nil {
|
||||
return errors.New("'params' should not be nil")
|
||||
}
|
||||
var ok bool
|
||||
this.params, ok = params.(*userconfigs.SMSSenderTencentSMSParams)
|
||||
if !ok {
|
||||
return errors.New("invalid params type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 发送短信
|
||||
func (this *TencentSMSSender) Send(mobile string, body string, code string) (resp string, isOk bool, err error) {
|
||||
var credential = common.NewCredential(this.params.AccessKeyId, this.params.AccessKeySecret)
|
||||
var cpf = profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
// cpf.HttpProfile.ReqTimeout = 5
|
||||
cpf.HttpProfile.Endpoint = "sms.tencentcloudapi.com"
|
||||
cpf.SignMethod = "HmacSHA1"
|
||||
|
||||
client, err := sms.NewClient(credential, "ap-guangzhou", cpf)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
var request = sms.NewSendSmsRequest()
|
||||
request.SmsSdkAppId = common.StringPtr(this.params.SDKAppId)
|
||||
request.SignName = common.StringPtr(this.params.Sign)
|
||||
request.TemplateId = common.StringPtr(this.params.TemplateId)
|
||||
request.TemplateParamSet = common.StringPtrs([]string{code})
|
||||
request.PhoneNumberSet = common.StringPtrs([]string{"+86" + mobile}) // TODO 支持海外手机号?
|
||||
request.SessionContext = common.StringPtr("")
|
||||
request.ExtendCode = common.StringPtr("")
|
||||
request.SenderId = common.StringPtr("")
|
||||
|
||||
// 通过client对象调用想要访问的接口,需要传入请求对象
|
||||
_, err = client.SendSms(request)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
return "", true, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package smssenders_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/senders/smssenders"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTencentSMSSender_Send(t *testing.T) {
|
||||
var sender = smssenders.NewAliyunSMSSender()
|
||||
err := sender.Init(&userconfigs.SMSSenderTencentSMSParams{
|
||||
SDKAppId: "123456",
|
||||
Sign: "SIGN",
|
||||
TemplateId: "123456",
|
||||
AccessKeyId: "ACCESS KEY ID",
|
||||
AccessKeySecret: "ACCESS KEY SECRET",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, err = sender.Send("13888888888", "", "1234")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
89
EdgeAPI/internal/senders/smssenders/sender_web_hook.go
Normal file
89
EdgeAPI/internal/senders/smssenders/sender_web_hook.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package smssenders
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebHookSender struct {
|
||||
params *userconfigs.SMSSenderWebHookParams
|
||||
}
|
||||
|
||||
func NewWebHookSender() *WebHookSender {
|
||||
return &WebHookSender{}
|
||||
}
|
||||
|
||||
func (this *WebHookSender) Init(params any) error {
|
||||
if params == nil {
|
||||
return errors.New("'params' should not be nil")
|
||||
}
|
||||
var ok bool
|
||||
this.params, ok = params.(*userconfigs.SMSSenderWebHookParams)
|
||||
if !ok {
|
||||
return errors.New("invalid params type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 发送短信
|
||||
func (this *WebHookSender) Send(mobile string, body string, code string) (result string, isOk bool, err error) {
|
||||
var encodedQuery = (&url.Values{
|
||||
"mobile": []string{mobile},
|
||||
"body": []string{body},
|
||||
"code": []string{code},
|
||||
}).Encode()
|
||||
var webHookURL = this.params.URL
|
||||
var reader io.Reader
|
||||
if this.params.Method == "POST" {
|
||||
reader = strings.NewReader(encodedQuery)
|
||||
} else {
|
||||
if strings.Contains(webHookURL, "?") {
|
||||
webHookURL += "&" + encodedQuery
|
||||
} else {
|
||||
webHookURL += "?" + encodedQuery
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(this.params.Method, webHookURL, reader)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("create request failed: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
|
||||
if this.params.Method == "POST" {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
resp, err := utils.SharedHttpClient(10 * time.Second).Do(req)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if resp == nil || resp.Body == nil {
|
||||
return "", false, errors.New("invalid response")
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
respData, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "StatusCode: " + types.String(resp.StatusCode) + ": " + string(respData), false, nil
|
||||
}
|
||||
|
||||
return string(respData), true, nil
|
||||
}
|
||||
62
EdgeAPI/internal/senders/smssenders/sender_web_hook_test.go
Normal file
62
EdgeAPI/internal/senders/smssenders/sender_web_hook_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package smssenders_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/senders/smssenders"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWebHookSender_Send_Get(t *testing.T) {
|
||||
var sender = smssenders.NewWebHookSender()
|
||||
err := sender.Init(&userconfigs.SMSSenderWebHookParams{
|
||||
URL: "http://192.168.2.41:2345/sms/get",
|
||||
Method: http.MethodGet,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, isOk, err := sender.Send("13888888888", "THIS IS BODY", "1234")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(isOk)
|
||||
t.Log("response:", resp)
|
||||
}
|
||||
|
||||
func TestWebHookSender_Send_Get_Fail(t *testing.T) {
|
||||
var sender = smssenders.NewWebHookSender()
|
||||
err := sender.Init(&userconfigs.SMSSenderWebHookParams{
|
||||
URL: "http://192.168.2.41:2345/404",
|
||||
Method: http.MethodGet,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, isOk, err := sender.Send("13888888888", "THIS IS BODY", "1234")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(isOk)
|
||||
t.Log("response:", resp)
|
||||
}
|
||||
|
||||
func TestWebHookSender_Send_POST(t *testing.T) {
|
||||
var sender = smssenders.NewWebHookSender()
|
||||
err := sender.Init(&userconfigs.SMSSenderWebHookParams{
|
||||
URL: "http://192.168.2.41:2345/sms/post",
|
||||
Method: http.MethodPost,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, isOk, err := sender.Send("13888888888", "THIS IS BODY", "1234")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(isOk)
|
||||
t.Log("response:", resp)
|
||||
}
|
||||
69
EdgeAPI/internal/senders/smssenders/types.go
Normal file
69
EdgeAPI/internal/senders/smssenders/types.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package smssenders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||
)
|
||||
|
||||
type SenderDefinition struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
func FindAllTypes() []*SenderDefinition {
|
||||
return []*SenderDefinition{
|
||||
{
|
||||
Name: "自定义HTTP接口",
|
||||
Code: userconfigs.SMSSenderWebHook,
|
||||
},
|
||||
{
|
||||
Name: "阿里云短信",
|
||||
Code: userconfigs.SMSSenderAliyunSMS,
|
||||
},
|
||||
{
|
||||
Name: "腾讯云短信",
|
||||
Code: userconfigs.SMSSenderTencentSMS,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateSender(senderType userconfigs.SMSSenderType, paramsJSON []byte) (sender SenderInterface, err error) {
|
||||
var params any
|
||||
|
||||
switch senderType {
|
||||
case userconfigs.SMSSenderWebHook:
|
||||
sender = NewWebHookSender()
|
||||
params = &userconfigs.SMSSenderWebHookParams{}
|
||||
case userconfigs.SMSSenderAliyunSMS:
|
||||
sender = NewAliyunSMSSender()
|
||||
params = &userconfigs.SMSSenderAliyunSMSParams{}
|
||||
case userconfigs.SMSSenderTencentSMS:
|
||||
sender = NewTencentSMSSender()
|
||||
params = &userconfigs.SMSSenderTencentSMSParams{}
|
||||
default:
|
||||
return nil, errors.New("could not find sender with type '" + senderType + "'")
|
||||
}
|
||||
|
||||
if sender == nil {
|
||||
return nil, errors.New("could not find sender with type '" + senderType + "'")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(paramsJSON, params)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("decode params failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = sender.Init(params)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("initialize sender failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user