├── .github
└── workflows
│ └── python-publish.yml
├── .gitignore
├── .travis.yml
├── CHANGES.txt
├── MANIFEST.in
├── README.md
├── README.rst
├── docs
└── push
│ └── push.md
├── examples
├── __init__.py
├── admin_example.py
├── batch_push_example.py
├── conf.py.sample
├── device_example.py
├── group_push_example.py
├── push_example.py
├── report_example.py
├── schedule_example.py
└── zone_example.py
├── jpush
├── __init__.py
├── common.py
├── core.py
├── device
│ ├── __init__.py
│ ├── core.py
│ └── entity.py
├── push
│ ├── __init__.py
│ ├── audience.py
│ ├── core.py
│ └── payload.py
├── report
│ ├── __init__.py
│ └── core.py
└── schedule
│ ├── __init__.py
│ ├── core.py
│ └── schedulepayload.py
├── setup.cfg
├── setup.py
└── tests
├── __init__.py
├── conf.py.example
├── devices
├── test_devices.py
└── test_entity.py
├── push
├── test_audience.py
└── test_message.py
├── report
└── test_report.py
└── schedule
└── test_schedule.py
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Upload Python Package
10 |
11 | on:
12 | release:
13 | types: [published]
14 |
15 | jobs:
16 | deploy:
17 |
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Set up Python
23 | uses: actions/setup-python@v2
24 | with:
25 | python-version: '3.x'
26 | - name: Install dependencies
27 | run: |
28 | python -m pip install --upgrade pip
29 | pip install build
30 | pip install wheel
31 | - name: Build package
32 | run: python -m build
33 | - name: Publish package
34 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
35 | with:
36 | user: __token__
37 | password: ${{ secrets.PYPI_API_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | build/
3 | dist/
4 | jpush.egg-info/
5 | .idea/
6 | examples/conf.py
7 | /tests/conf.py
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.6"
4 | - "2.7"
5 | # - "3.2"
6 | # - "3.3"
7 | # - "3.4"
8 | - "3.5"
9 | - "3.6"
10 |
11 | # command to install dependencies
12 | install:
13 | - pip install .
14 |
15 | # command to run tests
16 | script: nosetests tests/* --verbosity=2
17 |
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | Version 3.1.0
2 | -----------
3 | **Released 2015 Nov 9**
4 | Fix device api some bugs;
5 |
6 |
7 | Version 3.1.0
8 | -----------
9 | **Released 2015 August 20**
10 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | # file GENERATED by distutils, do NOT edit
2 | include *.txt
3 | recursive-include examples *
4 | include CHANGES.txt
5 | include LICENSE.txt
6 | include README.rst
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JPush API Python Client
2 |
3 | ## 概述
4 | 这是 JPush REST API 的 Python 版本封装开发包,是由极光推送官方提供的,一般支持最新的 API 功能。
5 |
6 | 对应的 REST API 文档:
7 |
8 | ## 兼容版本
9 | + Python 2.7
10 | + Python 3
11 |
12 | ## 环境配置
13 |
14 | pip 方式:
15 | ```
16 | sudo pip install jpush
17 | ```
18 | easy_install 方式:
19 | ```
20 | sudo easy_install jpush
21 | ```
22 | 使用源码方式:
23 | ```
24 | sudo python setup.py install
25 | ```
26 |
27 |
28 | ## 代码样例
29 |
30 | > 代码样例在 jpush-api-python-client 中的 examples 文件夹中,[点击查看所有 examples ](https://github.com/jpush/jpush-api-python-client/tree/master/examples) 。
31 |
32 | > 以下片断来自项目代码里的文件:jpush-api-python-client 中的 examples/push_examples 目录下的 example_all.py
33 |
34 | > 这个样例演示了消息推送,日志设置,异常处理。
35 |
36 | ```
37 | _jpush = jpush.JPush(app_key, master_secret)
38 | push = _jpush.create_push()
39 | # if you set the logging level to "DEBUG",it will show the debug logging.
40 | _jpush.set_logging("DEBUG")
41 | push.audience = jpush.all_
42 | push.notification = jpush.notification(alert="hello python jpush api")
43 | push.platform = jpush.all_
44 | try:
45 | response=push.send()
46 | except common.Unauthorized:
47 | raise common.Unauthorized("Unauthorized")
48 | except common.APIConnectionException:
49 | raise common.APIConnectionException("conn error")
50 | except common.JPushFailure:
51 | print ("JPushFailure")
52 | except:
53 | print ("Exception")
54 | ```
55 | ## 日志说明
56 | logging level 默认的是 WARNING ,为了方便调试建议设置为 DEBUG
57 | 设置方法为:
58 | ```
59 | _jpush.set_logging("DEBUG")
60 | ```
61 |
62 | ## 异常说明
63 |
64 | + Unauthorized
65 | + AppKey,Master Secret 错误,验证失败必须改正。
66 |
67 | + APIConnectionException
68 | + 包含错误的信息:比如超时,无网络等情况。
69 |
70 | + JPushFailure
71 | + 请求出错,参考业务返回码。
72 |
73 | ## HTTP 状态码
74 |
75 | 参考文档:
76 |
77 | Push v3 API 状态码 参考文档:
78 |
79 | Report API 状态码 参考文档:
80 |
81 | Device API 状态码 参考文档:
82 |
83 | Push Schedule API 状态码 参考文档:
84 |
85 | [Release页面](https://github.com/jpush/jpush-api-python-client/releases) 有详细的版本发布记录与下载。
86 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | =======================
2 | JPush API Python Client
3 | =======================
4 |
5 | JPush's officially supported Python client library for accessing JPush APIs.
6 |
7 | JPush Rest API Documents: http://docs.jpush.io/server/rest_api_v3_push/
8 |
9 | You can download the latest release file here: https://github.com/jpush/jpush-api-python-client/releases
10 |
11 | ------------
12 | Installation
13 | ------------
14 | To install jpush-api-python-client, simply:
15 |
16 | $ sudo pip install jpush
17 |
18 | or alternatively install via easy_install:
19 |
20 | $ sudo easy_install jpush
21 |
22 |
23 | or from source:
24 |
25 | $ sudo python setup.py install
26 |
27 | -------
28 | Testing
29 | -------
30 | For running the tests, you need the standard `unittest` module, shipped
31 | with Python.
32 |
33 | To run jpush-api-python-client tests, simply:
34 |
35 | $ nosetests tests/push tests/devices --verbosity=2
36 |
37 | --------
38 | Examples
39 | --------
40 | You can see more examples in https://github.com/jpush/jpush-api-python-client/blob/master/examples
41 |
42 | Simple iOS Push
43 | ---------------
44 | >>> import jpush as jpush
45 | >>> from conf import app_key, master_secret
46 | >>> _jpush = jpush.JPush(app_key, master_secret)
47 | >>> push = _jpush.create_push()
48 | >>> push.audience = jpush.all_
49 | >>> ios_msg = jpush.ios(alert="Hello, IOS JPush!", badge="+1", sound="a.caf", extras={'k1':'v1'})
50 | >>> android_msg = jpush.android(alert="Hello, android msg")
51 | >>> push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg)
52 | >>> push.options = {"time_to_live":86400, "sendno":12345,"apns_production":True}
53 | >>> push.platform = jpush.platform("ios")
54 | >>> push.send()
55 |
56 |
57 | Get taglist
58 | -----------------
59 | >>> import jpush as jpush
60 | >>> from conf import app_key, master_secret
61 | >>> _jpush = jpush.JPush(app_key, master_secret)
62 | >>> device = _jpush.create_device()
63 | >>> device.get_taglist()
64 |
65 | --------
66 | Questions
67 | --------
68 | The best place to ask questions is our community site:
69 | http://community.jpush.cn/
70 |
--------------------------------------------------------------------------------
/docs/push/push.md:
--------------------------------------------------------------------------------
1 | ## 初始化
2 | 实例化JPush对象
3 |
4 | ```
5 | _jpush = jpush.JPush(app_key, master_secret)
6 | ```
7 |
8 | 参数说明
9 |
10 | > app_key https://www.jpush.cn/ 控制台获取
11 |
12 | > master_secret https://www.jpush.cn/ 控制台获取
13 |
14 | 返回值
15 |
16 | > JPush 实例
17 |
18 | ## Push api
19 |
20 | ### 初始化push对象
21 |
22 | ```
23 | push = _jpush.create_push()
24 | ```
25 | 参数说明 (无)
26 |
27 | 返回值
28 |
29 | > push 实例
30 |
31 | #### audience 设置
32 |
33 | ##### tag 设置
34 | ```
35 | tag(*tags)
36 | ```
37 |
38 | 参数说明
39 |
40 | tags 例如:tag("tag1", "tag2")
41 |
42 |
43 | 返回值
44 | > payload 字典
45 |
46 | ##### tag_and 设置
47 | ```
48 | tag_and(*tag_ands)
49 | ```
50 |
51 | 参数说明
52 |
53 | tags 例如:tag_and("tag1", "tag2")
54 |
55 | 返回值
56 | > payload 字典
57 |
58 | ##### tag_not 设置
59 | ```
60 | tag_not(*tag_nots)
61 | ```
62 |
63 | 参数说明
64 |
65 | tags 例如:tag_not("tag1", "tag2")
66 |
67 | 返回值
68 | > payload 字典
69 |
70 | ##### alias 设置
71 | ```
72 | alias(*alias)
73 | ```
74 |
75 | 参数说明
76 |
77 | alias 例如:alias("alias1", "alias2")
78 |
79 | 返回值
80 | > payload 字典
81 |
82 | ##### registration_id 设置
83 | ```
84 | registration_id(*reg_ids)
85 | ```
86 |
87 | 参数说明
88 |
89 | registration_id 例如:tag("registration_id1", "registration_id2")
90 |
91 | 返回值
92 |
93 | > payload 字典
94 |
95 | ##### 推送目标说明
96 |
97 | 推送设备对象,表示一条推送可以被推送到哪些设备列表。确认推送设备对象,JPush 提供了多种方式,比如:别名、标签、注册ID、分群、广播等。
98 |
99 | * all
100 |
101 | 如果要发广播(全部设备),则直接填写 “all”。
102 |
103 | * 推送目标
104 |
105 | 广播外的设备选择方式,有如下几种:
106 |
107 |
108 |
109 |
110 | 关键字 |
111 | 含义 |
112 | 类型 |
113 | 说明 |
114 | 备注 |
115 |
116 |
117 | tag |
118 | JSON Array |
119 | 标签 |
120 | 数组。多个标签之间是 OR 的关系,即取并集。 |
121 | 用标签来进行大规模的设备属性、用户属性分群。 一次推送最多 20 个。- 有效的 tag 组成:字母(区分大小写)、数字、下划线、汉字。
- 限制:每一个 tag 的长度限制为 40 字节。(判断长度需采用UTF-8编码)
|
122 |
123 |
124 | tag_and |
125 | JSON Array |
126 | 标签AND |
127 | 数组。多个标签之间是 AND 关系,即取交集。 |
128 | 注册与 tag 区分。一次推送最多 20 个。 |
129 |
130 |
131 | alias |
132 | JSON Array |
133 | 别名 |
134 | 数组。多个别名之间是 OR 关系,即取并集。 |
135 | 用别名来标识一个用户。一个设备只能绑定一个别名,但多个设备可以绑定同一个别名。一次推送最多 1000 个。- 有效的 alias 组成:字母(区分大小写)、数字、下划线、汉字。
- 限制:每一个 alias 的长度限制为 40 字节。(判断长度需采用UTF-8编码)
|
136 |
137 |
138 | registration_id |
139 | JSON Array |
140 | 注册ID |
141 | 数组。多个注册ID之间是 OR 关系,即取并集。 |
142 | 设备标识。一次推送最多 1000 个。 |
143 |
144 |
145 |
146 |
147 |
148 | #### notification 设置
149 | ```
150 | notification(alert=None, ios=None, android=None, winphone=None)
151 | ```
152 | 参数说明
153 |
154 | * alert
155 |
156 | > 通知的内容在各个平台上,都可能只有这一个最基本的属性 "alert"。
157 | 这个位置的 "alert" 属性(直接在 notification 对象下),是一个快捷定义,各平台的 alert 信息如果都一样,则可不定义。如果各平台有定义,则覆盖这里的定义。
158 |
159 | * ios
160 |
161 | > ios payload 字典 查看 [ios payload](https://github.com/jpush/jpush-api-python-client/blob/master/docs/push/push.md#ios-payload)
162 |
163 | * android
164 |
165 | > android payload 字典 查看 [android payload](https://github.com/jpush/jpush-api-python-client/blob/master/docs/push/push.md#android-payload)
166 |
167 |
168 | 返回值
169 |
170 | > notification payload
171 |
172 | ##### ios payload
173 | ```
174 | ios(alert=None, badge='+1', sound=None, content_available=False, mutable_content=False, category=None, extras=None, sound_disable=False, thread_id=None):
175 | ```
176 |
177 | 参数说明
178 |
179 |
180 |
181 | 关键字 |
182 | 类型 |
183 | 选项 |
184 | 含义 |
185 | 说明 |
186 |
187 |
188 | alert |
189 | string |
190 | 必填 |
191 | 通知内容 |
192 |
193 | 这里指定内容将会覆盖上级统一指定的 alert 信息;内容为空则不展示到通知栏。支持字符串形式也支持官方定义的 alert payload 结构,在该结构中包含 title 和 subtitle 等官方支持的 key
194 | |
195 |
196 |
197 | sound |
198 | string |
199 | 可选 |
200 | 通知提示声音 |
201 |
202 | 普通通知: string类型,如果无此字段,则此消息无声音提示;有此字段,如果找到了指定的声音就播放该声音,否则播放默认声音,如果此字段为空字符串,iOS 7 为默认声音,iOS 8 及以上系统为无声音。说明:JPush 官方 SDK 会默认填充声音字段,提供另外的方法关闭声音,详情查看各 SDK 的源码。
203 | 告警通知: JSON Object ,支持官方定义的 payload 结构,在该结构中包含 critical 、name 和 volume 等官方支持的 key
204 | |
205 |
206 |
207 | badge |
208 | int |
209 | 可选 |
210 | 应用角标 |
211 | 如果不填,表示不改变角标数字;否则把角标数字改为指定的数字;为 0 表示清除。JPush 官方 API Library(SDK) 会默认填充badge值为"+1",详情参考:badge +1 |
212 |
213 |
214 | content-available |
215 | boolean |
216 | 可选 |
217 | 推送唤醒 |
218 | 推送的时候携带"content-available":true 说明是 Background Remote Notification,如果不携带此字段则是普通的Remote Notification。详情参考:Background Remote Notification |
219 |
220 |
221 | mutable-content |
222 | boolean |
223 | 可选 |
224 | 通知扩展 |
225 | 推送的时候携带 ”mutable-content":true 说明是支持iOS10的UNNotificationServiceExtension,如果不携带此字段则是普通的 Remote Notification。详情参考:UNNotificationServiceExtension |
226 |
227 |
228 | category |
229 | string |
230 | 可选 |
231 | |
232 | IOS8才支持。设置APNs payload中的"category"字段值 |
233 |
234 |
235 | thread-id |
236 | string |
237 | 可选 |
238 | 通知分组 |
239 | ios 的远程通知通过该属性来对通知进行分组,同一个 thread-id 的通知归为一组。 |
240 |
241 |
242 | extras |
243 | JSON Object |
244 | 可选 |
245 | 扩展字段 |
246 | 这里自定义 Key/value 信息,以供业务使用。 |
247 |
248 |
249 |
250 |
251 | 返回值
252 |
253 | > android payload 字典
254 |
255 | ##### android payload
256 | ```
257 | android(alert, title=None, builder_id=None, extras=None, priority=None, category=None, style=None, alert_type=None, big_text=None, inbox=None, big_pic_path=None, large_icon=None, intent=None)
258 | ```
259 | 参数说明
260 |
261 |
262 |
263 | 关键字 |
264 | 类型 |
265 | 选项 |
266 | 含义 |
267 | 说明 |
268 |
269 |
270 | alert |
271 | string |
272 | 必填 |
273 | 通知内容 |
274 | 这里指定了,则会覆盖上级统一指定的 alert 信息;内容可以为空字符串,则表示不展示到通知栏。 |
275 |
276 |
277 | title |
278 | string |
279 | 可选 |
280 | 通知标题 |
281 | 如果指定了,则通知里原来展示 App名称的地方,将展示成这个字段。 |
282 |
283 |
284 | builder_id |
285 | int |
286 | 可选 |
287 | 通知栏样式ID |
288 | Android SDK 可设置通知栏样式,这里根据样式 ID 来指定该使用哪套样式。 |
289 |
290 |
291 | extras |
292 | JSON Object |
293 | 可选 |
294 | 扩展字段 |
295 | 这里自定义 JSON 格式的 Key/Value 信息,以供业务使用。 |
296 |
297 |
298 |
299 |
300 |
301 | 返回值
302 |
303 | > android payload 字典
304 |
305 | #### message 设置
306 |
307 | ```
308 | message(msg_content, title=None, content_type=None, extras=None)
309 | ```
310 |
311 | 参数说明
312 |
313 |
314 |
315 |
316 | 关键字 |
317 | 类型 |
318 | 选项 |
319 | 含义 |
320 |
321 |
322 | msg_content |
323 | string |
324 | 必填 |
325 | 消息内容本身 |
326 |
327 |
328 | title |
329 | string |
330 | 可选 |
331 | 消息标题 |
332 |
333 |
334 | content_type |
335 | string |
336 | 可选 |
337 | 消息内容类型 |
338 |
339 |
340 | extras |
341 | JSON Object |
342 | 可选 |
343 | JSON 格式的可选参数 |
344 |
345 |
346 |
347 |
348 | 返回值
349 |
350 | >message payload
351 |
352 | #### smsmessage 设置
353 |
354 | ```
355 | smsmessage(content,delay_time)
356 | ```
357 |
358 | * 参数说明
359 |
360 |
361 |
362 |
363 | 关键字 |
364 | 类型 |
365 | 选项 |
366 | 示例 |
367 |
368 |
369 | content |
370 | string |
371 | 必填 |
372 | 不能超过480个字符。"你好,JPush"为8个字符。超过67个字符的内容(含签名)会被拆分成多条短信下发。 |
373 |
374 |
375 | delay_time |
376 | int |
377 | 必填 |
378 | 单位为秒,不能超过24小时。设置为0,表示立即发送短信。该参数仅对android平台有效,iOS 和 Winphone平台则会立即发送短信 |
379 |
380 |
381 |
382 |
383 | * 返回值
384 |
385 | > smsmessage payload
386 |
387 | #### platform 设置
388 |
389 | ```
390 | platform(*types)
391 | ```
392 |
393 | * 参数说明
394 |
395 | JPush 当前支持 Android, iOS, Windows Phone 三个平台的推送。其关键字分别为:"android", "ios","winphone"。
396 |
397 | * 返回值
398 |
399 | > platform tuple
400 |
401 | #### options 设置
402 |
403 | ```
404 | options(options)
405 | ```
406 |
407 | * 参数说明
408 |
409 |
410 |
411 |
412 | 关键字 |
413 | 类型 |
414 | 选项 |
415 | 含义 |
416 | 说明 |
417 |
418 |
419 | sendno |
420 | int |
421 | 可选 |
422 | 推送序号 |
423 | 纯粹用来作为 API 调用标识,API 返回时被原样返回,以方便 API 调用方匹配请求与返回。 |
424 |
425 |
426 | time_to_live |
427 | int |
428 | 可选 |
429 | 离线消息保留时长(秒) |
430 | 推送当前用户不在线时,为该用户保留多长时间的离线消息,以便其上线时再次推送。默认 86400 (1 天),最长 10 天。设置为 0 表示不保留离线消息,只有推送当前在线的用户可以收到。 |
431 |
432 |
433 | override_msg_id |
434 | long |
435 | 可选 |
436 | 要覆盖的消息ID |
437 | 如果当前的推送要覆盖之前的一条推送,这里填写前一条推送的 msg_id 就会产生覆盖效果,即:1)该 msg_id 离线收到的消息是覆盖后的内容;2)即使该 msg_id Android 端用户已经收到,如果通知栏还未清除,则新的消息内容会覆盖之前这条通知;覆盖功能起作用的时限是:1 天。如果在覆盖指定时限内该 msg_id 不存在,则返回 1003 错误,提示不是一次有效的消息覆盖操作,当前的消息不会被推送。 |
438 |
439 |
440 | apns_production |
441 | boolean |
442 | 可选 |
443 | APNs是否生产环境 |
444 | True 表示推送生产环境,False 表示要推送开发环境;如果不指定则为推送生产环境。JPush 官方 API LIbrary (SDK) 默认设置为推送 “开发环境”。 |
445 |
446 |
447 | big_push_duration |
448 | int |
449 | 可选 |
450 | 定速推送时长(分钟) |
451 | 又名缓慢推送,把原本尽可能快的推送速度,降低下来,给定的n分钟内,均匀地向这次推送的目标用户推送。最大值为1400.未设置则不是定速推送。 |
452 |
453 |
454 |
455 |
456 | * 返回值
457 |
458 | > options 字典
459 |
--------------------------------------------------------------------------------
/examples/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
4 |
5 | import jpush
6 |
7 | from .conf import app_key, master_secret, dev_key, dev_secret
8 |
9 | from . import device_example
10 | from . import push_example
11 | from . import report_example
12 | from . import schedule_example
13 | from . import group_push_example
14 | from . import admin_example
15 | from . import zone_example
16 |
17 | __all__ = [
18 | device_example,
19 | push_example,
20 | report_example,
21 | schedule_example,
22 | group_push_example,
23 | admin_example,
24 | zone_example,
25 | ]
26 |
--------------------------------------------------------------------------------
/examples/admin_example.py:
--------------------------------------------------------------------------------
1 | from . import jpush, dev_key, dev_secret
2 |
3 | admin = jpush.Admin(dev_key, dev_secret)
4 | admin.set_logging("DEBUG")
5 |
6 | def create_app():
7 | response = admin.create_app('aaa', 'cn.jpush.app')
8 | return response
9 |
10 | def delete_app(app_key):
11 | response = admin.delete_app(app_key)
12 | return response
13 |
--------------------------------------------------------------------------------
/examples/batch_push_example.py:
--------------------------------------------------------------------------------
1 | from conf import app_key, master_secret
2 | import jpush
3 |
4 | _jpush = jpush.JPush(app_key, master_secret)
5 | _jpush.set_logging("DEBUG")
6 |
7 | push = _jpush.create_push()
8 | single_payload_list = [
9 | {'platform':'all', 'target':'regid1', 'notification':{'alert':'alert content'}},
10 | {'platform':'all', 'target':'regid2', 'notification':{'alert':'alert content'}}
11 | ]
12 | response = push.batch_push_by_regid(single_payload_list)
13 | print(response)
--------------------------------------------------------------------------------
/examples/conf.py.sample:
--------------------------------------------------------------------------------
1 | # please put your app_key and master_secret here
2 | app_key = u'xxxxxx'
3 | master_secret = u'xxxxxx'
4 |
5 | dev_key = u'xxxxxx'
6 | dev_secret = u'xxxxxx'
7 |
--------------------------------------------------------------------------------
/examples/device_example.py:
--------------------------------------------------------------------------------
1 | from . import jpush, app_key, master_secret
2 |
3 | _jpush = jpush.JPush(app_key, master_secret)
4 | _jpush.set_logging("DEBUG")
5 | device = _jpush.create_device()
6 |
7 | def alias_user():
8 | alias = "alias1"
9 | platform = "android,ios,hmos"
10 | device.get_aliasuser(alias, platform)
11 |
12 | def ctrl_tag():
13 | reg_id = '090c1f59f89'
14 | entity = jpush.device_tag("")
15 | device.set_deviceinfo(reg_id, entity)
16 |
17 | def get_device():
18 | reg_id = '090c1f59f89'
19 | device.get_deviceinfo(reg_id)
20 |
21 | def delete_alias():
22 | alias = "alias1"
23 | platform = "android,ios,hmos"
24 | device.delete_alias(alias, platform)
25 |
26 | def delete_tag():
27 | tag = "ddd"
28 | platform = "android,ios,hmos"
29 | device.delete_tag(tag, platform)
30 |
31 | def check_tag():
32 | tag = "ddd"
33 | registration_id = '090c1f59f89'
34 | device.check_taguserexist(tag, registration_id)
35 |
36 | def tag_list():
37 | device.get_taglist()
38 |
39 | def tag_update_user():
40 | tag = "ddd"
41 | entity = jpush.device_regid(jpush.add("090c1f59f89"))
42 | device.update_tagusers(tag, entity)
43 |
44 | def update_device():
45 | reg_id = '1507bfd3f7c466c355c'
46 | entity = jpush.device_tag(jpush.add("ddd", "tageee"))
47 | result=device.set_devicemobile(reg_id, entity)
48 | print (result.status_code)
49 | print (result.payload)
50 |
51 | def update_device_mobile():
52 | reg_id = '1507bfd3f7c466c355c'
53 | entity = jpush.device_mobile("18588232140")
54 | device.set_devicemobile(reg_id, entity)
55 |
56 | def get_device_status():
57 | reg_id = '1507bfd3f7c466c355c'
58 | device.get_device_status(reg_id)
59 |
--------------------------------------------------------------------------------
/examples/group_push_example.py:
--------------------------------------------------------------------------------
1 | from . import jpush
2 | from jpush import common
3 |
4 | group_key = u'xxxxxx'
5 | group_secret = u'xxxxxx'
6 |
7 | group = jpush.GroupPush(group_key, group_secret)
8 | group.set_logging("DEBUG")
9 |
10 | def all():
11 | push = group.create_push()
12 | push.audience = jpush.all_
13 | push.notification = jpush.notification(alert="!hello python jpush api")
14 | push.platform = jpush.all_
15 | try:
16 | response=push.send()
17 | except common.Unauthorized:
18 | raise common.Unauthorized("Unauthorized")
19 | except common.APIConnectionException:
20 | raise common.APIConnectionException("conn")
21 | except common.JPushFailure:
22 | print ("JPushFailure")
23 | except:
24 | print ("Exception")
25 |
--------------------------------------------------------------------------------
/examples/push_example.py:
--------------------------------------------------------------------------------
1 | from conf import app_key, master_secret
2 | import jpush
3 |
4 | _jpush = jpush.JPush(app_key, master_secret)
5 | _jpush.set_logging("DEBUG")
6 |
7 | def alias():
8 | push = _jpush.create_push()
9 | alias=["alias1", "alias2"]
10 | alias1={"alias": alias}
11 | print(alias1)
12 | push.audience = jpush.audience(
13 | jpush.tag("tag1", "tag2"),
14 | alias1
15 | )
16 |
17 | push.notification = jpush.notification(alert="Hello world with audience!")
18 | push.platform = jpush.all_
19 | print (push.payload)
20 | push.send()
21 |
22 | def all():
23 | push = _jpush.create_push()
24 | push.audience = jpush.all_
25 | push.notification = jpush.notification(alert="!hello python jpush api")
26 | push.platform = jpush.all_
27 | try:
28 | response=push.send()
29 | except common.Unauthorized:
30 | raise common.Unauthorized("Unauthorized")
31 | except common.APIConnectionException:
32 | raise common.APIConnectionException("conn")
33 | except common.JPushFailure:
34 | print ("JPushFailure")
35 | except:
36 | print ("Exception")
37 |
38 | def audience():
39 | push = _jpush.create_push()
40 |
41 | push.audience = jpush.audience(
42 | jpush.tag("tag1", "tag2"),
43 | jpush.alias("alias1", "alias2")
44 | )
45 |
46 |
47 | push.notification = jpush.notification(alert="Hello world with audience!")
48 | push.platform = jpush.all_
49 | print (push.payload)
50 | push.send()
51 |
52 |
53 | def notification():
54 | push = _jpush.create_push()
55 |
56 | push.audience = jpush.all_
57 | push.platform = jpush.all_
58 |
59 | ios = jpush.ios(alert="Hello, IOS JPush!", sound="a.caf", extras={'k1':'v1'})
60 | android = jpush.android(alert="Hello, Android msg", priority=1, style=1, alert_type=1,big_text='jjjjjjjjjj', extras={'k1':'v1'})
61 | hmos = jpush.hmos(alert="Hello, HMOS JPush!", category="category", large_icon="large_icon", intent={"url":"action.system.home"}, extras={'k1':'v1'}, style="style", inbox="inbox")
62 |
63 | push.notification = jpush.notification(alert="Hello, JPush!", android=android, ios=ios, hmos=hmos)
64 |
65 | # pprint (push.payload)
66 | result = push.send()
67 |
68 | def options():
69 | push = _jpush.create_push()
70 | push.audience = jpush.all_
71 | push.notification = jpush.notification(alert="Hello, world!")
72 | push.platform = jpush.all_
73 | push.options = {"time_to_live":86400, "sendno":12345,"apns_production":True}
74 | push.send()
75 |
76 | def platfrom_msg():
77 | push = _jpush.create_push()
78 | push.audience = jpush.all_
79 | ios_msg = jpush.ios(alert="Hello, IOS JPush!", badge="+1", sound="a.caf", extras={'k1':'v1'})
80 | android_msg = jpush.android(alert="Hello, android msg")
81 | hmos_msg = jpush.hmos(alert="Hello, HMOS JPush msg")
82 | push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg, hmos=hmos_msg)
83 | push.message=jpush.message("content",extras={'k2':'v2','k3':'v3'})
84 | push.platform = jpush.all_
85 | push.send()
86 |
87 |
88 | def silent():
89 | push = _jpush.create_push()
90 | push.audience = jpush.all_
91 | ios_msg = jpush.ios(alert="Hello, IOS JPush!", badge="+1", extras={'k1':'v1'}, sound_disable=True)
92 | android_msg = jpush.android(alert="Hello, android msg")
93 | hmos_msg = jpush.hmos(alert="Hello, HMOS JPush msg")
94 | push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg, hmos=hmos_msg)
95 | push.platform = jpush.all_
96 | push.send()
97 |
98 |
99 | def sms():
100 | push = _jpush.create_push()
101 | push.audience = jpush.all_
102 | push.notification = jpush.notification(alert="a sms message from python jpush api")
103 | push.platform = jpush.all_
104 | push.smsmessage=jpush.smsmessage("a sms message from python jpush api",0)
105 | print (push.payload)
106 | push.send()
107 |
108 | def validate():
109 | push = _jpush.create_push()
110 | push.audience = jpush.all_
111 | push.notification = jpush.notification(alert="Hello, world!")
112 | push.platform = jpush.all_
113 | push.send_validate()
--------------------------------------------------------------------------------
/examples/report_example.py:
--------------------------------------------------------------------------------
1 | from conf import app_key, master_secret
2 | import jpush
3 |
4 | _jpush = jpush.JPush(app_key, master_secret)
5 | _jpush.set_logging("DEBUG")
6 | report=_jpush.create_report()
7 |
8 | def messages():
9 | report.get_messages("3289406737")
10 |
11 | def messages_detail():
12 | report.get_messages_detail("3289406737")
13 |
14 | def received():
15 | report.get_received("3289406737")
16 |
17 | def received_detail():
18 | report.get_received_detail("3289406737")
19 |
20 | def users():
21 | report.get_users("DAY","2016-04-10","3")
22 |
23 | def status():
24 | msgid = '3289406737'
25 | regid = 'xxx'
26 | report.get_status_message(int(msgid), [regid])
27 |
28 | messages_detail()
29 | received_detail()
--------------------------------------------------------------------------------
/examples/schedule_example.py:
--------------------------------------------------------------------------------
1 | from . import jpush, app_key, master_secret
2 |
3 | _jpush = jpush.JPush(app_key, master_secret)
4 | _jpush.set_logging("DEBUG")
5 | schedule = _jpush.create_schedule()
6 |
7 | def delete_schedule():
8 | schedule.delete_schedule("e9c553d0-0850-11e6-b6d4-0021f652c102")
9 |
10 | def get_schedule():
11 | schedule.get_schedule_by_id("e9c553d0-0850-11e6-b6d4-0021f652c102")
12 |
13 | def get_schedule_list():
14 | schedule.get_schedule_list("1")
15 |
16 | def post_schedule():
17 | push = _jpush.create_push()
18 | push.audience = jpush.all_
19 | push.notification = jpush.notification(alert="Hello, world!")
20 | push.platform = jpush.all_
21 | push=push.payload
22 |
23 | trigger=jpush.schedulepayload.trigger("2016-07-17 12:00:00")
24 | schedulepayload=jpush.schedulepayload.schedulepayload("name",True,trigger,push)
25 | result=schedule.post_schedule(schedulepayload)
26 | print (result.status_code)
27 |
28 | def put_schedule():
29 | push = _jpush.create_push()
30 | push.audience = jpush.all_
31 | push.notification = jpush.notification(alert="Hello, world!")
32 | push.platform = jpush.all_
33 | push=push.payload
34 |
35 | trigger=jpush.schedulepayload.trigger("2016-05-17 12:00:00")
36 | schedulepayload=jpush.schedulepayload.schedulepayload("update a new name", True, trigger, push)
37 | schedule.put_schedule(schedulepayload,"17349f00-0852-11e6-91b1-0021f653c902")
38 |
--------------------------------------------------------------------------------
/examples/zone_example.py:
--------------------------------------------------------------------------------
1 | from . import jpush, app_key, master_secret
2 |
3 | def default():
4 | _jpush = jpush.JPush(app_key, master_secret)
5 | _jpush.set_logging("DEBUG")
6 |
7 | push = _jpush.create_push()
8 | push.audience = jpush.all_
9 | push.notification = jpush.notification(alert="!hello python jpush api")
10 | push.platform = jpush.all_
11 | try:
12 | response=push.send()
13 | except common.Unauthorized:
14 | raise common.Unauthorized("Unauthorized")
15 | except common.APIConnectionException:
16 | raise common.APIConnectionException("conn")
17 | except common.JPushFailure:
18 | print ("JPushFailure")
19 | except:
20 | print ("Exception")
21 |
22 | def bj():
23 | _jpush = jpush.JPush(app_key, master_secret, zone = 'bj')
24 | _jpush.set_logging("DEBUG")
25 |
26 | push = _jpush.create_push()
27 | push.audience = jpush.all_
28 | push.notification = jpush.notification(alert="!hello python jpush api")
29 | push.platform = jpush.all_
30 | try:
31 | response=push.send()
32 | except common.Unauthorized:
33 | raise common.Unauthorized("Unauthorized")
34 | except common.APIConnectionException:
35 | raise common.APIConnectionException("conn")
36 | except common.JPushFailure:
37 | print ("JPushFailure")
38 | except:
39 | print ("Exception")
40 |
--------------------------------------------------------------------------------
/jpush/__init__.py:
--------------------------------------------------------------------------------
1 | """Python package for using the JPush API"""
2 | from .core import JPush, GroupPush, Admin
3 | from .common import JPushFailure, Unauthorized
4 |
5 | from .push import (
6 | Push,
7 | all_,
8 | tag,
9 | tag_and,
10 | tag_not,
11 | alias,
12 | registration_id,
13 | notification,
14 | ios,
15 | android,
16 | hmos,
17 | winphone,
18 | platform,
19 | audience,
20 | options,
21 | message,
22 | smsmessage,
23 | )
24 |
25 | from .device import (
26 | Device,
27 | add,
28 | remove,
29 | device_tag,
30 | device_alias,
31 | device_regid,
32 | device_mobile,
33 | )
34 |
35 | from .report import (
36 | Report,
37 | ReportResponse,
38 | )
39 |
40 | from .schedule import (
41 | Schedule,
42 | schedulepayload,
43 | )
44 |
45 | __all__ = [
46 | JPush,
47 | GroupPush,
48 | Admin,
49 | JPushFailure,
50 | Unauthorized,
51 | all_,
52 | Push,
53 | tag,
54 | tag_and,
55 | tag_not,
56 | alias,
57 | registration_id,
58 | notification,
59 | ios,
60 | android,
61 | hmos,
62 | winphone,
63 | message,
64 | smsmessage,
65 | platform,
66 | audience,
67 | options,
68 | Device,
69 | add,
70 | remove,
71 | device_tag,
72 | device_alias,
73 | device_regid,
74 | Report,
75 | ReportResponse,
76 | Schedule,
77 | schedulepayload,
78 | ]
79 |
80 | __version__ = '3.3.9'
81 | VERSION = tuple(map(int, __version__.split('.')))
82 |
83 | # Silence urllib3 INFO logging by default
84 |
85 | import logging
86 | logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARNING)
87 |
--------------------------------------------------------------------------------
/jpush/common.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import requests
4 |
5 | ZONES = {
6 | 'DEFAULT': {
7 | 'PUSH': 'https://api.jpush.cn/v3/',
8 | 'REPORT': 'https://report.jpush.cn/v3/',
9 | 'DEVICE': 'https://device.jpush.cn/v3/devices/',
10 | 'ALIAS': 'https://device.jpush.cn/v3/aliases/',
11 | 'TAG': 'https://device.jpush.cn/v3/tags/',
12 | 'SCHEDULE': 'https://api.jpush.cn/v3/schedules/',
13 | 'ADMIN': 'https://admin.jpush.cn/v1/'
14 | },
15 | 'BJ':{
16 | 'PUSH': 'https://bjapi.push.jiguang.cn/v3/',
17 | 'REPORT': 'https://bjapi.push.jiguang.cn/v3/report/',
18 | 'DEVICE': 'https://bjapi.push.jiguang.cn/v3/device/',
19 | 'ALIAS': 'https://bjapi.push.jiguang.cn/v3/device/aliases/',
20 | 'TAG': 'https://bjapi.push.jiguang.cn/v3/device/tags/',
21 | 'SCHEDULE': 'https://bjapi.push.jiguang.cn/v3/push/schedules/',
22 | 'ADMIN': 'https://admin.jpush.cn/v1/'
23 | }
24 | }
25 |
26 | logger = logging.getLogger('jpush')
27 |
28 | def get_url(key, zone='default'):
29 | return ZONES[zone.upper()][key.upper()]
30 |
31 | class Unauthorized(Exception):
32 | """Raised when we get a 401 from the server"""
33 | def __init__(self, value):
34 | self.value = value
35 |
36 | def __str__(self):
37 | return repr(self.value)
38 |
39 |
40 | class JPushFailure(Exception):
41 | """Raised when we get an error response from the server.
42 | :param args: For backwards compatibility, ``*args`` includes the status and
43 | response body.
44 | """
45 |
46 | error = None
47 | error_code = None
48 | details = None
49 | response = None
50 |
51 | def __init__(self, error, error_code, details, response, *args):
52 | self.error = error
53 | self.error_code = error_code
54 | self.details = details
55 | self.response = response
56 | # super(self, JPushFailure).__init__(*args)
57 |
58 | @classmethod
59 | def from_response(cls, response):
60 | """Instantiate a ValidationFailure from a Response object"""
61 | try:
62 | payload = response.json()
63 | error = payload.get('error')
64 | error_code = error.get('code')
65 | details = error.get('message')
66 | except ValueError:
67 | error = response.reason
68 | error_code = None
69 | details = response.content
70 |
71 | logger.error(
72 | "Request failed with status %d: '%s %s': %s",
73 | response.status_code, error_code, error, json.dumps(details))
74 |
75 | return cls(error, error_code, details, response, response.status_code, response.content)
76 |
77 | def __str__(self):
78 | return repr(self.response.content)
79 |
80 |
81 | class APIConnectionException(Exception):
82 | def __init__(self, value):
83 | self.value = value
84 | # 修复celery的错误,参考https://github.com/celery/celery/issues/3623
85 | super(Exception, self).__init__(value)
86 |
87 | def __str__(self):
88 | return repr(self.value)
89 |
90 |
91 | class APIRequestException(Exception):
92 | def __init__(self, value):
93 | self.value = value
94 | super(Exception, self).__init__(value)
95 |
96 | def __str__(self):
97 | return repr(self.value)
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/jpush/core.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import warnings
4 |
5 | import requests
6 | from . import common
7 | from .push import Push
8 | from .device import Device
9 | from .report import Report
10 | from .schedule import Schedule
11 |
12 |
13 | logger = logging.getLogger('jpush')
14 |
15 |
16 | class JPush(object):
17 | def __init__(self, key, secret, timeout=30, zone = 'default'):
18 | self.key = key
19 | self.secret = secret
20 | self.timeout = timeout
21 | self.zone = zone
22 | self.session = requests.Session()
23 | self.session.auth = (key, secret)
24 |
25 | def _request(self, method, body, url, content_type=None, version=None, params=None):
26 | headers = {}
27 | headers['user-agent'] = 'jpush-api-python-client'
28 | headers['connection'] = 'keep-alive'
29 | headers['content-type'] = 'application/json;charset:utf-8'
30 |
31 | logger.debug("Making %s request to %s. Headers:\n\t%s\nBody:\n\t%s",
32 | method, url, '\n\t'.join('%s: %s' % (key, value) for (key, value) in headers.items()), body)
33 | try:
34 | response = self.session.request(method, url, data=body, params=params,
35 | headers=headers, timeout=self.timeout)
36 | except requests.exceptions.ConnectTimeout:
37 | raise common.APIConnectionException("Connection to api.jpush.cn timed out.")
38 | except Exception:
39 | raise common.APIRequestException("Connection to api.jpush.cn error.")
40 |
41 | logger.debug("Received %s response. Headers:\n\t%s\nBody:\n\t%s", response.status_code, '\n\t'.join(
42 | '%s: %s' % (key, value) for (key, value) in response.headers.items()), response.content)
43 |
44 | if response.status_code == 401:
45 | raise common.Unauthorized("Please check your AppKey and Master Secret")
46 | elif not (200 <= response.status_code < 300):
47 | raise common.JPushFailure.from_response(response)
48 | return response
49 |
50 | def push(self, payload):
51 | """Push this payload to the specified recipients.
52 |
53 | Payload: a dictionary the contents to send, e.g.:
54 | {'aps': {'alert': 'Hello'}, 'android': {'alert': 'Hello'}}
55 | """
56 | warnings.warn(
57 | "JPush.push() is deprecated. See documentation on upgrading.",
58 | DeprecationWarning)
59 | body = json.dumps(payload)
60 | url = common.get_url('push', self.zone) + 'push'
61 | self._request('POST', body, url, 'application/json', version=1)
62 |
63 | def set_logging(self, level):
64 | level_list= ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
65 | if level in level_list:
66 | if(level == "CRITICAL"):
67 | logging.basicConfig(level=logging.CRITICAL)
68 | if (level == "ERROR"):
69 | logging.basicConfig(level=logging.ERROR)
70 | if (level == "WARNING"):
71 | logging.basicConfig(level=logging.WARNING)
72 | if (level == "INFO"):
73 | logging.basicConfig(level=logging.INFO)
74 | if (level == "DEBUG"):
75 | logging.basicConfig(level=logging.DEBUG)
76 | if (level == "NOTSET"):
77 | logging.basicConfig(level=logging.NOTSET)
78 | else:
79 | print ("set logging level failed ,the level is invalid.")
80 |
81 | def create_push(self):
82 | """Create a Push notification."""
83 | return Push(self)
84 |
85 | def create_device(self):
86 | """Create a Device information."""
87 | return Device(self)
88 |
89 | def create_report(self):
90 | """Create a Report."""
91 | return Report(self)
92 |
93 | def create_schedule(self):
94 | """Create a Schedule."""
95 | return Schedule(self)
96 |
97 | class GroupPush(JPush):
98 |
99 | def __init__(self, key, secret):
100 | JPush.__init__(self, 'group-' + key, secret)
101 |
102 | def create_push(self):
103 | """Create a Group Push notification."""
104 | return Push(self, end_point = 'grouppush')
105 |
106 | class Admin(JPush):
107 | def __init__(self, key, secret):
108 | JPush.__init__(self, key, secret)
109 |
110 | def create_app(self, app_name, android_package, group_name=None):
111 | url = 'https://admin.jpush.cn/v1/app'
112 | entity = {
113 | 'app_name': app_name,
114 | 'android_package': android_package,
115 | 'group_name': group_name
116 | }
117 | body = json.dumps(entity)
118 | response = self._request('post', body, url, content_type=None, version=3)
119 | return AdminResponse(response)
120 |
121 | def delete_app(self, app_key):
122 | url = 'https://admin.jpush.cn/v1/app/' + app_key + '/delete'
123 | response = self._request('post', None, url, content_type=None, version=3)
124 | return AdminResponse(response)
125 |
126 | class AdminResponse(object):
127 |
128 | payload = None
129 | status_code = None
130 |
131 | def __init__(self, response):
132 | self.status_code = response.status_code
133 | if 0 != len(response.content):
134 | data = response.json()
135 | self.payload = data
136 | elif 200 == response.status_code:
137 | self.payload = "success"
138 |
139 | def get_status_code(self):
140 | return self.status_code
141 |
142 | def __str__(self):
143 | return "Admin response Payload: {0}".format(self.payload)
144 |
--------------------------------------------------------------------------------
/jpush/device/__init__.py:
--------------------------------------------------------------------------------
1 | from .core import Device
2 |
3 | from .entity import (
4 | add,
5 | remove,
6 | device_tag,
7 | device_alias,
8 | device_regid,
9 | device_mobile,
10 | )
11 |
12 | __all__ = [
13 | Device,
14 | add,
15 | remove,
16 | device_tag,
17 | device_alias,
18 | device_regid,
19 | device_mobile,
20 | ]
21 |
--------------------------------------------------------------------------------
/jpush/device/core.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from jpush import common
3 | import json
4 |
5 |
6 | class Device(object):
7 | """Device info query/update..
8 |
9 | """
10 | def __init__(self, jpush, zone = None):
11 | self._jpush = jpush
12 | self.entity = None
13 | self.zone = zone or jpush.zone
14 |
15 | def send(self, method, url, body = None, content_type=None, version=3, params = None):
16 | """Send the request
17 |
18 | """
19 | response = self._jpush._request(method, body, url, content_type, version=3, params = params)
20 | return DeviceResponse(response)
21 |
22 | def get_taglist(self):
23 | """Get deviceinfo with registration id.
24 | """
25 | url = common.get_url('tag', self.zone)
26 | info = self.send("GET", url)
27 | return info
28 |
29 | def get_deviceinfo(self, registration_id):
30 | """Get deviceinfo with registration id.
31 | """
32 | url = common.get_url('device', self.zone) + registration_id
33 | info = self.send("GET", url)
34 | return info
35 |
36 | def set_deviceinfo(self, registration_id, entity):
37 | """Update deviceinfo with registration id.
38 | """
39 | url = common.get_url('device', self.zone) + registration_id
40 | body = json.dumps(entity)
41 | info = self.send("POST", url, body)
42 | return info
43 |
44 | def set_devicemobile(self, registration_id, entity):
45 | """Update deviceinfo with registration id.
46 | """
47 | url = common.get_url('device', self.zone) + registration_id
48 | body = json.dumps(entity)
49 | info = self.send("POST", url, body)
50 | return info
51 |
52 | def delete_tag(self, tag, platform=None):
53 | """Delete registration id tag.
54 | """
55 | url = common.get_url('tag', self.zone) + tag
56 | params = { 'platform': platform } if platform else None
57 | info = self.send("DELETE", url, params = params)
58 | return info
59 |
60 | def update_tagusers(self, tag, entity):
61 | """Add/Remove specified tag users.
62 | """
63 | url = common.get_url('tag', self.zone) + tag
64 | body = json.dumps(entity)
65 | info = self.send("POST", url, body)
66 | return info
67 |
68 | def check_taguserexist(self, tag, registration_id):
69 | """Check registration id whether in tag.
70 | """
71 | url = common.get_url('tag', self.zone) + tag + "/registration_ids/" + registration_id
72 | info = self.send("GET", url)
73 | return info
74 |
75 | def delete_alias(self, alias, platform=None):
76 | """Delete appkey alias.
77 | """
78 | url = common.get_url('alias', self.zone) + alias
79 | params = { 'platform': platform } if platform else None
80 | info = self.send("DELETE", url, params = params)
81 | return info
82 |
83 | def get_aliasuser(self, alias, platform=None):
84 | """Get appkey alias users.
85 | """
86 | url = common.get_url('alias', self.zone) + alias
87 | params = { 'platform': platform } if platform else None
88 | info = self.send("GET", url, params = params)
89 | return info
90 |
91 | def get_device_status(self, reg_ids):
92 | """Get Online Status of User (VIP Exclusive Interface)
93 | """
94 | url = common.get_url('device', self.zone) + 'status'
95 |
96 | if isinstance(reg_ids, str):
97 | reg_ids = [ reg_ids ]
98 |
99 | entity = { 'registration_ids': reg_ids }
100 | body = json.dumps(entity)
101 | info = self.send("POST", url, body)
102 | return info
103 |
104 | class DeviceResponse(object):
105 | """Response to a successful device request send.
106 |
107 | Right now this is a fairly simple wrapper around the json payload response,
108 | but making it an object gives us some flexibility to add functionality
109 | later.
110 |
111 | """
112 | payload = None
113 | status_code = None
114 |
115 | def __init__(self, response):
116 | self.status_code = response.status_code
117 | if 0 != len(response.content):
118 | data = response.json()
119 | self.payload = data
120 | elif 200 == response.status_code:
121 | self.payload = "success"
122 |
123 | def get_status_code(self):
124 | return self.status_code
125 |
126 | def __str__(self):
127 | return "Device response Payload: {0}".format(self.payload)
128 |
--------------------------------------------------------------------------------
/jpush/device/entity.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf8 -*-
3 | import sys
4 |
5 | if 2 != sys.version_info[0]:
6 | unicode = str
7 |
8 | def add(*types):
9 | """Select a (list of) to be added objects(s)
10 |
11 | >>> add("registrationid1", "registrationid2")
12 | {'add': ['registrationid1', 'registrationid2']}
13 | >>> add("tag1", "tag2")
14 | {'add': ['tag1', 'tag2']}
15 | >>> add("alias1", "alias2")
16 | {'add': ['alias1', 'alias2']}
17 | """
18 | vadd = [v for v in types]
19 | return {"add": vadd}
20 |
21 | def remove(*types):
22 | """Select a (list of) to be removed objects(s)
23 |
24 | >>> remove("registrationid1", "registrationid2")
25 | {'remove': ['registrationid1', 'registrationid2']}
26 | >>> remove("tag1", "tag2")
27 | {'remove': ['tag1', 'tag2']}
28 | >>> remove("alias1", "alias2")
29 | {'remove': ['alias1', 'alias2']}
30 | """
31 | vremove = [v for v in types]
32 | return {"remove": vremove}
33 |
34 | def device_tag(*types):
35 | """Get a tag object
36 |
37 | >>> device_tag("")
38 | {'tags': ''}
39 | >>> device_tag("tag1")
40 | {'tags': 'tag1'}
41 | >>> device_tag(add("tag1", "tag2"), remove("tag3", "tag4"))
42 | {'tags': {'add': ['tag1', 'tag2'], 'remove': ['tag3', 'tag4']}}
43 | """
44 | tag = {}
45 | if 1 == len(types) and isinstance(types[0], (str, unicode)):
46 | tag["tags"] = types[0]
47 | return tag
48 | tag["tags"] = {}
49 | for t in types:
50 | for key in t:
51 | if key not in ('add', 'remove'):
52 | raise ValueError("Invalid tag '%s'" % t)
53 | tag["tags"][key] = t[key]
54 | return tag
55 |
56 |
57 | def device_mobile(device_mobile):
58 | mobile={}
59 | mobile["mobile"]=device_mobile
60 | return mobile
61 |
62 |
63 | def device_alias(*types):
64 | """Get an alias object
65 |
66 | >>> device_alias("")
67 | {'alias': ''}
68 | >>> device_alias("alias1")
69 | {'alias': 'alias1'}
70 | >>> device_alias(add("alias1", "alias2"), remove("alias3", "alias4"))
71 | {'alias': {'add': ['alias1', 'alias2'], 'remove': ['alias3', 'alias4']}}
72 | """
73 | alias = {}
74 | if 1 == len(types) and isinstance(types[0], (str, unicode)):
75 | alias["alias"] = types[0]
76 | return alias
77 | alias["alias"] = {}
78 | for t in types:
79 | for key in t:
80 | if key not in ('add', 'remove'):
81 | raise ValueError("Invalid alias '%s'" % t)
82 | alias["alias"][key] = t[key]
83 | return alias
84 |
85 |
86 | def device_regid(*types):
87 | """Get a registration_id object
88 |
89 | >>> device_regid("")
90 | {'registration_ids': ''}
91 | >>> device_regid("registration_id1")
92 | {'registration_ids': 'registration_id1'}
93 | >>> device_regid(add("registration_id1", "registration_id2"), remove("registration_id3", "registration_id4"))
94 | {'registration_ids': {'add': ['registration_id1', 'registration_id2'], 'remove': ['registration_id3', 'registration_id4']}}
95 | """
96 | registration_id = {}
97 | if 1 == len(types) and isinstance(types[0], (str, unicode)):
98 | registration_id["registration_ids"] = types[0]
99 | return registration_id
100 | registration_id["registration_ids"] = {}
101 | for t in types:
102 | for key in t:
103 | if key not in ('add', 'remove'):
104 | raise ValueError("Invalid registration_id '%s'" % t)
105 | registration_id["registration_ids"][key] = t[key]
106 | return registration_id
107 |
108 | if "__main__" == __name__:
109 | print (add("1", "2"))
110 | print (device_tag(add("a", "b"), remove('1', '2')))
111 |
--------------------------------------------------------------------------------
/jpush/push/__init__.py:
--------------------------------------------------------------------------------
1 | from .core import Push
2 |
3 | from .audience import (
4 | tag,
5 | tag_and,
6 | tag_not,
7 | alias,
8 | registration_id,
9 | segment,
10 | abtest
11 | )
12 |
13 | from .payload import (
14 | android,
15 | ios,
16 | winphone,
17 | hmos,
18 | platform,
19 | cid,
20 | notification,
21 | message,
22 | audience,
23 | options,
24 | smsmessage,
25 | )
26 |
27 | # Common selector for audience & platform
28 |
29 | all_ = "all"
30 | """Select all, to do a broadcast.
31 |
32 | Used in both ``audience`` and ``platform``.
33 | """
34 |
35 | __all__ = [
36 | all_,
37 | Push,
38 | tag,
39 | tag_and,
40 | tag_not,
41 | alias,
42 | registration_id,
43 | segment,
44 | abtest,
45 | notification,
46 | message,
47 | platform,
48 | cid,
49 | audience,
50 | options,
51 | smsmessage,
52 | ]
53 |
--------------------------------------------------------------------------------
/jpush/push/audience.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # Value selectors; aliases, tags, etc.
4 |
5 | def tag(*tags):
6 | """Select a (list of) tag(s)."""
7 | vtag = [t for t in tags]
8 | return {"tag": vtag}
9 |
10 | def tag_and(*tag_ands):
11 | """Select a (list of) tag_and(s)."""
12 | vtag_and = [t for t in tag_ands]
13 | return {"tag_and": vtag_and}
14 |
15 | def tag_not(*tag_nots):
16 | """Select a (list of) tag_not(s)."""
17 | vtag_not = [t for t in tag_nots]
18 | return {"tag_not": vtag_not}
19 |
20 | def alias(*alias):
21 | """Select a (list of) alias(es)."""
22 | valias = [t for t in alias]
23 | return {"alias": valias}
24 |
25 | def registration_id(*reg_ids):
26 | """Select a (list of) registration_id(s)."""
27 | vregistration_id = [t for t in reg_ids]
28 | return {"registration_id": vregistration_id}
29 |
30 | def segment(*segments):
31 | """Select a (list of) segment(s)."""
32 | vsegment = [t for t in segments]
33 | return {"segment": vsegment}
34 |
35 | def abtest(*abtests):
36 | """Select a (list of) abtest(s)."""
37 | vabtest = [t for t in abtests]
38 | return {"abtest": vabtest}
39 |
--------------------------------------------------------------------------------
/jpush/push/core.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | from jpush import common
4 |
5 | logger = logging.getLogger('jpush')
6 |
7 |
8 | class Push(object):
9 | """A push notification. Set audience, message, etc, and send."""
10 |
11 | def __init__(self, jpush, end_point = 'push', zone = None):
12 | self._jpush = jpush
13 | self.audience = None
14 | self.notification = None
15 | self.platform = None
16 | self.cid = None
17 | self.options = None
18 | self.message = None
19 | self.smsmessage=None
20 | self.end_point = end_point
21 | self.zone = zone or jpush.zone
22 |
23 | @property
24 | def payload(self):
25 | data = {
26 | "audience": self.audience,
27 | "platform": self.platform,
28 | }
29 | if (self.notification is None) and (self.message is None):
30 | raise ValueError("Notification and message cannot be both empty")
31 | if self.cid is not None:
32 | data['cid'] = self.cid
33 | if self.notification is not None:
34 | data['notification'] = self.notification
35 | if self.smsmessage is not None:
36 | data['sms_message'] = self.smsmessage
37 | if self.options is not None:
38 | data['options'] = self.options
39 | if self.message is not None:
40 | data['message'] = self.message
41 | return data
42 |
43 | def send(self):
44 | """Send the notification.
45 |
46 | :returns: :py:class:`PushResponse` object with ``push_ids`` and
47 | other response data.
48 | :raises JPushFailure: Request failed.
49 | :raises Unauthorized: Authentication failed.
50 |
51 | """
52 | body = json.dumps(self.payload)
53 | url = common.get_url('push', self.zone) + self.end_point
54 | response = self._jpush._request('POST', body, url, 'application/json', version=3)
55 | return PushResponse(response)
56 |
57 | def send_validate(self):
58 | """Send the notification to validate.
59 |
60 | :returns: :py:class:`PushResponse` object with ``push_ids`` and
61 | other response data.
62 | :raises JPushFailure: Request failed.
63 | :raises Unauthorized: Authentication failed.
64 |
65 | """
66 | body = json.dumps(self.payload)
67 | url = common.get_url('push', self.zone) + 'push/validate'
68 |
69 | response = self._jpush._request('POST', body, url, 'application/json', version=3)
70 | return PushResponse(response)
71 |
72 | def get_cid(self, count, type = None):
73 | body = None
74 | url = common.get_url('push', self.zone) + 'push/cid'
75 |
76 | params = {
77 | 'count': count,
78 | 'type': type
79 | }
80 | response = self._jpush._request('GET', body, url, 'application/json', version=3, params = params)
81 | return PushResponse(response)
82 |
83 | def batch_push_by_regid(self, single_payload_list):
84 | cid_response = self.get_cid(len(single_payload_list), 'push')
85 | cidlist = cid_response.payload['cidlist']
86 | batch_payload = {"pushlist":{}}
87 | for index in range(len(single_payload_list)):
88 | batch_payload["pushlist"][cidlist[index]] = single_payload_list[index]
89 | body = json.dumps(batch_payload)
90 | url = common.get_url('push', self.zone) + 'push/batch/regid/single'
91 | response = self._jpush._request('POST', body, url, 'application/json', version=3)
92 | return PushResponse(response)
93 |
94 | def batch_push_by_alias(self, single_payload_list):
95 | cid_response = self.get_cid(len(single_payload_list), 'push')
96 | cidlist = cid_response.payload['cidlist']
97 | batch_payload = {"pushlist":{}}
98 | for index in range(len(single_payload_list)):
99 | batch_payload["pushlist"][cidlist[index]] = single_payload_list[index]
100 | body = json.dumps(batch_payload)
101 | url = common.get_url('push', self.zone) + 'push/batch/alias/single'
102 | response = self._jpush._request('POST', body, url, 'application/json', version=3)
103 | return PushResponse(response)
104 |
105 |
106 | class PushResponse(object):
107 | """Response to a successful push notification send.
108 |
109 | Right now this is a fairly simple wrapper around the json payload response,
110 | but making it an object gives us some flexibility to add functionality
111 | later.
112 |
113 | """
114 | payload = None
115 | status_code = None
116 |
117 | def __init__(self, response):
118 | self.status_code = response.status_code
119 | data = response.json()
120 | self.payload = data
121 |
122 | def get_status_code(self):
123 | return self.status_code
124 |
125 | def __str__(self):
126 | return "Response Payload: {0}".format(self.payload)
127 |
--------------------------------------------------------------------------------
/jpush/push/payload.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 |
4 | # Valid autobadge values: auto, +N, -N
5 | VALID_AUTOBADGE = re.compile(r'^(auto|[+-][\d]+)$')
6 |
7 |
8 | PY2 = sys.version_info[0] == 2
9 |
10 | if not PY2:
11 | string_types = (str,)
12 | else:
13 | string_types = (str, unicode)
14 |
15 |
16 | def notification(alert=None, ios=None, android=None, winphone=None,hmos=None):
17 | """Create a notification payload.
18 |
19 | :keyword alert: A simple text alert, applicable for all platforms.
20 | :keyword ios: An iOS platform override, as generated by :py:func:`ios`.
21 | :keyword android: An Android platform override, as generated by :py:func:`android`.
22 | :keyword winphone: A MPNS platform override, as generated by :py:func:`mpns`.
23 | :keyword hmos: A hmos platform override, as generated by :py:func:`hmos`.
24 |
25 | """
26 | payload = {}
27 | if alert is not None:
28 | payload['alert'] = alert
29 | if ios is not None:
30 | payload['ios'] = ios
31 | if android is not None:
32 | payload['android'] = android
33 | if winphone is not None:
34 | payload['winphone'] = winphone
35 | if hmos is not None:
36 | payload['hmos'] = hmos
37 | if not payload:
38 | raise ValueError("Notification body may not be empty")
39 | return payload
40 |
41 |
42 | def ios(alert=None, badge='+1', sound=None, content_available=False,
43 | mutable_content=False, category=None, extras=None, sound_disable=False, thread_id=None):
44 | """iOS/APNS specific platform override payload.
45 |
46 | :keyword alert: iOS format alert, as either a string or dictionary.
47 | :keyword badge: An integer badge value or an *autobadge* string.
48 | :keyword sound: An string sound file to play.
49 | :keyword content_available: If True, pass on the content-available command
50 | for Newsstand iOS applications.
51 | :keyword extras: A set of key/value pairs to include in the push payload
52 | sent to the device.
53 | :keyword sound_disalbe: Disable sound to implement slient push.
54 | :keyword mutable_content: If True, enables modifying notification content in iOS service extension.
55 | :keyword category: String category for iOS notification action buttons.
56 | :keyword thread_id: String identifier to group related notifications in iOS.
57 |
58 | >>> ios(alert='Hello!', sound='cat.caf',
59 | ... extras={'articleid': '12345'})
60 | {'sound': 'cat.caf', 'extras': {'articleid': '12345'}, 'alert': 'Hello!'}
61 |
62 | """
63 | payload = {}
64 | if alert is not None:
65 | if not isinstance(alert, (string_types, dict)):
66 | raise ValueError("iOS alert must be a string or dictionary")
67 | payload['alert'] = alert
68 | if badge is not None:
69 | if not (isinstance(badge, str) or isinstance(badge, int)):
70 | raise ValueError("iOS badge must be an integer or string")
71 | if isinstance(badge, str) and not VALID_AUTOBADGE.match(badge):
72 | raise ValueError("Invalid iOS autobadge value")
73 | payload['badge'] = badge
74 | if not sound_disable:
75 | if sound is not None:
76 | payload['sound'] = sound
77 | else:
78 | payload['sound'] = 'default'
79 | if content_available:
80 | payload['content-available'] = 1
81 | if mutable_content:
82 | payload['mutable-content'] = 1
83 | if category:
84 | payload['category'] = category
85 | if thread_id:
86 | payload['thread-id'] = thread_id
87 | if extras is not None:
88 | payload['extras'] = extras
89 | return payload
90 |
91 | def android(alert, title=None, builder_id=None, extras=None,
92 | priority=None, category=None, style=None, alert_type=None,
93 | big_text=None, inbox=None, big_pic_path=None, large_icon=None, intent=None, channel_id=None):
94 | """Android specific platform override payload.
95 |
96 | :keyword alert: String alert text.If you set alert to a empty string,then the payload
97 | will not display on notification bar.
98 | more info:https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#notification
99 | :keyword alert: String alert text.
100 | :keyword title: String
101 | :keyword builder_id: Integer
102 | :keyword extras: A set of key/value pairs to include in the push payload
103 | sent to the device.
104 | """
105 | payload = {}
106 | if alert is not None:
107 | payload['alert'] = alert
108 | if title is not None:
109 | payload['title'] = title
110 | if builder_id is not None:
111 | payload['builder_id'] = builder_id
112 | if channel_id is not None:
113 | payload['channel_id'] = channel_id
114 | if priority is not None:
115 | payload['priority'] = priority
116 | if category is not None:
117 | payload['category'] = category
118 | if style is not None:
119 | payload['style'] = style
120 | if alert_type is not None:
121 | payload['alert_type'] = alert_type
122 | if big_text is not None:
123 | payload['big_text'] = big_text
124 | if inbox is not None:
125 | payload['inbox'] = inbox
126 | if big_pic_path is not None:
127 | payload['big_pic_path'] = big_pic_path
128 | if large_icon is not None:
129 | payload['large_icon'] = large_icon
130 | if intent is not None:
131 | payload['intent'] = intent
132 | if extras is not None:
133 | payload['extras'] = extras
134 | return payload
135 |
136 |
137 | def winphone(alert, title=None, _open_page=None, extras=None):
138 | """MPNS specific platform override payload.
139 |
140 | Must include exactly one of ``alert``, ``title``, ``_open_page``, or ``extras``.
141 |
142 | """
143 | if len(list(filter(None, (alert, _open_page, title)))) != 1:
144 | raise ValueError("MPNS payload must have one notification type.")
145 | payload = {}
146 | if alert is not None:
147 | payload['alert'] = alert
148 | if title is not None:
149 | payload['title'] = title
150 | if _open_page is not None:
151 | payload['_open_page'] = _open_page
152 | if extras is not None:
153 | payload['extras'] = extras
154 | return payload
155 |
156 | def hmos(alert, title=None, category=None, large_icon=None, intent=None, extras=None, style=None, inbox=None):
157 | """Hmos specific platform override payload.
158 | more info:https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push#hmos
159 |
160 | :keyword alert: String alert text.
161 | :keyword title: String
162 | :keyword category: String
163 | :keyword large_icon: String
164 | :keyword intent: A set of key/value pairs to include in the push payload
165 | :keyword extras: A set of key/value pairs to include in the push payload
166 | :keyword style: String
167 | :keyword inbox: String
168 | """
169 | payload = {}
170 | if alert is not None:
171 | payload['alert'] = alert
172 | if title is not None:
173 | payload['title'] = title
174 | if category is not None:
175 | payload['category'] = category
176 | if large_icon is not None:
177 | payload['large_icon'] = large_icon
178 | if intent is not None:
179 | payload['intent'] = intent
180 | if extras is not None:
181 | payload['extras'] = extras
182 | if style is not None:
183 | payload['style'] = style
184 | if inbox is not None:
185 | payload['inbox'] = inbox
186 | return payload
187 |
188 | def message(msg_content, title=None, content_type=None, extras=None):
189 | """Inner-conn push message payload creation.
190 |
191 | :param msg_content: Required, string
192 | :param title: Optional, string
193 | :keyword content_type: Optional, MIME type of the body
194 | :keyword extras: Optional, dictionary of string values.
195 |
196 | """
197 | payload = {
198 | 'msg_content': msg_content,
199 | }
200 | if title is not None:
201 | payload['title'] = title
202 | if content_type is not None:
203 | payload['content_type'] = content_type
204 | if extras is not None:
205 | payload['extras'] = extras
206 | return payload
207 |
208 | def smsmessage(delay_time, temp_id, temp_para = None, signid = None, active_filter = True):
209 | payload = {}
210 | payload["delay_time"]=delay_time
211 | payload["temp_id"]=temp_id
212 | if temp_para is not None:
213 | payload['temp_para'] = temp_para
214 | if signid is not None:
215 | payload['signid'] = signid
216 | if not active_filter:
217 | payload['active_filter'] = False
218 | return payload
219 |
220 |
221 | def cid(cid):
222 | payload = {}
223 | payload["cid"]=cid
224 | return payload
225 |
226 | def platform(*types):
227 | """Create a platform specifier.
228 |
229 | >>> platform('ios', 'winphone')
230 | ['ios', 'winphone']
231 | >>> platform('ios', 'symbian')
232 | Traceback (most recent call last):
233 | ...
234 | ValueError: Invalid platform 'symbian'
235 |
236 | """
237 | if len(types) == 1 and types[0] == 'all':
238 | return 'all'
239 | for t in types:
240 | if t not in ('ios', 'android', 'winphone', 'hmos'):
241 | raise ValueError("Invalid platform '%s'" % t)
242 | return [t for t in types]
243 |
244 | def options(options):
245 | """Create options object."""
246 | return {"options": options}
247 |
248 | def audience(*types):
249 | """Select audience that match all of the given selectors.
250 |
251 | >>> audience(tag('sports'), tag_and('business'))
252 | {'audience': {'tag':['sports'], 'tag_and':['business']}}
253 |
254 | """
255 | if 1 == len(types) and 'all' == types[0]:
256 | return "all"
257 | audience = {}
258 | for t in types:
259 | for key in t:
260 | if key not in ('tag', 'tag_and', 'tag_not', 'alias', 'registration_id', 'segment', 'abtest'):
261 | raise ValueError("Invalid audience '%s'" % t)
262 | audience[key] = t[key]
263 | return audience
264 |
--------------------------------------------------------------------------------
/jpush/report/__init__.py:
--------------------------------------------------------------------------------
1 | from .core import Report, ReportResponse
2 |
3 | __all__ = [
4 | Report,
5 | ReportResponse,
6 | ]
--------------------------------------------------------------------------------
/jpush/report/core.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from jpush import common
3 |
4 | logger = logging.getLogger('jpush')
5 |
6 |
7 | class Report(object):
8 | """JPush Report API V3"""
9 | def __init__(self, jpush, zone = None):
10 | self._jpush = jpush
11 | self.zone = zone or jpush.zone
12 |
13 | def send(self, method, url, body = None, content_type=None, version=3, params = None):
14 | """Send the request
15 | """
16 | response = self._jpush._request(method, body,url,content_type,version=3, params = params)
17 | return ReportResponse(response)
18 |
19 | def get_received(self,msg_ids):
20 | url = common.get_url('report', self.zone) + 'received'
21 | params = { 'msg_ids': msg_ids }
22 | received = self.send("GET", url, params = params)
23 | return received
24 |
25 | def get_received_detail(self, msg_ids):
26 | url = common.get_url('report', self.zone) + 'received/detail'
27 | params = {'msg_ids': msg_ids}
28 | response = self.send("GET", url, params = params)
29 | return response
30 |
31 | def get_status_message(self, msg_id, reg_ids, date=None):
32 | import json
33 | url = common.get_url('report', self.zone) + 'status/message'
34 | if not isinstance(reg_ids, list):
35 | reg_ids = [reg_ids]
36 | body = {
37 | 'msg_id': msg_id,
38 | 'registration_ids': reg_ids
39 | }
40 | if date is not None:
41 | body['date'] = date
42 | body = json.dumps(body)
43 | sm = self.send("POST", url, body = body)
44 | return sm
45 |
46 | def get_messages(self, msg_ids):
47 | url = common.get_url('report', self.zone) + 'messages'
48 | params = { 'msg_ids': msg_ids }
49 | messages = self.send("GET", url, params = params)
50 | return messages
51 |
52 | def get_messages_detail(self, msg_ids):
53 | url = common.get_url('report', self.zone) + 'messages/detail'
54 | params = {'msg_ids': msg_ids}
55 | response = self.send("GET", url, params = params)
56 | return response
57 |
58 | def get_users(self, time_unit,start,duration):
59 | url = common.get_url('report', self.zone) + 'users'
60 | params = {
61 | 'time_unit': time_unit,
62 | 'start': start,
63 | 'duration': duration
64 | }
65 | users = self.send("GET", url, params = params)
66 | return users
67 |
68 |
69 | class ReportResponse(object):
70 |
71 | payload = None
72 | status_code = None
73 |
74 | def __init__(self, response):
75 | self.status_code = response.status_code
76 | if 0 != len(response.content):
77 | data = response.json()
78 | self.payload = data
79 | elif 200 == response.status_code:
80 | self.payload = "success"
81 |
82 | def get_status_code(self):
83 | return self.status_code
84 |
85 | def __str__(self):
86 | return "Report response Payload: {0}".format(self.payload)
--------------------------------------------------------------------------------
/jpush/schedule/__init__.py:
--------------------------------------------------------------------------------
1 | from .core import Schedule
2 |
3 | __all__ = [
4 | Schedule,
5 | ]
--------------------------------------------------------------------------------
/jpush/schedule/core.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | from jpush import common
4 |
5 | logger = logging.getLogger('jpush')
6 |
7 |
8 | class Schedule(object):
9 | """JPush Report API V3"""
10 | def __init__(self, jpush, zone = None):
11 | self._jpush = jpush
12 | self.zone = zone or jpush.zone
13 |
14 | def send(self, method, url, body = None, content_type=None, version=3, params = None):
15 | response = self._jpush._request(method, body, url, content_type, version=3, params = params)
16 | return ScheduleResponse(response)
17 |
18 | def post_schedule(self, schedulepayload):
19 | url = common.get_url('schedule', self.zone)
20 | body = json.dumps(schedulepayload)
21 | result = self.send("POST", url, body)
22 | return result
23 |
24 | def get_schedule_by_id(self, schedule_id):
25 | url = common.get_url('schedule', self.zone) + schedule_id
26 | result = self.send("GET", url)
27 | return result
28 |
29 | def get_schedule_list(self, page = 1):
30 | url = common.get_url('schedule', self.zone)
31 | params = { 'page': page }
32 | result = self.send("GET", url, params = params)
33 | return result
34 |
35 | def put_schedule(self, schedulepayload, schedule_id):
36 | url = common.get_url('schedule', self.zone) + schedule_id
37 | body = json.dumps(schedulepayload)
38 | result = self.send("PUT", url, body)
39 | return result
40 |
41 | def delete_schedule(self,schedule_id):
42 | url = common.get_url('schedule', self.zone) + schedule_id
43 | result = self.send("DELETE", url)
44 | return result
45 |
46 | def get_msg_ids(self, schedule_id):
47 | url = common.BASE_SCHEDULEURL + schedule_id + '/msg_ids'
48 | body = None
49 | result = self.send("GET", url, body)
50 | return result
51 |
52 | class ScheduleResponse(object):
53 | """Response to a successful device request send.
54 |
55 | Right now this is a fairly simple wrapper around the json payload response,
56 | but making it an object gives us some flexibility to add functionality
57 | later.
58 |
59 | """
60 | payload = None
61 | status_code = None
62 |
63 | def __init__(self, response):
64 | self.status_code = response.status_code
65 | if 0 != len(response.content):
66 | data = response.json()
67 | self.payload = data
68 | elif 200 == response.status_code:
69 | self.payload = "success"
70 |
71 | def get_status_code(self):
72 | return self.status_code
73 |
74 | def __str__(self):
75 | return "Schedule response Payload: {0}".format(self.payload)
76 |
--------------------------------------------------------------------------------
/jpush/schedule/schedulepayload.py:
--------------------------------------------------------------------------------
1 | import re
2 | from jpush import push
3 |
4 | def schedulepayload(name=None, enabled=None, trigger=None, push=None):
5 | schedulepayload = {}
6 | if name is not None:
7 | schedulepayload['name'] = name
8 | if enabled is not None:
9 | schedulepayload['enabled'] = enabled
10 | if trigger is not None:
11 | schedulepayload['trigger'] = trigger
12 | if push is not None:
13 | schedulepayload['push'] = push
14 | if not schedulepayload:
15 | raise ValueError("schedule payload may not be empty")
16 | return schedulepayload
17 |
18 |
19 | def trigger(time, start=None, end=None,time_unit=None,frequency=None,point=None):
20 | if(start==None and end==None and time_unit==None and frequency==None):
21 | trigger={}
22 | single={}
23 | single["time"]=time
24 | trigger["single"]=single
25 | return trigger
26 | else:
27 | trigger={}
28 | periodical={}
29 | if time is not None:
30 | periodical['time'] = time
31 | if start is not None:
32 | periodical['start'] = start
33 | if end is not None:
34 | periodical['end'] = end
35 | if time_unit is not None:
36 | periodical['time_unit'] = time_unit
37 | if frequency is not None:
38 | periodical['frequency'] = frequency
39 | if point is not None:
40 | periodical['point'] = point
41 | trigger["periodical"]=periodical
42 | return trigger
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | universal = 1
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import re
3 | import ast
4 | try:
5 | from setuptools import setup
6 | except (ImportError):
7 | from distutils.core import setup
8 |
9 | _version_re = re.compile(r'__version__\s+=\s+(.*)')
10 |
11 | with open('jpush/__init__.py', 'rb') as f:
12 | version = str(ast.literal_eval(_version_re.search(
13 | f.read().decode('utf-8')).group(1)))
14 |
15 | setup(
16 | name='jpush',
17 | version=version,
18 | description='JPush\'s officially supported Python client library',
19 | keywords=('JPush', 'JPush API', 'Android Push', 'iOS Push', 'HMOS Push'),
20 | license='MIT License',
21 | long_description=open("README.rst", "r").read(),
22 | long_description_content_type="text/markdown",
23 | url='https://github.com/jpush/jpush-api-python-client',
24 | author='jpush',
25 | author_email='support@jpush.cn',
26 |
27 | packages=['jpush', 'jpush.push', 'jpush.device', 'jpush.report', 'jpush.schedule'],
28 | platforms='any',
29 | classifiers=[
30 | 'Environment :: Console',
31 | 'Intended Audience :: Developers',
32 | 'License :: OSI Approved :: MIT License',
33 | 'Operating System :: OS Independent',
34 | 'Programming Language :: Python',
35 | ],
36 |
37 | install_requires=[
38 | 'requests>=1.2',
39 | ],
40 | )
41 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpush/jpush-api-python-client/12fc3561219dff8ad021182a8da37f08f0ff9c49/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conf.py.example:
--------------------------------------------------------------------------------
1 | # please put your app_key and master_secret here
2 | app_key = u'xxxxxx'
3 | master_secret = u'xxxxxx'
4 |
--------------------------------------------------------------------------------
/tests/devices/test_devices.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from tests.conf import app_key, master_secret
3 | from jpush import device
4 | from jpush import common
5 | import jpush as jpush
6 |
7 |
8 | _jpush = jpush.JPush(app_key, master_secret)
9 | device = _jpush.create_device()
10 | _jpush.set_logging("DEBUG")
11 |
12 |
13 | class TestEntity(unittest.TestCase):
14 | def test_create_device(self):
15 | reg_id = '1507bfd3f7c466c355c'
16 | entity = jpush.device_tag(jpush.add("ddd", "tageee"))
17 | result = device.set_devicemobile(reg_id, entity)
18 | self.assertEqual(result.status_code, 200)
19 |
20 | def test_aliasuser(self):
21 | alias = "alias1"
22 | platform = "android,ios,hmos"
23 | result = device.get_aliasuser(alias, platform)
24 | self.assertEqual(result.status_code, 200)
25 |
26 | def test_clear_tag(self):
27 | reg_id = '090c1f59f89'
28 | entity = jpush.device_tag("")
29 | try:
30 | device.set_deviceinfo(reg_id, entity)
31 | except common.JPushFailure:
32 | self.assertEqual(1, 1)
33 | except:
34 | self.assertEqual(1, 0)
35 |
36 | def test_get_device(self):
37 | reg_id = '090c1f59f89'
38 | try:
39 | device.get_deviceinfo(reg_id)
40 | except common.JPushFailure:
41 | self.assertEqual(1, 1)
42 | except:
43 | self.assertEqual(1, 0)
44 |
45 | def test_remove_alias(self):
46 | alias = "alias1"
47 | platform = "android,ios,hmos"
48 | result = device.delete_alias(alias, platform)
49 | self.assertEqual(result.status_code, 200)
50 |
51 | def test_remove_tags(self):
52 | tag = "ddd"
53 | platform = "android,ios,hmos"
54 | result = device.delete_tag(tag, platform)
55 | self.assertEqual(result.status_code, 200)
56 |
57 | def test_tag_list(self):
58 | result = device.get_taglist()
59 | self.assertEqual(result.status_code, 200)
60 |
61 | def test_set_device_mobile(self):
62 | reg_id = '1507bfd3f7c466c355c'
63 | entity = jpush.device_tag(jpush.add("ddd", "tageee"))
64 | result = device.set_devicemobile(reg_id, entity)
65 | self.assertEqual(result.status_code, 200)
66 |
67 | def test_device_mobile(self):
68 | reg_id = '1507bfd3f7c466c355c'
69 | entity = jpush.device_mobile("18588232140")
70 | result = device.set_devicemobile(reg_id, entity)
71 | self.assertEqual(result.status_code, 200)
--------------------------------------------------------------------------------
/tests/devices/test_entity.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import jpush
3 |
4 |
5 | class TestEntity(unittest.TestCase):
6 |
7 | def test_basic_entity(self):
8 | entities = (
9 | (jpush.add, 'tag1', {'add': ['tag1']}),
10 | (jpush.remove, 'tag1', {'remove': ['tag1']}),
11 | (jpush.device_tag, 'tag1', {'tags': 'tag1'}),
12 | (jpush.device_alias, 'alias1', {'alias': 'alias1'}),
13 | (jpush.device_regid, 'registration_id1', {'registration_ids': 'registration_id1'}),
14 | )
15 | for entity, value, result in entities:
16 | self.assertEqual(entity(value), result)
17 |
18 | def test_compound_entity(self):
19 | self.assertEqual(
20 | jpush.device_tag(jpush.add("tag1", "tag2")),
21 | {'tags':{'add':['tag1', 'tag2']}})
22 |
23 | self.assertEqual(
24 | jpush.device_tag(jpush.remove("tag1", "tag2")),
25 | {'tags':{'remove':['tag1', 'tag2']}})
26 |
27 | self.assertEqual(
28 | jpush.device_alias(jpush.add("alias1", "alias2"), jpush.remove("alias3", "alias4")),
29 | {'alias':{'add':['alias1', 'alias2'], 'remove':['alias3', 'alias4']}})
30 |
31 | self.assertEqual(
32 | jpush.device_regid(jpush.add("regid1", "regid2"), jpush.remove("regid3", "regid4")),
33 | {'registration_ids':{'add':['regid1', 'regid2'], 'remove':['regid3', 'regid4']}})
34 |
--------------------------------------------------------------------------------
/tests/push/test_audience.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import jpush as jpush
3 | from tests.conf import app_key, master_secret
4 | from jpush import common
5 |
6 |
7 | class TestAudience(unittest.TestCase):
8 |
9 | def test_basic_selectors(self):
10 | selectors = (
11 | (jpush.tag, 'tag1', {'tag': ['tag1']}),
12 | (jpush.tag_and, 'tag1', {'tag_and': ['tag1']}),
13 | (jpush.alias, 'alias1', {'alias':['alias1']}),
14 | (jpush.registration_id, 'regid1', {'registration_id':['regid1']}),
15 | )
16 | for selector, value, result in selectors:
17 | self.assertEqual(selector(value), result)
18 |
19 | def test_audience(self):
20 | _jpush = jpush.JPush(app_key, master_secret)
21 |
22 | push = _jpush.create_push()
23 | push.audience = jpush.audience(
24 | jpush.tag("tag1", "tag2"),
25 | jpush.alias("alias1", "alias2")
26 | )
27 | push.notification = jpush.notification(alert="Hello world with audience!")
28 | push.platform = jpush.all_
29 | try:
30 | response = push.send()
31 |
32 | self.assertEqual(response.status_code, 200)
33 | except common.Unauthorized as e:
34 | self.assertFalse(isinstance(e, common.Unauthorized))
35 | raise common.Unauthorized("Unauthorized")
36 | except common.APIConnectionException as e:
37 | self.assertFalse(isinstance(e, common.APIConnectionException))
38 | raise common.APIConnectionException("conn")
39 | except common.JPushFailure as e:
40 | self.assertFalse(isinstance(e, common.JPushFailure))
41 | print ("JPushFailure")
42 | except:
43 | self.assertFalse(1)
44 | print ("Exception")
45 |
--------------------------------------------------------------------------------
/tests/push/test_message.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import unittest
3 | import jpush as jpush
4 | from tests.conf import app_key, master_secret
5 | from jpush import common
6 |
7 |
8 | class TestMessage(unittest.TestCase):
9 |
10 | def test_simple_alert(self):
11 | self.assertEqual(jpush.notification(alert='中文'), {'alert':'中文'})
12 |
13 | def test_ios(self):
14 | self.assertEqual(
15 | jpush.notification(ios=jpush.ios(alert="Hello", badge="+1", sound="a.caf", extras={'k1':'v1'})),
16 | {'ios': {'sound': 'a.caf', 'extras': {'k1': 'v1'}, 'badge': '+1', 'alert': 'Hello'}}
17 | )
18 |
19 | def test_iossilent(self):
20 | self.assertEqual(
21 | jpush.notification(ios=jpush.ios(alert="Hello", badge="+1", extras={'k1':'v1'}, sound_disable=True)),
22 | {'ios': {'extras': {'k1': 'v1'}, 'badge': '+1', 'alert': 'Hello'}}
23 | )
24 |
25 | def test_android(self):
26 | self.assertEqual(
27 | jpush.notification(android=jpush.android(alert="Hello", extras={'k2':'v2'})),
28 | {'android': {'extras': {'k2': 'v2'}, 'alert': 'Hello'}}
29 | )
30 |
31 | def test_winphone(self):
32 | self.assertEqual(
33 | jpush.notification(winphone=jpush.winphone(alert="Hello", extras={'k3':'v3'})),
34 | {'winphone': {'extras': {'k3': 'v3'}, 'alert': 'Hello'}}
35 | )
36 |
37 | def test_hmos(self):
38 | self.assertEqual(
39 | jpush.notification(hmos=jpush.hmos(alert="Hello", title="Title", extras={'k4':'v4'})),
40 | {'hmos': {'extras': {'k4': 'v4'}, 'alert': 'Hello', 'title': 'Title'}}
41 | )
42 |
43 | def test_push(self):
44 | _jpush = jpush.JPush(app_key, master_secret)
45 | push = _jpush.create_push()
46 | push.audience = jpush.all_
47 | push.notification = jpush.notification(alert="hello python jpush api")
48 | push.platform = jpush.all_
49 | try:
50 | response = push.send()
51 | self.assertEqual(response.status_code, 200)
52 | except common.Unauthorized as e:
53 | self.assertFalse(isinstance(e, common.Unauthorized))
54 | raise common.Unauthorized("Unauthorized")
55 | except common.APIConnectionException as e:
56 | self.assertFalse(isinstance(e, common.APIConnectionException))
57 | raise common.APIConnectionException("conn")
58 | except common.JPushFailure as e:
59 | self.assertFalse(isinstance(e, common.JPushFailure))
60 | print ("JPushFailure")
61 | except:
62 | self.assertFalse(1)
63 | print ("Exception")
64 |
--------------------------------------------------------------------------------
/tests/report/test_report.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import jpush as jpush
3 | from tests.conf import app_key, master_secret
4 | from jpush import common
5 |
6 | _jpush = jpush.JPush(app_key, master_secret)
7 | schedule = _jpush.create_schedule()
8 | _jpush.set_logging("DEBUG")
9 |
10 | report=_jpush.create_report();
11 |
12 |
13 | class TestEntity(unittest.TestCase):
14 | def test_messages(self):
15 | result = report.get_messages("3289406737")
16 | self.assertEqual(result.status_code, 200)
17 |
18 | def test_get_schedule_by_id(self):
19 | result = report.get_received("3289406737")
20 | self.assertEqual(result.status_code, 200)
21 |
22 | def test_get_schedule_by_invalid_id(self):
23 | try:
24 | result = report.get_users("DAY","2016-04-10","3")
25 | self.assertEqual(result.status_code, 200)
26 | except common.JPushFailure as e:
27 | self.assertIsInstance(e, common.JPushFailure)
28 |
29 |
--------------------------------------------------------------------------------
/tests/schedule/test_schedule.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from jpush import schedule
3 | import jpush as jpush
4 | from jpush import common
5 | from tests.conf import app_key, master_secret
6 | import datetime
7 |
8 | _jpush = jpush.JPush(app_key, master_secret)
9 | schedule = _jpush.create_schedule()
10 | # _jpush.set_logging("DEBUG")
11 |
12 | push = _jpush.create_push()
13 | push.audience = jpush.all_
14 | push.notification = jpush.notification(alert="Hello, world!")
15 | push.platform = jpush.all_
16 | push=push.payload
17 |
18 | now = datetime.datetime.now()
19 | start_time = (now + datetime.timedelta(days=10)).strftime("%Y-%m-%d %H:%M:%S")
20 | end_time = (now +datetime.timedelta(days=20)).strftime("%Y-%m-%d %H:%M:%S")
21 |
22 | class TestEntity(unittest.TestCase):
23 |
24 | def test_post_schedule(self):
25 | trigger = jpush.schedulepayload.trigger(start_time)
26 | schedulepayload = jpush.schedulepayload.schedulepayload("name", True, trigger, push)
27 | result = schedule.post_schedule(schedulepayload)
28 | self.assertEqual(result.status_code, 200)
29 |
30 | def test_get_schedule_by_id(self):
31 | schedule_id = schedule.get_schedule_list("1").payload['schedules'][0]['schedule_id']
32 | result = schedule.get_schedule_by_id(schedule_id)
33 | self.assertEqual(result.status_code, 200)
34 |
35 | def test_get_schedule_list(self):
36 | result = schedule.get_schedule_list("1")
37 | self.assertEqual(result.status_code, 200)
38 |
39 | # def test_put_schedule(self):
40 | # task = schedule.get_schedule_list("1").payload['schedules'][0]
41 | # schedule_id = task['schedule_id']
42 |
43 | # trigger = jpush.schedulepayload.trigger(task['trigger']['single']['time'])
44 | # schedulepayload = jpush.schedulepayload.schedulepayload("update_a_new_name", True, trigger, push)
45 | # result = schedule.put_schedule(schedulepayload, schedule_id)
46 | # self.assertEqual(result.status_code, 200)
47 |
--------------------------------------------------------------------------------