def send_letter(self, template_id, emails, dynamic_template_data=None, categories=None): categories = categories or [] dynamic_template_data = dynamic_template_data or {} mail = Mail() mail.template_id = template_id mail.from_email = Email(email=settings.EMAIL_FROM, name=settings.DEFAULT_FROM_EMAIL) for email in emails: personalization = Personalization() personalization.add_to(Email(email)) personalization.dynamic_template_data = dynamic_template_data mail.add_personalization(personalization) for category in categories: mail.add_category(Category(category)) return self._send_letter(mail.get())
def sender_new_order_status(order_queryset): """ Sending an email to the client to indicate a change of status. """ done = True for order in order_queryset: data = { 'reforder': order.reference, 'clientname': order.first_name, 'price': order.total_price, 'status': order.status } 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_UPDATE_CMD') mail.subject = "Le statu de votre commande a changée" p = Personalization() p.add_to(Email(order.email, f"{order.last_name} {order.first_name}")) 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()) if response.status_code == 202: continue else: done = False return done
def send_email( sender=settings.CLOUDCV_TEAM_EMAIL, recipient=None, template_id=None, template_data={}, ): """Function to send email Keyword Arguments: sender {string} -- Email of sender (default: {settings.TEAM_EMAIL}) recipient {string} -- Recipient email address template_id {string} -- Sendgrid template id template_data {dict} -- Dictionary to substitute values in subject and email body """ try: sg = sendgrid.SendGridAPIClient( apikey=os.environ.get("SENDGRID_API_KEY") ) sender = Email(sender) mail = Mail() mail.from_email = sender mail.template_id = template_id to_list = Personalization() to_list.dynamic_template_data = template_data to_email = Email(recipient) to_list.add_to(to_email) mail.add_personalization(to_list) sg.client.mail.send.post(request_body=mail.get()) except Exception: logger.warning( "Cannot make sendgrid call. Please check if SENDGRID_API_KEY is present." ) return
def _notify(job): try: logger.debug( f"Sending email notification to {job.user_name} <{job.user_email}>" ) sendgrid = SendGridAPIClient() mail = Mail() mail.from_email = Email("DD-DeCaF <*****@*****.**>") mail.template_id = "d-8caebf4f862b4c67932515c45c5404cc" personalization = Personalization() personalization.add_to(Email(job.user_email)) personalization.dynamic_template_data = { "name": job.user_name, "product": job.product_name, "organism": job.organism_name, "results_url": f"https://caffeine.dd-decaf.eu/jobs/{job.job_id}", } mail.add_personalization(personalization) sendgrid.client.mail.send.post(request_body=mail.get()) except Exception as error: # Suppress any problem so it doesn't mark the entire workflow as failed, # but do log a warning for potential follow-up. logger.warning( "Unable to send email notification upon job completion", exc_info=error, )
def sender_reset_password(user, host): """ Send the email to the user to reset his password. """ link = f"{host}/user/reset_password/" data = { "code": user.check_code, "link": link, "username": str(user) } 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_RESET_PASSWORD') mail.subject = "Réinitialisation de mot de passe." p = Personalization() p.add_to(Email(user.email, str(user))) 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
def send_email(self): # send email using the self.cleaned_data dictionary first_name = self.cleaned_data.get("fname") last_name = self.cleaned_data.get("lname") email = self.cleaned_data.get("email") phone = self.cleaned_data.get("phone") message = self.cleaned_data.get("message") # Issue response to client via sendgrid API sendgrid_client = SendGridAPIClient(config('SENDGRID_API_KEY')) mail = Mail() mail.from_email = From(config('EMAIL_FROM')) mail.to_email = To(email) mail.subject = "Your Enquiry with Mayan Web Studio" mail.template_id = config('SENDGRID_TEMPLATE_ID') p = Personalization() p.add_to(To(email)) p.dynamic_template_data = { "firstName": first_name, "lastName": last_name, "phone": phone } mail.add_personalization(p) response = sendgrid_client.client.mail.send.post( request_body=mail.get()) print(response.status_code) print(response.body) print(response.headers) # Send notification email to Mayan subject, from_email, to = 'New Enquiry for Mayan Web Studio', config( 'EMAIL_FROM'), '*****@*****.**' text_content = 'This is an important message.' html_content = ''' <h3>New Enquiry from</h3> <ul> <li>First Name: $(first_name)</li> <li>Last Name: $(last_name)</li> <li>Email: $(email)</li> <li>Phone: $(phone)</li> <li>Message: $(message)</li> </ul> ''' html_content = html_content.replace("$(first_name)", first_name) html_content = html_content.replace("$(last_name)", last_name) html_content = html_content.replace("$(email)", email) html_content = html_content.replace("$(phone)", phone) html_content = html_content.replace("$(message)", message) msg = EmailMultiAlternatives(subject, text_content, from_email, [to]) msg.attach_alternative(html_content, "text/html") msg.send()
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) if message.mail_options and message.mail_options.get('from_name'): mail.from_email.name = message.mail_options.get('from_name') 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)) 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 send_mail(self, template_id, sender, recipient, data_dict): mail = Mail() mail.template_id = template_id mail.from_email = Email(sender) personalization = Personalization() personalization.add_to(Email(recipient)) personalization.dynamic_template_data = data_dict mail.add_personalization(personalization) try: response = sg.client.mail.send.post(request_body=mail.get()) except exceptions.BadRequestsError as e: print("INSIDE") print(e.body) exit() print(response.status_code) print(response.body) print(response.headers)
def sender_activate_account(user, host): """ Send the email to the user to active his account. """ link = f"{host}/user/active_account?id={user.pk}&key={user.activate_key}" data = {"username": str(user), "link": link} 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_ACTIVE_ACCOUNT') mail.subject = "Activation de compte client" p = Personalization() p.add_to(Email(user.email, str(user))) 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
def send_reset_email(self): token = self.get_reset_token() try: logger.debug(f"Sending email with reset link to " f"{self.full_name} <{self.email}>") sendgrid = SendGridAPIClient() mail = Mail() mail.from_email = Email("DD-DeCaF <*****@*****.**>") mail.template_id = "d-f1addc67e51f4d0e8966d340c24551a4" personalization = Personalization() personalization.add_to(Email(self.email)) personalization.dynamic_template_data = { "link": f"{app.config['ROOT_URL']}/password-reset/{token}" } mail.add_personalization(personalization) sendgrid.client.mail.send.post(request_body=mail.get()) return '''An email has been sent with instructions to reset your password.''', 200 except Exception as error: logger.warning("Unable to send email with password reset link", exc_info=error) return "Unable to send email with password reset link", 502
def test_dynamic_template_data(self): p = Personalization() p.add_to(Email('*****@*****.**')) p.dynamic_template_data = { 'customer': { 'name': 'Bob', 'returning': True }, 'total': 42 } expected = { 'to': [{'email': '*****@*****.**'}], 'dynamic_template_data': { 'customer': { 'name': 'Bob', 'returning': True }, 'total': 42 } } self.assertDictEqual(p.get(), expected)
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)) 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) return mail
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 _build_sg_personalization( self, msg: EmailMessage, extra_headers: Iterable[Header], to: Optional[List[str]] = None, existing_personalizations: Optional[Personalization] = None, ) -> Personalization: """ Constructs a Sendgrid Personalization instance / row for the given recipients. If no "per row" personalizations are provided, the personalization data is populated from msg. Args: msg: The base Django Email message object - used to fill (missing) personalization data extra_headers: The non "reply-to" headers for the personalization. to: The email addresses for the given personalization. existing_personalizations: Personalization data, eg. dynamic_template_data or substitutions. A given value should have key equivalent to the corresponding EmailMessage attr Returns: A sendgrid personalization instance """ if existing_personalizations is None: if not to: raise ValueError( "Either msg.to or msg.personalizations (with recipients) must be set" ) personalization = Personalization() for addr in to: personalization.add_to(Email(*self._parse_email_address(addr))) elif existing_personalizations.tos: personalization = existing_personalizations else: raise ValueError("Each msg personalization must have recipients") if not personalization.ccs: for addr in msg.cc: personalization.add_cc(Email(*self._parse_email_address(addr))) if not personalization.bccs: for addr in msg.bcc: personalization.add_bcc( Email(*self._parse_email_address(addr))) if not personalization.custom_args: for k, v in getattr(msg, "custom_args", {}).items(): personalization.add_custom_arg(CustomArg(k, v)) if self._is_transaction_template(msg): if personalization.subject or 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 if not personalization.subject: personalization.subject = msg.subject for header in extra_headers: personalization.add_header(header) # 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, "template_id"): if not personalization.substitutions: for k, v in getattr(msg, "substitutions", {}).items(): personalization.add_substitution(Substitution(k, v)) dtd = personalization.dynamic_template_data or getattr( msg, "dynamic_template_data", None) if dtd: if SENDGRID_5: logger.warning( "dynamic_template_data not available in sendgrid version < 6" ) else: personalization.dynamic_template_data = dtd return personalization
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
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 _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 _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 # 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()