fix(email): warmer onboarding copy + Reply-To/Date headers
Personalize the access-request and approval emails, add a real Date header and a Reply-To (approval -> admin, new-request -> requester) so they read as a conversation rather than a no-reply blast — small nudges for deliverability; sender reputation still does most of the work.
This commit is contained in:
parent
a92112cefb
commit
8135527d75
1 changed files with 26 additions and 11 deletions
|
|
@ -8,6 +8,7 @@ import logging
|
||||||
import smtplib
|
import smtplib
|
||||||
import ssl
|
import ssl
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
|
from email.utils import formatdate
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
|
|
||||||
|
|
@ -18,7 +19,11 @@ def email_enabled() -> bool:
|
||||||
return bool(settings.smtp_host and settings.smtp_user and settings.smtp_password)
|
return bool(settings.smtp_host and settings.smtp_user and settings.smtp_password)
|
||||||
|
|
||||||
|
|
||||||
def _send(to: list[str], subject: str, body: str) -> bool:
|
def _admin_contact() -> str | None:
|
||||||
|
return next(iter(sorted(settings.admin_email_set)), None)
|
||||||
|
|
||||||
|
|
||||||
|
def _send(to: list[str], subject: str, body: str, reply_to: str | None = None) -> bool:
|
||||||
recipients = [e for e in to if e]
|
recipients = [e for e in to if e]
|
||||||
if not recipients:
|
if not recipients:
|
||||||
return False
|
return False
|
||||||
|
|
@ -29,6 +34,11 @@ def _send(to: list[str], subject: str, body: str) -> bool:
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
msg["From"] = settings.smtp_from or settings.smtp_user
|
msg["From"] = settings.smtp_from or settings.smtp_user
|
||||||
msg["To"] = ", ".join(recipients)
|
msg["To"] = ", ".join(recipients)
|
||||||
|
# A real Date and a Reply-To (so it's a conversation, not a no-reply blast) both
|
||||||
|
# nudge spam filters the right way; reputation still does most of the work.
|
||||||
|
msg["Date"] = formatdate(localtime=True)
|
||||||
|
if reply_to:
|
||||||
|
msg["Reply-To"] = reply_to
|
||||||
msg.set_content(body)
|
msg.set_content(body)
|
||||||
try:
|
try:
|
||||||
with smtplib.SMTP(settings.smtp_host, settings.smtp_port, timeout=20) as s:
|
with smtplib.SMTP(settings.smtp_host, settings.smtp_port, timeout=20) as s:
|
||||||
|
|
@ -43,18 +53,23 @@ def _send(to: list[str], subject: str, body: str) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def send_access_approved(email: str) -> bool:
|
def send_access_approved(email: str) -> bool:
|
||||||
return _send(
|
admin = _admin_contact()
|
||||||
[email],
|
body = (
|
||||||
"Your Subfeed access is approved",
|
"Hi,\n\n"
|
||||||
"Good news — your access to Subfeed has been approved.\n\n"
|
"Your request to use Subfeed has been approved — welcome aboard.\n\n"
|
||||||
"Sign in with this Google account to start browsing your subscriptions.\n",
|
"Just open Subfeed and sign in with this Google account, and your YouTube\n"
|
||||||
|
"subscriptions feed will be ready.\n\n"
|
||||||
|
"If you weren't expecting this, you can ignore the message.\n\n"
|
||||||
|
"— Subfeed"
|
||||||
|
+ (f"\n\nQuestions? Just reply to this email ({admin})." if admin else "")
|
||||||
)
|
)
|
||||||
|
return _send([email], "You're in — your Subfeed access is approved", body, reply_to=admin)
|
||||||
|
|
||||||
|
|
||||||
def send_admin_new_request(admins: list[str], requester: str) -> bool:
|
def send_admin_new_request(admins: list[str], requester: str) -> bool:
|
||||||
return _send(
|
body = (
|
||||||
admins,
|
f"{requester} just requested access to Subfeed.\n\n"
|
||||||
"Subfeed: new access request",
|
"Open Subfeed → Settings → Account → Access requests to approve or deny it.\n\n"
|
||||||
f"{requester} has requested access to Subfeed.\n\n"
|
"(Reply to this email to reach the requester directly.)\n"
|
||||||
"Approve or deny it in Settings → Admin.\n",
|
|
||||||
)
|
)
|
||||||
|
return _send(admins, f"Subfeed access request from {requester}", body, reply_to=requester)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue