baodan/api/insurance/utils/email.py
2026-07-12 14:17:18 +08:00

112 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""邮件发送工具。"""
import smtplib
import socket
from email.message import EmailMessage
from html import escape
from insurance.config import get_config
class EmailSendError(Exception):
"""邮件发送失败,携带可展示给用户的错误信息。"""
def __init__(self, user_message: str):
super().__init__(user_message)
self.user_message = user_message
def _get_mail_config(key: str, default=None):
value = get_config(key, "")
if value != "":
return value
try:
from configs import dify_config
value = getattr(dify_config, key, None)
return default if value is None else value
except Exception:
return default
def _as_bool(value) -> bool:
if isinstance(value, bool):
return value
return str(value or "").strip().lower() in ("true", "1", "yes", "on")
def _send_with_dify_mail(to_email: str, subject: str, content: str) -> bool:
"""优先复用 Dify 已初始化的邮件客户端,兼容 smtp/resend/sendgrid。"""
try:
from extensions.ext_mail import mail
except Exception:
return False
if not getattr(mail, "is_inited", lambda: False)():
return False
html = "<pre style=\"font-family: sans-serif; white-space: pre-wrap;\">" + escape(content) + "</pre>"
try:
mail.send(to=to_email, subject=subject, html=html)
return True
except Exception as exc:
raise EmailSendError("Dify 邮件服务发送失败,请检查邮件服务配置") from exc
def send_email(to_email: str, subject: str, content: str) -> bool:
"""通过 Dify 风格的 SMTP 配置发送纯文本邮件。"""
if _send_with_dify_mail(to_email, subject, content):
return True
mail_type = str(_get_mail_config("MAIL_TYPE", "") or "").strip().lower()
host = _get_mail_config("SMTP_SERVER", "")
if mail_type and mail_type != "smtp":
raise EmailSendError("当前邮件服务未初始化,请检查 Dify 邮件服务配置")
if not mail_type and not host:
raise EmailSendError("邮件服务未配置,请联系管理员")
port = int(_get_mail_config("SMTP_PORT", "587"))
username = _get_mail_config("SMTP_USERNAME", "")
password = _get_mail_config("SMTP_PASSWORD", "")
default_send_from = _get_mail_config("MAIL_DEFAULT_SEND_FROM", "")
from_email = default_send_from
# Dify 语义SMTP_USE_TLS=true 且 SMTP_OPPORTUNISTIC_TLS=false 表示 SMTP_SSL
# SMTP_USE_TLS=true 且 SMTP_OPPORTUNISTIC_TLS=true 表示 STARTTLS。
use_tls = _as_bool(_get_mail_config("SMTP_USE_TLS", "false"))
opportunistic_tls = _as_bool(_get_mail_config("SMTP_OPPORTUNISTIC_TLS", "false"))
use_starttls = use_tls and opportunistic_tls
use_ssl = use_tls and not use_starttls
placeholders = {"smtp.example.com", "smtp.gmail.com", "your_smtp_password", "no-reply@example.com"}
if not host or not from_email:
raise EmailSendError("邮件服务未配置,请联系管理员")
if host in placeholders or password in placeholders or any(value in from_email for value in placeholders):
raise EmailSendError("邮件服务仍是示例配置,请联系管理员")
message = EmailMessage()
message["Subject"] = subject
message["From"] = from_email
message["To"] = to_email
message.set_content(content)
try:
if use_ssl:
with smtplib.SMTP_SSL(host, port, timeout=10) as server:
if username and password:
server.login(username, password)
server.send_message(message)
else:
with smtplib.SMTP(host, port, timeout=10) as server:
if use_starttls:
server.starttls()
if username and password:
server.login(username, password)
server.send_message(message)
except smtplib.SMTPAuthenticationError as exc:
raise EmailSendError("SMTP 认证失败,请检查邮箱账号或授权码") from exc
except (smtplib.SMTPConnectError, socket.timeout, TimeoutError, OSError) as exc:
raise EmailSendError("SMTP 连接失败,请检查服务器地址、端口和网络") from exc
except smtplib.SMTPException as exc:
raise EmailSendError("SMTP 发送失败,请检查邮箱服务配置") from exc
return True