2022-09-15 14:59:13 -07:00
|
|
|
from flask import current_app, json
|
2017-03-16 18:15:49 +00:00
|
|
|
|
2018-03-16 14:00:49 +00:00
|
|
|
from app.celery.service_callback_tasks import (
|
2021-03-10 13:55:06 +00:00
|
|
|
create_complaint_callback_data,
|
2018-07-18 17:03:16 +01:00
|
|
|
create_delivery_status_callback_data,
|
2021-03-10 13:55:06 +00:00
|
|
|
send_complaint_to_service,
|
|
|
|
|
send_delivery_status_to_service,
|
2018-03-16 14:00:49 +00:00
|
|
|
)
|
2017-12-01 21:13:01 +00:00
|
|
|
from app.config import QueueNames
|
2021-03-10 13:55:06 +00:00
|
|
|
from app.dao.complaint_dao import save_complaint
|
2022-09-15 14:59:13 -07:00
|
|
|
from app.dao.notifications_dao import dao_get_notification_history_by_reference
|
2021-03-10 13:55:06 +00:00
|
|
|
from app.dao.service_callback_api_dao import (
|
|
|
|
|
get_service_complaint_callback_api_for_service,
|
|
|
|
|
get_service_delivery_status_callback_api_for_service,
|
|
|
|
|
)
|
|
|
|
|
from app.models import Complaint
|
2022-09-15 14:59:13 -07:00
|
|
|
from app.notifications.callbacks import create_complaint_callback_data
|
|
|
|
|
|
|
|
|
|
|
2022-09-20 20:11:09 -07:00
|
|
|
def determine_notification_bounce_type(ses_message):
|
2022-09-15 14:59:13 -07:00
|
|
|
notification_type = ses_message["notificationType"]
|
|
|
|
|
if notification_type in ["Delivery", "Complaint"]:
|
|
|
|
|
return notification_type
|
2017-03-16 18:15:49 +00:00
|
|
|
|
2022-09-15 14:59:13 -07:00
|
|
|
if notification_type != "Bounce":
|
|
|
|
|
raise KeyError(f"Unhandled notification type {notification_type}")
|
2017-03-16 18:15:49 +00:00
|
|
|
|
2018-06-07 12:30:04 +01:00
|
|
|
remove_emails_from_bounce(ses_message)
|
2022-09-15 14:59:13 -07:00
|
|
|
current_app.logger.info("SES bounce dict: {}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")")))
|
|
|
|
|
if ses_message["bounce"]["bounceType"] == "Permanent":
|
|
|
|
|
return "Permanent"
|
|
|
|
|
return "Temporary"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _determine_provider_response(ses_message):
|
|
|
|
|
if ses_message["notificationType"] != "Bounce":
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
bounce_type = ses_message["bounce"]["bounceType"]
|
|
|
|
|
bounce_subtype = ses_message["bounce"]["bounceSubType"]
|
|
|
|
|
|
|
|
|
|
# See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html
|
|
|
|
|
if bounce_type == "Permanent" and bounce_subtype == "Suppressed":
|
|
|
|
|
return "The email address is on our email provider suppression list"
|
|
|
|
|
elif bounce_type == "Permanent" and bounce_subtype == "OnAccountSuppressionList":
|
|
|
|
|
return "The email address is on the GC Notify suppression list"
|
|
|
|
|
elif bounce_type == "Transient" and bounce_subtype == "AttachmentRejected":
|
|
|
|
|
return "The email was rejected because of its attachments"
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_aws_responses(ses_message):
|
2022-09-20 20:11:09 -07:00
|
|
|
status = determine_notification_bounce_type(ses_message)
|
2022-09-15 14:59:13 -07:00
|
|
|
|
|
|
|
|
base = {
|
|
|
|
|
"Permanent": {
|
|
|
|
|
"message": "Hard bounced",
|
|
|
|
|
"success": False,
|
|
|
|
|
"notification_status": "permanent-failure",
|
|
|
|
|
},
|
|
|
|
|
"Temporary": {
|
|
|
|
|
"message": "Soft bounced",
|
|
|
|
|
"success": False,
|
|
|
|
|
"notification_status": "temporary-failure",
|
|
|
|
|
},
|
|
|
|
|
"Delivery": {
|
|
|
|
|
"message": "Delivered",
|
|
|
|
|
"success": True,
|
|
|
|
|
"notification_status": "delivered",
|
|
|
|
|
},
|
|
|
|
|
"Complaint": {
|
|
|
|
|
"message": "Complaint",
|
|
|
|
|
"success": True,
|
|
|
|
|
"notification_status": "delivered",
|
|
|
|
|
},
|
|
|
|
|
}[status]
|
|
|
|
|
|
|
|
|
|
base["provider_response"] = _determine_provider_response(ses_message)
|
|
|
|
|
|
|
|
|
|
return base
|
2018-05-30 16:25:49 +01:00
|
|
|
|
|
|
|
|
|
2018-06-05 17:23:24 +01:00
|
|
|
def handle_complaint(ses_message):
|
2018-07-18 17:03:16 +01:00
|
|
|
recipient_email = remove_emails_from_complaint(ses_message)[0]
|
2022-09-15 14:59:13 -07:00
|
|
|
current_app.logger.info("Complaint from SES: \n{}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")")))
|
2018-06-04 17:29:58 +01:00
|
|
|
try:
|
2022-09-15 14:59:13 -07:00
|
|
|
reference = ses_message["mail"]["messageId"]
|
2018-06-04 17:29:58 +01:00
|
|
|
except KeyError as e:
|
|
|
|
|
current_app.logger.exception("Complaint from SES failed to get reference from message", e)
|
|
|
|
|
return
|
2022-09-15 14:59:13 -07:00
|
|
|
notification = dao_get_notification_history_by_reference(reference)
|
|
|
|
|
ses_complaint = ses_message.get("complaint", None)
|
2018-06-04 17:29:58 +01:00
|
|
|
|
|
|
|
|
complaint = Complaint(
|
|
|
|
|
notification_id=notification.id,
|
|
|
|
|
service_id=notification.service_id,
|
2022-09-15 14:59:13 -07:00
|
|
|
ses_feedback_id=ses_complaint.get("feedbackId", None) if ses_complaint else None,
|
|
|
|
|
complaint_type=ses_complaint.get("complaintFeedbackType", None) if ses_complaint else None,
|
|
|
|
|
complaint_date=ses_complaint.get("timestamp", None) if ses_complaint else None,
|
2018-06-04 17:29:58 +01:00
|
|
|
)
|
|
|
|
|
save_complaint(complaint)
|
2018-07-18 17:03:16 +01:00
|
|
|
return complaint, notification, recipient_email
|
2018-06-04 17:29:58 +01:00
|
|
|
|
|
|
|
|
|
2018-06-07 14:54:40 +01:00
|
|
|
def remove_mail_headers(dict_to_edit):
|
2022-09-15 14:59:13 -07:00
|
|
|
if dict_to_edit["mail"].get("headers"):
|
|
|
|
|
dict_to_edit["mail"].pop("headers")
|
|
|
|
|
if dict_to_edit["mail"].get("commonHeaders"):
|
|
|
|
|
dict_to_edit["mail"].pop("commonHeaders")
|
2018-06-07 14:54:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove_emails_from_bounce(bounce_dict):
|
|
|
|
|
remove_mail_headers(bounce_dict)
|
2022-09-15 15:48:37 -07:00
|
|
|
bounce_dict["mail"].pop("destination", None)
|
|
|
|
|
bounce_dict["bounce"].pop("bouncedRecipients", None)
|
2018-06-07 14:54:40 +01:00
|
|
|
|
|
|
|
|
|
2018-05-30 16:45:18 +01:00
|
|
|
def remove_emails_from_complaint(complaint_dict):
|
2018-06-07 14:54:40 +01:00
|
|
|
remove_mail_headers(complaint_dict)
|
2022-09-15 14:59:13 -07:00
|
|
|
complaint_dict["complaint"].pop("complainedRecipients")
|
|
|
|
|
return complaint_dict["mail"].pop("destination")
|
2018-05-30 16:45:18 +01:00
|
|
|
|
2022-09-15 15:48:37 -07:00
|
|
|
|
2022-09-15 14:59:13 -07:00
|
|
|
def check_and_queue_callback_task(notification):
|
|
|
|
|
# queue callback task only if the service_callback_api exists
|
|
|
|
|
service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id)
|
|
|
|
|
if service_callback_api:
|
|
|
|
|
notification_data = create_delivery_status_callback_data(notification, service_callback_api)
|
|
|
|
|
send_delivery_status_to_service.apply_async([str(notification.id), notification_data], queue=QueueNames.CALLBACKS)
|
2018-05-30 16:45:18 +01:00
|
|
|
|
2018-07-18 17:03:16 +01:00
|
|
|
|
|
|
|
|
def _check_and_queue_complaint_callback_task(complaint, notification, recipient):
|
|
|
|
|
# queue callback task only if the service_callback_api exists
|
|
|
|
|
service_callback_api = get_service_complaint_callback_api_for_service(service_id=notification.service_id)
|
|
|
|
|
if service_callback_api:
|
|
|
|
|
complaint_data = create_complaint_callback_data(complaint, notification, service_callback_api, recipient)
|
2022-09-15 15:48:37 -07:00
|
|
|
send_complaint_to_service.apply_async([complaint_data], queue=QueueNames.CALLBACKS)
|
|
|
|
|
|