Commit 77789b16 authored by hupantingxue's avatar hupantingxue

Initialize api 3.0 python client code;

parent 8cefb88a
APP_KEY = 'xxxxx'
MASTER_SECRET = 'xxxxxx'
MSG_JSONOBJ = {xxx}
Examples
========
Common setup:
.. code-block:: python
import jpush as jpush
jpush = jpush.JPush(app_key, master_secret)
Simple broadcast to all devices
-------------------------------
.. code-block:: python
push = jpush.create_push()
push.audience = jpush.all_
push.notification = jpush.notification(alert="Hello, world!")
push.device_types = jpush.all_
push.send()
Complex audience with iOS & Android specifics
---------------------------------------------
.. code-block:: python
push = jpush.create_push()
push.audience = jpush.audience(
jpush.tag("breakingnews"),
jpush.tag_and("sports"),
)
)
push.notification = jpush.notification(
ios=jpush.ios(
alert="Kim Jong-Un wins U.S. Open",
badge="+1",
extra={"articleid": "123456"}
),
android=jpush.android(
alert="Breaking Special Android News! Glorious Leader Kim Jong-Un wins U.S. Open!",
extra={"articleid": "http://m.example.com/123456"}
)
)
push.platform = jpush.platform('ios', 'android')
push.send()
Single iOS push
---------------
.. code-block:: python
push = jpush.create_push()
push.audience = jpush.registration_id('fffffffffff')
push.notification = jpush.notification(
ios=jpush.ios(alert="Kim Jong-Un is following you on Twitter"))
push.platform = jpush.platform('ios')
push.send()
# -*- coding: utf-8 -*-
import time
from jpush import JPushClient
import conf
sendno = int(time.time())
app_key = conf.APP_KEY
master_secret = conf.MASTER_SECRET
msg_json = conf.MSG_JSONOBJ
jpush_client = JPushClient(master_secret)
# Send message
jpush_client.send_msg(msg_json)
from jpush import RecvClient
recv_client = RecvClient('appkey', 'master_secret')
recv_client.get_recv([0, 1, 2])
recv_client.get_recv('0,1,2')
from .push import JPushClient
from .recv import RecvClient
"""Python package for using the JPush API"""
from .core import JPush
from .common import JPushFailure, Unauthorized
__version__ = '3.0.0'
VERSION = tuple(map(int, __version__.split('.')))
__all__ = ['JPushClient', 'RecvClient']
from .push import (
Push,
all_,
tag,
tag_and,
alias,
registration_id,
notification,
ios,
android,
winphone,
platform,
message,
)
__all__ = [
JPush,
JPushFailure,
Unauthorized,
all_,
Push,
tag,
tag_and,
alias,
registration_id,
notification,
ios,
android,
winphone,
message,
platform,
]
# Silence urllib3 INFO logging by default
import logging
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARNING)
import json
import logging
BASE_URL = "https://api.jpush.cn/"
PUSH_URL = BASE_URL + 'v3/push/'
logger = logging.getLogger('jpush')
class Unauthorized(Exception):
"""Raised when we get a 401 from the server"""
class JPushFailure(Exception):
"""Raised when we get an error response from the server.
:param args: For backwards compatibility, ``*args`` includes the status and
response body.
"""
error = None
error_code = None
details = None
response = None
def __init__(self, error, error_code, details, response, *args):
self.error = error
self.error_code = error_code
self.details = details
self.response = response
super(JPushFailure, self).__init__(*args)
@classmethod
def from_response(cls, response):
"""Instantiate a ValidationFailure from a Response object"""
try:
payload = response.json()
error = payload.get('error')
error_code = error.get('code')
details = error.get('message')
except ValueError:
error = response.reason
error_code = None
details = response.content
logger.error(
"Request failed with status %d: '%s %s': %s",
response.status_code, error_code, error, json.dumps(details))
return cls(error, error_code, details, response, response.status_code,
response.content)
import json
import logging
import warnings
import requests
from . import common, __about__
from .push import Push
logger = logging.getLogger('jpush')
class JPush(object):
def __init__(self, key, secret):
self.key = key
self.secret = secret
self.session = requests.Session()
self.session.auth = (key, secret)
def _request(self, method, body, url, content_type=None,
version=None, params=None):
headers = {}
if content_type:
headers['content-type'] = content_type
logger.debug("Making %s request to %s. Headers:\n\t%s\nBody:\n\t%s",
method, url, '\n\t'.join(
'%s: %s' % (key, value) for (key, value) in headers.items()),
body)
response = self.session.request(
method, url, data=body, params=params, headers=headers)
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)
if response.status_code == 401:
raise common.Unauthorized
elif not (200 <= response.status_code < 300):
raise common.JPushFailure.from_response(response)
return response
def push(self, payload):
"""Push this payload to the specified recipients.
Payload: a dictionary the contents to send, e.g.:
{'aps': {'alert': 'Hello'}, 'android': {'alert': 'Hello'}}
"""
warnings.warn(
"JPush.push() is deprecated. See documentation on upgrading.",
DeprecationWarning)
body = json.dumps(payload)
self._request('POST', body, common.PUSH_URL,
'application/json', version=1)
def create_push(self):
"""Create a Push notification."""
return Push(self)
# -*- coding: utf-8 -*-
#
# Push API
#
import base64
import json
import hashlib
import urllib
import urllib2
API_URL = "https://api.jpush.cn/v3/push"
class JPushClient:
"""JPush Python Client Class"""
def __init__(self, app_key, master_secret):
self.app_key = app_key
self.master_secret = master_secret
def _send_msg(self, params):
'''Push API for all kinds of message and notification,
dict params restore all parameters'''
try:
send_param=json.dumps(params)
except Exception as e:
print 'params should be json object ', e
try:
base64string = base64.encodestring('%s:%s' % (self.app_key, self.master_secret))[:-1]
req = urllib2.Request(API_URL)
req.add_header("Authorization", "Basic %s" % base64string)
req.add_header("Content-Type", "application/json")
api_post = urllib2.urlopen(req, urllib.urlencode(params), timeout=5)
print api_post.read()
except urllib2.HTTPError as e:
error_message = e.read()
print error_message
except Exception as e:
print 'send message fail ', e
def send_msg(self, params):
'''Push API for message send.
params must be json-format string;'''
try:
self._send_msg(params)
except Exception as e:
print e
from .core import Push
from .audience import (
tag,
tag_and,
alias,
registration_id,
)
from .payload import (
android,
ios,
winphone,
platform,
notification,
message,
)
# Common selector for audience & platform
all_ = "all"
"""Select all, to do a broadcast.
Used in both ``audience`` and ``platform``.
"""
__all__ = [
all_,
Push,
tag,
tag_and,
alias,
registration_id,
notification,
message,
platform,
]
import re
DEVICE_TOKEN_FORMAT = re.compile(r'^[0-9a-fA-F]{64}$')
PIN_FORMAT = re.compile(r'^[0-9a-fA-F]{8}$')
UUID_FORMAT = re.compile(
r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}'
r'-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')
# Value selectors; device IDs, aliases, tags, etc.
def tag(tag):
"""Select a single tag."""
return {"tag": tag}
def tag_and(tag_and):
"""Select a single tag_and."""
return {"tag_and": tag_and}
def alias(alias):
"""Select a single alias."""
return {"alias": alias}
def registration_id(registration_id):
"""Select a (list of) registration_id(s)."""
return {"registration_id": registration_id}
def audience(*children):
"""Select audience that match all of the given selectors.
>>> audience(tag('sports'), tag_and('business'))
{'audience': {'tag': 'sports'}, {'tag_and': 'business'}}
"""
return {"audience": {child for child in children}}
import json
import logging
from jpush import common
logger = logging.getLogger('jpush')
class Push(object):
"""A push notification. Set audience, message, etc, and send."""
def __init__(self, jpush):
self._jpush = jpush
self.audience = None
self.notification = None
self.platform = None
self.options = None
self.message = None
@property
def payload(self):
data = {
"audience": self.audience,
"paltform": self.platform,
}
if (self.notification is None) and (self.message is None):
raise ValueError("Notification and message cannot be both empty")
if self.notification is not None:
data['notification'] = self.notification
if self.options is not None:
data['options'] = self.options
if self.message is not None:
data['message'] = self.message
return data
def send(self):
"""Send the notification.
:returns: :py:class:`PushResponse` object with ``push_ids`` and
other response data.
:raises AirshipFailure: Request failed.
:raises Unauthorized: Authentication failed.
"""
body = json.dumps(self.payload)
response = self._jpush._request('POST', body,
common.PUSH_URL, 'application/json', version=3)
data = response.json()
return PushResponse(response)
class PushResponse(object):
"""Response to a successful push notification send.
Right now this is a fairly simple wrapper around the json payload response,
but making it an object gives us some flexibility to add functionality
later.
"""
payload = None
def __init__(self, response):
data = response.json()
self.payload = data
def __str__(self):
return "Response Payload: {0}".format(self.payload)
import re
# Valid autobadge values: auto, +N, -N
VALID_AUTOBADGE = re.compile(r'^(auto|[+-][\d]+)$')
def notification(alert=None, ios=None, android=None, winphone=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`.
"""
payload = {}
if alert is not None:
payload['alert'] = alert
if ios is not None:
payload['ios'] = ios
if android is not None:
payload['android'] = android
if winphone is not None:
payload['winphone'] = winphone
if not payload:
raise ValueError("Notification body may not be empty")
return payload
def ios(alert=None, badge=None, sound=None, content_available=False,
extras=None):
"""iOS/APNS specific platform override payload.
:keyword alert: iOS format alert, as either a string or dictionary.
:keyword badge: An integer badge value or an *autobadge* string.
: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
sent to the device.
>>> ios(alert='Hello!', sound='cat.caf',
... extra={'articleid': '12345'})
{'sound': 'cat.caf', 'extra': {'articleid': '12345'}, 'alert': 'Hello!'}
"""
payload = {}
if alert is not None:
if not isinstance(alert, basestring) or isinstance(alert, dict):
raise ValueError("iOS alert must be a string or dictionary")
payload['alert'] = alert
if badge is not None:
if not (isinstance(badge, basestring) or isinstance(badge, int)):
raise ValueError("iOS badge must be an integer or string")
if isinstance(badge, basestring) and not VALID_AUTOBADGE.match(badge):
raise ValueError("Invalid iOS autobadge value")
payload['badge'] = badge
if sound is not None:
payload['sound'] = sound
else:
payload['sound'] = 'default'
if content_available:
payload['content-available'] = 1
if extra is not None:
payload['extras'] = extras
return payload
def android(alert, title=None, builder_id=None, extras=None):
"""Android specific platform override payload.
All keyword arguments are optional.
:keyword alert: String alert text.
:keyword title: String
:keyword builder_id: Integer
:keyword delay_while_idle: Boolean
:keyword extra: A set of key/value pairs to include in the push payload
sent to the device. All values must be strings.
"""
payload = {}
if alert is not None:
payload['alert'] = alert
if title is not None:
payload['title'] = collapse_key
if builder_id is not None:
payload['builder_id'] = builder_id
if extra is not None:
payload['extra'] = extra
return payload
def winphone(alert, title=None, _open_page=None, extras=None):
"""MPNS specific platform override payload.
Must include exactly one of ``alert``, ``title``, ``_open_page``, or ``extras``.
"""
if len(filter(None, (alert, toast, tile))) != 1:
raise ValueError("MPNS payload must have one notification type.")
payload = {}
if alert is not None:
payload['alert'] = alert
if title is not None:
payload['title'] = title
if _open_page is not None:
payload['_open_page'] = _open_page
if extras is not None:
payload['extras'] = extras
return payload
def message(msg_content, title=None, content_type=None, extras=None):
"""Inner-conn push message payload creation.
:param msg_content: Required, string
:param title: Optional, string
:keyword content_type: Optional, MIME type of the body
:keyword extras: Optional, dictionary of string values.
"""
payload = {
'msg_content': msg_content,
}
if title is not None:
payload['title'] = title
if content_type is not None:
payload['content_type'] = content_type
if extras is not None:
payload['extras'] = extras
return payload
def platform(*types):
"""Create a platform specifier.
>>> platform('ios', 'winphone')
['ios', 'winphone']
>>> platform('ios', 'symbian')
Traceback (most recent call last):
...
ValueError: Invalid device type 'symbian'
"""
if len(types) == 1 and types[0] == 'all':
return 'all'
for t in types:
if t not in ('ios', 'android', 'winphone'):
raise ValueError("Invalid platform '%s'" % t)
return [t for t in types]
# -*- coding: utf-8 -*-
#
# Report Received API
#
import httplib
API_HOST = "report.jpush.cn"
API_PATH = "/v2/received"
class RecvClient:
""" JPush Report Received Python Client Class"""
def __init__(self, appkey, master_secret, timeout=5):
self._timeout = timeout
self._auth = ("%s:%s" % (appkey, master_secret)).encode("base64").strip()
self._conn = None
self._reconnect()
def _reconnect(self):
if self._conn is None:
self._conn = httplib.HTTPSConnection(API_HOST,
timeout=self._timeout)
def close(self):
"""close connection"""
if self._conn:
self._conn.close()
def __del__(self):
try:
self.close()
except:
pass
def get_recv(self, msg_ids):
"""get recv result"""
if isinstance(msg_ids, list):
msg_ids = [str(i) for i in msg_ids]
msg_ids = ",".join(msg_ids)
url = "%s?msg_ids=%s" % (API_PATH, msg_ids)
auth_header = {"Authorization": "Basic %s" % self._auth}
if self._conn is None:
self._reconnect()
try:
self._conn.request(method="GET", url=url, headers=auth_header)
print self._conn.getresponse().read()
except Exception, e:
self._conn = None
print "Request receive result error: %s" % e
import unittest
import sys
sys.path.append('../..')
import jpush as jpush
class TestAudience(unittest.TestCase):
def test_basic_selectors(self):
selectors = (
(jpush.tag, 'test', {'tag': 'test'}),
(jpush.tag_and, 'test', {'tag_and': 'test'}),
(jpush.alias, 'test', {'alias': 'test'}),
(jpush.registration_id, 'test', {'registration_id': 'test'}),
)
for selector, value, result in selectors:
self.assertEqual(selector(value), result)
def test_invalid_device_selectors(self):
selectors = (
(jpush.device_token, 'f' * 63),
(jpush.device_token, 'f' * 65),
(jpush.device_token, '0123'),
(jpush.device_token, 'X' * 64),
(jpush.device_pin, '1234567'),
(jpush.device_pin, 'x' * 8),
(jpush.apid, 'foobar'),
(jpush.apid, '074e84a2-9ed9-4eee-9ca4-cc597bfdbef33'),
(jpush.apid, '074e84a2-9ed9-4eee-9ca4-cc597bfdbef'),
(jpush.wns, '074e84a2-9ed9-4eee-9ca4-cc597bfdbef'),
(jpush.mpns, '074e84a2-9ed9-4eee-9ca4-cc597bfdbef'),
)
for selector, value in selectors:
self.assertRaises(ValueError, selector, value)
def test_compound_selectors(self):
self.assertEqual(
jpush.or_(jpush.tag('foo'), jpush.tag('bar')),
{'or': [{'tag': 'foo'}, {'tag': 'bar'}]})
self.assertEqual(
jpush.and_(jpush.tag('foo'), jpush.tag('bar')),
{'and': [{'tag': 'foo'}, {'tag': 'bar'}]})
self.assertEqual(
jpush.not_(jpush.tag('foo')),
{'not': {'tag': 'foo'}})
import unittest
import sys
sys.path.append('../..')
import jpush as jpush
class TestMessage(unittest.TestCase):
def test_simple_alert(self):
self.assertEqual(
jpush.notification(alert='Hello'),
{'alert': 'Hello'})
def test_ios(self):
self.assertEqual(
jpush.notification(ios=jpush.ios(
alert='Hello',
badge='+1',
sound='cat.caf',
extra={'more': 'stuff'}
)),
{'ios': {
'alert': 'Hello',
'badge': '+1',
'sound': 'cat.caf',
'extra': {
'more': 'stuff',
}
}})
self.assertEqual(
jpush.notification(ios=jpush.ios(content_available=True)),
{'ios': { 'content-available': True}})
import unittest
import requests
import sys
sys.path.append('../..')
import jpush as jpush
class TestPush(unittest.TestCase):
def test_full_payload(self):
p = jpush.Push(None)
p.audience = jpush.all_
p.notification = jpush.notification(alert='Hello')
p.options = {}
p.paltform = jpush.all_
p.message = jpush.message("Title", "Body", "text/html", "utf8")
self.assertEqual(p.payload, {
"audience": "all",
"notification": {"alert": "Hello"},
"platform": "all",
"options": {},
"message": {
"msg_content": "Hello message from jpush",
}
})
# -*- coding: utf-8 -*-
'''Test sending msg'''
import time
import sys
sys.path.append("..")
from jpush import JPushClient
sendno = int(time.time())
app_key = 'dd1066407b044738b6479275'
master_secret = '2b38ce69b1de2a7fa95706ea'
msg_json = {"platform":"all","audience":"all", "notification":{"android":{"alert":"Pall Nandroid alert"}},"options":{"time_to_live":60, "sendno":sendno, "apns_production":True}}
jpush_client = JPushClient(app_key, master_secret)
jpush_client.send_msg(msg_json)
#-*- coding: utf-8 -*-
'''Test sending msg'''
import time
import sys
import json
sys.path.append("..")
from jpush import JPushClient
sendno = int(time.time())
app_key = 'dd1066407b044738b6479275'
master_secret = '2b38ce69b1de2a7fa95706ea'
msg_json = '''{"platform":"android,ios,winphone","audience":"all","notification":{"android":{"alert":"android 内容发送时间%s","title":"android 标题","builder_id":2,"extras":{"android jian1键":"android zhi1值","android jian2":"android zhi2"}},"ios":{"alert":"ios推送内容","sound":"sound.caf","badge":2,"content-avaliable":1},"winphone":{"alert":"winphone 内容","title":"WinPhone title","_open_page":"Page1.xaml","extras":{"winphone key1":"winphone value1"}}},"options":{"time_to_live":60, "sendno":%d}}''' %(time.strftime("%Y%m%d %H:%M:%S"), sendno)
msg_json = json.loads(msg_json)
print msg_json
jpush_client = JPushClient(app_key, master_secret)
jpush_client.send_msg(msg_json)
#-*- coding: utf-8 -*-
'''Test sending msg'''
import time
import sys
sys.path.append("..")
from jpush import JPushClient
sendno = int(time.time())
app_key = 'dd1066407b044738b6479275'
master_secret = '2b38ce69b1de2a7fa95706ea'
msg_json = {"platform":"all","audience":"all", "notification":{"alert":u"Pall Nall alert通知"},"options":{"time_to_live":60, "sendno":sendno, "override_msg_id":1154755988}}
print msg_json
jpush_client = JPushClient(app_key, master_secret)
jpush_client.send_msg(msg_json)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment