from fastcore.test import test_eq
MarkdownMerge
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.
= get_addr('from@example.com', 'Jeremy Howard')
from_addr = [get_addr('to@example.com', 'Jeremy')]
to_addrs 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
= MIMEMultipart()
msg 'settings.ini')
attach_file(msg, = msg.get_payload()[0]
part 'application/octet-stream')
test_eq(part.get_content_type(), 'Content-Disposition'], 'attachment; filename=settings.ini')
test_eq(part[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
= create_multipart_msg('Test Subject', from_addr, to_addrs, md='**Bold text**', html='<b>Bold text</b>')
msg 'Subject'], 'Test Subject')
test_eq(msg['From'], str(from_addr))
test_eq(msg[len(msg.get_payload()), 2)
test_eq(0].get_content_type(), 'text/plain')
test_eq(msg.get_payload()[1].get_content_type(), 'text/html') test_eq(msg.get_payload()[
md2email
md2email (subj, from_addr, to_addrs, md, attach=None)
Create a multipart email from markdown
= md2email('Test md2email', 'support@answer.ai', 'j@answer.ai', '**Markdown** test with _attachment_', attach='settings.ini')
test_msg2 'Subject'], 'Test md2email')
test_eq(test_msg2[= test_msg2.get_payload()
payload len(payload), 3)
test_eq(2]['Content-Disposition'], 'attachment; filename=settings.ini') test_eq(payload[
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
= payload[1].get_payload()
html HTML(html)
Markdown test with attachment
= payload[2].get_payload() att
import base64
= base64.b64decode(payload[2].get_payload())
decoded 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
='email-smtp.us-west-2.amazonaws.com'
servernm=os.getenv('SES_SMTP_USER')
username=os.getenv('SES_SMTP_PASS') password
= dict(host=servernm, port=587, user=username, password=password, use_ssl=False, use_tls=True)
smtp_cfg = create_multipart_msg('Test from stdlib', 'support@answer.ai', 'j@answer.ai', md='**Test message**', html='<b>Test message</b>')
test_msg # 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.
= "**Hello {name}!**\n\nYour special number is: *{num}*" msg
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.
= [{'name': 'Jeremy', 'num': 42}, {'name': 'Rachel', 'num': 7}] inserts
= MarkdownMerge(['aaa@answer.ai', 'bbb@answer.ai'], 'from@answer.ai', 'Test merge',
mm =smtp_cfg, inserts=inserts, test=True) msg, smtp_cfg
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()