Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -120,18 +120,33 @@ def _ok_html(text):
|
|
120 |
return f"<div style='padding:10px;border:1px solid #2ea043;border-radius:5px;color:#2ea043;background-color:#333333;'>✅ {text}</div>"
|
121 |
|
122 |
# --- Email support ---
|
123 |
-
|
124 |
-
|
125 |
-
host = os.getenv("SMTP_HOST")
|
126 |
-
port = int(os.getenv("SMTP_PORT", "587"))
|
127 |
-
user = os.getenv("SMTP_USER")
|
128 |
-
pwd = os.getenv("SMTP_PASS")
|
129 |
-
from_addr = os.getenv("EMAIL_FROM")
|
130 |
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
|
134 |
try:
|
|
|
135 |
msg = EmailMessage()
|
136 |
msg["From"] = from_addr
|
137 |
msg["To"] = recipient
|
@@ -142,10 +157,17 @@ def send_email(recipient: str, subject: str, body_text: str, attachment_name: st
|
|
142 |
payload = json.dumps(attachment_json, indent=2).encode("utf-8")
|
143 |
msg.add_attachment(payload, maintype="application", subtype="json", filename=attachment_name)
|
144 |
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
return ""
|
150 |
except Exception as e:
|
151 |
return f"Email send failed: {e}"
|
|
|
120 |
return f"<div style='padding:10px;border:1px solid #2ea043;border-radius:5px;color:#2ea043;background-color:#333333;'>✅ {text}</div>"
|
121 |
|
122 |
# --- Email support ---
|
123 |
+
import base64, json, requests
|
124 |
+
from email.message import EmailMessage
|
|
|
|
|
|
|
|
|
|
|
125 |
|
126 |
+
def _gmail_access_token() -> str:
|
127 |
+
token_url = "https://oauth2.googleapis.com/token"
|
128 |
+
data = {
|
129 |
+
"client_id": os.getenv("GMAIL_CLIENT_ID"),
|
130 |
+
"client_secret": os.getenv("GMAIL_CLIENT_SECRET"),
|
131 |
+
"refresh_token": os.getenv("GMAIL_REFRESH_TOKEN"),
|
132 |
+
"grant_type": "refresh_token",
|
133 |
+
}
|
134 |
+
r = requests.post(token_url, data=data, timeout=20)
|
135 |
+
r.raise_for_status()
|
136 |
+
return r.json()["access_token"]
|
137 |
+
|
138 |
+
def send_email(recipient: str, subject: str, body_text: str,
|
139 |
+
attachment_name: str = None, attachment_json: dict = None) -> str:
|
140 |
+
"""
|
141 |
+
Sends via Gmail API. Returns '' on success, or an error string.
|
142 |
+
"""
|
143 |
+
from_addr = os.getenv("EMAIL_FROM")
|
144 |
+
if not all([os.getenv("GMAIL_CLIENT_ID"), os.getenv("GMAIL_CLIENT_SECRET"),
|
145 |
+
os.getenv("GMAIL_REFRESH_TOKEN"), from_addr]):
|
146 |
+
return "Gmail API not configured (set GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN, EMAIL_FROM)."
|
147 |
|
148 |
try:
|
149 |
+
# Build MIME message
|
150 |
msg = EmailMessage()
|
151 |
msg["From"] = from_addr
|
152 |
msg["To"] = recipient
|
|
|
157 |
payload = json.dumps(attachment_json, indent=2).encode("utf-8")
|
158 |
msg.add_attachment(payload, maintype="application", subtype="json", filename=attachment_name)
|
159 |
|
160 |
+
# Base64url encode the raw RFC822 message
|
161 |
+
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode("utf-8")
|
162 |
+
|
163 |
+
# Get access token and send
|
164 |
+
access_token = _gmail_access_token()
|
165 |
+
api_url = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
|
166 |
+
headers = {"Authorization": f"Bearer {access_token}", "Content-Type": "application/json"}
|
167 |
+
r = requests.post(api_url, headers=headers, json={"raw": raw}, timeout=20)
|
168 |
+
|
169 |
+
if r.status_code >= 400:
|
170 |
+
return f"Gmail API error {r.status_code}: {r.text[:300]}"
|
171 |
return ""
|
172 |
except Exception as e:
|
173 |
return f"Email send failed: {e}"
|