diff --git a/app/__init__.py b/app/__init__.py index f26aaaa..8fe9131 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,3 +1,4 @@ +__version__ = "1.1.0" import os from flask import Flask, render_template, jsonify from app.db import db diff --git a/app/api/emails.py b/app/api/emails.py index df1f314..b1cf42d 100644 --- a/app/api/emails.py +++ b/app/api/emails.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, request from app.models import Account -from app.services import graph, imap, verification +from app.services import graph, imap, smtp, verification import os bp = Blueprint("emails", __name__, url_prefix="/api/emails") @@ -28,7 +28,6 @@ def get_emails(): token = token_res["access_token"] messages = graph.fetch_messages(token, top=15, proxy_url=proxy_url) else: - # Fallback to IMAP messages = imap.fetch_inbox_messages( email=acct.email, password=acct.password or acct.refresh_token, @@ -51,3 +50,52 @@ def get_emails(): }) 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 diff --git a/app/services/graph.py b/app/services/graph.py index 12224a3..9b74e7c 100644 --- a/app/services/graph.py +++ b/app/services/graph.py @@ -420,3 +420,32 @@ def delete_emails_graph( ) 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) diff --git a/app/services/smtp.py b/app/services/smtp.py new file mode 100644 index 0000000..354e67b --- /dev/null +++ b/app/services/smtp.py @@ -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