Skip to content

Instantly share code, notes, and snippets.

@guillain
Forked from jriguera/email_notify.py
Created August 5, 2017 22:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guillain/46d558a4bb87ee480744ac0c5dd367f1 to your computer and use it in GitHub Desktop.
Save guillain/46d558a4bb87ee480744ac0c5dd367f1 to your computer and use it in GitHub Desktop.
Email from Python with Jinja2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Fork from jriguera/email_notify.py
# Url: https://gist.github.com/jriguera/f3191528b7676bd60af5
# Thanks to Jriguera!!!
# Python 3 and compatibility with Python 2
from __future__ import unicode_literals, print_function
import os, sys,logging, re, smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from jinja2 import Environment, FileSystemLoader
class EmailNotification(object):
EMAIL_REGEX = re.compile('([\w\-\.\']+@(\w[\w\-]+\.)+[\w\-]+)')
HTML_REGEX = re.compile('(^<!DOCTYPE html.*?>)')
def __init__(self, smtphost=None, smtpport=None, fromuser=None, fromemail=None, login=None,
password=None, templatedir='templates', logger=None, ssl=None):
self.logger = logger
if not logger:
logging.basicConfig()
self.logger = logging.getLogger(__name__)
self.smtp = smtphost
if self.smtp in (None,''):
self.logger.error('No SMTP server provided')
return
self.port = smtpport
if self.port in (None,''):
self.port = 25
self.reply = fromemail
if self.reply in (None,''):
self.logger.error('No Email source provided')
return
self.mfrom = "%s <%s>" % (fromuser, fromemail)
if self.mfrom in (None,''):
self.logger.error('No User namme as sender provided')
return
self.smtplogin = login
self.smtppass = password
self.ssl = ssl
if self.ssl in (None,''):
self.ssl = 0
if os.path.isdir(templatedir):
self.templatedir = templatedir
else:
self.templatedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), templatedir)
self.env = Environment(loader=FileSystemLoader(self.templatedir))
def _mailrender(self, data, template):
template = template + ".tmpl"
self.logger.debug("- Rendering template '%s'" % (template))
text = self.env.get_template(template)
msg = text.render(data)
return msg
def _smtpconnect(self):
try:
self.logger.info('- SMTP connection')
if self.ssl:
smtp = smtplib.SMTP_SSL(self.smtp, self.port)
else:
smtp = smtplib.SMTP(self.smtp, self.port)
except Exception as e:
self.logger.error("Cannot connect with '%s:%s': %s" % (self.smtp, self.port, e))
raise
smtp.ehlo()
if self.smtplogin:
try:
self.logger.info('- Login ongoing')
smtp.login(self.smtplogin, self.smtppass)
except smtplib.SMTPException as e:
self.logger.error("Cannot auth with '%s' on %s: %s" % (self.smtplogin, self.smtp, e))
raise
return smtp
def _smtpsend(self, smtp, recipient, subject, content):
if self.HTML_REGEX.match(content) is None:
self.logger.debug("- Sending text mail to '%s'" % (recipient))
msg = MIMEText(content)
else:
self.logger.debug("- Sending html mail to '%s'" % (recipient))
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(content, 'html', 'utf-8'))
msg['From'] = self.mfrom
msg['To'] = recipient
msg['Reply-to'] = self.reply
msg['Subject'] = subject
smtp.sendmail(self.mfrom, [recipient], msg.as_string())
def send_email(self, recipient, subject, msg):
smtp = self._smtpconnect()
try:
self.logger.info('- Sending email')
self._smtpsend(smtp, recipient, subject, msg)
except smtplib.SMTPException as e:
self.logger.error("Cannot send mail to '%s': %s" % (recipient, e))
raise
finally:
smtp.quit()
def send_bulk(self, msgs):
smtp = self._smtpconnect()
processed = 0
for (recipient, subject, msg) in msgs:
try:
self.logger.info('- Sendng bulk email: recipient:' + recipient + ', subject: ' + subject + ', msg:' + msg)
self._smtpsend(smtp, recipient, subject, msg)
except smtplib.SMTPException as e:
self.logger.error("Cannot send mail to '%s': %s" % (recipient, e))
else:
processed += 1
smtp.quit()
return processed
def mailout(self, email, name, subject, data, template):
if email is None:
error = "Email is empty!"
self.logger.error(error)
raise ValueError(error)
elif self.EMAIL_REGEX.match(email) is None:
error = "Invalid email address!"
self.logger.error(error)
raise ValueError(error)
msg = self._mailrender(data, template)
self.send_email(email, subject, msg)
def mailbulk(self, email_data, template):
elist = []
for edata in email_data:
try:
email = edata["email"]
name = edata["name"]
subject = edata["subject"]
data = edata["data"]
except Exception as e:
continue
if email is None:
error = "Email is empty!"
self.logger.error(error)
continue
elif self.EMAIL_REGEX.match(email) is None:
error = "Invalid email address!"
self.logger.error(error)
continue
msg = self._mailrender(data, template)
elist.append((email, subject, msg))
return self.send_bulk(elist)
def main(argv):
e = EmailNotification(
smtphost="smtp.gmail.com",
smtpport=465,
fromuser="toto",
fromemail="toto@gmail.com",
login='toto@gmail.com',
password='app password',
logger=logging.basicConfig(level=logging.DEBUG),
ssl=1)
elist = [{
"email": "tata@gmail.com, tutu@gmail.com",
"name": "Tata",
"subject": "My subject",
"data" : {
"dear": "Tata",
"msg": "How are you?"
}
}]
e.mailbulk(elist, "email-html_notify")
if __name__ == "__main__":
main(sys.argv)
<!DOCTYPE html>
<html>
<head>
<title>OpenStack notification</title>
<style type="text/css">
span.bold {font-weight: bold;}
table.noborder {border: 0px; padding: 8px;}
th {text-align: left;}
</style>
</head>
<body>
<p>
Dear {{ dear }},
</p>
{% block content %}
{% endblock %}
<p>
Regards, <br />
<br />
The Platform Engineering OpenStack support team
</p>
<p>
-- <br />
This email has been sent to the email address associated with your OpenStack login.
They only relate to events that may affect your OpenStack resources. If you
receive multiple copies of this email, it may be due to the fact that you
are a member of multiple projects.
</p>
</body>
</html>
{% extends "email-html_base.tmpl" %}
{% block content %}
<p>
{{ msg }}
</p>
<p>
<b>AFFECTED INSTANCES:</b>
</p>
<table class='noborder'>
<tr>
<th>UUID</th><th>IP Address</th><th>Host</th>
</tr>
{% for instance in instances -%}
<tr>
<td>{{ instance.id }}</td><td>{{ instance.accessIPv4 }}</td><td>{{ instance.name }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment