230 lines
9.6 KiB
Python
230 lines
9.6 KiB
Python
"""HTML and plain-text templates for transactional email."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import html
|
|
|
|
BRAND_NAME = "aGentic Email"
|
|
BRAND_HANDLE = "@gentic.email"
|
|
|
|
|
|
def _escape(value: str) -> str:
|
|
return html.escape(value, quote=True)
|
|
|
|
|
|
def build_employee_invitation_email(
|
|
*,
|
|
member_first_name: str,
|
|
member_email: str,
|
|
manager_name: str,
|
|
company_domain: str,
|
|
invite_url: str,
|
|
expires_hours: int = 48,
|
|
) -> tuple[str, str, str]:
|
|
first_name = member_first_name.strip() or "there"
|
|
organization = company_domain.strip()
|
|
manager = manager_name.strip() or "Your administrator"
|
|
|
|
subject = f"{manager} invited you to join {organization}"
|
|
|
|
body_text = (
|
|
f"Hello {first_name},\n\n"
|
|
f"{manager} invited you to join {organization}.\n\n"
|
|
f"Your work email:\n"
|
|
f"{member_email}\n\n"
|
|
f"Set up your account:\n"
|
|
f"{invite_url}\n\n"
|
|
f"This invitation expires in {expires_hours} hours.\n\n"
|
|
f"If you weren't expecting this invitation,\n"
|
|
f"you can safely ignore this email.\n\n"
|
|
f"—\n"
|
|
f"{BRAND_NAME}\n"
|
|
f"This invitation was sent because {manager} created an account "
|
|
f"for you at {organization}."
|
|
)
|
|
|
|
preheader = (
|
|
f"{manager} invited you to join {organization}. "
|
|
f"Set up your work email account."
|
|
)
|
|
|
|
safe_first = _escape(first_name)
|
|
safe_manager = _escape(manager)
|
|
safe_org = _escape(organization)
|
|
safe_email = _escape(member_email)
|
|
safe_invite = _escape(invite_url)
|
|
safe_preheader = _escape(preheader)
|
|
|
|
body_html = f"""<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<meta name="color-scheme" content="light" />
|
|
<meta name="supported-color-schemes" content="light" />
|
|
<title>{_escape(subject)}</title>
|
|
</head>
|
|
<body style="margin:0;padding:0;background-color:#fffdfb;color:#080821;
|
|
font-family:'DM Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
|
|
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent;">
|
|
{safe_preheader}
|
|
</div>
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0"
|
|
style="background-color:#fffdfb;padding:32px 16px;">
|
|
<tr>
|
|
<td align="center">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0"
|
|
style="max-width:600px;border-collapse:separate;">
|
|
<tr>
|
|
<td style="padding:0 0 20px;">
|
|
<table role="presentation" cellspacing="0" cellpadding="0">
|
|
<tr>
|
|
<td style="width:36px;height:34px;background-color:#080821;border-radius:10px 10px 12px 12px;
|
|
text-align:center;vertical-align:middle;">
|
|
<span style="display:inline-block;width:8px;height:8px;background-color:#ff6a52;
|
|
border-radius:50%;margin-top:8px;"></span>
|
|
</td>
|
|
<td style="padding-left:12px;font-size:18px;font-weight:700;color:#080821;letter-spacing:-0.3px;">
|
|
gentic<span style="color:#67677a;font-weight:500;">.email</span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="background-color:#ffffff;border:1px solid #eee9e5;border-radius:20px;
|
|
box-shadow:0 18px 48px rgba(8,8,33,0.06);overflow:hidden;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
<tr>
|
|
<td style="padding:28px 32px 18px;background:linear-gradient(135deg,#fffdfb 0%,#fff6f2 100%);
|
|
border-bottom:1px solid #f3ece7;">
|
|
<p style="margin:0 0 10px;font-size:12px;font-weight:700;letter-spacing:1px;
|
|
text-transform:uppercase;color:#ff6a52;">Work email invitation</p>
|
|
<h1 style="margin:0;font-size:26px;line-height:1.25;font-weight:700;color:#080821;">
|
|
{safe_manager} invited you to join<br />
|
|
<span style="color:#ff6a52;">{safe_org}</span>
|
|
</h1>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:28px 32px 8px;">
|
|
<p style="margin:0 0 24px;font-size:16px;line-height:1.65;color:#080821;">
|
|
Hello {safe_first},
|
|
</p>
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0"
|
|
style="margin:0 0 24px;background-color:#fff3ef;border:1px solid #ffe1d8;border-radius:14px;">
|
|
<tr>
|
|
<td style="padding:18px 20px;">
|
|
<p style="margin:0 0 8px;font-size:12px;font-weight:700;letter-spacing:0.4px;
|
|
text-transform:uppercase;color:#c85e49;">Your work email</p>
|
|
<p style="margin:0;font-size:15px;font-weight:600;color:#080821;">{safe_email}</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<table role="presentation" cellspacing="0" cellpadding="0" style="margin:0 0 24px;">
|
|
<tr>
|
|
<td style="border-radius:12px;background-color:#080821;">
|
|
<a href="{safe_invite}" style="display:inline-block;padding:14px 24px;font-size:15px;
|
|
font-weight:700;color:#ffffff;text-decoration:none;border-radius:12px;">
|
|
Set up your account
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<p style="margin:0 0 16px;font-size:14px;line-height:1.65;color:#67677a;">
|
|
This invitation expires in {expires_hours} hours.
|
|
</p>
|
|
<p style="margin:0;font-size:14px;line-height:1.65;color:#67677a;">
|
|
If you weren't expecting this invitation,<br />
|
|
you can safely ignore this email.
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:20px 32px 28px;border-top:1px solid #f0ebe7;background-color:#faf8f6;">
|
|
<p style="margin:0;font-size:12px;line-height:1.6;color:#a8a4ae;">
|
|
This invitation was sent because {safe_manager} created an account
|
|
for you at {safe_org}.
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</body>
|
|
</html>"""
|
|
|
|
return subject, body_text, body_html
|
|
|
|
|
|
def build_welcome_email(
|
|
*,
|
|
first_name: str,
|
|
work_email: str,
|
|
organization: str | None,
|
|
login_url: str,
|
|
is_company_account: bool,
|
|
) -> tuple[str, str, str]:
|
|
name = first_name.strip() or "there"
|
|
subject = "Welcome to AGMail"
|
|
org_line = (
|
|
f"Your {work_email} mailbox is ready."
|
|
if is_company_account
|
|
else f"Your {work_email} address is active."
|
|
)
|
|
admin_note = (
|
|
"Your organization administrator manages this account."
|
|
if is_company_account and organization
|
|
else ""
|
|
)
|
|
|
|
body_text = (
|
|
f"Hello {name},\n\n"
|
|
f"Welcome to {BRAND_NAME}. {org_line}\n\n"
|
|
f"What you can do now:\n"
|
|
f"- Compose your first email from the inbox\n"
|
|
f"- Ask AG Agent to summarize threads and surface action items\n"
|
|
f"- Review account and security settings\n\n"
|
|
f"Sign in: {login_url}\n\n"
|
|
f"{admin_note}\n\n"
|
|
f"—\n"
|
|
f"AGMail Team\n"
|
|
f"{BRAND_NAME}"
|
|
).strip()
|
|
|
|
safe_name = _escape(name)
|
|
safe_email = _escape(work_email)
|
|
safe_login = _escape(login_url)
|
|
safe_org = _escape(organization or "")
|
|
safe_admin = _escape(admin_note)
|
|
|
|
body_html = f"""<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8" /><title>{_escape(subject)}</title></head>
|
|
<body style="margin:0;padding:32px 16px;background:#fffdfb;color:#080821;font-family:'DM Sans',sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="100%" style="max-width:600px;background:#fff;border:1px solid #eee9e5;border-radius:20px;">
|
|
<tr><td style="padding:28px 32px;">
|
|
<p style="margin:0 0 12px;font-size:12px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:#ff6a52;">Welcome</p>
|
|
<h1 style="margin:0 0 16px;font-size:24px;">Welcome to AGMail</h1>
|
|
<p style="margin:0 0 16px;font-size:15px;line-height:1.7;color:#67677a;">Hello {safe_name}, your mailbox <strong>{safe_email}</strong> is ready.</p>
|
|
{"<p style='margin:0 0 16px;font-size:14px;color:#67677a;'>Organization: <strong>" + safe_org + "</strong></p>" if organization else ""}
|
|
<ul style="margin:0 0 20px;padding-left:20px;color:#67677a;font-size:14px;line-height:1.7;">
|
|
<li>Compose email from your inbox</li>
|
|
<li>Use AG Agent for summaries and action items</li>
|
|
<li>Review security settings after sign-in</li>
|
|
</ul>
|
|
<a href="{safe_login}" style="display:inline-block;padding:12px 18px;border-radius:10px;background:#080821;color:#fff;text-decoration:none;font-weight:700;">Open AGMail</a>
|
|
{"<p style='margin:18px 0 0;font-size:12px;color:#8d8792;'>" + safe_admin + "</p>" if admin_note else ""}
|
|
</td></tr>
|
|
</table>
|
|
</td></tr>
|
|
</table>
|
|
</body></html>"""
|
|
|
|
return subject, body_text, body_html
|