lark.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. type larkMessageRequestCardElementText struct {
  17. Content string `json:"content"`
  18. Tag string `json:"tag"`
  19. }
  20. type larkMessageRequestCardElement struct {
  21. Tag string `json:"tag"`
  22. Text larkMessageRequestCardElementText `json:"text"`
  23. }
  24. type larkTextContent struct {
  25. Text string `json:"text"`
  26. }
  27. type larkCardContent struct {
  28. Config struct {
  29. WideScreenMode bool `json:"wide_screen_mode"`
  30. EnableForward bool `json:"enable_forward"`
  31. }
  32. Elements []larkMessageRequestCardElement `json:"elements"`
  33. }
  34. type larkMessageRequest struct {
  35. MessageType string `json:"msg_type"`
  36. Timestamp string `json:"timestamp"`
  37. Sign string `json:"sign"`
  38. Content larkTextContent `json:"content"`
  39. Card larkCardContent `json:"card"`
  40. }
  41. type larkMessageResponse struct {
  42. Code int `json:"code"`
  43. Message string `json:"msg"`
  44. }
  45. func getLarkAtPrefix(message *model.Message) string {
  46. atPrefix := ""
  47. if message.To != "" {
  48. if message.To == "@all" {
  49. atPrefix = "<at user_id=\"all\">所有人</at>"
  50. } else {
  51. ids := strings.Split(message.To, "|")
  52. for _, id := range ids {
  53. atPrefix += fmt.Sprintf("<at user_id=\"%s\"> </at>", id)
  54. }
  55. }
  56. }
  57. return atPrefix
  58. }
  59. func SendLarkMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
  60. // https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN#e1cdee9f
  61. messageRequest := larkMessageRequest{
  62. MessageType: "text",
  63. }
  64. atPrefix := getLarkAtPrefix(message)
  65. if message.Content == "" {
  66. messageRequest.MessageType = "text"
  67. messageRequest.Content.Text = atPrefix + message.Description
  68. } else {
  69. messageRequest.MessageType = "interactive"
  70. messageRequest.Card.Config.WideScreenMode = true
  71. messageRequest.Card.Config.EnableForward = true
  72. messageRequest.Card.Elements = append(messageRequest.Card.Elements, larkMessageRequestCardElement{
  73. Tag: "div",
  74. Text: larkMessageRequestCardElementText{
  75. Content: atPrefix + message.Content,
  76. Tag: "lark_md",
  77. },
  78. })
  79. }
  80. now := time.Now()
  81. timestamp := now.Unix()
  82. sign, err := larkSign(channel_.Secret, timestamp)
  83. if err != nil {
  84. return err
  85. }
  86. messageRequest.Sign = sign
  87. messageRequest.Timestamp = strconv.FormatInt(timestamp, 10)
  88. jsonData, err := json.Marshal(messageRequest)
  89. if err != nil {
  90. return err
  91. }
  92. resp, err := http.Post(channel_.URL, "application/json",
  93. bytes.NewBuffer(jsonData))
  94. if err != nil {
  95. return err
  96. }
  97. var res larkMessageResponse
  98. err = json.NewDecoder(resp.Body).Decode(&res)
  99. if err != nil {
  100. return err
  101. }
  102. if res.Code != 0 {
  103. return errors.New(res.Message)
  104. }
  105. return nil
  106. }
  107. func larkSign(secret string, timestamp int64) (string, error) {
  108. // https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN
  109. // timestamp + key -> sha256 -> base64 encode
  110. stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secret
  111. var data []byte
  112. h := hmac.New(sha256.New, []byte(stringToSign))
  113. _, err := h.Write(data)
  114. if err != nil {
  115. return "", err
  116. }
  117. signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
  118. return signature, nil
  119. }