Spaces:
Sleeping
Sleeping
Try to send an email
Browse files- agent.py +1 -1
- app.py +25 -11
- email_sender.py +28 -43
agent.py
CHANGED
|
@@ -15,7 +15,7 @@ def generate_email(client, to_email: str, context: str, tone: str) -> tuple[str,
|
|
| 15 |
Contexte : {context}
|
| 16 |
Ton : {tone}
|
| 17 |
|
| 18 |
-
Réponds en deux parties :
|
| 19 |
1. Objet : ligne d'objet de l'email
|
| 20 |
2. Corps : contenu de l'email bien rédigé, en respectant le ton demandé.
|
| 21 |
Commence chaque partie par son étiquette.
|
|
|
|
| 15 |
Contexte : {context}
|
| 16 |
Ton : {tone}
|
| 17 |
|
| 18 |
+
Réponds de manière courte en deux parties :
|
| 19 |
1. Objet : ligne d'objet de l'email
|
| 20 |
2. Corps : contenu de l'email bien rédigé, en respectant le ton demandé.
|
| 21 |
Commence chaque partie par son étiquette.
|
app.py
CHANGED
|
@@ -8,17 +8,31 @@ def on_generate(to_email, context, tone):
|
|
| 8 |
subject, body = generate_email(model, to_email, context, tone)
|
| 9 |
return subject, body
|
| 10 |
|
| 11 |
-
def on_send(
|
| 12 |
-
success, message = send_email(
|
| 13 |
return message
|
| 14 |
|
| 15 |
-
with gr.Blocks(title="Assistant Email
|
| 16 |
-
gr.Markdown("# ✉️
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
tone = gr.Dropdown(choices=["Formel", "Chaleureux", "Direct", "Poli"],
|
| 23 |
value="Formel", label="Ton")
|
| 24 |
|
|
@@ -32,6 +46,6 @@ with gr.Blocks(title="Assistant Email Pro - HuggingFace LLM") as demo:
|
|
| 32 |
status = gr.Markdown()
|
| 33 |
|
| 34 |
generate_btn.click(on_generate, inputs=[to_email, context, tone], outputs=[subject, body])
|
| 35 |
-
send_btn.click(on_send, inputs=[
|
| 36 |
|
| 37 |
-
demo.launch()
|
|
|
|
| 8 |
subject, body = generate_email(model, to_email, context, tone)
|
| 9 |
return subject, body
|
| 10 |
|
| 11 |
+
def on_send(refresh_token, from_email, to_email, subject, body):
|
| 12 |
+
success, message = send_email(refresh_token, to_email, subject, body, from_email)
|
| 13 |
return message
|
| 14 |
|
| 15 |
+
with gr.Blocks(title="Assistant Email - Gmail sécurisé", theme="soft") as demo:
|
| 16 |
+
gr.Markdown("# ✉️ Générateur d’e-mails avec envoi Gmail sécurisé")
|
| 17 |
+
|
| 18 |
+
with gr.Accordion("🔐 Connexion Gmail - instructions", open=False):
|
| 19 |
+
gr.Markdown("""
|
| 20 |
+
### Étapes pour générer votre token Gmail :
|
| 21 |
+
|
| 22 |
+
1. Ouvrez [Google OAuth Playground](https://developers.google.com/oauthplayground)
|
| 23 |
+
2. Cliquez sur ⚙️ (roue dentée) en haut à droite → cochez **Use your own OAuth credentials**
|
| 24 |
+
- `client_id` : fourni par le Space (dans Hugging Face Secrets)
|
| 25 |
+
- `client_secret` : fourni également
|
| 26 |
+
3. Choisissez ce scope :
|
| 27 |
+
https://www.googleapis.com/auth/gmail.send
|
| 28 |
+
4. Cliquez sur **Authorize APIs** → puis sur **Exchange authorization code for tokens**
|
| 29 |
+
5. Copiez-collez ici le `refresh_token`
|
| 30 |
+
""")
|
| 31 |
+
|
| 32 |
+
refresh_token = gr.Textbox(label="🔑 Refresh Token Gmail (copié depuis OAuth Playground)", type="password")
|
| 33 |
+
from_email = gr.Textbox(label="Adresse Gmail utilisée (expéditeur)")
|
| 34 |
+
to_email = gr.Textbox(label="Destinataire", placeholder="exemple@insee.fr")
|
| 35 |
+
context = gr.Textbox(label="Contexte ou objectif", lines=3)
|
| 36 |
tone = gr.Dropdown(choices=["Formel", "Chaleureux", "Direct", "Poli"],
|
| 37 |
value="Formel", label="Ton")
|
| 38 |
|
|
|
|
| 46 |
status = gr.Markdown()
|
| 47 |
|
| 48 |
generate_btn.click(on_generate, inputs=[to_email, context, tone], outputs=[subject, body])
|
| 49 |
+
send_btn.click(on_send, inputs=[refresh_token, from_email, to_email, subject, body], outputs=status)
|
| 50 |
|
| 51 |
+
demo.launch()
|
email_sender.py
CHANGED
|
@@ -1,56 +1,41 @@
|
|
| 1 |
import os
|
| 2 |
import base64
|
| 3 |
-
import pickle
|
| 4 |
from email.mime.text import MIMEText
|
| 5 |
-
from google_auth_oauthlib.flow import InstalledAppFlow
|
| 6 |
from google.auth.transport.requests import Request
|
|
|
|
| 7 |
from googleapiclient.discovery import build
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
creds
|
| 25 |
-
token_path = get_token_path(sender_email)
|
| 26 |
-
|
| 27 |
-
if os.path.exists(token_path):
|
| 28 |
-
with open(token_path, "rb") as token:
|
| 29 |
-
creds = pickle.load(token)
|
| 30 |
-
|
| 31 |
-
if not creds or not creds.valid:
|
| 32 |
-
if creds and creds.expired and creds.refresh_token:
|
| 33 |
-
creds.refresh(Request())
|
| 34 |
-
else:
|
| 35 |
-
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_PATH, SCOPES)
|
| 36 |
-
creds = flow.run_local_server(port=0)
|
| 37 |
-
with open(token_path, "wb") as token:
|
| 38 |
-
pickle.dump(creds, token)
|
| 39 |
-
|
| 40 |
return build("gmail", "v1", credentials=creds)
|
| 41 |
|
| 42 |
-
def send_email(
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
message["subject"] = subject
|
| 48 |
-
message["from"] = sender_email
|
| 49 |
-
raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
| 50 |
-
send_message = {"raw": raw_message}
|
| 51 |
|
| 52 |
-
try:
|
| 53 |
sent = service.users().messages().send(userId="me", body=send_message).execute()
|
| 54 |
-
return True, f"✅ Email envoyé à {to_email} (ID: {sent['id']})"
|
| 55 |
except Exception as e:
|
| 56 |
return False, f"❌ Échec de l'envoi : {e}"
|
|
|
|
| 1 |
import os
|
| 2 |
import base64
|
|
|
|
| 3 |
from email.mime.text import MIMEText
|
|
|
|
| 4 |
from google.auth.transport.requests import Request
|
| 5 |
+
from google.oauth2.credentials import Credentials
|
| 6 |
from googleapiclient.discovery import build
|
| 7 |
|
| 8 |
+
def authenticate_gmail_with_refresh_token(refresh_token: str):
|
| 9 |
+
client_id = os.getenv("GMAIL_CLIENT_ID")
|
| 10 |
+
client_secret = os.getenv("GMAIL_CLIENT_SECRET")
|
| 11 |
+
|
| 12 |
+
if not client_id or not client_secret:
|
| 13 |
+
raise ValueError("Client ID ou Client Secret manquant (définir dans les secrets Hugging Face)")
|
| 14 |
+
|
| 15 |
+
creds = Credentials(
|
| 16 |
+
None,
|
| 17 |
+
refresh_token=refresh_token,
|
| 18 |
+
token_uri="https://oauth2.googleapis.com/token",
|
| 19 |
+
client_id=client_id,
|
| 20 |
+
client_secret=client_secret,
|
| 21 |
+
scopes=["https://www.googleapis.com/auth/gmail.send"]
|
| 22 |
+
)
|
| 23 |
+
creds.refresh(Request())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
return build("gmail", "v1", credentials=creds)
|
| 25 |
|
| 26 |
+
def send_email(refresh_token: str, to_email: str, subject: str, body: str, from_email: str):
|
| 27 |
+
try:
|
| 28 |
+
service = authenticate_gmail_with_refresh_token(refresh_token)
|
| 29 |
+
|
| 30 |
+
message = MIMEText(body)
|
| 31 |
+
message["to"] = to_email
|
| 32 |
+
message["subject"] = subject
|
| 33 |
+
message["from"] = from_email
|
| 34 |
|
| 35 |
+
raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
| 36 |
+
send_message = {"raw": raw_message}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
|
|
|
| 38 |
sent = service.users().messages().send(userId="me", body=send_message).execute()
|
| 39 |
+
return True, f"✅ Email envoyé à {to_email} (ID : {sent['id']})"
|
| 40 |
except Exception as e:
|
| 41 |
return False, f"❌ Échec de l'envoi : {e}"
|