MarkdownMerge

API details
from fastcore.test import test_eq

Utility functions


get_addr

 get_addr (email, name=None)

Convert email and optional name into an email Address object

Specify from_addr and to_addrs as either a string, or an Address object (created with get_addr). Note the to_addrs is a list.

from_addr = get_addr('from@example.com', 'Jeremy Howard')
to_addrs = [get_addr('to@example.com', 'Jeremy')]
print(from_addr)
print(to_addrs[0])
Jeremy Howard <from@example.com>
Jeremy <to@example.com>

attach_file

 attach_file (msg, f)

Attach file f to message msg

msg = MIMEMultipart()
attach_file(msg, 'settings.ini')
part = msg.get_payload()[0]
test_eq(part.get_content_type(), 'application/octet-stream')
test_eq(part['Content-Disposition'], 'attachment; filename=settings.ini')
assert len(part.get_payload())>20

create_multipart_msg

 create_multipart_msg (subj, from_addr, to_addrs, md=None, html=None,
                       attach=None)

Create a multipart email with markdown text and HTML

msg = create_multipart_msg('Test Subject', from_addr, to_addrs, md='**Bold text**', html='<b>Bold text</b>')
test_eq(msg['Subject'], 'Test Subject')
test_eq(msg['From'], str(from_addr))
test_eq(len(msg.get_payload()), 2)
test_eq(msg.get_payload()[0].get_content_type(), 'text/plain')
test_eq(msg.get_payload()[1].get_content_type(), 'text/html')

md2email

 md2email (subj, from_addr, to_addrs, md, attach=None)

Create a multipart email from markdown

test_msg2 = md2email('Test md2email', 'support@answer.ai', 'j@answer.ai', '**Markdown** test with _attachment_', attach='settings.ini')
test_eq(test_msg2['Subject'], 'Test md2email')
payload = test_msg2.get_payload()
test_eq(len(payload), 3)
test_eq(payload[2]['Content-Disposition'], 'attachment; filename=settings.ini')

The basic email body is the plain text message (note that the template variables in {} will be filled in by MarkdownMerge):

print(payload[0].get_payload())
**Markdown** test with _attachment_

Most email software is set up to display the HTML version:

from IPython.display import HTML
html = payload[1].get_payload()
HTML(html)

Markdown test with attachment

att = payload[2].get_payload()
import base64
decoded = base64.b64decode(payload[2].get_payload())
print(decoded.decode('utf-8')[:35])
[DEFAULT]
lib_name = markdown_merge

smtp_connection

 smtp_connection (host, port, user=None, password=None, use_ssl=True,
                  use_tls=False)

Context manager for SMTP connection

servernm='email-smtp.us-west-2.amazonaws.com'
username=os.getenv('SES_SMTP_USER')
password=os.getenv('SES_SMTP_PASS')
smtp_cfg = dict(host=servernm, port=587, user=username, password=password, use_ssl=False, use_tls=True)
test_msg = create_multipart_msg('Test from stdlib', 'support@answer.ai', 'j@answer.ai', md='**Test message**', html='<b>Test message</b>')
# with smtp_connection(**smtp_cfg) as conn: conn.send_message(test_msg)

MarkdownMerge

 MarkdownMerge (addrs, from_addr, subj, msg, smtp_cfg=None, inserts=None,
                test=False)

Send templated email merge messages formatted with Markdown

Your message should be in markdown format. It will be converted into a two part email, containing both a plain text and an HTML part, so recipients will see whatever format they’re set as their preference for viewing mail. Anything in curly brackets {} will be replaced with the contents of the inserts dictionary for that address. If there are no bracketed variables to replace, then you don’t need to pass any inserts.

msg = "**Hello {name}!**\n\nYour special number is: *{num}*"

inserts is a list of dictionaries. For each dictionary, the keys should match the bracketed names in your email template, and the values will be filled in to those sections.

inserts = [{'name': 'Jeremy', 'num': 42}, {'name': 'Rachel', 'num': 7}]
mm = MarkdownMerge(['aaa@answer.ai', 'bbb@answer.ai'], 'from@answer.ai', 'Test merge',
                   msg, smtp_cfg=smtp_cfg, inserts=inserts, test=True)
mm.send_msgs()
To: aaa@answer.ai
----------------------------------------
**Hello Jeremy!**

Your special number is: *42*
========================================

To: bbb@answer.ai
----------------------------------------
**Hello Rachel!**

Your special number is: *7*
========================================

Use pause to avoid sending too many messages too quickly; many SMTP servers restrict sending speed to avoid abuse. If you get an error during sending (e.g. “too many messages”), then wait an hour or so, then continue sending, using a larger pause value.

NB: You can just call send_msgs again when resending, since the successfully sent message count is saved, and those messages are not re-sent (unless you call reset). This includes test sends, therefore you should run reset after a test send.

To reset the counter to 0, call reset:

mm.reset()