expo-community / expo-server-sdk-python

Exponent Server SDK
MIT License
146 stars 42 forks source link

Update documentation with publish_multiple example #61

Open samuel-andres opened 1 year ago

samuel-andres commented 1 year ago

The README.md is pretty clear on how to handle the errors when sending a single push notification, however, most of the times we will be sending the same push notification to a bunch of devices, so in that case, I think it would be greatly valuable an example of how to handle that case.

sergioisidoro commented 7 months ago

Here's something if anyone wants to take use of.

I think the trick here to match push tickets to tokens is to keep a parallel list of the same order, since the docs mention:

data will contain an array of push tickets in the same order in which the messages were sent

def push_expo_notifications(user, title, body):
    messages = []
    token_ids = []

    for device in user.devices.filter(active=True):
        messages.append(
            PushMessage(
                to=device.expo_token,
                title=title,
                body=body
            )
        )
        token_ids.append(device.id)

    try:
        responses = PushClient().publish_multiple(messages)
    except PushServerError as exc:
        # Encountered some likely formatting/validation error.
        raise
    except (ConnectionError) as exc:
        # Encountered some Connection or HTTP error.
        raise

    for index, response in enumerate(responses):
        try:
          response.validate_response()
        except DeviceNotRegisteredError:
            # Mark the push token as inactive
            UserDevice.objects.filter(id=token_ids[index]).update(active=False)
            # Or use the token from push_message - NOTE that `to` field may be a list of recipients according to the API
            UserDevice.objects.filter(expo_token=response.push_message.to).update(active=False)
        except PushTicketError as exc:
            # Encountered some other per-notification error.
            raise
YPCrumble commented 6 months ago

@sergioisidoro doesn't this just use the order to get the device token? Isn't the device token already present in each response via response.push_message.to?

sergioisidoro commented 6 months ago

I think you're right. I checked that publish returned push tickets, and completely missed that the push ticket already contained push_message in the named tuple.

Ironically, it uses the exact same method I'm using here, using the index of the response to match the push message to the push receipt 😅

https://github.com/expo-community/expo-server-sdk-python/blob/f7f131729f3ff601ec6cd91ed7cb5d4a4cb6774d/exponent_server_sdk/__init__.py#L393-L396

I'll update the example, and my own code. Thanks @YPCrumble !

Just an important caveat, that with the PRs that enable multiple senders, the to field might be a list of recipients:

[
  {
    "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "sound": "default",
    "body": "Hello world!"
  },
  {
    "to": "ExponentPushToken[yyyyyyyyyyyyyyyyyyyyyy]",
    "badge": 1,
    "body": "You've got mail"
  },
  {
    "to": [
      "ExponentPushToken[zzzzzzzzzzzzzzzzzzzzzz]",
      "ExponentPushToken[aaaaaaaaaaaaaaaaaaaaaa]"
    ],
    "body": "Breaking news!"
  }
]
YPCrumble commented 6 months ago

I think your insight that the responses are in the same order is what I needed for my use case. If I publish ten notifications, and only one fails for whatever reason, I want to retry that ticket only.