On Sat, May 17, 2025 at 04:39:07PM +0200, Sébastien Hinderer wrote: > Hello Chris, thanks a lot for your feedback! > > Chris Green (2025/05/17 14:39 +0100): > > On Sat, May 17, 2025 at 12:49:16PM +0200, Sébastien Hinderer wrote: > > > For instance here is the configuration I have for this list: > > > > > > subscribe mutt-users@mutt.org > > > fcc-hook '~t mutt-users@mutt.org|~c mutt-users@mutt.org' > > > '=list/mutt-users/' > > > save-hook '~t mutt-users@mutt.org|~c mutt-users@mutt.org' > > > '=list/mutt-users/' > > > > > > It's definitely not unbearable, but each time I add a mailing list and > > > duplicate such lines I feel a bit sad about the redundancy, to the point > > > that I regularly find myself thinking that one day I will write a > > > generator to generate my Mutt configuration from a less verbose format. > > > > > Yes. What I do is have a configuration file in which I have all my > > lists and this is used to autogenerate the required subscribe and > > other mutt configuration. > > Yes it's what I said I intended to do. You may want to share you > rscript! > I think I have done before but see attached files anyway.
The mutt configuration works using two very simple Python scripts as follows (I've attached these two scripts as well) :- # # # Mailing lists, see ~/.mutt/filter and ~/mutt/bin/filter.py for details. # lists `~/.mutt/bin/getLists.py` subscribe `~/.mutt/bin/getLists.py` # # # getAliases.py gets aliases for mailing lists from the filter file # source ~/.mutt/bin/getAliases.py| Here's a section of the file 'filter' which is what I change to add or remove mailing lists (and some other things):- letsencrypt Li list-id help.community.letsencrypt.org mercurial Li list-post mercur...@mercurial-scm.org mercurial Li list-post mercur...@lists.mercurial-scm.org mopidy Li list-id q&a.discourse.mopidy.com mutt Li list-post mutt-users@mutt.org netman Li list-post networkmana...@lists.freedesktop.org owfs Li list-post owfs-develop...@lists.sourceforge.net Owfs-developers openLibrary Li list-post ol-disc...@archive.org photini Li list-post phot...@googlegroups.com postfix Li list-post postfix-us...@postfix.org pfx quodlibet Li list-post quod-libet-developm...@googlegroups.com > > The same file is used by a Python script > > that routes incoming list mail to separate mailboxes. > > Which is the part I would not keep, to save me from not reading most of > my e-mails ahah. :) But the script could also generate procmail rules... > My Python script does what procmail does, no need for procmail as well. > Best wishes, > > Seb. > -- Chris Green
#!/usr/bin/python3 # # # license Apache v2 (http://www.apache.org/licenses/LICENSE-2.0) # author Chris Green - ch...@isbd.co.uk # # # # Mail filtering script # import mailbox import os import sys import time import mailLib import shlex # # # Redirect any exceptions to a file # sys.stderr = open("/home/chris/tmp/mail.err", 'a') # # # Some constants (i.e. configuration) # home = "/home/chris" logfile = home + "/tmp/mail.log" filtfile = home + "/.mutt/filter" mldir = home + "/mail/" indir = mldir + "In/" # # # Set to log to mail.log in ~/tmp with name 'filter' and the envelope/from # log = mailLib.initLog("filter") # # # Initialise destination mailbox name to its default "In/default" # dest = indir + "default" # # # Read the message from standard input and make a message object from it # msg = mailbox.MaildirMessage(sys.stdin.buffer.read()) # # # See if there's a match in our filter file # f = open(filtfile, 'r') for ln in f: # for each line in filter if ln[0] == '#': # ignore comments continue # # # split the line into fields, shlex.split() does quoted strings # fld = shlex.split(ln) # # # copy the fields into better named variables # nm = fld[0] # name/alias destdir = fld[1] + "/" # the destination directory header = fld[2] # the header to find the match in address = fld[3].lower() # list address to match if len(fld) == 5: # if there's a 5th field sbstrip = '[' + fld[4] + ']' # string to strip out of subject dbasbstrip = '(' + fld[4] + ')' # string to strip from DBA subject # # # Does the address in the header match this entry # if (address in str(msg.get(header, "unknown").lower())): # # # set the destination directory # if nm[0:3] == "dba": # # # For some reason when the Subject: has accented characters in it the returned # utf-8 string has spaces replaced by underscores, so we need to change them back # subject = msg.get("subject", "unknown").replace("_", " ") # # # For DBA messages strip out the (<forum section>) and put the # destination in From: and change To: so we can get the DBA alias when replying # if len(fld) == 5 and dbasbstrip in subject: msg.replace_header("Subject", str(subject.replace(dbasbstrip, ''))) dest = mldir + destdir + nm msg.replace_header("From", dest) msg.replace_header("To", "barges.org") break # match found so stop searching else: dest = mldir + destdir + nm # # # Strip out list name (4th field) from subject if it's there # if len(fld) == 5 and sbstrip in str(msg.get("subject", "unknown")): msg.replace_header("Subject", str(msg.get("subject").replace(sbstrip, ''))) break # match found so stop searching # # # match not found so continue the for loop # # # # deliver the message # mailLib.spamFilter(msg) mailLib.smsFilter(msg) mailLib.deliverMdMsg(dest, msg, log)
#!/usr/bin/python3 # # # license Apache v2 (http://www.apache.org/licenses/LICENSE-2.0) # author Chris Green - ch...@isbd.co.uk # # # # Get mailing list addresses from filter file to provide mutt aliases # import sys # # # Set directories and filenames # home = "/home/chris" filtfile = home + "/.mutt/filter" # # # Read from 'filter' line by line # f = open(filtfile, 'r') for ln in f: if ln[0] == '#': # ignore comments continue # # # split the line into fields # fld = ln.split() # # # Only write aliases for mailing lists # if ((fld[1] == "Li") or (fld[1] == "Gm")): sys.stdout.write("alias ") sys.stdout.write(fld[0] + " ") # # # List-Id matches need the list name converted to an E-Mail address # if (fld[2].lower() == "list-id"): sys.stdout.write(fld[3].replace(".", "@", 1) + "\n") else: sys.stdout.write(fld[3] + "\n")
#!/usr/bin/python3 # # # license Apache v2 (http://www.apache.org/licenses/LICENSE-2.0) # author Chris Green - ch...@isbd.co.uk # # # # Get mailing lists from filter for mutt 'lists' command # import sys # # # Set directory and filename # home = "/home/chris" filtfile = home + "/.mutt/filter" # # # Read filter line by line # f = open(filtfile, 'r') for ln in f: if ln[0] == '#': # ignore comments continue # # # split the line into fields # fld = ln.split() # # # output the address if 'Li' or 'Gm' in second column # if ((fld[1] == "Li") or (fld[1] == "Gm")): # # # List-Id matches need the list name converted to an E-Mail address # if (fld[2].lower() == "list-id"): sys.stdout.write(fld[3].replace(".", "@", 1) + " ") else: sys.stdout.write(fld[3] + " ")
# # # license Apache v2 (http://www.apache.org/licenses/LICENSE-2.0) # author Chris Green - ch...@isbd.co.uk # import mailbox import logging import logging.handlers import os import time from email.header import decode_header from datetime import datetime # # # log a message # def initLog(name): log = logging.getLogger(name) log.setLevel(logging.DEBUG) f = logging.handlers.RotatingFileHandler("/home/chris/tmp/mail.log", 'a', 1000000, 4) f.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') f.setFormatter(formatter) log.addHandler(f) return log # # # Deliver a message to a local maildir # def deliverMdMsg(dest, msg, log): # # # Create the destination maildir instance # md = mailbox.Maildir(dest, factory=None) log.info("\n From: " + str(msg.get("From", "unknown")) + "\n To:" + dest + "\n") # # # Put the incoming message in the appropriate maildir # No need to lock, it's a maildir # try: md.add(msg) except Exception as e: log.info("Failed to store message:" + str(e)) return # # # Get a message header as a string # def getHdr(msg, header): hdr = str(msg.get(header, "empty")) res = decode_header(hdr)[0][0] return str("\n " + header + ": " + str(res)) # # # Spam filter, initially just for junk in BBB list # def spamFilter(msg): if "@gmail.com" in msg.get("from", "unknown"): if msg.get("to", "unknown") == "BeagleBone <beagleb...@googlegroups.com>": dt = datetime.now().isoformat(timespec='minutes') f = open("/home/chris/tmp/spam.log") f.write(dt + msg.get("subject") + " from " + msg.get("from")) f.close() # # # Change the From: address on SMS->E-Mail messages from my AandA VOIP # def smsFilter(msg): if "s...@aa.net.uk" in msg.get("from", "unknown"): if "+447906626760" in msg.get("from", "unknown"): msg.replace_header("From", "SMS from Maxine <max...@isbd.co.uk>"); elif "+447906226675" in msg.get("from", "unknown"): msg.replace_header("From", "SMS from Chris <ch...@isbd.co.uk>"); elif "+447537170394" in msg.get("from", "unknown"): msg.replace_header("From", "SMS from Chris <ch...@isbd.co.uk>"); elif "+447780705062" in msg.get("from", "unknown"): msg.replace_header("From", "SMS from Zelma <ze...@isbd.co.uk>");