py-wecom-sdk 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 郭磊
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,323 @@
1
+ Metadata-Version: 2.3
2
+ Name: py-wecom-sdk
3
+ Version: 0.1.0
4
+ Summary: 企业微信(WeCom)开放接口的 Python SDK,基于 httpx + pydantic,同步与异步统一封装。
5
+ Keywords: wecom,wechat-work,企业微信,sdk,httpx,pydantic,群机器人
6
+ Author: guolei
7
+ Author-email: guolei <174000902@qq.com>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 郭磊
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
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3.10
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Classifier: Programming Language :: Python :: 3 :: Only
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Classifier: Typing :: Typed
41
+ Requires-Dist: anyio>=4.16.0
42
+ Requires-Dist: httpx>=0.28.1
43
+ Requires-Dist: pydantic>=2.14.3
44
+ Requires-Dist: diskcache>=5.6.0 ; extra == 'all'
45
+ Requires-Dist: redis>=5.0.0 ; extra == 'all'
46
+ Requires-Dist: diskcache>=5.6.0 ; extra == 'diskcache'
47
+ Requires-Dist: redis>=5.0.0 ; extra == 'redis'
48
+ Maintainer: guolei
49
+ Maintainer-email: guolei <174000902@qq.com>
50
+ Requires-Python: >=3.10
51
+ Project-URL: Homepage, https://gitee.com/guolei19850528/py_wecom_sdk
52
+ Project-URL: Repository, https://gitee.com/guolei19850528/py_wecom_sdk.git
53
+ Project-URL: Issues, https://gitee.com/guolei19850528/py_wecom_sdk/issues
54
+ Provides-Extra: all
55
+ Provides-Extra: diskcache
56
+ Provides-Extra: redis
57
+ Description-Content-Type: text/markdown
58
+
59
+ # py_wecom_sdk
60
+
61
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
62
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org/licenses/MIT)
63
+ [![httpx](https://img.shields.io/badge/httpx-0.28%2B-orange)](https://github.com/encode/httpx)
64
+ [![pydantic](https://img.shields.io/badge/pydantic-2.14%2B-emerald)](https://github.com/pydantic/pydantic)
65
+
66
+ 企业微信(WeCom)开放接口的 Python SDK,基于 **httpx + pydantic v2**,**同步 / 异步统一封装**。
67
+
68
+ > 官方文档:<https://developer.work.weixin.qq.com/document/path/91770>
69
+
70
+ ---
71
+
72
+ ## ✨ 特性
73
+
74
+ - **同步 / 异步统一 API** — `GroupRobotAPI` 与 `GroupRobotAsyncAPI` 接口完全一致,只需替换 import
75
+ - **类型安全** — pydantic v2 自动校验所有请求 / 响应,Mypy strict 模式 100% 通过
76
+ - **零魔法基类** — 同步 / 异步共享 `_BaseAPI`,初始化和资源管理逻辑一次实现
77
+ - **丰富的消息类型** — 文本、Markdown、Markdown V2、图片、图文、文件、语音、模板卡片
78
+ - **素材上传支持** — `upload_media()` 统一处理文件 / 语音上传并返回 `media_id`
79
+ - **HTTP 连接池管理** — `close()` / `with` / `async with` 三种方式释放连接
80
+ - **企业级依赖** — httpx(encode 维护)+ pydantic v2(比 v1 快 5-50 倍)
81
+
82
+ ## 📦 安装
83
+
84
+ ### 使用 pip
85
+
86
+ ```bash
87
+ pip install py-wecom-sdk
88
+ ```
89
+
90
+ ### 使用 uv(推荐)
91
+
92
+ ```bash
93
+ uv add py-wecom-sdk
94
+ ```
95
+
96
+ ### 安装可选依赖
97
+
98
+ ```bash
99
+ # DiskCache 本地缓存后端
100
+ uv add "py-wecom-sdk[diskcache]"
101
+
102
+ # Redis 分布式缓存后端
103
+ uv add "py-wecom-sdk[redis]"
104
+
105
+ # 全部可选依赖
106
+ uv add "py-wecom-sdk[all]"
107
+ ```
108
+
109
+ ## 🚀 快速开始
110
+
111
+ ### 同步客户端
112
+
113
+ ```python
114
+ from py_wecom_sdk import WeComAPIProvider
115
+
116
+ api = WeComAPIProvider.group_robot_api(key="YOUR_WEBHOOK_KEY")
117
+
118
+ # 发送文本
119
+ resp = api.send_text("Hello, WeCom!")
120
+ assert resp.ok
121
+
122
+ # 发送 Markdown
123
+ api.send_markdown("## 标题\n> <font color='info'>重要通知</font>")
124
+
125
+ # 发送图文
126
+ api.send_news([{"title": "标题", "url": "https://example.com"}])
127
+
128
+ # 发送文件(需先上传)
129
+ uploaded = api.upload_media("/path/to/file.pdf", media_type="file")
130
+ api.send_file(uploaded.media_id)
131
+
132
+ # 释放连接池
133
+ api.close()
134
+ # 或使用上下文管理器:
135
+ # with WeComAPIProvider.group_robot_api(key="YOUR_KEY") as api: ...
136
+ ```
137
+
138
+ ### 异步客户端
139
+
140
+ ```python
141
+ import asyncio
142
+ from py_wecom_sdk import WeComAPIProvider
143
+
144
+ async def main():
145
+ async with WeComAPIProvider.group_robot_async_api(key="YOUR_WEBHOOK_KEY") as api:
146
+ resp = await api.send_text("Hello from async!")
147
+ assert resp.ok
148
+ await api.send_markdown_v2("**Markdown V2** 也很好用")
149
+ uploaded = await api.upload_media(b"fake data", filename="demo.txt", media_type="file")
150
+ await api.send_file(uploaded.media_id)
151
+
152
+ asyncio.run(main())
153
+ ```
154
+
155
+ ### 通过原始 dict 发送任意消息类型
156
+
157
+ 所有便捷方法底层都调用 `send(payload)`,你可以直接传入符合企业微信协议的字典:
158
+
159
+ ```python
160
+ api.send({"msgtype": "text", "text": {"content": "原始 dict", "mentioned_list": ["userid1"]}})
161
+ ```
162
+
163
+ ## 📖 功能清单
164
+
165
+ ### `GroupRobotAPI` / `GroupRobotAsyncAPI`
166
+
167
+ | 方法 | 功能 | 官方文档 |
168
+ |------|------|----------|
169
+ | `send(payload)` | 发送任意类型消息(原始 dict) | — |
170
+ | `send_text(content, mentioned_list, mentioned_mobile_list)` | 文本消息,支持 @ 指定成员 | [文本类型](https://developer.work.weixin.qq.com/document/path/91770#文本类型) |
171
+ | `send_markdown(content)` | Markdown 消息 | [Markdown 类型](https://developer.work.weixin.qq.com/document/path/91770#markdown类型) |
172
+ | `send_markdown_v2(content)` | Markdown V2 消息 | [Markdown V2](https://developer.work.weixin.qq.com/document/path/91770#markdown-v2类型) |
173
+ | `send_image(base64, md5)` | 图片消息(限 2MB,JPG / PNG) | [图片类型](https://developer.work.weixin.qq.com/document/path/91770#图片类型) |
174
+ | `send_news(articles)` | 图文消息(1-8 条) | [图文类型](https://developer.work.weixin.qq.com/document/path/91770#图文类型) |
175
+ | `send_file(media_id)` | 文件消息(需先上传) | [文件类型](https://developer.work.weixin.qq.com/document/path/91770#文件类型) |
176
+ | `send_voice(media_id)` | 语音消息(需先上传) | [语音类型](https://developer.work.weixin.qq.com/document/path/91770#语音类型) |
177
+ | `send_template_card(card)` | 模板卡片(text_notice / news_notice) | [模板卡片](https://developer.work.weixin.qq.com/document/path/91770#模版卡片类型) |
178
+ | `upload_media(media, filename, media_type)` | 上传文件 / 语音素材,返回 `media_id` | [文件上传接口](https://developer.work.weixin.qq.com/document/path/91770#文件上传接口) |
179
+
180
+ ### `send_text` 默认 @ 配置
181
+
182
+ 实例化时可传入默认 @ 列表,每次 `send_text` 会自动合并:
183
+
184
+ ```python
185
+ api = WeComAPIProvider.group_robot_api(
186
+ key="YOUR_KEY",
187
+ mentioned_list=["userid1"], # 每次自动 @ userid1
188
+ mentioned_mobile_list=["13800138000"],
189
+ )
190
+ api.send_text("重要通知", mentioned_list=["userid2"]) # 合并 @ userid1 + userid2
191
+ ```
192
+
193
+ ### `UploadMediaResponse.ok` 多一层校验
194
+
195
+ 上传响应除了业务成功(`errcode == 0`),还额外校验 `media_id` 非空:
196
+
197
+ ```python
198
+ uploaded = api.upload_media("/path/to/file.pdf", media_type="file")
199
+ if not uploaded.ok:
200
+ print(f"上传失败:errcode={uploaded.errcode}, errmsg={uploaded.errmsg}")
201
+ ```
202
+
203
+ ## 🏗️ 项目结构
204
+
205
+ ```
206
+ py_wecom_sdk/
207
+ ├── pyproject.toml # 项目配置(PEP 621 + Ruff + Mypy + Pytest)
208
+ ├── LICENSE # MIT License
209
+ ├── README.md # 中文文档
210
+ ├── README.en.md # 英文文档
211
+ ├── .python-version # 锁定 Python 版本(3.10)
212
+ ├── .gitignore
213
+ ├── .env.example # 示例 .env 文件
214
+ │
215
+ ├── src/
216
+ │ └── py_wecom_sdk/ # 包根目录(src-layout)
217
+ │ ├── __init__.py # 导出 WeComAPIProvider
218
+ │ ├── provider.py # APIProvider 统一入口
219
+ │ └── core/
220
+ │ ├── group_robot/ # 群机器人模块 ✅ 已实现
221
+ │ │ ├── __init__.py # 公共导出
222
+ │ │ ├── _api.py # GroupRobotAPI + GroupRobotAsyncAPI + _BaseAPI
223
+ │ │ └── _models.py # SendResponse / UploadMediaResponse / BaseResponse
224
+ │ └── token/ # access_token 模块 🚧 预留
225
+ │ ├── __init__.py
226
+ │ ├── _client.py
227
+ │ └── _models.py
228
+ │
229
+ ├── examples/
230
+ │ └── group_robot.py # 群机器人完整示例(同步 + 异步 + 多消息类型)
231
+ │
232
+ └── tests/
233
+ ├── conftest.py
234
+ └── test_group_robot.py # 单元测试(respx mock)
235
+ ```
236
+
237
+ ## 🛠️ 开发
238
+
239
+ ### 环境准备
240
+
241
+ ```bash
242
+ # 克隆仓库
243
+ git clone https://gitee.com/guolei19850528/py_wecom_sdk.git
244
+ cd py_wecom_sdk
245
+
246
+ # 安装 uv(包管理器)
247
+ curl -LsSf https://astral.sh/uv/install.sh | sh
248
+
249
+ # 创建虚拟环境 + 安装全部依赖(含 dev)
250
+ uv sync --group dev
251
+ ```
252
+
253
+ ### 代码质量
254
+
255
+ ```bash
256
+ # Ruff:lint(修复) + format
257
+ uv run ruff check --fix src tests
258
+ uv run ruff format src tests
259
+
260
+ # Mypy(strict 模式)
261
+ uv run mypy src
262
+ ```
263
+
264
+ ### 测试
265
+
266
+ ```bash
267
+ # 运行全部测试(离线,使用 respx mock)
268
+ uv run pytest
269
+
270
+ # 运行需要真实网络的集成测试
271
+ cp .env.example .env
272
+ # 编辑 .env 填入 WECOM_GROUP_ROBOT_API_KEY
273
+ uv run pytest -m real_network
274
+ ```
275
+
276
+ ### 示例运行
277
+
278
+ ```bash
279
+ # 编辑 .env 填入机器人 key
280
+ cp .env.example .env
281
+ vim .env
282
+
283
+ # 同步发送
284
+ uv run python examples/group_robot.py
285
+
286
+ # 异步发送 + 图片 / 文件 / 语音
287
+ uv run python examples/group_robot.py --async --image ./test.png --file ./report.pdf
288
+ ```
289
+
290
+ ### 构建发布
291
+
292
+ ```bash
293
+ # 构建 wheel + sdist
294
+ uv build
295
+
296
+ # 发布到 PyPI / Gitee
297
+ uv publish --repository gitee
298
+ ```
299
+
300
+ ## 📋 路线图
301
+
302
+ - [x] 群机器人模块(group_robot)
303
+ - [x] 同步 / 异步统一 API
304
+ - [x] 全部 9 种消息类型
305
+ - [x] 素材上传接口
306
+ - [x] pydantic 数据模型校验
307
+ - [ ] access_token 模块(token)
308
+ - [ ] 缓存后端:内存 / DiskCache / Redis
309
+ - [ ] 自动刷新 + 并发去重
310
+ - [ ] 通讯录模块(contact)
311
+ - [ ] 消息推送模块(message)
312
+
313
+ ## 📄 许可证
314
+
315
+ 本项目基于 **MIT License** 开源,详见 [LICENSE](LICENSE)。
316
+
317
+ ## 📎 相关链接
318
+
319
+ - [企业微信开发者中心](https://developer.work.weixin.qq.com/)
320
+ - [群机器人接口文档](https://developer.work.weixin.qq.com/document/path/91770)
321
+ - [httpx 官方仓库](https://github.com/encode/httpx)
322
+ - [pydantic v2 文档](https://docs.pydantic.dev/)
323
+ - [uv 官方仓库](https://github.com/astral-sh/uv)
@@ -0,0 +1,265 @@
1
+ # py_wecom_sdk
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org/licenses/MIT)
5
+ [![httpx](https://img.shields.io/badge/httpx-0.28%2B-orange)](https://github.com/encode/httpx)
6
+ [![pydantic](https://img.shields.io/badge/pydantic-2.14%2B-emerald)](https://github.com/pydantic/pydantic)
7
+
8
+ 企业微信(WeCom)开放接口的 Python SDK,基于 **httpx + pydantic v2**,**同步 / 异步统一封装**。
9
+
10
+ > 官方文档:<https://developer.work.weixin.qq.com/document/path/91770>
11
+
12
+ ---
13
+
14
+ ## ✨ 特性
15
+
16
+ - **同步 / 异步统一 API** — `GroupRobotAPI` 与 `GroupRobotAsyncAPI` 接口完全一致,只需替换 import
17
+ - **类型安全** — pydantic v2 自动校验所有请求 / 响应,Mypy strict 模式 100% 通过
18
+ - **零魔法基类** — 同步 / 异步共享 `_BaseAPI`,初始化和资源管理逻辑一次实现
19
+ - **丰富的消息类型** — 文本、Markdown、Markdown V2、图片、图文、文件、语音、模板卡片
20
+ - **素材上传支持** — `upload_media()` 统一处理文件 / 语音上传并返回 `media_id`
21
+ - **HTTP 连接池管理** — `close()` / `with` / `async with` 三种方式释放连接
22
+ - **企业级依赖** — httpx(encode 维护)+ pydantic v2(比 v1 快 5-50 倍)
23
+
24
+ ## 📦 安装
25
+
26
+ ### 使用 pip
27
+
28
+ ```bash
29
+ pip install py-wecom-sdk
30
+ ```
31
+
32
+ ### 使用 uv(推荐)
33
+
34
+ ```bash
35
+ uv add py-wecom-sdk
36
+ ```
37
+
38
+ ### 安装可选依赖
39
+
40
+ ```bash
41
+ # DiskCache 本地缓存后端
42
+ uv add "py-wecom-sdk[diskcache]"
43
+
44
+ # Redis 分布式缓存后端
45
+ uv add "py-wecom-sdk[redis]"
46
+
47
+ # 全部可选依赖
48
+ uv add "py-wecom-sdk[all]"
49
+ ```
50
+
51
+ ## 🚀 快速开始
52
+
53
+ ### 同步客户端
54
+
55
+ ```python
56
+ from py_wecom_sdk import WeComAPIProvider
57
+
58
+ api = WeComAPIProvider.group_robot_api(key="YOUR_WEBHOOK_KEY")
59
+
60
+ # 发送文本
61
+ resp = api.send_text("Hello, WeCom!")
62
+ assert resp.ok
63
+
64
+ # 发送 Markdown
65
+ api.send_markdown("## 标题\n> <font color='info'>重要通知</font>")
66
+
67
+ # 发送图文
68
+ api.send_news([{"title": "标题", "url": "https://example.com"}])
69
+
70
+ # 发送文件(需先上传)
71
+ uploaded = api.upload_media("/path/to/file.pdf", media_type="file")
72
+ api.send_file(uploaded.media_id)
73
+
74
+ # 释放连接池
75
+ api.close()
76
+ # 或使用上下文管理器:
77
+ # with WeComAPIProvider.group_robot_api(key="YOUR_KEY") as api: ...
78
+ ```
79
+
80
+ ### 异步客户端
81
+
82
+ ```python
83
+ import asyncio
84
+ from py_wecom_sdk import WeComAPIProvider
85
+
86
+ async def main():
87
+ async with WeComAPIProvider.group_robot_async_api(key="YOUR_WEBHOOK_KEY") as api:
88
+ resp = await api.send_text("Hello from async!")
89
+ assert resp.ok
90
+ await api.send_markdown_v2("**Markdown V2** 也很好用")
91
+ uploaded = await api.upload_media(b"fake data", filename="demo.txt", media_type="file")
92
+ await api.send_file(uploaded.media_id)
93
+
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ ### 通过原始 dict 发送任意消息类型
98
+
99
+ 所有便捷方法底层都调用 `send(payload)`,你可以直接传入符合企业微信协议的字典:
100
+
101
+ ```python
102
+ api.send({"msgtype": "text", "text": {"content": "原始 dict", "mentioned_list": ["userid1"]}})
103
+ ```
104
+
105
+ ## 📖 功能清单
106
+
107
+ ### `GroupRobotAPI` / `GroupRobotAsyncAPI`
108
+
109
+ | 方法 | 功能 | 官方文档 |
110
+ |------|------|----------|
111
+ | `send(payload)` | 发送任意类型消息(原始 dict) | — |
112
+ | `send_text(content, mentioned_list, mentioned_mobile_list)` | 文本消息,支持 @ 指定成员 | [文本类型](https://developer.work.weixin.qq.com/document/path/91770#文本类型) |
113
+ | `send_markdown(content)` | Markdown 消息 | [Markdown 类型](https://developer.work.weixin.qq.com/document/path/91770#markdown类型) |
114
+ | `send_markdown_v2(content)` | Markdown V2 消息 | [Markdown V2](https://developer.work.weixin.qq.com/document/path/91770#markdown-v2类型) |
115
+ | `send_image(base64, md5)` | 图片消息(限 2MB,JPG / PNG) | [图片类型](https://developer.work.weixin.qq.com/document/path/91770#图片类型) |
116
+ | `send_news(articles)` | 图文消息(1-8 条) | [图文类型](https://developer.work.weixin.qq.com/document/path/91770#图文类型) |
117
+ | `send_file(media_id)` | 文件消息(需先上传) | [文件类型](https://developer.work.weixin.qq.com/document/path/91770#文件类型) |
118
+ | `send_voice(media_id)` | 语音消息(需先上传) | [语音类型](https://developer.work.weixin.qq.com/document/path/91770#语音类型) |
119
+ | `send_template_card(card)` | 模板卡片(text_notice / news_notice) | [模板卡片](https://developer.work.weixin.qq.com/document/path/91770#模版卡片类型) |
120
+ | `upload_media(media, filename, media_type)` | 上传文件 / 语音素材,返回 `media_id` | [文件上传接口](https://developer.work.weixin.qq.com/document/path/91770#文件上传接口) |
121
+
122
+ ### `send_text` 默认 @ 配置
123
+
124
+ 实例化时可传入默认 @ 列表,每次 `send_text` 会自动合并:
125
+
126
+ ```python
127
+ api = WeComAPIProvider.group_robot_api(
128
+ key="YOUR_KEY",
129
+ mentioned_list=["userid1"], # 每次自动 @ userid1
130
+ mentioned_mobile_list=["13800138000"],
131
+ )
132
+ api.send_text("重要通知", mentioned_list=["userid2"]) # 合并 @ userid1 + userid2
133
+ ```
134
+
135
+ ### `UploadMediaResponse.ok` 多一层校验
136
+
137
+ 上传响应除了业务成功(`errcode == 0`),还额外校验 `media_id` 非空:
138
+
139
+ ```python
140
+ uploaded = api.upload_media("/path/to/file.pdf", media_type="file")
141
+ if not uploaded.ok:
142
+ print(f"上传失败:errcode={uploaded.errcode}, errmsg={uploaded.errmsg}")
143
+ ```
144
+
145
+ ## 🏗️ 项目结构
146
+
147
+ ```
148
+ py_wecom_sdk/
149
+ ├── pyproject.toml # 项目配置(PEP 621 + Ruff + Mypy + Pytest)
150
+ ├── LICENSE # MIT License
151
+ ├── README.md # 中文文档
152
+ ├── README.en.md # 英文文档
153
+ ├── .python-version # 锁定 Python 版本(3.10)
154
+ ├── .gitignore
155
+ ├── .env.example # 示例 .env 文件
156
+ │
157
+ ├── src/
158
+ │ └── py_wecom_sdk/ # 包根目录(src-layout)
159
+ │ ├── __init__.py # 导出 WeComAPIProvider
160
+ │ ├── provider.py # APIProvider 统一入口
161
+ │ └── core/
162
+ │ ├── group_robot/ # 群机器人模块 ✅ 已实现
163
+ │ │ ├── __init__.py # 公共导出
164
+ │ │ ├── _api.py # GroupRobotAPI + GroupRobotAsyncAPI + _BaseAPI
165
+ │ │ └── _models.py # SendResponse / UploadMediaResponse / BaseResponse
166
+ │ └── token/ # access_token 模块 🚧 预留
167
+ │ ├── __init__.py
168
+ │ ├── _client.py
169
+ │ └── _models.py
170
+ │
171
+ ├── examples/
172
+ │ └── group_robot.py # 群机器人完整示例(同步 + 异步 + 多消息类型)
173
+ │
174
+ └── tests/
175
+ ├── conftest.py
176
+ └── test_group_robot.py # 单元测试(respx mock)
177
+ ```
178
+
179
+ ## 🛠️ 开发
180
+
181
+ ### 环境准备
182
+
183
+ ```bash
184
+ # 克隆仓库
185
+ git clone https://gitee.com/guolei19850528/py_wecom_sdk.git
186
+ cd py_wecom_sdk
187
+
188
+ # 安装 uv(包管理器)
189
+ curl -LsSf https://astral.sh/uv/install.sh | sh
190
+
191
+ # 创建虚拟环境 + 安装全部依赖(含 dev)
192
+ uv sync --group dev
193
+ ```
194
+
195
+ ### 代码质量
196
+
197
+ ```bash
198
+ # Ruff:lint(修复) + format
199
+ uv run ruff check --fix src tests
200
+ uv run ruff format src tests
201
+
202
+ # Mypy(strict 模式)
203
+ uv run mypy src
204
+ ```
205
+
206
+ ### 测试
207
+
208
+ ```bash
209
+ # 运行全部测试(离线,使用 respx mock)
210
+ uv run pytest
211
+
212
+ # 运行需要真实网络的集成测试
213
+ cp .env.example .env
214
+ # 编辑 .env 填入 WECOM_GROUP_ROBOT_API_KEY
215
+ uv run pytest -m real_network
216
+ ```
217
+
218
+ ### 示例运行
219
+
220
+ ```bash
221
+ # 编辑 .env 填入机器人 key
222
+ cp .env.example .env
223
+ vim .env
224
+
225
+ # 同步发送
226
+ uv run python examples/group_robot.py
227
+
228
+ # 异步发送 + 图片 / 文件 / 语音
229
+ uv run python examples/group_robot.py --async --image ./test.png --file ./report.pdf
230
+ ```
231
+
232
+ ### 构建发布
233
+
234
+ ```bash
235
+ # 构建 wheel + sdist
236
+ uv build
237
+
238
+ # 发布到 PyPI / Gitee
239
+ uv publish --repository gitee
240
+ ```
241
+
242
+ ## 📋 路线图
243
+
244
+ - [x] 群机器人模块(group_robot)
245
+ - [x] 同步 / 异步统一 API
246
+ - [x] 全部 9 种消息类型
247
+ - [x] 素材上传接口
248
+ - [x] pydantic 数据模型校验
249
+ - [ ] access_token 模块(token)
250
+ - [ ] 缓存后端:内存 / DiskCache / Redis
251
+ - [ ] 自动刷新 + 并发去重
252
+ - [ ] 通讯录模块(contact)
253
+ - [ ] 消息推送模块(message)
254
+
255
+ ## 📄 许可证
256
+
257
+ 本项目基于 **MIT License** 开源,详见 [LICENSE](LICENSE)。
258
+
259
+ ## 📎 相关链接
260
+
261
+ - [企业微信开发者中心](https://developer.work.weixin.qq.com/)
262
+ - [群机器人接口文档](https://developer.work.weixin.qq.com/document/path/91770)
263
+ - [httpx 官方仓库](https://github.com/encode/httpx)
264
+ - [pydantic v2 文档](https://docs.pydantic.dev/)
265
+ - [uv 官方仓库](https://github.com/astral-sh/uv)