You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 5.6 kB

3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package wechat
  2. import (
  3. "code.gitea.io/gitea/modules/log"
  4. "code.gitea.io/gitea/modules/setting"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/go-resty/resty/v2"
  8. "strconv"
  9. "time"
  10. )
  11. var (
  12. client *resty.Client
  13. )
  14. const (
  15. GRANT_TYPE = "client_credential"
  16. ACCESS_TOKEN_PATH = "/cgi-bin/token"
  17. QR_CODE_PATH = "/cgi-bin/qrcode/create"
  18. GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material"
  19. SEND_TEMPLATE_PATH = "/cgi-bin/message/template/send"
  20. ACTION_QR_STR_SCENE = "QR_STR_SCENE"
  21. ERR_CODE_ACCESSTOKEN_EXPIRE = 42001
  22. ERR_CODE_ACCESSTOKEN_INVALID = 40001
  23. )
  24. type AccessTokenResponse struct {
  25. Access_token string
  26. Expires_in int
  27. }
  28. type QRCodeResponse struct {
  29. Ticket string `json:"ticket"`
  30. Expire_Seconds int `json:"expire_seconds"`
  31. Url string `json:"url"`
  32. }
  33. type QRCodeRequest struct {
  34. Action_name string `json:"action_name"`
  35. Action_info ActionInfo `json:"action_info"`
  36. Expire_seconds int `json:"expire_seconds"`
  37. }
  38. type MaterialRequest struct {
  39. Type string `json:"type"`
  40. Offset int `json:"offset"`
  41. Count int `json:"count"`
  42. }
  43. type TemplateMsgRequest struct {
  44. ToUser string `json:"touser"`
  45. TemplateId string `json:"template_id"`
  46. Url string `json:"url"`
  47. ClientMsgId string `json:"client_msg_id"`
  48. Data interface{} `json:"data"`
  49. }
  50. type TemplateValue struct {
  51. Value string `json:"value"`
  52. Color string `json:"color"`
  53. }
  54. type CloudbrainTaskData struct {
  55. First TemplateValue `json:"first"`
  56. Keyword1 TemplateValue `json:"keyword1"`
  57. Keyword2 TemplateValue `json:"keyword2"`
  58. Keyword3 TemplateValue `json:"keyword3"`
  59. Remark TemplateValue `json:"remark"`
  60. }
  61. type ActionInfo struct {
  62. Scene Scene `json:"scene"`
  63. }
  64. type Scene struct {
  65. Scene_str string `json:"scene_str"`
  66. }
  67. type ErrorResponse struct {
  68. Errcode int
  69. Errmsg string
  70. }
  71. func getWechatRestyClient() *resty.Client {
  72. if client == nil {
  73. client = resty.New()
  74. client.SetTimeout(time.Duration(setting.WechatApiTimeoutSeconds) * time.Second)
  75. }
  76. return client
  77. }
  78. // api doc:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  79. func callAccessToken() *AccessTokenResponse {
  80. client := getWechatRestyClient()
  81. var result AccessTokenResponse
  82. _, err := client.R().
  83. SetQueryParam("grant_type", GRANT_TYPE).
  84. SetQueryParam("appid", setting.WechatAppId).
  85. SetQueryParam("secret", setting.WechatAppSecret).
  86. SetResult(&result).
  87. Get(setting.WechatApiHost + ACCESS_TOKEN_PATH)
  88. if err != nil {
  89. log.Error("get wechat access token failed,e=%v", err)
  90. return nil
  91. }
  92. return &result
  93. }
  94. //callQRCodeCreate call the wechat api to create qr-code,
  95. // api doc: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
  96. func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
  97. client := getWechatRestyClient()
  98. body := &QRCodeRequest{
  99. Action_name: ACTION_QR_STR_SCENE,
  100. Action_info: ActionInfo{Scene: Scene{Scene_str: sceneStr}},
  101. Expire_seconds: setting.WechatQRCodeExpireSeconds,
  102. }
  103. bodyJson, _ := json.Marshal(body)
  104. var result QRCodeResponse
  105. r, err := client.R().
  106. SetHeader("Content-Type", "application/json").
  107. SetQueryParam("access_token", GetWechatAccessToken()).
  108. SetBody(bodyJson).
  109. SetResult(&result).
  110. Post(setting.WechatApiHost + QR_CODE_PATH)
  111. if err != nil {
  112. log.Error("create QR code failed,e=%v", err)
  113. return nil, false
  114. }
  115. errCode := getErrorCodeFromResponse(r)
  116. if errCode == ERR_CODE_ACCESSTOKEN_EXPIRE || errCode == ERR_CODE_ACCESSTOKEN_INVALID {
  117. return nil, true
  118. }
  119. if result.Url == "" {
  120. return nil, false
  121. }
  122. log.Info("%v", r)
  123. return &result, false
  124. }
  125. //getMaterial
  126. // api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
  127. func getMaterial(mType string, offset, count int) (interface{}, bool) {
  128. client := getWechatRestyClient()
  129. body := &MaterialRequest{
  130. Type: mType,
  131. Offset: offset,
  132. Count: count,
  133. }
  134. bodyJson, _ := json.Marshal(body)
  135. r, err := client.R().
  136. SetHeader("Content-Type", "application/json").
  137. SetQueryParam("access_token", GetWechatAccessToken()).
  138. SetBody(bodyJson).
  139. Post(setting.WechatApiHost + GET_MATERIAL_PATH)
  140. if err != nil {
  141. log.Error("create QR code failed,e=%v", err)
  142. return nil, false
  143. }
  144. a := r.Body()
  145. resultMap := make(map[string]interface{}, 0)
  146. json.Unmarshal(a, &resultMap)
  147. errcode := resultMap["errcode"]
  148. if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
  149. return nil, true
  150. }
  151. log.Info("%v", r)
  152. return &resultMap, false
  153. }
  154. func getErrorCodeFromResponse(r *resty.Response) int {
  155. a := r.Body()
  156. resultMap := make(map[string]interface{}, 0)
  157. json.Unmarshal(a, &resultMap)
  158. code := resultMap["errcode"]
  159. if code == nil {
  160. return -1
  161. }
  162. c, _ := strconv.Atoi(fmt.Sprint(code))
  163. return c
  164. }
  165. func sendTemplateMsg(req TemplateMsgRequest) (error, bool) {
  166. client := getWechatRestyClient()
  167. bodyJson, _ := json.Marshal(req)
  168. r, err := client.R().
  169. SetHeader("Content-Type", "application/json").
  170. SetQueryParam("access_token", GetWechatAccessToken()).
  171. SetBody(bodyJson).
  172. Post(setting.WechatApiHost + SEND_TEMPLATE_PATH)
  173. if err != nil {
  174. log.Error("sendTemplateMsg,e=%v", err)
  175. return nil, false
  176. }
  177. a := r.Body()
  178. resultMap := make(map[string]interface{}, 0)
  179. json.Unmarshal(a, &resultMap)
  180. errcode := resultMap["errcode"]
  181. log.Info("sendTemplateMsg,%v", r)
  182. if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
  183. return nil, true
  184. }
  185. return nil, false
  186. }