import logging
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import aiosmtplib
from app.config import settings
logger = logging.getLogger(__name__)
def build_email_shell(
*,
eyebrow: str,
title: str,
body_html: str,
action_label: str | None = None,
action_url: str | None = None,
summary_html: str | None = None,
footer_note: str = "此邮件由系统自动发送,请勿直接回复。",
) -> str:
action_html = ""
if action_label and action_url:
action_html = f"""
"""
summary_block = ""
if summary_html:
summary_block = f"""
{summary_html}
"""
return f"""
{eyebrow}
香港大学中国商业学院
HKU ICB · 班级信息管理平台
{title}
{body_html}
{summary_block}
{action_html}
"""
async def send_email(to: str, subject: str, html_body: str) -> bool:
"""Send HTML email via SMTP. Returns True on success."""
if not settings.smtp_host:
logger.info(f"SMTP not configured, skipping email to {to}: {subject}")
return False
msg = MIMEMultipart("alternative")
msg["From"] = f"{settings.smtp_from_name} <{settings.smtp_from_email}>"
msg["To"] = to
msg["Subject"] = subject
msg.attach(MIMEText(html_body, "html"))
try:
await aiosmtplib.send(
msg,
hostname=settings.smtp_host,
port=settings.smtp_port,
username=settings.smtp_user,
password=settings.smtp_password,
use_tls=True,
)
return True
except Exception as e:
logger.error(f"Failed to send email to {to}: {e}")
return False
async def send_account_activated_email(member_email: str) -> bool:
login_url = f"{settings.frontend_url.rstrip('/')}/login"
html = build_email_shell(
eyebrow="Account Ready",
title="账号已激活,可以开始使用",
body_html="""
你的 HKU ICB 班级账号已经成功激活。
现在可以使用注册邮箱登录平台,进入班级空间查看公告、排期、资源与成员信息。
""",
action_label="立即登录",
action_url=login_url,
)
return await send_email(member_email, "HKU ICB:账号激活成功", html)
async def send_email_verification_code_email(member_email: str, code: str) -> bool:
html = build_email_shell(
eyebrow="Email Verification",
title="请验证你的邮箱地址",
body_html="""
你正在激活 HKU ICB 班级账号,请使用下方验证码完成邮箱验证。
验证码 10 分钟内有效;如果不是你本人操作,可以忽略此邮件。
""",
summary_html=f"""
邮箱验证码
{code}
""",
)
return await send_email(member_email, "HKU ICB:邮箱验证码", html)
async def send_class_notification_email(
emails: list[str],
subject: str,
title: str,
body: str,
action_url: str | None = None,
eyebrow: str = "Class Update",
action_label: str | None = "查看详情",
summary_html: str | None = None,
):
"""Send a styled notification email to class members."""
normalized_body = body.replace("\n", "
")
html = build_email_shell(
eyebrow=eyebrow,
title=title,
body_html=f"{normalized_body}
",
action_label=action_label if action_url else None,
action_url=action_url,
summary_html=summary_html,
)
for email in emails:
await send_email(email, subject, html)