Files
mayfly-go/server/internal/msg/msgx/sender/ding_bot.go
2025-07-27 21:02:48 +08:00

109 lines
2.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package sender
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"mayfly-go/internal/msg/msgx"
"mayfly-go/pkg/httpx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
"strings"
"net/url"
"time"
)
type dingBotMsgReq struct {
MsgType string `json:"msgtype"`
Text struct {
Content string `json:"content"`
} `json:"text"`
Markdown struct {
Title string `json:"title"`
Text string `json:"text"`
} `json:"markdown"`
At struct {
// AtUserIds []string `json:"atUserIds"`
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
} `json:"at"`
}
type dingBotMsgResp struct {
Code int `json:"errcode"`
Message string `json:"errmsg"`
}
// DingBotSender 钉钉机器人消息发送
type DingBotSender struct{}
func (d DingBotSender) Send(ctx context.Context, channel *msgx.Channel, msg *msgx.Msg) error {
// https://open.dingtalk.com/document/robots/custom-robot-access#title-72m-8ag-pqw
msgReq := dingBotMsgReq{}
params := msg.Params
receiver := collx.ArrayMapFilter(msg.Receivers, func(a msgx.Receiver) (string, bool) {
return a.Mobile, a.Mobile != ""
})
if len(receiver) > 0 {
msgReq.At.AtMobiles = receiver
// 替换文本中的receiver使用@mobile用于@指定用户
params[msgx.ReceiverKey] = strings.Join(collx.ArrayMap(receiver, func(a string) string { return "@" + a }), "")
} else {
msgReq.At.IsAtAll = true
params[msgx.ReceiverKey] = ""
}
content, err := stringx.TemplateResolve(msg.Content, params)
if err != nil {
return err
}
if msg.Type == msgx.MsgTypeMarkdown {
msgReq.MsgType = "markdown"
msgReq.Markdown.Title = msg.Title
msgReq.Markdown.Text = content
} else {
msgReq.MsgType = "text"
msgReq.Text.Content = content
}
timestamp := time.Now().UnixMilli()
sign, err := d.sign(channel.Extra.GetStr("secret"), timestamp)
if err != nil {
return err
}
var res dingBotMsgResp
err = httpx.NewReq(fmt.Sprintf("%s&timestamp=%d&sign=%s", channel.URL, timestamp, sign)).
PostObj(msgReq).
BodyTo(&res)
if err != nil {
return err
}
if res.Code != 0 {
return errors.New(res.Message)
}
return nil
}
func (d DingBotSender) sign(secret string, timestamp int64) (string, error) {
// https://open.dingtalk.com/document/robots/customize-robot-security-settings
// timestamp + key -> sha256 -> URL encode
stringToSign := fmt.Sprintf("%d\n%s", timestamp, secret)
h := hmac.New(sha256.New, []byte(secret))
_, err := h.Write([]byte(stringToSign))
if err != nil {
return "", err
}
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
signature = url.QueryEscape(signature)
return signature, nil
}