-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgodingtalk.go
159 lines (142 loc) · 3.41 KB
/
godingtalk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package godingtalk
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
// 基础设置
const (
ExpiresIn = 7200
OapiURL = "oapi.dingtalk.com"
TokenFile = ".token"
)
// 错误集合
var (
ErrEmptyToken = errors.New("can't refresh a empty token")
ErrTokenAvaliable = errors.New("token is avaliable")
)
type Unmarshalable interface {
checkErr() error
}
type Base struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
type DingtalkClient struct {
Client *http.Client
BaseURL string
AppKey string
AppSecret string
params url.Values
AccessToken AccessTokenResp
ExpiresTime interface{}
}
type AccessTokenContent struct {
Token string `json:"access_token"` // 这里用来存储之后要用的凭证码
ExpiresTime int64 `json:"expires_in"` // 凭证码过期的时间(= 凭证创建时间 + 7200s)
}
type AccessTokenResp struct {
Base
AccessTokenContent
}
// 为Base实现Unmarshallable接口,ErrorCode不为空,肯定为可Unmarshal的
func (b Base) checkErr() error {
if b.ErrCode != 0 {
return fmt.Errorf("%d:%s", b.ErrCode, b.ErrMsg)
}
return nil
}
func NewDingtalkClient(appkey, appsecret string) *DingtalkClient {
return &DingtalkClient{
Client: &http.Client{
Timeout: time.Duration(time.Second * 60),
},
BaseURL: OapiURL,
AppKey: appkey,
AppSecret: appsecret,
}
}
// setToken 获取到token后存入Client中并持久化存储到文本
func (d *DingtalkClient) setToken() error {
params := url.Values{}
access := AccessTokenResp{}
err := d.getNewAccessToken("gettoken", params, nil, &access)
if err != nil {
return err
}
if access.ErrCode != 0 {
return errors.New(access.ErrMsg)
}
d.AccessToken.Token = access.Token
d.AccessToken.ExpiresTime = time.Now().Unix() + ExpiresIn
return nil
}
func (d *DingtalkClient) Init() error {
if d.AccessToken.Token == "" {
tr, err := readToken()
if err != nil {
goto GETTOKEN
}
switch t := tr.(type) {
case *AccessTokenResp:
if checkExpireTime(t.ExpiresTime) {
goto GETTOKEN
} else {
d.AccessToken.Token = t.Token
d.AccessToken.ExpiresTime = t.ExpiresTime
}
}
}
GETTOKEN:
if err := d.setToken(); err != nil {
return err
}
if err := storeToken(d.AccessToken); err != nil {
return err
}
d.params = make(url.Values)
d.params.Set("access_token", d.AccessToken.Token)
return nil
}
// RefreshToken 刷新token,根据现有Client中的token判断
// 暴露为外部可调用函数,方便手动刷新现有token的需求
func (d *DingtalkClient) RefreshToken() error {
return d.setToken()
}
// checkExpireTime 判断token是否过期
// if expire then return true
func checkExpireTime(etime int64) bool {
if time.Now().Unix() < etime {
return false
}
return true
}
func storeToken(tokenResp Unmarshalable) error {
if err := tokenResp.checkErr(); err != nil {
return err
}
data, err := json.Marshal(tokenResp)
if err != nil {
return err
}
return ioutil.WriteFile(TokenFile, data, 0666)
}
func readToken() (Unmarshalable, error) {
bytes, err := ioutil.ReadFile(TokenFile)
if err != nil {
return nil, err
}
tokenResp := &AccessTokenResp{}
err = json.Unmarshal(bytes, tokenResp)
return tokenResp, err
}
func rpc(d *DingtalkClient, path string, params url.Values, reqData interface{}, respData Unmarshalable) error {
if err := d.httpRequestWithStd(path, params, reqData, respData); err != nil {
return err
}
return nil
}