mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
77 lines
3.1 KiB
Python
77 lines
3.1 KiB
Python
import json
|
|
from datetime import datetime
|
|
|
|
from django.utils.timezone import utc
|
|
|
|
from .base import AnymailBaseWebhookView
|
|
from ..signals import AnymailTrackingEvent, EventType, RejectReason, tracking
|
|
|
|
|
|
class SendinBlueTrackingWebhookView(AnymailBaseWebhookView):
|
|
"""Handler for SendinBlue delivery and engagement tracking webhooks"""
|
|
|
|
esp_name = "SendinBlue"
|
|
signal = tracking
|
|
|
|
def parse_events(self, request):
|
|
esp_event = json.loads(request.body.decode('utf-8'))
|
|
return [self.esp_to_anymail_event(esp_event)]
|
|
|
|
# SendinBlue's webhook payload data doesn't seem to be documented anywhere.
|
|
# There's a list of webhook events at https://apidocs.sendinblue.com/webhooks/#3.
|
|
event_types = {
|
|
# Map SendinBlue event type: Anymail normalized (event type, reject reason)
|
|
"request": (EventType.QUEUED, None), # received even if message won't be sent (e.g., before "blocked")
|
|
"delivered": (EventType.DELIVERED, None),
|
|
"hard_bounce": (EventType.BOUNCED, RejectReason.BOUNCED),
|
|
"soft_bounce": (EventType.BOUNCED, RejectReason.BOUNCED),
|
|
"blocked": (EventType.REJECTED, RejectReason.BLOCKED),
|
|
"spam": (EventType.COMPLAINED, RejectReason.SPAM),
|
|
"invalid_email": (EventType.BOUNCED, RejectReason.INVALID),
|
|
"deferred": (EventType.DEFERRED, None),
|
|
"opened": (EventType.OPENED, None), # see also unique_opened below
|
|
"click": (EventType.CLICKED, None),
|
|
"unsubscribe": (EventType.UNSUBSCRIBED, None),
|
|
"list_addition": (EventType.SUBSCRIBED, None), # shouldn't occur for transactional messages
|
|
"unique_opened": (EventType.OPENED, None), # you'll *also* receive an "opened"
|
|
}
|
|
|
|
def esp_to_anymail_event(self, esp_event):
|
|
esp_type = esp_event.get("event")
|
|
event_type, reject_reason = self.event_types.get(esp_type, (EventType.UNKNOWN, None))
|
|
recipient = esp_event.get("email")
|
|
|
|
try:
|
|
# SendinBlue supplies "ts", "ts_event" and "date" fields, which seem to be based on the
|
|
# timezone set in the account preferences (and possibly with inconsistent DST adjustment).
|
|
# "ts_epoch" is the only field that seems to be consistently UTC; it's in milliseconds
|
|
timestamp = datetime.fromtimestamp(esp_event["ts_epoch"] / 1000.0, tz=utc)
|
|
except (KeyError, ValueError):
|
|
timestamp = None
|
|
|
|
try:
|
|
tags = [esp_event["tag"]]
|
|
except KeyError:
|
|
tags = []
|
|
|
|
try:
|
|
metadata = json.loads(esp_event["X-Mailin-custom"])
|
|
except (KeyError, TypeError):
|
|
metadata = {}
|
|
|
|
return AnymailTrackingEvent(
|
|
description=None,
|
|
esp_event=esp_event,
|
|
event_id=None, # SendinBlue doesn't provide a unique event id
|
|
event_type=event_type,
|
|
message_id=esp_event.get("message-id"),
|
|
metadata=metadata,
|
|
mta_response=esp_event.get("reason"),
|
|
recipient=recipient,
|
|
reject_reason=reject_reason,
|
|
tags=tags,
|
|
timestamp=timestamp,
|
|
user_agent=None,
|
|
click_url=esp_event.get("link"),
|
|
)
|