Example #1
0
    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)
Example #2
0
 def __init__(self):
     # apns configuration
     session = Session()
     if conf.getboolean('application', 'debug'):
         con = session.new_connection("feedback_sandbox", cert_file=conf.get('apns', 'cert_sandbox'))
     else:
         con = session.new_connection("feedback_production", cert_file=conf.get('apns', 'cert_production'))
     self.srv = APNs(con)
Example #3
0
 def __init__(self):
     # apns configuration
     self.session = Session()
     # rabbitmq configuration
     self.cm = PikaConnectionManager(username=conf.get('rabbitmq', 'username'),
                                     password=conf.get('rabbitmq', 'password'),
                                     host=conf.get('rabbitmq', 'host'),
                                     heartbeat_interval=conf.getint('rabbitmq', 'worker_heartbeat_interval'))
     self.cm.channel.exchange_declare(exchange='pns_exchange', type='direct', durable=True)
     self.cm.channel.queue_declare(queue='pns_apns_queue', durable=True)
     self.cm.channel.queue_bind(exchange='pns_exchange', queue='pns_apns_queue', routing_key='pns_apns')
     self.cm.channel.basic_qos(prefetch_count=1)
     self.cm.channel.basic_consume(self._callback, queue='pns_apns_queue')
Example #4
0
class StdIOBackendTest(Python26Mixin, unittest.TestCase):
    """ Test stdio features. """
    def setUp(self):
        self.session = Session(pool="apns_clerk.backends.stdio")

    def test_locking(self):
        """ Test thread locking mechanism """
        lock = self.session.pool.create_lock()

        self.assertIsNotNone(lock)
        self.assertTrue(hasattr(lock, "acquire"))
        self.assertTrue(hasattr(lock, "release"))

        lock.acquire()
        lock.release()

    def test_certificates(self):
        """ Test pyOpenSSL certificates. """
        cert = self.session.pool.get_certificate({
            "cert_string": CERTIFICATE,
            "key_string": PRIVATE_KEY,
            "passphrase": PRIVATE_PASS
        })

        cert2 = self.session.pool.get_certificate({
            "cert_string": (CERTIFICATE + b"\n" + PRIVATE_KEY),
            "passphrase":
            PRIVATE_PASS
        })

        self.assertIsNotNone(cert.get_context())
        self.assertEqual(cert, cert2)

    def test_outdate(self):
        # we are not allowed to do any IO in tests, so no real connections.
        # however, it is good idea to test utility functions even with empty pool.
        self.session.outdate(datetime.timedelta(seconds=60))
        self.session.shutdown()
Example #5
0
class StdIOBackendTest(Python26Mixin, unittest.TestCase):
    """ Test stdio features. """

    def setUp(self):
        self.session = Session(pool="apns_clerk.backends.stdio")

    def test_locking(self):
        """ Test thread locking mechanism """
        lock = self.session.pool.create_lock()
        self.assertIsNotNone(lock)
        self.assertTrue(hasattr(lock, "acquire"))
        self.assertTrue(hasattr(lock, "release"))
        lock.acquire()
        lock.release()

    def test_certificates(self):
        """ Test pyOpenSSL certificates. """
        cert = self.session.pool.get_certificate({
            "cert_string": CERTIFICATE,
            "key_string": PRIVATE_KEY,
            "passphrase": PRIVATE_PASS
        })

        cert2 = self.session.pool.get_certificate({
            "cert_string": (CERTIFICATE + b"\n" + PRIVATE_KEY),
            "passphrase": PRIVATE_PASS
        })

        self.assertIsNotNone(cert.get_context())
        self.assertEqual(cert, cert2)
        
    def test_outdate(self):
        # we are not allowed to do any IO in tests, so no real connections.
        # however, it is good idea to test utility functions even with empty pool.
        self.session.outdate(datetime.timedelta(seconds=60))
        self.session.shutdown()
Example #6
0
 def _push_connection(self):
     if not self.session:
         self.session = Session()
     return self.session.get_connection(address=self.push_address,
                                        cert_file=self.cert_file)
Example #7
0
class APNSProvider(NotificationsProvider):
    provider_set = 'apns-device-tokens'

    def __init__(self, cert_file, push_address="push_sandbox",
                 feedback_address="feedback_sandbox"):
        self.cert_file = cert_file
        self.push_address = push_address
        self.feedback_address = feedback_address

        self.session = None

    def _feedback_connection(self):
        return Session().new_connection(address=self.feedback_address,
                                        cert_file=self.cert_file)

    def feedback_server(self):
        con = self._feedback_connection()
        return APNs(con)

    def _push_connection(self):
        if not self.session:
            self.session = Session()
        return self.session.get_connection(address=self.push_address,
                                           cert_file=self.cert_file)

    def push_server(self):
        con = self._push_connection()
        return APNs(con)

    def handles(self, platform):
        if platform is iOS:
            return True
        else:
            return False

    def _get_all_device_tokens(self):
        return kv_store.smembers(self.provider_set)

    def notify(self, message, alert, device_tokens=None, expiry=DEFAULT_EXPIRY):
        """Send a push notification through APNs, this can be to specific
        devices or all devices we have stored in ``self.provider_set``

        APNs queues push notifications as devices become available unlike GCM
        This means you attach an expiry on each push notification. So if a push
        notification becomes irrelevant it can be considered expired.

        :param message: User provided string to be pushed to all devices.
        :param alert: Associated alert
        :param device_tokens: Pass specific device tokens to push to. If no
                              tokens are passed then all devices found in
                              ``self.provider_set`` will be pushed to.
        :param expiry: How long should APNS keep this message queued for
                       devices which are not immediately available to receive
                       the push notification.
        """
        if device_tokens is None:
            device_tokens = self._get_all_device_tokens()

        logger.info("APNS: Push notification to {0} clients. Message: {1}"
                    .format(len(device_tokens), message))

        # Return if there are no device tokens to push to
        if not device_tokens:
            logger.warning("APNs: No device_tokens to push to")
            return False
        message = Message(device_tokens, alert=message, expiry=expiry)

        server = self.push_server()
        result = server.send(message)
        self.handle_result(result)
        # Retry if we have some failures
        if result.needs_retry():
            message = result.retry()
            logger.info("APNs: Retrying for {0} device tokens."
                        .format(len(message.tokens)))
            result = server.send(message)
            self.handle_result(result)

    def handle_result(self, result):
        """Logs the errors from APNS

        Removes any tokens which cause failures.
        """
        # Check failures. Check codes in APNs reference docs.
        for token, reason in result.failed.items():
            code, errmsg = reason
            logger.warning("APNs: Device failed: {0}, reason: {1}".format(
                token, errmsg))
            self.remove_token(token)
            logger.info("APNs: Removed token: {0}".format(token))
        # Check failures not related to devices.
        for code, errmsg in result.errors:
            logger.warning("APNs: Error: {0}".format(errmsg))

    def feedback(self):
        """Feedback is a process through which APNs notifies you of clients
        becoming unavailable to receiving push notifications. Most likely this
        means they have uninstalled the app.
        """
        server = self.feedback_server()
        for token, since in server.feedback():
            kv_store.srem(self.provider_set, token)
            logger.info("APNs: Token {0} is unavailable since {1}".format(
                token, since))
Example #8
0
 def setUp(self):
     self.session = Session(pool="apns_clerk.backends.stdio")
Example #9
0
 def setUp(self):
     self.session = Session(pool="apns_clerk.backends.stdio")
Example #10
0
 def get_session(self):
     return Session()
Example #11
0
    def get_session(self, push=None, feedback=None):
        backend = DummyBackend(push=push, feedback=feedback)

        return Session(pool=backend)
Example #12
0
class APNSWorker(object):
    def __init__(self):
        # apns configuration
        self.session = Session()
        # rabbitmq configuration
        self.cm = PikaConnectionManager(username=conf.get('rabbitmq', 'username'),
                                        password=conf.get('rabbitmq', 'password'),
                                        host=conf.get('rabbitmq', 'host'),
                                        heartbeat_interval=conf.getint('rabbitmq', 'worker_heartbeat_interval'))
        self.cm.channel.exchange_declare(exchange='pns_exchange', type='direct', durable=True)
        self.cm.channel.queue_declare(queue='pns_apns_queue', durable=True)
        self.cm.channel.queue_bind(exchange='pns_exchange', queue='pns_apns_queue', routing_key='pns_apns')
        self.cm.channel.basic_qos(prefetch_count=1)
        self.cm.channel.basic_consume(self._callback, queue='pns_apns_queue')

    def start(self):
        self.cm.channel.start_consuming()

    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)
Example #13
0
logger = logging.getLogger(__name__)
from django.forms.models import model_to_dict
from django.contrib.auth import *
from service import BaseService
import json
from apns_clerk import Message, Session, APNs
from apns_clerk.apns import Result
from apns_clerk.backends.dummy import Backend as DummyBackend
from emsg_simple_api.settings import APNS_CERT_PATH,DEBUG

'''
emsg_server 回调接口文档
https://github.com/cc14514/emsg_sdk/wiki/emsg_server-%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3
'''

session = Session()
# push_sandbox 测试证书
# push_production 生产证书
con = session.get_connection("push_sandbox", cert_file=APNS_CERT_PATH)

class user_message(BaseService):
    def offline(self,body):
        '''
        离线消息回调,其中 params 是 packet
        :param body:
        :return:
        '''
        sn = body['sn']
        packet = body['params']
        # text image geo audio
        logger.info('[emsg_offline] packet = %s' % packet)
Example #14
0
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()
Example #15
0
import django.utils.timezone as dtz
logger = logging.getLogger(__name__)
from django.forms.models import model_to_dict
from django.contrib.auth import *
from service import BaseService
import json
from apns_clerk import Message, Session, APNs
from apns_clerk.apns import Result
from apns_clerk.backends.dummy import Backend as DummyBackend
from emsg_simple_api.settings import APNS_CERT_PATH, DEBUG
'''
emsg_server 回调接口文档
https://github.com/cc14514/emsg_sdk/wiki/emsg_server-%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3
'''

session = Session()
# push_sandbox 测试证书
# push_production 生产证书
con = session.get_connection("push_sandbox", cert_file=APNS_CERT_PATH)


class user_message(BaseService):
    def offline(self, body):
        '''
        离线消息回调,其中 params 是 packet
        :param body:
        :return:
        '''
        sn = body['sn']
        packet = body['params']
        # text image geo audio
Example #16
0
 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)