1
0

lark-app.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package channel
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "message-pusher/common"
  8. "message-pusher/model"
  9. "net/http"
  10. "strings"
  11. )
  12. type larkAppTokenRequest struct {
  13. AppID string `json:"app_id"`
  14. AppSecret string `json:"app_secret"`
  15. }
  16. type larkAppTokenResponse struct {
  17. Code int `json:"code"`
  18. Msg string `json:"msg"`
  19. TenantAccessToken string `json:"tenant_access_token"`
  20. Expire int `json:"expire"`
  21. }
  22. type LarkAppTokenStoreItem struct {
  23. AppID string
  24. AppSecret string
  25. AccessToken string
  26. }
  27. func (i *LarkAppTokenStoreItem) Key() string {
  28. return i.AppID + i.AppSecret
  29. }
  30. func (i *LarkAppTokenStoreItem) IsShared() bool {
  31. var count int64 = 0
  32. model.DB.Model(&model.Channel{}).Where("secret = ? and app_id = ? and type = ?",
  33. i.AppSecret, i.AppID, model.TypeLarkApp).Count(&count)
  34. return count > 1
  35. }
  36. func (i *LarkAppTokenStoreItem) IsFilled() bool {
  37. return i.AppID != "" && i.AppSecret != ""
  38. }
  39. func (i *LarkAppTokenStoreItem) Token() string {
  40. return i.AccessToken
  41. }
  42. func (i *LarkAppTokenStoreItem) Refresh() {
  43. // https://open.feishu.cn/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/auth-v3/auth/tenant_access_token_internal
  44. tokenRequest := larkAppTokenRequest{
  45. AppID: i.AppID,
  46. AppSecret: i.AppSecret,
  47. }
  48. tokenRequestData, err := json.Marshal(tokenRequest)
  49. responseData, err := http.Post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
  50. "application/json; charset=utf-8", bytes.NewBuffer(tokenRequestData))
  51. if err != nil {
  52. common.SysError("failed to refresh access token: " + err.Error())
  53. return
  54. }
  55. defer responseData.Body.Close()
  56. var res larkAppTokenResponse
  57. err = json.NewDecoder(responseData.Body).Decode(&res)
  58. if err != nil {
  59. common.SysError("failed to decode larkAppTokenResponse: " + err.Error())
  60. return
  61. }
  62. if res.Code != 0 {
  63. common.SysError(res.Msg)
  64. return
  65. }
  66. i.AccessToken = res.TenantAccessToken
  67. common.SysLog("access token refreshed")
  68. }
  69. type larkAppMessageRequest struct {
  70. ReceiveId string `json:"receive_id"`
  71. MsgType string `json:"msg_type"`
  72. Content string `json:"content"`
  73. }
  74. type larkAppMessageResponse struct {
  75. Code int `json:"code"`
  76. Msg string `json:"msg"`
  77. }
  78. func parseLarkAppTarget(target string) (string, string, error) {
  79. parts := strings.Split(target, ":")
  80. if len(parts) != 2 {
  81. return "", "", errors.New("无效的飞书应用号消息接收者参数")
  82. }
  83. return parts[0], parts[1], nil
  84. }
  85. func SendLarkAppMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
  86. // https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create
  87. rawTarget := message.To
  88. if rawTarget == "" {
  89. rawTarget = channel_.AccountId
  90. }
  91. targetType, target, err := parseLarkAppTarget(rawTarget)
  92. if err != nil {
  93. return err
  94. }
  95. request := larkAppMessageRequest{
  96. ReceiveId: target,
  97. }
  98. atPrefix := getLarkAtPrefix(message)
  99. if message.Description != "" {
  100. request.MsgType = "text"
  101. content := larkTextContent{Text: atPrefix + message.Description}
  102. contentData, err := json.Marshal(content)
  103. if err != nil {
  104. return err
  105. }
  106. request.Content = string(contentData)
  107. } else {
  108. request.MsgType = "interactive"
  109. content := larkCardContent{}
  110. content.Config.WideScreenMode = true
  111. content.Config.EnableForward = true
  112. content.Elements = append(content.Elements, larkMessageRequestCardElement{
  113. Tag: "div",
  114. Text: larkMessageRequestCardElementText{
  115. Content: atPrefix + message.Content,
  116. Tag: "lark_md",
  117. },
  118. })
  119. contentData, err := json.Marshal(content)
  120. if err != nil {
  121. return err
  122. }
  123. request.Content = string(contentData)
  124. }
  125. requestData, err := json.Marshal(request)
  126. if err != nil {
  127. return err
  128. }
  129. key := fmt.Sprintf("%s%s", channel_.AppId, channel_.Secret)
  130. accessToken := TokenStoreGetToken(key)
  131. url := fmt.Sprintf("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=%s", targetType)
  132. req, _ := http.NewRequest("POST", url, bytes.NewReader(requestData))
  133. req.Header.Set("Authorization", "Bearer "+accessToken)
  134. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  135. resp, err := http.DefaultClient.Do(req)
  136. if err != nil {
  137. return err
  138. }
  139. var res larkAppMessageResponse
  140. err = json.NewDecoder(resp.Body).Decode(&res)
  141. if err != nil {
  142. return err
  143. }
  144. if res.Code != 0 {
  145. return errors.New(res.Msg)
  146. }
  147. return nil
  148. }