djacobs / PyAPNs

Python library for interacting with the Apple Push Notification service (APNs)
http://pypi.python.org/pypi/apns/
MIT License
1.22k stars 377 forks source link

How do we handle errors in enhanced mode? #125

Closed pcompassion closed 7 years ago

pcompassion commented 9 years ago

When there's a bad token, or connection is broken for other reasons, there seems to be a way to recover it.

I'm struggling to google how I should implement the recovery.

Wouldn't it be nice to include howto-recover-from-error in the readme?

jimhorng commented 9 years ago

@pcompassion, APNS should recover the connection on next message sending, please let me know if it doesn't, thanks :)

pcompassion commented 8 years ago

I must be doing something wrong then.

I tried send_message_frame below, and it won't send push after it encounters a bogus registration_id.

I find some ios devices don't receive push notification when using send_message but I have no idea how to start debugging..

 if settings.DEBUG:
     use_sandbox = True
 else:
     use_sandbox = False
 APNS_CERTIFICATE_COMBINED = settings.PUSH_NOTIFICATIONS_SETTINGS['APNS_CERTIFICATE_COMBINED']
 # https://github.com/djacobs/PyAPNs/issues/108
 apns = APNs(use_sandbox=use_sandbox, cert_file=APNS_CERTIFICATE_COMBINED, enhanced=True)

    def send_message_frame(self, message, **kwargs):
         frame = Frame()
         expiry = time.time() + 3600
         priority = 10

         payload = Payload(
             alert=message, sound=kwargs.pop("sound", "default"),
             custom=kwargs.pop("extra", None)
         )

         for registration_id in self.values_list("registration_id", flat=True):
             try:
                 identifier = registration_id
                 frame.add_item(registration_id, payload, identifier, expiry, priority)
-            except TypeError:
                 logger.exception("apns typeerror")
                 pass

         apns.gateway_server.send_notification_multiple(frame)

     def send_message(self, message, **kwargs):

         payload = Payload(
             alert=message, sound=kwargs.pop("sound", "default"),
             custom=kwargs.pop("extra", None)
         )

         # for registration_id in self.values_list("registration_id", flat=True):
         for device in self.all():
             registration_id = device.registration_id
             identifier = device.id
             try:
                 apns.gateway_server.send_notification(registration_id, payload, identifier=identifier)
             except TypeError:
                 # (3297, u'UIUserNotificationSettings:0x1702\
                 # 28ca0;types:(none);')
                 # self.filter(id=device.id).delete()
                 logger.exception("apns typeerror")
jimhorng commented 8 years ago

@pcompassion , your identifier must be unique for each message in order to let PyAPNs identify which message to re-send, please refer to https://github.com/djacobs/PyAPNs#enhanced-message-with-immediate-error-response , the document might not clear enough, which is already address in this PR #128

pcompassion commented 8 years ago

I tried unique identifier (fixed the above code).

I'd like to see something like got error-response from APNS:(8, 1) rebuilding connection to APNS resending 9 notifications to APNS resending notification with id:2 to APNS resending notification with id:3 to APNS resending notification with id:4 to APNS

But I don't see any logging messages, I only know some of the devices which have valid registration_id didn't receive push notification (when using bulk-send) I know his registration-id is fine because I can send a push-notification when he is the only recipient.

From your answer, it seems I shouldn't see someone who has a legitamate registration_id not receiving push, but I do see such case.
How should I go about debugging it?

jimhorng commented 8 years ago

@pcompassion , Thanks for your feedbacks,

FYR

kjvenky commented 8 years ago

@jimhorng Can you share the code of how you did the mapping?

jimhorng commented 8 years ago

@kjvenky, My example for maintain sent message and reg_id

def _handle_ios_error_response(error_response):
    status_code = error_response[apns.ER_STATUS]
    notification_id = error_response[apns.ER_IDENTIFER]
    do_some_error_handling

ios_sent_queue = OrderedDict()
MAX_QUEUE = 100000
apns_sender = apns.APNs()
apns_sender.gateway_server.register_response_listener(_handle_ios_error_response)
if len(ios_sent_queue) >= MAX_QUEUE:
    ios_sent_queue.popitem(last=False)
ios_sent_queue.update({notification_id: reg_id})
kjvenky commented 8 years ago

@jimhorng the issue with this is at this line "do_some_error_handling" I am not able to able to map reg_id to the notifications_id in the _handle_iso_error_response. Did you try that? I want to mark the red_ids as invalid in the data base. Where are you sending the notifications in this. Any other urls with sample you know?

jimhorng commented 8 years ago

@kjvenky , as you can see at ios_sent_queue.update({notification_id: reg_id}), it maintains a notification id and reg_id mapping, so that you can just ios_sent_queue[notification_id] to get reg_id, and then query for reg_id in DB to update relevant data :)

kjvenky commented 8 years ago

I think in this case it works well because in the error handling function will look in the next scope level which has the mapping.

I am trying to put notification sending in a separate send_notification(mapping is defined in this) function in which case there is no way to pass the mapping from the send_notification to the ios_response_handler

kjvenky commented 8 years ago

Hey I figured out. I think i should just define a function in a function like this:

def send_notifictions():

Do mapping

def ios_handler:
    # Do handling of errors
# Send notifications
kjvenky commented 8 years ago

@jimhorng Any suggestions on how to collect all these errors in async and do something on all of them at once?