feat: 完成一般消息通知能力的开发
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package notice
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dingTalkRenderer struct{}
|
||||
|
||||
func (dingTalkRenderer) Capabilities() Capabilities {
|
||||
return Capabilities{Text: true, ColorText: true, Markdown: true, Card: true}
|
||||
}
|
||||
|
||||
func (dingTalkRenderer) Render(config Config, message Message) (renderedRequest, error) {
|
||||
body := map[string]any{}
|
||||
switch message.Type {
|
||||
case MessageText:
|
||||
body = map[string]any{"msgtype": "text", "text": map[string]any{"content": message.Content}}
|
||||
case MessageColorText:
|
||||
body = dingTalkMarkdown("消息通知", dingTalkColorMarkdown(message.Segments))
|
||||
case MessageMarkdown:
|
||||
body = dingTalkMarkdown(plainTitle(message.Title, "消息通知"), message.Content)
|
||||
case MessageCard:
|
||||
content := cardMarkdown(message.Card)
|
||||
if strings.TrimSpace(content) == "" {
|
||||
content = message.Card.Title
|
||||
}
|
||||
if len(message.Card.Actions) == 0 {
|
||||
body = dingTalkMarkdown(plainTitle(message.Card.Title, "消息通知"), content)
|
||||
break
|
||||
}
|
||||
buttons := make([]any, 0, len(message.Card.Actions))
|
||||
for _, action := range message.Card.Actions {
|
||||
buttons = append(buttons, map[string]any{"title": action.Text, "actionURL": action.URL})
|
||||
}
|
||||
body = map[string]any{"msgtype": "actionCard", "actionCard": map[string]any{
|
||||
"title": plainTitle(message.Card.Title, "消息通知"), "text": content,
|
||||
"btnOrientation": "0", "btns": buttons,
|
||||
}}
|
||||
}
|
||||
webhook := config.Webhook
|
||||
secret := signatureSecret(config)
|
||||
if secret != "" {
|
||||
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||
sign := dingTalkSign(timestamp, secret)
|
||||
parsed, err := url.Parse(webhook)
|
||||
if err != nil {
|
||||
return renderedRequest{}, err
|
||||
}
|
||||
query := parsed.Query()
|
||||
query.Set("timestamp", timestamp)
|
||||
query.Set("sign", sign)
|
||||
parsed.RawQuery = query.Encode()
|
||||
webhook = parsed.String()
|
||||
}
|
||||
return marshalRequest(webhook, body)
|
||||
}
|
||||
|
||||
func (dingTalkRenderer) ValidateResponse(response networkResponse) error {
|
||||
if err := validateHTTP(ChannelDingTalk, response); err != nil {
|
||||
return err
|
||||
}
|
||||
code, message, err := responseStatus(response.Body, "errcode", "", "errmsg", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("notice: parse dingtalk response: %w", err)
|
||||
}
|
||||
if code != "0" {
|
||||
return &PlatformError{Channel: ChannelDingTalk, Code: code, Message: message}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dingTalkSign(timestamp, secret string) string {
|
||||
digest := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = digest.Write([]byte(timestamp + "\n" + secret))
|
||||
return base64.StdEncoding.EncodeToString(digest.Sum(nil))
|
||||
}
|
||||
|
||||
func dingTalkMarkdown(title, content string) map[string]any {
|
||||
return map[string]any{"msgtype": "markdown", "markdown": map[string]any{"title": title, "text": content}}
|
||||
}
|
||||
|
||||
func dingTalkColorMarkdown(segments []TextSegment) string {
|
||||
var builder strings.Builder
|
||||
for _, segment := range segments {
|
||||
prefix := map[Color]string{
|
||||
ColorInfo: "【提示】", ColorSuccess: "【成功】", ColorWarning: "【警告】", ColorDanger: "【异常】",
|
||||
}[segment.Color]
|
||||
content := escapeText(segment.Text)
|
||||
if prefix != "" {
|
||||
content = prefix + content
|
||||
}
|
||||
if segment.Bold || segment.Color == ColorWarning || segment.Color == ColorDanger {
|
||||
content = "**" + content + "**"
|
||||
}
|
||||
builder.WriteString(content)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
Reference in New Issue
Block a user