ErisPulse-WechatMpAdapter 4.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,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: ErisPulse-WechatMpAdapter
3
+ Version: 4.0.0
4
+ Summary: ErisPulse的微信公众号(WechatMp)适配模块
5
+ Author: ErisPulse
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/ErisPulse/ErisPulse-WechatMpAdapter
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: cryptography
12
+ Dynamic: license-file
13
+
14
+ # ErisPulse-WechatMpAdapter
15
+
16
+ ErisPulse 的微信公众号(WechatMp)适配模块。遵循 ErisPulse 多账户适配器规范,通过被动回调(Webhook)接收消息,通过客服消息/模板消息接口主动发送消息。
17
+
18
+ ## 特性
19
+
20
+ - **多账户支持**:每个账户对应一个公众号,独立配置与回调路径
21
+ - **消息加解密**:自动处理明文/兼容/安全三种模式,支持 AES-256-CBC 解密
22
+ - **签名验证**:GET 接入验证与 POST 消息回调均校验签名
23
+ - **access_token 管理**:自动获取、缓存并提前 5 分钟刷新
24
+ - **完整消息类型**:文本、图片、语音、视频、图文、音乐、模板、菜单消息
25
+ - **OneBot12 标准**:严格遵循事件转换规范,平台扩展字段统一使用 `mp_` 前缀
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ epsdk install
31
+
32
+ # 选择安装适配器
33
+ # 安装 WechatMp
34
+ ```
35
+
36
+ 依赖:`cryptography`(用于消息加解密)。
37
+
38
+ ## 快速开始
39
+
40
+ ### 1. 配置
41
+
42
+ 在 ErisPulse 的 `config.toml` 中添加公众号账户配置:
43
+
44
+ ```toml
45
+ [WechatMpAdapter.accounts.default]
46
+ appid = "wx1234567890abcdef"
47
+ appsecret = "your_app_secret_here"
48
+ token = "your_callback_token" # 公众号后台「基本配置」中的Token
49
+ encoding_aes_key = "" # 明文模式留空;安全模式需填写43位密钥
50
+ callback_path = "/mp/default" # 回调路径
51
+ verified = true # 是否为认证服务号(影响消息发送策略)
52
+ enable = true
53
+ ```
54
+
55
+ 多账户配置:
56
+
57
+ ```toml
58
+ [WechatMpAdapter.accounts.main]
59
+ appid = "wxaaaaaaaaaaaaaaaa"
60
+ appsecret = "secret1"
61
+ token = "token1"
62
+ callback_path = "/mp/main"
63
+ enable = true
64
+
65
+ [WechatMpAdapter.accounts.shop]
66
+ appid = "wxbbbbbbbbbbbbbbbb"
67
+ appsecret = "secret2"
68
+ token = "token2"
69
+ callback_path = "/mp/shop"
70
+ enable = true
71
+ ```
72
+
73
+ ### 2. 公众号后台配置
74
+
75
+ 1. 登录微信公众平台 → 「设置与开发」→「基本配置」
76
+ 2. 填写 **服务器配置**:
77
+ - **URL**:`http://你的服务器地址:端口/<callback_path>`(例如 `/mp/default`)
78
+ - **Token**:与配置中的 `token` 一致
79
+ - **EncodingAESKey**:安全模式必填,与配置中的 `encoding_aes_key` 一致
80
+ - **消息加解密方式**:明文 / 兼容 / 安全(适配器自动适配)
81
+
82
+ ### 3. 处理消息
83
+
84
+ ```python
85
+ from ErisPulse.Core import adapter
86
+
87
+ @adapter.on("message", platform="mp")
88
+ async def on_message(event):
89
+ openid = event.get_openid() # 发送者 OpenID
90
+ msg_type = event.get_msg_type() # text/image/voice/...
91
+ content = event.get_content() # 纯文本内容
92
+
93
+ if msg_type == "text" and content == "你好":
94
+ await event.reply(
95
+ event.send.Text("你好,欢迎使用公众号!")
96
+ )
97
+
98
+ @adapter.on("notice", platform="mp")
99
+ async def on_notice(event):
100
+ mp_event = event.get_event()
101
+ if mp_event == "subscribe":
102
+ openid = event.get_openid()
103
+ await event.send.Text("感谢关注!")
104
+ ```
105
+
106
+ ## 发送消息
107
+
108
+ ```python
109
+ # 获取发送句柄(target_id = 用户 OpenID)
110
+ send = adapter.get_send(platform="mp", detail_type="private",
111
+ target_id="oABC123...", account_id="default")
112
+
113
+ # 文本
114
+ await send.Text("Hello World").send()
115
+
116
+ # 图片(URL / 本地路径 / bytes)
117
+ await send.Image("https://example.com/photo.jpg").send()
118
+
119
+ # 图文消息
120
+ await send.News([
121
+ {"title": "标题", "description": "描述",
122
+ "url": "https://example.com", "picurl": "https://example.com/pic.jpg"},
123
+ ]).send()
124
+
125
+ # 音乐
126
+ await send.Music(url="https://example.com/song.mp3",
127
+ title="歌曲", description="歌手").send()
128
+
129
+ # 模板消息
130
+ await send.Template(
131
+ template_id="TEMPLATE_ID",
132
+ data={"first": {"value": "通知"}, "keyword1": {"value": "内容"}},
133
+ url="https://example.com",
134
+ ).send()
135
+
136
+ # 菜单消息
137
+ await send.Menu(
138
+ head_content="请选择:",
139
+ list_=[{"id": "opt1", "content": "选项一"},
140
+ {"id": "opt2", "content": "选项二"}],
141
+ tail_content="",
142
+ ).send()
143
+ ```
144
+
145
+ > **注意**:客服消息只能在用户与公众号交互后 48 小时内发送。超出时限请使用模板消息。
146
+
147
+ ## 重要限制
148
+
149
+ ### 1. 认证服务号 vs 未认证号
150
+
151
+ 微信公众号的消息发送能力取决于**是否认证服务号**,在账户配置中通过 `verified` 字段控制:
152
+
153
+ | 能力 | 认证服务号 (`verified=true`) | 未认证号 (`verified=false`) |
154
+ |------|---------------------------|---------------------------|
155
+ | 被动回复(回调中直接响应) | ✅ | ✅ |
156
+ | 客服消息主动推送 (`message/custom/send`) | ✅ | ❌ 接口无权限 |
157
+ | 模板消息 (`message/template/send`) | ✅ | ❌ 接口无权限 |
158
+ | 单次回调可发送消息数 | 仅 1 条 | 仅 1 条 |
159
+
160
+ **未认证号只能使用被动回复**,即每次用户发消息时,适配器在 5 秒内返回一条 XML 响应。模块中的 `event.reply()` 会被自动拦截为被动回复,超出时长的回复将被丢弃。
161
+
162
+ > 提示:如果你的公众号未认证,在微信公众平台看到的 `errcode: 48001 api unauthorized` 报错是正常的——该接口未开
163
+
164
+ ### 2. 被动回复文本长度
165
+
166
+ 微信对被动回复 XML 中 `Content` 字段的限制:
167
+
168
+ | 内容类型 | 限制 |
169
+ |---------|------|
170
+ | 纯英文/数字/符号 | ~2048 字符 |
171
+ | 中文字符(UTF-8 每字 3 字节) | ~682 字 |
172
+ | 中英混合 | 折中计算 |
173
+
174
+ 超过长度时适配器会自动截断并追加 `...(内容已截断)` 提示。
175
+
176
+ ### 3. 回复时效性
177
+
178
+ 被动回复(所有公众号)
179
+ - 微信要求 **5 秒内**返回 XML 响应
180
+ - 超时后微信会重试 3 次
181
+ - 适配器默认超时设为 4.5 秒,超时后回复丢弃
182
+
183
+ 客服消息(仅认证服务号)
184
+ - 用户发送消息后 **48 小时内**可主动推送
185
+ - 超出时限需使用模板消息
186
+ - 受微信频率限制控制
187
+
188
+ ### 4. 消息加解密
189
+
190
+ 适配器支持三种微信加密模式,通过 `encoding_aes_key` 配置:
191
+
192
+ | 模式 | `encoding_aes_key` | 说明 |
193
+ |------|-------------------|------|
194
+ | 明文模式 | 留空 | 消息体明文传输,无需加解密依赖 |
195
+ | 兼容模式 | 填写 43 位密钥 | 明文 + 密文同时发送(适配器优先使用明文) |
196
+ | 安全模式 | 填写 43 位密钥 | 仅密文传输,需 `cryptography` 库 |
197
+
198
+ ## 消息段类型映射
199
+
200
+ | OneBot12 消息段 | 微信消息类型 |
201
+ |----------------|------------|
202
+ | `text` | 文本 / 链接(转文本) |
203
+ | `image` | 图片(自动上传 media_id) |
204
+ | `voice` | 语音(自动上传 media_id) |
205
+ | `video` | 视频/小视频(自动上传 media_id) |
206
+ | `news` | 图文消息 |
207
+ | `music` | 音乐消息 |
208
+ | `template` | 模板消息 |
209
+ | `mp_menu` | 菜单消息 |
210
+
211
+ ## 事件类型
212
+
213
+ 所有事件 `platform` 为 `mp`,`detail_type` 统一为 `private`。
214
+
215
+ - **message**:用户消息(text/image/voice/video/shortvideo/location/link)
216
+ - **notice**:事件通知(subscribe/unsubscribe/scan/location_report/menu_click/menu_view/template_send_finish 等)
217
+
218
+ 详见 [platform-features.md](platform-features.md)。
219
+
220
+ ## 许可证
221
+
222
+ MIT License
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ ErisPulse_WechatMpAdapter.egg-info/PKG-INFO
5
+ ErisPulse_WechatMpAdapter.egg-info/SOURCES.txt
6
+ ErisPulse_WechatMpAdapter.egg-info/dependency_links.txt
7
+ ErisPulse_WechatMpAdapter.egg-info/entry_points.txt
8
+ ErisPulse_WechatMpAdapter.egg-info/requires.txt
9
+ ErisPulse_WechatMpAdapter.egg-info/top_level.txt
10
+ WechatMpAdapter/Converter.py
11
+ WechatMpAdapter/Core.py
12
+ WechatMpAdapter/__init__.py
@@ -0,0 +1,3 @@
1
+ [erispulse.adapter]
2
+ mp = WechatMpAdapter:WechatMpAdapter
3
+ wechat_mp = WechatMpAdapter:WechatMpAdapter
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ErisPulse
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,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: ErisPulse-WechatMpAdapter
3
+ Version: 4.0.0
4
+ Summary: ErisPulse的微信公众号(WechatMp)适配模块
5
+ Author: ErisPulse
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/ErisPulse/ErisPulse-WechatMpAdapter
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: cryptography
12
+ Dynamic: license-file
13
+
14
+ # ErisPulse-WechatMpAdapter
15
+
16
+ ErisPulse 的微信公众号(WechatMp)适配模块。遵循 ErisPulse 多账户适配器规范,通过被动回调(Webhook)接收消息,通过客服消息/模板消息接口主动发送消息。
17
+
18
+ ## 特性
19
+
20
+ - **多账户支持**:每个账户对应一个公众号,独立配置与回调路径
21
+ - **消息加解密**:自动处理明文/兼容/安全三种模式,支持 AES-256-CBC 解密
22
+ - **签名验证**:GET 接入验证与 POST 消息回调均校验签名
23
+ - **access_token 管理**:自动获取、缓存并提前 5 分钟刷新
24
+ - **完整消息类型**:文本、图片、语音、视频、图文、音乐、模板、菜单消息
25
+ - **OneBot12 标准**:严格遵循事件转换规范,平台扩展字段统一使用 `mp_` 前缀
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ epsdk install
31
+
32
+ # 选择安装适配器
33
+ # 安装 WechatMp
34
+ ```
35
+
36
+ 依赖:`cryptography`(用于消息加解密)。
37
+
38
+ ## 快速开始
39
+
40
+ ### 1. 配置
41
+
42
+ 在 ErisPulse 的 `config.toml` 中添加公众号账户配置:
43
+
44
+ ```toml
45
+ [WechatMpAdapter.accounts.default]
46
+ appid = "wx1234567890abcdef"
47
+ appsecret = "your_app_secret_here"
48
+ token = "your_callback_token" # 公众号后台「基本配置」中的Token
49
+ encoding_aes_key = "" # 明文模式留空;安全模式需填写43位密钥
50
+ callback_path = "/mp/default" # 回调路径
51
+ verified = true # 是否为认证服务号(影响消息发送策略)
52
+ enable = true
53
+ ```
54
+
55
+ 多账户配置:
56
+
57
+ ```toml
58
+ [WechatMpAdapter.accounts.main]
59
+ appid = "wxaaaaaaaaaaaaaaaa"
60
+ appsecret = "secret1"
61
+ token = "token1"
62
+ callback_path = "/mp/main"
63
+ enable = true
64
+
65
+ [WechatMpAdapter.accounts.shop]
66
+ appid = "wxbbbbbbbbbbbbbbbb"
67
+ appsecret = "secret2"
68
+ token = "token2"
69
+ callback_path = "/mp/shop"
70
+ enable = true
71
+ ```
72
+
73
+ ### 2. 公众号后台配置
74
+
75
+ 1. 登录微信公众平台 → 「设置与开发」→「基本配置」
76
+ 2. 填写 **服务器配置**:
77
+ - **URL**:`http://你的服务器地址:端口/<callback_path>`(例如 `/mp/default`)
78
+ - **Token**:与配置中的 `token` 一致
79
+ - **EncodingAESKey**:安全模式必填,与配置中的 `encoding_aes_key` 一致
80
+ - **消息加解密方式**:明文 / 兼容 / 安全(适配器自动适配)
81
+
82
+ ### 3. 处理消息
83
+
84
+ ```python
85
+ from ErisPulse.Core import adapter
86
+
87
+ @adapter.on("message", platform="mp")
88
+ async def on_message(event):
89
+ openid = event.get_openid() # 发送者 OpenID
90
+ msg_type = event.get_msg_type() # text/image/voice/...
91
+ content = event.get_content() # 纯文本内容
92
+
93
+ if msg_type == "text" and content == "你好":
94
+ await event.reply(
95
+ event.send.Text("你好,欢迎使用公众号!")
96
+ )
97
+
98
+ @adapter.on("notice", platform="mp")
99
+ async def on_notice(event):
100
+ mp_event = event.get_event()
101
+ if mp_event == "subscribe":
102
+ openid = event.get_openid()
103
+ await event.send.Text("感谢关注!")
104
+ ```
105
+
106
+ ## 发送消息
107
+
108
+ ```python
109
+ # 获取发送句柄(target_id = 用户 OpenID)
110
+ send = adapter.get_send(platform="mp", detail_type="private",
111
+ target_id="oABC123...", account_id="default")
112
+
113
+ # 文本
114
+ await send.Text("Hello World").send()
115
+
116
+ # 图片(URL / 本地路径 / bytes)
117
+ await send.Image("https://example.com/photo.jpg").send()
118
+
119
+ # 图文消息
120
+ await send.News([
121
+ {"title": "标题", "description": "描述",
122
+ "url": "https://example.com", "picurl": "https://example.com/pic.jpg"},
123
+ ]).send()
124
+
125
+ # 音乐
126
+ await send.Music(url="https://example.com/song.mp3",
127
+ title="歌曲", description="歌手").send()
128
+
129
+ # 模板消息
130
+ await send.Template(
131
+ template_id="TEMPLATE_ID",
132
+ data={"first": {"value": "通知"}, "keyword1": {"value": "内容"}},
133
+ url="https://example.com",
134
+ ).send()
135
+
136
+ # 菜单消息
137
+ await send.Menu(
138
+ head_content="请选择:",
139
+ list_=[{"id": "opt1", "content": "选项一"},
140
+ {"id": "opt2", "content": "选项二"}],
141
+ tail_content="",
142
+ ).send()
143
+ ```
144
+
145
+ > **注意**:客服消息只能在用户与公众号交互后 48 小时内发送。超出时限请使用模板消息。
146
+
147
+ ## 重要限制
148
+
149
+ ### 1. 认证服务号 vs 未认证号
150
+
151
+ 微信公众号的消息发送能力取决于**是否认证服务号**,在账户配置中通过 `verified` 字段控制:
152
+
153
+ | 能力 | 认证服务号 (`verified=true`) | 未认证号 (`verified=false`) |
154
+ |------|---------------------------|---------------------------|
155
+ | 被动回复(回调中直接响应) | ✅ | ✅ |
156
+ | 客服消息主动推送 (`message/custom/send`) | ✅ | ❌ 接口无权限 |
157
+ | 模板消息 (`message/template/send`) | ✅ | ❌ 接口无权限 |
158
+ | 单次回调可发送消息数 | 仅 1 条 | 仅 1 条 |
159
+
160
+ **未认证号只能使用被动回复**,即每次用户发消息时,适配器在 5 秒内返回一条 XML 响应。模块中的 `event.reply()` 会被自动拦截为被动回复,超出时长的回复将被丢弃。
161
+
162
+ > 提示:如果你的公众号未认证,在微信公众平台看到的 `errcode: 48001 api unauthorized` 报错是正常的——该接口未开
163
+
164
+ ### 2. 被动回复文本长度
165
+
166
+ 微信对被动回复 XML 中 `Content` 字段的限制:
167
+
168
+ | 内容类型 | 限制 |
169
+ |---------|------|
170
+ | 纯英文/数字/符号 | ~2048 字符 |
171
+ | 中文字符(UTF-8 每字 3 字节) | ~682 字 |
172
+ | 中英混合 | 折中计算 |
173
+
174
+ 超过长度时适配器会自动截断并追加 `...(内容已截断)` 提示。
175
+
176
+ ### 3. 回复时效性
177
+
178
+ 被动回复(所有公众号)
179
+ - 微信要求 **5 秒内**返回 XML 响应
180
+ - 超时后微信会重试 3 次
181
+ - 适配器默认超时设为 4.5 秒,超时后回复丢弃
182
+
183
+ 客服消息(仅认证服务号)
184
+ - 用户发送消息后 **48 小时内**可主动推送
185
+ - 超出时限需使用模板消息
186
+ - 受微信频率限制控制
187
+
188
+ ### 4. 消息加解密
189
+
190
+ 适配器支持三种微信加密模式,通过 `encoding_aes_key` 配置:
191
+
192
+ | 模式 | `encoding_aes_key` | 说明 |
193
+ |------|-------------------|------|
194
+ | 明文模式 | 留空 | 消息体明文传输,无需加解密依赖 |
195
+ | 兼容模式 | 填写 43 位密钥 | 明文 + 密文同时发送(适配器优先使用明文) |
196
+ | 安全模式 | 填写 43 位密钥 | 仅密文传输,需 `cryptography` 库 |
197
+
198
+ ## 消息段类型映射
199
+
200
+ | OneBot12 消息段 | 微信消息类型 |
201
+ |----------------|------------|
202
+ | `text` | 文本 / 链接(转文本) |
203
+ | `image` | 图片(自动上传 media_id) |
204
+ | `voice` | 语音(自动上传 media_id) |
205
+ | `video` | 视频/小视频(自动上传 media_id) |
206
+ | `news` | 图文消息 |
207
+ | `music` | 音乐消息 |
208
+ | `template` | 模板消息 |
209
+ | `mp_menu` | 菜单消息 |
210
+
211
+ ## 事件类型
212
+
213
+ 所有事件 `platform` 为 `mp`,`detail_type` 统一为 `private`。
214
+
215
+ - **message**:用户消息(text/image/voice/video/shortvideo/location/link)
216
+ - **notice**:事件通知(subscribe/unsubscribe/scan/location_report/menu_click/menu_view/template_send_finish 等)
217
+
218
+ 详见 [platform-features.md](platform-features.md)。
219
+
220
+ ## 许可证
221
+
222
+ MIT License