feat(v1.1.0): add send mail support via Graph API and SMTP

This commit is contained in:
juanjuanjuanidea
2026-07-24 13:58:44 +08:00
parent 015a63043d
commit e5e1ef3a2d
4 changed files with 102 additions and 2 deletions
+1
View File
@@ -1,3 +1,4 @@
__version__ = "1.1.0"
import os import os
from flask import Flask, render_template, jsonify from flask import Flask, render_template, jsonify
from app.db import db from app.db import db
+50 -2
View File
@@ -1,6 +1,6 @@
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from app.models import Account from app.models import Account
from app.services import graph, imap, verification from app.services import graph, imap, smtp, verification
import os import os
bp = Blueprint("emails", __name__, url_prefix="/api/emails") bp = Blueprint("emails", __name__, url_prefix="/api/emails")
@@ -28,7 +28,6 @@ def get_emails():
token = token_res["access_token"] token = token_res["access_token"]
messages = graph.fetch_messages(token, top=15, proxy_url=proxy_url) messages = graph.fetch_messages(token, top=15, proxy_url=proxy_url)
else: else:
# Fallback to IMAP
messages = imap.fetch_inbox_messages( messages = imap.fetch_inbox_messages(
email=acct.email, email=acct.email,
password=acct.password or acct.refresh_token, password=acct.password or acct.refresh_token,
@@ -51,3 +50,52 @@ def get_emails():
}) })
return jsonify(results) return jsonify(results)
@bp.post("/send")
def send_email():
data = request.json or {}
email = data.get("email")
to_email = data.get("to_email")
subject = data.get("subject", "No Subject")
body = data.get("body", "")
if not email or not to_email:
return jsonify({"error": "email and to_email required"}), 400
acct = Account.query.filter_by(email=email).first()
if not acct:
return jsonify({"error": "account not found"}), 404
proxy_url = os.getenv("PROXY_URL")
if acct.method == "graph":
token_res = graph.get_access_token_graph_result(
client_id=acct.client_id,
refresh_token=acct.refresh_token,
proxy_url=proxy_url
)
if isinstance(token_res, dict) and "access_token" in token_res:
token = token_res["access_token"]
success = graph.send_mail_graph(token, to_email, subject, body, proxy_url=proxy_url)
if success:
return jsonify({"success": True, "message": "Email sent via Graph API"})
return jsonify({"error": "Graph API send mail failed"}), 500
return jsonify({"error": "Token refresh failed"}), 400
else:
try:
success = smtp.send_mail_smtp(
host=os.getenv("SMTP_HOST", "smtp.office365.com"),
port=int(os.getenv("SMTP_PORT", "587")),
username=acct.email,
password=acct.password or acct.refresh_token,
to_email=to_email,
subject=subject,
body_text=body,
use_tls=True
)
if success:
return jsonify({"success": True, "message": "Email sent via SMTP"})
except Exception as e:
return jsonify({"error": f"SMTP send mail failed: {str(e)}"}), 500
return jsonify({"error": "Send mail failed"}), 500
+29
View File
@@ -420,3 +420,32 @@ def delete_emails_graph(
) )
return result return result
def send_mail_graph(access_token: str, to_email: str, subject: str, body_text: str, proxy_url: str = None) -> bool:
"""使用 Microsoft Graph API 发送邮件"""
url = "https://graph.microsoft.com/v1.0/me/sendMail"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
payload = {
"message": {
"subject": subject,
"body": {
"contentType": "Text",
"content": body_text
},
"toRecipients": [
{
"emailAddress": {
"address": to_email
}
}
]
},
"saveToSentItems": "true"
}
proxies = build_proxies(proxy_url) if "build_proxies" in globals() else ({"http": proxy_url, "https": proxy_url} if proxy_url else None)
res = requests.post(url, json=payload, headers=headers, proxies=proxies, timeout=30)
return res.status_code in (200, 202)
+22
View File
@@ -0,0 +1,22 @@
import smtplib
from email.message import EmailMessage
def send_mail_smtp(host: str, port: int, username: str, password: str, to_email: str, subject: str, body_text: str, use_tls: bool = True, use_ssl: bool = False) -> bool:
"""使用 SMTP 协议发送邮件"""
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = username
msg["To"] = to_email
msg.set_content(body_text)
if use_ssl:
with smtplib.SMTP_SSL(host, port, timeout=20) as server:
server.login(username, password)
server.send_message(msg)
else:
with smtplib.SMTP(host, port, timeout=20) as server:
if use_tls:
server.starttls()
server.login(username, password)
server.send_message(msg)
return True