Bot automatisé pour nettoyer les emails Gmail selon des règles personnalisées.
- Suppression/archivage automatique des emails selon des règles
- Filtrage par sujet, expéditeur, destinataire, contenu du body, label
- Opérateurs: contient, contient (exact), égal, commence par, finit par
- Support regex via checkbox dédiée
- Condition d'âge (ex: emails de plus de 3 jours)
- Pagination automatique (traite jusqu'à 500 messages par règle)
- TUI (Terminal User Interface) pour gérer les règles
- Filtrage des règles en temps réel (vim-like avec
/) - Modal d'exécution avec logs en temps réel (sujet, expéditeur, date)
- Indicateur visuel du mode dry-run
- Suivi de la dernière exécution de chaque règle
- Logs de toutes les actions effectuées avec rotation automatique
- Logs de progression pendant la recherche (affiche le nombre de messages récupérés)
- Mode dry-run pour tester sans modifier
- Exclusion configurable des dossiers (Corbeille, Spam, Brouillons, Envoyés)
- Rate limiting automatique de l'API (40 appels/sec pour respecter les quotas Google)
- Durée d'exécution affichée en fin de traitement et dans le rapport email
- Python ≥ 3.10. Le code utilise la syntaxe d'union PEP 604 (
X | None) évaluée à l'exécution ; sur Python 3.9 ou antérieur, la TUI plante au démarrage (TypeError: unsupported operand type(s) for |). - Un compte de service Google Workspace avec Domain-Wide Delegation (voir plus bas).
# Configurer l'environnement
cp .env.example .env
# Éditer .env avec vos paramètres
# Lancer (le venv est créé automatiquement)
./manage.shmanage.sh crée le venv au premier lancement et le reconstruit automatiquement
s'il est cassé (interpréteur supprimé/déplacé → symlink venv/bin/python mort) ou
trop ancien (< 3.10). Si aucun Python ≥ 3.10 n'est trouvé, il s'arrête avec une
erreur explicite plutôt que de bâtir un environnement à moitié fonctionnel.
Ou manuellement:
# Créer un environnement virtuel
python3 -m venv venv
source venv/bin/activate
# Installer les dépendances
pip install -r requirements.txt- Créer un projet dans Google Cloud Console
- Activer l'API Gmail
- Créer un Service Account avec Domain-Wide Delegation
- Télécharger la clé JSON et la placer dans
credentials.json - Dans Google Admin Console:
- Security > API Controls > Domain-wide Delegation
- Ajouter le Client ID du Service Account
- Scope requis:
https://www.googleapis.com/auth/gmail.modify
./manage.sh
# ou
./manage.sh tuiRaccourcis clavier:
/- Filtrer les règles (vim-like: taper le texte, Enter pour confirmer, Escape pour effacer)n- Nouvelle règleg- Générer des suggestions de règles (scan du compte, voir ci-dessous)a- Exécuter toutes les règles activess- Exécuter la règle sélectionnéet- Tester la connexion Gmaild- Activer/désactiver le mode dry-runq- Quitter
L'indicateur jaune "DRY MODE" s'affiche en haut quand le mode simulation est actif.
Touche g (ou bouton Suggest) dans la TUI. Le bot scanne les messages récents
(6 derniers mois par défaut), repère les motifs qui reviennent souvent et pour
lesquels aucune règle n'existe encore, puis propose des règles de suppression
prêtes à créer — classées par nombre d'occurrences.
Regroupement sur deux axes :
- Expéditeur : adresses
Fromfréquentes → règlefrom contains <adresse>. - Sujet : phrases de sujet récurrentes (normalisées :
[tags], chiffres, dates et hostnames variables sont ignorés) → règlesubject contains <phrase>. La phrase proposée est le plus long segment littéral commun au groupe, donc lecontainsmatche bien tous les messages concernés.
Dans l'écran de suggestions :
c(ou Create) : créer la règle sélectionnée.a(ou Create All) : créer toutes les suggestions listées.Esc: fermer.
Les suggestions déjà couvertes par une règle active (ou en doublon exact) sont
automatiquement écartées. Rien n'est créé sans votre action — vous validez chaque
règle.
# Exécuter le nettoyage
./manage.sh run
# Mode dry-run (ne fait aucune modification)
./manage.sh dry
# Tester la connexion
./manage.sh test
# Installer/mettre à jour les dépendances
./manage.sh install
# Déployer sur le serveur de production (via Plesk Git)
./manage.sh deploySur le serveur de production, un alias est disponible :
cleangmail # Lance la TUI
cleangmail run # Exécute toutes les règles
cleangmail dry # Dry-run (simulation)
cleangmail test # Test connexion GmailCron configuré : tous les jours à 4h00
# Éditer le crontab
crontab -e
# Tous les jours à 4h — alerte email UNIQUEMENT si le run échoue (via chronic)
MAILTO="vous@example.com"
0 4 * * * /usr/bin/chronic /chemin/vers/manage.sh runAlertes email :
chronic(paquetmoreutils) bufferise la sortie et ne la laisse passer — donccronenvoie un mail àMAILTO— qu'en cas d'échec (code retour ≠ 0). En cas de succès, tout est masqué : pas de mail quotidien inutile.
⚠️ Éviter... run >> logs/cron.log 2>&1: rediriger toute la sortie vers un fichier privecronde sortie à envoyer, donc aucun mail ne part, même en cas d'erreur (une panne peut passer inaperçue longtemps). L'application écrit de toute façon son proprelogs/cleaner.log.
Cron ne démarre pas automatiquement sur WSL2. Pour l'activer:
# Vérifier/démarrer cron
sudo service cron status
sudo service cron startPour démarrer cron automatiquement, ajouter dans /etc/wsl.conf:
[boot]
command = service cron startNote: WSL2 s'arrête après ~8 secondes d'inactivité. Pour que les tâches cron tournent en permanence:
- Garder un terminal WSL ouvert
- Ou utiliser le Task Scheduler Windows pour lancer
wsl -e /chemin/vers/manage.sh run
Supprimer tous les messages de plus de 3 jours contenant "Host Up" dans le sujet:
| Paramètre | Valeur |
|---|---|
| Name | Cleanup Host Up alerts |
| Field | subject |
| Operator | contains |
| Value | Host Up |
| Action | delete |
| Older than days | 3 |
Supprimer les notifications Plesk (toutes versions):
| Paramètre | Valeur |
|---|---|
| Name | Plesk Updates |
| Field | subject |
| Regex | ✓ (coché) |
| Value | Plesk .* Update is Live |
| Action | delete |
| Older than days | 7 |
Note regex: .* signifie "n'importe quels caractères". Le pattern ci-dessus matche "Plesk Obsidian 18.0.74 Update is Live".
Supprimer tous les messages avec le label "Notifications" de plus de 30 jours:
| Paramètre | Valeur |
|---|---|
| Name | Cleanup old notifications |
| Field | label |
| Operator | equals |
| Value | Notifications |
| Action | delete |
| Older than days | 30 |
Note: Utiliser le nom du label tel qu'il apparaît dans Gmail (ex: "Ma Catégorie - Sous-label") ou le slug technique (ex: "ma-categorie---sous-label"). Les deux formats fonctionnent.
gmail-cleaner/
├── manage.sh # Script de gestion (point d'entrée)
├── cleaner.py # Script principal (cron)
├── tui.py # Interface terminal
├── src/
│ ├── config.py # Configuration
│ ├── database.py # Modèles et base SQLite
│ ├── gmail_client.py # Client API Gmail
│ └── rules_engine.py # Moteur de règles
├── data/ # Base de données SQLite
├── logs/ # Fichiers de logs
├── .env # Configuration (non versionné)
└── credentials.json # Clé Service Account (non versionné)
| Variable | Description | Défaut |
|---|---|---|
| GOOGLE_CREDENTIALS_PATH | Chemin vers credentials.json | ./credentials.json |
| GMAIL_USER_EMAIL | Email à impersonner | (requis) |
| DATABASE_PATH | Chemin base SQLite | ./data/gmail_cleaner.db |
| LOG_PATH | Dossier des logs | ./logs |
| LOG_LEVEL | Niveau de log | INFO |
| LOG_MAX_SIZE | Taille max du log avant rotation (octets) | 5242880 (5 MB) |
| LOG_BACKUP_COUNT | Nombre de fichiers de backup | 3 |
| DRY_RUN | Mode simulation | false |
| MAX_SEARCH_RESULTS | Messages max par règle (voir limites API ci-dessous) | 500 |
| PYTHON_PATH | Chemin vers Python | (auto-détecté) |
| Exclusion de dossiers | ||
| EXCLUDE_TRASH | Exclure la corbeille de la recherche | false |
| EXCLUDE_SPAM | Exclure le spam de la recherche | false |
| EXCLUDE_DRAFTS | Exclure les brouillons de la recherche | false |
| EXCLUDE_SENT | Exclure les envoyés de la recherche ( |
false |
| Rapport email | ||
| SMTP_ENABLED | Activer l'envoi de rapport par email | false |
| SMTP_HOST | Serveur SMTP | (requis si SMTP_ENABLED) |
| SMTP_PORT | Port SMTP | 587 |
| SMTP_USER | Utilisateur SMTP | (requis si SMTP_ENABLED) |
| SMTP_PASSWORD | Mot de passe SMTP | (requis si SMTP_ENABLED) |
| SMTP_FROM | Adresse expéditeur | (requis si SMTP_ENABLED) |
| SMTP_TO | Adresse destinataire | (requis si SMTP_ENABLED) |
| SMTP_TLS | Utiliser STARTTLS | true |
| Déploiement | ||
| DEPLOY_SSH_HOST | Alias SSH du serveur de production | (requis pour deploy) |
| DEPLOY_PLESK_DOMAIN | Domaine Plesk | (requis pour deploy) |
| DEPLOY_PLESK_REPO | Nom du repo Git dans Plesk | (requis pour deploy) |
Gmail applique le label SENT selon l'en-tête From:, pas selon le transport.
Un message dont le From: est votre propre adresse est classé SENT même s'il
vous est livré (il porte alors à la fois INBOX et SENT) — et même s'il a été
émis par un service tiers (NAS, monitoring, backup…) ou relayé par un provider
(Mandrill, SES, SendGrid…). Seul compte le fait que le From: = votre adresse.
Conséquence : avec EXCLUDE_SENT=true, la requête ajoute -in:sent et toutes ces
notifications auto-adressées sont ignorées par les règles, alors qu'elles sont bien
dans la boîte de réception.
- Si vous voulez que les règles traitent ces messages (ex. purger des alertes de
sauvegarde/monitoring envoyées depuis votre propre domaine) → laissez
EXCLUDE_SENT=false(défaut). - Ne passez
EXCLUDE_SENT=trueque si vous tenez à préserver tout ce qui porte le labelSENT, en acceptant d'exclure aussi ce type de mail auto-adressé.
Symptôme typique : une règle affiche Found N messages mais 0 matched, ou ne trouve
qu'une poignée de messages alors que la boîte en contient beaucoup plus.
Quand SMTP_ENABLED=true, un rapport est envoyé par email après l'exécution via cleaner.py.
Quand le rapport est envoyé :
| Commande | Email envoyé |
|---|---|
manage.sh run |
✅ Oui |
manage.sh dry |
✅ Oui |
| Cron | ✅ Oui |
| TUI (Run All / Run Selected) | ❌ Non |
Contenu du rapport :
- Date et mode d'exécution (LIVE/DRY RUN)
- Durée d'exécution
- Nombre de règles traitées
- Messages trouvés, actions réussies/échouées
Exemple de sujet :
[Gmail Cleaner] Rapport du 2024-01-20 04:00 - Aucune action[Gmail Cleaner] Rapport du 2024-01-20 04:00 - 2 erreur(s)
La rotation des logs est gérée automatiquement par l'application (pas besoin de logrotate).
Fonctionnement :
- Quand
cleaner.logatteint la taille max (LOG_MAX_SIZE) → renommé encleaner.log.1 - Les anciens backups sont décalés (
.1→.2→.3) - Le plus ancien est supprimé quand le nombre dépasse
LOG_BACKUP_COUNT - Un nouveau
cleaner.logvide est créé
Exemple avec les valeurs par défaut :
logs/
├── cleaner.log # Fichier actif (max 5 MB)
├── cleaner.log.1 # Backup le plus récent
├── cleaner.log.2 # Backup intermédiaire
└── cleaner.log.3 # Backup le plus ancien (supprimé à la prochaine rotation)
L'application intègre un rate limiting automatique (40 appels/sec) pour respecter les quotas Google Workspace.
| Limite | Valeur |
|---|---|
| Quota par projet | 1,200,000 units/min |
| Quota par utilisateur | 15,000 units/min |
| messages.list | 5 units |
| messages.get | 5 units |
| messages.trash | 5 units |
| messages.delete | 10 units |
| Capacité effective | ~1000-1500 emails/min |
Avec MAX_SEARCH_RESULTS=500 (défaut), chaque règle peut traiter jusqu'à 500 messages. Pour des dossiers volumineux, cette valeur peut être augmentée.
Référence : Gmail API Usage Limits