#!/usr/bin/env python3 # gmail-arriere.py — passe UNIQUE sur l'arriéré Gmail des 13 filtres créés le 27/09/2026 : # ce qui est encore en réception de ces expéditeurs reçoit le même traitement que le filtre # (hors réception + marqué lu + label). Rien n'est supprimé ni mis à la corbeille. # Validation propriétaire : « ok pour traiter l'arriéré dans gmail » (27/09/2026). # # Usage (dans le conteneur) : python3 gmail-arriere.py [--dry-run] import subprocess, json, sys, time from datetime import datetime ACCOUNT = "tallagrand.thierry@gmail.com" LOG = "/home/node/.openclaw/workspace/gmail-housekeeping.md" DRY = "--dry-run" in sys.argv PAGE, LOT, PAUSE = 100, 50, 3 # quota Gmail : petites pages, petits lots, pauses REGLES = [ ("Promos", "from:no-reply@info.bricomarche.com"), ("Promos", "from:carrefour@email.carrefour.fr"), ("Promos", "from:cds.newsletter-cdiscount.com"), ("Promos", "from:n.weldom.fr"), ("Promos", "from:crm.samsung.fr"), ("Promos", "from:reply.ebay.fr"), ("Promos", "from:news_frfr@avis-comms.international"), ("Promos", "from:news.suunto.com"), ("Promos", "from:mail.aliexpress.com OR from:selections.aliexpress.com OR from:newarrival.aliexpress.com OR from:promotion@aliexpress.com"), ("Newsletters", "from:beehiiv.com"), ("Dev-Tools", "from:ollama.com"), ("Dev-Tools", "from:go.explore.redhat.com"), ] def gog(args, retries=4): for k in range(retries): r = subprocess.run(["gog", "gmail"] + args + ["-a", ACCOUNT], capture_output=True, text=True, timeout=120) if r.returncode == 0: return r.stdout if "rateLimitExceeded" in r.stderr or "429" in r.stderr: wait = 30 * (k + 1); print(f" quota atteint, pause {wait}s", flush=True); time.sleep(wait); continue raise RuntimeError(r.stderr.strip()[:300]) raise RuntimeError("quota Gmail : abandon après 4 tentatives") def ids_en_reception(query): d = json.loads(gog(["search", f"({query}) in:inbox", "-j", "--max", str(PAGE)])) return [t["id"] for t in d.get("threads", d.get("messages", [])) if t.get("id")] lignes = []; total = 0 print(f"=== gmail-arriere {'DRY-RUN' if DRY else 'GO'} — {datetime.now():%Y-%m-%d %H:%M} ===", flush=True) for label, query in REGLES: n = 0 while True: ids = ids_en_reception(query) if not ids: break if DRY: n = len(ids); print(f" (dry-run) {query[:60]:60s} ≥ {n} fils en réception → {label}"); break for i in range(0, len(ids), LOT): lot = ids[i:i + LOT] gog(["batch", "modify"] + lot + ["--add", label, "--remove", "INBOX,UNREAD", "--force"]) n += len(lot); time.sleep(PAUSE) if len(ids) < PAGE: break time.sleep(PAUSE) print(f" {query[:60]:60s} → {n:4d} fils → {label}", flush=True) lignes.append(f"| {query[:70]} | {label} | {n} |"); total += n print(f"=== fin : {total} fils archivés ===") if not DRY: with open(LOG, "a") as f: f.write(f"\n## {datetime.now():%Y-%m-%d %H:%M:%S} — gmail-arriere.py (arriéré des 13 filtres du 27/09 : hors réception + lu + label)\n\n| Requête | Label | Fils |\n|---|---|---:|\n" + "\n".join(lignes) + f"\n\nTotal : {total}\n")