Files
ai-kefu-jc/app/services/email_service.py
T
2026-09-29 14:15:12 +08:00

130 lines
4.9 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 html
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__)
TAG_LABELS = {
"interested": "意向强", "pending": "待确认",
"not_interested": "无意向", "": "未评估",
}
CONTACT_LABELS = {"phone": "手机", "whatsapp": "WhatsApp", "email": "邮箱", "wechat": "微信"}
def _esc(v) -> str:
return html.escape(str(v)) if v else ""
def _flat(v) -> str:
"""主题行用:折叠空白(堵换行注入头部的通道)+ 限长。"""
return " ".join(str(v).split())[:60]
def _contact_rows(contacts: dict) -> str:
return "".join(
f"<tr><td style='padding:6px 12px;font-weight:bold;'>{html.escape(CONTACT_LABELS.get(k, k))}</td>"
f"<td style='padding:6px 12px;'>{_esc(v)}</td></tr>"
for k, v in (contacts or {}).items() if v
)
async def _send(msg: MIMEMultipart) -> bool:
if not all([settings.SMTP_HOST, settings.SMTP_USER, settings.NOTIFY_EMAIL]):
logger.warning("SMTP not configured, skipping email")
return False
try:
await aiosmtplib.send(
msg, hostname=settings.SMTP_HOST, port=settings.SMTP_PORT,
username=settings.SMTP_USER, password=settings.SMTP_PASS,
use_tls=True,
)
logger.info("Lead email sent to %s", settings.NOTIFY_EMAIL)
return True
except Exception:
logger.exception("Failed to send lead email")
return False
async def send_lead_email(
customer_profile: dict,
contact_info: dict,
reasoning: str,
conversation_messages: list[dict],
intent_level: str = "",
) -> bool:
tag = TAG_LABELS.get(intent_level, "未评估")
contacts = dict(contact_info or {})
country = (customer_profile or {}).get("目标国家", "")
if not country:
country = (customer_profile or {}).get("target_country", "未知")
msg = MIMEMultipart("alternative")
contact_parts = [
f"{CONTACT_LABELS.get(k, k)}: {_flat(v)}" for k, v in contacts.items() if v
]
subject = f"[新询盘·{tag}] {_flat(country)}"
if contact_parts:
subject += " | " + ", ".join(contact_parts)
msg["Subject"] = subject
msg["From"] = f"【XYH】 <{settings.SMTP_USER}>"
msg["To"] = settings.NOTIFY_EMAIL
info_rows = "".join(
f"<tr><td style='padding:6px 12px;font-weight:bold;'>{_esc(k)}</td>"
f"<td style='padding:6px 12px;'>{_esc(v)}</td></tr>"
for k, v in (customer_profile or {}).items() if not str(k).startswith("_")
)
chat_lines = "".join(
f"<div style='margin:4px 0;'><b>{'客户' if m.get('role') == 'user' else 'AI'}:</b> {_esc(m.get('content', ''))}</div>"
for m in conversation_messages or []
)
html_body = f"""
<html><body style="font-family:sans-serif;color:#333;">
<h2>🎉 新询盘({html.escape(tag)})</h2>
<h3>联系方式</h3>
<table style="border-collapse:collapse;border:1px solid #ddd;">{_contact_rows(contacts)}</table>
<h3>客户画像</h3>
<table style="border-collapse:collapse;border:1px solid #ddd;">{info_rows}</table>
<h3>评估理由</h3><p>{_esc(reasoning)}</p>
<h3>完整对话记录</h3>
<div style="background:#f5f5f5;padding:12px;border-radius:6px;">{chat_lines}</div>
<p style="color:#999;font-size:12px;">注:邮件为触发时快照,对话可能仍在进行,最新记录请以后台为准。</p>
</body></html>
"""
msg.attach(MIMEText(html_body, "html", "utf-8"))
return await _send(msg)
async def send_returning_lead_email(contact_info: dict) -> bool:
"""D5: 24h 内同联系方式再访,只发一行轻提醒。"""
contact_parts = [
f"{CONTACT_LABELS.get(k, k)}: {_flat(v)}" for k, v in (contact_info or {}).items() if v
]
msg = MIMEMultipart("alternative")
msg["Subject"] = f"[线索回访] {' | '.join(contact_parts) or '已知客户'}再次来访"
msg["From"] = f"【XYH】 <{settings.SMTP_USER}>"
msg["To"] = settings.NOTIFY_EMAIL
msg.attach(MIMEText(
f"<html><body><p>该联系方式 24h 内已发过完整询盘,客户又开启了新对话,"
f"详情请查后台。</p></body></html>", "html", "utf-8"))
return await _send(msg)
async def send_correction_email(contact_info: dict, count: int = 1) -> bool:
"""D4: 联系方式变化(改号/新增/无提示换值)后的轻量更正邮件,每会话上限3封。"""
msg = MIMEMultipart("alternative")
msg["Subject"] = f"[询盘·更正{count}] 客户联系方式已更新(以本封为准)"
msg["From"] = f"【XYH】 <{settings.SMTP_USER}>"
msg["To"] = settings.NOTIFY_EMAIL
msg.attach(MIMEText(
f"<html><body><p>这是本会话第 {count} 次更正(上限 3 次),请以本封为准:</p>"
f"<table>{_contact_rows(contact_info)}</table>"
f"</body></html>", "html", "utf-8"))
return await _send(msg)