ding.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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, channel_ *model.Channel) error {
  35. // https://open.dingtalk.com/document/robots/custom-robot-access#title-72m-8ag-pqw
  36. messageRequest := dingMessageRequest{
  37. MessageType: "text",
  38. }
  39. if message.Content == "" {
  40. messageRequest.MessageType = "text"
  41. messageRequest.Text.Content = message.Description
  42. } else {
  43. messageRequest.MessageType = "markdown"
  44. messageRequest.Markdown.Title = message.Title
  45. messageRequest.Markdown.Text = message.Content
  46. }
  47. if message.To != "" {
  48. if message.To == "@all" {
  49. messageRequest.At.IsAtAll = true
  50. } else {
  51. messageRequest.At.AtUserIds = strings.Split(message.To, "|")
  52. }
  53. }
  54. timestamp := time.Now().UnixMilli()
  55. sign, err := dingSign(channel_.Secret, timestamp)
  56. if err != nil {
  57. return err
  58. }
  59. jsonData, err := json.Marshal(messageRequest)
  60. if err != nil {
  61. return err
  62. }
  63. resp, err := http.Post(fmt.Sprintf("%s&timestamp=%d&sign=%s", channel_.URL, timestamp, sign), "application/json",
  64. bytes.NewBuffer(jsonData))
  65. if err != nil {
  66. return err
  67. }
  68. var res dingMessageResponse
  69. err = json.NewDecoder(resp.Body).Decode(&res)
  70. if err != nil {
  71. return err
  72. }
  73. if res.Code != 0 {
  74. return errors.New(res.Message)
  75. }
  76. return nil
  77. }
  78. func dingSign(secret string, timestamp int64) (string, error) {
  79. // https://open.dingtalk.com/document/robots/customize-robot-security-settings
  80. // timestamp + key -> sha256 -> URL encode
  81. stringToSign := fmt.Sprintf("%d\n%s", timestamp, secret)
  82. h := hmac.New(sha256.New, []byte(secret))
  83. _, err := h.Write([]byte(stringToSign))
  84. if err != nil {
  85. return "", err
  86. }
  87. signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
  88. signature = url.QueryEscape(signature)
  89. return signature, nil
  90. }