0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-06 21:58:48 +00:00
healthchecks_healthchecks/hc/lib/emails.py

125 lines
3.5 KiB
Python

from __future__ import annotations
import time
from email.utils import make_msgid
from smtplib import SMTPDataError, SMTPServerDisconnected
from threading import Thread
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string as render
class EmailThread(Thread):
MAX_TRIES = 3
def __init__(self, message):
Thread.__init__(self)
self.message = message
def run(self):
for attempt in range(0, self.MAX_TRIES):
try:
# Make sure each retry creates a new connection:
self.message.connection = None
self.message.send()
# No exception--great! Return from the retry loop
return
except (SMTPServerDisconnected, SMTPDataError) as e:
if attempt + 1 == self.MAX_TRIES:
# This was the last attempt and it failed:
# re-raise the exception
raise e
# Wait 1s before retrying
time.sleep(1)
def make_message(name, to, ctx, headers={}):
subject = render("emails/%s-subject.html" % name, ctx).strip()
body = render("emails/%s-body-text.html" % name, ctx)
html = render("emails/%s-body-html.html" % name, ctx)
domain = settings.DEFAULT_FROM_EMAIL.split("@")[-1].strip(">")
headers["Message-ID"] = make_msgid(domain=domain)
# Make sure the From: header contains our display From: address
if "From" not in headers:
headers["From"] = settings.DEFAULT_FROM_EMAIL
# If EMAIL_MAIL_FROM_TMPL is set, prepare a custom MAIL FROM address
bounce_id = headers.pop("X-Bounce-ID", "bounces")
if settings.EMAIL_MAIL_FROM_TMPL:
from_email = settings.EMAIL_MAIL_FROM_TMPL % bounce_id
else:
from_email = settings.DEFAULT_FROM_EMAIL
msg = EmailMultiAlternatives(
subject, body, from_email=from_email, to=(to,), headers=headers
)
msg.attach_alternative(html, "text/html")
return msg
def send(msg, block=False):
assert settings.EMAIL_HOST, (
"No SMTP configuration,"
" see https://github.com/healthchecks/healthchecks#sending-emails"
)
t = EmailThread(msg)
if block or hasattr(settings, "BLOCKING_EMAILS"):
# In tests, we send emails synchronously
# so we can inspect the outgoing messages
t.run()
else:
# Outside tests, we send emails on thread,
# so there is no delay for the user.
t.start()
def login(to, ctx):
send(make_message("login", to, ctx))
def transfer_request(to, ctx):
send(make_message("transfer-request", to, ctx))
def alert(to, ctx, headers={}):
send(make_message("alert", to, ctx, headers=headers))
def verify_email(to, ctx):
send(make_message("verify-email", to, ctx))
def report(to, ctx, headers={}):
m = make_message("report", to, ctx, headers=headers)
send(m, block=True)
def deletion_notice(to, ctx, headers={}):
m = make_message("deletion-notice", to, ctx, headers=headers)
send(m, block=True)
def deletion_scheduled(to, ctx, headers={}):
m = make_message("deletion-scheduled", to, ctx, headers=headers)
send(m, block=True)
def sms_limit(to, ctx):
send(make_message("sms-limit", to, ctx))
def call_limit(to, ctx):
send(make_message("phone-call-limit", to, ctx))
def sudo_code(to, ctx):
send(make_message("sudo-code", to, ctx))
def signal_rate_limited(to, ctx):
send(make_message("signal-rate-limited", to, ctx))