workspace: rapport solaire, prevision de nuit, concierge, courrier, fiche allegee

- Fiche agent maison (AGENTS.md) : prevision de nuit relayee par script, import
  EDF, regle JEEP midi, saisie vehicule/niveau/depart, concierge qui relaie
  « ✅ Noté » ou « Rien n'est noté », groupe A deplace (28 649 -> 25 831 car.).
- Nouvelles references : reference-concierge.md, reference-workspace.md.
- Scripts : rapport-solaire.js, prevision-nuit.js, airtria-ha-vehicule.js,
  concierge-live.js (answer ne sort plus jamais vide, mode CONCIERGE_SIMULATION).
- Courrier Orange / AIRTRIA / Gmail : audits, regles, menage, recents.
- Bancs EVCS tests-pv (G5 accepte « anomal »).
- .gitignore : liste blanche etendue aux nouveaux scripts et bancs.
This commit is contained in:
2026-09-30 11:32:44 +02:00
parent 973a736bc2
commit f121f0a196
37 changed files with 4030 additions and 328 deletions

View File

@@ -0,0 +1,63 @@
#!/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")