notify.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package service
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/sha256"
  6. "encoding/base64"
  7. "encoding/json"
  8. "fmt"
  9. "github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/global"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "time"
  14. )
  15. type NotifyService struct {
  16. }
  17. //@author: [Espoir](https://github.com/nightsimon)
  18. //@function: SendTextMessage
  19. //@description: 发送钉钉文字信息
  20. //@params content string发送的文字内容
  21. //@params atMobiles []string 艾特的手机号
  22. //@params isAtAll bool 是否艾特全体
  23. //@return: err error
  24. func (e *NotifyService) SendTextMessage(content string, atMobiles []string, isAtAll bool) (err error) {
  25. msg := map[string]interface{}{
  26. "msgtype": "text",
  27. "text": map[string]string{
  28. "content": content,
  29. },
  30. "at": map[string]interface{}{
  31. "atMobiles": atMobiles,
  32. "isAtAll": isAtAll,
  33. },
  34. }
  35. return SendMessage(msg)
  36. }
  37. //@author: [Espoir](https://github.com/nightsimon)
  38. //@function: SendLinkMessage
  39. //@description: 发送钉钉图文链接信息
  40. //@params content string 发送的文字内容
  41. //@params title string 发送的标题
  42. //@params picUrl string 艾特的手机号
  43. //@params messageUrl string 是否艾特全体
  44. //@return: err error
  45. func (e *NotifyService) SendLinkMessage(content, title, picUrl, messageUrl string) (err error) {
  46. msg := map[string]interface{}{
  47. "msgtype": "link",
  48. "link": map[string]string{
  49. "text": content,
  50. "title": title,
  51. "picUrl": picUrl,
  52. "messageUrl": messageUrl,
  53. },
  54. }
  55. return SendMessage(msg)
  56. }
  57. //@author: [Espoir](https://github.com/nightsimon)
  58. //@function: SendMarkdownMessage
  59. //@description: 发送钉钉Markdown信息
  60. //@params content 发送的文字内容
  61. //@params title 发送的标题
  62. //@params atMobiles []string 艾特的手机号
  63. //@params isAtAll bool 是否艾特全体
  64. //@return: err error
  65. func (e *NotifyService) SendMarkdownMessage(content, title string, atMobiles []string, isAtAll bool) (err error) {
  66. msg := map[string]interface{}{
  67. "msgtype": "markdown",
  68. "markdown": map[string]string{
  69. "text": content,
  70. "title": title,
  71. },
  72. "at": map[string]interface{}{
  73. "atMobiles": atMobiles,
  74. "isAtAll": isAtAll,
  75. },
  76. }
  77. return SendMessage(msg)
  78. }
  79. func SendMessage(msg interface{}) error {
  80. body := bytes.NewBuffer(nil)
  81. err := json.NewEncoder(body).Encode(msg)
  82. if err != nil {
  83. return fmt.Errorf("msg json failed, msg: %v, err: %v", msg, err.Error())
  84. }
  85. value := url.Values{}
  86. value.Set("access_token", global.GlobalConfig_.Token)
  87. if global.GlobalConfig_.Secret != "" {
  88. t := time.Now().UnixNano() / 1e6
  89. value.Set("timestamp", fmt.Sprintf("%d", t))
  90. value.Set("sign", sign(t, global.GlobalConfig_.Secret))
  91. }
  92. request, err := http.NewRequest(http.MethodPost, global.GlobalConfig_.Url, body)
  93. if err != nil {
  94. return fmt.Errorf("error request: %v", err.Error())
  95. }
  96. request.URL.RawQuery = value.Encode()
  97. request.Header.Add("Content-Type", "application/json")
  98. res, err := (&http.Client{}).Do(request)
  99. if err != nil {
  100. return fmt.Errorf("send dingTalk message failed, error: %v", err.Error())
  101. }
  102. defer func() { _ = res.Body.Close() }()
  103. result, err := ioutil.ReadAll(res.Body)
  104. if res.StatusCode != 200 {
  105. return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "http code is not 200"))
  106. }
  107. if err != nil {
  108. return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
  109. }
  110. type response struct {
  111. ErrCode int `json:"errcode"`
  112. }
  113. var ret response
  114. if err := json.Unmarshal(result, &ret); err != nil {
  115. return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
  116. }
  117. if ret.ErrCode != 0 {
  118. return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "errcode is not 0"))
  119. }
  120. return nil
  121. }
  122. func httpError(request *http.Request, response *http.Response, body []byte, error string) string {
  123. return fmt.Sprintf(
  124. "http request failure, error: %s, status code: %d, %s %s, body:\n%s",
  125. error,
  126. response.StatusCode,
  127. request.Method,
  128. request.URL.String(),
  129. string(body),
  130. )
  131. }
  132. func sign(t int64, secret string) string {
  133. strToHash := fmt.Sprintf("%d\n%s", t, secret)
  134. hmac256 := hmac.New(sha256.New, []byte(secret))
  135. hmac256.Write([]byte(strToHash))
  136. data := hmac256.Sum(nil)
  137. return base64.StdEncoding.EncodeToString(data)
  138. }
  139. // 其余方法请参考 https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f8710afbfzduV#topic-2026027