Initial commit
Claude has done a lot of work, but there are still some errors which cause the mails to be distributed to wrong mail boxes. I am now going to let codex and claude compete. They will have their own branches and merge to main as soon as we are somewhere where we can switch AI.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Download alle e-mails van een IMAP-account naar lokale .eml-bestanden.
|
||||
Gebruik: python download_mailbox.py
|
||||
|
||||
Resultaat: mailbox/<mapnaam>/<id>.eml
|
||||
Het script is herstart-safe: al gedownloade mails worden overgeslagen.
|
||||
"""
|
||||
|
||||
import imaplib
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import getpass
|
||||
from pathlib import Path
|
||||
|
||||
IMAP_HOST = "australius.nl"
|
||||
IMAP_PORT = 993
|
||||
LOCAL_DIR = Path(__file__).parent / "mailbox"
|
||||
|
||||
|
||||
def parse_folder_name(line: bytes) -> str | None:
|
||||
"""Haal mapnaam op uit IMAP LIST-response."""
|
||||
decoded = line.decode("utf-8", errors="replace")
|
||||
# Formaat: (\Flags) "delimiter" "naam" of (\Flags) "delimiter" naam
|
||||
match = re.match(r'\(.*?\)\s+".*?"\s+(.*)', decoded)
|
||||
if not match:
|
||||
return None
|
||||
name = match.group(1).strip().strip('"')
|
||||
return name if name else None
|
||||
|
||||
|
||||
def safe_path(name: str) -> str:
|
||||
"""Zet mapnaam om naar een veilige directorynaam."""
|
||||
return re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", name)
|
||||
|
||||
|
||||
def download():
|
||||
username = input("E-mailadres: ").strip()
|
||||
password = getpass.getpass("Wachtwoord: ")
|
||||
|
||||
print(f"\nVerbinden met {IMAP_HOST}:{IMAP_PORT} ...")
|
||||
try:
|
||||
mail = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
|
||||
mail.login(username, password)
|
||||
except Exception as exc:
|
||||
print(f"Mislukt: {exc}")
|
||||
sys.exit(1)
|
||||
|
||||
print("Ingelogd.\n")
|
||||
|
||||
# Alle mappen ophalen
|
||||
status, raw_folders = mail.list()
|
||||
if status != "OK":
|
||||
print("Kan mappenlijst niet ophalen.")
|
||||
sys.exit(1)
|
||||
|
||||
folders = [parse_folder_name(f) for f in raw_folders if f]
|
||||
folders = [f for f in folders if f]
|
||||
|
||||
print(f"Gevonden mappen ({len(folders)}):")
|
||||
for f in folders:
|
||||
print(f" {f}")
|
||||
print()
|
||||
|
||||
total_new = total_skip = total_err = 0
|
||||
num_folders = len(folders)
|
||||
|
||||
for folder_idx, folder in enumerate(folders, 1):
|
||||
folder_prefix = f"[map {folder_idx}/{num_folders}] {folder}"
|
||||
|
||||
quoted = f'"{folder}"'
|
||||
status, data = mail.select(quoted, readonly=True)
|
||||
if status != "OK":
|
||||
print(f"{folder_prefix}: kan niet openen – overgeslagen.")
|
||||
continue
|
||||
|
||||
num = int(data[0])
|
||||
local_dir = LOCAL_DIR / safe_path(folder)
|
||||
local_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if num == 0:
|
||||
print(f"{folder_prefix}: leeg.")
|
||||
continue
|
||||
|
||||
status, data = mail.search(None, "ALL")
|
||||
if status != "OK":
|
||||
print(f"{folder_prefix}: zoeken mislukt – overgeslagen.")
|
||||
continue
|
||||
|
||||
ids = data[0].split()
|
||||
num_to_fetch = sum(1 for uid in ids if not (local_dir / f"{uid.decode()}.eml").exists())
|
||||
new = skip = err = 0
|
||||
|
||||
for msg_idx, uid in enumerate(ids, 1):
|
||||
path = local_dir / f"{uid.decode()}.eml"
|
||||
if path.exists():
|
||||
skip += 1
|
||||
continue
|
||||
done = new + err + 1
|
||||
print(f"\r{folder_prefix}: bericht {done}/{num_to_fetch} ", end="", flush=True)
|
||||
try:
|
||||
status, msg_data = mail.fetch(uid, "(RFC822)")
|
||||
if status == "OK" and msg_data and msg_data[0]:
|
||||
path.write_bytes(msg_data[0][1])
|
||||
new += 1
|
||||
else:
|
||||
err += 1
|
||||
except Exception:
|
||||
err += 1
|
||||
|
||||
total_new += new
|
||||
total_skip += skip
|
||||
total_err += err
|
||||
parts = [f"{new} nieuw", f"{skip} al aanwezig"]
|
||||
if err:
|
||||
parts.append(f"{err} fout")
|
||||
print(f"\r{folder_prefix}: " + ", ".join(parts) + " ")
|
||||
|
||||
mail.logout()
|
||||
print(f"\nKlaar. {total_new} gedownload, {total_skip} overgeslagen", end="")
|
||||
if total_err:
|
||||
print(f", {total_err} fouten", end="")
|
||||
print(f".\nOpgeslagen in: {LOCAL_DIR.resolve()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
LOCAL_DIR.mkdir(exist_ok=True)
|
||||
download()
|
||||
Reference in New Issue
Block a user