def send_apple_push_notification(user_id: int, devices: List[DeviceToken], payload_data: Dict[str, Any]) -> None: logging.info("APNs: Sending notification for user %d to %d devices", user_id, len(devices)) payload = APNsPayload(**modernize_apns_payload(payload_data)) expiration = int(time.time() + 24 * 3600) client = get_apns_client() retries_left = APNS_MAX_RETRIES for device in devices: # TODO obviously this should be made to actually use the async def attempt_send() -> Optional[str]: stream_id = client.send_notification_async( device.token, payload, topic='org.zulip.Zulip', expiration=expiration) try: return client.get_notification_result(stream_id) except HTTP20Error as e: logging.warning("APNs: HTTP error sending for user %d to device %s: %s", user_id, device.token, e.__class__.__name__) return None result = attempt_send() while result is None and retries_left > 0: retries_left -= 1 result = attempt_send() if result is None: result = "HTTP error, retries exhausted" if result == 'Success': logging.info("APNs: Success sending for user %d to device %s", user_id, device.token) else: logging.warning("APNs: Failed to send for user %d to device %s: %s", user_id, device.token, result)
def send_apple_push_notification(user_id: int, devices: List[DeviceToken], payload_data: Dict[str, Any], remote: bool = False) -> None: client = get_apns_client() if client is None: logging.warning( "APNs: Dropping a notification because nothing configured. " "Set PUSH_NOTIFICATION_BOUNCER_URL (or APNS_CERT_FILE).") return if remote: DeviceTokenClass = RemotePushDeviceToken else: DeviceTokenClass = PushDeviceToken logging.info("APNs: Sending notification for user %d to %d devices", user_id, len(devices)) payload = APNsPayload(**modernize_apns_payload(payload_data)) expiration = int(time.time() + 24 * 3600) retries_left = APNS_MAX_RETRIES for device in devices: # TODO obviously this should be made to actually use the async def attempt_send() -> Optional[str]: stream_id = client.send_notification_async(device.token, payload, topic='org.zulip.Zulip', expiration=expiration) try: return client.get_notification_result(stream_id) except HTTP20Error as e: logging.warning( "APNs: HTTP error sending for user %d to device %s: %s", user_id, device.token, e.__class__.__name__) return None result = attempt_send() while result is None and retries_left > 0: retries_left -= 1 result = attempt_send() if result is None: result = "HTTP error, retries exhausted" if result == 'Success': logging.info("APNs: Success sending for user %d to device %s", user_id, device.token) elif result in [ "Unregistered", "BadDeviceToken", "DeviceTokenNotForTopic" ]: logging.info("APNs: Removing invalid/expired token %s (%s)" % (device.token, result)) # We remove all entries for this token (There # could be multiple for different Zulip servers). DeviceTokenClass.objects.filter( token=device.token, kind=DeviceTokenClass.APNS).delete() else: logging.warning( "APNs: Failed to send for user %d to device %s: %s", user_id, device.token, result)
def send_apple_push_notification(user_id, devices, payload_data): # type: (int, List[DeviceToken], Dict[str, Any]) -> None if not devices: return logging.info("APNs: Sending notification for user %d to %d devices", user_id, len(devices)) payload = APNsPayload(**payload_data) expiration = int(time.time() + 24 * 3600) client = get_apns_client() for device in devices: # TODO obviously this should be made to actually use the async stream_id = client.send_notification_async(device.token, payload, topic='org.zulip.Zulip', expiration=expiration) result = client.get_notification_result(stream_id) if result == 'Success': logging.info("APNs: Success sending for user %d to device %s", user_id, device.token) else: logging.warn("APNs: Failed to send for user %d to device %s: %s", user_id, device.token, result)
def send_apple_push_notification(user_id: int, devices: List[DeviceToken], payload_data: Dict[str, Any], remote: bool = False) -> None: # We lazily do the APNS imports as part of optimizing Zulip's base # import time; since these are only needed in the push # notification queue worker, it's best to only import them in the # code that needs them. from apns2.payload import Payload as APNsPayload from apns2.client import APNsClient from hyper.http20.exceptions import HTTP20Error client = get_apns_client() # type: APNsClient if client is None: logging.warning( "APNs: Dropping a notification because nothing configured. " "Set PUSH_NOTIFICATION_BOUNCER_URL (or APNS_CERT_FILE).") return if remote: DeviceTokenClass = RemotePushDeviceToken else: DeviceTokenClass = PushDeviceToken logging.info("APNs: Sending notification for user %d to %d devices", user_id, len(devices)) payload = APNsPayload(**modernize_apns_payload(payload_data)) expiration = int(time.time() + 24 * 3600) retries_left = APNS_MAX_RETRIES for device in devices: # TODO obviously this should be made to actually use the async def attempt_send() -> Optional[str]: stream_id = client.send_notification_async(device.token, payload, topic='org.zulip.Zulip', expiration=expiration) try: return client.get_notification_result(stream_id) except HTTP20Error as e: logging.warning( "APNs: HTTP error sending for user %d to device %s: %s", user_id, device.token, e.__class__.__name__) return None result = attempt_send() while result is None and retries_left > 0: retries_left -= 1 result = attempt_send() if result is None: result = "HTTP error, retries exhausted" if result[0] == "Unregistered": # For some reason, "Unregistered" result values have a # different format, as a tuple of the pair ("Unregistered", 12345132131). result = result[0] # type: ignore # APNS API is inconsistent if result == 'Success': logging.info("APNs: Success sending for user %d to device %s", user_id, device.token) elif result in [ "Unregistered", "BadDeviceToken", "DeviceTokenNotForTopic" ]: logging.info("APNs: Removing invalid/expired token %s (%s)" % (device.token, result)) # We remove all entries for this token (There # could be multiple for different Zulip servers). DeviceTokenClass.objects.filter( token=device.token, kind=DeviceTokenClass.APNS).delete() else: logging.warning( "APNs: Failed to send for user %d to device %s: %s", user_id, device.token, result)