feat: add backend/smtp_client.py
This commit is contained in:
parent
119c22a4ba
commit
8d9b0ff2fa
59
backend/smtp_client.py
Normal file
59
backend/smtp_client.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""SMTP 발송: smtplib STARTTLS + Sent 폴더 자동 저장"""
|
||||
import smtplib, ssl, asyncio
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from email.utils import formatdate
|
||||
from typing import Optional
|
||||
|
||||
SMTP_HOST = "localhost"
|
||||
SMTP_PORT = 587
|
||||
|
||||
|
||||
async def send_mail(
|
||||
username: str, password: str,
|
||||
to: str, subject: str, body: str,
|
||||
cc: Optional[str] = None, bcc: Optional[str] = None,
|
||||
is_html: bool = False,
|
||||
attachments: Optional[list] = None,
|
||||
) -> bytes:
|
||||
"""발송 성공 시 raw 메시지 바이트 반환 (Sent 폴더 저장용)"""
|
||||
msg = MIMEMultipart("alternative" if is_html else "mixed")
|
||||
msg["From"] = username
|
||||
msg["To"] = to
|
||||
msg["Subject"] = subject
|
||||
msg["Date"] = formatdate(localtime=True)
|
||||
if cc: msg["Cc"] = cc
|
||||
|
||||
msg.attach(MIMEText(body, "html" if is_html else "plain", "utf-8"))
|
||||
|
||||
if attachments:
|
||||
for name, data, ctype in attachments:
|
||||
part = MIMEBase(*ctype.split("/", 1))
|
||||
part.set_payload(data)
|
||||
encoders.encode_base64(part)
|
||||
part.add_header("Content-Disposition", "attachment", filename=name)
|
||||
msg.attach(part)
|
||||
|
||||
recipients = [r.strip() for r in to.split(",")]
|
||||
if cc: recipients += [r.strip() for r in cc.split(",")]
|
||||
if bcc: recipients += [r.strip() for r in bcc.split(",")]
|
||||
|
||||
raw_bytes = msg.as_bytes()
|
||||
user_short = username.split("@")[0]
|
||||
|
||||
def _do():
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
s = smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=15)
|
||||
s.ehlo()
|
||||
s.starttls(context=ctx)
|
||||
s.ehlo()
|
||||
s.login(user_short, password)
|
||||
s.sendmail(username, recipients, raw_bytes)
|
||||
s.quit()
|
||||
|
||||
await asyncio.get_event_loop().run_in_executor(None, _do)
|
||||
return raw_bytes
|
||||
Loading…
Reference in New Issue
Block a user