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 4.3 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. ACTION_QR_STR_SCENE = "QR_STR_SCENE"
  20. ERR_CODE_ACCESSTOKEN_EXPIRE = 42001
  21. ERR_CODE_ACCESSTOKEN_INVALID = 40001
  22. )
  23. type AccessTokenResponse struct {
  24. Access_token string
  25. Expires_in int
  26. }
  27. type QRCodeResponse struct {
  28. Ticket string `json:"ticket"`
  29. Expire_Seconds int `json:"expire_seconds"`
  30. Url string `json:"url"`
  31. }
  32. type QRCodeRequest struct {
  33. Action_name string `json:"action_name"`
  34. Action_info ActionInfo `json:"action_info"`
  35. Expire_seconds int `json:"expire_seconds"`
  36. }
  37. type MaterialRequest struct {
  38. Type string `json:"type"`
  39. Offset int `json:"offset"`
  40. Count int `json:"count"`
  41. }
  42. type ActionInfo struct {
  43. Scene Scene `json:"scene"`
  44. }
  45. type Scene struct {
  46. Scene_str string `json:"scene_str"`
  47. }
  48. type ErrorResponse struct {
  49. Errcode int
  50. Errmsg string
  51. }
  52. func getWechatRestyClient() *resty.Client {
  53. if client == nil {
  54. client = resty.New()
  55. client.SetTimeout(time.Duration(setting.WechatApiTimeoutSeconds) * time.Second)
  56. }
  57. return client
  58. }
  59. // api doc:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  60. func callAccessToken() *AccessTokenResponse {
  61. client := getWechatRestyClient()
  62. log.Info("start to get wechat access token")
  63. var result AccessTokenResponse
  64. _, err := client.R().
  65. SetQueryParam("grant_type", GRANT_TYPE).
  66. SetQueryParam("appid", setting.WechatAppId).
  67. SetQueryParam("secret", setting.WechatAppSecret).
  68. SetResult(&result).
  69. Get(setting.WechatApiHost + ACCESS_TOKEN_PATH)
  70. if err != nil {
  71. log.Error("get wechat access token failed,e=%v", err)
  72. return nil
  73. }
  74. log.Info("get wechat access token result=%v", result)
  75. return &result
  76. }
  77. //callQRCodeCreate call the wechat api to create qr-code,
  78. // api doc: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
  79. func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
  80. client := getWechatRestyClient()
  81. body := &QRCodeRequest{
  82. Action_name: ACTION_QR_STR_SCENE,
  83. Action_info: ActionInfo{Scene: Scene{Scene_str: sceneStr}},
  84. Expire_seconds: setting.WechatQRCodeExpireSeconds,
  85. }
  86. bodyJson, _ := json.Marshal(body)
  87. var result QRCodeResponse
  88. r, err := client.R().
  89. SetHeader("Content-Type", "application/json").
  90. SetQueryParam("access_token", GetWechatAccessToken()).
  91. SetBody(bodyJson).
  92. SetResult(&result).
  93. Post(setting.WechatApiHost + QR_CODE_PATH)
  94. if err != nil {
  95. log.Error("create QR code failed,e=%v", err)
  96. return nil, false
  97. }
  98. errCode := getErrorCodeFromResponse(r)
  99. if errCode == ERR_CODE_ACCESSTOKEN_EXPIRE || errCode == ERR_CODE_ACCESSTOKEN_INVALID {
  100. return nil, true
  101. }
  102. if result.Url == "" {
  103. return nil, false
  104. }
  105. log.Info("%v", r)
  106. return &result, false
  107. }
  108. //getMaterial
  109. // api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
  110. func getMaterial(mType string, offset, count int) (interface{}, bool) {
  111. client := getWechatRestyClient()
  112. body := &MaterialRequest{
  113. Type: mType,
  114. Offset: offset,
  115. Count: count,
  116. }
  117. bodyJson, _ := json.Marshal(body)
  118. r, err := client.R().
  119. SetHeader("Content-Type", "application/json").
  120. SetQueryParam("access_token", GetWechatAccessToken()).
  121. SetBody(bodyJson).
  122. Post(setting.WechatApiHost + GET_MATERIAL_PATH)
  123. if err != nil {
  124. log.Error("create QR code failed,e=%v", err)
  125. return nil, false
  126. }
  127. a := r.Body()
  128. resultMap := make(map[string]interface{}, 0)
  129. json.Unmarshal(a, &resultMap)
  130. errcode := resultMap["errcode"]
  131. if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
  132. return nil, true
  133. }
  134. log.Info("%v", r)
  135. return &resultMap, false
  136. }
  137. func getErrorCodeFromResponse(r *resty.Response) int {
  138. a := r.Body()
  139. resultMap := make(map[string]interface{}, 0)
  140. json.Unmarshal(a, &resultMap)
  141. code := resultMap["errcode"]
  142. if code == nil {
  143. return -1
  144. }
  145. c, _ := strconv.Atoi(fmt.Sprint(code))
  146. return c
  147. }