wechat-publish-sdk 1.0.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.
- wechat_publish_sdk-1.0.0/LICENSE +21 -0
- wechat_publish_sdk-1.0.0/PKG-INFO +232 -0
- wechat_publish_sdk-1.0.0/README.md +201 -0
- wechat_publish_sdk-1.0.0/pyproject.toml +58 -0
- wechat_publish_sdk-1.0.0/setup.cfg +4 -0
- wechat_publish_sdk-1.0.0/tests/test_client.py +156 -0
- wechat_publish_sdk-1.0.0/tests/test_integration.py +304 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk/__init__.py +61 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk/client.py +351 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk/exceptions.py +36 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk/models.py +83 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk.egg-info/PKG-INFO +232 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk.egg-info/SOURCES.txt +14 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk.egg-info/dependency_links.txt +1 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk.egg-info/requires.txt +7 -0
- wechat_publish_sdk-1.0.0/wechat_publish_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tang Cheng
|
|
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,
|
|
9
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
Software is 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,
|
|
20
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
21
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wechat-publish-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: 微信公众号发布服务 Python SDK
|
|
5
|
+
Author-email: Tang Cheng <tangcheng@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tangcheng/wechat-publish-service
|
|
8
|
+
Project-URL: Documentation, https://github.com/tangcheng/wechat-publish-service#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/tangcheng/wechat-publish-service.git
|
|
10
|
+
Keywords: wechat,publish,sdk,api,mp
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: requests>=2.28.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# WeChat Publish SDK
|
|
33
|
+
|
|
34
|
+
微信公众号发布服务的 Python SDK,提供稳定、易用的 API 接口。
|
|
35
|
+
|
|
36
|
+
## 特性
|
|
37
|
+
|
|
38
|
+
- ✅ **API 版本化** - 通过 `api_version` 参数支持多个版本
|
|
39
|
+
- ✅ **自动签名** - 无需手动计算 HMAC-SHA256 签名
|
|
40
|
+
- ✅ **类型安全** - 完整的类型注解,IDE 提示友好
|
|
41
|
+
- ✅ **错误处理** - 统一的异常体系
|
|
42
|
+
- ✅ **简洁易用** - 一个客户端解决所有操作
|
|
43
|
+
- ✅ **默认账号** - 设置 `default_account` 后无需每次传 account
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install wechat-publish-sdk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 快速开始
|
|
52
|
+
|
|
53
|
+
### 初始化客户端
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import os
|
|
57
|
+
from wechat_publish_sdk import WeChatClient
|
|
58
|
+
|
|
59
|
+
client = WeChatClient(
|
|
60
|
+
base_url="http://localhost:3000",
|
|
61
|
+
signing_key=os.getenv("SIGNING_KEY"),
|
|
62
|
+
default_account="mingdeng", # 可选:设置默认账号
|
|
63
|
+
api_version="v1" # 可选:默认 "v1"
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 发布文章
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from wechat_publish_sdk import WeChatClient, PublishRequest
|
|
71
|
+
|
|
72
|
+
# 基本发布
|
|
73
|
+
result = client.publish_article(
|
|
74
|
+
PublishRequest(
|
|
75
|
+
title="测试文章",
|
|
76
|
+
content="# 这是测试内容"
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if result.success:
|
|
81
|
+
print(f"发布成功,draft_id: {result.draft_id}")
|
|
82
|
+
else:
|
|
83
|
+
print(f"发布失败: {result.message}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 发布带封面的文章
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# 先上传封面
|
|
90
|
+
upload_result = client.upload_image(
|
|
91
|
+
UploadRequest(
|
|
92
|
+
account="mingdeng",
|
|
93
|
+
file_path="path/to/cover.jpg"
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if upload_result.success:
|
|
98
|
+
media_id = upload_result.media_id
|
|
99
|
+
|
|
100
|
+
# 发布文章时使用封面
|
|
101
|
+
result = client.publish_article(
|
|
102
|
+
PublishRequest(
|
|
103
|
+
title="带封面的文章",
|
|
104
|
+
content="# 文章内容",
|
|
105
|
+
thumb_media_id=media_id,
|
|
106
|
+
show_cover_pic=1,
|
|
107
|
+
author="作者名",
|
|
108
|
+
digest="文章摘要"
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 批量发布
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
articles = [
|
|
117
|
+
{"title": "文章1", "content": "内容1"},
|
|
118
|
+
{"title": "文章2", "content": "内容2"},
|
|
119
|
+
{"title": "文章3", "content": "内容3"},
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
for article in articles:
|
|
123
|
+
result = client.publish_article(
|
|
124
|
+
PublishRequest(**article)
|
|
125
|
+
)
|
|
126
|
+
print(f"{article['title']}: {result.message}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 查询素材列表
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
result = client.list_materials(
|
|
133
|
+
account="mingdeng",
|
|
134
|
+
material_type="image",
|
|
135
|
+
offset=0,
|
|
136
|
+
count=20
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if result.success:
|
|
140
|
+
print(f"共 {result.total_count} 个素材")
|
|
141
|
+
for item in result.items:
|
|
142
|
+
print(f" - {item.name}: {item.url}")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Markdown 渲染
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from wechat_publish_sdk import RenderRequest
|
|
149
|
+
|
|
150
|
+
result = client.render_markdown(
|
|
151
|
+
RenderRequest(
|
|
152
|
+
content="# 标题\n\n内容",
|
|
153
|
+
theme="orange"
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if result.success:
|
|
158
|
+
print(result.html)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## API 版本控制
|
|
162
|
+
|
|
163
|
+
SDK 支持多版本 API,通过初始化时指定 `api_version` 参数:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# 使用 v1 稳定版
|
|
167
|
+
client_v1 = WeChatClient(
|
|
168
|
+
base_url="http://localhost:3000",
|
|
169
|
+
signing_key="key",
|
|
170
|
+
api_version="v1"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# 使用 dev 开发版
|
|
174
|
+
client_dev = WeChatClient(
|
|
175
|
+
base_url="http://localhost:3000",
|
|
176
|
+
signing_key="key",
|
|
177
|
+
api_version="dev"
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 异常处理
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from wechat_publish_sdk import (
|
|
185
|
+
WeChatClient, PublishRequest,
|
|
186
|
+
ValidationError, AccountNotFoundError, PublishFailedError
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
result = client.publish_article(PublishRequest(...))
|
|
191
|
+
except ValidationError as e:
|
|
192
|
+
print(f"参数错误: {e}")
|
|
193
|
+
except AccountNotFoundError as e:
|
|
194
|
+
print(f"账号不存在: {e}")
|
|
195
|
+
except PublishFailedError as e:
|
|
196
|
+
print(f"发布失败: {e}")
|
|
197
|
+
except WeChatPublishError as e:
|
|
198
|
+
print(f"其他错误: {e}")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 配置说明
|
|
202
|
+
|
|
203
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
204
|
+
|------|------|------|------|
|
|
205
|
+
| base_url | str | 是 | 后端服务地址 |
|
|
206
|
+
| signing_key | str | 是 | HMAC 签名密钥(十六进制)|
|
|
207
|
+
| default_account | str | 否 | 默认账号 |
|
|
208
|
+
| api_version | str | 否 | API 版本,默认 "v1" |
|
|
209
|
+
| timeout | int | 否 | 请求超时(秒),默认 30 |
|
|
210
|
+
|
|
211
|
+
## 开发
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# 克隆项目
|
|
215
|
+
git clone https://github.com/tangcheng/wechat-publish-sdk.git
|
|
216
|
+
|
|
217
|
+
# 安装开发依赖
|
|
218
|
+
pip install -e ".[dev]"
|
|
219
|
+
|
|
220
|
+
# 运行测试
|
|
221
|
+
pytest
|
|
222
|
+
|
|
223
|
+
# 代码格式化
|
|
224
|
+
black wechat_publish_sdk/
|
|
225
|
+
|
|
226
|
+
# 类型检查
|
|
227
|
+
mypy wechat_publish_sdk/
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT License
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# WeChat Publish SDK
|
|
2
|
+
|
|
3
|
+
微信公众号发布服务的 Python SDK,提供稳定、易用的 API 接口。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- ✅ **API 版本化** - 通过 `api_version` 参数支持多个版本
|
|
8
|
+
- ✅ **自动签名** - 无需手动计算 HMAC-SHA256 签名
|
|
9
|
+
- ✅ **类型安全** - 完整的类型注解,IDE 提示友好
|
|
10
|
+
- ✅ **错误处理** - 统一的异常体系
|
|
11
|
+
- ✅ **简洁易用** - 一个客户端解决所有操作
|
|
12
|
+
- ✅ **默认账号** - 设置 `default_account` 后无需每次传 account
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install wechat-publish-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
### 初始化客户端
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import os
|
|
26
|
+
from wechat_publish_sdk import WeChatClient
|
|
27
|
+
|
|
28
|
+
client = WeChatClient(
|
|
29
|
+
base_url="http://localhost:3000",
|
|
30
|
+
signing_key=os.getenv("SIGNING_KEY"),
|
|
31
|
+
default_account="mingdeng", # 可选:设置默认账号
|
|
32
|
+
api_version="v1" # 可选:默认 "v1"
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 发布文章
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from wechat_publish_sdk import WeChatClient, PublishRequest
|
|
40
|
+
|
|
41
|
+
# 基本发布
|
|
42
|
+
result = client.publish_article(
|
|
43
|
+
PublishRequest(
|
|
44
|
+
title="测试文章",
|
|
45
|
+
content="# 这是测试内容"
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if result.success:
|
|
50
|
+
print(f"发布成功,draft_id: {result.draft_id}")
|
|
51
|
+
else:
|
|
52
|
+
print(f"发布失败: {result.message}")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 发布带封面的文章
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# 先上传封面
|
|
59
|
+
upload_result = client.upload_image(
|
|
60
|
+
UploadRequest(
|
|
61
|
+
account="mingdeng",
|
|
62
|
+
file_path="path/to/cover.jpg"
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if upload_result.success:
|
|
67
|
+
media_id = upload_result.media_id
|
|
68
|
+
|
|
69
|
+
# 发布文章时使用封面
|
|
70
|
+
result = client.publish_article(
|
|
71
|
+
PublishRequest(
|
|
72
|
+
title="带封面的文章",
|
|
73
|
+
content="# 文章内容",
|
|
74
|
+
thumb_media_id=media_id,
|
|
75
|
+
show_cover_pic=1,
|
|
76
|
+
author="作者名",
|
|
77
|
+
digest="文章摘要"
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 批量发布
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
articles = [
|
|
86
|
+
{"title": "文章1", "content": "内容1"},
|
|
87
|
+
{"title": "文章2", "content": "内容2"},
|
|
88
|
+
{"title": "文章3", "content": "内容3"},
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
for article in articles:
|
|
92
|
+
result = client.publish_article(
|
|
93
|
+
PublishRequest(**article)
|
|
94
|
+
)
|
|
95
|
+
print(f"{article['title']}: {result.message}")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 查询素材列表
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
result = client.list_materials(
|
|
102
|
+
account="mingdeng",
|
|
103
|
+
material_type="image",
|
|
104
|
+
offset=0,
|
|
105
|
+
count=20
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if result.success:
|
|
109
|
+
print(f"共 {result.total_count} 个素材")
|
|
110
|
+
for item in result.items:
|
|
111
|
+
print(f" - {item.name}: {item.url}")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Markdown 渲染
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from wechat_publish_sdk import RenderRequest
|
|
118
|
+
|
|
119
|
+
result = client.render_markdown(
|
|
120
|
+
RenderRequest(
|
|
121
|
+
content="# 标题\n\n内容",
|
|
122
|
+
theme="orange"
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if result.success:
|
|
127
|
+
print(result.html)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API 版本控制
|
|
131
|
+
|
|
132
|
+
SDK 支持多版本 API,通过初始化时指定 `api_version` 参数:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
# 使用 v1 稳定版
|
|
136
|
+
client_v1 = WeChatClient(
|
|
137
|
+
base_url="http://localhost:3000",
|
|
138
|
+
signing_key="key",
|
|
139
|
+
api_version="v1"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# 使用 dev 开发版
|
|
143
|
+
client_dev = WeChatClient(
|
|
144
|
+
base_url="http://localhost:3000",
|
|
145
|
+
signing_key="key",
|
|
146
|
+
api_version="dev"
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 异常处理
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from wechat_publish_sdk import (
|
|
154
|
+
WeChatClient, PublishRequest,
|
|
155
|
+
ValidationError, AccountNotFoundError, PublishFailedError
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
result = client.publish_article(PublishRequest(...))
|
|
160
|
+
except ValidationError as e:
|
|
161
|
+
print(f"参数错误: {e}")
|
|
162
|
+
except AccountNotFoundError as e:
|
|
163
|
+
print(f"账号不存在: {e}")
|
|
164
|
+
except PublishFailedError as e:
|
|
165
|
+
print(f"发布失败: {e}")
|
|
166
|
+
except WeChatPublishError as e:
|
|
167
|
+
print(f"其他错误: {e}")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 配置说明
|
|
171
|
+
|
|
172
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
173
|
+
|------|------|------|------|
|
|
174
|
+
| base_url | str | 是 | 后端服务地址 |
|
|
175
|
+
| signing_key | str | 是 | HMAC 签名密钥(十六进制)|
|
|
176
|
+
| default_account | str | 否 | 默认账号 |
|
|
177
|
+
| api_version | str | 否 | API 版本,默认 "v1" |
|
|
178
|
+
| timeout | int | 否 | 请求超时(秒),默认 30 |
|
|
179
|
+
|
|
180
|
+
## 开发
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# 克隆项目
|
|
184
|
+
git clone https://github.com/tangcheng/wechat-publish-sdk.git
|
|
185
|
+
|
|
186
|
+
# 安装开发依赖
|
|
187
|
+
pip install -e ".[dev]"
|
|
188
|
+
|
|
189
|
+
# 运行测试
|
|
190
|
+
pytest
|
|
191
|
+
|
|
192
|
+
# 代码格式化
|
|
193
|
+
black wechat_publish_sdk/
|
|
194
|
+
|
|
195
|
+
# 类型检查
|
|
196
|
+
mypy wechat_publish_sdk/
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT License
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wechat-publish-sdk"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "微信公众号发布服务 Python SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tang Cheng", email = "tangcheng@example.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["wechat", "publish", "sdk", "api", "mp"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = [
|
|
30
|
+
"requests>=2.28.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=7.0",
|
|
36
|
+
"pytest-cov>=4.0",
|
|
37
|
+
"black>=23.0",
|
|
38
|
+
"mypy>=1.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/tangcheng/wechat-publish-service"
|
|
43
|
+
Documentation = "https://github.com/tangcheng/wechat-publish-service#readme"
|
|
44
|
+
Repository = "https://github.com/tangcheng/wechat-publish-service.git"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
where = ["."]
|
|
48
|
+
include = ["wechat_publish_sdk*"]
|
|
49
|
+
|
|
50
|
+
[tool.black]
|
|
51
|
+
line-length = 100
|
|
52
|
+
target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
|
|
53
|
+
|
|
54
|
+
[tool.mypy]
|
|
55
|
+
python_version = "3.8"
|
|
56
|
+
warn_return_any = true
|
|
57
|
+
warn_unused_configs = true
|
|
58
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""单元测试"""
|
|
2
|
+
import pytest
|
|
3
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
4
|
+
from wechat_publish_sdk import WeChatClient, PublishRequest, ValidationError
|
|
5
|
+
from wechat_publish_sdk.exceptions import AccountNotFoundError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestWeChatClient:
|
|
9
|
+
"""客户端测试"""
|
|
10
|
+
|
|
11
|
+
def test_init(self):
|
|
12
|
+
"""测试客户端初始化"""
|
|
13
|
+
client = WeChatClient(
|
|
14
|
+
base_url="http://localhost:3000",
|
|
15
|
+
signing_key="a" * 64 # 64 hex chars = 32 bytes
|
|
16
|
+
)
|
|
17
|
+
assert client.base_url == "http://localhost:3000"
|
|
18
|
+
assert client.api_version == "v1"
|
|
19
|
+
assert client.endpoint_base == "http://localhost:3000/api/v1/mp"
|
|
20
|
+
|
|
21
|
+
def test_init_with_custom_version(self):
|
|
22
|
+
"""测试自定义版本"""
|
|
23
|
+
client = WeChatClient(
|
|
24
|
+
base_url="http://localhost:3000",
|
|
25
|
+
signing_key="a" * 64,
|
|
26
|
+
api_version="dev"
|
|
27
|
+
)
|
|
28
|
+
assert client.api_version == "dev"
|
|
29
|
+
assert client.endpoint_base == "http://localhost:3000/api/dev/mp"
|
|
30
|
+
|
|
31
|
+
def test_make_signature(self):
|
|
32
|
+
"""测试签名生成"""
|
|
33
|
+
client = WeChatClient(
|
|
34
|
+
base_url="http://localhost:3000",
|
|
35
|
+
signing_key="a" * 64
|
|
36
|
+
)
|
|
37
|
+
sig = client._make_signature("test_account", 1234567890, "body_hash_value")
|
|
38
|
+
assert isinstance(sig, str)
|
|
39
|
+
assert len(sig) == 64 # SHA256 hex = 64 chars
|
|
40
|
+
|
|
41
|
+
def test_get_account_with_default(self):
|
|
42
|
+
"""测试使用默认账号"""
|
|
43
|
+
client = WeChatClient(
|
|
44
|
+
base_url="http://localhost:3000",
|
|
45
|
+
signing_key="a" * 64,
|
|
46
|
+
default_account="default_account"
|
|
47
|
+
)
|
|
48
|
+
account = client._get_account()
|
|
49
|
+
assert account == "default_account"
|
|
50
|
+
|
|
51
|
+
def test_get_account_without_default_raises(self):
|
|
52
|
+
"""测试无默认账号且不传 account 时抛异常"""
|
|
53
|
+
client = WeChatClient(
|
|
54
|
+
base_url="http://localhost:3000",
|
|
55
|
+
signing_key="a" * 64
|
|
56
|
+
)
|
|
57
|
+
with pytest.raises(ValidationError):
|
|
58
|
+
client._get_account()
|
|
59
|
+
|
|
60
|
+
def test_get_account_explicit(self):
|
|
61
|
+
"""测试显式指定账号"""
|
|
62
|
+
client = WeChatClient(
|
|
63
|
+
base_url="http://localhost:3000",
|
|
64
|
+
signing_key="a" * 64,
|
|
65
|
+
default_account="default"
|
|
66
|
+
)
|
|
67
|
+
account = client._get_account("explicit_account")
|
|
68
|
+
assert account == "explicit_account"
|
|
69
|
+
|
|
70
|
+
@patch('wechat_publish_sdk.client.requests.Session.post')
|
|
71
|
+
def test_publish_article_success(self, mock_post):
|
|
72
|
+
"""测试发布文章成功"""
|
|
73
|
+
mock_post.return_value = Mock(
|
|
74
|
+
json=lambda: {"success": True, "message": "发布成功", "draft_id": "draft_123"}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
client = WeChatClient(
|
|
78
|
+
base_url="http://localhost:3000",
|
|
79
|
+
signing_key="a" * 64,
|
|
80
|
+
default_account="test_account"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
result = client.publish_article(
|
|
84
|
+
PublishRequest(
|
|
85
|
+
title="测试标题",
|
|
86
|
+
content="# 测试内容"
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
assert result.success is True
|
|
91
|
+
assert result.draft_id == "draft_123"
|
|
92
|
+
assert mock_post.called
|
|
93
|
+
|
|
94
|
+
@patch('wechat_publish_sdk.client.requests.Session.post')
|
|
95
|
+
def test_publish_article_404(self, mock_post):
|
|
96
|
+
"""测试发布文章 404"""
|
|
97
|
+
mock_post.return_value = Mock(
|
|
98
|
+
status_code=404,
|
|
99
|
+
json=lambda: {"success": False, "message": "接口不存在"}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
client = WeChatClient(
|
|
103
|
+
base_url="http://localhost:3000",
|
|
104
|
+
signing_key="a" * 64,
|
|
105
|
+
default_account="test_account"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
with pytest.raises(AccountNotFoundError):
|
|
109
|
+
client.publish_article(
|
|
110
|
+
PublishRequest(
|
|
111
|
+
title="测试",
|
|
112
|
+
content="内容"
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def test_publish_article_validation_error(self):
|
|
117
|
+
"""测试参数验证失败"""
|
|
118
|
+
client = WeChatClient(
|
|
119
|
+
base_url="http://localhost:3000",
|
|
120
|
+
signing_key="a" * 64
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
with pytest.raises(ValidationError):
|
|
124
|
+
client.publish_article(
|
|
125
|
+
PublishRequest(
|
|
126
|
+
title="", # 空标题
|
|
127
|
+
content="内容"
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
with pytest.raises(ValidationError):
|
|
132
|
+
client.publish_article(
|
|
133
|
+
PublishRequest(
|
|
134
|
+
title="标题",
|
|
135
|
+
content="" # 空内容
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@patch('wechat_publish_sdk.client.os.path.exists')
|
|
140
|
+
def test_upload_image_file_not_found(self, mock_exists):
|
|
141
|
+
"""测试文件不存在"""
|
|
142
|
+
mock_exists.return_value = False
|
|
143
|
+
|
|
144
|
+
client = WeChatClient(
|
|
145
|
+
base_url="http://localhost:3000",
|
|
146
|
+
signing_key="a" * 64
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
from wechat_publish_sdk import UploadRequest
|
|
150
|
+
with pytest.raises(ValidationError):
|
|
151
|
+
client.upload_image(
|
|
152
|
+
UploadRequest(
|
|
153
|
+
account="test",
|
|
154
|
+
file_path="/nonexistent/file.jpg"
|
|
155
|
+
)
|
|
156
|
+
)
|