#!/usr/bin/env python3 """Recupera In-Reply-To/References del .eml y recalcula threadId en Wino Mail. Uso: python3 rethread2.py """ import sys, re, sqlite3, os, email bd = sys.argv[1] mails_dir = sys.argv[2] conn = sqlite3.connect(bd) cur = conn.cursor() cur.execute("SELECT id, messageId FROM MailCopy") rows = cur.fetchall() # indexar emls por message-id en nombre de archivo eml_by_mid = {} for root, _, files in os.walk(mails_dir): for f in files: if not f.endswith(".eml"): continue # nombre base contiene el message-id antes de .eml base = f[:-4] eml_by_mid[base] = os.path.join(root, f) def parse_headers(p): try: msg = email.message_from_bytes(open(p, "rb").read()) except Exception: return None mid = msg.get("Message-ID", "") irt = msg.get("In-Reply-To", "") refs = msg.get("References", "") return mid, irt, refs def thread_id(mid, irt, refs): if refs: m = re.search(r"<([^>]+)>", refs) return m.group(1) if m else refs.strip("<>") if irt: m = re.search(r"<([^>]+)>", irt) return m.group(1) if m else irt.strip("<>") if mid: m = re.search(r"<([^>]+)>", mid) return m.group(1) if m else mid.strip("<>") return "" upd_thread = 0 upd_irt = 0 upd_refs = 0 not_found = 0 no_headers = 0 for rid, mid in rows: mid = (mid or "").strip() # buscar eml: por nombre base == mid, o mid contenido p = None if mid in eml_by_mid: p = eml_by_mid[mid] if p is None: # intentar coincidencia parcial (dominio distinto) mid_nodot = mid.split("@")[0][:20] for base, path in eml_by_mid.items(): if mid_nodot and mid_nodot in base: p = path break if p is None: not_found += 1 continue hdrs = parse_headers(p) if hdrs is None: not_found += 1 continue emid, eirt, erefs = hdrs tid = thread_id(emid or mid, eirt, erefs) # guardar headers recuperados cur.execute("UPDATE MailCopy SET inReplyTo=?, \"references\"=?, threadId=? WHERE id=?", (eirt.strip() if eirt else "", erefs.strip() if erefs else "", tid or "", rid)) if tid: upd_thread += 1 if eirt.strip(): upd_irt += 1 if erefs.strip(): upd_refs += 1 conn.commit() print(f"correos: {len(rows)}") print(f"eml no encontrados: {not_found}") print(f"inReplyTo recuperado: {upd_irt}") print(f"references recuperado: {upd_refs}") print(f"threadId asignado: {upd_thread}") # agrupar en hilos print("\n=== hilos detectados (threadId con >1 correo) ===") cur.execute("SELECT threadId, COUNT(*), MAX(sender) FROM MailCopy WHERE threadId!='' GROUP BY threadId HAVING COUNT(*)>1 ORDER BY COUNT(*) DESC LIMIT 15") for row in cur.fetchall(): print(row) conn.close()