Self-hostable website analytics on Django: a straightforward collector API, dashboards, a world map, and PDF reports.
analyticsdjangodockerhandcodedpythonself-hostedsqliteviteweb-analytics
1import logging
2import smtplib
3from concurrent.futures import ThreadPoolExecutor
4
5import dns.resolver
6from django.core.mail.backends.base import BaseEmailBackend
7
8log = logging.getLogger(__name__)
9
10_POOL = ThreadPoolExecutor(max_workers=4, thread_name_prefix="mailer")
11
12
13def _deliver(message):
14 try:
15 by_domain = {}
16 for rcpt in message.to + message.cc + message.bcc:
17 by_domain.setdefault(rcpt.rsplit("@", 1)[1], []).append(rcpt)
18
19 payload = message.message().as_bytes()
20 sender = message.from_email
21
22 for domain, rcpts in by_domain.items():
23 mxs = sorted(
24 dns.resolver.resolve(domain, "MX"),
25 key=lambda r: r.preference,
26 )
27 for mx in mxs:
28 host = str(mx.exchange).rstrip(".")
29 try:
30 with smtplib.SMTP(
31 host, 25, local_hostname="bythewood.me", timeout=30
32 ) as smtp:
33 smtp.ehlo()
34 try:
35 smtp.starttls()
36 smtp.ehlo()
37 except smtplib.SMTPNotSupportedError:
38 pass
39 smtp.sendmail(sender, rcpts, payload)
40 break
41 except Exception as exc:
42 log.warning("MX %s failed for %s: %s", host, domain, exc)
43 else:
44 log.error("all MX hosts failed for %s", domain)
45 except Exception:
46 log.exception("mail delivery failed")
47
48
49class DirectMXBackend(BaseEmailBackend):
50 def send_messages(self, email_messages):
51 for message in email_messages:
52 _POOL.submit(_deliver, message)
53 return len(email_messages)