py-wecom-toolkit 3.0.0__tar.gz → 3.0.2__tar.gz

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.
Files changed (43) hide show
  1. py_wecom_toolkit-3.0.2/PKG-INFO +492 -0
  2. py_wecom_toolkit-3.0.2/README.md +440 -0
  3. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/pyproject.toml +3 -1
  4. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/__init__.py +1 -1
  5. py_wecom_toolkit-3.0.0/PKG-INFO +0 -264
  6. py_wecom_toolkit-3.0.0/README.md +0 -214
  7. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/.gitignore +0 -0
  8. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/EXAMPLES.md +0 -0
  9. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/LICENSE +0 -0
  10. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/.env.example +0 -0
  11. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/basic_async.py +0 -0
  12. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/basic_sync.py +0 -0
  13. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/config.py +0 -0
  14. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/send_message.py +0 -0
  15. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/server/upload_file.py +0 -0
  16. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/.env.example +0 -0
  17. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/async_basic.py +0 -0
  18. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/async_file.py +0 -0
  19. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/async_text.py +0 -0
  20. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/basic.py +0 -0
  21. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/config.py +0 -0
  22. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/file.py +0 -0
  23. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/image.py +0 -0
  24. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/markdown.py +0 -0
  25. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/markdown_v2.py +0 -0
  26. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/news.py +0 -0
  27. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/template_card.py +0 -0
  28. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/test_image.png +0 -0
  29. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/text.py +0 -0
  30. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/examples/webhook/voice.py +0 -0
  31. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/__init__.py +0 -0
  32. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/cache.py +0 -0
  33. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/client.py +0 -0
  34. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/exceptions.py +0 -0
  35. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/media.py +0 -0
  36. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/message.py +0 -0
  37. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/models.py +0 -0
  38. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/server/token.py +0 -0
  39. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/webhook/__init__.py +0 -0
  40. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/webhook/client.py +0 -0
  41. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/webhook/exceptions.py +0 -0
  42. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/webhook/models.py +0 -0
  43. {py_wecom_toolkit-3.0.0 → py_wecom_toolkit-3.0.2}/src/py_wecom_toolkit/webhook/types.py +0 -0
@@ -0,0 +1,492 @@
1
+ Metadata-Version: 2.1
2
+ Name: py-wecom-toolkit
3
+ Version: 3.0.2
4
+ Summary: 企业微信 Python 扩展包 - 支持 Webhook 与 Server 端 API
5
+ Project-URL: Homepage, https://gitee.com/guolei19850528/py_wecom_toolkit
6
+ Project-URL: Documentation, https://gitee.com/guolei19850528/py_wecom_toolkit#readme
7
+ Author-email: 郭磊 <174000902@qq.com>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2024 py-wecom-toolkit
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: enterprise,webhook,wechat,wecom,work
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.9
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Requires-Python: >=3.9
40
+ Requires-Dist: httpx>=0.24.0
41
+ Requires-Dist: pydantic>=2.0.0
42
+ Provides-Extra: dev
43
+ Requires-Dist: build>=1.0.0; extra == 'dev'
44
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
45
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
46
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
47
+ Requires-Dist: twine>=4.0.0; extra == 'dev'
48
+ Provides-Extra: server
49
+ Requires-Dist: diskcache>=5.6.0; extra == 'server'
50
+ Requires-Dist: redis>=5.0.0; extra == 'server'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # py-wecom-toolkit
54
+
55
+ 企业微信 Python 扩展包 - 支持 Webhook 与 Server 端 API.
56
+
57
+ - **版本**: 3.0.2
58
+ - **作者**: 郭磊
59
+ - **邮箱**: 174000902@qq.com
60
+ - **项目主页**: https://gitee.com/guolei19850528/py_wecom_toolkit
61
+ - **PyPI**: https://pypi.org/project/py-wecom-toolkit/
62
+
63
+ ## 项目结构
64
+
65
+ ```
66
+ py-wecom-toolkit/
67
+ ├── pyproject.toml # 项目构建配置
68
+ ├── README.md # 项目说明文档
69
+ ├── LICENSE # MIT 许可证
70
+ ├── src/
71
+ │ └── py_wecom_toolkit/ # 源代码包
72
+ │ ├── __init__.py # 包入口,导出所有公共类
73
+ │ ├── webhook/ # Webhook 消息发送模块(原“群机器人”)
74
+ │ │ ├── __init__.py
75
+ │ │ ├── client.py # WebhookClient / AsyncWebhookClient
76
+ │ │ ├── exceptions.py # Webhook 异常定义
77
+ │ │ ├── models.py # pydantic v2 消息模型(8种消息类型)
78
+ │ │ └── types.py # 类型别名
79
+ │ └── server/ # Server 端 API 模块(企业微信服务端接口)
80
+ │ ├── __init__.py
81
+ │ ├── client.py # WeComServerClient / AsyncWeComServerClient
82
+ │ ├── token.py # AccessToken 管理(支持缓存)
83
+ │ ├── cache.py # MemoryCache / DiskCache / RedisCache
84
+ │ ├── models.py # Server API 响应模型
85
+ │ ├── exceptions.py # Server 异常定义
86
+ │ ├── message.py # 应用消息发送(MessageSendMixin)
87
+ │ └── media.py # 素材上传/下载(MediaMixin)
88
+ ├── tests/ # 测试用例
89
+ │ ├── __init__.py
90
+ │ ├── webhook/ # Webhook 测试
91
+ │ │ ├── __init__.py
92
+ │ │ ├── test_client.py # 同步客户端测试
93
+ │ │ ├── test_async_client.py # 异步客户端测试
94
+ │ │ ├── test_models.py # 消息模型测试
95
+ │ │ └── test_integration.py # 真实集成测试
96
+ │ └── server/ # Server 端测试
97
+ │ ├── __init__.py
98
+ │ ├── test_client.py # 同步客户端测试
99
+ │ ├── test_async_client.py # 异步客户端测试
100
+ │ ├── test_token.py # AccessToken 管理器测试
101
+ │ ├── test_cache.py # 缓存测试
102
+ │ ├── test_integration_real.py # 真实集成测试
103
+ │ └── test_upload_readme_to_guolei.py # 上传README并发送测试
104
+ └── examples/ # 演示代码
105
+ ├── webhook/ # Webhook 示例
106
+ │ ├── README.md
107
+ │ ├── .env.example # 环境变量模板
108
+ │ ├── config.py # 配置读取
109
+ │ ├── basic.py # 基础用法(上下文管理器、手动连接、自定义配置、原始dict)
110
+ │ ├── text.py # 文本消息示例
111
+ │ ├── markdown.py # Markdown 消息示例
112
+ │ ├── markdown_v2.py # Markdown V2 消息示例
113
+ │ ├── image.py # 图片消息示例
114
+ │ ├── news.py # 图文消息示例
115
+ │ ├── file.py # 文件消息示例
116
+ │ ├── voice.py # 语音消息示例
117
+ │ ├── template_card.py # 模版卡片消息示例
118
+ │ ├── async_basic.py # 异步基础示例
119
+ │ ├── async_text.py # 异步文本消息示例
120
+ │ └── async_file.py # 异步文件消息示例
121
+ └── server/ # Server 端示例
122
+ ├── README.md
123
+ ├── .env.example # 环境变量模板
124
+ ├── config.py # 配置读取
125
+ ├── basic_sync.py # 同步基础示例
126
+ ├── basic_async.py # 异步基础示例
127
+ ├── send_message.py # 发送应用消息示例
128
+ └── upload_file.py # 上传文件并发送示例
129
+ ```
130
+
131
+ ## 安装
132
+
133
+ ```bash
134
+ pip install py-wecom-toolkit
135
+ ```
136
+
137
+ 安装包含 Server 端支持 (diskcache / redis 缓存):
138
+
139
+ ```bash
140
+ pip install py-wecom-toolkit[server]
141
+ ```
142
+
143
+ 开发环境(包含测试、代码检查、打包、上传工具):
144
+
145
+ ```bash
146
+ pip install py-wecom-toolkit[dev]
147
+ ```
148
+
149
+ ## 功能特性
150
+
151
+ ### Webhook 模块
152
+ - 支持全部 8 种 Webhook 消息类型: text, markdown, markdown_v2, image, news, file, voice, template_card
153
+ - 基于 pydantic v2 的消息模型验证
154
+ - 基于 httpx 的异步 HTTP 客户端
155
+ - 同步与异步客户端双模式支持 (`WebhookClient` / `AsyncWebhookClient`)
156
+ - 消息发送支持 @ 成员列表合并与去重
157
+ - 文件上传接口 (base64 编码 + md5 校验)
158
+
159
+ ### Server 端模块
160
+ - **access_token 管理**: 自动获取与缓存 (支持 memory/diskcache/redis 三种后端)
161
+ - **接口 IP 段**: 获取企业微信服务器 IP 段
162
+ - **回调 IP 段**: 获取企业微信回调服务器 IP 段
163
+ - **应用消息发送**: 文本、Markdown、图片、文件、语音、图文、模版卡片等
164
+ - **素材管理**: 上传临时素材、下载临时素材
165
+ - 同步与异步客户端双模式支持 (`WeComServerClient` / `AsyncWeComServerClient`)
166
+ - agentid 支持:可在客户端初始化时指定,发送消息时未传则自动使用客户端 agentid
167
+
168
+ ## 快速开始
169
+
170
+ ### Webhook 消息发送
171
+
172
+ ```python
173
+ import asyncio
174
+ from py_wecom_toolkit import WebhookClient
175
+
176
+ async def main():
177
+ client = WebhookClient(key="你的webhook_key")
178
+
179
+ # 发送文本消息
180
+ await client.send_text("Hello, World!")
181
+
182
+ # 发送文本消息并 @某人
183
+ await client.send_text(
184
+ "你好, 请查看",
185
+ mentioned_list=["zhangsan"],
186
+ mentioned_mobile_list=["13800001111"],
187
+ )
188
+
189
+ # 发送 Markdown 消息
190
+ await client.send_markdown(
191
+ '实时新增用户反馈<font color="warning">132例</font>'
192
+ )
193
+
194
+ # 发送图文消息
195
+ await client.send_news([{
196
+ "title": "中秋节礼品领取",
197
+ "description": "今年中秋节公司有豪礼相送",
198
+ "url": "https://work.weixin.qq.com",
199
+ "picurl": "https://example.com/pic.png",
200
+ }])
201
+
202
+ # 发送图片
203
+ await client.send_image("/path/to/image.png")
204
+
205
+ # 上传文件并发送
206
+ media_id = await client.upload_media("/path/to/file.pdf")
207
+ await client.send_file(media_id)
208
+
209
+ await client.close()
210
+
211
+ asyncio.run(main())
212
+ ```
213
+
214
+ ### Webhook 消息发送 (同步)
215
+
216
+ ```python
217
+ from py_wecom_toolkit import WebhookClient
218
+
219
+ # 创建同步客户端,支持上下文管理器自动管理连接池
220
+ with WebhookClient(key="你的webhook_key") as client:
221
+
222
+ # 发送文本消息
223
+ resp = client.send_text("Hello, World!")
224
+ print(f"发送结果: {resp.errmsg}")
225
+
226
+ # 发送文本消息并 @某人
227
+ resp = client.send_text(
228
+ "你好, 请查看",
229
+ mentioned_list=["zhangsan"],
230
+ mentioned_mobile_list=["13800001111"],
231
+ )
232
+ print(f"发送结果: {resp.errmsg}")
233
+
234
+ # 发送 Markdown 消息
235
+ resp = client.send_markdown(
236
+ '实时新增用户反馈<font color="warning">132例</font>'
237
+ )
238
+ print(f"发送结果: {resp.errmsg}")
239
+
240
+ # 发送图文消息
241
+ resp = client.send_news([{
242
+ "title": "中秋节礼品领取",
243
+ "description": "今年中秋节公司有豪礼相送",
244
+ "url": "https://work.weixin.qq.com",
245
+ "picurl": "https://example.com/pic.png",
246
+ }])
247
+ print(f"发送结果: {resp.errmsg}")
248
+
249
+ # 发送图片
250
+ resp = client.send_image("/path/to/image.png")
251
+ print(f"发送结果: {resp.errmsg}")
252
+
253
+ # 上传文件并发送
254
+ media_id = client.upload_media("/path/to/file.pdf")
255
+ resp = client.send_file(media_id)
256
+ print(f"发送结果: {resp.errmsg}")
257
+ ```
258
+
259
+ ### Server 端 API (同步)
260
+
261
+ ```python
262
+ from py_wecom_toolkit import WeComServerClient
263
+
264
+ # 关键配置请通过环境变量或配置文件读取,不要硬编码在代码中
265
+ client = WeComServerClient(
266
+ corpid="你的CorpID",
267
+ corpsecret="你的CorpSecret",
268
+ agentid="你的AgentID",
269
+ )
270
+
271
+ # 获取 access_token (自动缓存)
272
+ token = client.get_access_token()
273
+ print(f"access_token: {token}")
274
+
275
+ # 获取企业微信接口 IP 段
276
+ iplist = client.get_iplist()
277
+ print(f"接口 IP 段: {iplist.ip_list}")
278
+
279
+ # 获取企业微信回调 IP 段
280
+ callback_ip = client.get_callback_ip()
281
+ print(f"回调 IP 段: {callback_ip.ip_list}")
282
+
283
+ # 发送文本应用消息
284
+ resp = client.send_text(
285
+ content="Hello, WeCom!",
286
+ touser="Guolei",
287
+ )
288
+ print(f"发送结果: {resp.errmsg}")
289
+
290
+ # 上传素材并发送文件消息
291
+ media = client.upload_media("/path/to/README.md", media_type="file")
292
+ resp = client.send_file(media.media_id, touser="Guolei")
293
+ print(f"发送文件结果: {resp.errmsg}")
294
+
295
+ client.close()
296
+ ```
297
+
298
+ ### Server 端 API (异步)
299
+
300
+ ```python
301
+ import asyncio
302
+ from py_wecom_toolkit import AsyncWeComServerClient
303
+
304
+ async def main():
305
+ client = AsyncWeComServerClient(
306
+ corpid="你的CorpID",
307
+ corpsecret="你的CorpSecret",
308
+ agentid="你的AgentID",
309
+ )
310
+
311
+ # 获取 access_token (自动缓存)
312
+ token = await client.async_get_access_token()
313
+ print(f"access_token: {token}")
314
+
315
+ # 获取接口 IP 段
316
+ iplist = await client.async_get_iplist()
317
+ print(f"接口 IP 段: {iplist.ip_list}")
318
+
319
+ # 发送文本应用消息
320
+ resp = await client.async_send_text(
321
+ content="Hello, WeCom Async!",
322
+ touser="Guolei",
323
+ )
324
+ print(f"发送结果: {resp.errmsg}")
325
+
326
+ # 上传素材并发送文件消息
327
+ media = await client.async_upload_media("/path/to/README.md", media_type="file")
328
+ resp = await client.async_send_file(media.media_id, touser="Guolei")
329
+ print(f"发送文件结果: {resp.errmsg}")
330
+
331
+ await client.close()
332
+
333
+ asyncio.run(main())
334
+ ```
335
+
336
+ ### 高级用法
337
+
338
+ ```python
339
+ import httpx
340
+ from py_wecom_toolkit import WebhookClient
341
+
342
+ # 使用自定义 httpx 客户端
343
+ async with httpx.AsyncClient(timeout=30.0) as http_client:
344
+ client = WebhookClient(key="xxx", client=http_client)
345
+ await client.send_text("使用自定义客户端")
346
+
347
+ # 使用上下文管理器
348
+ async with WebhookClient(key="xxx") as client:
349
+ await client.send_text("自动管理连接池")
350
+
351
+ # 使用 diskcache 缓存 access_token
352
+ from py_wecom_toolkit.server.cache import DiskCache
353
+ client = WeComServerClient(
354
+ corpid="xxx",
355
+ corpsecret="yyy",
356
+ agentid="zzz",
357
+ cache=DiskCache(directory=".wecom_cache"),
358
+ )
359
+
360
+ # 使用 redis 缓存 access_token
361
+ from py_wecom_toolkit.server.cache import RedisCache
362
+ client = WeComServerClient(
363
+ corpid="xxx",
364
+ corpsecret="yyy",
365
+ agentid="zzz",
366
+ cache=RedisCache(host="localhost", port=6379),
367
+ )
368
+ ```
369
+
370
+ ## API 参考
371
+
372
+ ### WebhookClient / AsyncWebhookClient
373
+
374
+ | 方法 | 说明 |
375
+ |------|------|
376
+ | `send(message)` / `async_send(message)` | 发送任意类型的消息 |
377
+ | `send_text(content, ...)` / `async_send_text(...)` | 发送文本消息 |
378
+ | `send_markdown(content)` / `async_send_markdown(content)` | 发送 Markdown 消息 |
379
+ | `send_markdown_v2(content)` / `async_send_markdown_v2(content)` | 发送 Markdown V2 消息 |
380
+ | `send_image(path)` / `async_send_image(path)` | 发送图片消息 |
381
+ | `send_news(articles)` / `async_send_news(articles)` | 发送图文消息 |
382
+ | `send_file(media_id)` / `async_send_file(media_id)` | 发送文件消息 |
383
+ | `send_voice(media_id)` / `async_send_voice(media_id)` | 发送语音消息 |
384
+ | `send_template_card(card)` / `async_send_template_card(card)` | 发送模版卡片消息 |
385
+ | `upload_media(path, type)` / `async_upload_media(path, type)` | 上传媒体文件 |
386
+
387
+ ### WeComServerClient / AsyncWeComServerClient
388
+
389
+ | 方法 | 说明 |
390
+ |------|------|
391
+ | `get_access_token()` / `async_get_access_token()` | 获取 access_token (自动缓存) |
392
+ | `get_iplist()` / `async_get_iplist()` | 获取企业微信接口 IP 段 |
393
+ | `get_callback_ip()` / `async_get_callback_ip()` | 获取企业微信回调 IP 段 |
394
+ | `send_text(content, touser, ...)` / `async_send_text(...)` | 发送文本应用消息 |
395
+ | `send_message(request)` / `async_send_message(request)` | 发送任意类型应用消息 |
396
+ | `send_file(media_id, touser, ...)` / `async_send_file(...)` | 发送文件应用消息 |
397
+ | `upload_media(path, type)` / `async_upload_media(path, type)` | 上传临时素材 |
398
+ | `download_media(media_id)` / `async_download_media(media_id)` | 下载临时素材 |
399
+
400
+ ## 测试用例
401
+
402
+ 项目包含完整的单元测试和集成测试:
403
+
404
+ ```bash
405
+ # 运行所有测试
406
+ pytest tests/
407
+
408
+ # 仅运行 Webhook 测试
409
+ pytest tests/webhook/
410
+
411
+ # 仅运行 Server 端测试
412
+ pytest tests/server/
413
+
414
+ # 运行真实集成测试(需要配置有效的 Webhook Key / CorpID/CorpSecret/AgentID)
415
+ pytest tests/webhook/test_integration.py
416
+ pytest tests/server/test_integration_real.py
417
+ ```
418
+
419
+ 测试覆盖:
420
+ - `tests/webhook/test_client.py` - 同步 Webhook 客户端测试
421
+ - `tests/webhook/test_async_client.py` - 异步 Webhook 客户端测试
422
+ - `tests/webhook/test_models.py` - Webhook 消息模型测试
423
+ - `tests/webhook/test_integration.py` - Webhook 真实集成测试
424
+ - `tests/server/test_client.py` - 同步 Server 客户端测试
425
+ - `tests/server/test_async_client.py` - 异步 Server 客户端测试
426
+ - `tests/server/test_token.py` - AccessToken 管理器测试
427
+ - `tests/server/test_cache.py` - 缓存测试 (memory/diskcache/redis)
428
+ - `tests/server/test_integration_real.py` - Server 端真实集成测试
429
+ - `tests/server/test_upload_readme_to_guolei.py` - 上传 README 并发送测试
430
+
431
+ ## 演示代码
432
+
433
+ ### Webhook 示例 (`examples/webhook/`)
434
+
435
+ | 文件 | 说明 |
436
+ |------|------|
437
+ | `basic.py` | 基础用法(上下文管理器、手动连接、自定义配置、原始dict) |
438
+ | `text.py` | 文本消息示例 |
439
+ | `markdown.py` | Markdown 消息示例 |
440
+ | `markdown_v2.py` | Markdown V2 消息示例 |
441
+ | `image.py` | 图片消息示例 |
442
+ | `news.py` | 图文消息示例 |
443
+ | `file.py` | 文件消息示例 |
444
+ | `voice.py` | 语音消息示例 |
445
+ | `template_card.py` | 模版卡片消息示例 |
446
+ | `async_basic.py` | 异步基础示例 |
447
+ | `async_text.py` | 异步文本消息示例 |
448
+ | `async_file.py` | 异步文件消息示例 |
449
+ | `config.py` | 配置读取 |
450
+ | `.env.example` | 环境变量模板 |
451
+
452
+ ### Server 端示例 (`examples/server/`)
453
+
454
+ | 文件 | 说明 |
455
+ |------|------|
456
+ | `basic_sync.py` | 同步基础示例(access_token / IP 段) |
457
+ | `basic_async.py` | 异步基础示例(access_token / IP 段) |
458
+ | `send_message.py` | 发送文本/Markdown 应用消息示例 |
459
+ | `upload_file.py` | 上传文件并发送文件应用消息示例 |
460
+ | `config.py` | 配置读取 |
461
+ | `.env.example` | 环境变量模板 |
462
+
463
+ 使用前请先配置环境变量或 `.env` 文件:
464
+
465
+ ```bash
466
+ cp examples/webhook/.env.example examples/webhook/.env
467
+ # 或
468
+ cp examples/server/.env.example examples/server/.env
469
+ ```
470
+
471
+ ## 构建与发布
472
+
473
+ ```bash
474
+ # 安装开发依赖
475
+ pip install py-wecom-toolkit[dev]
476
+
477
+ # 构建分发包
478
+ python -m build
479
+
480
+ # 上传到 PyPI
481
+ python -m twine upload dist/*
482
+ ```
483
+
484
+ ## 消息类型参考
485
+
486
+ 参考 [企业微信文档](https://developer.work.weixin.qq.com/document/path/91770) 获取 Webhook 消息格式说明.
487
+
488
+ 参考 [企业微信服务端API](https://developer.work.weixin.qq.com/document/path/90664) 获取 Server 端 API 说明.
489
+
490
+ ## License
491
+
492
+ MIT