def test_non_ascii(self): # meta-data size. ensure 'alert' is included. empty_msg_size = len( Message(tokens=[VALID_TOKEN_ONE], alert="a").get_json_payload()) - 1 max_utf8_size = 3 # size of maximum utf8 encoded character in bytes chinese_str = ( u'\u5187\u869a\u5487\u6b8f\u5cca\u9f46\u9248\u6935\u4ef1\u752a' u'\u67cc\u521e\u62b0\u530a\u6748\u9692\u5c6e\u653d\u588f\u6678') chinese_msg_size = len( Message(tokens=[VALID_TOKEN_ONE], alert=chinese_str).get_json_payload()) self.assertLessEqual(chinese_msg_size, empty_msg_size + len(chinese_str) * max_utf8_size) max_emoji_size = 4 # size of maximum utf8 encoded character in bytes # emoji emoji_str = u'\U0001f601\U0001f603\U0001f638\U00002744' emoji_msg_size = len( Message(tokens=VALID_TOKEN_ONE, alert=emoji_str).get_json_payload()) self.assertLessEqual(emoji_msg_size, empty_msg_size + len(emoji_str) * max_emoji_size)
def test_send(self): # success, retry + include-failed, don't-retry + include-failed session = self.get_session(push=(None, 1, 3)) msg = Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], alert="my alert", badge=10, content_available=1, my_extra=15) srv = APNs(self.get_connection(session)) res = srv.send(msg) self.assertEqual(len(res.failed), 0) self.assertEqual(len(res.errors), 0) self.assertFalse(res.needs_retry()) srv = APNs(self.get_connection(session)) self.assertEqual(session.pool.push_result_pos, 0) session.pool.push_result_pos += 1 res = srv.send(msg) self.assertEqual(len(res.failed), 0) self.assertEqual(len(res.errors), 1) self.assertTrue(res.needs_retry()) # indeed, we have used the cache self.assertEqual(session.pool.new_connections, 1) srv = APNs(self.get_connection(session)) res = srv.send(msg) self.assertEqual(len(res.failed), 0) self.assertEqual(len(res.errors), 1) self.assertFalse(res.needs_retry()) # indeed, new connection, we haven't used the cache self.assertEqual(session.pool.new_connections, 2) messages = [ Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], alert="bar alert", badge=4, my_extra=15), Message(tokens=[VALID_TOKEN_THREE], alert="foo alert", badge=0, content_available=1, more_extra=15) ] srv = APNs(self.get_connection(session)) res = srv.send(messages) self.assertEqual(len(res.failed), 0) self.assertEqual(len(res.errors), 0) self.assertFalse(res.needs_retry())
def test_validate_tokens(self): with self.assertRaises(ValueError): Message(tokens="") with self.assertRaises(ValueError): Message(tokens=[""]) with self.assertRaises(ValueError): Message(tokens=[VALID_TOKEN_ONE, INVALID_TOKEN_ONE]) with self.assertRaises(TypeError): Message(tokens=[INVALID_TOKEN_TWO])
def setUp(self): self.now = datetime.datetime.now() # Typical message self.message = Message( tokens=VALID_TOKEN_ONE, alert=u"Russian: \u0421\u0430\u0440\u0434\u0430\u0440", badge=10, sound="test.mp3", content_available=1, expiry=self.now + datetime.timedelta(days=3), priority=30, extra={'key': 'value'}) # Message with a custom payload self.raw_message = Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], payload=self.message.payload, priority=5, expiry=datetime.timedelta(days=5))
def setUp(self): self.now = datetime.datetime.now() # Typical message self.message = Message( "0123456789ABCDEF", alert=u"Russian: \u0421\u0430\u0440\u0434\u0430\u0440", badge=10, sound="test.mp3", content_available=1, expiry=self.now + datetime.timedelta(days=3), priority=30, extra={'key': 'value'} ) # Message with a custom payload self.raw_message = Message( ["0123456789ABCDEF", "FEDCBA9876543210"], payload=self.message.payload, priority=5, expiry=datetime.timedelta(days=5) )
def test_send(self): if not self._certificate_available(): # Skip, no certificate available return session = self.get_session() # Test with single message msg = Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], alert="my alert", badge=10, content_available=1, my_extra=15) srv = APNs(self.get_connection(session)) res = srv.send(msg) self.assertEqual(len(res.failed), 1) self.assertEqual(len(res.errors), 0) self.assertTrue(res.needs_retry()) # Test with multiple messages messages = [ Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], alert="bar alert", badge=4, my_extra=15), Message(tokens=[VALID_TOKEN_THREE], alert="foo alert", badge=0, content_available=1, more_extra=15) ] srv = APNs(self.get_connection(session)) res = srv.send(messages) self.assertEqual(len(res.failed), 1) self.assertEqual(len(res.errors), 0) self.assertTrue(res.needs_retry())
def test_serialization(self): # standard pickle s_message = pickle.dumps(self.message) s_raw_message = pickle.dumps(self.raw_message) c_message = pickle.loads(s_message) c_raw_message = pickle.loads(s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key)) # custom s_message = self.message.__getstate__() s_raw_message = self.raw_message.__getstate__() # JSON/XML/etc and store/send s_message = json.dumps(s_message) s_raw_message = json.dumps(s_raw_message) # unserialize s_message = json.loads(s_message) s_raw_message = json.loads(s_raw_message) # reconstruct c_message = Message(**s_message) c_raw_message = Message(**s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key))
def _apns_push(self, packet): envelope = packet.get('envelope') to_jid = envelope.get('to') to = to_jid.split('@')[0] user_info = UserInfo.objects.get(id=to) if user_info.device_token: logger.info('APNS : %s' % user_info.device_token) device_token = user_info.device_token payload = packet.get('payload') attrs = payload.get('attrs') messageType = attrs.get('messageType') if 'text' == messageType: content = payload.get('content') elif 'image' == messageType: content = '收到一张图片' elif 'geo' == messageType: content = '收到一个坐标分享' elif 'audio' == messageType: content = '收到一条语音' elif 'contact' == messageType: # 联系人接口通知 action = attrs.get('action') if 'add' == action: content = '添加联系人通知' elif 'accept' == action: content = '接受好友申请通知' elif 'reject' == action: content = '拒绝好友申请通知' total = packet.get('total') if content and device_token: message = Message(tokens=[device_token], alert=content, badge=int(total), content_available=1, my_extra=15) srv = APNs(con) res = srv.send(message) # Check failures. Check codes in APNs reference docs. for token, reason in res.failed.items(): code, errmsg = reason logger.info("Device faled: {0}, reason: {1}".format( token, errmsg)) # Check failures not related to devices. for code, errmsg in res.errors: logger.error("Error: %s" % errmsg)
class APNsClerkMessageTest(Python26Mixin, unittest.TestCase): """ Test Message API. """ def setUp(self): self.now = datetime.datetime.now() # Typical message self.message = Message( tokens=VALID_TOKEN_ONE, alert=u"Russian: \u0421\u0430\u0440\u0434\u0430\u0440", badge=10, sound="test.mp3", content_available=1, expiry=self.now + datetime.timedelta(days=3), priority=30, extra={'key': 'value'}) # Message with a custom payload self.raw_message = Message(tokens=[VALID_TOKEN_ONE, VALID_TOKEN_TWO], payload=self.message.payload, priority=5, expiry=datetime.timedelta(days=5)) def test_payload(self): payload = self.message.get_json_payload() self.assertIsInstance(payload, six.binary_type) unicode_src = payload.decode('utf-8') payload = json.loads(unicode_src) self.assertEqual( payload["aps"], { "alert": self.message.alert, "badge": self.message.badge, "sound": self.message.sound, "content-available": self.message.content_available }) for k, v in self.message.extra.items(): self.assertEqual(payload[k], v) def test_serialization(self): # standard pickle s_message = pickle.dumps(self.message) s_raw_message = pickle.dumps(self.raw_message) c_message = pickle.loads(s_message) c_raw_message = pickle.loads(s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key)) # custom s_message = self.message.__getstate__() s_raw_message = self.raw_message.__getstate__() # JSON/XML/etc and store/send s_message = json.dumps(s_message) s_raw_message = json.dumps(s_raw_message) # unserialize s_message = json.loads(s_message) s_raw_message = json.loads(s_raw_message) # reconstruct c_message = Message(**s_message) c_raw_message = Message(**s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key)) def test_non_ascii(self): # meta-data size. ensure 'alert' is included. empty_msg_size = len( Message(tokens=[VALID_TOKEN_ONE], alert="a").get_json_payload()) - 1 max_utf8_size = 3 # size of maximum utf8 encoded character in bytes chinese_str = ( u'\u5187\u869a\u5487\u6b8f\u5cca\u9f46\u9248\u6935\u4ef1\u752a' u'\u67cc\u521e\u62b0\u530a\u6748\u9692\u5c6e\u653d\u588f\u6678') chinese_msg_size = len( Message(tokens=[VALID_TOKEN_ONE], alert=chinese_str).get_json_payload()) self.assertLessEqual(chinese_msg_size, empty_msg_size + len(chinese_str) * max_utf8_size) max_emoji_size = 4 # size of maximum utf8 encoded character in bytes # emoji emoji_str = u'\U0001f601\U0001f603\U0001f638\U00002744' emoji_msg_size = len( Message(tokens=VALID_TOKEN_ONE, alert=emoji_str).get_json_payload()) self.assertLessEqual(emoji_msg_size, empty_msg_size + len(emoji_str) * max_emoji_size) def test_batch(self): # binary serialization in ridiculously small buffer =) b_message = list(self.message.batch(10)) b_raw_message = list(self.raw_message.batch(10)) # number of batches self.assertEqual(len(b_message), 1) self.assertEqual(len(b_raw_message), 2) # lets read stuff back. number of sent before ID's is of course 0. self.check_message(b_message[0], 0, self.message) self.check_message(b_raw_message[0], 0, self.raw_message) self.check_message(b_raw_message[1], 1, self.raw_message) def check_message(self, batch, itr, msg): sent, data = batch # we send batches of 1 token size self.assertEqual(sent, itr) # |COMMAND|FRAME-LEN|{token}|{payload}|{id:4}|{expiry:4}|{priority:1} command, frame_len = struct.unpack(">BI", data[0:5]) self.assertEqual(command, 2) self.assertEqual(frame_len, len(data) - 5) off = 5 restored = {} for itm in range(1, 6): hdr, length = struct.unpack(">BH", data[off:(off + 3)]) off += 3 value = data[off:(off + length)] off += length if hdr == 1: restored['token'] = binascii.hexlify(value).decode('ascii') elif hdr == 2: restored['payload'] = json.loads(value.decode('utf-8')) elif hdr == 3: restored['index'] = struct.unpack(">I", value)[0] elif hdr == 4: restored['expiry'] = struct.unpack(">I", value)[0] elif hdr == 5: restored['priority'] = struct.unpack(">B", value)[0] for key in ('token', 'payload', 'index', 'expiry', 'priority'): if key not in restored: self.fail("Binary message is missing: %s" % key) # check message self.assertEqual(msg.tokens[itr].lower(), restored['token'].lower()) self.assertEqual(msg.payload['aps'], restored['payload']['aps']) restored['payload'].pop('aps') self.assertEqual(msg.extra, restored['payload']) self.assertEqual(restored['index'], itr) self.assertEqual(msg.expiry, restored['expiry']) self.assertEqual(msg.priority, restored['priority']) def test_retry(self): # include failed r_message = self.message.retry(0, True) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.message, key), getattr(r_message, key)) # nothing to retry, we skip the token self.assertEqual(self.message.retry(0, False), None) # include failed r_raw_message = self.raw_message.retry(0, True) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.raw_message, key), getattr(r_raw_message, key)) # skip failed r_raw_message = self.raw_message.retry(0, False) self.assertEqual(self.raw_message.tokens[1:], r_raw_message.tokens) for key in ('alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.raw_message, key), getattr(r_raw_message, key))
def setUp(self): self.msg = Message([VALID_TOKEN_ONE, VALID_TOKEN_TWO], alert="message")
def _callback(self, ch, method, properties, body): """ send apns notifications :param ch: :param method: :param properties: :param body: :return: """ self.session.outdate(timedelta(minutes=5)) if conf.getboolean('application', 'debug'): self.apns_con = self.session.get_connection("push_sandbox", cert_file=conf.get('apns', 'cert_sandbox')) else: self.apns_con = self.session.get_connection("push_production", cert_file=conf.get('apns', 'cert_production')) message = loads(body) logger.debug('payload: %s' % message) badge = None sound = 'default' content_available = None # time to live ttl = timedelta(days=5) if 'apns' in message['payload']: if 'badge' in message['payload']['apns']: badge = message['payload']['apns']['badge'] if 'sound' in message['payload']['apns']: sound = message['payload']['apns']['sound'] if 'content_available' in message['payload']['apns']: content_available = message['payload']['apns']['content_available'] if 'ttl' in message['payload']: ttl = timedelta(seconds=message['payload']['ttl']) message = Message(message['devices'], alert=message['payload']['alert'], badge=badge, sound=sound, content_available=content_available, expiry=ttl, extra=message['payload']['data'] if 'data' in message['payload'] else None) try: srv = APNs(self.apns_con) response = srv.send(message) logger.debug('apns response: %s' % response) except Exception as ex: ch.basic_ack(delivery_tag=method.delivery_tag) logger.exception(ex) return # Check failures. Check codes in APNs reference docs. for token, reason in response.failed.items(): code, errmsg = reason # according to APNs protocol the token reported here # is garbage (invalid or empty), stop using and remove it. logger.info('delivery failure apns_token: %s, reason: %s' % (token, errmsg)) device_obj = Device.query.filter_by(platform_id=token).first() if device_obj: db.session.delete(device_obj) try: db.session.commit() except Exception as ex: db.session.rollback() logger.exception(ex) # Check failures not related to devices. for code, errmsg in response.errors: logger.error(errmsg) # Check if there are tokens that can be retried if response.needs_retry(): # repeat with retry_message or reschedule your task srv.send(response.retry()) ch.basic_ack(delivery_tag=method.delivery_tag)
def send_apns_message(device, app, message_type, data=None): """ Send an Apple Push Notification message. """ token_list = [device.token] unique_key = device.token if message_type == TYPE_CALL: unique_key = data['unique_key'] message = Message(token_list, payload=get_call_push_payload( unique_key, data['phonenumber'], data['caller_id'])) elif message_type == TYPE_MESSAGE: message = Message(token_list, payload=get_message_push_payload(data['message'])) else: logger.warning('{0} | TRYING TO SENT MESSAGE OF UNKNOWN TYPE: {1}', unique_key, message_type) session = Session() push_mode = settings.APNS_PRODUCTION if device.sandbox: # Sandbox push mode. push_mode = settings.APNS_SANDBOX full_cert_path = os.path.join(settings.CERT_DIR, app.push_key) con = session.get_connection(push_mode, cert_file=full_cert_path) srv = APNs(con) try: logger.info( '{0} | Sending APNS \'{1}\' message at time:{2} to {3} Data:{4}'. format( unique_key, message_type, datetime.datetime.fromtimestamp( time()).strftime('%H:%M:%S.%f'), device.token, data)) res = srv.send(message) except Exception: logger.exception('{0} | Error sending APNS message'.format( unique_key, )) else: # Check failures. Check codes in APNs reference docs. for token, reason in res.failed.items(): code, errmsg = reason # According to APNs protocol the token reported here # is garbage (invalid or empty), stop using and remove it. logger.warning( '{0} | Sending APNS message failed for device: {1}, reason: {2}' .format(unique_key, token, errmsg)) # Check failures not related to devices. for code, errmsg in res.errors: logger.warning('{0} | Error sending APNS message. \'{1}\''.format( unique_key, errmsg)) # Check if there are tokens that can be retried. if res.needs_retry(): logger.info('{0} | Could not sent APNS message, retrying...') # Repeat with retry_message or reschedule your task. res.retry()
class APNsclerkMessageTest(Python26Mixin, unittest.TestCase): """ Test Message API. """ def setUp(self): self.now = datetime.datetime.now() # Typical message self.message = Message( "0123456789ABCDEF", alert=u"Russian: \u0421\u0430\u0440\u0434\u0430\u0440", badge=10, sound="test.mp3", content_available=1, expiry=self.now + datetime.timedelta(days=3), priority=30, extra={'key': 'value'} ) # Message with a custom payload self.raw_message = Message( ["0123456789ABCDEF", "FEDCBA9876543210"], payload=self.message.payload, priority=5, expiry=datetime.timedelta(days=5) ) def test_payload(self): payload = self.message.get_json_payload() self.assertIsInstance(payload, six.binary_type) unicode_src = payload.decode('utf-8') payload = json.loads(unicode_src) self.assertEqual(payload["aps"], { "alert": self.message.alert, "badge": self.message.badge, "sound": self.message.sound, "content-available": self.message.content_available }) for k, v in self.message.extra.items(): self.assertEqual(payload[k], v) def test_serialization(self): # standard pickle s_message = pickle.dumps(self.message) s_raw_message = pickle.dumps(self.raw_message) c_message = pickle.loads(s_message) c_raw_message = pickle.loads(s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key)) # custom s_message = self.message.__getstate__() s_raw_message = self.raw_message.__getstate__() # JSON/XML/etc and store/send s_message = json.dumps(s_message) s_raw_message = json.dumps(s_raw_message) # unserialize s_message = json.loads(s_message) s_raw_message = json.loads(s_raw_message) # reconstruct c_message = Message(**s_message) c_raw_message = Message(**s_raw_message) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'extra', 'priority', '_payload'): self.assertEqual(getattr(self.message, key), getattr(c_message, key)) self.assertEqual(getattr(self.raw_message, key), getattr(c_raw_message, key)) def test_non_ascii(self): # meta-data size. ensure 'alert' is included. empty_msg_size = len(Message(tokens=[], alert="a").get_json_payload()) - 1 MAX_UTF8_SIZE = 3 # size of maximum utf8 encoded character in bytes chinese_str = ( u'\u5187\u869a\u5487\u6b8f\u5cca\u9f46\u9248\u6935\u4ef1\u752a' u'\u67cc\u521e\u62b0\u530a\u6748\u9692\u5c6e\u653d\u588f\u6678') chinese_msg_size = len(Message(tokens=[], alert=chinese_str).get_json_payload()) self.assertLessEqual( chinese_msg_size, empty_msg_size + len(chinese_str) * MAX_UTF8_SIZE) MAX_EMOJI_SIZE = 4 # size of maximum utf8 encoded character in bytes # emoji emoji_str = (u'\U0001f601\U0001f603\U0001f638\U00002744') emoji_msg_size = len(Message(tokens="", alert=emoji_str).get_json_payload()) self.assertLessEqual( emoji_msg_size, empty_msg_size + len(emoji_str) * MAX_EMOJI_SIZE) def test_batch(self): # binary serialization in ridiculously small buffer =) b_message = list(self.message.batch(10)) b_raw_message = list(self.raw_message.batch(10)) # number of batches self.assertEqual(len(b_message), 1) self.assertEqual(len(b_raw_message), 2) # lets read stuff back. number of sent before ID's is of course 0. self.check_message(b_message[0], 0, self.message) self.check_message(b_raw_message[0], 0, self.raw_message) self.check_message(b_raw_message[1], 1, self.raw_message) def check_message(self, batch, itr, msg): sent, data = batch # we send batches of 1 token size self.assertEqual(sent, itr) # |COMMAND|FRAME-LEN|{token}|{payload}|{id:4}|{expiry:4}|{priority:1} command, frame_len = struct.unpack(">BI", data[0:5]) self.assertEqual(command, 2) self.assertEqual(frame_len, len(data) - 5) off = 5 restored = {} for itm in range(1, 6): hdr, length = struct.unpack(">BH", data[off:(off+3)]) off += 3 value = data[off:(off+length)] off += length if hdr == 1: restored['token'] = binascii.hexlify(value).decode('ascii') elif hdr == 2: restored['payload'] = json.loads(value.decode('utf-8')) elif hdr == 3: restored['index'] = struct.unpack(">I", value)[0] elif hdr == 4: restored['expiry'] = struct.unpack(">I", value)[0] elif hdr == 5: restored['priority'] = struct.unpack(">B", value)[0] for key in ('token', 'payload', 'index', 'expiry', 'priority'): if key not in restored: self.fail("Binary message is missing: %s" % key) # check message self.assertEqual(msg.tokens[itr].lower(), restored['token'].lower()) self.assertEqual(msg.payload['aps'], restored['payload']['aps']) restored['payload'].pop('aps') self.assertEqual(msg.extra, restored['payload']) self.assertEqual(restored['index'], itr) self.assertEqual(msg.expiry, restored['expiry']) self.assertEqual(msg.priority, restored['priority']) def test_retry(self): # include failed r_message = self.message.retry(0, True) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.message, key), getattr(r_message, key)) # nothing to retry, we skip the token self.assertEqual(self.message.retry(0, False), None) # include failed r_raw_message = self.raw_message.retry(0, True) for key in ('tokens', 'alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.raw_message, key), getattr(r_raw_message, key)) # skip failed r_raw_message = self.raw_message.retry(0, False) self.assertEqual(self.raw_message.tokens[1:], r_raw_message.tokens) for key in ('alert', 'badge', 'sound', 'content_available', 'expiry', 'priority', 'extra'): self.assertEqual(getattr(self.raw_message, key), getattr(r_raw_message, key))