def send_message(self, tokens, alert=None, badge=None, sound=None, expiry=None, payload=None, **extra): """ Send a message. This will not retry but will call the failed_callback in case of failure .. seealso:: :py:class:`apnsclient.apns.Message` for a better description of the parameters :param tokens: The tokens to send to :param alert: The alert message :param badge: The badge to show :param sound: The sound to play :param expiry: A timestamp when message will expire :param payload: The payload :param extra: Extra info """ connection = self.get_connection() if connection is None: return None if not tokens: return False message = Message(tokens, alert, badge, sound, expiry, payload, **extra) srv = APNs(connection) res = srv.send(message) if res.failed and self.failed_callback: for key, reason in res.failed.iterkeys(): self.failed_callback(key, reason) return res
def _do_push_to_apns_service(user, message, apns_connection): # type: (UserProfile, Message, Connection) -> None if not apns_connection: logging.info("Not delivering APNS message %s to user %s due to missing connection" % (message, user)) return apns_client = APNs(apns_connection) ret = apns_client.send(message) if not ret: logging.warning("APNS: Failed to send push notification for clients %s" % (message.tokens,)) return for token, reason in ret.failed.items(): code, errmsg = reason b64_token = hex_to_b64(token) logging.warning("APNS: Failed to deliver APNS notification to %s, reason: %s" % (b64_token, errmsg)) if code == 8: # Invalid Token, remove from our database logging.warning("APNS: Removing token from database due to above failure") PushDeviceToken.objects.get(user=user, token=b64_token).delete() # Check failures not related to devices. for code, errmsg in ret.errors: logging.warning("APNS: Unknown error when delivering APNS: %s" % (errmsg,)) if ret.needs_retry(): logging.warning("APNS: delivery needs a retry, trying again") retry_msg = ret.retry() ret = apns_client.send(retry_msg) for code, errmsg in ret.errors: logging.warning("APNS: Unknown error when delivering APNS: %s" % (errmsg,))
def push(self, ios_users, device_tokens, alert, extra_kwargs): """Send message to ios. The method will block until the whole message is sent. The method returns :class:`Result` object, which you can examine for possible errors and retry attempts. """ pid = os.getpid() pname = multiprocessing.current_process().name try: msg = Message(device_tokens, alert=alert, badge=0, **extra_kwargs) apns_service = APNs(self.apns_conn) ret_obj = apns_service.send(msg) failed_message = ret_obj._failed if not failed_message: LOG.info("pid(%s) pname(%s) apns pusher send data" " to ios_users(%s) success" % (pid, pname, ios_users)) else: LOG.warn("pid(%s) pname(%s) apns pusher send result" " part failed: %r" %(pid, pname, failed_message)) except Exception as _ex: LOG.error("pid(%s) pname(%s) apns send data exception: %s" % (pid, pname, str(_ex))) return False return True
def push(token, message, badge_num, name, item_type): con = Session.new_connection( ("gateway.push.apple.com", 2195), cert_file="cert.pem", passphrase="this is the queue push key") message_packet = Message( token, alert=message, badge=badge_num, user=name, sound="default", itemType=item_type) srv = APNs(con) res = srv.send(message_packet) # Check failures. Check codes in APNs reference docs. for token, reason in res.failed.items(): code, errmsg = reason if res.needs_retry(): retry_message = res.retry() res = srv.send(retry_message)
def check_apns_feedback(): feedback_connection = session.get_connection(settings.APNS_FEEDBACK, cert_file=settings.APNS_CERT_FILE) apns_client = APNs(feedback_connection, tail_timeout=20) for token, since in apns_client.feedback(): since_date = timestamp_to_datetime(since) logging.info("Found unavailable token %s, unavailable since %s" % (token, since_date)) PushDeviceToken.objects.filter(token=hex_to_b64(token), last_updates__lt=since_date, type=PushDeviceToken.APNS).delete() logging.info("Finished checking feedback for stale tokens")
def test_send(self): # success, retry + include-failed, don't-retry + include-failed backend = DummyBackend(push=(None, 1, 3)) session = Session(pool=backend) msg = Message(["0123456789ABCDEF", "FEDCBA9876543210"], alert="my alert", badge=10, content_available=1, my_extra=15) push_con = session.get_connection("push_production", cert_string="certificate") srv = APNs(push_con) res = srv.send(msg) self.assertEqual(len(res.failed), 0) self.assertEqual(len(res.errors), 0) self.assertFalse(res.needs_retry()) push_con2 = session.get_connection("push_production", cert_string="certificate") srv = APNs(push_con2) 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) push_con = session.new_connection("push_production", cert_string="certificate") srv = APNs(push_con) 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)
def push_to_ios_devices_raw(devices=None, **kwargs): conn = Session.new_connection(settings.IOS_PUSH_SERVER, cert_file=settings.IOS_CERT) srv = APNs(conn) if devices is None: devices = APNSDevice.objects.filter(active=True).values_list( "registration_id", flat=True) message = Message(devices, **kwargs) res = srv.send(message) if res.needs_retry(): push_to_ios_devices_raw.delay(devices=res.retry().tokens, **kwargs)
def check_feedback_services_for_tokens(self): """ used only for feedback connection on schedule, remove not available tokens """ services = APNs(self.connection) try: for token, when in services.feedback(): last_update = self.get_last_update_of_token(token) if last_update and last_update < when: self.remove_token(token) except: print "Can't connect to APNs, looks like network is down"
def get_ios_device_feedback(): conn = Session.new_connection(settings.IOS_FEEDBACK_SERVER, cert_file=settings.IOS_CERT) srv = APNs(conn, tail_timeout=10) for token, since in srv.feedback(): print token try: obj = APNSDevice.objects.get(registration_id=token) obj.active = False obj.save() except APNSDevice.DoesNotExist: pass
def send_push_notifications(tokens, title, message, cert_file, url_args=None): url_args = url_args or ["", ""] session = Session() conn = session.get_connection("push_production", cert_file=cert_file) apns = APNs(conn) payload = {} payload["aps"] = {} payload["aps"]["alert"] = { "title": title, "body": message, } payload["aps"]["url-args"] = url_args message = Message(tokens, payload=payload) apns.send(message)
def remove_tokens(): # feedback needs no persistent connections. con = Session.new_connection(("gateway.push.apple.com", 2196), cert_file=os.path.join(os.getcwd(), 'cert.pem'), passphrase="this is the queue push key") # feedback server might be slow, so allow it to time out in 10 seconds srv = APNs(con, tail_timeout=10) # automatically closes connection for you for token, since in srv.feedback(): user = db.session.query(User).filter(User.device_token == token).one() user.device_token = None db.session.add(user) db.session.commit()
def check_apns_feedback(): feedback_connection = session.get_connection( settings.APNS_FEEDBACK, cert_file=settings.APNS_CERT_FILE) apns_client = APNs(feedback_connection, tail_timeout=20) for token, since in apns_client.feedback(): since_date = timestamp_to_datetime(since) logging.info("Found unavailable token %s, unavailable since %s" % (token, since_date)) PushDeviceToken.objects.filter(token=hex_to_b64(token), last_updates__lt=since_date, type=PushDeviceToken.APNS).delete() logging.info("Finished checking feedback for stale tokens")
def send_ios_push_notifications(self, tokens, message): con = self.ios_session.get_connection("push_production", cert_file=self.config.get('push', 'push_certificate_file')) message = Message(tokens, alert=message, badge=1, sound='default') # Send the message. 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 push failed: {0}, reason: {1}".format(token, errmsg)) LOGGER.info("Successfully sended {} to {}.".format(message.alert, tokens))
def connect_apns_server(cls, sandbox, p12, secret, timestamp): pub_key, priv_key = cls.gen_pem(p12, secret) session = Session(read_tail_timeout=1) address = 'push_sandbox' if sandbox else 'push_production' conn = session.get_connection(address, cert_string=pub_key, key_string=priv_key) apns = APNs(conn) return apns
def send_message(self, tokens, alert=None, badge=None, sound=None, content_available=None, expiry=None, payload=None, **extra): """ Send a message. This will not retry but will call the failed_callback in case of failure .. seealso:: :py:class:`apnsclient.apns.Message` for a better description of the parameters :param tokens: The tokens to send to :param alert: The alert message :param badge: The badge to show :param sound: The sound to play :param expiry: A timestamp when message will expire :param payload: The payload :param extra: Extra info """ connection = self.get_connection() if connection is None: return None if not tokens: return False message = Message(tokens, alert, badge, sound, expiry, payload, content_available, **extra) srv = APNs(connection) res = srv.send(message) if res.failed and self.failed_callback: for key, reason in res.failed.iteritems(): self.failed_callback(key, reason) return res
def _do_push_to_apns_service(user, message, apns_connection): # type: (UserProfile, Message, Connection) -> None if not apns_connection: logging.info( "Not delivering APNS message %s to user %s due to missing connection" % (message, user)) return apns_client = APNs(apns_connection) ret = apns_client.send(message) if not ret: logging.warning( "APNS: Failed to send push notification for clients %s" % (message.tokens, )) return for token, reason in ret.failed.items(): code, errmsg = reason b64_token = hex_to_b64(token) logging.warning( "APNS: Failed to deliver APNS notification to %s, reason: %s" % (b64_token, errmsg)) if code == 8: # Invalid Token, remove from our database logging.warning( "APNS: Removing token from database due to above failure") PushDeviceToken.objects.get(user=user, token=b64_token).delete() # Check failures not related to devices. for code, errmsg in ret.errors: logging.warning("APNS: Unknown error when delivering APNS: %s" % (errmsg, )) if ret.needs_retry(): logging.warning("APNS: delivery needs a retry, trying again") retry_msg = ret.retry() ret = apns_client.send(retry_msg) for code, errmsg in ret.errors: logging.warning("APNS: Unknown error when delivering APNS: %s" % (errmsg, ))
def send_notification(self, list_tokens, message_title="", message_body="", badge=1): """ push notifications to apns server, used only with push_connection=True :param message_body: :param message_title: :param list_tokens: all tokens to send notifications :param badge: number of something new in server """ service = APNs(self.connection) try: res = service.send(Message(list_tokens, alert={"title": message_title, "body": message_body}, badge=badge)) except: print "Can't connect to APNs, looks like network is down" else: for token, reason in res.failed.items(): code, errmsg = reason print "Device failed: {0}, reason: {1}".format(token, errmsg) for code, errmsg in res.errors: print "Error: {}".format(errmsg) if res.needs_retry(): res.retry()
def connect_apns(cls, appid): logging.debug("connecting apns") p12, secret, timestamp = cls.get_p12(appid) if not p12: return None if sandbox: pem_file = "/tmp/app_%s_sandbox_%s.pem" % (appid, timestamp) address = 'push_sandbox' else: pem_file = "/tmp/app_%s_%s.pem" % (appid, timestamp) address = 'push_production' if not os.path.isfile(pem_file): pem = cls.gen_pem(p12, secret) f = open(pem_file, "wb") f.write(pem) f.close() session = Session(read_tail_timeout=1) conn = session.get_connection(address, cert_file=pem_file) apns = APNs(conn) return apns
def test_feedback(self): backend = DummyBackend(feedback=5) session = Session(pool=backend) feed_con = session.new_connection("feedback_production", cert_string="certificate") srv = APNs(feed_con) self.assertEqual(len(list(srv.feedback())), 5)
class ApnsFeedback(QApplication): name = "feedbacker" version = "v0.1" def init_app(self): super(ApnsFeedback, self).init_app() # init log logging.setup("feedbacker") LOG.info("apns feedback service run.") # init apns self._init_apns() # init db self.pms = PushMsgService() def run(self): self.service = APNs(self.apns_conn) self._fetch_invalid_tokens() def _init_apns(self): """Obtain new connection to APNs. This method will not re-use existing connection from the pool. The connection will be closed after use. Unlike: `get_connection` this method does not cache the connection. Use it to fetch feedback from APNs and then close when you are done. """ run_path = os.getcwd() cert_file = "%s/%s/%s" %( run_path, CONF.IOS.cert_child_path, CONF.IOS.cert_file_name ) try: session = Session() self.apns_conn = session.new_connection( address=CONF.IOS.feedback_address, cert_file=cert_file, passphrase=CONF.IOS.cert_passphrase ) except Exception as _ex: LOG.error("init feedback non-cached connection exception: %s" % str(_ex)) def _fetch_invalid_tokens(self): """Fetch feedback from APNs. Feedback service return all invaild tokens for once run. Result sytle: ('invalid token', datetime.datetime.now()) """ try: for token, when in self.service.feedback(): LOG.info("when(%s) find invalid token: %s" % (when, token)) # update token not available status = self.pms.DEVICE_NOT_AVALIABLE self.pms.update_device_available_status(token, status) except Exception as _ex: LOG.error("connect to apns error: %s" % str(_ex))
def run(self): self.service = APNs(self.apns_conn) self._fetch_invalid_tokens()
#!/usr/bin/env python # -*- coding: utf-8 -*- # # Author: jinlong.yang # from apnsclient import ( Session, APNs ) from pprint import pprint session = Session() conn =session.new_connection("feedback_sandbox", cert_file="apns.pem", passphrase="1234") service = APNs(conn) pprint(vars(service)) try: # on any IO failure after successfull connection this generator # will simply stop iterating. you will pick the rest of the tokens # during next feedback session. # feedback的接口取到的是上次推送的过程中出现的已卸载应用的设备token,而且获取一次之后就会清空 for token, when in service.feedback(): # every time a devices sends you a token, you should store # {token: given_token, last_update: datetime.datetime.now()}) print token print when # the token wasn't updated after the failure has # been reported, so the token is invalid and you should
def feedback(self): connection = self.get_connection() if connection is None: return None srv = APNs(connection) return srv.feedback()
# from apnsclient import ( Session, Message, APNs ) from pprint import pprint # device token and private key's passwd deviceToken = '097271a3744d951fe233729b649b0d5bcdfbf7e0c8c10e11fa99d02e5cfa87ac'; invalid_deviceToken = '097271a3744d951fe233729b649b0d5bcdfbf7e0c8c10e11fa99d02e5cfa87ed'; passphrase = '1234'; # 使用session对象来创建一个连接池 session = Session() conn = session.get_connection("push_sandbox", cert_file="apns.pem", passphrase=passphrase) # 发送推送和得到反馈 tokenList = [] tokenList.append(deviceToken) #tokenList.append(invalid_deviceToken) msg = Message(tokenList, alert="use python send", badge=10) # send message service = APNs(conn) res = service.send(msg) pprint(vars(res)) print res.message.__dict__