This commit is contained in:
2018-07-26 15:08:08 -04:00
5 changed files with 173 additions and 138 deletions

View File

@@ -36,14 +36,21 @@ Usage
ssl=True, ssl=True,
ssl_context=None, ssl_context=None,
starttls=False) as imbox: starttls=False) as imbox:
# Get all folders # Get all folders
status, folders_with_additional_info = imbox.folders() status, folders_with_additional_info = imbox.folders()
# Gets all messages # Gets all messages from the inbox
all_messages = imbox.messages() all_inbox_messages = imbox.messages()
# Unread messages # Unread messages
unread_messages = imbox.messages(unread=True) unread_inbox_messages = imbox.messages(unread=True)
# Flagged messages
inbox_flagged_messages = imbox.messages(flagged=True)
# Un-flagged messages
inbox_unflagged_messages = imbox.messages(unflagged=True)
# Flagged messages # Flagged messages
flagged_messages = imbox.messages(flagged=True) flagged_messages = imbox.messages(flagged=True)
@@ -52,27 +59,30 @@ Usage
unflagged_messages = imbox.messages(unflagged=True) unflagged_messages = imbox.messages(unflagged=True)
# Messages sent FROM # Messages sent FROM
messages_from_martin = imbox.messages(sent_from='martin@amon.cx') inbox_messages_from = imbox.messages(sent_from='sender@example.org')
# Messages sent TO # Messages sent TO
messages_to_martin = imbox.messages(sent_to='martin@amon.cx') inbox_messages_to = imbox.messages(sent_to='receiver@example.org')
# Messages received before specific date # Messages received before specific date
messages_received_before_20130731 = imbox.messages(date__lt=datetime.date(2013, 7, 31)) inbox_messages_received_before = imbox.messages(date__lt=datetime.date(2018, 7, 31))
# Messages received after specific date # Messages received after specific date
messages_received_after_20130730 = imbox.messages(date__gt=datetime.date(2013, 7, 30)) inbox_messages_received_after = imbox.messages(date__gt=datetime.date(2018, 7, 30))
# Messages received on a specific date # Messages received on a specific date
messages_received_20130730 = imbox.messages(date__on=datetime.date(2013, 7, 30)) inbox_messages_received_on_date = imbox.messages(date__on=datetime.date(2018, 7, 30))
# Messages from a specific folder
messages_from_folder = imbox.messages(folder='Social')
# Messages whose subjects contain a string # Messages whose subjects contain a string
messages_subject_christmas = imbox.messages(subject='Christmas') inbox_messages_subject_christmas = imbox.messages(subject='Christmas')
# Messages from a specific folder # Messages from a specific folder
messages_in_folder_social = imbox.messages(folder='Social') messages_in_folder_social = imbox.messages(folder='Social')
for uid, message in all_messages: for uid, message in all_inbox_messages:
# Every message is an object with the following keys # Every message is an object with the following keys
message.sent_from message.sent_from
@@ -132,7 +142,7 @@ Usage
# mark the message as read # mark the message as read
imbox.mark_seen(uid) imbox.mark_seen(uid)
Changelog Changelog

View File

@@ -1,131 +1,8 @@
from imbox.imap import ImapTransport from imbox.imbox import Imbox
from imbox.parser import parse_email, fetch_email_by_uid
from imbox.query import build_search_query
import logging
logger = logging.getLogger(__name__)
__version_info__ = (0, 9, 5) __version_info__ = (0, 9, 5)
__version__ = '.'.join([str(x) for x in __version_info__]) __version__ = '.'.join([str(x) for x in __version_info__])
class Imbox: __all__ = ['Imbox']
def __init__(self, hostname, username=None, password=None, ssl=True,
port=None, ssl_context=None, policy=None, starttls=False):
self.server = ImapTransport(hostname, ssl=ssl, port=port,
ssl_context=ssl_context, starttls=starttls)
self.hostname = hostname
self.username = username
self.password = password
self.parser_policy = policy
self.connection = self.server.connect(username, password)
logger.info("Connected to IMAP Server with user {username} on {hostname}{ssl}".format(
hostname=hostname, username=username, ssl=(" over SSL" if ssl or starttls else "")))
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.logout()
def logout(self):
self.connection.close()
self.connection.logout()
logger.info("Disconnected from IMAP Server {username}@{hostname}".format(
hostname=self.hostname, username=self.username))
def mark_seen(self, uid):
logger.info("Mark UID {} with \\Seen FLAG".format(int(uid)))
self.connection.uid('STORE', uid, '+FLAGS', '(\\Seen)')
def mark_flag(self, uid):
logger.info("Mark UID {} with \\Flagged FLAG".format(int(uid)))
self.connection.uid('STORE', uid, '+FLAGS', '(\\Flagged)')
def delete(self, uid):
logger.info("Mark UID {} with \\Deleted FLAG and expunge.".format(int(uid)))
self.connection.expunge()
def copy(self, uid, destination_folder):
logger.info("Copy UID {} to {} folder".format(int(uid), str(destination_folder)))
return self.connection.uid('COPY', uid, destination_folder)
def move(self, uid, destination_folder):
logger.info("Move UID {} to {} folder".format(int(uid), str(destination_folder)))
if self.copy(uid, destination_folder):
self.delete(uid)
def messages(self, **kwargs):
folder = kwargs.get('folder', False)
msg = ""
if folder:
self.connection.select(folder)
msg = " from folder '{}'".format(folder)
logger.info("Fetch list of messages{}".format(msg))
return Messages(connection=self.connection,
parser_policy=self.parser_policy,
**kwargs)
def folders(self):
return self.connection.list()
class Messages:
def __init__(self,
connection,
parser_policy,
**kwargs):
self.connection = connection
self.parser_policy = parser_policy
self.kwargs = kwargs
self._uid_list = self._query_uids(**kwargs)
logger.debug("Fetch all messages for UID in {}".format(self._uid_list))
def _fetch_email(self, uid):
return fetch_email_by_uid(uid=uid,
connection=self.connection,
parser_policy=self.parser_policy)
def _query_uids(self, **kwargs):
query_ = build_search_query(**kwargs)
message, data = self.connection.uid('search', None, query_)
if data[0] is None:
return []
return data[0].split()
def _fetch_email_list(self):
for uid in self._uid_list:
yield uid, self._fetch_email(uid)
def __repr__(self):
if len(self.kwargs) > 0:
return 'Messages({})'.format('\n'.join('{}={}'.format(key, value)
for key, value in self.kwargs.items()))
return 'Messages(ALL)'
def __iter__(self):
return self._fetch_email_list()
def __next__(self):
return self
def __len__(self):
return len(self._uid_list)
def __getitem__(self, index):
uids = self._uid_list[index]
if not isinstance(uids, list):
uid = uids
return uid, self._fetch_email(uid)
return [(uid, self._fetch_email(uid))
for uid in uids]

72
imbox/imbox.py Normal file
View File

@@ -0,0 +1,72 @@
from imbox.imap import ImapTransport
from imbox.messages import Messages
import logging
logger = logging.getLogger(__name__)
class Imbox:
def __init__(self, hostname, username=None, password=None, ssl=True,
port=None, ssl_context=None, policy=None, starttls=False):
self.server = ImapTransport(hostname, ssl=ssl, port=port,
ssl_context=ssl_context, starttls=starttls)
self.hostname = hostname
self.username = username
self.password = password
self.parser_policy = policy
self.connection = self.server.connect(username, password)
logger.info("Connected to IMAP Server with user {username} on {hostname}{ssl}".format(
hostname=hostname, username=username, ssl=(" over SSL" if ssl or starttls else "")))
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.logout()
def logout(self):
self.connection.close()
self.connection.logout()
logger.info("Disconnected from IMAP Server {username}@{hostname}".format(
hostname=self.hostname, username=self.username))
def mark_seen(self, uid):
logger.info("Mark UID {} with \\Seen FLAG".format(int(uid)))
self.connection.uid('STORE', uid, '+FLAGS', '(\\Seen)')
def mark_flag(self, uid):
logger.info("Mark UID {} with \\Flagged FLAG".format(int(uid)))
self.connection.uid('STORE', uid, '+FLAGS', '(\\Flagged)')
def delete(self, uid):
logger.info("Mark UID {} with \\Deleted FLAG and expunge.".format(int(uid)))
self.connection.expunge()
def copy(self, uid, destination_folder):
logger.info("Copy UID {} to {} folder".format(int(uid), str(destination_folder)))
return self.connection.uid('COPY', uid, destination_folder)
def move(self, uid, destination_folder):
logger.info("Move UID {} to {} folder".format(int(uid), str(destination_folder)))
if self.copy(uid, destination_folder):
self.delete(uid)
def messages(self, **kwargs):
folder = kwargs.get('folder', False)
if folder:
self.connection.select(folder)
msg = " from folder '{}'".format(folder)
else:
msg = " from inbox"
logger.info("Fetch list of messages{}".format(msg))
return Messages(connection=self.connection,
parser_policy=self.parser_policy,
**kwargs)
def folders(self):
return self.connection.list()

62
imbox/messages.py Normal file
View File

@@ -0,0 +1,62 @@
from imbox.parser import fetch_email_by_uid
from imbox.query import build_search_query
import logging
logger = logging.getLogger(__name__)
class Messages:
def __init__(self,
connection,
parser_policy,
**kwargs):
self.connection = connection
self.parser_policy = parser_policy
self.kwargs = kwargs
self._uid_list = self._query_uids(**kwargs)
logger.debug("Fetch all messages for UID in {}".format(self._uid_list))
def _fetch_email(self, uid):
return fetch_email_by_uid(uid=uid,
connection=self.connection,
parser_policy=self.parser_policy)
def _query_uids(self, **kwargs):
query_ = build_search_query(**kwargs)
message, data = self.connection.uid('search', None, query_)
if data[0] is None:
return []
return data[0].split()
def _fetch_email_list(self):
for uid in self._uid_list:
yield uid, self._fetch_email(uid)
def __repr__(self):
if len(self.kwargs) > 0:
return 'Messages({})'.format('\n'.join('{}={}'.format(key, value)
for key, value in self.kwargs.items()))
return 'Messages(ALL)'
def __iter__(self):
return self._fetch_email_list()
def __next__(self):
return self
def __len__(self):
return len(self._uid_list)
def __getitem__(self, index):
uids = self._uid_list[index]
if not isinstance(uids, list):
uid = uids
return uid, self._fetch_email(uid)
return [(uid, self._fetch_email(uid))
for uid in uids]

View File

@@ -1,8 +1,10 @@
import imaplib
import io import io
import re import re
import email import email
import base64 import base64
import quopri import quopri
import sys
import time import time
from datetime import datetime from datetime import datetime
from email.header import decode_header from email.header import decode_header
@@ -129,15 +131,27 @@ def decode_content(message):
def fetch_email_by_uid(uid, connection, parser_policy): def fetch_email_by_uid(uid, connection, parser_policy):
message, data = connection.uid('fetch', uid, '(BODY.PEEK[])') message, data = connection.uid('fetch', uid, '(BODY.PEEK[] FLAGS)')
logger.debug("Fetched message for UID {}".format(int(uid))) logger.debug("Fetched message for UID {}".format(int(uid)))
raw_email = data[0][1]
raw_headers, raw_email = data[0]
email_object = parse_email(raw_email, policy=parser_policy) email_object = parse_email(raw_email, policy=parser_policy)
flags = parse_flags(raw_headers.decode())
email_object.__dict__['flags'] = flags
return email_object return email_object
def parse_flags(headers):
"""Copied from https://github.com/girishramnani/gmail/blob/master/gmail/message.py"""
if len(headers) == 0:
return []
if sys.version_info[0] == 3:
headers = bytes(headers, "ascii")
return list(imaplib.ParseFlags(headers))
def parse_email(raw_email, policy=None): def parse_email(raw_email, policy=None):
if isinstance(raw_email, bytes): if isinstance(raw_email, bytes):
raw_email = str_encode(raw_email, 'utf-8', errors='ignore') raw_email = str_encode(raw_email, 'utf-8', errors='ignore')