py-wecom 0.1.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/__init__.py +2 -0
- py_wecom/server/__init__.py +352 -0
- py_wecom/server/materials.py +160 -0
- py_wecom/server/messages.py +659 -0
- py_wecom/server/responses.py +28 -0
- py_wecom/server/utils.py +76 -0
- py_wecom/webhook/__init__.py +644 -0
- py_wecom/webhook/messages.py +131 -0
- py_wecom/webhook/responses.py +49 -0
- py_wecom/webhook/utils.py +118 -0
- py_wecom-0.1.0.dist-info/METADATA +295 -0
- py_wecom-0.1.0.dist-info/RECORD +15 -0
- py_wecom-0.1.0.dist-info/WHEEL +5 -0
- py_wecom-0.1.0.dist-info/licenses/LICENSE +21 -0
- py_wecom-0.1.0.dist-info/top_level.txt +1 -0
py_wecom/__init__.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
from typing import List, Optional, Tuple, Union
|
|
4
|
+
|
|
5
|
+
import diskcache
|
|
6
|
+
import httpx
|
|
7
|
+
import redis
|
|
8
|
+
from pydantic import HttpUrl
|
|
9
|
+
from .utils import convert_to_errcode_eq_0, errcode_eq_0_validator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Base:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
base_url: HttpUrl = "https://qyapi.weixin.qq.com",
|
|
16
|
+
corpid: Optional[str] = None,
|
|
17
|
+
corpsecret: Optional[str] = None,
|
|
18
|
+
agentid: Optional[Union[str, int]] = None,
|
|
19
|
+
cache_config: Optional[dict] = None,
|
|
20
|
+
client_kwargs: Optional[dict] = None,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
初始化Server实例
|
|
24
|
+
|
|
25
|
+
参数:
|
|
26
|
+
base_url: 企业微信API基础URL,默认"https://qyapi.weixin.qq.com"
|
|
27
|
+
corpid: 企业ID,可在企业微信管理后台获取
|
|
28
|
+
corpsecret: 应用密钥,可在企业微信管理后台获取
|
|
29
|
+
agentid: 企业应用ID,可在企业微信管理后台获取
|
|
30
|
+
cache_config: 缓存配置字典,包含instance(缓存实例)、key(缓存键名)、expire(过期时间)
|
|
31
|
+
client_kwargs: httpx客户端配置参数
|
|
32
|
+
"""
|
|
33
|
+
self.base_url = base_url[:-1] if base_url.endswith("/") else base_url
|
|
34
|
+
self.corpid = corpid or ""
|
|
35
|
+
self.corpsecret = corpsecret or ""
|
|
36
|
+
self.agentid = agentid or ""
|
|
37
|
+
self.cache_config = cache_config or {}
|
|
38
|
+
self.cache_config = {
|
|
39
|
+
**{
|
|
40
|
+
"instance": None,
|
|
41
|
+
"key": f"pywecom_server_{self.corpid}_{self.agentid}",
|
|
42
|
+
"expire": 7100,
|
|
43
|
+
},
|
|
44
|
+
**self.cache_config,
|
|
45
|
+
}
|
|
46
|
+
self.client_kwargs = client_kwargs or {}
|
|
47
|
+
self.client_kwargs = {
|
|
48
|
+
**{
|
|
49
|
+
"base_url": self.base_url,
|
|
50
|
+
"timeout": 60,
|
|
51
|
+
},
|
|
52
|
+
**self.client_kwargs,
|
|
53
|
+
}
|
|
54
|
+
self.access_token = ""
|
|
55
|
+
|
|
56
|
+
def client(self) -> httpx.Client:
|
|
57
|
+
"""
|
|
58
|
+
创建并返回同步HTTP客户端
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
httpx.Client: 配置好的同步HTTP客户端实例
|
|
62
|
+
"""
|
|
63
|
+
return httpx.Client(**self.client_kwargs)
|
|
64
|
+
|
|
65
|
+
def async_client(self) -> httpx.AsyncClient:
|
|
66
|
+
"""
|
|
67
|
+
创建并返回异步HTTP客户端
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
httpx.AsyncClient: 配置好的异步HTTP客户端实例
|
|
71
|
+
"""
|
|
72
|
+
return httpx.AsyncClient(**self.client_kwargs)
|
|
73
|
+
|
|
74
|
+
def gettoken(
|
|
75
|
+
self,
|
|
76
|
+
client: Optional[httpx.Client] = None,
|
|
77
|
+
**kwargs
|
|
78
|
+
) -> httpx.Response:
|
|
79
|
+
"""
|
|
80
|
+
获取企业微信access_token
|
|
81
|
+
|
|
82
|
+
@see:https://developer.work.weixin.qq.com/document/path/91039
|
|
83
|
+
Args:
|
|
84
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
85
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
89
|
+
"""
|
|
90
|
+
kwargs = kwargs or {}
|
|
91
|
+
kwargs = {
|
|
92
|
+
**{
|
|
93
|
+
"url": "/cgi-bin/gettoken",
|
|
94
|
+
"method": "GET",
|
|
95
|
+
"params": {
|
|
96
|
+
"corpid": self.corpid,
|
|
97
|
+
"corpsecret": self.corpsecret,
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
**kwargs
|
|
101
|
+
}
|
|
102
|
+
if isinstance(client, httpx.Client):
|
|
103
|
+
response = client.request(**kwargs)
|
|
104
|
+
else:
|
|
105
|
+
with self.client() as _client:
|
|
106
|
+
response = _client.request(**kwargs)
|
|
107
|
+
return response
|
|
108
|
+
|
|
109
|
+
def get_api_domain_ip(
|
|
110
|
+
self,
|
|
111
|
+
client: Optional[httpx.Client] = None,
|
|
112
|
+
**kwargs
|
|
113
|
+
) -> httpx.Response:
|
|
114
|
+
"""
|
|
115
|
+
获取企业微信API域名IP
|
|
116
|
+
|
|
117
|
+
@see:https://developer.work.weixin.qq.com/document/path/92520
|
|
118
|
+
Args:
|
|
119
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
120
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
124
|
+
"""
|
|
125
|
+
kwargs = kwargs or {}
|
|
126
|
+
kwargs = {
|
|
127
|
+
**{
|
|
128
|
+
"url": "/cgi-bin/get_api_domain_ip",
|
|
129
|
+
"method": "GET",
|
|
130
|
+
"params": {
|
|
131
|
+
"access_token": self.access_token,
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
**kwargs
|
|
135
|
+
}
|
|
136
|
+
if isinstance(client, httpx.Client):
|
|
137
|
+
response = client.request(**kwargs)
|
|
138
|
+
else:
|
|
139
|
+
with self.client() as _client:
|
|
140
|
+
response = _client.request(**kwargs)
|
|
141
|
+
return response
|
|
142
|
+
|
|
143
|
+
def getcallbackip(
|
|
144
|
+
self,
|
|
145
|
+
client: Optional[httpx.Client] = None,
|
|
146
|
+
**kwargs
|
|
147
|
+
) -> httpx.Response:
|
|
148
|
+
"""
|
|
149
|
+
获取企业微信回调IP
|
|
150
|
+
|
|
151
|
+
@see:https://developer.work.weixin.qq.com/document/path/92521
|
|
152
|
+
Args:
|
|
153
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
154
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
158
|
+
"""
|
|
159
|
+
kwargs = kwargs or {}
|
|
160
|
+
kwargs = {
|
|
161
|
+
**{
|
|
162
|
+
"url": "/cgi-bin/getcallbackip",
|
|
163
|
+
"method": "GET",
|
|
164
|
+
"params": {
|
|
165
|
+
"access_token": self.access_token,
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
**kwargs
|
|
169
|
+
}
|
|
170
|
+
if isinstance(client, httpx.Client):
|
|
171
|
+
response = client.request(**kwargs)
|
|
172
|
+
else:
|
|
173
|
+
with self.client() as _client:
|
|
174
|
+
response = _client.request(**kwargs)
|
|
175
|
+
return response
|
|
176
|
+
|
|
177
|
+
def refresh_access_token(
|
|
178
|
+
self,
|
|
179
|
+
gettoken_func_kwargs: Optional[dict] = None,
|
|
180
|
+
get_api_domain_ip_func_kwargs: Optional[dict] = None
|
|
181
|
+
) -> "Base":
|
|
182
|
+
gettoken_func_kwargs = gettoken_func_kwargs or {}
|
|
183
|
+
get_api_domain_ip_func_kwargs = get_api_domain_ip_func_kwargs or {}
|
|
184
|
+
cache_key = self.cache_config.get("key", f"pywecom_server_{self.corpid}_{self.agentid}")
|
|
185
|
+
cache_expire = self.cache_config.get("expire", 7100)
|
|
186
|
+
cache_instance = self.cache_config.get("instance", None)
|
|
187
|
+
if not isinstance(cache_instance, Union[diskcache.Cache, redis.Redis]):
|
|
188
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
189
|
+
self.gettoken(**gettoken_func_kwargs)
|
|
190
|
+
).access_token
|
|
191
|
+
else:
|
|
192
|
+
self.access_token = cache_instance.get(cache_key)
|
|
193
|
+
if not isinstance(self.access_token, str) or not len(self.access_token):
|
|
194
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
195
|
+
self.gettoken(**gettoken_func_kwargs)
|
|
196
|
+
).access_token
|
|
197
|
+
ip_list = convert_to_errcode_eq_0(
|
|
198
|
+
self.get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
199
|
+
).ip_list
|
|
200
|
+
if not isinstance(ip_list, list):
|
|
201
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
202
|
+
self.gettoken(**gettoken_func_kwargs)
|
|
203
|
+
).access_token
|
|
204
|
+
ip_list = convert_to_errcode_eq_0(
|
|
205
|
+
self.get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
206
|
+
).ip_list
|
|
207
|
+
if isinstance(ip_list, list):
|
|
208
|
+
if isinstance(cache_instance, diskcache.Cache):
|
|
209
|
+
cache_instance.set(cache_key, self.access_token, expire=cache_expire)
|
|
210
|
+
elif isinstance(cache_instance, redis.Redis):
|
|
211
|
+
cache_instance.set(cache_key, self.access_token, ex=cache_expire)
|
|
212
|
+
return self
|
|
213
|
+
|
|
214
|
+
async def async_gettoken(
|
|
215
|
+
self,
|
|
216
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
217
|
+
**kwargs
|
|
218
|
+
) -> httpx.Response:
|
|
219
|
+
"""
|
|
220
|
+
获取企业微信access_token
|
|
221
|
+
|
|
222
|
+
@see:https://developer.work.weixin.qq.com/document/path/91039
|
|
223
|
+
Args:
|
|
224
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
225
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
229
|
+
"""
|
|
230
|
+
kwargs = kwargs or {}
|
|
231
|
+
kwargs = {
|
|
232
|
+
**{
|
|
233
|
+
"url": "/cgi-bin/gettoken",
|
|
234
|
+
"method": "GET",
|
|
235
|
+
"params": {
|
|
236
|
+
"corpid": self.corpid,
|
|
237
|
+
"corpsecret": self.corpsecret,
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
**kwargs
|
|
241
|
+
}
|
|
242
|
+
if isinstance(client, httpx.AsyncClient):
|
|
243
|
+
response = await client.request(**kwargs)
|
|
244
|
+
else:
|
|
245
|
+
async with self.async_client() as _client:
|
|
246
|
+
response = await _client.request(**kwargs)
|
|
247
|
+
return response
|
|
248
|
+
|
|
249
|
+
async def async_get_api_domain_ip(
|
|
250
|
+
self,
|
|
251
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
252
|
+
**kwargs
|
|
253
|
+
) -> httpx.Response:
|
|
254
|
+
"""
|
|
255
|
+
获取企业微信API域名IP
|
|
256
|
+
|
|
257
|
+
@see:https://developer.work.weixin.qq.com/document/path/92520
|
|
258
|
+
Args:
|
|
259
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
260
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
264
|
+
"""
|
|
265
|
+
kwargs = kwargs or {}
|
|
266
|
+
kwargs = {
|
|
267
|
+
**{
|
|
268
|
+
"url": "/cgi-bin/get_api_domain_ip",
|
|
269
|
+
"method": "GET",
|
|
270
|
+
"params": {
|
|
271
|
+
"access_token": self.access_token,
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
**kwargs
|
|
275
|
+
}
|
|
276
|
+
if isinstance(client, httpx.Client):
|
|
277
|
+
response = client.request(**kwargs)
|
|
278
|
+
else:
|
|
279
|
+
async with self.async_client() as _client:
|
|
280
|
+
response = await _client.request(**kwargs)
|
|
281
|
+
return response
|
|
282
|
+
|
|
283
|
+
async def async_getcallbackip(
|
|
284
|
+
self,
|
|
285
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
286
|
+
**kwargs
|
|
287
|
+
) -> httpx.Response:
|
|
288
|
+
"""
|
|
289
|
+
获取企业微信回调IP
|
|
290
|
+
|
|
291
|
+
@see:https://developer.work.weixin.qq.com/document/path/92521
|
|
292
|
+
Args:
|
|
293
|
+
client: 可选的HTTP客户端实例,若不提供则自动创建
|
|
294
|
+
**kwargs: 请求参数,包含url、method、json等
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
httpx.Response: 发送响应结果,包含错误码和错误信息
|
|
298
|
+
"""
|
|
299
|
+
kwargs = kwargs or {}
|
|
300
|
+
kwargs = {
|
|
301
|
+
**{
|
|
302
|
+
"url": "/cgi-bin/getcallbackip",
|
|
303
|
+
"method": "GET",
|
|
304
|
+
"params": {
|
|
305
|
+
"access_token": self.access_token,
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
**kwargs
|
|
309
|
+
}
|
|
310
|
+
if isinstance(client, httpx.AsyncClient):
|
|
311
|
+
response = await client.request(**kwargs)
|
|
312
|
+
else:
|
|
313
|
+
async with self.async_client() as _client:
|
|
314
|
+
response = await _client.request(**kwargs)
|
|
315
|
+
return response
|
|
316
|
+
|
|
317
|
+
async def async_refresh_access_token(
|
|
318
|
+
self,
|
|
319
|
+
gettoken_func_kwargs: Optional[dict] = None,
|
|
320
|
+
get_api_domain_ip_func_kwargs: Optional[dict] = None
|
|
321
|
+
) -> "Base":
|
|
322
|
+
gettoken_func_kwargs = gettoken_func_kwargs or {}
|
|
323
|
+
get_api_domain_ip_func_kwargs = get_api_domain_ip_func_kwargs or {}
|
|
324
|
+
cache_key = self.cache_config.get("key", f"pywecom_server_{self.corpid}_{self.agentid}")
|
|
325
|
+
cache_expire = self.cache_config.get("expire", 7100)
|
|
326
|
+
cache_instance = self.cache_config.get("instance", None)
|
|
327
|
+
if not isinstance(cache_instance, Union[diskcache.Cache, redis.Redis]):
|
|
328
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
329
|
+
await self.async_gettoken(**gettoken_func_kwargs)
|
|
330
|
+
).access_token
|
|
331
|
+
else:
|
|
332
|
+
self.access_token = cache_instance.get(cache_key)
|
|
333
|
+
if not isinstance(self.access_token, str) or not len(self.access_token):
|
|
334
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
335
|
+
await self.async_gettoken(**gettoken_func_kwargs)
|
|
336
|
+
).access_token
|
|
337
|
+
ip_list = convert_to_errcode_eq_0(
|
|
338
|
+
await self.async_get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
339
|
+
).ip_list
|
|
340
|
+
if not isinstance(ip_list, list):
|
|
341
|
+
self.access_token = convert_to_errcode_eq_0(
|
|
342
|
+
await self.async_gettoken(**gettoken_func_kwargs)
|
|
343
|
+
).access_token
|
|
344
|
+
ip_list = convert_to_errcode_eq_0(
|
|
345
|
+
await self.async_get_api_domain_ip(**get_api_domain_ip_func_kwargs)
|
|
346
|
+
).ip_list
|
|
347
|
+
if isinstance(ip_list, list):
|
|
348
|
+
if isinstance(cache_instance, diskcache.Cache):
|
|
349
|
+
cache_instance.set(cache_key, self.access_token, expire=cache_expire)
|
|
350
|
+
elif isinstance(cache_instance, redis.Redis):
|
|
351
|
+
cache_instance.set(cache_key, self.access_token, ex=cache_expire)
|
|
352
|
+
return self
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
企业微信素材上传模块
|
|
5
|
+
|
|
6
|
+
该模块提供了企业微信素材上传的同步和异步方法,支持上传图片、视频、语音和普通文件。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from . import Base
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Uploader(Base):
|
|
17
|
+
"""
|
|
18
|
+
企业微信素材上传器类
|
|
19
|
+
|
|
20
|
+
继承自Base类,提供同步和异步的素材上传功能,支持多种文件类型的上传。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def upload(self, client: Optional[httpx.Client] = None, file_type: Optional[str] = "file", **kwargs):
|
|
24
|
+
"""
|
|
25
|
+
同步上传临时素材
|
|
26
|
+
|
|
27
|
+
调用企业微信API /cgi-bin/media/upload 上传临时素材,有效期为3天。
|
|
28
|
+
|
|
29
|
+
@see:https://developer.work.weixin.qq.com/document/path/90253
|
|
30
|
+
Args:
|
|
31
|
+
client: 可选的httpx.Client实例,用于复用连接。如果不传,将自动创建新的client。
|
|
32
|
+
file_type: 上传文件类型,支持 image、video、voice、file,默认为 file。
|
|
33
|
+
若传入的类型不在支持列表中,将自动转为 file。
|
|
34
|
+
**kwargs: 额外的请求参数,将与默认参数合并。
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
httpx.Response: API响应对象,包含上传后的素材ID等信息。
|
|
38
|
+
"""
|
|
39
|
+
# 验证文件类型,只支持指定的四种类型
|
|
40
|
+
file_type = "file" if file_type.lower() not in ["image", "video", "voice", "file"] else file_type
|
|
41
|
+
kwargs = kwargs or {}
|
|
42
|
+
kwargs = {
|
|
43
|
+
**{
|
|
44
|
+
"url": "/cgi-bin/media/upload",
|
|
45
|
+
"method": "POST",
|
|
46
|
+
"params": {
|
|
47
|
+
"access_token": self.access_token,
|
|
48
|
+
"type": file_type,
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
**kwargs
|
|
52
|
+
}
|
|
53
|
+
if isinstance(client, httpx.Client):
|
|
54
|
+
response = client.request(**kwargs)
|
|
55
|
+
else:
|
|
56
|
+
with self.client() as _client:
|
|
57
|
+
response = _client.request(**kwargs)
|
|
58
|
+
return response
|
|
59
|
+
|
|
60
|
+
def uploadimg(self, client: Optional[httpx.Client] = None, **kwargs):
|
|
61
|
+
"""
|
|
62
|
+
同步上传图片素材
|
|
63
|
+
|
|
64
|
+
调用企业微信API /cgi-bin/media/uploadimg 上传图片,返回的图片URL永久有效。
|
|
65
|
+
|
|
66
|
+
@see:https://developer.work.weixin.qq.com/document/path/90256
|
|
67
|
+
Args:
|
|
68
|
+
client: 可选的httpx.Client实例,用于复用连接。如果不传,将自动创建新的client。
|
|
69
|
+
**kwargs: 额外的请求参数,将与默认参数合并。
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
httpx.Response: API响应对象,包含图片URL等信息。
|
|
73
|
+
"""
|
|
74
|
+
kwargs = kwargs or {}
|
|
75
|
+
kwargs = {
|
|
76
|
+
**{
|
|
77
|
+
"url": "/cgi-bin/media/uploadimg",
|
|
78
|
+
"method": "POST",
|
|
79
|
+
"params": {
|
|
80
|
+
"access_token": self.access_token,
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
**kwargs
|
|
84
|
+
}
|
|
85
|
+
if isinstance(client, httpx.Client):
|
|
86
|
+
response = client.request(**kwargs)
|
|
87
|
+
else:
|
|
88
|
+
with self.client() as _client:
|
|
89
|
+
response = _client.request(**kwargs)
|
|
90
|
+
return response
|
|
91
|
+
|
|
92
|
+
async def async_upload(self, client: Optional[httpx.AsyncClient] = None, file_type: Optional[str] = "file",
|
|
93
|
+
**kwargs):
|
|
94
|
+
"""
|
|
95
|
+
异步上传临时素材
|
|
96
|
+
|
|
97
|
+
调用企业微信API /cgi-bin/media/upload 异步上传临时素材,有效期为3天。
|
|
98
|
+
|
|
99
|
+
@see:https://developer.work.weixin.qq.com/document/path/90253
|
|
100
|
+
Args:
|
|
101
|
+
client: 可选的httpx.AsyncClient实例,用于复用连接。如果不传,将自动创建新的client。
|
|
102
|
+
file_type: 上传文件类型,支持 image、video、voice、file,默认为 file。
|
|
103
|
+
若传入的类型不在支持列表中,将自动转为 file。
|
|
104
|
+
**kwargs: 额外的请求参数,将与默认参数合并。
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
httpx.Response: API响应对象,包含上传后的素材ID等信息。
|
|
108
|
+
"""
|
|
109
|
+
# 验证文件类型,只支持指定的四种类型
|
|
110
|
+
file_type = "file" if file_type.lower() not in ["image", "video", "voice", "file"] else file_type
|
|
111
|
+
kwargs = kwargs or {}
|
|
112
|
+
kwargs = {
|
|
113
|
+
**{
|
|
114
|
+
"url": "/cgi-bin/media/upload",
|
|
115
|
+
"method": "POST",
|
|
116
|
+
"params": {
|
|
117
|
+
"access_token": self.access_token,
|
|
118
|
+
"type": file_type,
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
**kwargs
|
|
122
|
+
}
|
|
123
|
+
if isinstance(client, httpx.AsyncClient):
|
|
124
|
+
response = await client.request(**kwargs)
|
|
125
|
+
else:
|
|
126
|
+
async with self.async_client() as _client:
|
|
127
|
+
response = await _client.request(**kwargs)
|
|
128
|
+
return response
|
|
129
|
+
|
|
130
|
+
async def async_uploadimg(self, client: Optional[httpx.AsyncClient] = None, **kwargs):
|
|
131
|
+
"""
|
|
132
|
+
异步上传图片素材
|
|
133
|
+
|
|
134
|
+
调用企业微信API /cgi-bin/media/uploadimg 异步上传图片,返回的图片URL永久有效。
|
|
135
|
+
|
|
136
|
+
@see:https://developer.work.weixin.qq.com/document/path/90256
|
|
137
|
+
Args:
|
|
138
|
+
client: 可选的httpx.AsyncClient实例,用于复用连接。如果不传,将自动创建新的client。
|
|
139
|
+
**kwargs: 额外的请求参数,将与默认参数合并。
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
httpx.Response: API响应对象,包含图片URL等信息。
|
|
143
|
+
"""
|
|
144
|
+
kwargs = kwargs or {}
|
|
145
|
+
kwargs = {
|
|
146
|
+
**{
|
|
147
|
+
"url": "/cgi-bin/media/uploadimg",
|
|
148
|
+
"method": "POST",
|
|
149
|
+
"params": {
|
|
150
|
+
"access_token": self.access_token,
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
**kwargs
|
|
154
|
+
}
|
|
155
|
+
if isinstance(client, httpx.AsyncClient):
|
|
156
|
+
response = await client.request(**kwargs)
|
|
157
|
+
else:
|
|
158
|
+
async with self.async_client() as _client:
|
|
159
|
+
response = await _client.request(**kwargs)
|
|
160
|
+
return response
|