mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Reformat code with automated tools
Apply standardized code style
This commit is contained in:
@@ -2,11 +2,18 @@ import json
|
||||
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
from .base import AnymailBaseWebhookView
|
||||
from ..exceptions import AnymailConfigurationError
|
||||
from ..inbound import AnymailInboundMessage
|
||||
from ..signals import inbound, tracking, AnymailInboundEvent, AnymailTrackingEvent, EventType, RejectReason
|
||||
from ..utils import getfirst, EmailAddress
|
||||
from ..signals import (
|
||||
AnymailInboundEvent,
|
||||
AnymailTrackingEvent,
|
||||
EventType,
|
||||
RejectReason,
|
||||
inbound,
|
||||
tracking,
|
||||
)
|
||||
from ..utils import EmailAddress, getfirst
|
||||
from .base import AnymailBaseWebhookView
|
||||
|
||||
|
||||
class PostmarkBaseWebhookView(AnymailBaseWebhookView):
|
||||
@@ -15,7 +22,7 @@ class PostmarkBaseWebhookView(AnymailBaseWebhookView):
|
||||
esp_name = "Postmark"
|
||||
|
||||
def parse_events(self, request):
|
||||
esp_event = json.loads(request.body.decode('utf-8'))
|
||||
esp_event = json.loads(request.body.decode("utf-8"))
|
||||
return [self.esp_to_anymail_event(esp_event)]
|
||||
|
||||
def esp_to_anymail_event(self, esp_event):
|
||||
@@ -29,40 +36,44 @@ class PostmarkTrackingWebhookView(PostmarkBaseWebhookView):
|
||||
|
||||
event_record_types = {
|
||||
# Map Postmark event RecordType --> Anymail normalized event type
|
||||
'Bounce': EventType.BOUNCED, # but check Type field for further info (below)
|
||||
'Click': EventType.CLICKED,
|
||||
'Delivery': EventType.DELIVERED,
|
||||
'Open': EventType.OPENED,
|
||||
'SpamComplaint': EventType.COMPLAINED,
|
||||
'SubscriptionChange': EventType.UNSUBSCRIBED,
|
||||
'Inbound': EventType.INBOUND, # future, probably
|
||||
"Bounce": EventType.BOUNCED, # but check Type field for further info (below)
|
||||
"Click": EventType.CLICKED,
|
||||
"Delivery": EventType.DELIVERED,
|
||||
"Open": EventType.OPENED,
|
||||
"SpamComplaint": EventType.COMPLAINED,
|
||||
"SubscriptionChange": EventType.UNSUBSCRIBED,
|
||||
"Inbound": EventType.INBOUND, # future, probably
|
||||
}
|
||||
|
||||
event_types = {
|
||||
# Map Postmark bounce/spam event Type --> Anymail normalized (event type, reject reason)
|
||||
'HardBounce': (EventType.BOUNCED, RejectReason.BOUNCED),
|
||||
'Transient': (EventType.DEFERRED, None),
|
||||
'Unsubscribe': (EventType.UNSUBSCRIBED, RejectReason.UNSUBSCRIBED),
|
||||
'Subscribe': (EventType.SUBSCRIBED, None),
|
||||
'AutoResponder': (EventType.AUTORESPONDED, None),
|
||||
'AddressChange': (EventType.AUTORESPONDED, None),
|
||||
'DnsError': (EventType.DEFERRED, None), # "temporary DNS error"
|
||||
'SpamNotification': (EventType.COMPLAINED, RejectReason.SPAM),
|
||||
'OpenRelayTest': (EventType.DEFERRED, None), # Receiving MTA is testing Postmark
|
||||
'Unknown': (EventType.UNKNOWN, None),
|
||||
'SoftBounce': (EventType.BOUNCED, RejectReason.BOUNCED), # might also receive HardBounce later
|
||||
'VirusNotification': (EventType.BOUNCED, RejectReason.OTHER),
|
||||
'ChallengeVerification': (EventType.AUTORESPONDED, None),
|
||||
'BadEmailAddress': (EventType.REJECTED, RejectReason.INVALID),
|
||||
'SpamComplaint': (EventType.COMPLAINED, RejectReason.SPAM),
|
||||
'ManuallyDeactivated': (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
'Unconfirmed': (EventType.REJECTED, None),
|
||||
'Blocked': (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
'SMTPApiError': (EventType.FAILED, None), # could occur if user also using Postmark SMTP directly
|
||||
'InboundError': (EventType.INBOUND_FAILED, None),
|
||||
'DMARCPolicy': (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
'TemplateRenderingFailed': (EventType.FAILED, None),
|
||||
'ManualSuppression': (EventType.UNSUBSCRIBED, RejectReason.UNSUBSCRIBED),
|
||||
# Map Postmark bounce/spam event Type
|
||||
# --> Anymail normalized (event type, reject reason)
|
||||
"HardBounce": (EventType.BOUNCED, RejectReason.BOUNCED),
|
||||
"Transient": (EventType.DEFERRED, None),
|
||||
"Unsubscribe": (EventType.UNSUBSCRIBED, RejectReason.UNSUBSCRIBED),
|
||||
"Subscribe": (EventType.SUBSCRIBED, None),
|
||||
"AutoResponder": (EventType.AUTORESPONDED, None),
|
||||
"AddressChange": (EventType.AUTORESPONDED, None),
|
||||
"DnsError": (EventType.DEFERRED, None), # "temporary DNS error"
|
||||
"SpamNotification": (EventType.COMPLAINED, RejectReason.SPAM),
|
||||
# Receiving MTA is testing Postmark:
|
||||
"OpenRelayTest": (EventType.DEFERRED, None),
|
||||
"Unknown": (EventType.UNKNOWN, None),
|
||||
# might also receive HardBounce later:
|
||||
"SoftBounce": (EventType.BOUNCED, RejectReason.BOUNCED),
|
||||
"VirusNotification": (EventType.BOUNCED, RejectReason.OTHER),
|
||||
"ChallengeVerification": (EventType.AUTORESPONDED, None),
|
||||
"BadEmailAddress": (EventType.REJECTED, RejectReason.INVALID),
|
||||
"SpamComplaint": (EventType.COMPLAINED, RejectReason.SPAM),
|
||||
"ManuallyDeactivated": (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
"Unconfirmed": (EventType.REJECTED, None),
|
||||
"Blocked": (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
# could occur if user also using Postmark SMTP directly:
|
||||
"SMTPApiError": (EventType.FAILED, None),
|
||||
"InboundError": (EventType.INBOUND_FAILED, None),
|
||||
"DMARCPolicy": (EventType.REJECTED, RejectReason.BLOCKED),
|
||||
"TemplateRenderingFailed": (EventType.FAILED, None),
|
||||
"ManualSuppression": (EventType.UNSUBSCRIBED, RejectReason.UNSUBSCRIBED),
|
||||
}
|
||||
|
||||
def esp_to_anymail_event(self, esp_event):
|
||||
@@ -70,7 +81,7 @@ class PostmarkTrackingWebhookView(PostmarkBaseWebhookView):
|
||||
try:
|
||||
esp_record_type = esp_event["RecordType"]
|
||||
except KeyError:
|
||||
if 'FromFull' in esp_event:
|
||||
if "FromFull" in esp_event:
|
||||
# This is an inbound event
|
||||
event_type = EventType.INBOUND
|
||||
else:
|
||||
@@ -81,59 +92,65 @@ class PostmarkTrackingWebhookView(PostmarkBaseWebhookView):
|
||||
if event_type == EventType.INBOUND:
|
||||
raise AnymailConfigurationError(
|
||||
"You seem to have set Postmark's *inbound* webhook "
|
||||
"to Anymail's Postmark *tracking* webhook URL.")
|
||||
"to Anymail's Postmark *tracking* webhook URL."
|
||||
)
|
||||
|
||||
if event_type in (EventType.BOUNCED, EventType.COMPLAINED):
|
||||
# additional info is in the Type field
|
||||
try:
|
||||
event_type, reject_reason = self.event_types[esp_event['Type']]
|
||||
event_type, reject_reason = self.event_types[esp_event["Type"]]
|
||||
except KeyError:
|
||||
pass
|
||||
if event_type == EventType.UNSUBSCRIBED:
|
||||
if esp_event['SuppressSending']:
|
||||
if esp_event["SuppressSending"]:
|
||||
# Postmark doesn't provide a way to distinguish between
|
||||
# explicit unsubscribes and bounces
|
||||
try:
|
||||
event_type, reject_reason = self.event_types[esp_event['SuppressionReason']]
|
||||
event_type, reject_reason = self.event_types[
|
||||
esp_event["SuppressionReason"]
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
event_type, reject_reason = self.event_types['Subscribe']
|
||||
event_type, reject_reason = self.event_types["Subscribe"]
|
||||
|
||||
recipient = getfirst(esp_event, ['Email', 'Recipient'], None) # Email for bounce; Recipient for open
|
||||
# Email for bounce; Recipient for open:
|
||||
recipient = getfirst(esp_event, ["Email", "Recipient"], None)
|
||||
|
||||
try:
|
||||
timestr = getfirst(esp_event, ['DeliveredAt', 'BouncedAt', 'ReceivedAt', 'ChangedAt'])
|
||||
timestr = getfirst(
|
||||
esp_event, ["DeliveredAt", "BouncedAt", "ReceivedAt", "ChangedAt"]
|
||||
)
|
||||
except KeyError:
|
||||
timestamp = None
|
||||
else:
|
||||
timestamp = parse_datetime(timestr)
|
||||
|
||||
try:
|
||||
event_id = str(esp_event['ID']) # only in bounce events
|
||||
event_id = str(esp_event["ID"]) # only in bounce events
|
||||
except KeyError:
|
||||
event_id = None
|
||||
|
||||
metadata = esp_event.get('Metadata', {})
|
||||
metadata = esp_event.get("Metadata", {})
|
||||
try:
|
||||
tags = [esp_event['Tag']]
|
||||
tags = [esp_event["Tag"]]
|
||||
except KeyError:
|
||||
tags = []
|
||||
|
||||
return AnymailTrackingEvent(
|
||||
description=esp_event.get('Description', None),
|
||||
description=esp_event.get("Description", None),
|
||||
esp_event=esp_event,
|
||||
event_id=event_id,
|
||||
event_type=event_type,
|
||||
message_id=esp_event.get('MessageID', None),
|
||||
message_id=esp_event.get("MessageID", None),
|
||||
metadata=metadata,
|
||||
mta_response=esp_event.get('Details', None),
|
||||
mta_response=esp_event.get("Details", None),
|
||||
recipient=recipient,
|
||||
reject_reason=reject_reason,
|
||||
tags=tags,
|
||||
timestamp=timestamp,
|
||||
user_agent=esp_event.get('UserAgent', None),
|
||||
click_url=esp_event.get('OriginalLink', None),
|
||||
user_agent=esp_event.get("UserAgent", None),
|
||||
click_url=esp_event.get("OriginalLink", None),
|
||||
)
|
||||
|
||||
|
||||
@@ -146,12 +163,14 @@ class PostmarkInboundWebhookView(PostmarkBaseWebhookView):
|
||||
if esp_event.get("RecordType", "Inbound") != "Inbound":
|
||||
raise AnymailConfigurationError(
|
||||
"You seem to have set Postmark's *%s* webhook "
|
||||
"to Anymail's Postmark *inbound* webhook URL." % esp_event["RecordType"])
|
||||
"to Anymail's Postmark *inbound* webhook URL." % esp_event["RecordType"]
|
||||
)
|
||||
|
||||
attachments = [
|
||||
AnymailInboundMessage.construct_attachment(
|
||||
content_type=attachment["ContentType"],
|
||||
content=attachment["Content"], base64=True,
|
||||
content=attachment["Content"],
|
||||
base64=True,
|
||||
filename=attachment.get("Name", "") or None,
|
||||
content_id=attachment.get("ContentID", "") or None,
|
||||
)
|
||||
@@ -160,11 +179,15 @@ class PostmarkInboundWebhookView(PostmarkBaseWebhookView):
|
||||
|
||||
message = AnymailInboundMessage.construct(
|
||||
from_email=self._address(esp_event.get("FromFull")),
|
||||
to=', '.join([self._address(to) for to in esp_event.get("ToFull", [])]),
|
||||
cc=', '.join([self._address(cc) for cc in esp_event.get("CcFull", [])]),
|
||||
# bcc? Postmark specs this for inbound events, but it's unclear how it could occur
|
||||
to=", ".join([self._address(to) for to in esp_event.get("ToFull", [])]),
|
||||
cc=", ".join([self._address(cc) for cc in esp_event.get("CcFull", [])]),
|
||||
# bcc? Postmark specs this for inbound events,
|
||||
# but it's unclear how it could occur
|
||||
subject=esp_event.get("Subject", ""),
|
||||
headers=[(header["Name"], header["Value"]) for header in esp_event.get("Headers", [])],
|
||||
headers=[
|
||||
(header["Name"], header["Value"])
|
||||
for header in esp_event.get("Headers", [])
|
||||
],
|
||||
text=esp_event.get("TextBody", ""),
|
||||
html=esp_event.get("HtmlBody", ""),
|
||||
attachments=attachments,
|
||||
@@ -176,36 +199,48 @@ class PostmarkInboundWebhookView(PostmarkBaseWebhookView):
|
||||
if "ReplyTo" in esp_event and "Reply-To" not in message:
|
||||
message["Reply-To"] = esp_event["ReplyTo"]
|
||||
|
||||
# Postmark doesn't have a separate envelope-sender field, but it can be extracted
|
||||
# from the Received-SPF header that Postmark will have added:
|
||||
if len(message.get_all("Received-SPF", [])) == 1: # (more than one? someone's up to something weird)
|
||||
# Postmark doesn't have a separate envelope-sender field, but it can
|
||||
# be extracted from the Received-SPF header that Postmark will have added.
|
||||
# (More than one Received-SPF? someone's up to something weird?)
|
||||
if len(message.get_all("Received-SPF", [])) == 1:
|
||||
received_spf = message["Received-SPF"].lower()
|
||||
if received_spf.startswith("pass") or received_spf.startswith("neutral"): # not fail/softfail
|
||||
message.envelope_sender = message.get_param("envelope-from", None, header="Received-SPF")
|
||||
if received_spf.startswith( # not fail/softfail
|
||||
"pass"
|
||||
) or received_spf.startswith("neutral"):
|
||||
message.envelope_sender = message.get_param(
|
||||
"envelope-from", None, header="Received-SPF"
|
||||
)
|
||||
|
||||
message.envelope_recipient = esp_event.get("OriginalRecipient", None)
|
||||
message.stripped_text = esp_event.get("StrippedTextReply", None)
|
||||
|
||||
message.spam_detected = message.get('X-Spam-Status', 'No').lower() == 'yes'
|
||||
message.spam_detected = message.get("X-Spam-Status", "No").lower() == "yes"
|
||||
try:
|
||||
message.spam_score = float(message['X-Spam-Score'])
|
||||
message.spam_score = float(message["X-Spam-Score"])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
return AnymailInboundEvent(
|
||||
event_type=EventType.INBOUND,
|
||||
timestamp=None, # Postmark doesn't provide inbound event timestamp
|
||||
event_id=esp_event.get("MessageID", None), # Postmark uuid, different from Message-ID mime header
|
||||
# Postmark doesn't provide inbound event timestamp:
|
||||
timestamp=None,
|
||||
# Postmark uuid, different from Message-ID mime header:
|
||||
event_id=esp_event.get("MessageID", None),
|
||||
esp_event=esp_event,
|
||||
message=message,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _address(full):
|
||||
"""Return an formatted email address from a Postmark inbound {From,To,Cc}Full dict"""
|
||||
"""
|
||||
Return a formatted email address
|
||||
from a Postmark inbound {From,To,Cc}Full dict
|
||||
"""
|
||||
if full is None:
|
||||
return ""
|
||||
return str(EmailAddress(
|
||||
display_name=full.get('Name', ""),
|
||||
addr_spec=full.get("Email", ""),
|
||||
))
|
||||
return str(
|
||||
EmailAddress(
|
||||
display_name=full.get("Name", ""),
|
||||
addr_spec=full.get("Email", ""),
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user