token.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package dialog
  2. import (
  3. stdcontext "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "sync"
  8. "time"
  9. "github.com/silenceper/wechat/v2/aispeech/config"
  10. "github.com/silenceper/wechat/v2/cache"
  11. )
  12. const accessTokenCacheKeyPrefix = "gowechat_aispeech_"
  13. var errEmptyAccessToken = errors.New("aispeech access_token is empty")
  14. // AccessToken 微信智能对话 access token.
  15. type AccessToken struct {
  16. cfg *config.Config
  17. accessTokenLock *sync.Mutex
  18. }
  19. // NewAccessToken new AccessToken.
  20. func NewAccessToken(cfg *config.Config) *AccessToken {
  21. if cfg.Cache == nil {
  22. panic("cache is needed")
  23. }
  24. return &AccessToken{
  25. cfg: cfg,
  26. accessTokenLock: new(sync.Mutex),
  27. }
  28. }
  29. // GetAccessToken 获取 access token.
  30. func (ak *AccessToken) GetAccessToken() (string, error) {
  31. return ak.GetAccessTokenContext(stdcontext.Background())
  32. }
  33. // GetAccessTokenContext 获取 access token.
  34. func (ak *AccessToken) GetAccessTokenContext(ctx stdcontext.Context) (string, error) {
  35. cacheKey := fmt.Sprintf("%s_access_token_%s_%s", accessTokenCacheKeyPrefix, ak.cfg.AppID, ak.cfg.Account)
  36. if val := cache.GetContext(ctx, ak.cfg.Cache, cacheKey); val != nil {
  37. if accessToken, ok := val.(string); ok && accessToken != "" {
  38. return accessToken, nil
  39. }
  40. }
  41. ak.accessTokenLock.Lock()
  42. defer ak.accessTokenLock.Unlock()
  43. if val := cache.GetContext(ctx, ak.cfg.Cache, cacheKey); val != nil {
  44. if accessToken, ok := val.(string); ok && accessToken != "" {
  45. return accessToken, nil
  46. }
  47. }
  48. accessToken, err := ak.getAccessTokenFromServer(ctx)
  49. if err != nil {
  50. return "", err
  51. }
  52. if err = cache.SetContext(ctx, ak.cfg.Cache, cacheKey, accessToken, 110*time.Minute); err != nil {
  53. return "", err
  54. }
  55. return accessToken, nil
  56. }
  57. func (ak *AccessToken) getAccessTokenFromServer(ctx stdcontext.Context) (string, error) {
  58. req := &AccessTokenRequest{Account: ak.cfg.Account}
  59. body, err := json.Marshal(req)
  60. if err != nil {
  61. return "", err
  62. }
  63. response, err := post(ctx, ak.cfg, tokenPath, body, "application/json", "", ak.cfg.AppID)
  64. if err != nil {
  65. return "", err
  66. }
  67. var res accessTokenData
  68. if _, err = decodeResponse(response, &res, "AISpeechGetAccessToken"); err != nil {
  69. return "", err
  70. }
  71. if res.AccessToken == "" {
  72. return "", errEmptyAccessToken
  73. }
  74. return res.AccessToken, nil
  75. }