engagelab-apppush 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.
- engagelab_apppush-0.1.0/LICENSE +21 -0
- engagelab_apppush-0.1.0/PKG-INFO +229 -0
- engagelab_apppush-0.1.0/README.md +204 -0
- engagelab_apppush-0.1.0/engagelab/__init__.py +157 -0
- engagelab_apppush-0.1.0/engagelab/_serialization.py +49 -0
- engagelab_apppush-0.1.0/engagelab/alias.py +58 -0
- engagelab_apppush-0.1.0/engagelab/client.py +152 -0
- engagelab_apppush-0.1.0/engagelab/device.py +82 -0
- engagelab_apppush-0.1.0/engagelab/errors.py +49 -0
- engagelab_apppush-0.1.0/engagelab/group_push.py +99 -0
- engagelab_apppush-0.1.0/engagelab/image.py +75 -0
- engagelab_apppush-0.1.0/engagelab/plan.py +114 -0
- engagelab_apppush-0.1.0/engagelab/push.py +281 -0
- engagelab_apppush-0.1.0/engagelab/schedule.py +136 -0
- engagelab_apppush-0.1.0/engagelab/status.py +107 -0
- engagelab_apppush-0.1.0/engagelab/tag.py +134 -0
- engagelab_apppush-0.1.0/engagelab/voice.py +68 -0
- engagelab_apppush-0.1.0/engagelab_apppush.egg-info/PKG-INFO +229 -0
- engagelab_apppush-0.1.0/engagelab_apppush.egg-info/SOURCES.txt +22 -0
- engagelab_apppush-0.1.0/engagelab_apppush.egg-info/dependency_links.txt +1 -0
- engagelab_apppush-0.1.0/engagelab_apppush.egg-info/top_level.txt +1 -0
- engagelab_apppush-0.1.0/pyproject.toml +36 -0
- engagelab_apppush-0.1.0/setup.cfg +4 -0
- engagelab_apppush-0.1.0/tests/test_all.py +1562 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EngageLab
|
|
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,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: engagelab-apppush
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: EngageLab AppPush REST API Python SDK (zero third-party dependencies)
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/engagelab-mt/engagelab-apppush-python
|
|
7
|
+
Project-URL: Repository, https://github.com/engagelab-mt/engagelab-apppush-python
|
|
8
|
+
Project-URL: Issues, https://github.com/engagelab-mt/engagelab-apppush-python/issues
|
|
9
|
+
Keywords: engagelab,push,notification,apppush,sdk
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# EngageLab AppPush Python SDK
|
|
27
|
+
|
|
28
|
+
EngageLab AppPush REST API 的 Python SDK,零第三方依赖,仅使用 Python 标准库。
|
|
29
|
+
|
|
30
|
+
## 安装
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install engagelab-apppush
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
或从源码安装:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 快速开始
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import engagelab
|
|
46
|
+
|
|
47
|
+
# 创建客户端(默认新加坡数据中心)
|
|
48
|
+
client = engagelab.Client("your-app-key", "your-master-secret")
|
|
49
|
+
|
|
50
|
+
# 或指定数据中心
|
|
51
|
+
client = engagelab.Client(
|
|
52
|
+
"your-app-key",
|
|
53
|
+
"your-master-secret",
|
|
54
|
+
base_url=engagelab.DataCenter.HONG_KONG,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# 发送推送
|
|
58
|
+
result = client.push.send(engagelab.PushParam(
|
|
59
|
+
from_="push",
|
|
60
|
+
to="all",
|
|
61
|
+
body=engagelab.PushBody(
|
|
62
|
+
platform="all",
|
|
63
|
+
notification=engagelab.NotificationMessage(
|
|
64
|
+
alert="Hello from Python SDK!",
|
|
65
|
+
android=engagelab.AndroidNotification(
|
|
66
|
+
alert="Hello Android!",
|
|
67
|
+
title="Test Push",
|
|
68
|
+
),
|
|
69
|
+
ios=engagelab.IOSNotification(
|
|
70
|
+
alert="Hello iOS!",
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
))
|
|
75
|
+
print(f"Push sent: msg_id={result.msg_id}")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 数据中心
|
|
79
|
+
|
|
80
|
+
| 常量 | 区域 | Base URL |
|
|
81
|
+
| --------------------------- | ----------- | ------------------------------------- |
|
|
82
|
+
| `DataCenter.SINGAPORE` | 新加坡 (默认) | `https://pushapi-sgp.engagelab.com` |
|
|
83
|
+
| `DataCenter.HONG_KONG` | 香港 | `https://pushapi-hk.engagelab.com` |
|
|
84
|
+
| `DataCenter.VIRGINIA` | 美国弗吉尼亚 | `https://pushapi-usva.engagelab.com` |
|
|
85
|
+
| `DataCenter.FRANKFURT` | 德国法兰克福 | `https://pushapi-defra.engagelab.com` |
|
|
86
|
+
|
|
87
|
+
## API 模块
|
|
88
|
+
|
|
89
|
+
### Push — 推送
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
client.push.send(param) # 创建推送
|
|
93
|
+
client.push.send_raw(raw_body) # 自定义 JSON 推送
|
|
94
|
+
client.push.validate(param) # 推送校验
|
|
95
|
+
client.push.withdraw(msg_id) # 消息撤回
|
|
96
|
+
client.push.batch_by_regid(param) # 按 Registration ID 批量推送
|
|
97
|
+
client.push.batch_by_alias(param) # 按 Alias 批量推送
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Group Push — 应用分组推送
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Group Push 使用独立认证: group-{GroupKey}:{GroupMasterSecret}
|
|
104
|
+
group_client = engagelab.GroupPushClient(
|
|
105
|
+
"group-key",
|
|
106
|
+
"group-master-secret",
|
|
107
|
+
base_url=engagelab.DataCenter.SINGAPORE,
|
|
108
|
+
)
|
|
109
|
+
group_client.send(param)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Device — 设备
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
client.device.get(registration_id) # 查询设备信息
|
|
116
|
+
client.device.set(registration_id, param) # 设置设备标签/别名
|
|
117
|
+
client.device.delete(registration_id) # 删除设备
|
|
118
|
+
client.device.get_status(param) # 查询设备在线状态
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Tag — 标签
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
client.tag.list() # 获取标签列表
|
|
125
|
+
client.tag.set(tag, param) # 添加/移除标签设备
|
|
126
|
+
client.tag.delete(tag, platforms=["android"]) # 删除标签
|
|
127
|
+
client.tag.get_count(tags, platforms=["android"]) # 查询标签设备数
|
|
128
|
+
client.tag.get_device_status(tag, registration_id) # 查询设备标签绑定状态
|
|
129
|
+
client.tag.get_quota(tags=["vip"], platforms=["android"]) # 查询标签配额
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Alias — 别名
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
client.alias.get(alias, platforms=["android"]) # 查询别名设备
|
|
136
|
+
client.alias.delete(alias, platforms=["android"]) # 删除别名
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Schedule — 定时任务
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
client.schedule.create(param) # 创建定时推送
|
|
143
|
+
client.schedule.update(id, param) # 更新定时推送
|
|
144
|
+
client.schedule.delete(id) # 删除定时推送
|
|
145
|
+
client.schedule.get(id) # 获取定时推送详情
|
|
146
|
+
client.schedule.list(page=1) # 获取定时推送列表
|
|
147
|
+
client.schedule.get_msg_ids(id) # 获取定时推送消息 ID
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Status — 统计
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
client.status.users(time_unit, start, duration) # 用户统计
|
|
154
|
+
client.status.message_detail(message_ids) # 消息送达统计
|
|
155
|
+
client.status.message_lifecycle(msg_id, registration_ids) # 消息生命周期
|
|
156
|
+
client.status.batch_message_detail(message_ids) # 批量消息统计
|
|
157
|
+
client.status.plan_detail(plan_id, message_ids) # 推送计划统计
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Plan — 推送计划
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
client.plan.create_or_update(param) # 创建/更新推送计划
|
|
164
|
+
client.plan.list(page_index=1, page_size=10, send_source=None, search_description="") # 查询列表
|
|
165
|
+
client.plan.query_msg(plan_ids, start_date, end_date) # 查询计划消息 ID
|
|
166
|
+
client.plan.delete(plan_id) # 删除推送计划
|
|
167
|
+
client.plan.batch_delete(plan_ids) # 批量删除推送计划
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Voice — 语音/TTS
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
client.voice.create(param) # 创建语音模板
|
|
174
|
+
client.voice.list() # 获取语音模板列表
|
|
175
|
+
client.voice.get(language) # 获取语音模板
|
|
176
|
+
client.voice.delete(language) # 删除语音模板
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Image — 图片
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
client.image.upload_oppo(file_path) # 上传 OPPO 大图 (文件路径)
|
|
183
|
+
client.image.upload_oppo_from_reader(filename, reader) # 上传 OPPO 大图 (file-like object)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 错误处理
|
|
187
|
+
|
|
188
|
+
SDK 使用 `engagelab.ApiError` 异常类型返回 API 错误:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
try:
|
|
192
|
+
result = client.push.send(param)
|
|
193
|
+
except engagelab.ApiError as e:
|
|
194
|
+
print(f"API Error: status={e.status_code} code={e.error.code} message={e.error.message}")
|
|
195
|
+
except Exception as e:
|
|
196
|
+
print(f"Network Error: {e}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 客户端配置
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# 指定数据中心
|
|
203
|
+
client = engagelab.Client("key", "secret",
|
|
204
|
+
base_url=engagelab.DataCenter.HONG_KONG,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# 自定义超时
|
|
208
|
+
client = engagelab.Client("key", "secret", timeout=60)
|
|
209
|
+
|
|
210
|
+
# 自定义 Base URL
|
|
211
|
+
client = engagelab.Client("key", "secret",
|
|
212
|
+
base_url="https://custom-api.example.com",
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 认证方式
|
|
217
|
+
|
|
218
|
+
| API 模块 | 认证 |
|
|
219
|
+
| ---------------------------------------------------------------------- | ----------------------------------------------- |
|
|
220
|
+
| Push / Device / Tag / Alias / Schedule / Status / Plan / Voice / Image | Basic Auth: `appKey:masterSecret` |
|
|
221
|
+
| Group Push | Basic Auth: `group-{GroupKey}:GroupMasterSecret` |
|
|
222
|
+
|
|
223
|
+
## 更新日志
|
|
224
|
+
|
|
225
|
+
详见 [CHANGELOG.md](CHANGELOG.md)。
|
|
226
|
+
|
|
227
|
+
## 许可证
|
|
228
|
+
|
|
229
|
+
MIT License
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# EngageLab AppPush Python SDK
|
|
2
|
+
|
|
3
|
+
EngageLab AppPush REST API 的 Python SDK,零第三方依赖,仅使用 Python 标准库。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install engagelab-apppush
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
或从源码安装:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import engagelab
|
|
21
|
+
|
|
22
|
+
# 创建客户端(默认新加坡数据中心)
|
|
23
|
+
client = engagelab.Client("your-app-key", "your-master-secret")
|
|
24
|
+
|
|
25
|
+
# 或指定数据中心
|
|
26
|
+
client = engagelab.Client(
|
|
27
|
+
"your-app-key",
|
|
28
|
+
"your-master-secret",
|
|
29
|
+
base_url=engagelab.DataCenter.HONG_KONG,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# 发送推送
|
|
33
|
+
result = client.push.send(engagelab.PushParam(
|
|
34
|
+
from_="push",
|
|
35
|
+
to="all",
|
|
36
|
+
body=engagelab.PushBody(
|
|
37
|
+
platform="all",
|
|
38
|
+
notification=engagelab.NotificationMessage(
|
|
39
|
+
alert="Hello from Python SDK!",
|
|
40
|
+
android=engagelab.AndroidNotification(
|
|
41
|
+
alert="Hello Android!",
|
|
42
|
+
title="Test Push",
|
|
43
|
+
),
|
|
44
|
+
ios=engagelab.IOSNotification(
|
|
45
|
+
alert="Hello iOS!",
|
|
46
|
+
),
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
))
|
|
50
|
+
print(f"Push sent: msg_id={result.msg_id}")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 数据中心
|
|
54
|
+
|
|
55
|
+
| 常量 | 区域 | Base URL |
|
|
56
|
+
| --------------------------- | ----------- | ------------------------------------- |
|
|
57
|
+
| `DataCenter.SINGAPORE` | 新加坡 (默认) | `https://pushapi-sgp.engagelab.com` |
|
|
58
|
+
| `DataCenter.HONG_KONG` | 香港 | `https://pushapi-hk.engagelab.com` |
|
|
59
|
+
| `DataCenter.VIRGINIA` | 美国弗吉尼亚 | `https://pushapi-usva.engagelab.com` |
|
|
60
|
+
| `DataCenter.FRANKFURT` | 德国法兰克福 | `https://pushapi-defra.engagelab.com` |
|
|
61
|
+
|
|
62
|
+
## API 模块
|
|
63
|
+
|
|
64
|
+
### Push — 推送
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
client.push.send(param) # 创建推送
|
|
68
|
+
client.push.send_raw(raw_body) # 自定义 JSON 推送
|
|
69
|
+
client.push.validate(param) # 推送校验
|
|
70
|
+
client.push.withdraw(msg_id) # 消息撤回
|
|
71
|
+
client.push.batch_by_regid(param) # 按 Registration ID 批量推送
|
|
72
|
+
client.push.batch_by_alias(param) # 按 Alias 批量推送
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Group Push — 应用分组推送
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Group Push 使用独立认证: group-{GroupKey}:{GroupMasterSecret}
|
|
79
|
+
group_client = engagelab.GroupPushClient(
|
|
80
|
+
"group-key",
|
|
81
|
+
"group-master-secret",
|
|
82
|
+
base_url=engagelab.DataCenter.SINGAPORE,
|
|
83
|
+
)
|
|
84
|
+
group_client.send(param)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Device — 设备
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
client.device.get(registration_id) # 查询设备信息
|
|
91
|
+
client.device.set(registration_id, param) # 设置设备标签/别名
|
|
92
|
+
client.device.delete(registration_id) # 删除设备
|
|
93
|
+
client.device.get_status(param) # 查询设备在线状态
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Tag — 标签
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
client.tag.list() # 获取标签列表
|
|
100
|
+
client.tag.set(tag, param) # 添加/移除标签设备
|
|
101
|
+
client.tag.delete(tag, platforms=["android"]) # 删除标签
|
|
102
|
+
client.tag.get_count(tags, platforms=["android"]) # 查询标签设备数
|
|
103
|
+
client.tag.get_device_status(tag, registration_id) # 查询设备标签绑定状态
|
|
104
|
+
client.tag.get_quota(tags=["vip"], platforms=["android"]) # 查询标签配额
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Alias — 别名
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
client.alias.get(alias, platforms=["android"]) # 查询别名设备
|
|
111
|
+
client.alias.delete(alias, platforms=["android"]) # 删除别名
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Schedule — 定时任务
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
client.schedule.create(param) # 创建定时推送
|
|
118
|
+
client.schedule.update(id, param) # 更新定时推送
|
|
119
|
+
client.schedule.delete(id) # 删除定时推送
|
|
120
|
+
client.schedule.get(id) # 获取定时推送详情
|
|
121
|
+
client.schedule.list(page=1) # 获取定时推送列表
|
|
122
|
+
client.schedule.get_msg_ids(id) # 获取定时推送消息 ID
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Status — 统计
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
client.status.users(time_unit, start, duration) # 用户统计
|
|
129
|
+
client.status.message_detail(message_ids) # 消息送达统计
|
|
130
|
+
client.status.message_lifecycle(msg_id, registration_ids) # 消息生命周期
|
|
131
|
+
client.status.batch_message_detail(message_ids) # 批量消息统计
|
|
132
|
+
client.status.plan_detail(plan_id, message_ids) # 推送计划统计
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Plan — 推送计划
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
client.plan.create_or_update(param) # 创建/更新推送计划
|
|
139
|
+
client.plan.list(page_index=1, page_size=10, send_source=None, search_description="") # 查询列表
|
|
140
|
+
client.plan.query_msg(plan_ids, start_date, end_date) # 查询计划消息 ID
|
|
141
|
+
client.plan.delete(plan_id) # 删除推送计划
|
|
142
|
+
client.plan.batch_delete(plan_ids) # 批量删除推送计划
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Voice — 语音/TTS
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
client.voice.create(param) # 创建语音模板
|
|
149
|
+
client.voice.list() # 获取语音模板列表
|
|
150
|
+
client.voice.get(language) # 获取语音模板
|
|
151
|
+
client.voice.delete(language) # 删除语音模板
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Image — 图片
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
client.image.upload_oppo(file_path) # 上传 OPPO 大图 (文件路径)
|
|
158
|
+
client.image.upload_oppo_from_reader(filename, reader) # 上传 OPPO 大图 (file-like object)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 错误处理
|
|
162
|
+
|
|
163
|
+
SDK 使用 `engagelab.ApiError` 异常类型返回 API 错误:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
try:
|
|
167
|
+
result = client.push.send(param)
|
|
168
|
+
except engagelab.ApiError as e:
|
|
169
|
+
print(f"API Error: status={e.status_code} code={e.error.code} message={e.error.message}")
|
|
170
|
+
except Exception as e:
|
|
171
|
+
print(f"Network Error: {e}")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 客户端配置
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
# 指定数据中心
|
|
178
|
+
client = engagelab.Client("key", "secret",
|
|
179
|
+
base_url=engagelab.DataCenter.HONG_KONG,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# 自定义超时
|
|
183
|
+
client = engagelab.Client("key", "secret", timeout=60)
|
|
184
|
+
|
|
185
|
+
# 自定义 Base URL
|
|
186
|
+
client = engagelab.Client("key", "secret",
|
|
187
|
+
base_url="https://custom-api.example.com",
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## 认证方式
|
|
192
|
+
|
|
193
|
+
| API 模块 | 认证 |
|
|
194
|
+
| ---------------------------------------------------------------------- | ----------------------------------------------- |
|
|
195
|
+
| Push / Device / Tag / Alias / Schedule / Status / Plan / Voice / Image | Basic Auth: `appKey:masterSecret` |
|
|
196
|
+
| Group Push | Basic Auth: `group-{GroupKey}:GroupMasterSecret` |
|
|
197
|
+
|
|
198
|
+
## 更新日志
|
|
199
|
+
|
|
200
|
+
详见 [CHANGELOG.md](CHANGELOG.md)。
|
|
201
|
+
|
|
202
|
+
## 许可证
|
|
203
|
+
|
|
204
|
+
MIT License
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""EngageLab AppPush Python SDK — zero third-party dependencies.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
import engagelab
|
|
6
|
+
|
|
7
|
+
client = engagelab.Client("app_key", "master_secret")
|
|
8
|
+
result = client.push.send(engagelab.PushParam(
|
|
9
|
+
to="all",
|
|
10
|
+
body=engagelab.PushBody(
|
|
11
|
+
platform="all",
|
|
12
|
+
notification=engagelab.NotificationMessage(alert="Hello!"),
|
|
13
|
+
),
|
|
14
|
+
))
|
|
15
|
+
print(result.msg_id)
|
|
16
|
+
|
|
17
|
+
For Group Push (separate authentication)::
|
|
18
|
+
|
|
19
|
+
gc = engagelab.GroupPushClient("group_key", "group_master_secret")
|
|
20
|
+
result = gc.send(engagelab.PushParam(...))
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from .client import Client, DataCenter
|
|
24
|
+
from .errors import ApiError, ErrorDetail
|
|
25
|
+
from .push import (
|
|
26
|
+
AndroidIntent,
|
|
27
|
+
AndroidNotification,
|
|
28
|
+
BatchPushParam,
|
|
29
|
+
BatchPushRequest,
|
|
30
|
+
BatchPushSingleResult,
|
|
31
|
+
CustomMessage,
|
|
32
|
+
HmosIntent,
|
|
33
|
+
HmosNotification,
|
|
34
|
+
IOSNotification,
|
|
35
|
+
LiveActivityAlert,
|
|
36
|
+
LiveActivityIOS,
|
|
37
|
+
LiveActivityMessage,
|
|
38
|
+
NotificationMessage,
|
|
39
|
+
Options,
|
|
40
|
+
PushBody,
|
|
41
|
+
PushParam,
|
|
42
|
+
PushResult,
|
|
43
|
+
PushTo,
|
|
44
|
+
PushWithdrawResult,
|
|
45
|
+
Seg,
|
|
46
|
+
)
|
|
47
|
+
from .device import (
|
|
48
|
+
DeviceGetResult,
|
|
49
|
+
DeviceSetParam,
|
|
50
|
+
DeviceSetTags,
|
|
51
|
+
DeviceStatusGetParam,
|
|
52
|
+
DeviceStatusGetResult,
|
|
53
|
+
)
|
|
54
|
+
from .tag import (
|
|
55
|
+
TagQuotaData,
|
|
56
|
+
TagQuotaGetResult,
|
|
57
|
+
TagRegistrationIDs,
|
|
58
|
+
TagSetParam,
|
|
59
|
+
TagsCountGetResult,
|
|
60
|
+
TagsGetResult,
|
|
61
|
+
)
|
|
62
|
+
from .alias import AliasStatusGetResult
|
|
63
|
+
from .schedule import (
|
|
64
|
+
SchedulePushDetailGetResult,
|
|
65
|
+
SchedulePushGetResult,
|
|
66
|
+
SchedulePushListResult,
|
|
67
|
+
SchedulePushParam,
|
|
68
|
+
SchedulePushResult,
|
|
69
|
+
ScheduleTrigger,
|
|
70
|
+
TriggerPeriodical,
|
|
71
|
+
TriggerSingle,
|
|
72
|
+
)
|
|
73
|
+
from .status import UserStatusGetResult, UserStatusItem, UserStatusPlatform
|
|
74
|
+
from .plan import (
|
|
75
|
+
PushPlanDeleteResult,
|
|
76
|
+
PushPlanInfo,
|
|
77
|
+
PushPlanListResult,
|
|
78
|
+
PushPlanParam,
|
|
79
|
+
PushPlanResult,
|
|
80
|
+
)
|
|
81
|
+
from .voice import VoiceListResult, VoiceParam, VoiceResult
|
|
82
|
+
from .image import ImageUploadResult
|
|
83
|
+
from .group_push import GroupPushClient, GroupPushResult
|
|
84
|
+
|
|
85
|
+
__version__ = "0.1.0"
|
|
86
|
+
|
|
87
|
+
__all__ = [
|
|
88
|
+
# Core
|
|
89
|
+
"Client",
|
|
90
|
+
"DataCenter",
|
|
91
|
+
"ApiError",
|
|
92
|
+
"ErrorDetail",
|
|
93
|
+
# Push
|
|
94
|
+
"PushParam",
|
|
95
|
+
"PushBody",
|
|
96
|
+
"PushTo",
|
|
97
|
+
"Seg",
|
|
98
|
+
"NotificationMessage",
|
|
99
|
+
"AndroidNotification",
|
|
100
|
+
"AndroidIntent",
|
|
101
|
+
"IOSNotification",
|
|
102
|
+
"HmosNotification",
|
|
103
|
+
"HmosIntent",
|
|
104
|
+
"CustomMessage",
|
|
105
|
+
"LiveActivityMessage",
|
|
106
|
+
"LiveActivityIOS",
|
|
107
|
+
"LiveActivityAlert",
|
|
108
|
+
"Options",
|
|
109
|
+
"PushResult",
|
|
110
|
+
"PushWithdrawResult",
|
|
111
|
+
"BatchPushParam",
|
|
112
|
+
"BatchPushRequest",
|
|
113
|
+
"BatchPushSingleResult",
|
|
114
|
+
# Device
|
|
115
|
+
"DeviceStatusGetParam",
|
|
116
|
+
"DeviceStatusGetResult",
|
|
117
|
+
"DeviceGetResult",
|
|
118
|
+
"DeviceSetParam",
|
|
119
|
+
"DeviceSetTags",
|
|
120
|
+
# Tag
|
|
121
|
+
"TagsGetResult",
|
|
122
|
+
"TagSetParam",
|
|
123
|
+
"TagRegistrationIDs",
|
|
124
|
+
"TagsCountGetResult",
|
|
125
|
+
"TagQuotaGetResult",
|
|
126
|
+
"TagQuotaData",
|
|
127
|
+
# Alias
|
|
128
|
+
"AliasStatusGetResult",
|
|
129
|
+
# Schedule
|
|
130
|
+
"SchedulePushParam",
|
|
131
|
+
"ScheduleTrigger",
|
|
132
|
+
"TriggerSingle",
|
|
133
|
+
"TriggerPeriodical",
|
|
134
|
+
"SchedulePushResult",
|
|
135
|
+
"SchedulePushGetResult",
|
|
136
|
+
"SchedulePushListResult",
|
|
137
|
+
"SchedulePushDetailGetResult",
|
|
138
|
+
# Status
|
|
139
|
+
"UserStatusGetResult",
|
|
140
|
+
"UserStatusItem",
|
|
141
|
+
"UserStatusPlatform",
|
|
142
|
+
# Plan
|
|
143
|
+
"PushPlanParam",
|
|
144
|
+
"PushPlanResult",
|
|
145
|
+
"PushPlanDeleteResult",
|
|
146
|
+
"PushPlanListResult",
|
|
147
|
+
"PushPlanInfo",
|
|
148
|
+
# Voice
|
|
149
|
+
"VoiceParam",
|
|
150
|
+
"VoiceResult",
|
|
151
|
+
"VoiceListResult",
|
|
152
|
+
# Image
|
|
153
|
+
"ImageUploadResult",
|
|
154
|
+
# Group Push
|
|
155
|
+
"GroupPushClient",
|
|
156
|
+
"GroupPushResult",
|
|
157
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Internal serialization utilities for converting between Python objects and JSON-compatible dicts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
from typing import Any, Dict, Type, TypeVar
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def to_dict(obj: Any) -> Any:
|
|
12
|
+
"""Recursively convert a dataclass instance (or dict/list) to a JSON-serializable dict.
|
|
13
|
+
|
|
14
|
+
``None`` values are omitted, mirroring Go's ``omitempty`` JSON tag behaviour.
|
|
15
|
+
"""
|
|
16
|
+
if obj is None:
|
|
17
|
+
return None
|
|
18
|
+
if isinstance(obj, dict):
|
|
19
|
+
return {k: to_dict(v) for k, v in obj.items() if v is not None}
|
|
20
|
+
if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
|
|
21
|
+
result: Dict[str, Any] = {}
|
|
22
|
+
field_map: Dict[str, str] = getattr(type(obj), "_FIELD_MAP", {})
|
|
23
|
+
for f in dataclasses.fields(obj):
|
|
24
|
+
val = getattr(obj, f.name)
|
|
25
|
+
if val is None:
|
|
26
|
+
continue
|
|
27
|
+
key = field_map.get(f.name, f.name)
|
|
28
|
+
result[key] = to_dict(val)
|
|
29
|
+
return result
|
|
30
|
+
if isinstance(obj, (list, tuple)):
|
|
31
|
+
return [to_dict(v) for v in obj]
|
|
32
|
+
return obj
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def from_dict(cls: Type[T], data: Any) -> T: # type: ignore[return]
|
|
36
|
+
"""Create a dataclass instance from a dict, mapping JSON keys back to field names."""
|
|
37
|
+
if data is None:
|
|
38
|
+
return None # type: ignore[return-value]
|
|
39
|
+
if not dataclasses.is_dataclass(cls):
|
|
40
|
+
return data # type: ignore[return-value]
|
|
41
|
+
field_map: Dict[str, str] = getattr(cls, "_FIELD_MAP", {})
|
|
42
|
+
kwargs: Dict[str, Any] = {}
|
|
43
|
+
for f in dataclasses.fields(cls):
|
|
44
|
+
json_key = field_map.get(f.name, f.name)
|
|
45
|
+
if json_key in data:
|
|
46
|
+
kwargs[f.name] = data[json_key]
|
|
47
|
+
elif f.name in data:
|
|
48
|
+
kwargs[f.name] = data[f.name]
|
|
49
|
+
return cls(**kwargs)
|