238 lines
6.7 KiB
ReStructuredText
238 lines
6.7 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/novagile/mailbot/
|
|
|
|
Documentation: http://mailbot.rtfd.org/
|
|
|
|
|
|
Installing
|
|
----------
|
|
|
|
From PyPI::
|
|
|
|
pip install mailbot
|
|
|
|
From github::
|
|
|
|
pip install -e http://github.com/novagile/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
|
|
|
|
from mycallbacks import MyCallback
|
|
|
|
|
|
mailbot = MailBot('imap.myserver.com', 'username', 'password')
|
|
|
|
# register your callback
|
|
mailbot.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.search``):
|
|
|
|
.. 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:
|
|
|
|
* unprocessed: MailBot hasn't started looking at this mail, it doesn't have the
|
|
``PROCESSING`` nor the ``PROCESSED`` flags
|
|
* ``PROCESSING``: MailBot is checking callbacks, and triggering them if needed
|
|
* ``PROCESSED``: MailBot is done with this mail, and won't process it anymore
|
|
|
|
|
|
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 matches from the
|
|
given patterns, if any. Matches will in fact be ``re.MatchObject``.
|
|
|
|
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 two ``re.MatchObject``, one for each regular expression.
|
|
|
|
If a regular expression doesn't match, then it'll return ``None``.
|
|
|
|
For each example subject, a ``re.MatchObject`` will be represented by its
|
|
matching groups:
|
|
|
|
* 'Hello Bryan, how are you?':
|
|
[['Hello Bryan, how are you?', 'Bryan', 'how are you?'], None]
|
|
* 'Hi Bryan, how are you?': [None, ['Hi Bryan', 'Bryan']]
|
|
* 'aloha, hi Bryan!': [None, ['hi Bryan', 'Bryan']]
|
|
* 'aloha Bryan': rules not respected, callback not triggered, [None, None]
|
|
|
|
|
|
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".
|