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)) # 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 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 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 test_personalizations_resolution(self): """ Tests that adding a Personalization() object directly to an EmailMessage object works as expected. Written to test functionality introduced in the PR: https://github.com/sklarsa/django-sendgrid-v5/pull/90 """ msg = EmailMessage( subject="Hello, World!", body="Hello, World!", from_email="Sam Smith <*****@*****.**>", to=["John Doe <*****@*****.**>", "*****@*****.**"], cc=["Stephanie Smith <*****@*****.**>"], bcc=["Sarah Smith <*****@*****.**>"], reply_to=["Sam Smith <*****@*****.**>"], ) # Tests that personalizations take priority test_str = "*****@*****.**" test_key_str = "my key" test_val_str = "my val" personalization = Personalization() if SENDGRID_5: personalization.add_to(Email(test_str)) personalization.add_cc(Email(test_str)) personalization.add_bcc(Email(test_str)) else: personalization.add_to(To(test_str)) personalization.add_cc(Cc(test_str)) personalization.add_bcc(Bcc(test_str)) personalization.add_custom_arg(CustomArg(test_key_str, test_val_str)) personalization.add_header(Header(test_key_str, test_val_str)) personalization.add_substitution( Substitution(test_key_str, test_val_str)) msg.personalizations = [personalization] result = self.backend._build_sg_mail(msg) personalization = result["personalizations"][0] for field in ("to", "cc", "bcc"): data = personalization[field] self.assertEqual(len(data), 1) self.assertEqual(data[0]["email"], test_str) for field in ("custom_args", "headers", "substitutions"): data = personalization[field] self.assertEqual(len(data), 1) self.assertIn(test_key_str, data) self.assertEqual(test_val_str, data[test_key_str])
def test_build_personalization_errors(self): msg = EmailMessage( subject="Hello, World!", body="Hello, World!", from_email="Sam Smith <*****@*****.**>", cc=["Stephanie Smith <*****@*****.**>"], bcc=["Sarah Smith <*****@*****.**>"], reply_to=["Sam Smith <*****@*****.**>"], ) test_str = "*****@*****.**" test_key_str = "my key" test_val_str = "my val" personalization = Personalization() if SENDGRID_5: personalization.add_cc(Email(test_str)) personalization.add_bcc(Email(test_str)) else: personalization.add_cc(Cc(test_str)) personalization.add_bcc(Bcc(test_str)) personalization.add_custom_arg(CustomArg(test_key_str, test_val_str)) personalization.add_header(Header(test_key_str, test_val_str)) personalization.add_substitution( Substitution(test_key_str, test_val_str)) msg.personalizations = [personalization] self.assertRaisesRegex( ValueError, "Each msg personalization must have recipients", self.backend._build_sg_mail, msg, ) delattr(msg, "personalizations") msg.dynamic_template_data = {"obi_wan": "hello there"} self.assertRaisesRegex( ValueError, r"Either msg\.to or msg\.personalizations \(with recipients\) must be set", self.backend._build_sg_mail, msg, )
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_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 _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()
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 _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 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)
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 send_email(to, subject, html_content, files=None, cc=None, bcc=None, sandbox_mode=False, **kwargs): """ Send an email with html content using `Sendgrid <https://sendgrid.com/>`__. 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`` property in `[email]`` section 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) 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())
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()