py-wecom-toolkit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_wecom_toolkit/__init__.py +43 -0
- py_wecom_toolkit/server/__init__.py +559 -0
- py_wecom_toolkit/server/materials.py +256 -0
- py_wecom_toolkit/server/messages.py +793 -0
- py_wecom_toolkit/server/responses.py +59 -0
- py_wecom_toolkit/server/utils.py +158 -0
- py_wecom_toolkit/webhook/__init__.py +406 -0
- py_wecom_toolkit/webhook/messages.py +971 -0
- py_wecom_toolkit/webhook/responses.py +191 -0
- py_wecom_toolkit/webhook/utils.py +310 -0
- py_wecom_toolkit-1.0.0.dist-info/METADATA +522 -0
- py_wecom_toolkit-1.0.0.dist-info/RECORD +15 -0
- py_wecom_toolkit-1.0.0.dist-info/WHEEL +5 -0
- py_wecom_toolkit-1.0.0.dist-info/licenses/LICENSE +21 -0
- py_wecom_toolkit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
py_wecom - 企业微信API Python SDK
|
|
5
|
+
|
|
6
|
+
提供企业微信API的完整Python封装,支持以下功能模块:
|
|
7
|
+
|
|
8
|
+
1. **Webhook模块** (`py_wecom.webhook`)
|
|
9
|
+
- 企业微信Webhook消息发送
|
|
10
|
+
- 支持文本、Markdown、图片、图文、文件、语音、模板卡片等消息类型
|
|
11
|
+
- 支持同步和异步两种调用方式
|
|
12
|
+
|
|
13
|
+
2. **Server模块** (`py_wecom.server`)
|
|
14
|
+
- 企业微信Server API集成
|
|
15
|
+
- 支持获取access_token、发送消息、上传素材等功能
|
|
16
|
+
- 支持同步和异步两种调用方式
|
|
17
|
+
|
|
18
|
+
快速开始:
|
|
19
|
+
|
|
20
|
+
**Webhook使用示例**:
|
|
21
|
+
>>> from py_wecom_toolkit.webhook import Webhook
|
|
22
|
+
>>> from py_wecom_toolkit.webhook.messages import Text, TextContent
|
|
23
|
+
>>> webhook = Webhook(key="your_webhook_key")
|
|
24
|
+
>>> response = webhook.send_text(content=Text(text=TextContent(content="Hello World")))
|
|
25
|
+
>>> print(response.json())
|
|
26
|
+
|
|
27
|
+
**Server使用示例**:
|
|
28
|
+
>>> from py_wecom_toolkit.server import Base
|
|
29
|
+
>>> from py_wecom_toolkit.server.messages import Sender, Text, TextContent
|
|
30
|
+
>>> from diskcache import Cache
|
|
31
|
+
>>> diskcache_default = Cache()
|
|
32
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret", cache_config={"instance": diskcache_default})
|
|
33
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
34
|
+
>>> server.refresh_access_token()
|
|
35
|
+
>>> sender = Sender(corpid="your_corpid", corpsecret="your_corpsecret", agentid="your_agentid", cache_config={"instance": diskcache_default})
|
|
36
|
+
>>> sender.refresh_access_token()
|
|
37
|
+
>>> response = sender.send_text(content=Text(text=TextContent(content="Hello from py-wecom!")), agentid="your_agentid")
|
|
38
|
+
>>> print(response.json())
|
|
39
|
+
|
|
40
|
+
参考文档:
|
|
41
|
+
- Webhook API: https://developer.work.weixin.qq.com/document/path/91770
|
|
42
|
+
- Server API: https://developer.work.weixin.qq.com/document/path/90664
|
|
43
|
+
"""
|
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
企业微信Server模块
|
|
5
|
+
|
|
6
|
+
提供企业微信Server API的客户端实现,支持同步和异步两种模式。
|
|
7
|
+
|
|
8
|
+
主要功能:
|
|
9
|
+
- 获取access_token(支持缓存)
|
|
10
|
+
- 获取API域名IP
|
|
11
|
+
- 获取回调IP
|
|
12
|
+
|
|
13
|
+
参考文档:
|
|
14
|
+
- https://developer.work.weixin.qq.com/document/path/90664
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from typing import Optional, Union
|
|
18
|
+
|
|
19
|
+
import diskcache
|
|
20
|
+
import httpx
|
|
21
|
+
import redis
|
|
22
|
+
from py_httpx_toolkit import Httpx
|
|
23
|
+
|
|
24
|
+
from .utils import build_success_instance, success_is_valid
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Base(Httpx):
|
|
28
|
+
"""
|
|
29
|
+
企业微信Server API基础客户端类
|
|
30
|
+
|
|
31
|
+
提供企业微信Server API的核心功能,包括access_token管理、IP查询等。
|
|
32
|
+
支持同步和异步两种模式,支持使用diskcache或Redis缓存access_token。
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
base_url: 企业微信API基础URL,默认为官方地址
|
|
36
|
+
corpid: 企业ID,可在企业微信管理后台获取
|
|
37
|
+
corpsecret: 应用密钥,可在企业微信管理后台获取
|
|
38
|
+
agentid: 企业应用ID,可在企业微信管理后台获取
|
|
39
|
+
cache_config: 缓存配置字典,包含instance(缓存实例)、key(缓存键名)、expire(过期时间)
|
|
40
|
+
client_kwargs: HTTP客户端配置参数
|
|
41
|
+
access_token: 当前的access_token
|
|
42
|
+
|
|
43
|
+
代码演示:
|
|
44
|
+
>>> from py_wecom_toolkit.server import Base
|
|
45
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
46
|
+
>>> server.refresh_access_token()
|
|
47
|
+
>>> print(server.access_token)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
base_url: Optional[str] = "https://qyapi.weixin.qq.com/",
|
|
53
|
+
corpid: Optional[str] = None,
|
|
54
|
+
corpsecret: Optional[str] = None,
|
|
55
|
+
agentid: Optional[Union[str, int]] = None,
|
|
56
|
+
cache_config: Optional[dict] = None,
|
|
57
|
+
client_kwargs: Optional[dict] = None,
|
|
58
|
+
):
|
|
59
|
+
# 设置基础URL,去除末尾斜杠以确保URL格式统一
|
|
60
|
+
self.base_url = base_url[:-1] if base_url.endswith("/") else base_url
|
|
61
|
+
# 设置企业ID,若为None则设为空字符串
|
|
62
|
+
self.corpid = corpid or ""
|
|
63
|
+
# 设置应用密钥,若为None则设为空字符串
|
|
64
|
+
self.corpsecret = corpsecret or ""
|
|
65
|
+
# 设置企业应用ID,若为None则设为空字符串
|
|
66
|
+
self.agentid = agentid or ""
|
|
67
|
+
# 设置缓存配置,若为None则设为空字典
|
|
68
|
+
self.cache_config = cache_config or {}
|
|
69
|
+
# 合并默认缓存配置与用户配置,用户配置优先覆盖默认值
|
|
70
|
+
# 默认配置:不使用缓存实例,缓存键名为py_wecom_server_{corpid}_{agentid},过期时间7100秒
|
|
71
|
+
self.cache_config = {
|
|
72
|
+
**{
|
|
73
|
+
"instance": None,
|
|
74
|
+
"key": f"py_wecom_server_{self.corpid}_{self.agentid}",
|
|
75
|
+
"expire": 7100,
|
|
76
|
+
},
|
|
77
|
+
**self.cache_config,
|
|
78
|
+
}
|
|
79
|
+
# 设置HTTP客户端配置,若为None则设为空字典
|
|
80
|
+
self.client_kwargs = client_kwargs or {}
|
|
81
|
+
# 合并默认客户端配置与用户配置,用户配置优先覆盖默认值
|
|
82
|
+
# 默认配置:基础URL、超时时间60秒
|
|
83
|
+
self.client_kwargs = {
|
|
84
|
+
**{
|
|
85
|
+
"base_url": self.base_url,
|
|
86
|
+
"timeout": 60,
|
|
87
|
+
"verify": False,
|
|
88
|
+
},
|
|
89
|
+
**self.client_kwargs,
|
|
90
|
+
}
|
|
91
|
+
# 初始化access_token为空字符串
|
|
92
|
+
self.access_token = None
|
|
93
|
+
|
|
94
|
+
def gettoken(
|
|
95
|
+
self,
|
|
96
|
+
client: Optional[httpx.Client] = None,
|
|
97
|
+
client_kwargs: Optional[dict] = None,
|
|
98
|
+
**kwargs
|
|
99
|
+
) -> httpx.Response:
|
|
100
|
+
"""
|
|
101
|
+
同步获取access_token
|
|
102
|
+
|
|
103
|
+
@see: https://developer.work.weixin.qq.com/document/path/91039
|
|
104
|
+
|
|
105
|
+
调用企业微信API /cgi-bin/gettoken 获取access_token,用于后续API调用的身份验证。
|
|
106
|
+
access_token的有效期为7200秒,需要定期刷新。
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
client: 可选的HTTP客户端实例,若提供则直接使用
|
|
110
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
111
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
httpx.Response: HTTP响应对象,成功时包含access_token字段
|
|
115
|
+
|
|
116
|
+
响应结构:
|
|
117
|
+
{
|
|
118
|
+
"errcode": 0,
|
|
119
|
+
"errmsg": "ok",
|
|
120
|
+
"access_token": "ACCESS_TOKEN",
|
|
121
|
+
"expires_in": 7200
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
代码演示:
|
|
125
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
126
|
+
>>> response = server.gettoken()
|
|
127
|
+
>>> print(response.json())
|
|
128
|
+
"""
|
|
129
|
+
client_kwargs = client_kwargs or {}
|
|
130
|
+
# 处理None值,确保kwargs是字典类型
|
|
131
|
+
kwargs = kwargs or {}
|
|
132
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
133
|
+
kwargs = {
|
|
134
|
+
# 默认请求参数:获取token的路径、GET方法、企业ID和应用密钥参数
|
|
135
|
+
**{
|
|
136
|
+
"method": "GET",
|
|
137
|
+
"url": "/cgi-bin/gettoken",
|
|
138
|
+
"params": {
|
|
139
|
+
"corpid": self.corpid,
|
|
140
|
+
"corpsecret": self.corpsecret,
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
**kwargs
|
|
144
|
+
}
|
|
145
|
+
return self.request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
146
|
+
|
|
147
|
+
async def async_gettoken(
|
|
148
|
+
self,
|
|
149
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
150
|
+
client_kwargs: Optional[dict] = None,
|
|
151
|
+
**kwargs
|
|
152
|
+
) -> httpx.Response:
|
|
153
|
+
"""
|
|
154
|
+
异步获取access_token
|
|
155
|
+
|
|
156
|
+
@see: https://developer.work.weixin.qq.com/document/path/91039
|
|
157
|
+
|
|
158
|
+
调用企业微信API /cgi-bin/gettoken 获取access_token,用于后续API调用的身份验证。
|
|
159
|
+
access_token的有效期为7200秒,需要定期刷新。
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
client: 可选的异步HTTP客户端实例,若提供则直接使用
|
|
163
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
164
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
httpx.Response: HTTP响应对象,成功时包含access_token字段
|
|
168
|
+
|
|
169
|
+
代码演示:
|
|
170
|
+
>>> import asyncio
|
|
171
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
172
|
+
>>> async def main():
|
|
173
|
+
>>> response = await server.async_gettoken()
|
|
174
|
+
>>> print(response.json())
|
|
175
|
+
>>> asyncio.run(main())
|
|
176
|
+
"""
|
|
177
|
+
client_kwargs = client_kwargs or {}
|
|
178
|
+
# 处理None值,确保kwargs是字典类型
|
|
179
|
+
kwargs = kwargs or {}
|
|
180
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
181
|
+
kwargs = {
|
|
182
|
+
# 默认请求参数:获取token的路径、GET方法、企业ID和应用密钥参数
|
|
183
|
+
**{
|
|
184
|
+
"method": "GET",
|
|
185
|
+
"url": "/cgi-bin/gettoken",
|
|
186
|
+
"params": {
|
|
187
|
+
"corpid": self.corpid,
|
|
188
|
+
"corpsecret": self.corpsecret,
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
**kwargs
|
|
192
|
+
}
|
|
193
|
+
return await self.async_request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
194
|
+
|
|
195
|
+
def get_api_domain_ip(
|
|
196
|
+
self,
|
|
197
|
+
client: Optional[httpx.Client] = None,
|
|
198
|
+
client_kwargs: Optional[dict] = None,
|
|
199
|
+
**kwargs
|
|
200
|
+
) -> httpx.Response:
|
|
201
|
+
"""
|
|
202
|
+
同步获取企业微信API域名IP列表
|
|
203
|
+
|
|
204
|
+
@see: https://developer.work.weixin.qq.com/document/path/90960
|
|
205
|
+
|
|
206
|
+
获取企业微信API服务器的IP地址列表,用于白名单配置等场景。
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
client: 可选的HTTP客户端实例,若提供则直接使用
|
|
210
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
211
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
httpx.Response: HTTP响应对象,成功时包含IP列表
|
|
215
|
+
|
|
216
|
+
响应结构:
|
|
217
|
+
{
|
|
218
|
+
"errcode": 0,
|
|
219
|
+
"errmsg": "ok",
|
|
220
|
+
"ip_list": ["IP1", "IP2", ...]
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
代码演示:
|
|
224
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
225
|
+
>>> server.refresh_access_token()
|
|
226
|
+
>>> response = server.get_api_domain_ip()
|
|
227
|
+
>>> print(response.json())
|
|
228
|
+
"""
|
|
229
|
+
client_kwargs = client_kwargs or {}
|
|
230
|
+
# 处理None值,确保kwargs是字典类型
|
|
231
|
+
kwargs = kwargs or {}
|
|
232
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
233
|
+
kwargs = {
|
|
234
|
+
**{
|
|
235
|
+
"method": "GET",
|
|
236
|
+
"url": "/cgi-bin/get_api_domain_ip",
|
|
237
|
+
"params": {
|
|
238
|
+
"access_token": self.access_token,
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
**kwargs
|
|
242
|
+
}
|
|
243
|
+
return self.request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
244
|
+
|
|
245
|
+
async def async_get_api_domain_ip(
|
|
246
|
+
self,
|
|
247
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
248
|
+
client_kwargs: Optional[dict] = None,
|
|
249
|
+
**kwargs
|
|
250
|
+
) -> httpx.Response:
|
|
251
|
+
"""
|
|
252
|
+
异步获取企业微信API域名IP列表
|
|
253
|
+
|
|
254
|
+
@see: https://developer.work.weixin.qq.com/document/path/90960
|
|
255
|
+
|
|
256
|
+
获取企业微信API服务器的IP地址列表,用于白名单配置等场景。
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
client: 可选的异步HTTP客户端实例,若提供则直接使用
|
|
260
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
261
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
httpx.Response: HTTP响应对象,成功时包含IP列表
|
|
265
|
+
|
|
266
|
+
代码演示:
|
|
267
|
+
>>> import asyncio
|
|
268
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
269
|
+
>>> await server.async_refresh_access_token()
|
|
270
|
+
>>> response = await server.async_get_api_domain_ip()
|
|
271
|
+
>>> print(response.json())
|
|
272
|
+
"""
|
|
273
|
+
client_kwargs = client_kwargs or {}
|
|
274
|
+
# 处理None值,确保kwargs是字典类型
|
|
275
|
+
kwargs = kwargs or {}
|
|
276
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
277
|
+
kwargs = {
|
|
278
|
+
**{
|
|
279
|
+
"method": "GET",
|
|
280
|
+
"url": "/cgi-bin/get_api_domain_ip",
|
|
281
|
+
"params": {
|
|
282
|
+
"access_token": self.access_token,
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
**kwargs
|
|
286
|
+
}
|
|
287
|
+
return await self.async_request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
288
|
+
|
|
289
|
+
def getcallbackip(
|
|
290
|
+
self,
|
|
291
|
+
client: Optional[httpx.Client] = None,
|
|
292
|
+
client_kwargs: Optional[dict] = None,
|
|
293
|
+
**kwargs
|
|
294
|
+
) -> httpx.Response:
|
|
295
|
+
"""
|
|
296
|
+
同步获取企业微信回调IP列表
|
|
297
|
+
|
|
298
|
+
@see: https://developer.work.weixin.qq.com/document/path/90961
|
|
299
|
+
|
|
300
|
+
获取企业微信服务器用于回调的IP地址列表,用于配置服务器白名单。
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
client: 可选的HTTP客户端实例,若提供则直接使用
|
|
304
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
305
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
httpx.Response: HTTP响应对象,成功时包含回调IP列表
|
|
309
|
+
|
|
310
|
+
响应结构:
|
|
311
|
+
{
|
|
312
|
+
"errcode": 0,
|
|
313
|
+
"errmsg": "ok",
|
|
314
|
+
"ip_list": ["IP1", "IP2", ...]
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
代码演示:
|
|
318
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
319
|
+
>>> server.refresh_access_token()
|
|
320
|
+
>>> response = server.getcallbackip()
|
|
321
|
+
>>> print(response.json())
|
|
322
|
+
"""
|
|
323
|
+
client_kwargs = client_kwargs or {}
|
|
324
|
+
# 处理None值,确保kwargs是字典类型
|
|
325
|
+
kwargs = kwargs or {}
|
|
326
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
327
|
+
kwargs = {
|
|
328
|
+
**{
|
|
329
|
+
"method": "GET",
|
|
330
|
+
"url": "/cgi-bin/getcallbackip",
|
|
331
|
+
"params": {
|
|
332
|
+
"access_token": self.access_token,
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
**kwargs
|
|
336
|
+
}
|
|
337
|
+
return self.request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
338
|
+
|
|
339
|
+
async def async_getcallbackip(
|
|
340
|
+
self,
|
|
341
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
342
|
+
client_kwargs: Optional[dict] = None,
|
|
343
|
+
**kwargs
|
|
344
|
+
) -> httpx.Response:
|
|
345
|
+
"""
|
|
346
|
+
异步获取企业微信回调IP列表
|
|
347
|
+
|
|
348
|
+
@see: https://developer.work.weixin.qq.com/document/path/90961
|
|
349
|
+
|
|
350
|
+
获取企业微信服务器用于回调的IP地址列表,用于配置服务器白名单。
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
client: 可选的异步HTTP客户端实例,若提供则直接使用
|
|
354
|
+
client_kwargs: 创建临时客户端时的额外配置参数
|
|
355
|
+
**kwargs: 额外的请求参数,将与默认参数合并
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
httpx.Response: HTTP响应对象,成功时包含回调IP列表
|
|
359
|
+
|
|
360
|
+
代码演示:
|
|
361
|
+
>>> import asyncio
|
|
362
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret")
|
|
363
|
+
>>> await server.async_refresh_access_token()
|
|
364
|
+
>>> response = await server.async_getcallbackip()
|
|
365
|
+
>>> print(response.json())
|
|
366
|
+
"""
|
|
367
|
+
client_kwargs = client_kwargs or {}
|
|
368
|
+
# 处理None值,确保kwargs是字典类型
|
|
369
|
+
kwargs = kwargs or {}
|
|
370
|
+
# 合并默认请求参数与用户参数,用户参数优先覆盖默认值
|
|
371
|
+
kwargs = {
|
|
372
|
+
**{
|
|
373
|
+
"method": "GET",
|
|
374
|
+
"url": "/cgi-bin/getcallbackip",
|
|
375
|
+
"params": {
|
|
376
|
+
"access_token": self.access_token,
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
**kwargs
|
|
380
|
+
}
|
|
381
|
+
return await self.async_request(client=client, client_kwargs=client_kwargs, **kwargs)
|
|
382
|
+
|
|
383
|
+
def refresh_access_token(
|
|
384
|
+
self,
|
|
385
|
+
gettoken_func_kwargs: Optional[dict] = None,
|
|
386
|
+
get_api_domain_ip_func_kwargs: Optional[dict] = None,
|
|
387
|
+
cache_config: Optional[dict] = None,
|
|
388
|
+
) -> "Base":
|
|
389
|
+
"""
|
|
390
|
+
同步刷新access_token
|
|
391
|
+
|
|
392
|
+
获取并更新access_token,支持使用diskcache或Redis进行缓存,减少API调用次数。
|
|
393
|
+
|
|
394
|
+
逻辑流程:
|
|
395
|
+
1. 合并缓存配置(实例配置与调用时配置)
|
|
396
|
+
2. 判断是否配置了缓存实例(diskcache.Cache或redis.Redis)
|
|
397
|
+
3. 如果未配置缓存:直接调用gettoken获取access_token
|
|
398
|
+
4. 如果配置了缓存:
|
|
399
|
+
- 优先从缓存获取access_token
|
|
400
|
+
- 如果缓存中没有或无效,调用gettoken获取并写入缓存
|
|
401
|
+
- 调用get_api_domain_ip验证token有效性
|
|
402
|
+
- 如果验证失败,重新获取并更新缓存
|
|
403
|
+
5. 更新实例的access_token属性
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
gettoken_func_kwargs: gettoken方法的额外参数
|
|
407
|
+
get_api_domain_ip_func_kwargs: get_api_domain_ip方法的额外参数
|
|
408
|
+
cache_config: 缓存配置字典,包含:
|
|
409
|
+
- instance: 缓存实例(diskcache.Cache或redis.Redis)
|
|
410
|
+
- key: 缓存键名,默认为"pywecom_server_{corpid}_{agentid}"
|
|
411
|
+
- expire: 缓存过期时间,默认为7100秒(小于7200秒官方有效期)
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Base: 返回self,支持链式调用
|
|
415
|
+
|
|
416
|
+
代码演示:
|
|
417
|
+
>>> from py_wecom_toolkit.server import Base
|
|
418
|
+
>>> import diskcache
|
|
419
|
+
>>> # 创建带缓存的实例
|
|
420
|
+
>>> cache = diskcache.Cache("./cache")
|
|
421
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret",
|
|
422
|
+
... cache_config={"instance": cache})
|
|
423
|
+
>>> server.refresh_access_token()
|
|
424
|
+
>>> print(server.access_token)
|
|
425
|
+
"""
|
|
426
|
+
gettoken_func_kwargs = gettoken_func_kwargs or {}
|
|
427
|
+
get_api_domain_ip_func_kwargs = get_api_domain_ip_func_kwargs or {}
|
|
428
|
+
cache_config = cache_config or {}
|
|
429
|
+
# 合并实例缓存配置与调用时的缓存配置,调用时配置优先
|
|
430
|
+
cache_config = {
|
|
431
|
+
**self.cache_config,
|
|
432
|
+
**cache_config,
|
|
433
|
+
}
|
|
434
|
+
cache_key = cache_config.get("key", f"pywecom_server_{self.corpid}_{self.agentid}")
|
|
435
|
+
cache_expire = cache_config.get("expire", 7100)
|
|
436
|
+
cache_inst = cache_config.get("instance", None)
|
|
437
|
+
|
|
438
|
+
# 【逻辑分支1】未配置缓存实例,直接获取token
|
|
439
|
+
if not isinstance(cache_inst, Union[diskcache.Cache, redis.Redis]):
|
|
440
|
+
response = self.gettoken(**gettoken_func_kwargs)
|
|
441
|
+
if success_is_valid(response):
|
|
442
|
+
self.access_token = response.json().get("access_token", None)
|
|
443
|
+
else:
|
|
444
|
+
# 【逻辑分支2】配置了缓存实例,优先使用缓存
|
|
445
|
+
# 从缓存获取access_token
|
|
446
|
+
self.access_token = cache_inst.get(cache_key)
|
|
447
|
+
# 如果缓存中没有有效token,重新获取
|
|
448
|
+
if not isinstance(self.access_token, str) or not len(self.access_token):
|
|
449
|
+
response = self.gettoken(**gettoken_func_kwargs)
|
|
450
|
+
if success_is_valid(response):
|
|
451
|
+
self.access_token = response.json().get("access_token", None)
|
|
452
|
+
# 根据缓存类型设置缓存
|
|
453
|
+
if isinstance(cache_inst, diskcache.Cache):
|
|
454
|
+
cache_inst.set(cache_key, self.access_token, expire=cache_expire)
|
|
455
|
+
if isinstance(cache_inst, redis.Redis):
|
|
456
|
+
cache_inst.set(cache_key, self.access_token, ex=cache_expire)
|
|
457
|
+
|
|
458
|
+
# 【验证机制】调用get_api_domain_ip验证token有效性
|
|
459
|
+
response = self.get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
460
|
+
if not success_is_valid(response):
|
|
461
|
+
# token失效,重新获取并更新缓存
|
|
462
|
+
response = self.gettoken(**gettoken_func_kwargs)
|
|
463
|
+
if success_is_valid(response):
|
|
464
|
+
self.access_token = response.json().get("access_token", None)
|
|
465
|
+
if isinstance(cache_inst, diskcache.Cache):
|
|
466
|
+
cache_inst.set(cache_key, self.access_token, expire=cache_expire)
|
|
467
|
+
if isinstance(cache_inst, redis.Redis):
|
|
468
|
+
cache_inst.set(cache_key, self.access_token, ex=cache_expire)
|
|
469
|
+
|
|
470
|
+
return self
|
|
471
|
+
|
|
472
|
+
async def async_refresh_access_token(
|
|
473
|
+
self,
|
|
474
|
+
gettoken_func_kwargs: Optional[dict] = None,
|
|
475
|
+
get_api_domain_ip_func_kwargs: Optional[dict] = None,
|
|
476
|
+
cache_config: Optional[dict] = None,
|
|
477
|
+
) -> "Base":
|
|
478
|
+
"""
|
|
479
|
+
异步刷新access_token
|
|
480
|
+
|
|
481
|
+
获取并更新access_token,支持使用diskcache或Redis进行缓存,减少API调用次数。
|
|
482
|
+
|
|
483
|
+
逻辑流程:
|
|
484
|
+
1. 合并缓存配置(实例配置与调用时配置)
|
|
485
|
+
2. 判断是否配置了缓存实例(diskcache.Cache或redis.Redis)
|
|
486
|
+
3. 如果未配置缓存:直接调用async_gettoken获取access_token
|
|
487
|
+
4. 如果配置了缓存:
|
|
488
|
+
- 优先从缓存获取access_token
|
|
489
|
+
- 如果缓存中没有或无效,调用async_gettoken获取并写入缓存
|
|
490
|
+
- 调用async_get_api_domain_ip验证token有效性
|
|
491
|
+
- 如果验证失败,重新获取并更新缓存
|
|
492
|
+
5. 更新实例的access_token属性
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
gettoken_func_kwargs: async_gettoken方法的额外参数
|
|
496
|
+
get_api_domain_ip_func_kwargs: async_get_api_domain_ip方法的额外参数
|
|
497
|
+
cache_config: 缓存配置字典,包含:
|
|
498
|
+
- instance: 缓存实例(diskcache.Cache或redis.Redis)
|
|
499
|
+
- key: 缓存键名,默认为"pywecom_server_{corpid}_{agentid}"
|
|
500
|
+
- expire: 缓存过期时间,默认为7100秒
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
Base: 返回self,支持链式调用
|
|
504
|
+
|
|
505
|
+
代码演示:
|
|
506
|
+
>>> import asyncio
|
|
507
|
+
>>> from py_wecom_toolkit.server import Base
|
|
508
|
+
>>> import redis
|
|
509
|
+
>>> # 创建带Redis缓存的实例
|
|
510
|
+
>>> r = redis.Redis(host='localhost', port=6379, db=0)
|
|
511
|
+
>>> server = Base(corpid="your_corpid", corpsecret="your_corpsecret",
|
|
512
|
+
... cache_config={"instance": r})
|
|
513
|
+
>>> await server.async_refresh_access_token()
|
|
514
|
+
>>> print(server.access_token)
|
|
515
|
+
"""
|
|
516
|
+
gettoken_func_kwargs = gettoken_func_kwargs or {}
|
|
517
|
+
get_api_domain_ip_func_kwargs = get_api_domain_ip_func_kwargs or {}
|
|
518
|
+
cache_config = cache_config or {}
|
|
519
|
+
# 合并实例缓存配置与调用时的缓存配置,调用时配置优先
|
|
520
|
+
cache_config = {
|
|
521
|
+
**self.cache_config,
|
|
522
|
+
**cache_config,
|
|
523
|
+
}
|
|
524
|
+
cache_key = cache_config.get("key", f"pywecom_server_{self.corpid}_{self.agentid}")
|
|
525
|
+
cache_expire = cache_config.get("expire", 7100)
|
|
526
|
+
cache_inst = cache_config.get("instance", None)
|
|
527
|
+
|
|
528
|
+
# 【逻辑分支1】未配置缓存实例,直接获取token
|
|
529
|
+
if not isinstance(cache_inst, Union[diskcache.Cache, redis.Redis]):
|
|
530
|
+
response = await self.async_gettoken(**gettoken_func_kwargs)
|
|
531
|
+
if success_is_valid(response):
|
|
532
|
+
self.access_token = response.json().get("access_token", None)
|
|
533
|
+
if isinstance(cache_inst, diskcache.Cache):
|
|
534
|
+
cache_inst.set(cache_key, self.access_token, expire=cache_expire)
|
|
535
|
+
if isinstance(cache_inst, redis.Redis):
|
|
536
|
+
cache_inst.set(cache_key, self.access_token, ex=cache_expire)
|
|
537
|
+
else:
|
|
538
|
+
# 【逻辑分支2】配置了缓存实例,优先使用缓存
|
|
539
|
+
# 从缓存获取access_token
|
|
540
|
+
self.access_token = cache_inst.get(cache_key)
|
|
541
|
+
# 如果缓存中没有有效token,重新获取
|
|
542
|
+
if not isinstance(self.access_token, str) or not len(self.access_token):
|
|
543
|
+
response = await self.async_gettoken(**gettoken_func_kwargs)
|
|
544
|
+
if success_is_valid(response):
|
|
545
|
+
self.access_token = response.json().get("access_token", None)
|
|
546
|
+
|
|
547
|
+
# 【验证机制】调用async_get_api_domain_ip验证token有效性
|
|
548
|
+
response = await self.async_get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
549
|
+
if not success_is_valid(response):
|
|
550
|
+
# token失效,重新获取并更新缓存
|
|
551
|
+
response = await self.async_gettoken(**gettoken_func_kwargs)
|
|
552
|
+
if success_is_valid(response):
|
|
553
|
+
self.access_token = response.json().get("access_token", None)
|
|
554
|
+
if isinstance(cache_inst, diskcache.Cache):
|
|
555
|
+
cache_inst.set(cache_key, self.access_token, expire=cache_expire)
|
|
556
|
+
if isinstance(cache_inst, redis.Redis):
|
|
557
|
+
cache_inst.set(cache_key, self.access_token, ex=cache_expire)
|
|
558
|
+
|
|
559
|
+
return self
|