Source code for libfritter.mailer

from email.mime.text import MIMEText
import logging
import smtplib
import traceback

from . import sqlitewrapper

CONFIG_ROOT = 'mailer'

def send_email(config, toaddr, subject, msg):
[docs] """Sends an email. Parameters ---------- config : dict_like Must have the following keys: * ``fromaddr`` - the senders address * ``smtpserver`` - the server to send the email via * ``username`` - the username for the server * ``password`` - the password for the server toaddr : str subject : str msg : str """ fromaddr = config['fromaddr'] msg = MIMEText(msg) msg["From"] = fromaddr msg["To"] = toaddr msg["Subject"] = subject server = smtplib.SMTP_SSL(config['smtpserver'], timeout = 5) smtp_user = config['username'] smtp_pass = config['password'] server.login(smtp_user, smtp_pass) r = server.sendmail(fromaddr, toaddr, str(msg)) try: server.quit() except smtplib.sslerror: pass return len(r) > 0 class Mailer(object):
[docs] def __init__(self, config, sql_connector, template_factory, sender = None, delay_send = True): """ Parameters ---------- config : object Configuration object which will be passed as the first parameter to the ``sender`` callable. sql_connector : callable Returning a sqlite connection template_factory : callable(name) Will be passed the name of a template, should return an ``EmailTemplate`` instance. sender : callable, optional Used to actually send the email. Defaults to ``send_email``. delay_send : bool, optional Whether or not to delay all sending of emails. Defaulting to ``True``, this means that when asked to send an email this class will instead cache it (via the provided SQL connection) for sending later. This avoids potentially length delays for the caller, at the expense of the email not being sent immediately. """ self._config = config self._sql_connector = sql_connector self._template_factory = template_factory self._sender = sender or send_email self._delay_send = delay_send self._logger = logging.getLogger('libfritter.mailer') def send_email(self, toaddr, subject, msg):
[docs] return self._sender(self._config, toaddr, subject, msg) def load_template(self, template_name, template_vars):
[docs] et = self._template_factory(template_name) subject = et.subject msg = et.format(template_vars) return subject, msg def send_email_template(self, toaddr, template_name, template_vars):
[docs] self._logger.info("about to send '{0}' to '{1}'.".format(template_name, toaddr)) subject, msg = self.load_template(template_name, template_vars) return self.send_email(toaddr, subject, msg) def store_template(self, toaddr, template_name, template_vars):
[docs] self._logger.debug("storing pending email: '{0}' to '{1}'.".format(template_name, toaddr)) ps = sqlitewrapper.PendingSend(self._sql_connector) ps.toaddr = toaddr ps.template_name = template_name ps.template_vars = template_vars ps.save() return ps def try_send(self, ps):
[docs] try: self.send_email_template(ps.toaddr, ps.template_name, ps.template_vars) ps.mark_sent() self._logger.info("sent '{0}' to '{1}'.".format(ps.template_name, ps.toaddr)) except: ps.retried() ps.last_error = traceback.format_exc() self._logger.exception("while sending {0}.".format(ps)) finally: try: ps.save() except: # no point trying to record the error as part of the pending send self._logger.exception("while saving {0}.".format(ps)) def email_template(self, toaddr, template_name, template_vars):
[docs] """Prepare a new email based on a template. This is expected to be the main entry point for sending new emails. Parameters ---------- toaddr : str The email addresss to send the email to. If empty this will raise ``ValueError``. template_name : str The identifier for the template to use. Will be passed to the template_factory this mailer was created with. template_vars : dict A map of values to format the template's body with. """ if not toaddr: self._logger.error("Rejecting request to send template '{0}' to empty address '{1}'.".format(template_name, toaddr)) raise ValueError("Cannot send email without a valid toaddr (got '{0}')".format(toaddr)) # always store the email ps = self.store_template(toaddr, template_name, template_vars) # see if we're meant to send it right away if not self._delay_send: self.try_send(ps)