wechat-corp-account.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. "time"
  12. )
  13. type wechatCorpAccountResponse struct {
  14. ErrorCode int `json:"errcode"`
  15. ErrorMessage string `json:"errmsg"`
  16. AccessToken string `json:"access_token"`
  17. ExpiresIn int `json:"expires_in"`
  18. }
  19. type WeChatCorpAccountTokenStoreItem struct {
  20. CorpId string
  21. AgentSecret string
  22. AgentId string
  23. AccessToken string
  24. }
  25. func (i *WeChatCorpAccountTokenStoreItem) Key() string {
  26. return i.CorpId + i.AgentId + i.AgentSecret
  27. }
  28. func (i *WeChatCorpAccountTokenStoreItem) IsShared() bool {
  29. appId := fmt.Sprintf("%s|%s", i.CorpId, i.AgentId)
  30. var count int64 = 0
  31. model.DB.Model(&model.Channel{}).Where("secret = ? and app_id = ? and type = ?",
  32. i.AgentSecret, appId, model.TypeWeChatCorpAccount).Count(&count)
  33. return count > 1
  34. }
  35. func (i *WeChatCorpAccountTokenStoreItem) IsFilled() bool {
  36. return i.CorpId != "" && i.AgentSecret != "" && i.AgentId != ""
  37. }
  38. func (i *WeChatCorpAccountTokenStoreItem) Token() string {
  39. return i.AccessToken
  40. }
  41. func (i *WeChatCorpAccountTokenStoreItem) Refresh() {
  42. // https://work.weixin.qq.com/api/doc/90000/90135/91039
  43. client := http.Client{
  44. Timeout: 5 * time.Second,
  45. }
  46. req, err := http.NewRequest("GET", fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s",
  47. i.CorpId, i.AgentSecret), nil)
  48. if err != nil {
  49. common.SysError(err.Error())
  50. return
  51. }
  52. responseData, err := client.Do(req)
  53. if err != nil {
  54. common.SysError("failed to refresh access token: " + err.Error())
  55. return
  56. }
  57. defer responseData.Body.Close()
  58. var res wechatCorpAccountResponse
  59. err = json.NewDecoder(responseData.Body).Decode(&res)
  60. if err != nil {
  61. common.SysError("failed to decode wechatCorpAccountResponse: " + err.Error())
  62. return
  63. }
  64. if res.ErrorCode != 0 {
  65. common.SysError(res.ErrorMessage)
  66. return
  67. }
  68. i.AccessToken = res.AccessToken
  69. common.SysLog("access token refreshed")
  70. }
  71. type wechatCorpMessageRequest struct {
  72. MessageType string `json:"msgtype"`
  73. ToUser string `json:"touser"`
  74. AgentId string `json:"agentid"`
  75. TextCard struct {
  76. Title string `json:"title"`
  77. Description string `json:"description"`
  78. URL string `json:"url"`
  79. } `json:"textcard"`
  80. Text struct {
  81. Content string `json:"content"`
  82. } `json:"text"`
  83. Markdown struct {
  84. Content string `json:"content"`
  85. } `json:"markdown"`
  86. }
  87. type wechatCorpMessageResponse struct {
  88. ErrorCode int `json:"errcode"`
  89. ErrorMessage string `json:"errmsg"`
  90. }
  91. func parseWechatCorpAccountAppId(appId string) (string, string, error) {
  92. parts := strings.Split(appId, "|")
  93. if len(parts) != 2 {
  94. return "", "", errors.New("无效的微信企业号配置")
  95. }
  96. return parts[0], parts[1], nil
  97. }
  98. func SendWeChatCorpMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
  99. // https://developer.work.weixin.qq.com/document/path/90236
  100. corpId, agentId, err := parseWechatCorpAccountAppId(channel_.AppId)
  101. if err != nil {
  102. return err
  103. }
  104. userId := channel_.AccountId
  105. clientType := channel_.Other
  106. agentSecret := channel_.Secret
  107. messageRequest := wechatCorpMessageRequest{
  108. ToUser: userId,
  109. AgentId: agentId,
  110. }
  111. if message.To != "" {
  112. messageRequest.ToUser = message.To
  113. }
  114. if message.Content == "" {
  115. if message.Title == "" {
  116. messageRequest.MessageType = "text"
  117. messageRequest.Text.Content = message.Description
  118. } else {
  119. messageRequest.MessageType = "textcard"
  120. messageRequest.TextCard.Title = message.Title
  121. messageRequest.TextCard.Description = message.Description
  122. messageRequest.TextCard.URL = common.ServerAddress
  123. }
  124. } else {
  125. if clientType == "plugin" {
  126. messageRequest.MessageType = "textcard"
  127. messageRequest.TextCard.Title = message.Title
  128. messageRequest.TextCard.Description = message.Description
  129. messageRequest.TextCard.URL = message.URL
  130. } else {
  131. messageRequest.MessageType = "markdown"
  132. messageRequest.Markdown.Content = message.Content
  133. }
  134. }
  135. jsonData, err := json.Marshal(messageRequest)
  136. if err != nil {
  137. return err
  138. }
  139. key := fmt.Sprintf("%s%s%s", corpId, agentId, agentSecret)
  140. accessToken := TokenStoreGetToken(key)
  141. resp, err := http.Post(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s", accessToken), "application/json",
  142. bytes.NewBuffer(jsonData))
  143. if err != nil {
  144. return err
  145. }
  146. var res wechatCorpMessageResponse
  147. err = json.NewDecoder(resp.Body).Decode(&res)
  148. if err != nil {
  149. return err
  150. }
  151. if res.ErrorCode != 0 {
  152. return errors.New(res.ErrorMessage)
  153. }
  154. return nil
  155. }