diff --git a/imbox/.DS_Store b/imbox/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/imbox/.DS_Store differ diff --git a/imbox/imbox.py b/imbox/imbox.py index f7b9de0..785875e 100644 --- a/imbox/imbox.py +++ b/imbox/imbox.py @@ -28,14 +28,16 @@ class Imbox: self.vendor = vendor or hostname_vendorname_dict.get(self.hostname) if self.vendor is not None: - self.authentication_error_message = name_authentication_string_dict.get(self.vendor) + self.authentication_error_message = name_authentication_string_dict.get( + self.vendor) try: self.connection = self.server.connect(username, password) except imaplib.IMAP4.error as e: if self.authentication_error_message is None: raise - raise imaplib.IMAP4.error(self.authentication_error_message + '\n' + str(e)) + raise imaplib.IMAP4.error( + self.authentication_error_message + '\n' + str(e)) 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 ""))) @@ -61,16 +63,18 @@ class Imbox: 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.uid('STORE', uid, '+FLAGS', '(\\Deleted)') + 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))) + 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))) + logger.info("Move UID {} to {} folder".format( + int(uid), str(destination_folder))) if self.copy(uid, destination_folder): self.delete(uid) @@ -83,7 +87,8 @@ class Imbox: messages_class = GmailMessages if folder: - self.connection.select(messages_class.folder_lookup.get((folder.lower())) or folder) + self.connection.select( + messages_class.FOLDER_LOOKUP.get((folder.lower())) or folder) msg = " from folder '{}'".format(folder) else: msg = " from inbox" diff --git a/imbox/messages.py b/imbox/messages.py index baefae7..fa39e63 100644 --- a/imbox/messages.py +++ b/imbox/messages.py @@ -1,14 +1,26 @@ -from imbox.parser import fetch_email_by_uid -from imbox.query import build_search_query - +import datetime import logging +from imbox.parser import fetch_email_by_uid + + logger = logging.getLogger(__name__) class Messages: - folder_lookup = {} + IMAP_ATTRIBUTE_LOOKUP = { + 'unread': '(UNSEEN)', + 'unflagged': '(UNFLAGGED)', + 'sent_from': '(FROM "{}")', + 'sent_to': '(TO "{}")', + 'date__gt': '(SINCE "{}")', + 'date__lt': '(BEFORE "{}")', + 'date__on': '(ON "{}")', + 'subject': '(SUBJECT "{}")', + } + + FOLDER_LOOKUP = {} def __init__(self, connection, @@ -28,12 +40,25 @@ class Messages: parser_policy=self.parser_policy) def _query_uids(self, **kwargs): - query_ = build_search_query(**kwargs) - message, data = self.connection.uid('search', None, query_) + query_ = self._build_search_query(**kwargs) + _, data = self.connection.uid('search', None, query_) if data[0] is None: return [] return data[0].split() + def _build_search_query(self, **kwargs): + query = [] + for name, value in kwargs.items(): + if value is not None: + if isinstance(value, datetime.date): + value = value.strftime('%d-%b-%Y') + query.append(self.IMAP_ATTRIBUTE_LOOKUP[name].format(value)) + + if query: + return " ".join(query) + + return "(ALL)" + def _fetch_email_list(self): for uid in self._uid_list: yield uid, self._fetch_email(uid) diff --git a/imbox/query.py b/imbox/query.py deleted file mode 100644 index 6e8d806..0000000 --- a/imbox/query.py +++ /dev/null @@ -1,65 +0,0 @@ -import datetime -import logging -# TODO - Validate query arguments - -logger = logging.getLogger(__name__) - - -def format_date(date): - if isinstance(date, datetime.date): - return date.strftime('%d-%b-%Y') - return date - - -def build_search_query(**kwargs): - - # Parse keyword arguments - unread = kwargs.get('unread', False) - unflagged = kwargs.get('unflagged', False) - flagged = kwargs.get('flagged', False) - sent_from = kwargs.get('sent_from', False) - sent_to = kwargs.get('sent_to', False) - date__gt = kwargs.get('date__gt', False) - date__lt = kwargs.get('date__lt', False) - date__on = kwargs.get('date__on', False) - subject = kwargs.get('subject') - uid__range = kwargs.get('uid__range') - - query = [] - - if unread: - query.append("(UNSEEN)") - - if unflagged: - query.append("(UNFLAGGED)") - - if flagged: - query.append("(FLAGGED)") - - if sent_from: - query.append('(FROM "%s")' % sent_from) - - if sent_to: - query.append('(TO "%s")' % sent_to) - - if date__gt: - query.append('(SINCE "%s")' % format_date(date__gt)) - - if date__lt: - query.append('(BEFORE "%s")' % format_date(date__lt)) - - if date__on: - query.append('(ON "%s")' % format_date(date__on)) - - if subject is not None: - query.append('(SUBJECT "%s")' % subject) - - if uid__range: - query.append('(UID %s)' % uid__range) - - if query: - logger.debug("IMAP query: {}".format(" ".join(query))) - return " ".join(query) - - logger.debug("IMAP query: {}".format("(ALL)")) - return "(ALL)" diff --git a/imbox/query.pyi b/imbox/query.pyi deleted file mode 100644 index 91734b9..0000000 --- a/imbox/query.pyi +++ /dev/null @@ -1,7 +0,0 @@ -import datetime -from typing import Union - - -def format_date(date: Union[str, datetime.date]) -> str: ... - -def build_search_query(**kwargs: Union[bool, str, datetime.date]) -> str: ... diff --git a/imbox/vendors/gmail.py b/imbox/vendors/gmail.py index 97c97d9..08d676e 100644 --- a/imbox/vendors/gmail.py +++ b/imbox/vendors/gmail.py @@ -6,7 +6,7 @@ class GmailMessages(Messages): 'https://myaccount.google.com/apppasswords') hostname = 'imap.gmail.com' name = 'gmail' - folder_lookup = { + FOLDER_LOOKUP = { 'all_mail': '"[Gmail]/All Mail"', 'all': '"[Gmail]/All Mail"', @@ -19,11 +19,12 @@ class GmailMessages(Messages): 'spam': '"[Gmail]/Spam"', 'starred': '"[Gmail]/Starred"', 'trash': '"[Gmail]/Trash"', - } def __init__(self, connection, parser_policy, **kwargs): + + self.IMAP_ATTRIBUTE_LOOKUP['subject'] = '(X-GM-RAW "{}")' super().__init__(connection, parser_policy, **kwargs)