def test_helloEmail(self): self.maxDiff = None """Minimum required to send an email""" mail = Mail() mail.from_email = Email("*****@*****.**") mail.subject = "Hello World from the SendGrid Python Library" personalization = Personalization() personalization.add_to(Email("*****@*****.**")) mail.add_personalization(personalization) mail.add_content(Content("text/plain", "some text here")) mail.add_content( Content( "text/html", "<html><body>some text here</body></html>")) self.assertEqual( json.dumps( mail.get(), sort_keys=True), '{"content": [{"type": "text/plain", "value": "some text here"}, ' '{"type": "text/html", ' '"value": "<html><body>some text here</body></html>"}], ' '"from": {"email": "*****@*****.**"}, "personalizations": ' '[{"to": [{"email": "*****@*****.**"}]}], ' '"subject": "Hello World from the SendGrid Python Library"}' ) self.assertTrue(isinstance(str(mail), str))
def test_unicode_values_in_substitutions_helper(self): """ Test that the Substitutions helper accepts unicode values """ self.maxDiff = None """Minimum required to send an email""" mail = Mail() mail.from_email = Email("*****@*****.**") mail.subject = "Testing unicode substitutions with the SendGrid Python Library" personalization = Personalization() personalization.add_to(Email("*****@*****.**")) personalization.add_substitution(Substitution("%city%", u"Αθήνα")) mail.add_personalization(personalization) mail.add_content(Content("text/plain", "some text here")) mail.add_content( Content( "text/html", "<html><body>some text here</body></html>")) expected_result = { "content": [ { "type": "text/plain", "value": "some text here" }, { "type": "text/html", "value": "<html><body>some text here</body></html>" } ], "from": { "email": "*****@*****.**" }, "personalizations": [ { "substitutions": { "%city%": u"Αθήνα" }, "to": [ { "email": "*****@*****.**" } ] } ], "subject": "Testing unicode substitutions with the SendGrid Python Library", } self.assertEqual( json.dumps(mail.get(), sort_keys=True), json.dumps(expected_result, sort_keys=True) )
def send_email(to, subject, html_content, files=None, dryrun=False, cc=None, bcc=None, mime_subtype='mixed', **kwargs): """ Send an email with html content using sendgrid. To use this plugin: 0. include sendgrid subpackage as part of your Airflow installation, e.g., pip install airflow[sendgrid] 1. update [email] backend in airflow.cfg, i.e., [email] email_backend = airflow.contrib.utils.sendgrid.send_email 2. configure Sendgrid specific environment variables at all Airflow instances: SENDGRID_MAIL_FROM={your-mail-from} SENDGRID_API_KEY={your-sendgrid-api-key}. """ mail = Mail() mail.from_email = Email(os.environ.get('SENDGRID_MAIL_FROM')) mail.subject = subject # Add the recipient list of to emails. personalization = Personalization() to = get_email_address_list(to) for to_address in to: personalization.add_to(Email(to_address)) if cc: cc = get_email_address_list(cc) for cc_address in cc: personalization.add_cc(Email(cc_address)) if bcc: bcc = get_email_address_list(bcc) for bcc_address in bcc: personalization.add_bcc(Email(bcc_address)) mail.add_personalization(personalization) mail.add_content(Content('text/html', html_content)) # Add custom_args to personalization if present pers_custom_args = kwargs.get('personalization_custom_args', None) if isinstance(pers_custom_args, dict): for key in pers_custom_args.keys(): personalization.add_custom_arg(CustomArg(key, pers_custom_args[key])) # Add email attachment. for fname in files or []: basename = os.path.basename(fname) attachment = Attachment() with open(fname, "rb") as f: attachment.content = base64.b64encode(f.read()) attachment.type = mimetypes.guess_type(basename)[0] attachment.filename = basename attachment.disposition = "attachment" attachment.content_id = '<%s>' % basename mail.add_attachment(attachment) _post_sendgrid_mail(mail.get())
def _prepare_sendgrid_data(self): self.ensure_one() s_mail = Mail() s_mail.set_from(Email(self.email_from)) s_mail.set_reply_to(Email(self.reply_to)) s_mail.add_custom_arg(CustomArg('odoo_id', self.message_id)) html = self.body_html or ' ' p = re.compile(r'<.*?>') # Remove HTML markers text_only = self.body_text or p.sub('', html.replace('<br/>', '\n')) s_mail.add_content(Content("text/plain", text_only)) s_mail.add_content(Content("text/html", html)) test_address = config.get('sendgrid_test_address') # TODO For now only one personalization (transactional e-mail) personalization = Personalization() personalization.set_subject(self.subject or ' ') if not test_address: if self.email_to: personalization.add_to(Email(self.email_to)) for recipient in self.recipient_ids: personalization.add_to(Email(recipient.email)) if self.email_cc: personalization.add_cc(Email(self.email_cc)) else: _logger.info('Sending email to test address {}'.format( test_address)) personalization.add_to(Email(test_address)) self.email_to = test_address if self.sendgrid_template_id: s_mail.set_template_id(self.sendgrid_template_id.remote_id) for substitution in self.substitution_ids: personalization.add_substitution(Substitution( substitution.key, substitution.value)) s_mail.add_personalization(personalization) for attachment in self.attachment_ids: s_attachment = Attachment() # Datas are not encoded properly for sendgrid s_attachment.set_content(base64.b64encode(base64.b64decode( attachment.datas))) s_attachment.set_filename(attachment.name) s_mail.add_attachment(s_attachment) return s_mail.get()
def test_sendgrid_api_key(self): """Tests if including SendGrid API will throw an Exception""" # Minimum required to send an email self.max_diff = None mail = Mail() mail.from_email = Email("*****@*****.**") mail.subject = "Hello World from the SendGrid Python Library" personalization = Personalization() personalization.add_to(Email("*****@*****.**")) mail.add_personalization(personalization) # Try to include SendGrid API key try: mail.add_content( Content( "text/plain", "some SG.2123b1B.1212lBaC here")) mail.add_content( Content( "text/html", "<html><body>some SG.Ba2BlJSDba.232Ln2 here</body></html>")) self.assertEqual( json.dumps( mail.get(), sort_keys=True), '{"content": [{"type": "text/plain", "value": "some text here"}, ' '{"type": "text/html", ' '"value": "<html><body>some text here</body></html>"}], ' '"from": {"email": "*****@*****.**"}, "personalizations": ' '[{"to": [{"email": "*****@*****.**"}]}], ' '"subject": "Hello World from the SendGrid Python Library"}' ) # Exception should be thrown except Exception: pass # Exception not thrown else: self.fail("Should have failed as SendGrid API key included")
def send_mail(from_email, to_email, subject, body, html=False, from_name="Gitcoin.co", cc_emails=None): # make sure this subscriber is saved get_or_save_email_subscriber(to_email, 'internal') # debug logs print("-- Sending Mail '{}' to {}".format(subject, to_email)) # setup sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) from_email = Email(from_email, from_name) to_email = Email(to_email) contenttype = "text/plain" if not html else "text/html" # build content content = Content(contenttype, html) if html else Content(contenttype, body) if settings.DEBUG: to_email = Email(settings.CONTACT_EMAIL) #just to be double secret sure of what were doing in dev subject = "[DEBUG] " + subject mail = Mail(from_email, subject, to_email, content) # build personalization (BCC + CC) p = Personalization() p.add_to(to_email) if cc_emails: #only add CCif not in prod for cc_addr in set(cc_emails): cc_addr = Email(cc_addr) if settings.DEBUG: cc_addr = to_email if cc_addr._email != to_email._email: p.add_to(cc_addr) p.add_bcc(Email(settings.BCC_EMAIL)) mail.add_personalization(p) # send mails response = sg.client.mail.send.post(request_body=mail.get()) return response
def _create_email(self, email: dict, email_id: str) -> Mail: self.log_debug('converting email %s to sendgrid format', email_id) mail = Mail() personalization = Personalization() for i, to in enumerate(email.get('to', [])): personalization.add_to(Email(to)) self.log_debug('added to %d to email %s', i, email_id) for i, cc in enumerate(email.get('cc', [])): personalization.add_cc(Email(cc)) self.log_debug('added cc %d to email %s', i, email_id) for i, bcc in enumerate(email.get('bcc', [])): personalization.add_bcc(Email(bcc)) self.log_debug('added bcc %d to email %s', i, email_id) mail.add_personalization(personalization) self.log_debug('added recipients to email %s', email_id) mail.subject = email.get('subject', '(no subject)') self.log_debug('added subject to email %s', email_id) mail.add_content( Content('text/html', email.get('body', '(no content)'))) self.log_debug('added content to email %s', email_id) mail.from_email = Email(email.get('from')) self.log_debug('added from to email %s', email_id) for i, attachment in enumerate(email.get('attachments', [])): mail.add_attachment(self._create_attachment(attachment)) self.log_debug('added attachment %d to email %s', i, email_id) self.log_debug('converted email %s to sendgrid format', email_id) return mail
def send_email(self, subject, message, recipient, attachments=None, ismultiple=False): mail = Mail( from_email="*****@*****.**", html_content=message, subject=subject, ) personalize = Personalization() if ismultiple: for email in recipient: #add_bcc isn't working right now, and there doesn't seem to be a straightforward workaround #will now work if any of the emails in recipient list is invalid personalize.add_to(Email(email)) else: personalize.add_to(Email(recipient)) mail.add_personalization(personalize) if attachments: encoded_file = base64.b64encode(attachments).decode() attachedFile = Attachment(FileContent(encoded_file), FileName('event.ics'), FileType('ical/ics'), Disposition('attachment')) mail.attachment = attachedFile try: response = self.client.mail.send.post(request_body=mail.get()) except Exception as e: print(e.message)
def send_email_sendgrid(to, subject, html_content, files=None, dryrun=False, cc=None, bcc=None, mime_subtype='mixed'): """ Send an email with html content using sendgrid. """ mail = Mail() mail.from_email = Email(configuration.get('sendgrid', 'SENDGRID_MAIL_FROM')) mail.subject = subject # Add the list of to emails. to = get_email_address_list(to) personalization = Personalization() for to_address in to: personalization.add_to(Email(to_address)) mail.add_personalization(personalization) mail.add_content(Content('text/html', html_content)) # Add email attachment. for fname in files or []: basename = os.path.basename(fname) attachment = Attachment() with open(fname, "rb") as f: attachment.content = base64.b64encode(f.read()) attachment.type = mimetypes.guess_type(basename)[0] attachment.filename = basename attachment.disposition = "attachment" attachment.content_id = '<%s>' % basename mail.add_attachment(attachment) _post_sendgrid_mail(mail.get())
def _prepare_email_message(self): """Prepare email message for Sendgrid :return: Sendgrid Email message """ mail = Mail() mail.from_email = Email(self.source) mail.subject = self.subject # personalization of email personalization = Personalization() # add multiple recipients for to_email in self._convert_var_type_to_list(self.destination): personalization.add_to(Email(to_email)) mail.add_personalization(personalization) # add cc (if any) if self.cc: for cc_email in self._convert_var_type_to_list(self.cc): personalization.add_cc(Email(cc_email)) mail.add_personalization(personalization) # add bcc (if any) if self.bcc: for bcc_email in self._convert_var_type_to_list(self.bcc): personalization.add_bcc(Email(bcc_email)) mail.add_personalization(personalization) # add content of email if self.body: mail.add_content(Content('text/plain', self.body)) if self.html_message: mail.add_content(Content('text/html', self.html_message)) # add attachments if self.attachment: mail.add_attachment(self.attachment) return mail.get()
def _build_sg_mail(self, email): mail = Mail() from_name, from_email = rfc822.parseaddr(email.from_email) # Python sendgrid client should improve # sendgrid/helpers/mail/mail.py:164 if not from_name: from_name = None mail.set_from(Email(from_email, from_name)) mail.set_subject(email.subject) personalization = Personalization() for e in email.to: personalization.add_to(Email(e)) for e in email.cc: personalization.add_cc(Email(e)) for e in email.bcc: personalization.add_bcc(Email(e)) personalization.set_subject(email.subject) mail.add_content(Content("text/plain", email.body)) if isinstance(email, EmailMultiAlternatives): for alt in email.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif email.content_subtype == "html": mail.contents = [] mail.add_content(Content("text/plain", ' ')) mail.add_content(Content("text/html", email.body)) if hasattr(email, 'categories'): for c in email.categories: mail.add_category(Category(c)) if hasattr(email, 'custom_args'): for k, v in email.custom_args.items(): mail.add_custom_arg(CustomArg(k, v)) if hasattr(email, 'template_id'): mail.set_template_id(email.template_id) if hasattr(email, 'substitutions'): for key, value in email.substitutions.items(): personalization.add_substitution(Substitution(key, value)) # SendGrid does not support adding Reply-To as an extra # header, so it needs to be manually removed if it exists. reply_to_string = "" for key, value in email.extra_headers.items(): if key.lower() == "reply-to": reply_to_string = value else: mail.add_header({key: value}) # Note that if you set a "Reply-To" header *and* the reply_to # attribute, the header's value will be used. if not mail.reply_to and hasattr(email, "reply_to") and email.reply_to: # SendGrid only supports setting Reply-To to a single address. # See https://github.com/sendgrid/sendgrid-csharp/issues/339. reply_to_string = email.reply_to[0] # Determine whether reply_to contains a name and email address, or # just an email address. if reply_to_string: reply_to_name, reply_to_email = rfc822.parseaddr(reply_to_string) if reply_to_name and reply_to_email: mail.set_reply_to(Email(reply_to_email, reply_to_name)) elif reply_to_email: mail.set_reply_to(Email(reply_to_email)) for attachment in email.attachments: if isinstance(attachment, MIMEBase): attach = Attachment() attach.set_filename(attachment.get_filename()) attach.set_content(base64.b64encode(attachment.get_payload())) mail.add_attachment(attach) elif isinstance(attachment, tuple): attach = Attachment() attach.set_filename(attachment[0]) base64_attachment = base64.b64encode(attachment[1]) if sys.version_info >= (3,): attach.set_content(str(base64_attachment, 'utf-8')) else: attach.set_content(base64_attachment) attach.set_type(attachment[2]) mail.add_attachment(attach) mail.add_personalization(personalization) return mail.get()
def notify(data, to, template_id=None, subject_template="N/A", body_template="N/A", sender=SENDGRID_SENDER, debug_options=None): if isinstance(to, basestring): raise Exception("Expected list in 'to'") apikey = get_sendgrid_api_key() if not apikey: logging.warn("%s is not set in config. Not sending emails" % CONFIG_APIKEY) return sg = sendgrid.SendGridAPIClient(apikey=apikey) subject = subject_template.format(**data) content = Content('text/html', body_template.format(**data)) mail = Mail() mail.set_from(sender) mail.set_subject(subject) mail.add_content(content) personalization = Personalization() for address in to: personalization.add_to(Email(address)) if template_id: ## flatten the data dict, adding prefix to nested dicts for key, value in data.iteritems(): if isinstance(value, dict): for key2, value2 in value.iteritems(): placeholder = "-%s__%s-" % (key, key2) personalization.add_substitution( Substitution(placeholder, value2)) else: placeholder = "-%s-" % key personalization.add_substitution( Substitution(placeholder, value)) mail.set_template_id(template_id) mail.add_personalization(personalization) no_emails = False if debug_options: for option in debug_options.split(","): if option == "noEmails": no_emails = True if no_emails: logging.info("noEmails debug option set, not sending email: %s" % mail.get()) else: response = sg.client.mail.send.post(request_body=mail.get()) logging.debug(response.status_code) logging.debug(response.body) logging.debug(response.headers)
def send_pdf_email_using_SendGrid(sender, receiver, mail_subject, mail_content, pdf_attachment, txt_attachment=None, cc_email=None): # Where it was uploaded Path. file_path = pdf_attachment with open(file_path, 'rb') as f: data = f.read() # Encode contents of file as Base 64 encoded = base64.b64encode(data).decode() """Build PDF attachment""" attachment = Attachment() attachment.file_content = FileContent(encoded) attachment.file_type = FileType('application/pdf') attachment.file_name = FileName('your_quote.pdf') attachment.disposition = Disposition('attachment') attachment.content_id = ContentId('Example Content ID') """ Add txt file """ if txt_attachment: file_path = txt_attachment with open(file_path, 'rb') as f: data = f.read() # Encode contents of file as Base 64 encoded = base64.b64encode(data).decode() """Build txt attachment""" attachment2 = Attachment() attachment2.file_content = FileContent(encoded) attachment2.file_type = FileType('text/html') attachment2.file_name = FileName('quote.txt') attachment2.disposition = Disposition('attachment') attachment2.content_id = ContentId('Text Example Content ID') message = Mail( from_email=sender, #to_emails = receiver, # Removed since it generates an extra email with SendGrid subject=mail_subject, html_content=mail_content) message.attachment = attachment if cc_email: cc = Email(cc_email) to = Email(receiver) p = Personalization() p.add_to(to) p.add_cc(cc) message.add_personalization(p) else: # no cc to = Email(receiver) p = Personalization() p.add_to(to) message.add_personalization(p) if txt_attachment: message.add_attachment(attachment2) try: sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY')) response = sg.send(message) print(response.status_code) print(response.body) print(response.headers) except Exception as e: print(e.message) return
def _build_sg_mail(self, msg): mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) mail.subject = msg.subject personalization = Personalization() for addr in msg.to: personalization.add_to(Email(*self._parse_email_address(addr))) for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) for addr in msg.bcc: personalization.add_bcc(Email(*self._parse_email_address(addr))) personalization.subject = msg.subject for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization.add_header(Header(k, v)) if hasattr(msg, "template_id"): mail.template_id = msg.template_id if hasattr(msg, "substitutions"): for k, v in msg.substitutions.items(): personalization.add_substitution(Substitution(k, v)) mail.add_personalization(personalization) if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to)) if reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name: raise ValueError("Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, basestring): if len(msg.reply_to) > 1: raise ValueError("Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email(*self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: attachment = Attachment() if isinstance(attch, MIMEBase): filename = attch.get_filename() if not filename: ext = mimetypes.guess_extension(attch.get_content_type()) filename = "part-{0}{1}".format(uuid.uuid4().hex, ext) attachment.filename = filename # todo: Read content if stream? attachment.content = attch.get_payload().replace("\n", "") attachment.type = attch.get_content_type() content_id = attch.get("Content-ID") if content_id: attachment.content_id = content_id attachment.disposition = "inline" else: filename, content, mimetype = attch attachment.filename = filename # todo: Read content if stream? attachment.content = base64.b64encode(content) attachment.type = mimetype mail.add_attachment(attachment) if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.open_tracking = OpenTracking(self.track_email) mail.tracking_settings = tracking_settings return mail.get()
def _build_sg_mail(self, msg: EmailMessage) -> Dict: """ Serializes a Django EmailMessage into its JSON representation. Returns a Dict of mail data to be consumed by the sendgrid api. """ mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) personalization_headers = [] for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization_headers.append(Header(k, v)) if hasattr(msg, "ip_pool_name"): if not isinstance(msg.ip_pool_name, str): raise ValueError( "ip_pool_name must be a str, got: {}; ".format( type(msg.ip_pool_name))) # Validate ip_pool_name length before attempting to add if not 2 <= len(msg.ip_pool_name) <= 64: raise ValueError( "the number of characters of ip_pool_name must be min 2 and max 64, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/" "index.html#-Request-Body-Parameters".format( len(msg.ip_pool_name))) if SENDGRID_5: ip_pool_name = msg.ip_pool_name else: ip_pool_name = IpPoolName(msg.ip_pool_name) mail.ip_pool_name = ip_pool_name if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to[0])) if (reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name): raise ValueError( "Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, str): if len(msg.reply_to) > 1: raise ValueError( "Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email( *self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: sg_attch = self._create_sg_attachment(attch) mail.add_attachment(sg_attch) if self._is_transaction_template(msg): if msg.body: logger.warning( "Message body is ignored in transactional template") else: msg.body = " " if msg.body == "" else msg.body if hasattr(msg, "template_id"): # Template mails should not have subject and content attributes mail.template_id = msg.template_id if not self._is_transaction_template(msg): # In sendgrid v6 we should not specify subject and content between request parameter # when we are sending a request for a transactional template mail.subject = msg.subject if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) if hasattr(msg, "personalizations"): for personalization in msg.personalizations: if type(personalization) == Dict: personalization = dict_to_personalization(personalization) assert type(personalization) == Personalization mail.add_personalization( self._build_sg_personalization( msg, personalization_headers, existing_personalizations=personalization, )) elif getattr(msg, "make_private", False): for to in msg.to: mail.add_personalization( self._build_sg_personalization( msg, personalization_headers, to=[to], )) else: mail.add_personalization( self._build_sg_personalization( msg, personalization_headers, to=msg.to, )) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) if hasattr(msg, "asm"): if "group_id" not in msg.asm: raise KeyError("group_id not found in asm") if "groups_to_display" in msg.asm: mail.asm = ASM(msg.asm["group_id"], msg.asm["groups_to_display"]) else: mail.asm = ASM(msg.asm["group_id"]) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings # Handle email tracking tracking_settings = getattr(msg, "tracking_settings", None) if not isinstance(tracking_settings, TrackingSettings): tracking_settings = TrackingSettings() if tracking_settings.open_tracking is None: tracking_settings.open_tracking = OpenTracking(self.track_email) if tracking_settings.click_tracking is None: tracking_settings.click_tracking = ClickTracking( self.track_clicks_html, self.track_clicks_plain) mail.tracking_settings = tracking_settings return mail.get()
def _build_sg_mail(self, email): mail = Mail() from_name, from_email = rfc822.parseaddr(email.from_email) # Python sendgrid client should improve # sendgrid/helpers/mail/mail.py:164 if not from_name: from_name = None mail.from_email = Email(from_email, from_name) mail.subject = email.subject mail_settings = MailSettings() personalization = Personalization() for e in email.to: personalization.add_to(Email(e)) for e in email.cc: personalization.add_cc(Email(e)) for e in email.bcc: personalization.add_bcc(Email(e)) personalization.subject = email.subject if email.content_subtype == "html": mail.add_content(Content("text/html", email.body)) else: mail.add_content(Content("text/plain", email.body)) if isinstance(email, EmailMultiAlternatives): for alt in email.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif email.content_subtype == "html": mail.contents = [] mail.add_content(Content("text/plain", ' ')) if hasattr(email, 'categories'): for c in email.categories: mail.add_category(Category(c)) if hasattr(email, 'custom_args'): for k, v in email.custom_args.items(): mail.add_custom_arg(CustomArg(k, v)) # Used to override the list management (password reset etc) if hasattr(email, 'bypass_list_management'): mail_settings.bypass_list_management = BypassListManagement( email.bypass_list_management) #Check for sandbox mode sandbox_mode = getattr(settings, "SENDGRID_SANDBOX", False) if sandbox_mode: sandbox_whitelist_domains = getattr( settings, "SENDGRID_SANDBOX_WHITELIST_DOMAINS", []) sandbox_whitelist = False for e in email.to: domain = e.split('@')[1] if domain in sandbox_whitelist_domains: sandbox_whitelist = True if not sandbox_whitelist: mail_settings.sandbox_mode = SandBoxMode(sandbox_mode) if hasattr(email, 'template_id'): mail.template_id = email.template_id # Version 3 dynamic data handle bars {{name}} if hasattr(email, 'dynamic_data'): personalization.dynamic_template_data = email.dynamic_data # Version 3 substitutions if hasattr(email, 'substitutions'): for key, value in email.substitutions.items(): personalization.add_substitution(Substitution(key, value)) # SendGrid does not support adding Reply-To as an extra # header, so it needs to be manually removed if it exists. reply_to_string = "" for key, value in email.extra_headers.items(): if key.lower() == "reply-to": reply_to_string = value else: mail.add_header({key: value}) # Note that if you set a "Reply-To" header *and* the reply_to # attribute, the header's value will be used. if not mail.reply_to and hasattr(email, "reply_to") and email.reply_to: # SendGrid only supports setting Reply-To to a single address. # See https://github.com/sendgrid/sendgrid-csharp/issues/339. reply_to_string = email.reply_to[0] # Determine whether reply_to contains a name and email address, or # just an email address. if reply_to_string: reply_to_name, reply_to_email = rfc822.parseaddr(reply_to_string) if reply_to_name and reply_to_email: mail.reply_to = Email(reply_to_email, reply_to_name) elif reply_to_email: mail.reply_to = Email(reply_to_email) for attachment in email.attachments: if isinstance(attachment, MIMEBase): attach = Attachment() attach.filename = attachment.get_filename() attach.content = base64.b64encode(attachment.get_payload()) mail.add_attachment(attach) elif isinstance(attachment, tuple): attach = Attachment() attach.filename = attachment[0] base64_attachment = base64.b64encode(attachment[1]) if sys.version_info >= (3, ): attach.content = str(base64_attachment, 'utf-8') else: attach.content = base64_attachment attach.type = attachment[2] mail.add_attachment(attach) mail.add_personalization(personalization) mail.mail_settings = mail_settings return mail.get()
def send_email(to, subject, html_content, files=None, dryrun=False, cc=None, bcc=None, mime_subtype='mixed', sandbox_mode=False, **kwargs): """ Send an email with html content using sendgrid. To use this plugin: 0. include sendgrid subpackage as part of your Airflow installation, e.g., pip install 'apache-airflow[sendgrid]' 1. update [email] backend in airflow.cfg, i.e., [email] email_backend = airflow.contrib.utils.sendgrid.send_email 2. configure Sendgrid specific environment variables at all Airflow instances: SENDGRID_MAIL_FROM={your-mail-from} SENDGRID_API_KEY={your-sendgrid-api-key}. """ if files is None: files = [] mail = Mail() from_email = kwargs.get('from_email') or os.environ.get('SENDGRID_MAIL_FROM') from_name = kwargs.get('from_name') or os.environ.get('SENDGRID_MAIL_SENDER') mail.from_email = Email(from_email, from_name) mail.subject = subject mail.mail_settings = MailSettings() if sandbox_mode: mail.mail_settings.sandbox_mode = SandBoxMode(enable=True) # Add the recipient list of to emails. personalization = Personalization() to = get_email_address_list(to) for to_address in to: personalization.add_to(Email(to_address)) if cc: cc = get_email_address_list(cc) for cc_address in cc: personalization.add_cc(Email(cc_address)) if bcc: bcc = get_email_address_list(bcc) for bcc_address in bcc: personalization.add_bcc(Email(bcc_address)) # Add custom_args to personalization if present pers_custom_args = kwargs.get('personalization_custom_args', None) if isinstance(pers_custom_args, dict): for key in pers_custom_args.keys(): personalization.add_custom_arg(CustomArg(key, pers_custom_args[key])) mail.add_personalization(personalization) mail.add_content(Content('text/html', html_content)) categories = kwargs.get('categories', []) for cat in categories: mail.add_category(Category(cat)) # Add email attachment. for fname in files: basename = os.path.basename(fname) attachment = Attachment() attachment.type = mimetypes.guess_type(basename)[0] attachment.filename = basename attachment.disposition = "attachment" attachment.content_id = '<{0}>'.format(basename) with open(fname, "rb") as f: attachment.content = base64.b64encode(f.read()).decode('utf-8') mail.add_attachment(attachment) _post_sendgrid_mail(mail.get())
def send_email(to, subject, html_content, files=None, dryrun=False, cc=None, bcc=None, mime_subtype='mixed', sandbox_mode=False, **kwargs): """ Send an email with html content using sendgrid. To use this plugin: 0. include sendgrid subpackage as part of your Airflow installation, e.g., pip install 'apache-airflow[sendgrid]' 1. update [email] backend in airflow.cfg, i.e., [email] email_backend = airflow.contrib.utils.sendgrid.send_email 2. configure Sendgrid specific environment variables at all Airflow instances: SENDGRID_MAIL_FROM={your-mail-from} SENDGRID_API_KEY={your-sendgrid-api-key}. """ if files is None: files = [] mail = Mail() from_email = kwargs.get('from_email') or os.environ.get( 'SENDGRID_MAIL_FROM') from_name = kwargs.get('from_name') or os.environ.get( 'SENDGRID_MAIL_SENDER') mail.from_email = Email(from_email, from_name) mail.subject = subject mail.mail_settings = MailSettings() if sandbox_mode: mail.mail_settings.sandbox_mode = SandBoxMode(enable=True) # Add the recipient list of to emails. personalization = Personalization() to = get_email_address_list(to) for to_address in to: personalization.add_to(Email(to_address)) if cc: cc = get_email_address_list(cc) for cc_address in cc: personalization.add_cc(Email(cc_address)) if bcc: bcc = get_email_address_list(bcc) for bcc_address in bcc: personalization.add_bcc(Email(bcc_address)) # Add custom_args to personalization if present pers_custom_args = kwargs.get('personalization_custom_args', None) if isinstance(pers_custom_args, dict): for key in pers_custom_args.keys(): personalization.add_custom_arg( CustomArg(key, pers_custom_args[key])) mail.add_personalization(personalization) mail.add_content(Content('text/html', html_content)) categories = kwargs.get('categories', []) for cat in categories: mail.add_category(Category(cat)) # Add email attachment. for fname in files: basename = os.path.basename(fname) attachment = Attachment() attachment.type = mimetypes.guess_type(basename)[0] attachment.filename = basename attachment.disposition = "attachment" attachment.content_id = '<{0}>'.format(basename) with open(fname, "rb") as file: attachment.content = base64.b64encode(file.read()).decode('utf-8') mail.add_attachment(attachment) _post_sendgrid_mail(mail.get())
class EmailClient(object): def __init__(self, empresa_id): try: self.empresa_id = empresa_id # llamar las configuraciones en la DB self.email_config = SendgridConf.get_sg_config(self.empresa_id) # crear los atributos de la instancia de SendGrid self.sg = SendGridAPIClient(api_key=self.email_config.api_key) # build message self.message = Mail() self.message.from_email = Email(self.email_config.asunto_email_dte, self.email_config.nombre_email_dte) # set personalization self.personalization = Personalization() logger.info("Se instanció EmailClient para la empresa : " + empresa_id) except Exception as e: logger.error("Error al instanciar EmailClient") logger.error(e) raise Exception(e) def enviar_correo_dte(self, correo): try: # valores de envío self.personalization.add_to( Email(correo.correo, correo.nombre_cliente)) self.message.subject = correo.asunto self.message.add_content(Content("text/html", correo.html)) # valores personalizados self.personalization.add_custom_arg( CustomArg('email_id', str(correo.id))) self.personalization.add_custom_arg( CustomArg('empresa', correo.empresa.rut)) self.personalization.add_custom_arg( CustomArg('rut_receptor', correo.rut_receptor)) self.personalization.add_custom_arg( CustomArg('rut_emisor', correo.rut_emisor)) self.personalization.add_custom_arg( CustomArg('tipo_envio', correo.tipo_envio)) self.personalization.add_custom_arg( CustomArg('tipo_dte', str(correo.tipo_dte.id_documento))) self.personalization.add_custom_arg( CustomArg('numero_folio', str(correo.numero_folio))) self.personalization.add_custom_arg( CustomArg('resolucion_receptor', str(correo.resolucion_receptor))) self.personalization.add_custom_arg( CustomArg('resolucion_emisor', str(correo.resolucion_emisor))) self.personalization.add_custom_arg( CustomArg('monto', str(correo.monto))) self.personalization.add_custom_arg( CustomArg('fecha_emision', str(correo.fecha_emision))) self.personalization.add_custom_arg( CustomArg('fecha_recepcion', str(correo.fecha_recepcion))) self.personalization.add_custom_arg( CustomArg('estado_documento', correo.estado_documento)) self.personalization.add_custom_arg( CustomArg('tipo_operacion', correo.tipo_operacion)) self.personalization.add_custom_arg( CustomArg('tipo_receptor', correo.tipo_receptor)) self.personalization.add_custom_arg( CustomArg('id_envio', str(correo.id_envio))) logger.info(correo) if correo.xml: attach = Attachment() attach.filename = get_file_name_from_storage(correo.xml.name) attach.content = base64.b64encode(correo.xml.file.read()) self.message.add_attachment(attach) if correo.pdf: attach = Attachment() attach.filename = get_file_name_from_storage(correo.pdf.name) attach.content = base64.b64encode(correo.pdf.file.read()) self.message.add_attachment(attach) if correo.adjunto1: attach = Attachment() attach.filename = get_file_name_from_storage( correo.adjunto1.name) attach.content = base64.b64encode(correo.adjunto1.file.read()) self.message.add_attachment(attach) self.message.add_personalization(self.personalization) # enviando el mail response = self.sg.client.mail.send.post( request_body=self.message.get()) # imprimiendo respuesta logger.info(response.status_code) logger.info(response.headers) logger.info(response.body) except Exception as e: logger.error("Error EmailClient.enviar_correo_dte ") logger.error(e) raise Exception(e) def send_report_to_user_with_attach(self, user_email, report): try: # parametros de correo reporte self.message.from_email = Email( self.email_config.asunto_email_reporte, self.email_config.nombre_email_reporte) # buscar usuario template_config = TemplateReporte.get_configuration( self.empresa_id) # preparar template del correo reporte user = User.objects.get(email=user_email) html = str(template_config.template_html).format( user_name=user.first_name) # valores de envío self.personalization.add_to(Email(user_email, user.first_name)) self.message.subject = template_config.asunto_reporte self.message.add_content(Content("text/html", html)) # adjuntar excel si esta if report['report']: attach = Attachment() attach.content = base64.b64encode(report['report']) attach.filename = report['name'] self.message.add_attachment(attach) self.message.add_personalization(self.personalization) # enviando el correo response = self.sg.client.mail.send.post( request_body=self.message.get()) # imprimiendo respuesta logger.info(response) logger.info(response.status_code) logger.info(response.headers) logger.info(response.body) except Exception as e: logger.error( "Error en EmailClient.send_report_to_user_with_attach") logger.error(e) raise Exception(e)
def _make_sendgrid_mail(self, message): mail = Mail() if message.sender: mail.from_email = Email(message.sender) else: mail.from_email = Email(self.default_sender) template_id = getattr(message, 'template_id', None) if template_id: mail.template_id = template_id if message.subject: mail.subject = message.subject if message.recipients: if type(message.recipients) == list: personalization = Personalization() for recipient in message.recipients: personalization.add_to(Email(recipient)) if message.cc: if type(message.cc) == list: for recipient in message.cc: personalization.add_cc(Email(recipient)) else: raise Exception("type(cc) must be list") if message.bcc: if type(message.bcc) == list: for recipient in message.bcc: personalization.add_bcc(Email(recipient)) else: raise Exception("type(bcc) must be list") dynamic_template_data = getattr(message, 'dynamic_template_data', None) if dynamic_template_data: personalization.dynamic_template_data = dynamic_template_data mail.add_personalization(personalization) else: raise Exception("unsupported type yet") if message.body: mail.add_content(Content("text/plain", message.body)) if message.html: mail.add_content(Content("text/html", message.html)) if message.reply_to: mail.reply_to = Email(message.reply_to) if message.attachments: for attachment in message.attachments: file_content = base64.b64encode( attachment.data).decode('UTF-8') mail.add_attachment( Attachment( file_content=file_content, file_name=attachment.filename, file_type=attachment.content_type, disposition=attachment.disposition, )) return mail
def _build_sg_mail(self, email): mail = Mail() from_name, from_email = rfc822.parseaddr(email.from_email) # Python sendgrid client should improve # sendgrid/helpers/mail/mail.py:164 if not from_name: from_name = None mail.from_email = Email(from_email, from_name) mail.subject = email.subject personalization = Personalization() for e in email.to: personalization.add_to(Email(e)) for e in email.cc: personalization.add_cc(Email(e)) for e in email.bcc: personalization.add_bcc(Email(e)) personalization.subject = email.subject mail.add_content(Content("text/plain", email.body)) if isinstance(email, EmailMultiAlternatives): for alt in email.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif email.content_subtype == "html": mail.contents = [] mail.add_content(Content("text/plain", ' ')) mail.add_content(Content("text/html", email.body)) if hasattr(email, 'categories'): for c in email.categories: mail.add_category(Category(c)) if hasattr(email, 'template_id'): mail.template_id = email.template_id if hasattr(email, 'substitutions'): for k, v in email.substitutions.items(): personalization.add_substitution(Substitution(k, v)) if hasattr(email, 'custom_args'): for item in email.custom_args: for k, v in item.items(): mail.add_custom_arg(CustomArg(k, v)) for k, v in email.extra_headers.items(): mail.add_header({k: v}) for attachment in email.attachments: if isinstance(attachment, MIMEBase): attach = Attachment() attach.filename = attachment.get_filename() attach.content = base64.b64encode(attachment.get_payload()) mail.add_attachment(attach) elif isinstance(attachment, tuple): attach = Attachment() attach.filename = attachment[0] base64_attachment = base64.b64encode(attachment[1]) if sys.version_info >= (3,): attach.content = str(base64_attachment, 'utf-8') else: attach.content = base64_attachment attach.type = attachment[2] mail.add_attachment(attach) mail.add_personalization(personalization) return mail.get()
def stuff(): """ Accept data from the form, generate, display, and email QR code to user """ for user in db.session.query(User).all(): if request.form['email'] == user.get_email(): return 'Email address {} already found in database!\ Please re-enter the form correctly!'.format(request.form['email']) if str(request.form['phone_number']) == str(user.get_phone()): return 'Phone number {} already found in database!\ Please re-enter the form correctly!'.format( request.form['phone_number']) codex_id = get_current_id() user = User(name=request.form['name'], email=request.form['email'], phone=request.form['phone_number'], codex_id=codex_id, department=DEPARTMENTS[request.form['department']]) try: db.session.add(user) db.session.commit() except exc.IntegrityError: return "Error occurred trying to enter values into the database!" img = generate_qr(request.form, codex_id) img.save('qr.png') img_data = open('qr.png', 'rb').read() encoded = base64.b64encode(img_data).decode() name = request.form['name'] from_email = Email(FROM_EMAIL) to_email = Email(request.form['email']) p = None if request.form['email_second_person'] and request.form[ 'name_second_person']: cc_email = Email(request.form['email_second_person']) name += ', {}'.format(request.form['name_second_person']) p = Personalization() p.add_to(cc_email) subject = 'Registration for CodeX April 2019 - ID {}'.format(codex_id) message = """<img src='https://drive.google.com/uc?id=12VCUzNvU53f_mR7Hbumrc6N66rCQO5r-&export=download'> <hr> {}, your registration is done! <br/> A QR code has been attached below! <br/> You're <b>required</b> to present this on the day of the event. """.format(name) content = Content('text/html', message) mail = Mail(from_email, subject, to_email, content) if p: mail.add_personalization(p) attachment = Attachment() attachment.type = 'image/png' attachment.filename = 'qr.png' attachment.content = encoded mail.add_attachment(attachment) response = sg.client.mail.send.post(request_body=mail.get()) print(response.status_code) print(response.body) print(response.headers) return 'Please save this QR Code. It has also been emailed to you.<br><img src=\ "data:image/png;base64, {}"/>'.format(encoded)
def send_summary_email(data, context): print(data, context) force_send = data.get('force_send', False) sg = sendgrid.SendGridAPIClient(api_key=os.environ['SENDGRID_API_KEY']) dt = datetime.datetime.now(pytz.timezone('US/Pacific')) client = datastore.Client() # cannot have multiple filters!? groups_query = client.query( kind='group', filters=[('end_date', '>=', (dt.date() - datetime.timedelta(days=1)).isoformat())], ) subject_prefix = '[DEV] ' if IS_DEVELOPMENT else '' for group in groups_query.fetch(): print(group) if dt.date().isoformat() < group['start_date']: continue final_summary_date = ( datetime.datetime.strptime(group['end_date'], '%Y-%m-%d').date() + datetime.timedelta(days=1) ).isoformat() is_final = dt.date().isoformat() == final_summary_date if not force_send and dt.weekday() != 0 and not is_final and not IS_DEVELOPMENT: continue goals_query = client.query( kind='goal2', ancestor=group.key ) goals = list(goals_query.fetch()) if not goals: print('no goals found for group {}'.format(group)) continue personalization = Personalization() total_progress = 0 total_denominator = 0 for goal in goals: print(goal) goal['denominator'] = len(goal.get('opportunities', [])) goal['progress'] = len(goal.get('completions', [])) goal['background-color'] = compute_goal_color(goal) goal['completed_last_week'] = False opportunities = goal.get('opportunities', []) if opportunities and max(opportunities) in goal.get('completions', []): goal['completed_last_week'] = True total_progress += goal['progress'] total_denominator += goal['denominator'] if IS_DEVELOPMENT and DEVELOPER_EMAIL_STRING not in goal['email']: continue personalization.add_to(To(email=goal['email'])) goals = sorted(goals, key=lambda x: x['progress'] / goal['denominator'], reverse=True) template = TEMPLATE_ENV.get_template("summary_email.tmpl") rendered = template.render(**locals()) mail = Mail( from_email=From(email="*****@*****.**"), subject=Subject(subject_prefix + "Eccountabot {}Summary {} {}".format( 'Final ' if is_final else '', group['name'], dt.date().isoformat() )), html_content=Content("text/html", rendered) ) mail.add_personalization(personalization) try: response = sg.client.mail.send.post(request_body=mail.get()) except exceptions.BadRequestsError as e: print(e.body) raise
def _build_sg_mail(self, msg): mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) mail.subject = msg.subject personalization = Personalization() for addr in msg.to: personalization.add_to(Email(*self._parse_email_address(addr))) for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) for addr in msg.bcc: personalization.add_bcc(Email(*self._parse_email_address(addr))) personalization.subject = msg.subject for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization.add_header(Header(k, v)) if hasattr(msg, "template_id"): mail.template_id = msg.template_id if hasattr(msg, "substitutions"): for k, v in msg.substitutions.items(): personalization.add_substitution(Substitution(k, v)) # write through the ip_pool_name attribute if hasattr(msg, "ip_pool_name"): if not isinstance(msg.ip_pool_name, basestring): raise ValueError( "ip_pool_name must be a string, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/" "index.html#-Request-Body-Parameters".format( type(msg.ip_pool_name))) if not 2 <= len(msg.ip_pool_name) <= 64: raise ValueError( "the number of characters of ip_pool_name must be min 2 and max 64, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/" "index.html#-Request-Body-Parameters".format( len(msg.ip_pool_name))) mail.ip_pool_name = msg.ip_pool_name # write through the send_at attribute if hasattr(msg, "send_at"): if not isinstance(msg.send_at, int): raise ValueError( "send_at must be an integer, got: {}; " "see https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html#-Send-At".format( type(msg.send_at))) personalization.send_at = msg.send_at mail.add_personalization(personalization) if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to)) if reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name: raise ValueError("Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, basestring): if len(msg.reply_to) > 1: raise ValueError("Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email(*self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: attachment = Attachment() if isinstance(attch, MIMEBase): filename = attch.get_filename() if not filename: ext = mimetypes.guess_extension(attch.get_content_type()) filename = "part-{0}{1}".format(uuid.uuid4().hex, ext) attachment.filename = filename # todo: Read content if stream? attachment.content = attch.get_payload().replace("\n", "") attachment.type = attch.get_content_type() content_id = attch.get("Content-ID") if content_id: # Strip brackets since sendgrid's api adds them if content_id.startswith("<") and content_id.endswith(">"): content_id = content_id[1:-1] attachment.content_id = content_id attachment.disposition = "inline" else: filename, content, mimetype = attch attachment.filename = filename # Convert content from chars to bytes, in both Python 2 and 3. # todo: Read content if stream? if isinstance(content, str): content = content.encode('utf-8') attachment.content = base64.b64encode(content).decode() attachment.type = mimetype mail.add_attachment(attachment) msg.body = ' ' if msg.body == '' else msg.body if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) if hasattr(msg, "asm"): if "group_id" not in msg.asm: raise KeyError("group_id not found in asm") if "groups_to_display" in msg.asm: mail.asm = ASM(msg.asm["group_id"], msg.asm["groups_to_display"]) else: mail.asm = ASM(msg.asm["group_id"]) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.open_tracking = OpenTracking(self.track_email) mail.tracking_settings = tracking_settings return mail.get()
def send_mail(from_email, _to_email, subject, body, html=False, from_name="Gitcoin.co", cc_emails=None, categories=None, debug_mode=False): """Send email via SendGrid.""" # make sure this subscriber is saved if not settings.SENDGRID_API_KEY: logger.warning('No SendGrid API Key set. Not attempting to send email.') return if categories is None: categories = ['default'] to_email = _to_email get_or_save_email_subscriber(to_email, 'internal') # setup from_name = str(from_name) subject = str(subject) sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) from_email = Email(from_email, from_name) to_email = Email(to_email) contenttype = "text/plain" if not html else "text/html" # build content content = Content(contenttype, html) if html else Content(contenttype, body) # TODO: A bit of a hidden state change here. Really confusing when doing development. # Maybe this should be a variable passed into the function the value is set upstream? if settings.IS_DEBUG_ENV or debug_mode: to_email = Email(settings.CONTACT_EMAIL) # just to be double secret sure of what were doing in dev subject = _("[DEBUG] ") + subject mail = Mail(from_email, subject, to_email, content) response = None # build personalization if cc_emails: p = Personalization() p.add_to(to_email) for cc_addr in set(cc_emails): cc_addr = Email(cc_addr) if settings.IS_DEBUG_ENV: cc_addr = to_email if cc_addr._email != to_email._email: p.add_to(cc_addr) mail.add_personalization(p) # categories for category in categories: mail.add_category(Category(category)) # debug logs logger.info(f"-- Sending Mail '{subject}' to {to_email}") try: response = sg.client.mail.send.post(request_body=mail.get()) except UnauthorizedError as e: logger.error(f'-- Sendgrid Mail failure - Unauthorized - Check sendgrid credentials') logger.error(e) except HTTPError as e: logger.error(f'-- Sendgrid Mail failure - {e}') return response
def test_kitchenSink(self): self.maxDiff = None """All settings set""" mail = Mail() mail.from_email = Email("*****@*****.**", "Example User") mail.subject = "Hello World from the SendGrid Python Library" personalization = Personalization() personalization.add_to(Email("*****@*****.**", "Example User")) personalization.add_to(Email("*****@*****.**", "Example User")) personalization.add_cc(Email("*****@*****.**", "Example User")) personalization.add_cc(Email("*****@*****.**", "Example User")) personalization.add_bcc(Email("*****@*****.**")) personalization.add_bcc(Email("*****@*****.**")) personalization.subject = "Hello World from the Personalized SendGrid Python Library" personalization.add_header(Header("X-Test", "test")) personalization.add_header(Header("X-Mock", "true")) personalization.add_substitution( Substitution("%name%", "Example User")) personalization.add_substitution(Substitution("%city%", "Denver")) personalization.add_custom_arg(CustomArg("user_id", "343")) personalization.add_custom_arg(CustomArg("type", "marketing")) personalization.send_at = 1443636843 mail.add_personalization(personalization) personalization2 = Personalization() personalization2.add_to(Email("*****@*****.**", "Example User")) personalization2.add_to(Email("*****@*****.**", "Example User")) personalization2.add_cc(Email("*****@*****.**", "Example User")) personalization2.add_cc(Email("*****@*****.**", "Example User")) personalization2.add_bcc(Email("*****@*****.**")) personalization2.add_bcc(Email("*****@*****.**")) personalization2.subject = "Hello World from the Personalized SendGrid Python Library" personalization2.add_header(Header("X-Test", "test")) personalization2.add_header(Header("X-Mock", "true")) personalization2.add_substitution( Substitution("%name%", "Example User")) personalization2.add_substitution(Substitution("%city%", "Denver")) personalization2.add_custom_arg(CustomArg("user_id", "343")) personalization2.add_custom_arg(CustomArg("type", "marketing")) personalization2.send_at = 1443636843 mail.add_personalization(personalization2) mail.add_content(Content("text/plain", "some text here")) mail.add_content( Content( "text/html", "<html><body>some text here</body></html>")) attachment = Attachment() attachment.content = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12" attachment.type = "application/pdf" attachment.filename = "balance_001.pdf" attachment.disposition = "attachment" attachment.content_id = "Balance Sheet" mail.add_attachment(attachment) attachment2 = Attachment() attachment2.content = "BwdW" attachment2.type = "image/png" attachment2.filename = "banner.png" attachment2.disposition = "inline" attachment2.content_id = "Banner" mail.add_attachment(attachment2) mail.template_id = "13b8f94f-bcae-4ec6-b752-70d6cb59f932" mail.add_section( Section( "%section1%", "Substitution Text for Section 1")) mail.add_section( Section( "%section2%", "Substitution Text for Section 2")) mail.add_header(Header("X-Test1", "test1")) mail.add_header(Header("X-Test3", "test2")) mail.add_header({"X-Test4": "test4"}) mail.add_category(Category("May")) mail.add_category(Category("2016")) mail.add_custom_arg(CustomArg("campaign", "welcome")) mail.add_custom_arg(CustomArg("weekday", "morning")) mail.send_at = 1443636842 mail.batch_id = "sendgrid_batch_id" mail.asm = ASM(99, [4, 5, 6, 7, 8]) mail.ip_pool_name = "24" mail_settings = MailSettings() mail_settings.bcc_settings = BCCSettings( True, Email("*****@*****.**")) mail_settings.bypass_list_management = BypassListManagement(True) mail_settings.footer_settings = FooterSettings( True, "Footer Text", "<html><body>Footer Text</body></html>") mail_settings.sandbox_mode = SandBoxMode(True) mail_settings.spam_check = SpamCheck( True, 1, "https://spamcatcher.sendgrid.com") mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.click_tracking = ClickTracking( True, True) tracking_settings.open_tracking = OpenTracking( True, "Optional tag to replace with the open image in the body of the message") tracking_settings.subscription_tracking = SubscriptionTracking( True, "text to insert into the text/plain portion of the message", "<html><body>html to insert into the text/html portion of the message</body></html>", "Optional tag to replace with the open image in the body of the message") tracking_settings.ganalytics = Ganalytics( True, "some source", "some medium", "some term", "some content", "some campaign") mail.tracking_settings = tracking_settings mail.reply_to = Email("*****@*****.**") expected_result = { "asm": { "group_id": 99, "groups_to_display": [4, 5, 6, 7, 8] }, "attachments": [ { "content": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3" "RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12", "content_id": "Balance Sheet", "disposition": "attachment", "filename": "balance_001.pdf", "type": "application/pdf" }, { "content": "BwdW", "content_id": "Banner", "disposition": "inline", "filename": "banner.png", "type": "image/png" } ], "batch_id": "sendgrid_batch_id", "categories": [ "May", "2016" ], "content": [ { "type": "text/plain", "value": "some text here" }, { "type": "text/html", "value": "<html><body>some text here</body></html>" } ], "custom_args": { "campaign": "welcome", "weekday": "morning" }, "from": { "email": "*****@*****.**", "name": "Example User" }, "headers": { "X-Test1": "test1", "X-Test3": "test2", "X-Test4": "test4" }, "ip_pool_name": "24", "mail_settings": { "bcc": { "email": "*****@*****.**", "enable": True }, "bypass_list_management": { "enable": True }, "footer": { "enable": True, "html": "<html><body>Footer Text</body></html>", "text": "Footer Text" }, "sandbox_mode": { "enable": True }, "spam_check": { "enable": True, "post_to_url": "https://spamcatcher.sendgrid.com", "threshold": 1 } }, "personalizations": [ { "bcc": [ { "email": "*****@*****.**" }, { "email": "*****@*****.**" } ], "cc": [ { "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" } ], "custom_args": { "type": "marketing", "user_id": "343" }, "headers": { "X-Mock": "true", "X-Test": "test" }, "send_at": 1443636843, "subject": "Hello World from the Personalized SendGrid " "Python Library", "substitutions": { "%city%": "Denver", "%name%": "Example User" }, "to": [ { "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" } ] }, { "bcc": [ { "email": "*****@*****.**" }, { "email": "*****@*****.**" } ], "cc": [ { "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" } ], "custom_args": { "type": "marketing", "user_id": "343" }, "headers": { "X-Mock": "true", "X-Test": "test" }, "send_at": 1443636843, "subject": "Hello World from the Personalized SendGrid " "Python Library", "substitutions": { "%city%": "Denver", "%name%": "Example User" }, "to": [ { "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" } ] } ], "reply_to": { "email": "*****@*****.**" }, "sections": { "%section1%": "Substitution Text for Section 1", "%section2%": "Substitution Text for Section 2" }, "send_at": 1443636842, "subject": "Hello World from the SendGrid Python Library", "template_id": "13b8f94f-bcae-4ec6-b752-70d6cb59f932", "tracking_settings": { "click_tracking": { "enable": True, "enable_text": True }, "ganalytics": { "enable": True, "utm_campaign": "some campaign", "utm_content": "some content", "utm_medium": "some medium", "utm_source": "some source", "utm_term": "some term" }, "open_tracking": { "enable": True, "substitution_tag": "Optional tag to replace with the " "open image in the body of the message" }, "subscription_tracking": { "enable": True, "html": "<html><body>html to insert into the text/html " "portion of the message</body></html>", "substitution_tag": "Optional tag to replace with the open" " image in the body of the message", "text": "text to insert into the text/plain portion of" " the message" } } } self.assertEqual( json.dumps(mail.get(), sort_keys=True), json.dumps(expected_result, sort_keys=True) )
def send_mail(from_email, _to_email, subject, body, html=False, from_name="Gitcoin.co", cc_emails=None, categories=None): """Send email via SendGrid.""" # make sure this subscriber is saved if not settings.SENDGRID_API_KEY: print('No SendGrid API Key set. Not attempting to send email.') return if categories is None: categories = ['default'] to_email = _to_email get_or_save_email_subscriber(to_email, 'internal') # setup from_name = str(from_name) subject = str(subject) sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) from_email = Email(from_email, from_name) to_email = Email(to_email) contenttype = "text/plain" if not html else "text/html" # build content content = Content(contenttype, html) if html else Content( contenttype, body) if settings.IS_DEBUG_ENV: to_email = Email( settings.CONTACT_EMAIL ) # just to be double secret sure of what were doing in dev subject = _("[DEBUG] ") + subject mail = Mail(from_email, subject, to_email, content) response = None # build personalization if cc_emails: p = Personalization() p.add_to(to_email) for cc_addr in set(cc_emails): cc_addr = Email(cc_addr) if settings.IS_DEBUG_ENV: cc_addr = to_email if cc_addr._email != to_email._email: p.add_to(cc_addr) mail.add_personalization(p) # categories for category in categories: mail.add_category(Category(category)) # debug logs print(f"-- Sending Mail '{subject}' to {_to_email}") # send mails try: response = sg.client.mail.send.post(request_body=mail.get()) except UnauthorizedError: print( f'-- Sendgrid Mail failure - Unauthorized - Check sendgrid credentials' ) except HTTPError as e: print(f'-- Sendgrid Mail failure - {e}') return response
def send_email( # pylint: disable=too-many-locals to: AddressesType, subject: str, html_content: str, files: Optional[AddressesType] = None, cc: Optional[AddressesType] = None, bcc: Optional[AddressesType] = None, sandbox_mode: bool = False, conn_id: str = "sendgrid_default", **kwargs, ) -> None: """ Send an email with html content using `Sendgrid <https://sendgrid.com/>`__. .. note:: For more information, see :ref:`email-configuration-sendgrid` """ if files is None: files = [] mail = Mail() from_email = kwargs.get('from_email') or os.environ.get( 'SENDGRID_MAIL_FROM') from_name = kwargs.get('from_name') or os.environ.get( 'SENDGRID_MAIL_SENDER') mail.from_email = Email(from_email, from_name) mail.subject = subject mail.mail_settings = MailSettings() if sandbox_mode: mail.mail_settings.sandbox_mode = SandBoxMode(enable=True) # Add the recipient list of to emails. personalization = Personalization() to = get_email_address_list(to) for to_address in to: personalization.add_to(Email(to_address)) if cc: cc = get_email_address_list(cc) for cc_address in cc: personalization.add_cc(Email(cc_address)) if bcc: bcc = get_email_address_list(bcc) for bcc_address in bcc: personalization.add_bcc(Email(bcc_address)) # Add custom_args to personalization if present pers_custom_args = kwargs.get('personalization_custom_args') if isinstance(pers_custom_args, dict): for key in pers_custom_args.keys(): personalization.add_custom_arg( CustomArg(key, pers_custom_args[key])) mail.add_personalization(personalization) mail.add_content(Content('text/html', html_content)) categories = kwargs.get('categories', []) for cat in categories: mail.add_category(Category(cat)) # Add email attachment. for fname in files: basename = os.path.basename(fname) with open(fname, "rb") as file: content = base64.b64encode(file.read()).decode('utf-8') attachment = Attachment( file_content=content, file_type=mimetypes.guess_type(basename)[0], file_name=basename, disposition="attachment", content_id=f"<{basename}>", ) mail.add_attachment(attachment) _post_sendgrid_mail(mail.get(), conn_id)
import sendgrid from sendgrid.helpers.mail import Category, Content, Email, Mail, Personalization from email_func import get_file_content, get_configurations if __name__ == "__main__": mail = Mail() mail.from_email = Email("*****@*****.**", "Rahul Shelke") mail.subject = "This is test email from sendgrid." personalization = Personalization() personalization.add_to(Email("*****@*****.**", "Rahul Shelke")) personalization.add_to(Email("*****@*****.**", "Rahul Shelke")) personalization.add_cc(Email("*****@*****.**", "Rahul Shelke")) personalization.add_bcc(Email("*****@*****.**", "Rahul Shelke")) mail.add_personalization(personalization) html = get_file_content( "{template}.html".format(template="email")).decode("utf8") text = get_file_content( "{template}.txt".format(template="email")).decode("utf8") mail.add_content(Content("text/plain", text)) mail.add_content(Content("text/html", html)) config = get_configurations() try: sendgrid_client = sendgrid.SendGridAPIClient( config['SENDGRID']['apikey']) response = sendgrid_client.send(mail) print(response.status_code) print(response.body)
def send(self, users=None): # FIXME Imported here due to circular dependency issues. from mist.api.notifications.models import UserNotificationPolicy if not users: users = self.ntf.owner.members elif not isinstance(users, list): users = [users] for user in users: # Prepare each user's information. Note that users may either be # instances of mist.api.users.models.User or e-mail addresses. if isinstance(user, User): to = user.email full_name = user.get_nice_name() first_name = user.first_name or full_name unsub_link = self.ntf.get_unsub_link(user.id) query_kwargs = {'owner': self.ntf.owner, 'user_id': user.id} else: to = user # Just an e-mail. full_name = first_name = "" unsub_link = self.ntf.get_unsub_link(user_id=None, email=user) query_kwargs = {'owner': self.ntf.owner, 'email': user} # Check the user's notification policy. try: np = UserNotificationPolicy.objects.get(**query_kwargs) if np.has_blocked(self.ntf): continue except UserNotificationPolicy.DoesNotExist: log.debug('No UserNotificationPolicy found for %s', user) if config.SENDGRID_EMAIL_NOTIFICATIONS_KEY: # Initialize SendGrid client. sg = SendGridAPIClient( apikey=config.SENDGRID_EMAIL_NOTIFICATIONS_KEY) mail = Mail() mail.from_email = Email(self.ntf.sender_email, self.ntf.sender_title) # Personalize e-mail. personalization = Personalization() personalization.subject = self.ntf.subject personalization.add_to(Email(to, full_name)) sub = Substitution("%name%", first_name) personalization.add_substitution(sub) if unsub_link: sub = Substitution("%nsub%", unsub_link) personalization.add_substitution(sub) mail.add_personalization(personalization) # Add content. mail.add_content(Content("text/plain", self.ntf.text_body)) if self.ntf.html_body: mail.add_content(Content("text/html", self.ntf.html_body)) # Attempt to send. try: sg.client.mail.send.post(request_body=mail.get()) except urllib2.URLError as exc: log.exception(repr(exc)) except Exception as exc: log.exception(repr(exc)) else: body = self.ntf.text_body.replace("%nsub%", unsub_link) send_email(self.ntf.subject, body, [to], sender=self.ntf.sender_email)
def obtain_mail(personalization, template): mail = Mail() mail.set_template_id(template) mail.set_from(Email(settings.DEFAULT_FROM_EMAIL, 'TapVet Team')) mail.add_personalization(personalization) return mail.get()
def _build_sg_mail(self, email): mail = Mail() from_name, from_email = rfc822.parseaddr(email.from_email) # Python sendgrid client should improve # sendgrid/helpers/mail/mail.py:164 if not from_name: from_name = None mail.set_from(Email(from_email, from_name)) mail.set_subject(email.subject) personalization = Personalization() for e in email.to: personalization.add_to(Email(e)) for e in email.cc: personalization.add_cc(Email(e)) for e in email.bcc: personalization.add_bcc(Email(e)) personalization.set_subject(email.subject) mail.add_content(Content("text/plain", email.body)) if isinstance(email, EmailMultiAlternatives): for alt in email.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif email.content_subtype == "html": mail.contents = [] mail.add_content(Content("text/plain", ' ')) mail.add_content(Content("text/html", email.body)) if hasattr(email, 'categories'): for c in email.categories: mail.add_category(Category(c)) if hasattr(email, 'template_id'): mail.set_template_id(email.template_id) if hasattr(email, 'substitutions'): for key, value in email.substitutions.items(): personalization.add_substitution(Substitution(key, value)) # SendGrid does not support adding Reply-To as an extra # header, so it needs to be manually removed if it exists. reply_to_string = "" for key, value in email.extra_headers.items(): if key.lower() == "reply-to": reply_to_string = value else: mail.add_header({key: value}) # Note that if you set a "Reply-To" header *and* the reply_to # attribute, the header's value will be used. if not mail.reply_to and hasattr(email, "reply_to") and email.reply_to: # SendGrid only supports setting Reply-To to a single address. # See https://github.com/sendgrid/sendgrid-csharp/issues/339. reply_to_string = email.reply_to[0] # Determine whether reply_to contains a name and email address, or # just an email address. if reply_to_string: reply_to_name, reply_to_email = rfc822.parseaddr(reply_to_string) if reply_to_name and reply_to_email: mail.set_reply_to(Email(reply_to_email, reply_to_name)) elif reply_to_email: mail.set_reply_to(Email(reply_to_email)) for attachment in email.attachments: if isinstance(attachment, MIMEBase): attach = Attachment() attach.set_filename(attachment.get_filename()) attach.set_content(base64.b64encode(attachment.get_payload())) mail.add_attachment(attach) elif isinstance(attachment, tuple): attach = Attachment() attach.set_filename(attachment[0]) base64_attachment = base64.b64encode(attachment[1]) if sys.version_info >= (3, ): attach.set_content(str(base64_attachment, 'utf-8')) else: attach.set_content(base64_attachment) attach.set_type(attachment[2]) mail.add_attachment(attach) mail.add_personalization(personalization) return mail.get()
def _build_sg_mail(self, msg): mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) mail.subject = msg.subject personalization = Personalization() for addr in msg.to: personalization.add_to(Email(*self._parse_email_address(addr))) for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) for addr in msg.bcc: personalization.add_bcc(Email(*self._parse_email_address(addr))) personalization.subject = msg.subject for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization.add_header(Header(k, v)) if hasattr(msg, "template_id"): mail.template_id = msg.template_id if hasattr(msg, "substitutions"): for k, v in msg.substitutions.items(): personalization.add_substitution(Substitution(k, v)) # write through the ip_pool_name attribute if hasattr(msg, "ip_pool_name"): if not isinstance(msg.ip_pool_name, basestring): raise ValueError( "ip_pool_name must be a string, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html#-Request-Body-Parameters" .format(type(msg.ip_pool_name))) if not 2 <= len(msg.ip_pool_name) <= 64: raise ValueError( "the number of characters of ip_pool_name must be min 2 and max 64, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html#-Request-Body-Parameters" .format(len(msg.ip_pool_name))) mail.ip_pool_name = msg.ip_pool_name # write through the send_at attribute if hasattr(msg, "send_at"): if not isinstance(msg.send_at, int): raise ValueError( "send_at must be an integer, got: {}; " "see https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html#-Send-At" .format(type(msg.send_at))) personalization.send_at = msg.send_at mail.add_personalization(personalization) if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to)) if reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name: raise ValueError( "Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, basestring): if len(msg.reply_to) > 1: raise ValueError( "Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email( *self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: attachment = Attachment() if isinstance(attch, MIMEBase): filename = attch.get_filename() if not filename: ext = mimetypes.guess_extension(attch.get_content_type()) filename = "part-{0}{1}".format(uuid.uuid4().hex, ext) attachment.filename = filename # todo: Read content if stream? attachment.content = attch.get_payload().replace("\n", "") attachment.type = attch.get_content_type() content_id = attch.get("Content-ID") if content_id: # Strip brackets since sendgrid's api adds them if content_id.startswith("<") and content_id.endswith(">"): content_id = content_id[1:-1] attachment.content_id = content_id attachment.disposition = "inline" else: filename, content, mimetype = attch attachment.filename = filename # todo: Read content if stream? if isinstance(content, str): content = content.encode() attachment.content = base64.b64encode(content).decode() attachment.type = mimetype mail.add_attachment(attachment) msg.body = ' ' if msg.body == '' else msg.body if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) if hasattr(msg, "asm"): if "group_id" not in msg.asm: raise KeyError("group_id not found in asm") if "groups_to_display" in msg.asm: mail.asm = ASM(msg.asm["group_id"], msg.asm["groups_to_display"]) else: mail.asm = ASM(msg.asm["group_id"]) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.open_tracking = OpenTracking(self.track_email) mail.tracking_settings = tracking_settings return mail.get()
def _build_sg_mail(self, msg: EmailMessage) -> Dict: """ Serializes a Django EmailMessage into its JSON representation. Returns a Dict of mail data to be consumed by the sendgrid api. """ mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) personalization = Personalization() for addr in msg.to: personalization.add_to(Email(*self._parse_email_address(addr))) for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) for addr in msg.bcc: personalization.add_bcc(Email(*self._parse_email_address(addr))) if hasattr(msg, "custom_args"): for k, v in msg.custom_args.items(): personalization.add_custom_arg(CustomArg(k, v)) if self._is_transaction_template(msg): if msg.subject: logger.warning( "Message subject is ignored in transactional template, " "please add it as template variable (e.g. {{ subject }}") # See https://github.com/sendgrid/sendgrid-nodejs/issues/843 else: personalization.subject = msg.subject for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization.add_header(Header(k, v)) if hasattr(msg, "ip_pool_name"): if not isinstance(msg.ip_pool_name, str): raise ValueError( "ip_pool_name must be a str, got: {}; ".format( type(msg.ip_pool_name))) # Validate ip_pool_name length before attempting to add if not 2 <= len(msg.ip_pool_name) <= 64: raise ValueError( "the number of characters of ip_pool_name must be min 2 and max 64, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/" "index.html#-Request-Body-Parameters".format( len(msg.ip_pool_name))) if SENDGRID_5: ip_pool_name = msg.ip_pool_name else: ip_pool_name = IpPoolName(msg.ip_pool_name) mail.ip_pool_name = ip_pool_name # write through the send_at attribute if hasattr(msg, "send_at"): if not isinstance(msg.send_at, int): raise ValueError( "send_at must be an integer, got: {}; " "see https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html#-Send-At" .format(type(msg.send_at))) personalization.send_at = msg.send_at if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to[0])) if (reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name): raise ValueError( "Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, str): if len(msg.reply_to) > 1: raise ValueError( "Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email( *self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: sg_attch = self._create_sg_attachment(attch) mail.add_attachment(sg_attch) if self._is_transaction_template(msg): if msg.body: logger.warning( "Message body is ignored in transactional template") else: msg.body = " " if msg.body == "" else msg.body if hasattr(msg, "template_id"): # Template mails should not have subject and content attributes mail.template_id = msg.template_id if hasattr(msg, "substitutions"): for k, v in msg.substitutions.items(): personalization.add_substitution(Substitution(k, v)) if hasattr(msg, "dynamic_template_data"): if SENDGRID_5: logger.warning( "dynamic_template_data not available in sendgrid version < 6" ) personalization.dynamic_template_data = msg.dynamic_template_data if not self._is_transaction_template(msg): # In sendgrid v6 we should not specify subject and content between request parameter # when we are sending a request for a transactional template mail.subject = msg.subject if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) mail.add_personalization(personalization) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) if hasattr(msg, "asm"): if "group_id" not in msg.asm: raise KeyError("group_id not found in asm") if "groups_to_display" in msg.asm: mail.asm = ASM(msg.asm["group_id"], msg.asm["groups_to_display"]) else: mail.asm = ASM(msg.asm["group_id"]) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.open_tracking = OpenTracking(self.track_email) tracking_settings.click_tracking = ClickTracking( self.track_clicks_html, self.track_clicks_plain) mail.tracking_settings = tracking_settings return mail.get()
def custom_send_mail(subject=None, html_message=None, from_email=None, recipient_list=None, text_message=None, email_instance=None): """ The role of this function is to handle the sending of an email message based on the configuration of the project setting.py file. - If settings.USE_SENDGRID = True, the mail will be sent with the python sendgrid package using the sendgrid REST api - If settings.USE_SMTP_EMAIL = True, the email will be send with django's send_mail function. - If settings.USE_SMTP_EMAIL and settings.USE_SENDGRID are both false, no email will be sent. :param subject: :param html_message: :param from_email: :param recipient_list: :return: """ if email_instance: subject = email_instance.get_subject() text_message = email_instance.get_text_message() html_message = email_instance.get_html_message() from_email = email_instance.get_from_email() recipient_list = email_instance.get_recipient_list() if settings.USE_SENDGRID: sg = SendGridAPIClient(settings.SENDGRID_API_KEY) from_email = Email(from_email) to_list = Personalization() for email in set(recipient_list): to_list.add_to(Email(email)) mail = Mail(from_email=from_email, to_emails=None, subject=subject, html_content=html_message, plain_text_content=text_message) mail.add_personalization(to_list) try: sg.send(mail) except BadRequestsError: print("bad request. email not sent") elif settings.USE_AWS_SES: my_config = Config(region_name='ca-central-1', signature_version='v4', retries={ 'max_attempts': 10, 'mode': 'standard' }) # from https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-sdk-python.html # The character encoding for the email. CHARSET = "UTF-8" # Create a new SES resource and specify a region. client = boto3.client( 'ses', aws_access_key_id=settings.AWS_ACCESS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, config=my_config) # Try to send the email. try: # Provide the contents of the email. response = client.send_email( Destination={'ToAddresses': recipient_list}, Message={ 'Body': { 'Html': { 'Charset': CHARSET, 'Data': nz(html_message, ''), }, 'Text': { 'Charset': CHARSET, 'Data': nz(text_message, ''), }, }, 'Subject': { 'Charset': CHARSET, 'Data': subject, }, }, Source=from_email, ) # Display an error if something goes wrong. except ClientError as e: print(e.response['Error']['Message']) else: print("Email sent! Message ID:"), print(response['MessageId']) elif settings.USE_SMTP_EMAIL: django_send_mail(subject=subject, message=nz(text_message, ''), html_message=html_message, from_email=from_email, recipient_list=recipient_list, fail_silently=False) else: print('No email configuration present in application...') if email_instance: print(email_instance) else: print("FROM: {}\nTO: {}\nSUBJECT: {}\nMESSAGE:{}".format( from_email, recipient_list, subject, html_message))
def test_kitchenSink(self): self.maxDiff = None """All settings set""" mail = Mail() mail.from_email = Email("*****@*****.**", "Example User") mail.subject = "Hello World from the SendGrid Python Library" personalization = Personalization() personalization.add_to(Email("*****@*****.**", "Example User")) personalization.add_to(Email("*****@*****.**", "Example User")) personalization.add_cc(Email("*****@*****.**", "Example User")) personalization.add_cc(Email("*****@*****.**", "Example User")) personalization.add_bcc(Email("*****@*****.**")) personalization.add_bcc(Email("*****@*****.**")) personalization.subject = "Hello World from the Personalized SendGrid Python Library" personalization.add_header(Header("X-Test", "test")) personalization.add_header(Header("X-Mock", "true")) personalization.add_substitution(Substitution("%name%", "Example User")) personalization.add_substitution(Substitution("%city%", "Denver")) personalization.add_custom_arg(CustomArg("user_id", "343")) personalization.add_custom_arg(CustomArg("type", "marketing")) personalization.send_at = 1443636843 mail.add_personalization(personalization) personalization2 = Personalization() personalization2.add_to(Email("*****@*****.**", "Example User")) personalization2.add_to(Email("*****@*****.**", "Example User")) personalization2.add_cc(Email("*****@*****.**", "Example User")) personalization2.add_cc(Email("*****@*****.**", "Example User")) personalization2.add_bcc(Email("*****@*****.**")) personalization2.add_bcc(Email("*****@*****.**")) personalization2.subject = "Hello World from the Personalized SendGrid Python Library" personalization2.add_header(Header("X-Test", "test")) personalization2.add_header(Header("X-Mock", "true")) personalization2.add_substitution( Substitution("%name%", "Example User")) personalization2.add_substitution(Substitution("%city%", "Denver")) personalization2.add_custom_arg(CustomArg("user_id", "343")) personalization2.add_custom_arg(CustomArg("type", "marketing")) personalization2.send_at = 1443636843 mail.add_personalization(personalization2) mail.add_content(Content("text/plain", "some text here")) mail.add_content( Content("text/html", "<html><body>some text here</body></html>")) attachment = Attachment() attachment.content = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12" attachment.type = "application/pdf" attachment.filename = "balance_001.pdf" attachment.disposition = "attachment" attachment.content_id = "Balance Sheet" mail.add_attachment(attachment) attachment2 = Attachment() attachment2.content = "BwdW" attachment2.type = "image/png" attachment2.filename = "banner.png" attachment2.disposition = "inline" attachment2.content_id = "Banner" mail.add_attachment(attachment2) mail.template_id = "13b8f94f-bcae-4ec6-b752-70d6cb59f932" mail.add_section( Section("%section1%", "Substitution Text for Section 1")) mail.add_section( Section("%section2%", "Substitution Text for Section 2")) mail.add_header(Header("X-Test1", "test1")) mail.add_header(Header("X-Test3", "test2")) mail.add_header({"X-Test4": "test4"}) mail.add_category(Category("May")) mail.add_category(Category("2016")) mail.add_custom_arg(CustomArg("campaign", "welcome")) mail.add_custom_arg(CustomArg("weekday", "morning")) mail.send_at = 1443636842 mail.batch_id = "sendgrid_batch_id" mail.asm = ASM(99, [4, 5, 6, 7, 8]) mail.ip_pool_name = "24" mail_settings = MailSettings() mail_settings.bcc_settings = BCCSettings(True, Email("*****@*****.**")) mail_settings.bypass_list_management = BypassListManagement(True) mail_settings.footer_settings = FooterSettings( True, "Footer Text", "<html><body>Footer Text</body></html>") mail_settings.sandbox_mode = SandBoxMode(True) mail_settings.spam_check = SpamCheck( True, 1, "https://spamcatcher.sendgrid.com") mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.click_tracking = ClickTracking(True, True) tracking_settings.open_tracking = OpenTracking( True, "Optional tag to replace with the open image in the body of the message" ) tracking_settings.subscription_tracking = SubscriptionTracking( True, "text to insert into the text/plain portion of the message", "<html><body>html to insert into the text/html portion of the message</body></html>", "Optional tag to replace with the open image in the body of the message" ) tracking_settings.ganalytics = Ganalytics(True, "some source", "some medium", "some term", "some content", "some campaign") mail.tracking_settings = tracking_settings mail.reply_to = Email("*****@*****.**") expected_result = { "asm": { "group_id": 99, "groups_to_display": [4, 5, 6, 7, 8] }, "attachments": [{ "content": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3" "RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12", "content_id": "Balance Sheet", "disposition": "attachment", "filename": "balance_001.pdf", "type": "application/pdf" }, { "content": "BwdW", "content_id": "Banner", "disposition": "inline", "filename": "banner.png", "type": "image/png" }], "batch_id": "sendgrid_batch_id", "categories": ["May", "2016"], "content": [{ "type": "text/plain", "value": "some text here" }, { "type": "text/html", "value": "<html><body>some text here</body></html>" }], "custom_args": { "campaign": "welcome", "weekday": "morning" }, "from": { "email": "*****@*****.**", "name": "Example User" }, "headers": { "X-Test1": "test1", "X-Test3": "test2", "X-Test4": "test4" }, "ip_pool_name": "24", "mail_settings": { "bcc": { "email": "*****@*****.**", "enable": True }, "bypass_list_management": { "enable": True }, "footer": { "enable": True, "html": "<html><body>Footer Text</body></html>", "text": "Footer Text" }, "sandbox_mode": { "enable": True }, "spam_check": { "enable": True, "post_to_url": "https://spamcatcher.sendgrid.com", "threshold": 1 } }, "personalizations": [{ "bcc": [{ "email": "*****@*****.**" }, { "email": "*****@*****.**" }], "cc": [{ "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" }], "custom_args": { "type": "marketing", "user_id": "343" }, "headers": { "X-Mock": "true", "X-Test": "test" }, "send_at": 1443636843, "subject": "Hello World from the Personalized SendGrid " "Python Library", "substitutions": { "%city%": "Denver", "%name%": "Example User" }, "to": [{ "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" }] }, { "bcc": [{ "email": "*****@*****.**" }, { "email": "*****@*****.**" }], "cc": [{ "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" }], "custom_args": { "type": "marketing", "user_id": "343" }, "headers": { "X-Mock": "true", "X-Test": "test" }, "send_at": 1443636843, "subject": "Hello World from the Personalized SendGrid " "Python Library", "substitutions": { "%city%": "Denver", "%name%": "Example User" }, "to": [{ "email": "*****@*****.**", "name": "Example User" }, { "email": "*****@*****.**", "name": "Example User" }] }], "reply_to": { "email": "*****@*****.**" }, "sections": { "%section1%": "Substitution Text for Section 1", "%section2%": "Substitution Text for Section 2" }, "send_at": 1443636842, "subject": "Hello World from the SendGrid Python Library", "template_id": "13b8f94f-bcae-4ec6-b752-70d6cb59f932", "tracking_settings": { "click_tracking": { "enable": True, "enable_text": True }, "ganalytics": { "enable": True, "utm_campaign": "some campaign", "utm_content": "some content", "utm_medium": "some medium", "utm_source": "some source", "utm_term": "some term" }, "open_tracking": { "enable": True, "substitution_tag": "Optional tag to replace with the " "open image in the body of the message" }, "subscription_tracking": { "enable": True, "html": "<html><body>html to insert into the text/html " "portion of the message</body></html>", "substitution_tag": "Optional tag to replace with the open" " image in the body of the message", "text": "text to insert into the text/plain portion of" " the message" } } } self.assertEqual(json.dumps(mail.get(), sort_keys=True), json.dumps(expected_result, sort_keys=True))
def _build_sg_mail(self, msg): mail = Mail() mail.from_email = Email(*self._parse_email_address(msg.from_email)) mail.subject = msg.subject personalization = Personalization() for addr in msg.to: personalization.add_to(Email(*self._parse_email_address(addr))) for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) for addr in msg.bcc: personalization.add_bcc(Email(*self._parse_email_address(addr))) if hasattr(msg, 'custom_args'): for k, v in msg.custom_args.items(): personalization.add_custom_arg(CustomArg(k, v)) personalization.subject = msg.subject for k, v in msg.extra_headers.items(): if k.lower() == "reply-to": mail.reply_to = Email(v) else: personalization.add_header(Header(k, v)) if hasattr(msg, "template_id"): mail.template_id = msg.template_id if hasattr(msg, "substitutions"): for k, v in msg.substitutions.items(): personalization.add_substitution(Substitution(k, v)) if hasattr(msg, "dynamic_template_data"): personalization.dynamic_template_data = msg.dynamic_template_data if hasattr(msg, "ip_pool_name"): if not isinstance(msg.ip_pool_name, basestring): raise ValueError( "ip_pool_name must be a {}, got: {}; ".format( type(msg.ip_pool_name))) # Validate ip_pool_name length before attempting to add if not 2 <= len(msg.ip_pool_name) <= 64: raise ValueError( "the number of characters of ip_pool_name must be min 2 and max 64, got: {}; " "see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/" "index.html#-Request-Body-Parameters".format( len(msg.ip_pool_name))) if SENDGRID_VERSION < "6": ip_pool_name = msg.ip_pool_name else: ip_pool_name = IpPoolName(msg.ip_pool_name) mail.ip_pool_name = ip_pool_name # write through the send_at attribute if hasattr(msg, "send_at"): if not isinstance(msg.send_at, int): raise ValueError( "send_at must be an integer, got: {}; " "see https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html#-Send-At".format( type(msg.send_at))) personalization.send_at = msg.send_at mail.add_personalization(personalization) if hasattr(msg, "reply_to") and msg.reply_to: if mail.reply_to: # If this code path is triggered, the reply_to on the sg mail was set in a header above reply_to = Email(*self._parse_email_address(msg.reply_to)) if reply_to.email != mail.reply_to.email or reply_to.name != mail.reply_to.name: raise ValueError("Sendgrid only allows 1 email in the reply-to field. " + "Reply-To header value != reply_to property value.") if not isinstance(msg.reply_to, basestring): if len(msg.reply_to) > 1: raise ValueError("Sendgrid only allows 1 email in the reply-to field") mail.reply_to = Email(*self._parse_email_address(msg.reply_to[0])) else: mail.reply_to = Email(*self._parse_email_address(msg.reply_to)) for attch in msg.attachments: sg_attch = self._create_sg_attachment(attch) mail.add_attachment(sg_attch) msg.body = ' ' if msg.body == '' else msg.body if isinstance(msg, EmailMultiAlternatives): mail.add_content(Content("text/plain", msg.body)) for alt in msg.alternatives: if alt[1] == "text/html": mail.add_content(Content(alt[1], alt[0])) elif msg.content_subtype == "html": mail.add_content(Content("text/plain", " ")) mail.add_content(Content("text/html", msg.body)) else: mail.add_content(Content("text/plain", msg.body)) if hasattr(msg, "categories"): for cat in msg.categories: mail.add_category(Category(cat)) if hasattr(msg, "asm"): if "group_id" not in msg.asm: raise KeyError("group_id not found in asm") if "groups_to_display" in msg.asm: mail.asm = ASM(msg.asm["group_id"], msg.asm["groups_to_display"]) else: mail.asm = ASM(msg.asm["group_id"]) mail_settings = MailSettings() mail_settings.sandbox_mode = SandBoxMode(self.sandbox_mode) mail.mail_settings = mail_settings tracking_settings = TrackingSettings() tracking_settings.open_tracking = OpenTracking(self.track_email) tracking_settings.click_tracking = ClickTracking(self.track_click) tracking_settings.subscription_tracking = SubscriptionTracking(self.subscription) mail.tracking_settings = tracking_settings return mail.get()
def _prepare_sendgrid_data(self): """ Prepare and creates the Sendgrid Email object :return: sendgrid.helpers.mail.Email object """ self.ensure_one() s_mail = Mail() s_mail.from_email = Email(self.email_from) if self.reply_to: s_mail.reply_to = Email(self.reply_to) # Add custom fields to match the tracking s_mail.add_custom_arg(CustomArg('odoo_id', self.message_id)) s_mail.add_custom_arg(CustomArg('odoo_db', self.env.cr.dbname)) headers = {'Message-Id': self.message_id} if self.headers: try: headers.update(safe_eval(self.headers)) except Exception: pass for h_name, h_val in headers.iteritems(): s_mail.add_header(Header(h_name, h_val)) html = self.body_html or ' ' p = re.compile(r'<.*?>') # Remove HTML markers text_only = self.body_text or p.sub('', html.replace('<br/>', '\n')) s_mail.add_content(Content("text/plain", text_only or ' ')) s_mail.add_content(Content("text/html", html)) test_address = config.get('sendgrid_test_address') # We use only one personalization for transactional e-mail personalization = Personalization() subject = self.subject and self.subject.encode( "utf_8") or "(No subject)" personalization.subject = subject addresses = set() if not test_address: if self.email_to: addresses = set(self.email_to.split(',')) for address in addresses: personalization.add_to(Email(address)) for recipient in self.recipient_ids: if recipient.email not in addresses: personalization.add_to(Email(recipient.email)) addresses.add(recipient.email) if self.email_cc and self.email_cc not in addresses: personalization.add_cc(Email(self.email_cc)) else: _logger.info( 'Sending email to test address {}'.format(test_address)) personalization.add_to(Email(test_address)) self.email_to = test_address if self.sendgrid_template_id: s_mail.template_id = self.sendgrid_template_id.remote_id for substitution in self.substitution_ids: personalization.add_substitution( Substitution(substitution.key, substitution.value.encode('utf-8'))) s_mail.add_personalization(personalization) for attachment in self.attachment_ids: s_attachment = Attachment() # Datas are not encoded properly for sendgrid s_attachment.content = base64.b64encode( base64.b64decode(attachment.datas)) s_attachment.filename = attachment.name s_mail.add_attachment(s_attachment) return s_mail
def sender_alert_new_cmd(order, user): """ Send the email to the owner for the new order. """ date_cmd = str(order.created).split(' ')[0].split('-') data = { "cmd": { "ref": order.reference, "nameclient": f"{user.last_name.upper()} {user.first_name}", "email": user.email, "adress": user.adress, "postalcode": str(user.postal_code), "city": user.city.upper(), "phonenumber": f"0{str(user.phone_number)}", "anotheradress": {}, "note": order.note, "date": f"{date_cmd[2]}/{date_cmd[1]}/{date_cmd[0]}", "subtotalprice": str(order.total_price - order.shipping_costs), "totalprice": str(order.total_price), "shippingcosts": str(order.shipping_costs), "products": [] } } if order.another_delivery_adress: data['cmd']['anotheradress'] = { "ifyes": True, "nameclient": f"{order.last_name.upper()} {order.first_name}", "adress": order.adress, "postalcode": str(order.postal_code), "city": order.city.upper(), "phonenumber": f"0{str(order.phone_number)}", } else: data['cmd']['anotheradress'] = { "ifyes": False, "nameclient": "none", "adress": "none", "postalcode": "none", "city": "none", "phonenumber": "none", } for product in OrderProductQuantity.objects.filter(id_order=order): data['cmd']['products'].append({ "name": product.id_product.name, "priceunit": str(product.price), "totalprice": str(product.get_price()), "quantity": str(product.quantity) }) mail = Mail() mail.from_email = Email( os.environ.get('FROM_EMAIL'), os.environ.get('FROM_NAME_EMAIL') ) mail.template_id = os.environ.get('ID_TEMPLATE_NEW_CMD') mail.subject = "NOUVELLE COMMANDE WEB" p = Personalization() p.add_to(Email( os.environ.get('OWNER_EMAIL'), os.environ.get('OWNER_NAME_EMAIL') )) p.dynamic_template_data = data mail.add_personalization(p) sg = SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY')) response = sg.client.mail.send.post(request_body=mail.get()) return response