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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ )