Add decisions-based mailing list routing

The prompts used to arrive at this change since the previous commit

User said that in the future they will run scripts themselves when instructed, then asked to take the next step after the decisions.json mailing-list routing report and plan.

Any special observations that may be relevant for version management for this version. Be brief.

Added mailinglist_routes.json as the normalized runtime policy and wired mail_routes.py to use it before legacy domain routes. No mailbox-affecting scripts were run; validation was local only.
This commit is contained in:
2026-07-04 16:23:48 +02:00
parent be480fef87
commit 701be98d47
4 changed files with 976 additions and 8 deletions
+36
View File
@@ -1,10 +1,14 @@
"""Shared mail routing policy for mailcat."""
import email.utils
import json
import re
from datetime import datetime
from functools import cache
from pathlib import Path
PREFIX = "INBOX."
MAILINGLIST_ROUTES_FILE = Path(__file__).parent / "mailinglist_routes.json"
DOMAIN_ROUTES: list[tuple[list[str], str]] = [
(["nl.abnamro.com", "abnamro.nl"], "Financieel.Bank.ABN AMRO"),
@@ -104,6 +108,31 @@ EXCLUDED_SOURCE_FOLDERS = {
ALREADY_SORTED_FOLDERS = {"INBOX.Facturen - verwerkt"}
@cache
def mailinglist_routes() -> list[dict]:
"""Return normalized mailing-list routing rules."""
if not MAILINGLIST_ROUTES_FILE.exists():
return []
return json.loads(MAILINGLIST_ROUTES_FILE.read_text(encoding="utf-8"))
def mailinglist_destination(from_addr: str) -> str | None:
"""Return a mailing-list destination based on normalized policy rules."""
domain = sender_domain(from_addr)
from_lo = from_addr.lower()
for rule in mailinglist_routes():
domains = {item.lower() for item in rule.get("domains", [])}
if domain and domain in domains:
return rule["mailbox"]
for needle in rule.get("from_contains", []):
if needle.lower() in from_lo:
return rule["mailbox"]
return None
def sender_domain(from_addr: str) -> str:
match = re.search(r"@([\w.\-]+)", from_addr)
return match.group(1).lower() if match else ""
@@ -126,6 +155,8 @@ def route_from_subject(from_addr: str, subject: str) -> str | None:
subj_lo = subject.lower()
if any(keyword in subj_lo for keyword in FACTUUR_KEYWORDS):
return "__FACTUUR_DATE__"
if destination := mailinglist_destination(from_addr):
return destination
return DOMAIN_LOOKUP.get(sender_domain(from_addr))
@@ -137,6 +168,11 @@ def is_source_folder(folder: str) -> bool:
def destination_folders(start_year: int = 2020, end_year: int = 2026) -> list[str]:
"""Return all destination folders without the INBOX. prefix."""
folders = {target for _, target in DOMAIN_ROUTES}
folders.update(
rule["mailbox"]
for rule in mailinglist_routes()
if rule.get("mailbox")
)
folders.update(
f"Financieel.Facturen.{year}.Q{quarter}"
for year in range(start_year, end_year + 1)
+924
View File
@@ -0,0 +1,924 @@
[
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Coursera",
"domains": ["coursera.org", "email.coursera.org", "m.mail.coursera.org", "m.learn.coursera.org"],
"source_decision_keys": ["coursera.org", "email.coursera.org", "m.mail.coursera.org", "m.learn.coursera.org"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Storytelling with Data",
"domains": ["storytellingwithdata.com"],
"source_decision_keys": ["storytellingwithdata.com"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.MIT Technology Review",
"domains": ["technologyreview.com", "fulfillment.technologyreview.com"],
"source_decision_keys": ["technologyreview.com", "fulfillment.technologyreview.com"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Overig.Lendahand",
"domains": ["lendahand.com"],
"source_decision_keys": ["lendahand.com"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Eva Keiffenheim",
"domains": ["evakeiffenheim.com"],
"from_contains": ["evakeiffenheim@substack.com", "eva keiffenheim"],
"source_decision_keys": ["evakeiffenheim.com", "substack.com", "eva keiffenheim s lifelong learning club", "eva keiffenheim from the learn letter"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Overig.The Good Roll",
"domains": ["thegoodroll.nl"],
"source_decision_keys": ["thegoodroll.nl"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.Eneco eMobility",
"domains": ["eneco-emobility.com", "eneco.nl"],
"source_decision_keys": ["eneco-emobility.com", "eneco.nl"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Nick Milo",
"domains": ["linkingyourthinking.com"],
"source_decision_keys": ["linkingyourthinking.com"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.OV & Reizen.NS",
"domains": ["ns.nl", "email.ns.nl"],
"source_decision_keys": ["ns.nl", "email.ns.nl"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.OneStopSourcing",
"domains": ["onestopsourcing.nl"],
"source_decision_keys": ["onestopsourcing.nl"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.AI Report",
"domains": ["beehiiv.com", "mail.beehiiv.com"],
"source_decision_keys": ["beehiiv.com", "mail.beehiiv.com"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.Flextender",
"domains": ["flextender.nl"],
"source_decision_keys": ["flextender.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Verzekering.Unive",
"domains": ["unive.nl", "nieuwsbrief.unive.nl"],
"source_decision_keys": ["nieuwsbrief.unive.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Bank.ICS",
"domains": ["icscards.nl", "icsmarketing.icscards.nl", "service.icscards.nl"],
"source_decision_keys": ["icsmarketing.icscards.nl"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Nieuws & Vakbladen.Vakblad Crisismanager",
"domains": ["crisismanager.nl"],
"source_decision_keys": ["crisismanager.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.CogSci Apps",
"domains": ["cogsciapps.com"],
"source_decision_keys": ["cogsciapps.com"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Ben Tiggelaar",
"domains": ["tiggelaar.nl"],
"source_decision_keys": ["tiggelaar.nl"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.IMMENS",
"domains": ["immens.nu"],
"source_decision_keys": ["immens.nu"]
},
{
"action": "keep",
"mailbox": "Financieel.Bank.ABN AMRO",
"domains": ["nl.abnamro.com", "abnamro.nl"],
"source_decision_keys": ["nl.abnamro.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.PostNL",
"domains": ["postnl.nl", "notificatie.postnl.nl", "edm.postnl.nl"],
"source_decision_keys": ["postnl.nl", "notificatie.postnl.nl", "edm.postnl.nl"]
},
{
"action": "keep",
"mailbox": "Bestellingen.123inkt",
"domains": ["123inkt.nl", "m.123inkt.nl"],
"source_decision_keys": ["123inkt.nl", "m.123inkt.nl"]
},
{
"action": "keep",
"mailbox": "Bestellingen.DHL",
"domains": ["dhlparcel.nl", "proxy.privault.rpip.nl"],
"source_decision_keys": ["dhlparcel.nl", "proxy.privault.rpip.nl"]
},
{
"action": "keep",
"mailbox": "Bestellingen.GLS",
"domains": ["gls-netherlands.com"],
"source_decision_keys": ["gls-netherlands.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Hosting.STRATO",
"domains": ["strato.com", "news.strato.com", "marketingradar.strato.com"],
"source_decision_keys": ["strato.com", "news.strato.com", "marketingradar.strato.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Telecom.Vodafone",
"domains": ["zakelijk.vodafone.nl"],
"source_decision_keys": ["zakelijk.vodafone.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Formspree",
"domains": ["formspree.io"],
"source_decision_keys": ["formspree.io"]
},
{
"action": "keep",
"mailbox": "Diensten.Telecom.KPN",
"domains": ["campagnes.kpn.com", "kpn.com"],
"source_decision_keys": ["campagnes.kpn.com", "kpn.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Hosting.Lets Encrypt",
"domains": ["letsencrypt.org"],
"source_decision_keys": ["letsencrypt.org"]
},
{
"action": "keep",
"mailbox": "Diensten.Backup.Backblaze",
"domains": ["backblaze.com"],
"source_decision_keys": ["backblaze.com"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Brian Keating",
"domains": ["briankeating.com"],
"source_decision_keys": ["briankeating.com"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.Get There",
"domains": ["getthere.nl"],
"source_decision_keys": ["getthere.nl"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.Jamie",
"domains": ["hey.meetjamie.ai"],
"source_decision_keys": ["hey.meetjamie.ai"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.Huckr",
"domains": ["huckr.ai"],
"source_decision_keys": ["huckr.ai"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.MIT Open Learning",
"domains": ["mit.edu"],
"source_decision_keys": ["mit.edu"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Allekabels",
"domains": ["allekabels.nl"],
"source_decision_keys": ["allekabels.nl"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.OV & Reizen.Booking",
"domains": ["booking.com"],
"source_decision_keys": ["booking.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Sophos",
"domains": ["cleverbridge.com", "home.sophos.com"],
"source_decision_keys": ["cleverbridge.com", "home.sophos.com"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.FastNed",
"domains": ["fastned.nl"],
"source_decision_keys": ["fastned.nl"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.OPnGO",
"domains": ["opngo.com"],
"source_decision_keys": ["opngo.com"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.Tesla",
"domains": ["tesla.com"],
"source_decision_keys": ["tesla.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Cursor",
"domains": ["cursor.com"],
"source_decision_keys": ["cursor.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.1Password",
"domains": ["1password.com"],
"source_decision_keys": ["1password.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Viking",
"domains": ["delivery.vikingdirect.nl", "online.vikingdirect.nl", "vikingdirect.nl"],
"source_decision_keys": ["delivery.vikingdirect.nl", "online.vikingdirect.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Matter",
"domains": ["getmatter.com"],
"source_decision_keys": ["getmatter.com"]
},
{
"action": "keep",
"mailbox": "Werk.Overig.Gemeente Groningen",
"domains": ["groningen.nl", "groningenbereikbaar.nl"],
"source_decision_keys": ["groningen.nl", "groningenbereikbaar.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Verzekering.VEZA",
"domains": ["heinenoord.nl"],
"source_decision_keys": ["heinenoord.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Kahoot",
"domains": ["team.kahoot.com"],
"source_decision_keys": ["team.kahoot.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Hosting.TransIP",
"domains": ["transip.nl"],
"source_decision_keys": ["transip.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Indigo Neo",
"domains": ["indigoneo.com"],
"source_decision_keys": ["indigoneo.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Office Centre",
"domains": ["officecentre.nl"],
"source_decision_keys": ["officecentre.nl"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.Shell Recharge",
"domains": ["shellrecharge.com"],
"source_decision_keys": ["shellrecharge.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Smartphonehoesjes",
"domains": ["smartphonehoesjes.nl"],
"source_decision_keys": ["smartphonehoesjes.nl"]
},
{
"action": "keep",
"mailbox": "Werk.Overig.BOIP",
"domains": ["boip.int"],
"source_decision_keys": ["boip.int"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Leren.Elizabeth Butler",
"domains": ["elizabethbutlermd.com"],
"source_decision_keys": ["elizabethbutlermd.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.bol",
"domains": ["bol.com", "feedback.bol.com"],
"source_decision_keys": ["bol.com", "feedback.bol.com"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.OV & Reizen.Flitsmeister",
"domains": ["flitsmeister.nl"],
"source_decision_keys": ["flitsmeister.nl"]
},
{
"action": "keep",
"mailbox": "Werk.Opdrachten.FutureXL",
"domains": ["futurexl.nl"],
"source_decision_keys": ["futurexl.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Bank.NPEX",
"domains": ["npex.nl"],
"source_decision_keys": ["npex.nl"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Nieuws & Vakbladen.The Presentation Guru",
"domains": ["presentation-guru.com"],
"source_decision_keys": ["presentation-guru.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Sligro",
"domains": ["sligro.nl"],
"source_decision_keys": ["sligro.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Bank.SnelStart",
"domains": ["snelstart.nl"],
"source_decision_keys": ["snelstart.nl"]
},
{
"action": "keep",
"mailbox": "Financieel.Bank.Tellow",
"domains": ["tellow.nl"],
"source_decision_keys": ["tellow.nl"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Coolblue",
"domains": ["coolblue.nl", "coolblue.eu"],
"source_decision_keys": ["coolblue.nl", "coolblue.eu"]
},
{
"action": "keep",
"mailbox": "Nieuwsbrieven.Nieuws & Vakbladen.Computable",
"domains": ["jaarbeurs.nl"],
"source_decision_keys": ["jaarbeurs.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Microsoft Teams",
"domains": ["microsoft365.com"],
"source_decision_keys": ["microsoft365.com"]
},
{
"action": "keep",
"mailbox": "Mobiliteit.EV.Mitsubishi",
"domains": ["mmsn.nl"],
"source_decision_keys": ["mmsn.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Claude",
"domains": ["claude.com"],
"source_decision_keys": ["claude.com"]
},
{
"action": "keep",
"mailbox": "Bestellingen.Makro",
"domains": ["makro.nl"],
"source_decision_keys": ["makro.nl"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.MindNode",
"domains": ["mindnode.com"],
"source_decision_keys": ["mindnode.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Netwerk.Netherfind",
"domains": ["netherfind.com"],
"source_decision_keys": ["netherfind.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Papers",
"domains": ["papersapp.com"],
"source_decision_keys": ["papersapp.com"]
},
{
"action": "keep",
"mailbox": "Diensten.Software.Perplexity",
"domains": ["perplexity.ai"],
"source_decision_keys": ["perplexity.ai"]
},
{
"action": "keep",
"mailbox": "Diensten.Email.Proton",
"domains": ["mail.proton.me"],
"source_decision_keys": ["mail.proton.me"],
"notes": "Zero-count keep alias from decisions.json; proton.me and notify.proton.me remain Afmelden entries."
},
{
"action": "afmelden",
"mailbox": "Afmelden.Netwerken.ResearchGate",
"domains": ["researchgatemail.net"],
"source_decision_keys": ["researchgatemail.net"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Fitness.STARS Personal Training",
"domains": ["virtuagym.com"],
"source_decision_keys": ["virtuagym.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Netwerken.LinkedIn",
"domains": ["linkedin.com"],
"source_decision_keys": ["linkedin.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Nieuws.Het Financieele Dagblad",
"domains": ["messagent.fdmediagroep.nl"],
"source_decision_keys": ["messagent.fdmediagroep.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Werk.FixedToday",
"domains": ["fixedtoday.nl"],
"source_decision_keys": ["fixedtoday.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Werk.Centre of Excellence Data Sharing Cloud",
"domains": ["coe-dsc.nl"],
"source_decision_keys": ["coe-dsc.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Netwerken.CIONET",
"domains": ["mn.co", "cionet.com"],
"source_decision_keys": ["mn.co", "cionet.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.Gusti",
"domains": ["gusti-leer.nl", "gusti-leder.de", "gusti-pelle.it"],
"source_decision_keys": ["gusti-leer.nl", "gusti-leder.de", "gusti-pelle.it"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.Forte Labs",
"domains": ["fortelabs.co", "fortelabs.com"],
"source_decision_keys": ["fortelabs.co", "fortelabs.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Telecom.Vonage",
"domains": ["vonage.com", "insights.vonage.com", "nexmo.com"],
"source_decision_keys": ["vonage.com", "insights.vonage.com", "nexmo.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Beveiliging.Nord",
"domains": ["nordvpn.com", "nordpass.com"],
"source_decision_keys": ["nordvpn.com", "nordpass.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Bestellingen.DHL eCommerce",
"domains": ["dhlecommerce.nl"],
"source_decision_keys": ["dhlecommerce.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.Taproot",
"domains": ["taproot.com"],
"source_decision_keys": ["taproot.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Productiviteit.Readwise",
"domains": ["readwise.io"],
"source_decision_keys": ["readwise.io"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.Scrimba",
"domains": ["scrimba.com"],
"source_decision_keys": ["scrimba.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Kilo Code",
"domains": ["kilocode.ai"],
"source_decision_keys": ["kilocode.ai"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Werk.Jooble",
"domains": ["nl.jooble.org"],
"source_decision_keys": ["nl.jooble.org"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Werk.Pleio IBDS",
"domains": ["pleio.nl", "mail.pleio.nl"],
"source_decision_keys": ["pleio.nl", "mail.pleio.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.FUN MOOC",
"domains": ["fun-mooc.fr"],
"source_decision_keys": ["fun-mooc.fr"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Stackfield",
"domains": ["stackfield.help"],
"source_decision_keys": ["stackfield.help"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Werk.De Staffing Groep",
"domains": ["destaffinggroep.nl"],
"source_decision_keys": ["destaffinggroep.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.Kaggle",
"domains": ["kaggle.com"],
"source_decision_keys": ["kaggle.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Diensten.Proton",
"domains": ["proton.me", "notify.proton.me"],
"source_decision_keys": ["proton.me", "notify.proton.me"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Genially",
"domains": ["genially.com"],
"source_decision_keys": ["genially.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Backup.IDrive",
"domains": ["idrive.com"],
"source_decision_keys": ["idrive.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Projectmanagement.PMRGI",
"domains": ["oppmi.com"],
"source_decision_keys": ["oppmi.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Energie.Zelf Energie Produceren",
"domains": ["zelfenergieproduceren.nl"],
"source_decision_keys": ["zelfenergieproduceren.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Nuclino",
"domains": ["nuclino.com"],
"source_decision_keys": ["nuclino.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Apparaten.reMarkable",
"domains": ["remarkable.com"],
"source_decision_keys": ["remarkable.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Olares",
"domains": ["olares.com"],
"source_decision_keys": ["olares.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Schrijven.Schrijf.be",
"domains": ["schrijf.be"],
"source_decision_keys": ["schrijf.be"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.VMware",
"domains": ["connect.vmware.com"],
"source_decision_keys": ["connect.vmware.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Audiogest",
"domains": ["loops.audiogest.app"],
"source_decision_keys": ["loops.audiogest.app"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.SmallCubed",
"domains": ["smallcubed.com"],
"source_decision_keys": ["smallcubed.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Energie.Energie Collectief Nederland",
"domains": ["titanomon.com"],
"source_decision_keys": ["titanomon.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Telecom.Vodafone",
"domains": ["vodafone.nl"],
"source_decision_keys": ["vodafone.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Mobiliteit.Fulli",
"domains": ["fulli.com"],
"source_decision_keys": ["fulli.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Advertenties.Google Ads",
"domains": ["google.com"],
"source_decision_keys": ["google.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Saga",
"domains": ["saga.so"],
"source_decision_keys": ["saga.so"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Financieel.Triodos",
"domains": ["triodos.nl"],
"source_decision_keys": ["triodos.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.TurboScribe",
"domains": ["turboscribe.ai"],
"source_decision_keys": ["turboscribe.ai"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Financieel.bunq",
"domains": ["bunq.com"],
"source_decision_keys": ["bunq.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.Airo Paradise",
"domains": ["ekwb.nl"],
"source_decision_keys": ["ekwb.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Parallels",
"domains": ["parallels-universe.com"],
"source_decision_keys": ["parallels-universe.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.OReilly",
"domains": ["et.oreilly.com"],
"source_decision_keys": ["et.oreilly.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Visme",
"domains": ["accounts.visme.com", "visme.co"],
"source_decision_keys": ["accounts.visme.com", "visme.co"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.De Vulpenwereld",
"domains": ["devulpenwereld.nl"],
"source_decision_keys": ["devulpenwereld.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Privacy.Incogni",
"domains": ["incogni.com"],
"source_decision_keys": ["incogni.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Smartsheet",
"domains": ["smartsheet.com"],
"source_decision_keys": ["smartsheet.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Netwerken.YourConnector",
"domains": ["yourconnector.nl"],
"source_decision_keys": ["yourconnector.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Zapier",
"domains": ["zapier.com"],
"source_decision_keys": ["zapier.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.ClickUp",
"domains": ["clickup.com"],
"source_decision_keys": ["clickup.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Presenteren.Duarte",
"domains": ["duarte.com"],
"source_decision_keys": ["duarte.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.PrintAbout",
"domains": ["printabout.nl"],
"source_decision_keys": ["printabout.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Cloud.Microsoft Azure",
"domains": ["promomail.microsoft.com"],
"source_decision_keys": ["promomail.microsoft.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Productiviteit.Tana",
"domains": ["tana.inc"],
"source_decision_keys": ["tana.inc"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.TIAS",
"domains": ["tias.edu"],
"source_decision_keys": ["tias.edu"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Media.VALUE IPTV VOD",
"domains": ["weblinux.de"],
"source_decision_keys": ["weblinux.de"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Productiviteit.Capacities",
"domains": ["capacities.io"],
"source_decision_keys": ["capacities.io"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Nieuws.Computable Enigma Research",
"domains": ["enigmaresearch.nl"],
"source_decision_keys": ["enigmaresearch.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.Euroflorist",
"domains": ["euroflorist.nl"],
"source_decision_keys": ["euroflorist.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Financieel.IEX Cloud",
"domains": ["iexcloud.io"],
"source_decision_keys": ["iexcloud.io"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Diensten.Jetpack",
"domains": ["jetpack.com"],
"source_decision_keys": ["jetpack.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.Linearity",
"domains": ["linearity.io"],
"source_decision_keys": ["linearity.io"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.LiquidText",
"domains": ["liquidtext.net"],
"source_decision_keys": ["liquidtext.net"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Software.MURAL",
"domains": ["mural.co"],
"source_decision_keys": ["mural.co"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Winkels.Office Noord",
"domains": ["officenoord.nl"],
"source_decision_keys": ["officenoord.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Presenteren.Prezent",
"domains": ["prezent.ai"],
"source_decision_keys": ["prezent.ai"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Projectmanagement.Wrike",
"domains": ["team.wrike.com"],
"source_decision_keys": ["team.wrike.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Mobiliteit.Alfen",
"domains": ["alfen.com"],
"source_decision_keys": ["alfen.com"],
"notes": "Conflicts with old EV keep route; decisions.json takes precedence."
},
{
"action": "afmelden",
"mailbox": "Afmelden.Productiviteit.GRID",
"domains": ["gridnewsletter.com"],
"source_decision_keys": ["gridnewsletter.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Legal.Henchman",
"domains": ["henchman.io"],
"source_decision_keys": ["henchman.io"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Presenteren.Pitch",
"domains": ["hi.pitch.com"],
"source_decision_keys": ["hi.pitch.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Wonen.How To Buy A Home",
"domains": ["howtobuyahome.com"],
"source_decision_keys": ["howtobuyahome.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Leren.Kodeco",
"domains": ["kodeco.com"],
"source_decision_keys": ["kodeco.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Nieuws.Medium",
"domains": ["medium.com"],
"source_decision_keys": ["medium.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Productiviteit.MindMeister",
"domains": ["mindmeister.com"],
"source_decision_keys": ["mindmeister.com"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Fitness.NEXT Personal Gym",
"domains": ["next-sports.nl"],
"source_decision_keys": ["next-sports.nl"]
},
{
"action": "afmelden",
"mailbox": "Afmelden.Projectmanagement.TeamGantt",
"domains": ["teamgantt.com"],
"source_decision_keys": ["teamgantt.com"]
},
{
"action": "review",
"mailbox": "Werk.Opdrachten.Circle8",
"domains": ["circle8.nl"],
"source_decision_keys": ["circle8.nl"],
"notes": "Decision value is s; keep as review until the user confirms the meaning."
}
]
@@ -14,7 +14,9 @@ Purpose: interpret `decisions.json` as the user intent for mailing-list handling
- `a` ("afmelden"): 89 entries, 2,683 messages.
- `s`: 1 entry, 26 messages.
The current sorter does not read `decisions.json`. It only reads the hardcoded routes in `mail_routes.py`. Therefore these decisions are currently advisory data, not active routing behavior.
The sorter still does not read `decisions.json` directly. The decisions have been normalized into `mailinglist_routes.json`, which is now loaded by `mail_routes.py` for routing and destination-folder generation.
The normalized policy contains 153 grouped rules and covers every key in `decisions.json` through `source_decision_keys`.
## Interpretation
@@ -247,21 +249,21 @@ These decisions conflict with the current hardcoded route table or expose incomp
## Plan To Add This Structure
1. Introduce a structured policy file generated from `decisions.json`.
1. Introduce a structured policy file generated from `decisions.json`. Done.
- Suggested file: `mailinglist_routes.json`.
- Fields: `action`, `mailbox`, `domains`, `sender_patterns`, `list_id_patterns`, `source_decision_keys`, `notes`.
- Fields: `action`, `mailbox`, `domains`, `from_contains`, `source_decision_keys`, `notes`.
- Do not make `decisions.json` itself the runtime file; it contains unsubscribe URLs and analysis artifacts.
2. Extend `mail_routes.py` to load mailing-list policy.
2. Extend `mail_routes.py` to load mailing-list policy. Done for domain and `From` substring matching.
- Keep `DOMAIN_ROUTES` for stable functional/business mail.
- Add a runtime lookup for generated mailing-list rules.
- Support domain matching, exact sender address matching, display-name matching, and List-ID/header matching.
3. Add `Afmelden` to destination folder creation.
3. Add `Afmelden` to destination folder creation. Done through `destination_folders()`.
- `maak_mappen.py` should create all `INBOX.Afmelden.*` folders from the generated policy.
- Existing `destination_folders()` should include both functional keep folders and unsubscribe review folders.
4. Update routing precedence.
4. Update routing precedence. Done for invoice, mailing-list policy, then legacy domain fallback.
- Invoice keyword routing should remain first.
- Exact/list-specific mailing-list rules should run before broad domain rules, especially for shared senders such as `substack.com`.
- Domain rules should remain the fallback for normal service/vendor mail.
@@ -280,4 +282,3 @@ These decisions conflict with the current hardcoded route table or expose incomp
- Run dry-run sorter on backup.
- Review planned moves.
- Run actual sorter only after explicit confirmation.
+8 -1
View File
@@ -161,7 +161,11 @@ That report interprets `decisions.json` as advisory intent:
- `keuze: "a"` means route below `INBOX.Afmelden.*` for unsubscribe/review.
- `keuze: "s"` is ambiguous and should be confirmed before automation.
- Obvious aliases from the same sender or organization should be combined, including Coursera, MIT Technology Review, Eva Keiffenheim/Substack, PostNL, STRATO, Forte Labs, CIONET, Gusti, Nord, Proton, Vonage/Nexmo, and Visme.
- The report is not active code yet; `verplaats_bestaand.py` still routes through `mail_routes.py`.
- The normalized policy is now in `mailinglist_routes.json`; `mail_routes.py` loads it before falling back to legacy `DOMAIN_ROUTES`.
- `mailinglist_routes.json` has 153 grouped rules and covers every key from `decisions.json` through `source_decision_keys`.
- `destination_folders()` now includes functional keep folders plus `Afmelden.*` folders from `mailinglist_routes.json`.
- Invoice keyword routing still takes precedence over mailing-list routing.
- Do not run mailbox-affecting scripts yourself in this project. The user wants to run scripts personally when instructed.
Key earlier findings:
@@ -174,6 +178,9 @@ Known remaining risk:
- `kopieer_naar_backup.py` should be hardened so append failures cannot be silently marked as folder completion.
- `verplaats_bestaand.py` should log failures with source folder, UID, destination, and server response.
- Mailing-list routing currently supports domain and `From` substring policy matching. It does not yet fetch or match `List-ID` headers.
- `circle8.nl` still has ambiguous decision value `s`; keep it as review/functional routing until the user confirms the meaning.
- `alfen.com`, `vodafone.nl`, `dhlecommerce.nl`, and Proton splits are implemented according to `decisions.json` precedence, but remain notable policy changes compared with older hardcoded domain routes.
## What To Do Next