Files
mailcat/maak_mappen.py
T
wienen 4780c18d70 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.
2026-07-04 11:18:05 +02:00

254 lines
7.9 KiB
Python

#!/usr/bin/env python3
"""
Maak alle nieuwe IMAP-mappen aan op australius.nl.
Bestaande mappen worden overgeslagen.
Gebruik: python3 maak_mappen.py
"""
import imaplib
import base64
import re
import sys
import config
# Sommige Dovecot-servers eisen "INBOX." als prefix voor submappen.
# Zet op "" als de server mappen zonder prefix accepteert.
PREFIX = "INBOX."
# ── mUTF-7 helpers (RFC 3501) ─────────────────────────────────────────────────
def to_mutf7(s: str) -> str:
"""Codeer een mapnaam naar Modified UTF-7 voor IMAP-aanroepen (RFC 3501).
Verschillen met standaard base64: geen padding, '/' vervangen door ','."""
res = []
buf = []
for c in s:
if 0x20 <= ord(c) <= 0x7e and c != "&":
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
buf.clear()
res.append(c)
elif c == "&":
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
buf.clear()
res.append("&-")
else:
buf.append(c)
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
return "".join(res)
def from_mutf7(s: str) -> str:
"""Decodeer een mUTF-7-mapnaam (van server) terug naar Unicode."""
res = []
i = 0
while i < len(s):
if s[i] == "&":
j = s.index("-", i + 1)
if j == i + 1:
res.append("&")
else:
encoded = s[i + 1 : j].replace(",", "/")
pad = (4 - len(encoded) % 4) % 4
res.append(
base64.b64decode(encoded + "=" * pad).decode("utf-16-be")
)
i = j + 1
else:
res.append(s[i])
i += 1
return "".join(res)
def mappen() -> list[str]:
"""Alle te maken mappen, zonder prefix (prefix wordt automatisch toegevoegd)."""
folders = [
# Nieuwsbrieven
"Nieuwsbrieven",
"Nieuwsbrieven.Leren",
"Nieuwsbrieven.Leren.AI Report",
"Nieuwsbrieven.Leren.Ben Tiggelaar",
"Nieuwsbrieven.Leren.Brian Keating",
"Nieuwsbrieven.Leren.Coursera",
"Nieuwsbrieven.Leren.Elizabeth Butler",
"Nieuwsbrieven.Leren.Eva Keiffenheim",
"Nieuwsbrieven.Leren.MIT Open Learning",
"Nieuwsbrieven.Leren.MIT Technology Review",
"Nieuwsbrieven.Leren.Nick Milo",
"Nieuwsbrieven.Leren.Storytelling with Data",
"Nieuwsbrieven.Nieuws & Vakbladen",
"Nieuwsbrieven.Nieuws & Vakbladen.Computable",
"Nieuwsbrieven.Nieuws & Vakbladen.The Presentation Guru",
"Nieuwsbrieven.Nieuws & Vakbladen.Vakblad Crisismanager",
"Nieuwsbrieven.Overig",
"Nieuwsbrieven.Overig.Lendahand",
"Nieuwsbrieven.Overig.The Good Roll",
# Financieel
"Financieel",
"Financieel.Bank",
"Financieel.Bank.ABN AMRO",
"Financieel.Bank.ICS",
"Financieel.Bank.NPEX",
"Financieel.Bank.SnelStart",
"Financieel.Bank.Tellow",
"Financieel.Verzekering",
"Financieel.Verzekering.Univé",
"Financieel.Verzekering.VEZA",
"Financieel.Facturen",
# Facturen per kwartaal: 2020 t/m huidig jaar + 1
*[
f"Financieel.Facturen.{jaar}.{kwartaal}"
for jaar in range(2020, 2027)
for kwartaal in ["Q1", "Q2", "Q3", "Q4"]
],
# Werk
"Werk",
"Werk.Opdrachten",
"Werk.Opdrachten.Circle8",
"Werk.Opdrachten.Flextender",
"Werk.Opdrachten.FutureXL",
"Werk.Opdrachten.Get There",
"Werk.Opdrachten.Huckr",
"Werk.Opdrachten.IMMENS",
"Werk.Opdrachten.Jamie",
"Werk.Opdrachten.OneStopSourcing",
"Werk.Overig",
"Werk.Overig.BOIP",
"Werk.Overig.Gemeente Groningen",
# Diensten
"Diensten",
"Diensten.Hosting",
"Diensten.Hosting.Backblaze",
"Diensten.Hosting.Lets Encrypt",
"Diensten.Hosting.STRATO",
"Diensten.Hosting.TransIP",
"Diensten.Software",
"Diensten.Software.1Password",
"Diensten.Software.CogSci Apps",
"Diensten.Software.Claude",
"Diensten.Software.Cursor",
"Diensten.Software.Kahoot",
"Diensten.Software.Matter",
"Diensten.Software.Microsoft Teams",
"Diensten.Software.MindNode",
"Diensten.Software.Papers",
"Diensten.Software.Perplexity",
"Diensten.Software.Sophos",
"Diensten.Telecom",
"Diensten.Telecom.KPN",
"Diensten.Telecom.Vodafone",
# Mobiliteit
"Mobiliteit",
"Mobiliteit.EV",
"Mobiliteit.EV.Alfen",
"Mobiliteit.EV.Eneco eMobility",
"Mobiliteit.EV.FastNed",
"Mobiliteit.EV.Mitsubishi",
"Mobiliteit.EV.OPnGO",
"Mobiliteit.EV.Shell Recharge",
"Mobiliteit.EV.Tesla",
"Mobiliteit.OV & Reizen",
"Mobiliteit.OV & Reizen.Booking",
"Mobiliteit.OV & Reizen.Flitsmeister",
"Mobiliteit.OV & Reizen.NS",
# Bestellingen
"Bestellingen",
"Bestellingen.123inkt",
"Bestellingen.Allekabels",
"Bestellingen.bol",
"Bestellingen.Coolblue",
"Bestellingen.DHL",
"Bestellingen.GLS",
"Bestellingen.Makro",
"Bestellingen.Office Centre",
"Bestellingen.PostNL",
"Bestellingen.Sligro",
"Bestellingen.Smartphonehoesjes",
"Bestellingen.Viking",
# Archief nieuwe jaren
"Archief.2023",
"Archief.2024",
"Archief.2025",
"Archief.2026",
]
return folders
def run():
cfg = config.load()
print(f"Account: {cfg.username}\n")
print(f"Verbinden met {cfg.imap_host}:{cfg.imap_port} ...")
try:
mail = imaplib.IMAP4_SSL(cfg.imap_host, cfg.imap_port)
mail.login(cfg.username, cfg.password)
except Exception as e:
print(f"Verbinding mislukt: {e}")
sys.exit(1)
# Bestaande mappen ophalen en decoderen vanuit mUTF-7
status, raw = mail.list()
existing = set()
for item in raw:
if not item:
continue
# Serverresponse is ASCII (mUTF-7); decodeer naar leesbare naam
raw_str = item.decode("ascii", errors="replace")
m = re.search(r'"?" (.*?)$', raw_str)
if m:
raw_name = m.group(1).strip('"')
try:
existing.add(from_mutf7(raw_name))
except Exception:
existing.add(raw_name)
te_maken = mappen()
print(f"\n{len(te_maken)} mappen te controleren ({len(existing)} bestaan al op server)\n")
aangemaakt = 0
overgeslagen = 0
fouten = 0
for folder in te_maken:
volledige_naam = PREFIX + folder
if volledige_naam in existing or folder in existing:
print(f" ✓ bestaat {folder}")
overgeslagen += 1
continue
# Codeer naar mUTF-7 voor IMAP (puur ASCII, ook voor &, é, enz.)
gecodeerd = to_mutf7(volledige_naam)
status, data = mail.create(f'"{gecodeerd}"')
if status == "OK":
print(f" + aangemaakt {folder}")
aangemaakt += 1
else:
print(f" ✗ FOUT {folder}: {data}")
fouten += 1
mail.logout()
print(f"\nKlaar: {aangemaakt} aangemaakt, {overgeslagen} bestonden al, {fouten} fouten.")
if __name__ == "__main__":
run()