-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tons of changes to get rudimentary Move() working
- Loading branch information
1 parent
2243e41
commit c1c74d1
Showing
14 changed files
with
296 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
_autosummary/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{{ fullname | escape | underline }} | ||
|
||
.. currentmodule:: {{ module }} | ||
|
||
.. autoclass:: {{ objname }} | ||
:members: | ||
:undoc-members: | ||
:special-members: | ||
:show-inheritance: | ||
|
||
.. autoclasstoc:: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
API reference | ||
============= | ||
|
||
.. autosummary:: | ||
:toctree: _autosummary | ||
:recursive: | ||
|
||
remote_email_filtering |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Getting started | ||
--------------- | ||
|
||
Just do it! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
Introduction | ||
============ | ||
|
||
``remote_email_filtering`` is a email client library that lets you use arbitrary | ||
Python code for automation. You can write rules or filters as code on any | ||
machine instead of being restricted to limited rulsets in hosted webmail or GUI | ||
email clients. | ||
|
||
As these actions are Python code, you can trigger other automation based on | ||
emails. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
__version__ = '0.1.0' | ||
|
||
from .main import * | ||
from .types import * | ||
from .main import start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,53 @@ | ||
""" | ||
Basic building blocks for doing stuff with a | ||
:class:`~remote_email_filtering.message.Message` | ||
""" | ||
|
||
import abc | ||
|
||
|
||
class Action(abc.ABC): | ||
"""A callable that does something with a Message""" | ||
""" | ||
A callable that does something with a :class:`~.message.Message` | ||
Instances will be called with :class:`~.message.Message` instances. | ||
The :attr:`remote` attribute will be set to a :class:`Remote` before | ||
calling. | ||
""" | ||
|
||
def __init__(self): | ||
self.remote = None | ||
|
||
@abc.abstractmethod | ||
def __call__(self, message): | ||
def __call__(self, msg) -> 'Iterable[Action]': | ||
""" | ||
When called, an :class:`Action` should return an iterable of other | ||
:class:`Action` s that will be applied to the ``msg`` being processed. | ||
""" | ||
pass | ||
|
||
|
||
class Stop(Action): | ||
""" | ||
Stop processing any futher :class:`Action` for the current | ||
:class:`~.message.Message` | ||
""" | ||
|
||
def __call__(self, msg): | ||
raise StopIteration() | ||
|
||
|
||
class Move(Action): | ||
def __init__(self, destination: tuple[str]): | ||
""" | ||
Move the :class:`~.message.Message` to ``destination`` directory. | ||
:param tuple[str] destination: the destination directory on the server | ||
""" | ||
super().__init__() | ||
self.destination = destination | ||
|
||
def __call__(self, msg): | ||
self.remote.move_message(msg, self.destination) | ||
return [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,47 @@ | ||
import datetime | ||
import itertools | ||
import time | ||
from pprint import pp | ||
import typing | ||
|
||
from . import types | ||
|
||
def filter_message(message, filters): | ||
pass | ||
|
||
|
||
def start_filtering(remote, | ||
filter_map=dict(), | ||
interval=datetime.timedelta(seconds=5)): | ||
def pipeline(message, actions): | ||
actions = iter(actions) | ||
while True: | ||
for dir in remote.list_dirs(): | ||
if dir in filter_map: | ||
for message in remote.get_messages(dir): | ||
filter_message(message, filter_map[dir]) | ||
try: | ||
action = next(actions) | ||
except StopIteration: | ||
break | ||
|
||
action.remote = message.remote | ||
try: | ||
further = action(message) | ||
if further is None: | ||
raise Exception(msg=f'Action: {action} returned None') | ||
except StopIteration: | ||
return | ||
actions = itertools.chain(further, actions) | ||
|
||
|
||
def start(remote, | ||
dir_actions: typing.Dict[types.Directory, typing.List['Action']] = dict(), | ||
interval=datetime.timedelta(seconds=5), | ||
count=float('inf')): | ||
""" | ||
Start applying :class:`~.action.Action` s to all messages in specified | ||
directories. | ||
:param Dict[Directory, List[Action]] dir_actions: | ||
:class:`~.action.Action` to apply to all messages in the directory | ||
:param interval: duration to wait after each pass | ||
:param count: number of times to loop through all directories | ||
""" | ||
while count > 0: | ||
for dir_ in remote.list_dirs(): | ||
if dir_ in dir_actions: | ||
for message in remote.get_messages(dir_): | ||
pipeline(message, dir_actions[dir_]) | ||
count -= 1 | ||
|
||
time.sleep(interval.seconds) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,73 @@ | ||
import email | ||
import email.header | ||
import email.policy | ||
import typing | ||
|
||
from . import types | ||
|
||
class Message(object): | ||
def __init__(self, rfc822_bytes): | ||
""" | ||
An email message with convenient properties | ||
""" | ||
def __init__(self, uid: types.Uid, | ||
envelope, remote, dir_=None, rfc822_bytes=None): | ||
""" | ||
:param uid: A unique identifier for a message within ``dir_`` | ||
:param envelope: The envelope structure parsed from headers | ||
:param remote: A :class:`~.remote.Remote` used to lazy-load the body | ||
:param tuple[str] dir_: the mailbox directory that this email is in | ||
""" | ||
self.uid = uid | ||
self.envelope = envelope._asdict() | ||
for field in ('cc', 'bcc', 'from_', 'reply_to', 'sender', 'to'): | ||
if not self.envelope[field]: | ||
self.envelope[field] = [] | ||
self.envelope[field] = tuple((types.Address.from_imapclient(x) | ||
for x in self.envelope[field])) | ||
|
||
self.remote = remote | ||
self.dir_ = dir_ | ||
self.raw = rfc822_bytes | ||
self.mail = email.message_from_bytes(self.raw) | ||
self._body = None | ||
if rfc822_bytes is not None: | ||
self._body = email.message_from_bytes(self.raw, | ||
policy=email.policy.default) | ||
|
||
@property | ||
def body(self): | ||
if self._body is None: | ||
self.raw = self.remote.fetch_body(self.uid) | ||
self._body = email.message_from_bytes(self.raw, | ||
policy=email.policy.default) | ||
return self._body | ||
|
||
@property | ||
def To(self): | ||
ret = self.mail['To'] | ||
if ret is None: | ||
ret = '' | ||
return ret | ||
return self.envelope['to'] | ||
|
||
@property | ||
def Cc(self): | ||
return self.envelope['cc'] | ||
|
||
@property | ||
def From(self): | ||
return self.envelope['from_'] | ||
|
||
@property | ||
def Recipients(self): | ||
return ', '.join(_ for _ in (self.mail['To'], self.mail['CC']) | ||
if _ is not None) | ||
return self.To + self.Cc | ||
|
||
@property | ||
def Subject(self): | ||
return self.mail['Subject'] | ||
return self.envelope['subject'] | ||
|
||
@property | ||
def SaneSubject(self): | ||
ret = self.mail['Subject'] | ||
def _SaneSubject(self): | ||
ret = self.body['Subject'] | ||
ret = email.header.decode_header(ret)[0] | ||
if ret[1] is not None: | ||
ret = ret[0].decode('ascii', errors='replace') | ||
else: | ||
ret = ret[0] | ||
ret = ret.replace('\n', '') | ||
return ret | ||
|
||
@property | ||
def From(self): | ||
return self.mail['From'] |
Oops, something went wrong.