feat: add feishu and aliyunsms

This commit is contained in:
engigu
2025-12-17 21:51:21 +08:00
parent f520dbf746
commit 64f746a7ec
16 changed files with 537 additions and 165 deletions
+19
View File
@@ -47,6 +47,25 @@ var HostedMsgCleanDefaultValueMap = map[string]string{
const AboutSectionName = "about"
// 消息格式类型常量
const (
FormatTypeText = "text"
FormatTypeHTML = "html"
FormatTypeMarkdown = "markdown"
)
// 消息类型常量
const (
MessageTypeEmail = "Email"
MessageTypeDtalk = "Dtalk"
MessageTypeQyWeiXin = "QyWeiXin"
MessageTypeFeishu = "Feishu"
MessageTypeCustom = "Custom"
MessageTypeWeChatOFAccount = "WeChatOFAccount"
MessageTypeMessageNest = "MessageNest"
MessageTypeAliyunSMS = "AliyunSMS"
)
// 限制goroutine的最大数量
var MaxSendTaskSemaphoreChan = make(chan string, 2048)
+133
View File
@@ -0,0 +1,133 @@
package message
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
type feishuResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
type Feishu struct {
AccessToken string
Secret string
}
// genSign 生成飞书签名
func (f *Feishu) genSign(timestamp int64) string {
if f.Secret == "" {
return ""
}
stringToSign := fmt.Sprintf("%v\n%s", timestamp, f.Secret)
h := hmac.New(sha256.New, []byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
return signature
}
// SendMessageText 发送文本消息
func (f *Feishu) SendMessageText(content string, atMobiles ...string) ([]byte, error) {
timestamp := time.Now().Unix()
sign := f.genSign(timestamp)
msg := map[string]interface{}{
"timestamp": strconv.FormatInt(timestamp, 10),
"sign": sign,
"msg_type": "text",
"content": map[string]interface{}{
"text": content,
},
}
return f.send(msg)
}
// SendMessageMarkdown 发送 Markdown 消息
func (f *Feishu) SendMessageMarkdown(title, content string, atMobiles ...string) ([]byte, error) {
timestamp := time.Now().Unix()
sign := f.genSign(timestamp)
// 处理 @ 人员
atContent := ""
if len(atMobiles) > 0 {
for _, mobile := range atMobiles {
if mobile == "all" {
atContent += "<at user_id=\"all\">所有人</at>"
} else {
atContent += fmt.Sprintf("<at user_id=\"%s\"></at>", mobile)
}
}
content = atContent + "\n" + content
}
msg := map[string]interface{}{
"timestamp": strconv.FormatInt(timestamp, 10),
"sign": sign,
"msg_type": "interactive",
"card": map[string]interface{}{
"header": map[string]interface{}{
"title": map[string]interface{}{
"tag": "plain_text",
"content": title,
},
},
"elements": []map[string]interface{}{
{
"tag": "markdown",
"content": content,
},
},
},
}
return f.send(msg)
}
// send 发送请求
func (f *Feishu) send(msg map[string]interface{}) ([]byte, error) {
url := fmt.Sprintf("https://open.feishu.cn/open-apis/bot/v2/hook/%s", f.AccessToken)
jsonData, err := json.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("JSON序列化失败: %v", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
var result feishuResponse
if err := json.Unmarshal(body, &result); err != nil {
return body, fmt.Errorf("解析响应失败: %v", err)
}
if result.Code != 0 {
return body, fmt.Errorf("飞书返回错误: %s", result.Msg)
}
return body, nil
}