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.
- py_wecom_sdk-0.1.0/LICENSE +21 -0
- py_wecom_sdk-0.1.0/PKG-INFO +323 -0
- py_wecom_sdk-0.1.0/README.md +265 -0
- py_wecom_sdk-0.1.0/pyproject.toml +217 -0
- py_wecom_sdk-0.1.0/pyproject.toml.orig +218 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/__init__.py +6 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/group_robot/__init__.py +17 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/group_robot/_api.py +488 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/group_robot/_models.py +50 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/token/__init__.py +1 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/token/_client.py +1 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/core/token/_models.py +1 -0
- py_wecom_sdk-0.1.0/src/py_wecom_sdk/provider.py +20 -0
|
@@ -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
|
+
[](https://www.python.org/)
|
|
62
|
+
[](https://opensource.org/licenses/MIT)
|
|
63
|
+
[](https://github.com/encode/httpx)
|
|
64
|
+
[](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
|
+
[](https://www.python.org/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/encode/httpx)
|
|
6
|
+
[](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)
|