diff --git a/README.txt b/README.txt index d7241ab..f235f11 100644 --- a/README.txt +++ b/README.txt @@ -18,18 +18,18 @@ message = EmailMessage() message.subject = 'Hello from Amazon SES! Test subject' message.bodyText = 'This is body text of test message.' -result = amazonSes.sendEmail('username@yourdomaintest.com', 'testmail@yourdomaintest.com', message) +result = amazonSes.sendEmail('username@yourdomaintest.com', 'testmail@yourdomaintest.com', message) print result.requestId print result.messageId -I want you to notice that in case Amazon returns some error, the API will raise an exception AmazonError which will contain errorType, code and message. +I want you to notice that in case Amazon returns some error, the API will raise an exception AmazonError which will contain errorType, code and message. If your Amazon SES account is not switched to production use, you can send a message only from/to verified email addresses. Here is an example of how to verify the email using the API: result = amazonSes.verifyEmailAddress('username@yourdomaintest.com') print result.requestId -You will receive the confirmation with a link which you should click to verify your email. +You will receive the confirmation with a link which you should click to verify your email. An example of how to receive information about SendQuota: @@ -44,7 +44,7 @@ VerifyEmailAddress DeleteVerifiedEmailAddress GetSendQuota ListVerifiedEmailAddresses -Methods return instances of AmazonResult or derived class (for example, method amazonSes.getSendQuota() returns the instance of AmazonSendQuota). +Methods return instances of AmazonResult or derived class (for example, method amazonSes.getSendQuota() returns the instance of AmazonSendQuota). Author =============== diff --git a/amazon_ses.py b/amazon_ses.py index 046ed48..53b5118 100644 --- a/amazon_ses.py +++ b/amazon_ses.py @@ -1,4 +1,4 @@ -#Copyright (c) 2011 Vladimir Pankratiev http://tagmask.com +#Copyright (c) 2011 Vladimir Pankratiev http://tagmask.com # #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,7 @@ def __init__(self, accessKeyID, secretAccessKey): def _getSignature(self, dateValue): h = hmac.new(key=self._secretAccessKey, msg=dateValue, digestmod=hashlib.sha256) return base64.b64encode(h.digest()).decode() - + def _getHeaders(self): headers = { 'Content-type': 'application/x-www-form-urlencoded' } d = datetime.utcnow() @@ -47,11 +47,11 @@ def _getHeaders(self): signature = self._getSignature(dateValue) headers['X-Amzn-Authorization'] = 'AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HMACSHA256, Signature=%s' % (self._accessKeyID, signature) return headers - + def _performAction(self, actionName, params=None): if not params: params = {} - params['Action'] = actionName + params['Action'] = actionName #https://email.us-east-1.amazonaws.com/ conn = httplib.HTTPSConnection('email.us-east-1.amazonaws.com') params = urllib.urlencode(params) @@ -60,24 +60,24 @@ def _performAction(self, actionName, params=None): responseResult = response.read() conn.close() return self._responseParser.parse(actionName, response.status, response.reason, responseResult) - + def verifyEmailAddress(self, emailAddress): params = { 'EmailAddress': emailAddress } return self._performAction('VerifyEmailAddress', params) - + def deleteVerifiedEmailAddress(self, emailAddress): params = { 'EmailAddress': emailAddress } return self._performAction('DeleteVerifiedEmailAddress', params) - + def getSendQuota(self): return self._performAction('GetSendQuota') - + def getSendStatistics(self): return self._performAction('GetSendStatistics') - + def listVerifiedEmailAddresses(self): return self._performAction('ListVerifiedEmailAddresses') - + def sendEmail(self, source, toAddresses, message, replyToAddresses=None, returnPath=None, ccAddresses=None, bccAddresses=None): params = { 'Source': source } for objName, addresses in zip(["ToAddresses", "CcAddresses", "BccAddresses"], [toAddresses, ccAddresses, bccAddresses]): @@ -86,10 +86,10 @@ def sendEmail(self, source, toAddresses, message, replyToAddresses=None, returnP for i, address in enumerate(addresses, 1): params['Destination.%s.member.%d' % (objName, i)] = address else: - params['Destination.%s.member.1' % objName] = addresses + params['Destination.%s.member.1' % objName] = addresses if not returnPath: returnPath = source - params['ReturnPath'] = returnPath + params['ReturnPath'] = returnPath params['Message.Subject.Charset'] = message.charset params['Message.Subject.Data'] = message.subject if message.bodyText: @@ -103,11 +103,11 @@ def sendEmail(self, source, toAddresses, message, replyToAddresses=None, returnP class EmailMessage: - def __init__(self): + def __init__(self, subject=None, bodyHtml=None, bodyText=None): self.charset = 'UTF-8' - self.subject = None - self.bodyHtml = None - self.bodyText = None + self.subject = subject + self.bodyHtml = bodyHtml + self.bodyText = bodyText @@ -116,29 +116,29 @@ def __init__(self, errorType, code, message): self.errorType = errorType self.code = code self.message = message - + class AmazonAPIError(Exception): def __init__(self, message): self.message = message - - - + + + class AmazonResult: def __init__(self, requestId): self.requestId = requestId - + class AmazonSendEmailResult(AmazonResult): def __init__(self, requestId, messageId): self.requestId = requestId self.messageId = messageId - + class AmazonSendQuota(AmazonResult): def __init__(self, requestId, max24HourSend, maxSendRate, sentLast24Hours): self.requestId = requestId self.max24HourSend = max24HourSend self.maxSendRate = maxSendRate self.sentLast24Hours = sentLast24Hours - + class AmazonSendDataPoint: def __init__(self, bounces, complaints, deliveryAttempts, rejects, timestamp): self.bounces = bounces @@ -146,105 +146,105 @@ def __init__(self, bounces, complaints, deliveryAttempts, rejects, timestamp): self.deliveryAttempts = deliveryAttempts self.rejects = rejects self.timestamp = timestamp - + class AmazonSendStatistics(AmazonResult): def __init__(self, requestId): self.requestId = requestId - self.members = [] - + self.members = [] + class AmazonVerifiedEmails(AmazonSendStatistics): pass - + class AmazonResponseParser: class XmlResponse: def __init__(self, str): self._rootElement = XML(str) self._namespace = self._rootElement.tag[1:].split("}")[0] - + def checkResponseName(self, name): if self._rootElement.tag == self._fixTag(self._namespace, name): return True else: - raise AmazonAPIError('ErrorResponse is invalid.') - + raise AmazonAPIError('ErrorResponse is invalid.') + def checkActionName(self, actionName): if self._rootElement.tag == self._fixTag(self._namespace, ('%sResponse' % actionName)): return True else: raise AmazonAPIError('Response of action "%s" is invalid.' % actionName) - + def getChild(self, *itemPath): node = self._findNode(self._rootElement, self._namespace, *itemPath) if node != None: return node else: raise AmazonAPIError('Node with the specified path was not found.') - - def getChildText(self, *itemPath): - node = self.getChild(*itemPath) + + def getChildText(self, *itemPath): + node = self.getChild(*itemPath) return node.text - + def _fixTag(self, namespace, tag): return '{%s}%s' % (namespace, tag) def _findNode(self, rootElement, namespace, *args): match = '.' for s in args: - match += '/{%s}%s' % (namespace, s) - return rootElement.find(match) - - + match += '/{%s}%s' % (namespace, s) + return rootElement.find(match) + + def __init__(self): self._simpleResultActions = ['DeleteVerifiedEmailAddress', 'VerifyEmailAddress'] - - def _parseSimpleResult(self, actionName, xmlResponse): + + def _parseSimpleResult(self, actionName, xmlResponse): if xmlResponse.checkActionName(actionName): requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId') return AmazonResult(requestId) - + def _parseSendQuota(self, actionName, xmlResponse): if xmlResponse.checkActionName(actionName): requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId') - value = xmlResponse.getChildText('GetSendQuotaResult', 'Max24HourSend') + value = xmlResponse.getChildText('GetSendQuotaResult', 'Max24HourSend') max24HourSend = float(value) value = xmlResponse.getChildText('GetSendQuotaResult', 'MaxSendRate') maxSendRate = float(value) value = xmlResponse.getChildText('GetSendQuotaResult', 'SentLast24Hours') sentLast24Hours = float(value) return AmazonSendQuota(requestId, max24HourSend, maxSendRate, sentLast24Hours) - + #def _parseSendStatistics(self, actionName, xmlResponse): # if xmlResponse.checkActionName(actionName): # requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId') - + def _parseListVerifiedEmails(self, actionName, xmlResponse): if xmlResponse.checkActionName(actionName): requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId') node = xmlResponse.getChild('ListVerifiedEmailAddressesResult', 'VerifiedEmailAddresses') result = AmazonVerifiedEmails(requestId) - for addr in node: + for addr in node: result.members.append(addr.text) return result - + def _parseSendEmail(self, actionName, xmlResponse): if xmlResponse.checkActionName(actionName): requestId = xmlResponse.getChildText('ResponseMetadata', 'RequestId') messageId = xmlResponse.getChildText('SendEmailResult', 'MessageId') return AmazonSendEmailResult(requestId, messageId) - + def _raiseError(self, xmlResponse): if xmlResponse.checkResponseName('ErrorResponse'): errorType = xmlResponse.getChildText('Error', 'Type') code = xmlResponse.getChildText('Error', 'Code') message = xmlResponse.getChildText('Error', 'Message') raise AmazonError(errorType, code, message) - - def parse(self, actionName, statusCode, reason, responseResult): + + def parse(self, actionName, statusCode, reason, responseResult): xmlResponse = self.XmlResponse(responseResult) log.info('Response status code: %s, reason: %s', statusCode, reason) log.debug(responseResult) - - result = None + + result = None if statusCode != 200: self._raiseError(xmlResponse) else: @@ -257,7 +257,7 @@ def parse(self, actionName, statusCode, reason, responseResult): #elif actionName == 'GetSendStatistics': # result = self._parseSendStatistics(actionName, xmlResponse) elif actionName == 'ListVerifiedEmailAddresses': - result = self._parseListVerifiedEmails(actionName, xmlResponse) + result = self._parseListVerifiedEmails(actionName, xmlResponse) else: raise AmazonAPIError('Action %s is not supported. Please contact: vladimir@tagmask.com' % (actionName,)) return result