fix(email): add text/plain part to every transactional + telecom email

All transactional/worker senders built multipart/alternative (or mixed)
messages with ONLY an HTML part. A single-part multipart/alternative is
malformed and HTML-only mail is a spam-score signal -- the same class of
deliverability bug that hurt the campaign pipeline, but on the telecom /
filing / customer-transactional path (499-Q reminders, RMD/FCC filing
review links, intake/completion/delivery emails, commissions, etc).

- worker_email.send_worker_email: auto-derive plaintext from HTML when
  caller omits text= (fixes the shared helper for all current+future use)
- 16 rolled-their-own senders in scripts/workers/** + scripts/formation/
  document_delivery.py: attach html_to_text(...) plaintext sibling before
  the HTML part (job_server + document_delivery wrap text+html in an
  alternative sub-part so PDFs still attach to the mixed root)
- api/src/email.ts: add dependency-free htmlToText() and default
  sendEmail text to it (fixes checkout/webhook HTML-only sends)

Verified: all py files compile + import at runtime, api tsc passes,
htmlToText handles hrefs/lists/entities, 11 plaintext unit tests pass.
Telecom campaign 407 (Jun 8) was HTML-only + sent in the DKIM-broken
window -> 384 sent / 0 clicks (same junked-mail signature).
This commit is contained in:
justin 2026-06-17 21:07:40 -05:00
parent 899b880e7f
commit b375385efd
19 changed files with 114 additions and 8 deletions

View file

@ -930,7 +930,13 @@ def _send_instant_delivery(
f"Your {service_name} documents are ready \u2014 Order {order_number}"
)
msg["Reply-To"] = "info@performancewest.net"
msg.attach(MIMEText(html, "html"))
# text+html in an alternative sub-part (HTML-only is malformed/spam-signal),
# then PDFs/DOCX attach to the mixed root below.
from scripts._email_plaintext import html_to_text
alt = MIMEMultipart("alternative")
alt.attach(MIMEText(html_to_text(html), "plain"))
alt.attach(MIMEText(html, "html"))
msg.attach(alt)
# Download deliverables from MinIO and attach (PDF + DOCX)
with tempfile.TemporaryDirectory() as tmpdir:
@ -1020,7 +1026,12 @@ def _send_filing_confirmation(
msg["To"] = customer_email
msg["Subject"] = f"\u2705 Filed: {service_name} \u2014 Confirmation {confirmation_number}"
msg["Reply-To"] = "info@performancewest.net"
msg.attach(MIMEText(html, "html"))
# text+html in an alternative sub-part (HTML-only is malformed/spam-signal).
from scripts._email_plaintext import html_to_text
alt = MIMEMultipart("alternative")
alt.attach(MIMEText(html_to_text(html), "plain"))
alt.attach(MIMEText(html, "html"))
msg.attach(alt)
# Attach confirmation PDFs
pdf_paths = [p for p in minio_paths if p.lower().endswith(".pdf")]