loko/loko/scripts/create_outlook_drafts.py

215 lines
7.5 KiB
Python
Executable file

#!/usr/bin/env python3
"""
Script pour la création de brouillons d'e-mails pour Outlook (PWA / Web sous Ubuntu).
Consomme le fichier 'drafts_payload.json' (ou le dossier généré lors de la création d'utilisateurs).
Modes disponibles :
- pwa (défaut) : Ouvre directement les deep links de composition dans Chromium avec la session Outlook connectée.
- html : Ouvre la page web interactive locale 'open_drafts.html'.
- eml : Exporte des fichiers brouillons standards .eml (RFC822 avec X-Unsent: 1).
Usage :
python scripts/create_outlook_drafts.py chemin/vers/drafts_payload.json
python scripts/create_outlook_drafts.py chemin/vers/drafts_payload.json --step
python scripts/create_outlook_drafts.py chemin/vers/drafts_payload.json --delay 3
python scripts/create_outlook_drafts.py chemin/vers/drafts_payload.json --mode html
python scripts/create_outlook_drafts.py chemin/vers/drafts_payload.json --mode eml
"""
import argparse
import email.message
import json
import os
import shutil
import subprocess
import sys
import time
import urllib.parse
import webbrowser
from pathlib import Path
def find_chromium_cmd() -> list:
"""Détecte la commande Chromium disponible pour ouvrir la session PWA sous Ubuntu."""
candidates = [
"/snap/bin/chromium",
"chromium",
"chromium-browser",
"google-chrome",
"google-chrome-stable",
]
for cmd in candidates:
if shutil.which(cmd):
# Vérifier si on peut ajouter le profil Default
return [cmd, "--profile-directory=Default"]
return []
def create_eml_draft(to_email: str, subject: str, body: str, output_path: Path):
"""Génère un fichier .eml standard marqué comme brouillon (X-Unsent: 1)."""
msg = email.message.EmailMessage()
msg["To"] = to_email
msg["Subject"] = subject
msg["X-Unsent"] = "1" # Marqueur standard de brouillon pour Outlook, Thunderbird, Apple Mail
msg.set_content(body)
with open(output_path, "wb") as f:
f.write(msg.as_bytes())
def main():
parser = argparse.ArgumentParser(
description="Crée des brouillons d'e-mails dans Outlook PWA / Web ou génère des fichiers brouillons."
)
parser.add_argument(
"input",
type=str,
help="Chemin vers le fichier drafts_payload.json ou le dossier de sortie de création d'utilisateurs.",
)
parser.add_argument(
"--mode",
choices=["pwa", "html", "eml"],
default="pwa",
help="Mode de traitement : 'pwa' (ouvre dans Outlook PWA), 'html' (ouvre la page locale), 'eml' (exporte des fichiers .eml).",
)
parser.add_argument(
"--delay",
type=float,
default=2.5,
help="Délai en secondes entre l'ouverture de chaque brouillon en mode automatique (défaut : 2.5s).",
)
parser.add_argument(
"--step",
action="store_true",
default=False,
help="Mode pas-à-pas : demande confirmation avant d'ouvrir chaque brouillon.",
)
parser.add_argument(
"--dry-run",
action="store_true",
default=False,
help="Affiche les actions prévues sans ouvrir de navigateur ni créer de fichiers.",
)
args = parser.parse_args()
input_path = Path(args.input)
if not input_path.exists():
sys.stderr.write(f"Erreur : '{args.input}' est introuvable.\n")
sys.exit(1)
# Résoudre le fichier drafts_payload.json
payload_file = input_path
if input_path.is_dir():
payload_file = input_path / "drafts_payload.json"
if not payload_file.exists():
sys.stderr.write(f"Erreur : Aucun fichier drafts_payload.json trouvé dans '{input_path}'.\n")
sys.exit(1)
try:
with open(payload_file, "r", encoding="utf-8") as f:
drafts = json.load(f)
except Exception as exc:
sys.stderr.write(f"Erreur lors de la lecture de {payload_file} : {exc}\n")
sys.exit(1)
if not isinstance(drafts, list) or not drafts:
print("Aucun brouillon trouvé dans la charge utile.")
return
print(f"Chargement de {len(drafts)} brouillon(s) depuis {payload_file.name}...")
# Mode HTML
if args.mode == "html":
html_file = payload_file.parent / "open_drafts.html"
if not html_file.exists():
sys.stderr.write(f"Page HTML introuvable à {html_file}.\n")
sys.exit(1)
print(f"Ouverture de la page interactive : {html_file.resolve()}")
if not args.dry_run:
webbrowser.open(f"file://{html_file.resolve()}")
return
# Mode EML
if args.mode == "eml":
eml_dir = payload_file.parent / "eml_drafts"
if not args.dry_run:
eml_dir.mkdir(parents=True, exist_ok=True)
print(f"Génération des fichiers .eml dans {eml_dir.resolve()}...")
for item in drafts:
username = item.get("username", "user")
out_eml = eml_dir / f"{username}_draft.eml"
print(f" -> {out_eml.name} ({item.get('to')})")
if not args.dry_run:
create_eml_draft(
to_email=item.get("to", ""),
subject=item.get("subject", ""),
body=item.get("body", ""),
output_path=out_eml,
)
print("Fichiers .eml générés avec succès.")
return
# Mode PWA (Défaut)
chromium_cmd = find_chromium_cmd()
if chromium_cmd:
print(f"Navigateur détecté pour la session PWA : {' '.join(chromium_cmd)}")
else:
print("Utilisation du navigateur système par défaut.")
print("\n" + "=" * 60)
print(f"Ouverture des brouillons dans Outlook ({len(drafts)} message(s))")
print("Outlook sauvegardera automatiquement chaque message dans le dossier 'Brouillons'.")
print("=" * 60 + "\n")
for idx, item in enumerate(drafts, start=1):
to_email = item.get("to", "")
subject = item.get("subject", "")
deeplink = item.get("outlook_deeplink")
first_name = item.get("first_name", "")
last_name = item.get("last_name", "")
if not deeplink:
params = urllib.parse.urlencode({
"to": to_email,
"subject": subject,
"body": item.get("body", ""),
})
deeplink = f"https://outlook.cloud.microsoft/mail/deeplink/compose?{params}"
print(f"[{idx}/{len(drafts)}] {first_name} {last_name} <{to_email}>")
if args.step:
choice = input(" -> Appuyez sur Entrée pour ouvrir dans Outlook (ou 'q' pour quitter, 's' pour passer) : ").strip().lower()
if choice == "q":
print("Arrêt demandé par l'utilisateur.")
break
elif choice == "s":
print(" Passé.")
continue
if args.dry_run:
print(f" [DRY-RUN] Ouverture de : {deeplink[:90]}...")
else:
if chromium_cmd:
try:
subprocess.Popen(
chromium_cmd + [deeplink],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except Exception:
webbrowser.open(deeplink)
else:
webbrowser.open(deeplink)
print(" -> Brouillon ouvert dans Outlook !")
if not args.step and idx < len(drafts) and not args.dry_run:
time.sleep(args.delay)
print("\nTous les brouillons ont été traités.")
if __name__ == "__main__":
main()