repos
/ analytics-django master

analytics-django

mirror archived upstream

Self-hostable website analytics on Django: a straightforward collector API, dashboards, a world map, and PDF reports.

analyticsdjangodockerhandcodedpythonself-hostedsqliteviteweb-analytics

1.7 KB · 54 lines · Python Raw History
 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)