@@ -0,0 +1,2742 @@
+from __future__ import absolute_import%0A%0Afrom zerver.models import UserProfile, AppleDeviceToken%0Afrom zerver.lib.timestamp import timestamp_to_datetime%0Afrom zerver.decorator import statsd_increment%0A%0Afrom apnsclient import Session, Connection, Message, APNs%0A%0Afrom django.conf import settings%0A%0Aimport base64, binascii, logging%0A%0A# Maintain a long-lived Session object to avoid having to re-SSL-handshake%0A# for each request%0Asession = Session()%0Aconnection = session.get_connection(settings.APNS_SANDBOX, cert_file=settings.APNS_CERT_FILE)%0A%0A%0Adef num_push_devices_for_user(user_profile):%0A    return AppleDeviceToken.objects.filter(user=user_profile).count()%0A%0A# We store the token as b64, but apns-client wants hex strings%0Adef b64_to_hex(data):%0A    return binascii.hexlify(base64.b64decode(data))%0A%0Adef hex_to_b64(data):%0A    return base64.b64encode(binascii.unhexlify(data))%0A%0A# Send a push notification to the desired clients%0A# extra_data is a dict that will be passed to the%0A# mobile app%0A@statsd_increment(%22apple_push_notification%22)%0Adef send_apple_push_notification(user, alert, **extra_data):%0A    # Sends a push notifications to all the PushClients%0A    # Only Apple Push Notifications clients are supported at the moment%0A    tokens = %5Bb64_to_hex(device.token) for device in AppleDeviceToken.objects.filter(user=user)%5D%0A%0A    logging.info(%22Sending apple push notification to devices: %25s%22 %25 (tokens,))%0A    message = Message(tokens, alert=alert, **extra_data)%0A%0A    apns_client = APNs(connection)%0A    ret = apns_client.send(message)%0A    if not ret:%0A       logging.warning(%22Failed to send push notification for clients %25s%22 %25 (tokens,))%0A       return%0A%0A    for token, reason in ret.failed.items():%0A        code, errmsg = reason%0A        logging.warning(%22Failed to deliver APNS notification to %25s, reason: %25s%22 %25 (token, errmsg))%0A%0A    # Check failures not related to devices.%0A    for code, errmsg in ret.errors:%0A        logging.warning(%22Unknown error when delivering APNS: %25s%22 %25  (errmsg,))%0A%0A    if ret.needs_retry():%0A        # TODO handle retrying by potentially scheduling a background job%0A        # or re-queueing%0A        logging.warning(%22APNS delivery needs a retry but ignoring%22)%0A%0A# NOTE: This is used by the check_apns_tokens manage.py command. Do not call it otherwise, as the%0A# feedback() call can take up to 15s%0Adef check_apns_feedback():%0A    apns_client = APNs(connection, tail_timeout=20)%0A%0A    for token, since in apns_client.feedback():%0A        since_date = timestamp_to_datetime(since)%0A        logging.info(%22Found unavailable token %25s, unavailable since %25s%22 %25 (token, since_date))%0A%0A        AppleDeviceToken.objects.filter(token=hex_to_b64(token), last_updates__lt=since_date).delete()%0A    logging.info(%22Finished checking feedback for stale tokens%22)%0A

Add a push notification module to handle mobile client notifications