mail filter in python?
Bill Janssen
janssen at parc.xerox.com
Mon Feb 7 20:47:01 EST 2000
I use a Python filter heavily with MH. I first run regular MH "inc
+inbox" to pick up the mail from the spool file, then read a sorting
routine from ~/.mailfilter.py, which is called on each message, with
the message itself as an object parameter. The object type of the message
is a subclass of mhlib.Message, which adds the methods
matches(NAMES, HEADERNAMES) -- see if any of the specified NAMES occur
in any of the headers with the specified HEADERNAMES
addr_matches(ADDRESS, HEADERNAME) -- returns True if ADDRESS occurs in
any of the instances of the header with HEADERNAME
sentto (NAMES) -- returns True if the message was sent to any of the
specified NAMES (which are just strings). It looks at "To", "Cc",
"Resent-To", and "Apparently-TO".
copy (FOLDERNAME, UNSEEN=1) -- copies the message into the folder
specified with FOLDERNAME. Adds to the `unseen' sequence if UNSEEN
is True.
refile (FOLDERNAME, UNSEEN=1) -- similar to `copy', but deletes the
message from the original folder.
resend (TO) -- resends the message to the list of addresses in the TO
parameter
discard () -- deletes the message from its folder
The filter itself looks like
------------------------------------------------------------
# This file contains the rule which sorts my incoming mail when invoked by the "mhfile" command
#
# This file must define the procedure "mailfilterfn()". It is eval'ed by the mhfile
# module upon load of mhfile.py. It can be re'evaled by calling mhfile.read_filter_fn().
import mhfile, rfc822
def mailfilterfn (msg):
if (msg.matches("<janssen>", "from")):
msg.refile("overhead/outgoing")
return
...
if (msg.sentto("python-list at cwi.nl", "python-list at python.org") or
msg.matches("comp.lang.python", "newsgroups")):
msg.refile("ext/python")
elif (msg.sentto("db-sig at python.org")):
msg.refile("ext/python/db-sig")
...
##################################################
##
## Put any leftover messages in "mail"
##
else:
if not filed_in_my_direct:
msg.refile("mail")
else:
msg.discard()
return
------------------------------------------------------------
and so forth. Here's the code snippet from mhfile.py:
------------------------------------------------------------
if (not read_filter_fn_if_necessary()):
sys.stderr.write("Can't read mail filter file.\n");
return
incoming = jmhlib.Folder(mhlib.MH(), 'inbox')
for msg in incoming.listmessages():
if verbose:
print 'processing message', msg
try:
m = jmhlib.Message(incoming, msg)
except:
if verbose:
print "couldn't access msg " + str(msg)
traceback.print_exc()
mailfilterfn (m)
m.fp.close()
------------------------------------------------------------
I'll leave the implementation of read_filter_fn_if_necessary as an
exercise for the reader :-; it's kind of fun!
Here's the contents of jmhlib.py:
------------------------------------------------------------
# subclass of mhlib.Message for adding common operations
import sys, os, mhlib, types, string, rfc822, time, pwd, re, shutil, traceback
from mhlib import Folder
def add_msg_to_unseen (folder, msgnum):
d = folder.getsequences()
unseen = ((d.has_key("unseen") and d["unseen"]) or [])
unseen.append(msgnum)
d["unseen"] = unseen
folder.putsequences(d)
class Message(mhlib.Message):
# Check to see if any of the specified 'names' are in any of the
# specified headers
def matches (self, names, headers):
if type(names) == types.StringType:
names = ( names , )
if type(headers) == types.StringType:
headers = ( headers , )
for header in headers:
headerval = self.getheader(header)
if not headerval:
continue
for name in names:
testname = string.lower(name)
testheader = string.lower(headerval)
# print "looking for <%s> in <%s>" % (testname, testheader)
if (string.find(testheader, testname) >= 0):
# print " *** found it"
return 1
return 0
def addr_matches (self, mail_addr, header_name):
# returns 1 if "mail_addr" is in any of the addresses in the headers with the name "header_name"
real_header_name = string.lower(header_name)
real_mail_addr = string.lower(mail_addr)
if not hasattr(self, "_addrs"):
self._addrs = {}
if not self._addrs.has_key(real_header_name):
self._addrs[real_header_name] = self.getaddrlist(real_header_name)
test_values = self._addrs[real_header_name]
for value in test_values:
# each value is a pair of (comment, mail_addr)
if ((string.find(string.lower(value[0]), real_mail_addr) >= 0) or
(string.find(string.lower(value[1]), real_mail_addr) >= 0)):
return 1
return 0
# was the message sent to any of the specified names?
def sentto (self, *names):
for header_name in ("to", "resent-to", "apparently-to", "cc"):
for name in names:
if self.addr_matches (name, header_name):
return 1
return 0
# put a copy of the message into another folder
def copy (self, foldername, unseen=1):
newfolder = Folder(self.folder.mh, foldername)
ton = newfolder.getlast() + 1
# print "copying %s into %s/%s" % (self.number, foldername, ton)
self.folder.copymessage(self.number, newfolder, ton)
if unseen:
add_msg_to_unseen (newfolder, ton)
# put the message in a different folder
def refile (self, foldername, unseen=1):
# print "putting %s into %s" % (self.number, foldername)
self.copy(foldername, unseen)
self.discard()
# resend the message to the specified names
def resend(self, *to):
os.system('/usr/lib/sendmail %s <%s' % (string.join(to), self.folder.getmessagefilename(self.number)))
# discard message
def discard(self):
self.folder.removemessages([self.number])
------------------------------------------------------------
Have fun!
Bill
More information about the Python-list
mailing list