91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
email_ui.py — 一键群发工具 (独立可跑版)
|
|
--------------------------------------------------
|
|
运行: python email_ui.py
|
|
"""
|
|
import os, logging, random
|
|
from datetime import datetime
|
|
import gradio as gr
|
|
from send_email_module import send_email # 同目录导入
|
|
|
|
EMAIL_CONFIG_KEY = "default" # 对应 config/default.json
|
|
LOG_FILE = "email_send_log.txt"
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
|
|
|
|
|
# -------------------- 内部函数 --------------------
|
|
def _load_template(file_obj):
|
|
if file_obj is None:
|
|
return ""
|
|
with open(file_obj.name, "r", encoding="utf-8") as f:
|
|
return f.read()
|
|
|
|
|
|
def preview_email(to_addrs, subject, html_file, language,
|
|
want_receipt, want_read_receipt):
|
|
tpl = _load_template(html_file)
|
|
if not tpl:
|
|
return gr.HTML.update("<p style='color:red'>❌ 请先上传 HTML 模板</p>"), None
|
|
|
|
preview = (tpl.replace("{{recipient_name}}", "there")
|
|
.replace("{{recipient_email}}", "example@example.com")
|
|
.replace("{{encoded_recipient_email}}", "ENCODED_EXAMPLE")
|
|
.replace("{{timestamp}}", datetime.now().isoformat()))
|
|
state = {
|
|
"addresses": [a.strip() for a in to_addrs.split(",") if a.strip()],
|
|
"subject": subject,
|
|
"template": tpl,
|
|
"language": language,
|
|
"want_receipt": want_receipt,
|
|
"want_read_receipt": want_read_receipt,
|
|
}
|
|
return gr.HTML.update(preview), state
|
|
|
|
|
|
def send_emails(state):
|
|
if not state:
|
|
return "⚠️ 请先 Preview。"
|
|
out_lines = []
|
|
for addr in state["addresses"]:
|
|
res = send_email(
|
|
EMAIL_CONFIG_KEY, addr, state["subject"], state["template"], "",
|
|
request_receipt=state["want_receipt"],
|
|
request_read_receipt=state["want_read_receipt"],
|
|
language=state["language"],
|
|
)
|
|
ts = datetime.now().isoformat(timespec="seconds")
|
|
line = f"{ts}\t{addr}\t{res['status']}\t{res['message']}"
|
|
out_lines.append(line)
|
|
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
f.write(line + "\n")
|
|
return "\n".join(out_lines)
|
|
|
|
|
|
# -------------------- Gradio UI --------------------
|
|
with gr.Blocks(css=".gr-button {min-width:6rem}") as demo:
|
|
gr.Markdown("## ✉️ 简易 EDM 群发工具")
|
|
|
|
addrs = gr.Textbox(label="收件人(逗号分隔)", placeholder="foo@bar.com, alice@x.com")
|
|
subj = gr.Textbox(label="主题", placeholder="Subject here")
|
|
tpl = gr.File(label="上传 HTML 模板")
|
|
lang = gr.Radio(["english", "chinese"], value="english", label="语言")
|
|
with gr.Row():
|
|
rcpt = gr.Checkbox(label="投递回执")
|
|
read_r = gr.Checkbox(label="已读回执")
|
|
|
|
preview_btn = gr.Button("Preview ⬇️")
|
|
send_btn = gr.Button("Send ✈️", interactive=False)
|
|
out_html = gr.HTML()
|
|
state_box = gr.State()
|
|
|
|
preview_btn.click(preview_email, [addrs, subj, tpl, lang, rcpt, read_r],
|
|
[out_html, state_box]) \
|
|
.then(lambda s: gr.Button.update(interactive=s is not None),
|
|
inputs=state_box, outputs=send_btn)
|
|
send_btn.click(send_emails, state_box, out_html)
|
|
|
|
if __name__ == "__main__":
|
|
demo.queue().launch(server_port=7880, show_api=False) # ← 关闭 API 元数据
|
|
|