commit: 7ec3fe97bbbf2e15825f2288526ed1da8915304e
parent: bee708b82f61322eacc72052e2f8ac0861015bef
author: Chris Noxz <chris@noxz.tech>
date: Sat, 11 Jun 2022 20:20:35 +0200
add new article about mail filters
4 files changed, 140 insertions(+)
diff --git a/noxz.tech/articles/index.www b/noxz.tech/articles/index.www
@@ -4,6 +4,13 @@ Articles
.DIVS articles
.ULS
+.LI
+.URL "simple_way_to_filter_email" "Simple way to filter email"
+posted on
+.B "2022-06-11"
+by
+.B "Chris Noxz"
+
.LI
.URL "askpass_implementation_for_doas" "Askpass implementation for doas"
posted on
diff --git a/noxz.tech/articles/simple_way_to_filter_email/.assemble b/noxz.tech/articles/simple_way_to_filter_email/.assemble
@@ -0,0 +1 @@
+index.html
diff --git a/noxz.tech/articles/simple_way_to_filter_email/.buildignore b/noxz.tech/articles/simple_way_to_filter_email/.buildignore
diff --git a/noxz.tech/articles/simple_way_to_filter_email/index.www b/noxz.tech/articles/simple_way_to_filter_email/index.www
@@ -0,0 +1,132 @@
+.HnS 0
+Simple way to filter email
+.HnE
+
+posted on
+.B "2022-06-11"
+by
+.B "Chris Noxz"
+
+Having a lot of different types of email had bugged me for some time, so I
+sought to find a solution to this problem. The logical way of solving this would
+be to somehow filter email automatically into directories based on the type of
+email, i.e., sender, list id, subject, etc.
+
+I looked into various already available solutions like procmail, notmuch, and
+alike. The problem with these ways of solving the issue was that they were
+overly complicated for the problem I needed to solve.
+
+So, I did what I usually do and implemented my own solution instead. As I only
+wanted to filter email, both new and old ones, by various header data and their
+age, I only needed to use
+.ICD awk ,
+.ICD grep ,
+.ICD find ,
+and
+.ICD mv .
+To keep it somewhat structured, I wrote everything as a POSIX compliant script,
+which I programmed to accept a configuration file. As I’m using maildir to store
+my email I made sure the script moved each email based on the directory it
+resides in \(en
+.I new ,
+.I cur ,
+or
+.I tmp .
+
+This is the script I came up with (feel free to use it as you like):
+
+.CDS
+.COS
+#!/bin/sh
+
+# Copyright © 2022 Chris Noxz <chris@noxz.tech>
+# This work is free. You can redistribute it and/or modify it under the
+# terms of the Do What The Fuck You Want To Public License, Version 2,
+# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
+
+# declare default configuration variables
+MAILDIR="${MAILDIR:-"${HOME}/mail"}"
+MAILFILTERRC="${MAILFILTERRC:-"${HOME}/.config/mfrc"}"
+
+# This script is used for applying sorting/filtering rules to maildirs
+# Use the following format for rules:
+# src|pat|dst
+# src: source directory relative to MAILDIR eg. INBOX or me@domain.com/INBOX
+# pat: regex pattern applied to mail header eg. ^From:.*me@domain.com
+# dst: destination directory relative to MAILDIR eg. Archive or My/Mail
+# example rule: INBOX|^From:.*github.com|github
+# this moves mail from INBOX to github retrieved from the domain github.com
+#
+# age based patterns can be used for archiving old emails ($AGE+<days>).
+# example rule: INBOX|$AGE+365|Archive
+# this archives mail from INBOX to Archive that are 365 days or older
+#
+# rules are read from ~/.config/mfrc by default
+
+# extract rules from rule file based on pattern "src|pat|dst"
+grep -o "^[^#][^|]\\+|[^|]\\+|[^|]\\+$" "${MAILFILTERRC}" | while read -r rule; do
+ # extract source directory
+ src="${MAILDIR}/${rule%%|*}"
+
+ # extract destination directory
+ dst="${MAILDIR}/${rule##*|}"
+
+ # extract pattern
+ ptn="${rule#*|}"
+ ptn="${ptn%%|*}"
+
+ # validate input (at least somewhat)
+ [ ! -d "${src}" ] && continue # skip if source doesn't exist
+ [ ! -d "${dst}" ] && continue # skip if destination doesn't exist
+ [ -z "${ptn}" ] && continue # skip if rule is empty
+
+ # check if age based rule: ^$AGE+...
+ if echo "${ptn}" | grep "^\\$AGE+" >/dev/null; then
+ age="${ptn##*+}"
+ if [ -n "$age" ] && [ "$age" -eq "$age" 2>/dev/null ]; then
+ # find file/mail based on age and move file/mail into correct
+ # subdirectory at destination
+ find "${src}"/* -maxdepth 1 -mtime +"${age}" -type f -exec \\
+ sh -c "f=\\"{}\\"; mv \\"\\${f}\\" \\"${dst}/\\$(dirname \\"\\${f}\\" | xargs basename)/\\"" \\;
+ fi
+ continue # done with this rule, continue with the next one
+ fi
+
+ # find pattern in file/mail until first empty line (only check header)...
+ awk "/^$/{nextfile} /${ptn}/{print FILENAME; nextfile}" "${src}"/*/* |\\
+ while read -r f; do
+ # ...and move file/mail into correct subdirectory at destination
+ mv "$f" "${dst}/$(dirname "${f}" | xargs basename)/"
+ done
+done
+.COE
+.CDE
+
+Rules created in the
+.ICD mfrc
+file can look like this:
+
+.CDS
+.COS
+# === chris@noxz.tech ===
+# =======================
+
+# move messages from github.com
+chris@noxz.tech/INBOX|^From:.*github.com|chris@noxz.tech/github
+
+# move messages from list: wiki.suckless.org
+chris@noxz.tech/INBOX|^List-Id:.*wiki.suckless.org|chris@noxz.tech/suckless-mailing-list/wiki
+
+# move 10 day old messages from INBOX to Archive
+chris@noxz.tech/INBOX|$AGE+10|chris@noxz.tech/Archive
+.COE
+.CDE
+
+I can now run the script either on demand or automatically each time I run
+.ICD mbsync
+to retrieve new email. As it turned out, it was easier to create my own script
+instead of using an existing way of solving my problem. This solution, however,
+is not the most efficient because every email is examined each time the script
+runs, rather than just the new ones. This could, of course, be fixed, but would
+mean I would have to create a new function for executing the filter on every
+email as well.