diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
new file mode 100644
index 0000000..a8091e8
--- /dev/null
+++ b/.github/workflows/python-publish.yml
@@ -0,0 +1,37 @@
+# This workflow will upload a Python Package using Twine when a release is created
+# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Upload Python Package
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ deploy:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.x'
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install build
+ pip install wheel
+ - name: Build package
+ run: python -m build
+ - name: Publish package
+ uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
+ with:
+ user: __token__
+ password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/.gitignore b/.gitignore
index b03798e..3de25b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ dist/
jpush.egg-info/
.idea/
examples/conf.py
+/tests/conf.py
diff --git a/docs/push/push.md b/docs/push/push.md
index 0c39d5b..17bd5cb 100644
--- a/docs/push/push.md
+++ b/docs/push/push.md
@@ -171,8 +171,7 @@ notification(alert=None, ios=None, android=None, winphone=None)
##### ios payload
```
-ios(alert=None, badge=None, sound=None, content_available=False,
- extras=None, sound_disable=False)
+ios(alert=None, badge='+1', sound=None, content_available=False, mutable_content=False, category=None, extras=None, sound_disable=False, thread_id=None):
```
参数说明
@@ -190,14 +189,19 @@ ios(alert=None, badge=None, sound=None, content_available=False,
extras |
JSON Object |
@@ -232,11 +250,11 @@ ios(alert=None, badge=None, sound=None, content_available=False,
返回值
-> ios payload 字典
+> android payload 字典
##### android payload
```
-android(alert, title=None, builder_id=None, extras=None)
+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)
```
参数说明
diff --git a/examples/batch_push_example.py b/examples/batch_push_example.py
new file mode 100644
index 0000000..31d4d98
--- /dev/null
+++ b/examples/batch_push_example.py
@@ -0,0 +1,13 @@
+from conf import app_key, master_secret
+import jpush
+
+_jpush = jpush.JPush(app_key, master_secret)
+_jpush.set_logging("DEBUG")
+
+push = _jpush.create_push()
+single_payload_list = [
+ {'platform':'all', 'target':'regid1', 'notification':{'alert':'alert content'}},
+ {'platform':'all', 'target':'regid2', 'notification':{'alert':'alert content'}}
+]
+response = push.batch_push_by_regid(single_payload_list)
+print(response)
\ No newline at end of file
diff --git a/examples/device_example.py b/examples/device_example.py
index 8c51fd5..344a8e5 100644
--- a/examples/device_example.py
+++ b/examples/device_example.py
@@ -6,7 +6,7 @@
def alias_user():
alias = "alias1"
- platform = "android,ios"
+ platform = "android,ios,hmos"
device.get_aliasuser(alias, platform)
def ctrl_tag():
@@ -20,12 +20,12 @@ def get_device():
def delete_alias():
alias = "alias1"
- platform = "android,ios"
+ platform = "android,ios,hmos"
device.delete_alias(alias, platform)
def delete_tag():
tag = "ddd"
- platform = "android,ios"
+ platform = "android,ios,hmos"
device.delete_tag(tag, platform)
def check_tag():
diff --git a/examples/push_example.py b/examples/push_example.py
index 0e5f03d..c854fbf 100644
--- a/examples/push_example.py
+++ b/examples/push_example.py
@@ -1,4 +1,5 @@
-from . import jpush, app_key, master_secret
+from conf import app_key, master_secret
+import jpush
_jpush = jpush.JPush(app_key, master_secret)
_jpush.set_logging("DEBUG")
@@ -57,8 +58,9 @@ def notification():
ios = jpush.ios(alert="Hello, IOS JPush!", sound="a.caf", extras={'k1':'v1'})
android = jpush.android(alert="Hello, Android msg", priority=1, style=1, alert_type=1,big_text='jjjjjjjjjj', extras={'k1':'v1'})
+ 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")
- push.notification = jpush.notification(alert="Hello, JPush!", android=android, ios=ios)
+ push.notification = jpush.notification(alert="Hello, JPush!", android=android, ios=ios, hmos=hmos)
# pprint (push.payload)
result = push.send()
@@ -76,7 +78,8 @@ def platfrom_msg():
push.audience = jpush.all_
ios_msg = jpush.ios(alert="Hello, IOS JPush!", badge="+1", sound="a.caf", extras={'k1':'v1'})
android_msg = jpush.android(alert="Hello, android msg")
- push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg)
+ hmos_msg = jpush.hmos(alert="Hello, HMOS JPush msg")
+ push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg, hmos=hmos_msg)
push.message=jpush.message("content",extras={'k2':'v2','k3':'v3'})
push.platform = jpush.all_
push.send()
@@ -87,7 +90,8 @@ def silent():
push.audience = jpush.all_
ios_msg = jpush.ios(alert="Hello, IOS JPush!", badge="+1", extras={'k1':'v1'}, sound_disable=True)
android_msg = jpush.android(alert="Hello, android msg")
- push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg)
+ hmos_msg = jpush.hmos(alert="Hello, HMOS JPush msg")
+ push.notification = jpush.notification(alert="Hello, JPush!", android=android_msg, ios=ios_msg, hmos=hmos_msg)
push.platform = jpush.all_
push.send()
@@ -106,4 +110,4 @@ def validate():
push.audience = jpush.all_
push.notification = jpush.notification(alert="Hello, world!")
push.platform = jpush.all_
- push.send_validate()
+ push.send_validate()
\ No newline at end of file
diff --git a/examples/report_example.py b/examples/report_example.py
index 9b89576..89a5b7e 100644
--- a/examples/report_example.py
+++ b/examples/report_example.py
@@ -1,4 +1,5 @@
-from . import jpush, app_key, master_secret
+from conf import app_key, master_secret
+import jpush
_jpush = jpush.JPush(app_key, master_secret)
_jpush.set_logging("DEBUG")
@@ -7,8 +8,22 @@
def messages():
report.get_messages("3289406737")
-def receivede():
+def messages_detail():
+ report.get_messages_detail("3289406737")
+
+def received():
report.get_received("3289406737")
+def received_detail():
+ report.get_received_detail("3289406737")
+
def users():
report.get_users("DAY","2016-04-10","3")
+
+def status():
+ msgid = '3289406737'
+ regid = 'xxx'
+ report.get_status_message(int(msgid), [regid])
+
+messages_detail()
+received_detail()
\ No newline at end of file
diff --git a/jpush/__init__.py b/jpush/__init__.py
index 129196b..902ab79 100644
--- a/jpush/__init__.py
+++ b/jpush/__init__.py
@@ -13,6 +13,7 @@
notification,
ios,
android,
+ hmos,
winphone,
platform,
audience,
@@ -57,6 +58,7 @@
notification,
ios,
android,
+ hmos,
winphone,
message,
smsmessage,
@@ -75,7 +77,7 @@
schedulepayload,
]
-__version__ = '3.3.0'
+__version__ = '3.3.9'
VERSION = tuple(map(int, __version__.split('.')))
# Silence urllib3 INFO logging by default
diff --git a/jpush/common.py b/jpush/common.py
index a638ee3..0cc6bb9 100644
--- a/jpush/common.py
+++ b/jpush/common.py
@@ -81,6 +81,8 @@ def __str__(self):
class APIConnectionException(Exception):
def __init__(self, value):
self.value = value
+ # 修复celery的错误,参考https://github.com/celery/celery/issues/3623
+ super(Exception, self).__init__(value)
def __str__(self):
return repr(self.value)
@@ -89,6 +91,7 @@ def __str__(self):
class APIRequestException(Exception):
def __init__(self, value):
self.value = value
+ super(Exception, self).__init__(value)
def __str__(self):
return repr(self.value)
diff --git a/jpush/core.py b/jpush/core.py
index f9ce09f..ea667f7 100644
--- a/jpush/core.py
+++ b/jpush/core.py
@@ -36,7 +36,7 @@ def _request(self, method, body, url, content_type=None, version=None, params=No
except requests.exceptions.ConnectTimeout:
raise common.APIConnectionException("Connection to api.jpush.cn timed out.")
except Exception:
- raise common.APIConnectionException("Connection to api.jpush.cn error.")
+ raise common.APIRequestException("Connection to api.jpush.cn error.")
logger.debug("Received %s response. Headers:\n\t%s\nBody:\n\t%s", response.status_code, '\n\t'.join(
'%s: %s' % (key, value) for (key, value) in response.headers.items()), response.content)
@@ -60,7 +60,7 @@ def push(self, payload):
url = common.get_url('push', self.zone) + 'push'
self._request('POST', body, url, 'application/json', version=1)
- def set_logging(self, level):
+ def set_logging(self, level):
level_list= ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
if level in level_list:
if(level == "CRITICAL"):
diff --git a/jpush/push/__init__.py b/jpush/push/__init__.py
index 6720ea6..efadd49 100644
--- a/jpush/push/__init__.py
+++ b/jpush/push/__init__.py
@@ -14,6 +14,7 @@
android,
ios,
winphone,
+ hmos,
platform,
cid,
notification,
diff --git a/jpush/push/core.py b/jpush/push/core.py
index 9976d9a..bcd12ea 100644
--- a/jpush/push/core.py
+++ b/jpush/push/core.py
@@ -80,6 +80,28 @@ def get_cid(self, count, type = None):
response = self._jpush._request('GET', body, url, 'application/json', version=3, params = params)
return PushResponse(response)
+ def batch_push_by_regid(self, single_payload_list):
+ cid_response = self.get_cid(len(single_payload_list), 'push')
+ cidlist = cid_response.payload['cidlist']
+ batch_payload = {"pushlist":{}}
+ for index in range(len(single_payload_list)):
+ batch_payload["pushlist"][cidlist[index]] = single_payload_list[index]
+ body = json.dumps(batch_payload)
+ url = common.get_url('push', self.zone) + 'push/batch/regid/single'
+ response = self._jpush._request('POST', body, url, 'application/json', version=3)
+ return PushResponse(response)
+
+ def batch_push_by_alias(self, single_payload_list):
+ cid_response = self.get_cid(len(single_payload_list), 'push')
+ cidlist = cid_response.payload['cidlist']
+ batch_payload = {"pushlist":{}}
+ for index in range(len(single_payload_list)):
+ batch_payload["pushlist"][cidlist[index]] = single_payload_list[index]
+ body = json.dumps(batch_payload)
+ url = common.get_url('push', self.zone) + 'push/batch/alias/single'
+ response = self._jpush._request('POST', body, url, 'application/json', version=3)
+ return PushResponse(response)
+
class PushResponse(object):
"""Response to a successful push notification send.
diff --git a/jpush/push/payload.py b/jpush/push/payload.py
index 0ed45fc..b816ff9 100644
--- a/jpush/push/payload.py
+++ b/jpush/push/payload.py
@@ -13,13 +13,14 @@
string_types = (str, unicode)
-def notification(alert=None, ios=None, android=None, winphone=None):
+def notification(alert=None, ios=None, android=None, winphone=None,hmos=None):
"""Create a notification payload.
:keyword alert: A simple text alert, applicable for all platforms.
:keyword ios: An iOS platform override, as generated by :py:func:`ios`.
:keyword android: An Android platform override, as generated by :py:func:`android`.
:keyword winphone: A MPNS platform override, as generated by :py:func:`mpns`.
+ :keyword hmos: A hmos platform override, as generated by :py:func:`hmos`.
"""
payload = {}
@@ -31,13 +32,15 @@ def notification(alert=None, ios=None, android=None, winphone=None):
payload['android'] = android
if winphone is not None:
payload['winphone'] = winphone
+ if hmos is not None:
+ payload['hmos'] = hmos
if not payload:
raise ValueError("Notification body may not be empty")
return payload
def ios(alert=None, badge='+1', sound=None, content_available=False,
- mutable_content=False, category=None, extras=None, sound_disable=False):
+ mutable_content=False, category=None, extras=None, sound_disable=False, thread_id=None):
"""iOS/APNS specific platform override payload.
:keyword alert: iOS format alert, as either a string or dictionary.
@@ -45,13 +48,16 @@ def ios(alert=None, badge='+1', sound=None, content_available=False,
:keyword sound: An string sound file to play.
:keyword content_available: If True, pass on the content-available command
for Newsstand iOS applications.
- :keyword extra: A set of key/value pairs to include in the push payload
+ :keyword extras: A set of key/value pairs to include in the push payload
sent to the device.
:keyword sound_disalbe: Disable sound to implement slient push.
+ :keyword mutable_content: If True, enables modifying notification content in iOS service extension.
+ :keyword category: String category for iOS notification action buttons.
+ :keyword thread_id: String identifier to group related notifications in iOS.
>>> ios(alert='Hello!', sound='cat.caf',
- ... extra={'articleid': '12345'})
- {'sound': 'cat.caf', 'extra': {'articleid': '12345'}, 'alert': 'Hello!'}
+ ... extras={'articleid': '12345'})
+ {'sound': 'cat.caf', 'extras': {'articleid': '12345'}, 'alert': 'Hello!'}
"""
payload = {}
@@ -76,12 +82,15 @@ def ios(alert=None, badge='+1', sound=None, content_available=False,
payload['mutable-content'] = 1
if category:
payload['category'] = category
+ if thread_id:
+ payload['thread-id'] = thread_id
if extras is not None:
payload['extras'] = extras
return payload
def 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):
+ priority=None, category=None, style=None, alert_type=None,
+ big_text=None, inbox=None, big_pic_path=None, large_icon=None, intent=None, channel_id=None):
"""Android specific platform override payload.
:keyword alert: String alert text.If you set alert to a empty string,then the payload
@@ -100,6 +109,8 @@ def android(alert, title=None, builder_id=None, extras=None,
payload['title'] = title
if builder_id is not None:
payload['builder_id'] = builder_id
+ if channel_id is not None:
+ payload['channel_id'] = channel_id
if priority is not None:
payload['priority'] = priority
if category is not None:
@@ -114,6 +125,10 @@ def android(alert, title=None, builder_id=None, extras=None,
payload['inbox'] = inbox
if big_pic_path is not None:
payload['big_pic_path'] = big_pic_path
+ if large_icon is not None:
+ payload['large_icon'] = large_icon
+ if intent is not None:
+ payload['intent'] = intent
if extras is not None:
payload['extras'] = extras
return payload
@@ -138,6 +153,38 @@ def winphone(alert, title=None, _open_page=None, extras=None):
payload['extras'] = extras
return payload
+def hmos(alert, title=None, category=None, large_icon=None, intent=None, extras=None, style=None, inbox=None):
+ """Hmos specific platform override payload.
+ more info:https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push#hmos
+
+ :keyword alert: String alert text.
+ :keyword title: String
+ :keyword category: String
+ :keyword large_icon: String
+ :keyword intent: A set of key/value pairs to include in the push payload
+ :keyword extras: A set of key/value pairs to include in the push payload
+ :keyword style: String
+ :keyword inbox: String
+ """
+ payload = {}
+ if alert is not None:
+ payload['alert'] = alert
+ if title is not None:
+ payload['title'] = title
+ if category is not None:
+ payload['category'] = category
+ if large_icon is not None:
+ payload['large_icon'] = large_icon
+ if intent is not None:
+ payload['intent'] = intent
+ if extras is not None:
+ payload['extras'] = extras
+ if style is not None:
+ payload['style'] = style
+ if inbox is not None:
+ payload['inbox'] = inbox
+ return payload
+
def message(msg_content, title=None, content_type=None, extras=None):
"""Inner-conn push message payload creation.
@@ -158,12 +205,16 @@ def message(msg_content, title=None, content_type=None, extras=None):
payload['extras'] = extras
return payload
-def smsmessage(delay_time, temp_id, temp_para = None):
+def smsmessage(delay_time, temp_id, temp_para = None, signid = None, active_filter = True):
payload = {}
payload["delay_time"]=delay_time
payload["temp_id"]=temp_id
if temp_para is not None:
payload['temp_para'] = temp_para
+ if signid is not None:
+ payload['signid'] = signid
+ if not active_filter:
+ payload['active_filter'] = False
return payload
@@ -186,7 +237,7 @@ def platform(*types):
if len(types) == 1 and types[0] == 'all':
return 'all'
for t in types:
- if t not in ('ios', 'android', 'winphone'):
+ if t not in ('ios', 'android', 'winphone', 'hmos'):
raise ValueError("Invalid platform '%s'" % t)
return [t for t in types]
diff --git a/jpush/report/core.py b/jpush/report/core.py
index a1085b9..83d6855 100644
--- a/jpush/report/core.py
+++ b/jpush/report/core.py
@@ -22,12 +22,39 @@ def get_received(self,msg_ids):
received = self.send("GET", url, params = params)
return received
+ def get_received_detail(self, msg_ids):
+ url = common.get_url('report', self.zone) + 'received/detail'
+ params = {'msg_ids': msg_ids}
+ response = self.send("GET", url, params = params)
+ return response
+
+ def get_status_message(self, msg_id, reg_ids, date=None):
+ import json
+ url = common.get_url('report', self.zone) + 'status/message'
+ if not isinstance(reg_ids, list):
+ reg_ids = [reg_ids]
+ body = {
+ 'msg_id': msg_id,
+ 'registration_ids': reg_ids
+ }
+ if date is not None:
+ body['date'] = date
+ body = json.dumps(body)
+ sm = self.send("POST", url, body = body)
+ return sm
+
def get_messages(self, msg_ids):
url = common.get_url('report', self.zone) + 'messages'
params = { 'msg_ids': msg_ids }
messages = self.send("GET", url, params = params)
return messages
+ def get_messages_detail(self, msg_ids):
+ url = common.get_url('report', self.zone) + 'messages/detail'
+ params = {'msg_ids': msg_ids}
+ response = self.send("GET", url, params = params)
+ return response
+
def get_users(self, time_unit,start,duration):
url = common.get_url('report', self.zone) + 'users'
params = {
diff --git a/jpush/schedule/core.py b/jpush/schedule/core.py
index c536cbf..69b2c03 100644
--- a/jpush/schedule/core.py
+++ b/jpush/schedule/core.py
@@ -43,6 +43,11 @@ def delete_schedule(self,schedule_id):
result = self.send("DELETE", url)
return result
+ def get_msg_ids(self, schedule_id):
+ url = common.BASE_SCHEDULEURL + schedule_id + '/msg_ids'
+ body = None
+ result = self.send("GET", url, body)
+ return result
class ScheduleResponse(object):
"""Response to a successful device request send.
@@ -67,4 +72,4 @@ def get_status_code(self):
return self.status_code
def __str__(self):
- return "Schedule response Payload: {0}".format(self.payload)
\ No newline at end of file
+ return "Schedule response Payload: {0}".format(self.payload)
diff --git a/setup.py b/setup.py
index 484e354..6e1f410 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@
try:
from setuptools import setup
except (ImportError):
- from distutils.core import setup
+ from distutils.core import setup
_version_re = re.compile(r'__version__\s+=\s+(.*)')
@@ -16,10 +16,10 @@
name='jpush',
version=version,
description='JPush\'s officially supported Python client library',
- keywords=('JPush', 'JPush API', 'Android Push', 'iOS Push'),
+ keywords=('JPush', 'JPush API', 'Android Push', 'iOS Push', 'HMOS Push'),
license='MIT License',
long_description=open("README.rst", "r").read(),
-
+ long_description_content_type="text/markdown",
url='https://github.com/jpush/jpush-api-python-client',
author='jpush',
author_email='support@jpush.cn',
diff --git a/tests/conf.py b/tests/conf.py
deleted file mode 100644
index 76aa676..0000000
--- a/tests/conf.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# please put your app_key and master_secret here
-app_key = u'6be9204c30b9473e87bad4dc'
-master_secret = u'cae22120eed6835e486399a7'
diff --git a/tests/conf.py.example b/tests/conf.py.example
new file mode 100644
index 0000000..c1f599a
--- /dev/null
+++ b/tests/conf.py.example
@@ -0,0 +1,3 @@
+# please put your app_key and master_secret here
+app_key = u'xxxxxx'
+master_secret = u'xxxxxx'
diff --git a/tests/devices/test_devices.py b/tests/devices/test_devices.py
index 96851a9..9bc0d54 100644
--- a/tests/devices/test_devices.py
+++ b/tests/devices/test_devices.py
@@ -19,7 +19,7 @@ def test_create_device(self):
def test_aliasuser(self):
alias = "alias1"
- platform = "android,ios"
+ platform = "android,ios,hmos"
result = device.get_aliasuser(alias, platform)
self.assertEqual(result.status_code, 200)
@@ -44,13 +44,13 @@ def test_get_device(self):
def test_remove_alias(self):
alias = "alias1"
- platform = "android,ios"
+ platform = "android,ios,hmos"
result = device.delete_alias(alias, platform)
self.assertEqual(result.status_code, 200)
def test_remove_tags(self):
tag = "ddd"
- platform = "android,ios"
+ platform = "android,ios,hmos"
result = device.delete_tag(tag, platform)
self.assertEqual(result.status_code, 200)
diff --git a/tests/push/test_message.py b/tests/push/test_message.py
index 13188bb..48dae4d 100644
--- a/tests/push/test_message.py
+++ b/tests/push/test_message.py
@@ -33,6 +33,12 @@ def test_winphone(self):
jpush.notification(winphone=jpush.winphone(alert="Hello", extras={'k3':'v3'})),
{'winphone': {'extras': {'k3': 'v3'}, 'alert': 'Hello'}}
)
+
+ def test_hmos(self):
+ self.assertEqual(
+ jpush.notification(hmos=jpush.hmos(alert="Hello", title="Title", extras={'k4':'v4'})),
+ {'hmos': {'extras': {'k4': 'v4'}, 'alert': 'Hello', 'title': 'Title'}}
+ )
def test_push(self):
_jpush = jpush.JPush(app_key, master_secret)