-
Notifications
You must be signed in to change notification settings - Fork 15
/
dingrobot.go
149 lines (132 loc) · 3.46 KB
/
dingrobot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package dingrobot
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
// Roboter is the interface implemented by Robot that can send multiple types of messages.
type Roboter interface {
SendText(content string, atMobiles []string, isAtAll bool) error
SendLink(title, text, messageURL, picURL string) error
SendMarkdown(title, text string, atMobiles []string, isAtAll bool) error
SendActionCard(title, text, singleTitle, singleURL, btnOrientation, hideAvatar string) error
SetSecret(secret string)
}
// Robot represents a dingtalk custom robot that can send messages to groups.
type Robot struct {
webHook string
secret string
}
// NewRobot returns a roboter that can send messages.
func NewRobot(webHook string) Roboter {
return &Robot{webHook: webHook}
}
// SetSecret set the secret to add additional signature when send request
func (r *Robot) SetSecret(secret string) {
r.secret = secret
}
// SendText send a text type message.
func (r Robot) SendText(content string, atMobiles []string, isAtAll bool) error {
return r.send(&textMessage{
MsgType: msgTypeText,
Text: textParams{
Content: content,
},
At: atParams{
AtMobiles: atMobiles,
IsAtAll: isAtAll,
},
})
}
// SendLink send a link type message.
func (r Robot) SendLink(title, text, messageURL, picURL string) error {
return r.send(&linkMessage{
MsgType: msgTypeLink,
Link: linkParams{
Title: title,
Text: text,
MessageURL: messageURL,
PicURL: picURL,
},
})
}
// SendMarkdown send a markdown type message.
func (r Robot) SendMarkdown(title, text string, atMobiles []string, isAtAll bool) error {
return r.send(&markdownMessage{
MsgType: msgTypeMarkdown,
Markdown: markdownParams{
Title: title,
Text: text,
},
At: atParams{
AtMobiles: atMobiles,
IsAtAll: isAtAll,
},
})
}
// SendActionCard send a action card type message.
func (r Robot) SendActionCard(title, text, singleTitle, singleURL, btnOrientation, hideAvatar string) error {
return r.send(&actionCardMessage{
MsgType: msgTypeActionCard,
ActionCard: actionCardParams{
Title: title,
Text: text,
SingleTitle: singleTitle,
SingleURL: singleURL,
BtnOrientation: btnOrientation,
HideAvatar: hideAvatar,
},
})
}
type dingResponse struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
func (r Robot) send(msg interface{}) error {
m, err := json.Marshal(msg)
if err != nil {
return err
}
webURL := r.webHook
if len(r.secret) != 0 {
webURL += genSignedURL(r.secret)
}
resp, err := http.Post(webURL, "application/json", bytes.NewReader(m))
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var dr dingResponse
err = json.Unmarshal(data, &dr)
if err != nil {
return err
}
if dr.Errcode != 0 {
return fmt.Errorf("dingrobot send failed: %v", dr.Errmsg)
}
return nil
}
func genSignedURL(secret string) string {
timeStr := fmt.Sprintf("%d", time.Now().UnixNano()/1e6)
sign := fmt.Sprintf("%s\n%s", timeStr, secret)
signData := computeHmacSha256(sign, secret)
encodeURL := url.QueryEscape(signData)
return fmt.Sprintf("×tamp=%s&sign=%s", timeStr, encodeURL)
}
func computeHmacSha256(message string, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(message))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}