client.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package wechat
  2. import (
  3. "encoding/json"
  4. "net/url"
  5. "time"
  6. )
  7. // Client wechat
  8. type Client struct {
  9. AppID string `json:"appid"`
  10. AppSecret string `json:"appsecret"`
  11. Token string `json:"token"`
  12. EncodingAESKey string `json:"encodingaeskey"`
  13. AccessToken string
  14. LastTokenTime int64
  15. }
  16. // FormAuthorize get code
  17. type FormAuthorize struct {
  18. Code string `form:"code"`
  19. State string `form:"state"`
  20. URL string `form:"url"`
  21. }
  22. // FormOpenID openid
  23. type FormOpenID struct {
  24. OpenID string `form:"openid"`
  25. }
  26. // FormMaterial Material
  27. type FormMaterial struct {
  28. Type string `form:"type"`
  29. Offset int `form:"offset"`
  30. Count int `form:"count"`
  31. }
  32. // UserInfo userinfo
  33. type UserInfo struct {
  34. SubScribe int `json:"subscribe"`
  35. OpenID string `json:"openid"`
  36. NickName string `json:"nickname"`
  37. Sex int `json:"sex"`
  38. Language string `json:"language"`
  39. City string `json:"city"`
  40. Province string `json:"province"`
  41. Country string `json:"country"`
  42. HeadImgURL string `json:"headimgurl"`
  43. SubscribeTime int `json:"subscribe_time"`
  44. Remark string `json:"remark"`
  45. GroupID int `json:"groupid"`
  46. TagidList []int `json:"tagid_list"`
  47. SubscribeScene string `json:"subscribe_scene"`
  48. QrScene int `json:"qr_scene"`
  49. QrSceneStr string `json:"qr_scene_str"`
  50. }
  51. // List openid list
  52. type List struct {
  53. OpenID []string `json:"openid"`
  54. }
  55. // UserList user list
  56. type UserList struct {
  57. Total int `json:"total"`
  58. Count int `json:"count"`
  59. Data List `json:"data"`
  60. NextOpenID string `json:"next_openid"`
  61. }
  62. type newsitem struct {
  63. Title string `json:"title"`
  64. Author string `json:"author"`
  65. Digest string `json:"digest"`
  66. Content string `json:"content"`
  67. ContentSourceURL string `json:"content_source_url"`
  68. ThumbMediaID string `json:"thumb_media_id"`
  69. ShowCoverPic int `json:"show_cover_pic"`
  70. URL string `json:"url"`
  71. ThumbURL string `json:"thumb_url"`
  72. NeedOpenComment int `json:"need_open_comment"`
  73. OnlyFansCanComment int `json:"only_fans_can_comment"`
  74. }
  75. type content struct {
  76. NewsItem []*newsitem `json:"news_item,omitempty"`
  77. CreateTime int `json:"create_time,omitempty"`
  78. UpdateTime int `json:"update_time,omitempty"`
  79. }
  80. type item struct {
  81. MediaID string `json:"media_id"`
  82. Name string `json:"name,omitempty"`
  83. UpdateTime int `json:"update_time"`
  84. URL string `json:"url,omitempty"`
  85. Content *content `json:"content,omitempty"`
  86. }
  87. // Material 素材
  88. type Material struct {
  89. TotalCount int `json:"total_count"`
  90. ItemCount int `json:"item_count"`
  91. Item []item `json:"item"`
  92. }
  93. // MiniProgramPage mini
  94. type MiniProgramPage struct {
  95. AppID string `json:"appid,omitempty"`
  96. PagePath string `json:"pagepath,omitempty"`
  97. }
  98. // ValueColor value color
  99. type ValueColor struct {
  100. Value string `json:"value"`
  101. Color string `json:"color,omitempty"`
  102. }
  103. // TemplateData data
  104. type TemplateData struct {
  105. First *ValueColor `json:"first"`
  106. Keyword1 *ValueColor `json:"keyword1,omitempty"`
  107. Keyword2 *ValueColor `json:"keyword2,omitempty"`
  108. Keyword3 *ValueColor `json:"keyword3,omitempty"`
  109. Keyword4 *ValueColor `json:"keyword4,omitempty"`
  110. Keyword5 *ValueColor `json:"keyword5,omitempty"`
  111. Remark *ValueColor `json:"remark,omitempty"`
  112. }
  113. // TemplateMessage template message
  114. type TemplateMessage struct {
  115. ToUser string `json:"touser"`
  116. TemplateID string `json:"template_id"`
  117. URL string `json:"url"`
  118. MiniProgram *MiniProgramPage `json:"miniprogram,omitempty"`
  119. Data TemplateData `json:"data"`
  120. }
  121. func key(appid string) string {
  122. return "wechat:client:" + appid
  123. }
  124. // NewClient new client
  125. func NewClient(appID, appSecret, token, encodingAESKey string) *Client {
  126. key := key(appID)
  127. if v, ok := cache.Load(key); ok {
  128. return v.(*Client)
  129. }
  130. c := &Client{AppID: appID, AppSecret: appSecret, Token: token, EncodingAESKey: encodingAESKey}
  131. cache.Store(key, c)
  132. return c
  133. }
  134. // getToken get token
  135. func (wc *Client) getToken() (token string, err error) {
  136. now := time.Now().Unix()
  137. if wc.LastTokenTime > 0 {
  138. if now-wc.LastTokenTime < TokenExpires {
  139. token = wc.AccessToken
  140. return
  141. }
  142. }
  143. uri := BaseURL + "/cgi-bin/token?"
  144. args := url.Values{}
  145. args.Add("grant_type", "client_credential")
  146. args.Add("appid", wc.AppID)
  147. args.Add("secret", wc.AppSecret)
  148. uri += args.Encode()
  149. var res Response
  150. if res, err = getJSON(uri); err == nil {
  151. wc.LastTokenTime = now
  152. wc.AccessToken = res.AccessToken
  153. token = wc.AccessToken
  154. key := key(wc.AppID)
  155. cache.Store(key, wc)
  156. }
  157. return
  158. }
  159. // GetCodeURL get code 授权获取 OpenID URL
  160. // /connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
  161. // 转向到
  162. // redirect_uri/?code=CODE&state=STATE
  163. func (wc Client) GetCodeURL(redirectURL, state string) (uri string) {
  164. uri = OpenURL + "/connect/oauth2/authorize?"
  165. args := url.Values{}
  166. args.Add("appid", wc.AppID)
  167. args.Add("redirect_uri", redirectURL)
  168. args.Add("response_type", "code")
  169. args.Add("scope", "snsapi_base")
  170. args.Add("state", state)
  171. uri += args.Encode()
  172. uri += "#wechat_redirect"
  173. return
  174. }
  175. // GetOpenID 获取 OpenID
  176. // /sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
  177. func (wc Client) GetOpenID(code string) (res Response, err error) {
  178. uri := BaseURL + "/sns/oauth2/access_token?"
  179. if wc.AccessToken, err = wc.getToken(); err != nil {
  180. return
  181. }
  182. args := url.Values{}
  183. args.Add("access_token", wc.AccessToken)
  184. args.Add("appid", wc.AppID)
  185. args.Add("secret", wc.AppSecret)
  186. args.Add("code", code)
  187. args.Add("grant_type", "authorization_code")
  188. uri += args.Encode()
  189. res, err = getJSON(uri)
  190. return
  191. }
  192. // GetUserInfo user info
  193. func (wc Client) GetUserInfo(openid string) (res UserInfo, err error) {
  194. uri := BaseURL + "/cgi-bin/user/info?"
  195. if wc.AccessToken, err = wc.getToken(); err != nil {
  196. return
  197. }
  198. args := url.Values{}
  199. args.Add("access_token", wc.AccessToken)
  200. args.Add("openid", openid)
  201. args.Add("lang", "zh_CN")
  202. uri += args.Encode()
  203. var body []byte
  204. body, err = getBody(uri)
  205. if err == nil {
  206. err = json.Unmarshal(body, &res)
  207. }
  208. return
  209. }
  210. // GetUserList user list
  211. func (wc Client) GetUserList(nextOpenID string) (res UserList, err error) {
  212. uri := BaseURL + "/cgi-bin/user/get?"
  213. if wc.AccessToken, err = wc.getToken(); err != nil {
  214. return
  215. }
  216. args := url.Values{}
  217. args.Add("access_token", wc.AccessToken)
  218. args.Add("next_openid", nextOpenID)
  219. uri += args.Encode()
  220. var body []byte
  221. body, err = getBody(uri)
  222. if err == nil {
  223. err = json.Unmarshal(body, &res)
  224. }
  225. return
  226. }
  227. // GetMaterial 永久资料
  228. func (wc Client) GetMaterial(mtype string, offset, count int) (res Material, err error) {
  229. uri := BaseURL + "/cgi-bin/material/batchget_material?"
  230. if wc.AccessToken, err = wc.getToken(); err != nil {
  231. return
  232. }
  233. args := url.Values{}
  234. data := make(map[string]interface{})
  235. args.Add("access_token", wc.AccessToken)
  236. data["type"] = mtype
  237. data["offset"] = offset
  238. data["count"] = count
  239. uri += args.Encode()
  240. var body []byte
  241. params, err := json.Marshal(data)
  242. body, err = postBody(uri, params)
  243. if err == nil {
  244. err = json.Unmarshal(body, &res)
  245. }
  246. return
  247. }
  248. // SendTemplateMessage send template message
  249. // POST /cgi-bin/message/template/send?access_token=ACCESS_TOKEN
  250. func (wc Client) SendTemplateMessage(template TemplateMessage) (res Response, err error) {
  251. uri := BaseURL + "/cgi-bin/message/template/send?"
  252. if wc.AccessToken, err = wc.getToken(); err != nil {
  253. return
  254. }
  255. args := url.Values{}
  256. args.Add("access_token", wc.AccessToken)
  257. uri += args.Encode()
  258. data, err := json.Marshal(template)
  259. if err != nil {
  260. return
  261. }
  262. res, err = postJSON(uri, data)
  263. return
  264. }