ding.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package channel
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/sha256"
  6. "encoding/base64"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "message-pusher/model"
  11. "net/http"
  12. "net/url"
  13. "strings"
  14. "time"
  15. )
  16. type dingMessageRequest struct {
  17. MessageType string `json:"msgtype"`
  18. Text struct {
  19. Content string `json:"content"`
  20. } `json:"text"`
  21. Markdown struct {
  22. Title string `json:"title"`
  23. Text string `json:"text"`
  24. } `json:"markdown"`
  25. At struct {
  26. AtUserIds []string `json:"atUserIds"`
  27. IsAtAll bool `json:"isAtAll"`
  28. }
  29. }
  30. type dingMessageResponse struct {
  31. Code int `json:"errcode"`
  32. Message string `json:"errmsg"`
  33. }
  34. func SendDingMessage(message *model.Message, user *model.User) error {
  35. // https://open.dingtalk.com/document/robots/custom-robot-access#title-72m-8ag-pqw
  36. if user.DingWebhookURL == "" {
  37. return errors.New("未配置钉钉群机器人消息推送方式")
  38. }
  39. messageRequest := dingMessageRequest{
  40. MessageType: "text",
  41. }
  42. if message.Content == "" {
  43. messageRequest.MessageType = "text"
  44. messageRequest.Text.Content = message.Description
  45. } else {
  46. messageRequest.MessageType = "markdown"
  47. messageRequest.Markdown.Title = message.Title
  48. messageRequest.Markdown.Text = message.Content
  49. }
  50. if message.To != "" {
  51. if message.To == "@all" {
  52. messageRequest.At.IsAtAll = true
  53. } else {
  54. messageRequest.At.AtUserIds = strings.Split(message.To, "|")
  55. }
  56. }
  57. timestamp := time.Now().UnixMilli()
  58. sign, err := dingSign(user.DingWebhookSecret, timestamp)
  59. if err != nil {
  60. return err
  61. }
  62. jsonData, err := json.Marshal(messageRequest)
  63. if err != nil {
  64. return err
  65. }
  66. resp, err := http.Post(fmt.Sprintf("%s&timestamp=%d&sign=%s", user.DingWebhookURL, timestamp, sign), "application/json",
  67. bytes.NewBuffer(jsonData))
  68. if err != nil {
  69. return err
  70. }
  71. var res dingMessageResponse
  72. err = json.NewDecoder(resp.Body).Decode(&res)
  73. if err != nil {
  74. return err
  75. }
  76. if res.Code != 0 {
  77. return errors.New(res.Message)
  78. }
  79. return nil
  80. }
  81. func dingSign(secret string, timestamp int64) (string, error) {
  82. // https://open.dingtalk.com/document/robots/customize-robot-security-settings
  83. // timestamp + key -> sha256 -> URL encode
  84. stringToSign := fmt.Sprintf("%d\n%s", timestamp, secret)
  85. h := hmac.New(sha256.New, []byte(secret))
  86. _, err := h.Write([]byte(stringToSign))
  87. if err != nil {
  88. return "", err
  89. }
  90. signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
  91. signature = url.QueryEscape(signature)
  92. return signature, nil
  93. }