mirror of
https://github.com/mskatoni/ni-mail.git
synced 2026-09-05 23:47:36 +08:00
feat(v1.1.0): add send mail support via Graph API and SMTP
This commit is contained in:
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user