Files
mailbot/docs/source/index.rst
Mathieu Agopian 5ed407560d changed owner
2013-06-18 14:06:19 +02:00

263 lines
7.4 KiB
ReStructuredText

Welcome to MailBot's documentation!
=======================================
MailBot is a little python library that let's you execute previously registered
callbacks on reception of emails.
This allows you to do fancy things like doing API calls, running scripts,
sending notifications, ...
Features
--------
MailBot does its best to:
* be fully tested
* apply the pep8 recommendations
* be lightweight, concise and readable
MailBot connects to a mail server using the IMAP protocol, thanks to the
excellent `IMAPClient from Menno Smits
<http://imapclient.readthedocs.org/en/latest/>`_.
Other resources
---------------
Fork it on: http://github.com/magopian/mailbot/
Documentation: http://mailbot.rtfd.org/
Installing
----------
From PyPI::
pip install mailbot
From github::
pip install -e http://github.com/magopian/mailbot/
Usage
-----
You first need to instantiate MailBot, giving it informations to connect to
your IMAP server. MailBot uses `IMAPClient
<http://imapclient.readthedocs.org/en/latest/#a-simple-example>`_, and as such
takes the same parameters.
You also need to provide the username and password. Here's an simple example:
.. code-block:: python
from mailbot import MailBot, register
from mycallbacks import MyCallback
mailbot = MailBot('imap.myserver.com', 'username', 'password')
# register your callback
register(MyCallback)
# check the unprocessed messages and trigger the callback
mailbot.process_messages()
You may want to place the ``process_messages`` in a loop, a celery task, or in
a cron job, tu regularly check new messages and process them.
Registering callbacks
---------------------
:file:`callbacks.py`:
.. code-block:: python
from mailbot import register, Callback
class MyCallback(Callback):
def trigger(self):
print("Mail received: {O}".format(self.subject))
register(MyCallback)
By default, callbacks will be executed on each and every mail received, unless
you specify it differently, either using the 'rules' attribute on the callback
class, or by registering with those rules:
Providing the rules as a parameter
----------------------------------
Here's a callback that will only be triggered if the subject matches the
pattern 'Hello ' followed by a word, anywhere in the subject (it uses
``re.findall``):
.. code-block:: python
from mailbot import register, Callback
class MyCallback(Callback):
rules = {'subject': [r'Hello (\w)']}
def trigger(self):
print("Mail received for {0}".format(self.matches['subject'][0]))
register(MyCallback)
This callback will be triggered on a mail received with the subject "Hello
Bryan", but won't if the subject is "Bye Bryan".
Providing the rules when registering
------------------------------------
The similar functionality can be achieved using a set of rules when
registering:
.. code-block:: python
from mailbot import register, Callback
class MyCallback(Callback):
def trigger(self):
print("Mail received for %s!" self.matches['subject'][0])
register(MyCallback, rules={'subject': [r'Hello (\w)']})
How does it work?
-----------------
When an email is received on the mail server the MailBot is connected to
(using the IMAP protocol), it'll check all the registered callbacks and their
rules.
If each provided rule (either as a class parameter or using the register)
matches the mail's subject, from, to, cc and body, the callback will be
triggered.
Mails are flagged according to their state, in the ``process_messages`` method:
* unread (unseen): mail to be processed by MailBot
* read (seen):
- starred (flagged): MailBot is checking callbacks, and triggering them if
needed, the mail is being processed
- not starred (unflagged): MailBot is done with this mail, and won't process
it anymore
Specifying a timeout
~~~~~~~~~~~~~~~~~~~~
To avoid a mail from staying in the "processing" state for too long (for
example because a previous ``process_message`` started processing it, but then
failed), you may specify a ``timeout`` parameter (in seconds) when
instantiating MailBot:
.. code-block:: python
from mailbot import MailBot
mailbot = MailBot('imap.myserver.com', 'username', 'password', timeout=180)
This doesn't mean that the mail will be reset after 3 minutes, but that when
``process_messages`` is called, it'll first reset mails that are in the
processing state and older than 3 minutes.
Specifying rules
----------------
Rules are regular expressions that will be tested against the various email
data:
* ``subject``: tested against the subject
* ``from``: tested against the mail sender
* ``to``: tested against each of the recipients in the "to" field
* ``cc``: tested against each of the recipients in the "cc" field
* ``body``: tested against the (text/plain) body of the mail
If no rule are provided, for example for the "from" field, then no rule will be
applied, and emails from any sender will potentially trigger the callback.
For each piece of data (subject, from, to, cc, body), the callback class,
once instantiated with the mail, and the ``check_rules`` method called, will
have the attribute ``self.matches[item]`` set with all the captures from the
given patterns, if any, or the full match.
Here are example subjects for the subject rules:
[``r'Hello (\w+), (.*)'``, ``r'[Hh]i (\w+)``]
For each of the following examples, ``self.matches['subject']`` will be a list
of all the captures for all the regular expressions.
If a regular expression doesn't match, then it'll return an empty list.
* 'Hello Bryan, how are you?': [('Bryan', 'how are you?')]
* 'Hi Bryan, how are you?': ['Bryan']
* 'aloha, hi Bryan!': ['Bryan']
* 'aloha Bryan': rules not respected, callback not triggered, []
Here are example subjects for the subject rules (no captures):
[``r'Hello \w+'``, ``r'[Hh]i \w+``]
* 'Hello Bryan, how are you?': ['Hello Bryan']
* 'Hi Bryan, how are you?': ['Hi Bryan']
* 'aloha, hi Bryan!': ['hi Bryan']
* 'aloha Bryan': rules not respected, callback not triggered, []
Rules checking
--------------
A callback will be triggered if the following applies:
* for each item/rule, **any** of the provided regular expressions matches
* **all** the rules (for all the provided items) are respected
Notice the "any" and the "all" there:
* for each rule, there may be several regular expressions. If any of those
match, then the rule is respected.
* if one rule doesn't match, the callback won't be triggered. Non existent
rules don't count, so you could have a single rule on the subject, and none
on the other items (from, to, cc, body).
As an example, let's take an email with the subject "Hello Bryan", from
"John@doe.com":
.. code-block:: python
from mailbot import register, Callback
class MyCallback(Callback):
rules = {'subject': [r'Hello (\w)', 'Hi!'], 'from': ['@doe.com']}
def trigger(self):
print("Mail received for {0}".format(self.matches['subject'][0]))
register(MyCallback)
All the rules are respected, and the callback will be triggered
* subject: even though 'Hi!' isn't found anywhere in the subject, the other
regular expression matches
* from: the regular expression matches
* to, cc, body: no rules provided, so they aren't taken into account
The last bullet point also means that if register a callback with no rules at
all, it'll be triggered on each and every email, making it a "catchall
callback".