bringing the repo up to date with the code
git-svn-id: https://vault.zeeksgeeks.com/svn/django_aws_ses/trunk@6 ed966f06-d3d6-432b-bc91-693151a5c6b4
This commit is contained in:
parent
54b5c97ce9
commit
823d6c1b2d
|
@ -6,7 +6,8 @@ from .models import (
|
|||
AwsSesUserAddon,
|
||||
ComplaintRecord,
|
||||
SendRecord,
|
||||
UnknownRecord
|
||||
UnknownRecord,
|
||||
BlackListedDomains,
|
||||
)
|
||||
|
||||
from . import settings
|
||||
|
@ -62,15 +63,22 @@ class ComplaintRecordAdmin(admin.ModelAdmin):
|
|||
admin.site.register(ComplaintRecord, ComplaintRecordAdmin)
|
||||
|
||||
class SendRecordAdmin(admin.ModelAdmin):
|
||||
model = ComplaintRecord
|
||||
model = SendRecord
|
||||
list_display = ('source', 'destination', 'subject', 'timestamp', 'status')
|
||||
list_filter = ('source', 'destination', 'subject', 'timestamp', 'status')
|
||||
|
||||
admin.site.register(SendRecord, SendRecordAdmin)
|
||||
|
||||
class UnknownRecordAdmin(admin.ModelAdmin):
|
||||
model = ComplaintRecord
|
||||
model = UnknownRecord
|
||||
list_display = ('event_type', 'aws_data')
|
||||
list_filter = ('event_type', 'aws_data')
|
||||
|
||||
admin.site.register(UnknownRecord, UnknownRecordAdmin)
|
||||
admin.site.register(UnknownRecord, UnknownRecordAdmin)
|
||||
|
||||
class BlackListedDomainsAdmin(admin.ModelAdmin):
|
||||
model = BlackListedDomains
|
||||
list_display = ('domain', 'timestamp')
|
||||
list_filter = ('domain', 'timestamp')
|
||||
|
||||
admin.site.register(BlackListedDomains, BlackListedDomainsAdmin)
|
|
@ -2,5 +2,6 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class DjangoAwsSesBackendConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'django_aws_ses'
|
||||
verbose_name = 'Django AWS SES'
|
||||
|
|
|
@ -13,7 +13,8 @@ import sys
|
|||
from . import settings
|
||||
from . import signals
|
||||
from . import utils
|
||||
from .models import BounceRecord
|
||||
from .models import BounceRecord
|
||||
from saleor.core.utils import email_exclusion_filter
|
||||
|
||||
logger = settings.logger
|
||||
|
||||
|
@ -82,6 +83,8 @@ class SESBackend(BaseEmailBackend):
|
|||
except Exception:
|
||||
if not self.fail_silently:
|
||||
raise
|
||||
|
||||
return True
|
||||
|
||||
def close(self):
|
||||
"""Close any open HTTP connections to the API server.
|
||||
|
@ -90,10 +93,13 @@ class SESBackend(BaseEmailBackend):
|
|||
|
||||
def send_messages(self, email_messages):
|
||||
"""Sends one or more EmailMessage objects and returns the number of
|
||||
email messages sent.
|
||||
email messages sent and a list of filtered emails.
|
||||
"""
|
||||
|
||||
|
||||
logger.info("1 --- start of send_messages")
|
||||
list_of_response = []
|
||||
num_sent = 0
|
||||
not_sent_list = []
|
||||
sent_message = {"Sent":""}
|
||||
calling_func = ''
|
||||
try:
|
||||
fcount = 0
|
||||
|
@ -103,22 +109,31 @@ class SESBackend(BaseEmailBackend):
|
|||
calling_func = sys._getframe(fcount).f_code.co_name
|
||||
|
||||
except Exception as e:
|
||||
logger.info("fcount:%s, called from exception = %s" , (fcount, e))
|
||||
logger.info("fcount:%s, called from exception = %s" % (fcount, e))
|
||||
|
||||
logger.info("called from %s" , (calling_func))
|
||||
logger.info("called from %s current throttle:%s" % (calling_func, self._throttle))
|
||||
|
||||
|
||||
|
||||
logger.info("send_messages")
|
||||
if not email_messages:
|
||||
return
|
||||
logger.info("no email messages returning")
|
||||
list_of_response.append({'error':'no email messages returning'})
|
||||
|
||||
new_conn_created = self.open()
|
||||
if new_conn_created:
|
||||
logger.info("created a new connection")
|
||||
|
||||
if not self.connection:
|
||||
# Failed silently
|
||||
return
|
||||
logger.info("no connection returning")
|
||||
list_of_response.append({'error':'no connection returning'})
|
||||
logger.info("DEBUGING EMAILS --- list_of_response:%s" % (list_of_response))
|
||||
logger.info("DEBUGING EMAILS --- return %s" % (num_sent))
|
||||
return num_sent
|
||||
|
||||
num_sent = 0
|
||||
|
||||
|
||||
source = settings.AWS_SES_RETURN_PATH
|
||||
|
||||
logger.info("email_messages: %s" % email_messages)
|
||||
|
@ -131,16 +146,43 @@ class SESBackend(BaseEmailBackend):
|
|||
# If settings.AWS_SES_CONFIGURATION_SET is a callable, pass it the
|
||||
# message object and dkim settings and expect it to return a string
|
||||
# containing the SES Configuration Set name.
|
||||
message.aws_ses_response = {'error':'not sent yet'}
|
||||
|
||||
logger.info("Sending signal(email_pre_send)")
|
||||
signals.email_pre_send.send_robust(self.__class__, message=message)
|
||||
logger.info("message to: %s, cc: %s, bcc: %s" % (message.to, message.cc, message.bcc))
|
||||
signals.email_pre_send.send_robust(self.__class__, message=message)
|
||||
|
||||
# for log in dir(message):
|
||||
# logger.info(log)
|
||||
pre_filter_recipients = message.recipients()
|
||||
logger.info("message.recipients() = %s" % message.recipients())
|
||||
|
||||
marketing = message.extra_headers.get("marketing","False")
|
||||
message.to = email_exclusion_filter(message.to,marketing)
|
||||
message.cc = email_exclusion_filter(message.cc,marketing)
|
||||
message.bcc = email_exclusion_filter(message.bcc,marketing)
|
||||
|
||||
message.to = utils.filter_recipiants(message.recipients())
|
||||
message.to = utils.filter_recipiants(message.to)
|
||||
message.cc = utils.filter_recipiants(message.cc)
|
||||
message.bcc = utils.filter_recipiants(message.bcc)
|
||||
|
||||
logger.info("message.recipients() after email_pre_send: %s" % message.recipients())
|
||||
|
||||
|
||||
|
||||
|
||||
if not message.recipients():
|
||||
logger.info("no recipients left after the filter")
|
||||
return False
|
||||
list_of_response.append({'error':'no recipients left after the filter'})
|
||||
message.aws_ses_response = {'error':'no recipients left after the filter'}
|
||||
sent_message = {"Not Sent":"No recipients left after filters"}
|
||||
continue
|
||||
|
||||
#raise Exception('No emails left after filters!')
|
||||
else:
|
||||
for email in pre_filter_recipients:
|
||||
if email not in message.recipients():
|
||||
not_sent_list.append(email)
|
||||
|
||||
if (settings.AWS_SES_CONFIGURATION_SET
|
||||
and 'X-SES-CONFIGURATION-SET' not in message.extra_headers):
|
||||
|
@ -164,13 +206,16 @@ class SESBackend(BaseEmailBackend):
|
|||
# Set the setting to 0 or None to disable throttling.
|
||||
if self._throttle:
|
||||
global recent_send_times
|
||||
logger.info("inside if _throttle recent_send_times:%s" % recent_send_times)
|
||||
|
||||
now = datetime.now()
|
||||
logger.info("inside if _throttle now:%s" % now)
|
||||
|
||||
# Get and cache the current SES max-per-second rate limit
|
||||
# returned by the SES API.
|
||||
logger.info("inside if _throttle calling get_rate_limit")
|
||||
rate_limit = self.get_rate_limit()
|
||||
logger.debug(u"send_messages.throttle rate_limit='{}'".format(rate_limit))
|
||||
logger.info("send_messages.throttle rate_limit='{}'".format(rate_limit))
|
||||
|
||||
# Prune from recent_send_times anything more than a few seconds
|
||||
# ago. Even though SES reports a maximum per-second, the way
|
||||
|
@ -208,7 +253,7 @@ class SESBackend(BaseEmailBackend):
|
|||
logger.info("Try to send raw email")
|
||||
#logger.info('message.message().as_string() = %s' % message.message().as_string())
|
||||
logger.info("source = %s" % source)
|
||||
logger.info("message.from_email = %s" % self.dkim_key)
|
||||
logger.info("message.from_email = %s" % message.from_email)
|
||||
logger.info("message.recipients() = %s" % message.recipients())
|
||||
|
||||
logger.info("dkim_key = %s" % self.dkim_key)
|
||||
|
@ -221,16 +266,21 @@ class SESBackend(BaseEmailBackend):
|
|||
# todo attachments?
|
||||
RawMessage={'Data': dkim_sign(message.message().as_string(),
|
||||
dkim_key=self.dkim_key,
|
||||
dkim_domain=self.dkim_domain,
|
||||
dkim_domain=self.dkim_domain,
|
||||
dkim_selector=self.dkim_selector,
|
||||
dkim_headers=self.dkim_headers)}
|
||||
)
|
||||
|
||||
list_of_response.append(response)
|
||||
|
||||
message.aws_ses_response = response
|
||||
|
||||
message.extra_headers['status'] = 200
|
||||
message.extra_headers['message_id'] = response['MessageId']
|
||||
message.extra_headers['request_id'] = response['ResponseMetadata']['RequestId']
|
||||
num_sent += 1
|
||||
if 'X-SES-CONFIGURATION-SET' in message.extra_headers:
|
||||
logger.debug(
|
||||
logger.info(
|
||||
u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}' "
|
||||
u"ses-configuration-set='{}'".format(
|
||||
message.from_email,
|
||||
|
@ -240,7 +290,7 @@ class SESBackend(BaseEmailBackend):
|
|||
message.extra_headers['X-SES-CONFIGURATION-SET']
|
||||
))
|
||||
else:
|
||||
logger.debug(u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}'".format(
|
||||
logger.info(u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}'".format(
|
||||
message.from_email,
|
||||
", ".join(message.recipients()),
|
||||
message.extra_headers['message_id'],
|
||||
|
@ -255,26 +305,41 @@ class SESBackend(BaseEmailBackend):
|
|||
message.extra_headers[key] = getattr(err, key, None)
|
||||
if not self.fail_silently:
|
||||
raise
|
||||
|
||||
if not_sent_list:
|
||||
sent_message.update({"Sent":"%s" % not_sent_list})
|
||||
|
||||
logger.info("new_conn_created: %s" % new_conn_created)
|
||||
if new_conn_created:
|
||||
logger.info("closing new connection after send")
|
||||
self.close()
|
||||
|
||||
return num_sent
|
||||
|
||||
logger.info("DEBUGING EMAILS --- list_of_response:%s" % (list_of_response))
|
||||
logger.info("DEBUGING EMAILS --- return %s, %s" % (num_sent, sent_message))
|
||||
|
||||
|
||||
return num_sent, sent_message
|
||||
|
||||
def get_rate_limit(self):
|
||||
logger.info("getting rate limit")
|
||||
if self._access_key_id in cached_rate_limits:
|
||||
logger.info("returning cached rate limit %s" % cached_rate_limits[self._access_key_id])
|
||||
return cached_rate_limits[self._access_key_id]
|
||||
|
||||
logger.info("creating AWS connection")
|
||||
new_conn_created = self.open()
|
||||
if not self.connection:
|
||||
logger.info("AWS connection creation failed")
|
||||
raise Exception(
|
||||
"No connection is available to check current SES rate limit.")
|
||||
try:
|
||||
quota_dict = self.connection.get_send_quota()
|
||||
logger.info("AWS quota dict %s" % quota_dict)
|
||||
max_per_second = quota_dict['MaxSendRate']
|
||||
ret = float(max_per_second)
|
||||
cached_rate_limits[self._access_key_id] = ret
|
||||
return ret
|
||||
finally:
|
||||
if new_conn_created:
|
||||
self.close()
|
||||
self.close()
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import hashlib
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
@ -25,9 +26,15 @@ class AwsSesSettings(models.Model):
|
|||
|
||||
@receiver(post_save, sender=Site)
|
||||
def update_awsses_settings(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
AwsSesSettings.objects.create(Site=instance)
|
||||
instance.AwsSesSettings.save()
|
||||
try:
|
||||
if created:
|
||||
AwsSesSettings.objects.create(site=instance)
|
||||
instance.awssessettings.save()
|
||||
except Exception as e:
|
||||
print("Exception saving site error:%s" % e)
|
||||
track = traceback.format_exc()
|
||||
print("Exception saving site track: %s" % (track))
|
||||
|
||||
|
||||
class AwsSesUserAddon(models.Model):
|
||||
user = models.OneToOneField(User, related_name='aws_ses', on_delete=models.CASCADE)
|
||||
|
@ -87,9 +94,12 @@ class BounceRecord(models.Model):
|
|||
status = models.CharField(max_length=255, blank=True, null=True,)
|
||||
action = models.CharField(max_length=255, blank=True, null=True,)
|
||||
feedback_id = models.TextField(max_length=255, blank=True, null=True,)
|
||||
diagnostic_code = models.CharField(max_length=255, blank=True, null=True,)
|
||||
diagnostic_code = models.CharField(max_length=2048, blank=True, null=True,)
|
||||
cleared = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=["email"]),]
|
||||
|
||||
def __str__(self):
|
||||
return "email: %s, type: %s, sub_type: %s, status: %s, date: %s" % (self.email, self.bounce_type, self.bounce_sub_type, self.status, self.timestamp)
|
||||
|
||||
|
@ -104,7 +114,6 @@ class ComplaintRecord(models.Model):
|
|||
return "email: %s, sub_type: %s, feedback_type: %s, date: %s" % (self.email, self.bounce_sub_type, self.feedback_type, self.timestamp)
|
||||
|
||||
class SendRecord(models.Model):
|
||||
|
||||
|
||||
SEND = 'Send'
|
||||
DELIVERED = 'Delivery'
|
||||
|
@ -121,6 +130,9 @@ class SendRecord(models.Model):
|
|||
aws_process_time = models.IntegerField()
|
||||
smtp_response = models.CharField(max_length=255, blank=True, null=True,)
|
||||
status = models.CharField(max_length=255, blank=True, null=True,)
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=["destination"]),]
|
||||
|
||||
def __str__(self):
|
||||
return "source: %s, destination: %s, subject: %s, date: %s" % (self.source, self.destination, self.subject, self.timestamp)
|
||||
|
@ -133,4 +145,10 @@ class UnknownRecord(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return "eventType: %s, timestamp: %s" % (self.eventType, self.timestamp)
|
||||
|
||||
|
||||
class BlackListedDomains(models.Model):
|
||||
domain = models.CharField(max_length=255, unique=True)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return "%s, blocked: %s" % (self.domain, self.timestamp)
|
|
@ -1,83 +1,106 @@
|
|||
from django.conf import settings
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
import logging
|
||||
|
||||
from .models import (
|
||||
AwsSesSettings
|
||||
)
|
||||
|
||||
|
||||
__all__ = ('ACCESS_KEY', 'SECRET_KEY', 'AWS_SES_REGION_NAME',
|
||||
'AWS_SES_REGION_ENDPOINT', 'AWS_SES_AUTO_THROTTLE',
|
||||
'AWS_SES_RETURN_PATH', 'DKIM_DOMAIN', 'DKIM_PRIVATE_KEY',
|
||||
'DKIM_SELECTOR', 'DKIM_HEADERS', 'TIME_ZONE', 'BASE_DIR',
|
||||
'BOUNCE_LIMIT','SES_BACKEND_DEBUG','SES_BACKEND_DEBUG_LOGFILE_PATH',
|
||||
'SES_BACKEND_DEBUG_LOGFILE_FORMATTER')
|
||||
|
||||
aws_ses_Settings = None
|
||||
|
||||
try:
|
||||
aws_ses_Settings, c = AwsSesSettings.objects.get_or_create(site_id=settings.SITE_ID)
|
||||
except Exception as e:
|
||||
print("AwsSesSettings does not exist: error: %s" % e)
|
||||
else:
|
||||
__all__ = ('ACCESS_KEY', 'SECRET_KEY', 'AWS_SES_REGION_NAME',
|
||||
'AWS_SES_REGION_ENDPOINT', 'AWS_SES_AUTO_THROTTLE',
|
||||
'AWS_SES_RETURN_PATH', 'DKIM_DOMAIN', 'DKIM_PRIVATE_KEY',
|
||||
'DKIM_SELECTOR', 'DKIM_HEADERS', 'TIME_ZONE', 'BASE_DIR',
|
||||
'BOUNCE_LIMIT','SES_BACKEND_DEBUG','SES_BACKEND_DEBUG_LOGFILE_PATH',
|
||||
'SES_BACKEND_DEBUG_LOGFILE_FORMATTER')
|
||||
|
||||
BASE_DIR = getattr(settings, 'BASE_DIR', None)
|
||||
ACCESS_KEY = aws_ses_Settings.access_key if aws_ses_Settings else None
|
||||
if ACCESS_KEY is None:
|
||||
ACCESS_KEY = getattr(settings, 'AWS_SES_ACCESS_KEY_ID',getattr(settings, 'AWS_ACCESS_KEY_ID', None))
|
||||
|
||||
if not BASE_DIR:
|
||||
raise RuntimeError('No BASE_DIR defined in project settings, django_aws_ses requires BASE_DIR to be defined and pointed at your root directory. i.e. BASE_DIR = os.path.dirname(os.path.abspath(__file__))')
|
||||
SECRET_KEY = aws_ses_Settings.secret_key if aws_ses_Settings else None
|
||||
if SECRET_KEY is None:
|
||||
SECRET_KEY = getattr(settings, 'AWS_SES_SECRET_ACCESS_KEY',getattr(settings, 'AWS_SECRET_ACCESS_KEY', None))
|
||||
|
||||
DEFAULT_FROM_EMAIL = getattr(settings, 'DEFAULT_FROM_EMAIL', 'no_reply@%s' % aws_ses_Settings.site.domain)
|
||||
AWS_SES_REGION_NAME = aws_ses_Settings.region_name if aws_ses_Settings else None
|
||||
if AWS_SES_REGION_NAME is None:
|
||||
AWS_SES_REGION_NAME = getattr(settings, 'AWS_SES_REGION_NAME',getattr(settings, 'AWS_DEFAULT_REGION', 'us-east-1'))
|
||||
|
||||
HOME_URL = getattr(settings, 'HOME_URL', '')
|
||||
AWS_SES_REGION_ENDPOINT = aws_ses_Settings.region_endpoint if aws_ses_Settings else None
|
||||
if AWS_SES_REGION_ENDPOINT is None:
|
||||
AWS_SES_REGION_ENDPOINT = getattr(settings, 'AWS_SES_REGION_ENDPOINT','email.us-east-1.amazonaws.com')
|
||||
|
||||
UNSUBSCRIBE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/unsebscribe.html')
|
||||
BASE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/base.html')
|
||||
BASE_DIR = getattr(settings, 'BASE_DIR', None)
|
||||
|
||||
DEFAULT_FROM_EMAIL = ""
|
||||
try:
|
||||
site = Site.objects.get_current()
|
||||
|
||||
DEFAULT_FROM_EMAIL = getattr(settings, 'DEFAULT_FROM_EMAIL', 'no_reply@%s' % site.domain)
|
||||
except Exception as e:
|
||||
print("Site Doesn't Exist, please configure Django sites")
|
||||
print("Error is: %s" % e)
|
||||
|
||||
ACCESS_KEY = aws_ses_Settings.access_key or getattr(settings, 'AWS_SES_ACCESS_KEY_ID',getattr(settings, 'AWS_ACCESS_KEY_ID', None))
|
||||
HOME_URL = getattr(settings, 'HOME_URL', '')
|
||||
|
||||
if not BASE_DIR:
|
||||
raise RuntimeError('No BASE_DIR defined in project settings, django_aws_ses requires BASE_DIR to be defined and pointed at your root directory. i.e. BASE_DIR = os.path.dirname(os.path.abspath(__file__))')
|
||||
|
||||
UNSUBSCRIBE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/unsebscribe.html')
|
||||
BASE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/base.html')
|
||||
|
||||
AWS_SES_REGION_ENDPOINT_URL = getattr(settings, 'AWS_SES_REGION_ENDPOINT_URL','https://' + AWS_SES_REGION_ENDPOINT)
|
||||
|
||||
SECRET_KEY = aws_ses_Settings.secret_key or getattr(settings, 'AWS_SES_SECRET_ACCESS_KEY',getattr(settings, 'AWS_SECRET_ACCESS_KEY', None))
|
||||
AWS_SES_AUTO_THROTTLE = getattr(settings, 'AWS_SES_AUTO_THROTTLE', 0.5)
|
||||
AWS_SES_RETURN_PATH = getattr(settings, 'AWS_SES_RETURN_PATH', None)
|
||||
|
||||
# if AWS_SES_RETURN_PATH is None:
|
||||
# AWS_SES_RETURN_PATH = "CF Doors <cdfbounced@zeeksgeeks.com>"
|
||||
|
||||
AWS_SES_REGION_NAME = aws_ses_Settings.region_name or getattr(settings, 'AWS_SES_REGION_NAME',getattr(settings, 'AWS_DEFAULT_REGION', 'us-east-1'))
|
||||
AWS_SES_CONFIGURATION_SET = getattr(settings, 'AWS_SES_CONFIGURATION_SET', None)
|
||||
|
||||
DKIM_DOMAIN = getattr(settings, "DKIM_DOMAIN", None)
|
||||
DKIM_PRIVATE_KEY = getattr(settings, 'DKIM_PRIVATE_KEY', None)
|
||||
DKIM_SELECTOR = getattr(settings, 'DKIM_SELECTOR', 'ses')
|
||||
DKIM_HEADERS = getattr(settings, 'DKIM_HEADERS', ('From', 'To', 'Cc', 'Subject'))
|
||||
|
||||
TIME_ZONE = settings.TIME_ZONE
|
||||
|
||||
VERIFY_BOUNCE_SIGNATURES = getattr(settings, 'AWS_SES_VERIFY_BOUNCE_SIGNATURES', True)
|
||||
|
||||
# Domains that are trusted when retrieving the certificate
|
||||
# used to sign bounce messages.
|
||||
BOUNCE_CERT_DOMAINS = getattr(settings, 'AWS_SNS_BOUNCE_CERT_TRUSTED_DOMAINS', (
|
||||
'amazonaws.com',
|
||||
'amazon.com',
|
||||
))
|
||||
|
||||
SES_BOUNCE_LIMIT = getattr(settings,'BOUNCE_LIMT', 1)
|
||||
|
||||
AWS_SES_REGION_ENDPOINT = aws_ses_Settings.region_endpoint or getattr(settings, 'AWS_SES_REGION_ENDPOINT','email.us-east-1.amazonaws.com')
|
||||
SES_BACKEND_DEBUG = getattr(settings,'SES_BACKEND_DEBUG', False)
|
||||
|
||||
AWS_SES_REGION_ENDPOINT_URL = getattr(settings, 'AWS_SES_REGION_ENDPOINT_URL','https://' + AWS_SES_REGION_ENDPOINT)
|
||||
|
||||
AWS_SES_AUTO_THROTTLE = getattr(settings, 'AWS_SES_AUTO_THROTTLE', 0.5)
|
||||
AWS_SES_RETURN_PATH = getattr(settings, 'AWS_SES_RETURN_PATH', None)
|
||||
AWS_SES_CONFIGURATION_SET = getattr(settings, 'AWS_SES_CONFIGURATION_SET', None)
|
||||
|
||||
DKIM_DOMAIN = getattr(settings, "DKIM_DOMAIN", None)
|
||||
DKIM_PRIVATE_KEY = getattr(settings, 'DKIM_PRIVATE_KEY', None)
|
||||
DKIM_SELECTOR = getattr(settings, 'DKIM_SELECTOR', 'ses')
|
||||
DKIM_HEADERS = getattr(settings, 'DKIM_HEADERS', ('From', 'To', 'Cc', 'Subject'))
|
||||
|
||||
TIME_ZONE = settings.TIME_ZONE
|
||||
|
||||
VERIFY_BOUNCE_SIGNATURES = getattr(settings, 'AWS_SES_VERIFY_BOUNCE_SIGNATURES', True)
|
||||
|
||||
# Domains that are trusted when retrieving the certificate
|
||||
# used to sign bounce messages.
|
||||
BOUNCE_CERT_DOMAINS = getattr(settings, 'AWS_SNS_BOUNCE_CERT_TRUSTED_DOMAINS', (
|
||||
'amazonaws.com',
|
||||
'amazon.com',
|
||||
))
|
||||
|
||||
SES_BOUNCE_LIMIT = getattr(settings,'BOUNCE_LIMT', 1)
|
||||
|
||||
SES_BACKEND_DEBUG = getattr(settings,'SES_BACKEND_DEBUG', False)
|
||||
|
||||
SES_BACKEND_DEBUG_LOGFILE_PATH = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_PATH', '%s/aws_ses.log' % BASE_DIR)
|
||||
|
||||
SES_BACKEND_DEBUG_LOGFILE_FORMATTER = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_FORMATTER', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
logger = logging.getLogger('django_aws_ses')
|
||||
# logger.setLevel(logging.WARNING)
|
||||
if SES_BACKEND_DEBUG:
|
||||
logger.setLevel(logging.INFO)
|
||||
# create a file handler
|
||||
if SES_BACKEND_DEBUG_LOGFILE_PATH:
|
||||
handler = logging.FileHandler(SES_BACKEND_DEBUG_LOGFILE_PATH)
|
||||
handler.setLevel(logging.INFO)
|
||||
# create a logging format
|
||||
formatter = logging.Formatter(SES_BACKEND_DEBUG_LOGFILE_FORMATTER)
|
||||
handler.setFormatter(formatter)
|
||||
# add the handlers to the logger
|
||||
logger.addHandler(handler)
|
||||
#logger.info('something we are logging')
|
||||
SES_BACKEND_DEBUG_LOGFILE_PATH = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_PATH', '%s/aws_ses.log' % BASE_DIR)
|
||||
|
||||
SES_BACKEND_DEBUG_LOGFILE_FORMATTER = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_FORMATTER', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
logger = logging.getLogger('django_aws_ses')
|
||||
# logger.setLevel(logging.WARNING)
|
||||
if SES_BACKEND_DEBUG:
|
||||
logger.setLevel(logging.INFO)
|
||||
# create a file handler
|
||||
if SES_BACKEND_DEBUG_LOGFILE_PATH:
|
||||
handler = logging.FileHandler(SES_BACKEND_DEBUG_LOGFILE_PATH)
|
||||
handler.setLevel(logging.INFO)
|
||||
# create a logging format
|
||||
formatter = logging.Formatter(SES_BACKEND_DEBUG_LOGFILE_FORMATTER)
|
||||
handler.setFormatter(formatter)
|
||||
# add the handlers to the logger
|
||||
logger.addHandler(handler)
|
||||
#logger.info('something we are logging')
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import base64
|
||||
import logging
|
||||
import time
|
||||
import re
|
||||
import dns.resolver
|
||||
from telnetlib import Telnet
|
||||
from builtins import str as text
|
||||
from builtins import bytes
|
||||
from io import StringIO
|
||||
|
@ -19,7 +23,9 @@ from . import settings
|
|||
from . import signals
|
||||
from .models import (
|
||||
BounceRecord,
|
||||
ComplaintRecord
|
||||
ComplaintRecord,
|
||||
BlackListedDomains,
|
||||
SendRecord,
|
||||
)
|
||||
|
||||
logger = settings.logger
|
||||
|
@ -211,30 +217,40 @@ def verify_bounce_message(msg):
|
|||
|
||||
@receiver(signals.email_pre_send)
|
||||
def receiver_email_pre_send(sender, message=None, **kwargs):
|
||||
logger.info("receiver_email_pre_send received signal")
|
||||
#logger.info("receiver_email_pre_send received signal")
|
||||
pass
|
||||
|
||||
def filter_recipiants(recipiant_list):
|
||||
logger.info("Starting filter_recipiants: %s" % recipiant_list)
|
||||
|
||||
if type(recipiant_list) != type([]):
|
||||
logger.info("putting emails into a list")
|
||||
recipiant_list = [recipiant_list]
|
||||
|
||||
if len(recipiant_list) > 0:
|
||||
recipiant_list = filter_recipiants_with_unsubscribe(recipiant_list)
|
||||
|
||||
recipiant_list = filter_recipiants_with_unsubscribe(recipiant_list)
|
||||
|
||||
if len(recipiant_list) > 0:
|
||||
recipiant_list = filter_recipiants_with_complaint_records(recipiant_list)
|
||||
|
||||
|
||||
if len(recipiant_list) > 0:
|
||||
recipiant_list = filter_recipiants_with_bounce_records(recipiant_list)
|
||||
|
||||
|
||||
if len(recipiant_list) > 0:
|
||||
recipiant_list = filter_recipiants_with_validater_email_domain(recipiant_list)
|
||||
|
||||
logger.info("recipiant list after filter_recipiants: %s" % recipiant_list)
|
||||
return recipiant_list
|
||||
|
||||
def filter_recipiants_with_unsubscribe(recipiant_list):
|
||||
"""
|
||||
filter message recipiants so we don't send emails to any email that have Unsubscribude
|
||||
"""
|
||||
logger.info("unsubscribe filter running")
|
||||
#logger.info("unsubscribe filter running")
|
||||
|
||||
logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
#logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
blacklist_emails = list(set([record.email for record in User.objects.filter(aws_ses__unsubscribe=True)]))
|
||||
|
||||
|
||||
if blacklist_emails:
|
||||
return filter_recipiants_with_blacklist(recipiant_list, blacklist_emails)
|
||||
else:
|
||||
|
@ -244,9 +260,9 @@ def filter_recipiants_with_complaint_records(recipiant_list):
|
|||
"""
|
||||
filter message recipiants so we don't send emails to any email that have a ComplaintRecord
|
||||
"""
|
||||
logger.info("complaint_records filter running")
|
||||
#logger.info("complaint_records filter running")
|
||||
|
||||
logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
#logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
blacklist_emails = list(set([record.email for record in ComplaintRecord.objects.filter(email__isnull=False)]))
|
||||
|
||||
if blacklist_emails:
|
||||
|
@ -259,21 +275,87 @@ def filter_recipiants_with_bounce_records(recipiant_list):
|
|||
filter message recipiants so we dont send emails to any email that has more BounceRecord
|
||||
the SES_BOUNCE_LIMIT
|
||||
"""
|
||||
logger.info("bounce_records filter running")
|
||||
#logger.info("bounce_records filter running")
|
||||
|
||||
#logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
|
||||
blacklist_emails = list(set([record.email for record in BounceRecord.objects.filter(email__isnull=False).annotate(total=Count('email')).filter(total__gte=settings.SES_BOUNCE_LIMIT)]))
|
||||
|
||||
logger.info("message.recipients() befor blacklist_emails filter: %s" % recipiant_list)
|
||||
blacklist_emails = list(set([record.email for record in BounceRecord.objects.filter(email__isnull=False).annotate(total=Count('email')).filter(total__gte=settings.SES_BOUNCE_LIMIT)]))
|
||||
if blacklist_emails:
|
||||
return filter_recipiants_with_blacklist(recipiant_list, blacklist_emails)
|
||||
else:
|
||||
return recipiant_list
|
||||
|
||||
def filter_recipiants_with_blacklist(recipiant_list, blacklist_emails):
|
||||
"""
|
||||
filter message recipiants with a list of email you dont want to email
|
||||
filter message recipiants with a list of emails you dont want to email
|
||||
"""
|
||||
logger.info("blacklist_emails filter list: %s" % blacklist_emails)
|
||||
filtered_recipiant_list = [email for email in recipiant_list if email not in blacklist_emails]
|
||||
filtered_recipiant_list = [email for email in recipiant_list if email not in blacklist_emails]
|
||||
|
||||
logger.info("filtered_recipiant_list: %s" % filtered_recipiant_list)
|
||||
return filtered_recipiant_list
|
||||
|
||||
def filter_recipiants_with_validater_email_domain(recipiant_list):
|
||||
debug_flag = True
|
||||
|
||||
sent_list = [e.destination for e in SendRecord.objects.filter(destination__in=recipiant_list).distinct("destination")]
|
||||
|
||||
test_list = [e for e in recipiant_list if e not in sent_list]
|
||||
|
||||
for e in test_list:
|
||||
|
||||
if not validater_email_domain(e):
|
||||
recipiant_list.remove(e)
|
||||
|
||||
return recipiant_list
|
||||
|
||||
def validater_email_domain(email):
|
||||
|
||||
if email.find("@") < 1:
|
||||
|
||||
return False
|
||||
domain = email.split("@")[-1]
|
||||
|
||||
if BlackListedDomains.objects.filter(domain=domain).count() > 0:
|
||||
return False
|
||||
|
||||
records = []
|
||||
try:
|
||||
records = dns.resolver.query(domain, 'MX')
|
||||
except dns.resolver.NoNameservers as e:
|
||||
return False
|
||||
|
||||
except dns.resolver.NoAnswer as e:
|
||||
return False
|
||||
|
||||
except dns.resolver.NXDOMAIN as e:
|
||||
return False
|
||||
|
||||
except dns.resolver.LifetimeTimeout as e:
|
||||
return False
|
||||
|
||||
if len(records) < 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def emailIsValid(email):
|
||||
|
||||
resp = False
|
||||
regex = re.compile(r'([A-Za-z0-9]+[.\-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+')
|
||||
if re.fullmatch(regex, email):
|
||||
resp = True
|
||||
|
||||
return resp
|
||||
|
||||
def validate_email(email):
|
||||
|
||||
if not emailIsValid(email):
|
||||
return False
|
||||
|
||||
if BounceRecord.objects.filter(email=email).count() >= settings.SES_BOUNCE_LIMIT:
|
||||
return False
|
||||
|
||||
if ComplaintRecord.objects.filter(email=email).count() > 0:
|
||||
return False
|
||||
|
||||
return validater_email_domain(email)
|
|
@ -249,6 +249,13 @@ def handle_bounce(request):
|
|||
bounce_type = bounce_obj.get('bounceType')
|
||||
bounce_subtype = bounce_obj.get('bounceSubType')
|
||||
bounce_recipients = bounce_obj.get('bouncedRecipients', [])
|
||||
logger.info(
|
||||
u'Received bounce notification: feedbackId: %s, bounceType: %s, bounceSubType: %s',
|
||||
feedback_id, bounce_type, bounce_subtype,
|
||||
extra={
|
||||
'notification': notification,
|
||||
},
|
||||
)
|
||||
|
||||
# create a BounceRecord so we can keep from sending to bad emails.
|
||||
logger.info('create records')
|
||||
|
@ -265,13 +272,6 @@ def handle_bounce(request):
|
|||
reporting_mta = bounce_obj.get('reportingMTA', None),
|
||||
)
|
||||
|
||||
logger.info(
|
||||
u'Received bounce notification: feedbackId: %s, bounceType: %s, bounceSubType: %s',
|
||||
feedback_id, bounce_type, bounce_subtype,
|
||||
extra={
|
||||
'notification': notification,
|
||||
},
|
||||
)
|
||||
|
||||
signals.bounce_received.send(
|
||||
sender=handle_bounce,
|
||||
|
|
Loading…
Reference in New Issue