Files
ai-mail-bot/src/smtp_client.py

68 lines
2.3 KiB
Python

import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from pathlib import Path
from src.config import EmailAccount
from src.retry import retry
logger = logging.getLogger(__name__)
@retry()
def send_reply(account: EmailAccount, to_addr: str, subject: str, body: str,
attachments: list[tuple[str, str]] | None = None):
"""发送回复邮件,支持附件。
Args:
attachments: [(本地文件路径, mime_type), ...] 列表
"""
if not account.smtp:
raise RuntimeError(f"账号 {account.username} 未配置 SMTP")
s = account.smtp
logger.info("SMTP 连接: %s:%d (ssl=%s)", s.server, s.port, s.ssl)
if s.ssl:
conn = smtplib.SMTP_SSL(s.server, s.port, timeout=15)
else:
conn = smtplib.SMTP(s.server, s.port, timeout=15)
if s.starttls:
conn.starttls()
conn.login(account.username, account.password)
logger.info("SMTP 登录成功")
reply_subject = subject if subject.lower().startswith("re:") else f"Re: {subject}"
logger.info("SMTP 发送: to=%s subject=%s body=%d 字符 attachments=%d",
to_addr, reply_subject, len(body), len(attachments or []))
if attachments:
msg = MIMEMultipart()
msg.attach(MIMEText(body, "plain", "utf-8"))
for filepath, mime_type in attachments:
p = Path(filepath)
if not p.exists():
logger.warning(" 附件不存在: %s", filepath)
continue
maintype, subtype = mime_type.split("/", 1)
with open(p, "rb") as f:
part = MIMEBase(maintype, subtype)
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", "attachment", filename=p.name)
msg.attach(part)
logger.info(" 添加附件: %s (%s, %d bytes)", p.name, mime_type, p.stat().st_size)
else:
msg = MIMEMultipart("alternative")
msg.attach(MIMEText(body, "plain", "utf-8"))
msg["From"] = account.username
msg["To"] = to_addr
msg["Subject"] = reply_subject
conn.sendmail(account.username, [to_addr], msg.as_string())
conn.quit()
logger.info("SMTP 发送完成")