Highly modular mail list for Python
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


+ What is pymalist

Pymalist is a minimal and modular mail list software for Python

+ How does pymalist work

pymalist works like this:

# using fetcher it fetches mail
 * if NothingMore exception is raised program ends
 * if other exception is raised, it is being passed to reactor
   and program ends
# mail is processed with processor
# processed mail is distributed with distributor
# go to #1

In component raises an exception (other than NothingMore) it
gets handled by reactor.

You have to supply fetcher, processor, distributor and reactor
objects the the play method. Basically you need to write a few
lines of code to do it, but you can use built-in fetchers,
distributors, processors and reactors.


   ---------------                  ---------------
  |               |   exception    |               |
  |    fetcher    |--------------->|    reactor    |
  |               |                |               |
   ---------------                  ---------------
          |                             ^     ^
          |                             |     |
   ---------------                      |     |
  |               |       exception     |     |
  |   processor   |---------------------      |
  |               |                           |
   ---------------                            |
          |                                   |
          |                                   |
   ---------------                            |
  |               |         exception         |
  |  distributor  |---------------------------
  |               |


+ The basic components

The basic fetchers are:
* Pop3Fetcher - to fetch mails from dedicated POP3 account
* StdInFetcher - to get one mail from stdin (from procmail)

The basic distributors are:
* SmtpDistributor - to distribute mails with SMTP server
* MultipleDistributor - distribute mails with many distributors
* DeliverToDistributor - distribute to given list of
                         recipients using given distributor

The basic processors are:
* SingleListProcessor - used for a sigle list list
                        (does not check To: header)
* ManyListsProcessor - container of many SingleListProcessors
                       that directs the mail to be processed
                       by right one

The basic reactor is:
* StdErrLogger - loggs each exception to standard error

+ Example mail list server

Example script, that fetches all mail from POP3 account processes
two mail lists and distributes mails with SMTP server and send each
copy of mail to archive mail address using the same SMTP distributor
as for distributing the emails to real recipients:


from pymalist import play
from fetchers import *
from processors import *
from distributors import *
from reactors import *

pop3 = Pop3Fetcher(
    host     = 'pop3.example.com',
    user     = 'example',
    password = 'hackyou',
    ssl      = True,

smtp = SmtpDistributor(
    host     = 'smtp.example.com',
    user     = 'example',
    password = 'hackyou',
    tls      = True,

    fetcher = pop3,
    distributor = MultipleDistributor(
        DeliverToDistributor(['archive@example.com'], smtp),

    processor = MoreListsProcessor(
            list_mail = 'Red mail list <red@list.example.com>',
            subject_prefix = '[Red] ',
            subscribers = ['you@example.com', 'me@example.com']
            list_mail = 'Green mail list <green@list.example.com>',
            subject_prefix = '[Green] ',
            subscribers = ['you@example.com', 'me@example.com']


You'll want to run the script periodically (for example in cron).

+ Customization or adding new features

If you need another logic to fetch, distribute, process or react
to exceptions, you need to create a custom fetcher, distributor,
processor or reactor.

Every fetcher object must have "fetch" method, that returns
the next mail as a email.Message object. Convert it from message
string by email.message_from_string(message_string). If there
is no new mail, raise fetchers.NothingMore exception.

Every distributor object must have "distribute" method, that
gets mail message (email.Message) with has additional
properties set:

* ml_sender - the original sender
* ml_send_to - addresses to send message to
* ml_list - "Mail List Name" <address@somewhere>

Distributor should distribute the mails (but of course it can
do anything - store, archive, send, play music, eject CD tray
and play 8-bit music on PC speaker).

Every processor must have "process" method, that takes one
argument - the mail message fetched and returns mail message
enhanced with at least ml_sender, ml_send_to and ml_list
properties (see above for explanation). An exception should
be raised if the message should not be distributed because of
some violation (person can't post to list or something). If
message should be delivered, but there is zero recipients,
just set ml_send_to property to empty list: [].

Every reactor implements "react" method that takes two
arguments: type of error (currently string "fetcher",
"processor" or "distributor") and the exception that was
raised. NothingMore exception raised by fetcher is not passed
to reactor.

+ Why did I write this

I wrote this software in a few hours, because I didn't find
any good mail list software that was flexible enough to

* read email from a remote mailbox (with POP3 or IMAP)
* distribute emails with a remote SMTP server (possibly using
  the same mailbox)
* require no root permissions
* is modular enough

I know my mail list is not perfect, because it has no
input/output queues, no (un)subscribe features nor mail
archive, but using the concepts of fetchers, distributors,
processors and reactors they can be easily plugged-in.

Author: Piotr Gabryjeluk
Started: 22 May 2009