Code Monkey home page Code Monkey logo

imap_tools's Introduction

imap_tools

Working with email and mailbox using IMAP protocol.

Python version 3.3+
License Apache-2.0
PyPI https://pypi.python.org/pypi/imap_tools/
IMAP VERSION 4rev1 - https://tools.ietf.org/html/rfc3501
  • Parsed email message attributes
  • Query builder for searching emails
  • Work with emails in folders (copy, delete, flag, move, seen)
  • Work with mailbox folders (list, set, get, create, exists, rename, delete, status)
  • No dependencies
$ pip install imap_tools
from imap_tools import MailBox, Q

# get list of email subjects from INBOX folder
with MailBox('imap.mail.com').login('[email protected]', 'password') as mailbox:
    subjects = [msg.subject for msg in mailbox.fetch()]

# get list of email subjects from INBOX folder - verbose version
mailbox = MailBox('imap.mail.com')
mailbox.login('[email protected]', 'password', initial_folder='INBOX')
subjects = [msg.subject for msg in mailbox.fetch(Q(all=True))]
mailbox.logout()

MailBox.fetch - email message generator, first searches email ids by criteria, then fetch and yields emails by one:

  • criteria: message search criteria, docs
  • charset: 'US-ASCII', indicates charset of the strings that appear in the search criteria. See rfc2978
  • limit: None, limit on the number of read emails, useful for actions with a large number of messages, like "move"
  • miss_defect: True, miss emails with defects
  • miss_no_uid: True, miss emails without uid
  • mark_seen: True, mark emails as seen on fetch
  • reverse: False, in order from the larger date to the smaller

Message public attributes are cached by functools.lru_cache

for message in mailbox.fetch():
    message.uid              # str or None: '123'
    message.subject          # str: 'some subject'
    message.from_            # str: '[email protected]'
    message.to               # tuple: ('[email protected]', '[email protected]', )
    message.cc               # tuple: ('[email protected]', )
    message.bcc              # tuple: ('[email protected]', )
    message.reply_to         # tuple: ('[email protected]', )
    message.date             # datetime.datetime: 1900-1-1 for unparsed, may be naive or with tzinfo
    message.date_str         # str: original date - 'Tue, 03 Jan 2017 22:26:59 +0500'
    message.text             # str: 'hi'
    message.html             # str: '<b>hi</b>'
    message.flags            # tuple: ('SEEN', 'FLAGGED', 'ENCRYPTED')
    message.headers          # dict: {'Received': ('from 1.m.ru', 'from 2.m.ru'), 'AntiVirus': ('Clean',)}

    for att in message.attachments:  # list: [Attachment objects]
        att.filename         # str: 'cat.jpg'
        att.content_type     # str: 'image/jpeg'
        att.payload          # bytes: b'\xff\xd8\xff\xe0\'

    message.obj              # email.message.Message: original object
    message.from_values      # dict or None: {'email': '[email protected]', 'name': 'Van', 'full': 'Van <[email protected]>'}
    message.to_values        # tuple: ({'email': '', 'name': '', 'full': ''},)
    message.cc_values        # tuple: ({'email': '', 'name': '', 'full': ''},)
    message.bcc_values       # tuple: ({'email': '', 'name': '', 'full': ''},)
    message.reply_to_values  # tuple: ({'email': '', 'name': '', 'full': ''},)

Possible search approaches:

from imap_tools import Q, AND, OR, NOT

mailbox.fetch(Q(subject='weather'))  # query, the str-like object - see below
mailbox.fetch('TEXT "hello"')  # str, use charset arg for non US-ASCII chars
mailbox.fetch(b'TEXT "\xd1\x8f"')  # bytes, charset arg is ignored

Implemented query builder for search logic described in rfc3501. See query examples.

  • Class AND and its alias Q are used to combine keys by the logical "and" condition.
  • Class OR is used to combine keys by the logical "or" condition.
  • Class NOT is used to invert the result of a logical expression.
  • Class H (Header) is used to search by headers.

If the "charset" argument is specified in MailBox.fetch, the search string will be encoded to this encoding. You can change this behavior by overriding MailBox._criteria_encoder or pass criteria as bytes in desired encoding.

from imap_tools import Q, AND, OR, NOT
# AND
Q(text='hello', new=True)  # '(TEXT "hello" NEW)'
# OR
OR(text='hello', date=datetime.date(2000, 3, 15))  # '(OR TEXT "hello" ON 15-Mar-2000)'
# NOT
NOT(text='hello', new=True)  # 'NOT (TEXT "hello" NEW)'
# complex
Q(OR(from_='[email protected]', text='"the text"'), NOT(OR(Q(answered=False), Q(new=True))), to='[email protected]')
# encoding
mailbox.fetch(Q(subject='привет'), charset='utf8')  # 'привет' will be encoded by MailBox._criteria_encoder
# python note: you can't do: Q(text='two', NOT(subject='one'))
Q(NOT(subject='one'), text='two')  # use kwargs after logic classes

The search key types are marked with * can accepts a sequence of values like list, tuple, set or generator.

Key Types Results Description
answered bool ANSWERED|UNANSWERED with|without the Answered flag
seen bool SEEN|UNSEEN with|without the Seen flag
flagged bool FLAGGED|UNFLAGGED with|without the Flagged flag
draft bool DRAFT|UNDRAFT with|without the Draft flag
deleted bool DELETED|UNDELETED with|without the Deleted flag
keyword str* KEYWORD KEY with the specified keyword flag
no_keyword str* UNKEYWORD KEY without the specified keyword flag
from_ str* FROM "[email protected]" contain specified str in envelope struct's FROM field
to str* TO "[email protected]" contain specified str in envelope struct's TO field
subject str* SUBJECT "hello" contain specified str in envelope struct's SUBJECT field
body str* BODY "some_key" contain specified str in body of the message
text str* TEXT "some_key" contain specified str in header or body of the message
bcc str* BCC "[email protected]" contain specified str in envelope struct's BCC field
cc str* CC "[email protected]" contain specified str in envelope struct's CC field
date datetime.date* ON 15-Mar-2000 internal date is within specified date
date_gte datetime.date* SINCE 15-Mar-2000 internal date is within or later than the specified date
date_lt datetime.date* BEFORE 15-Mar-2000 internal date is earlier than the specified date
sent_date datetime.date* SENTON 15-Mar-2000 rfc2822 Date: header is within the specified date
sent_date_gte datetime.date* SENTSINCE 15-Mar-2000 rfc2822 Date: header is within or later than the specified date
sent_date_lt datetime.date* SENTBEFORE 1-Mar-2000 rfc2822 Date: header is earlier than the specified date
size_gt int >= 0 LARGER 1024 rfc2822 size larger than specified number of octets
size_lt int >= 0 SMALLER 512 rfc2822 size smaller than specified number of octets
new True NEW have the Recent flag set but not the Seen flag
old True OLD do not have the Recent flag set
recent True RECENT have the Recent flag set
all True ALL all, criteria by default
uid iter(str)|str UID 1,2,17 corresponding to the specified unique identifier set
header H(str, str)* HEADER "A-Spam" "5.8" have a header that contains the specified str in the text

Server side search notes:

  • For string search keys a message matches if the string is a substring of the field. The matching is case-insensitive.
  • When searching by dates - email's time and timezone are disregarding.

You can use 2 approaches to perform these operations:

  • "in bulk" - Perform IMAP operation for message set per 1 command
  • "by one" - Perform IMAP operation for each message separately per N commands

Result of MailBox.fetch generator will be implicitly converted to uid list.

For actions with a large number of messages imap command may be too large and will throw an exception, use 'limit' argument for fetch in this case.

with MailBox('imap.mail.com').login('[email protected]', 'pwd', initial_folder='INBOX') as mailbox:

    # COPY all messages from current folder to folder1, *by one
    for msg in mailbox.fetch():
        res = mailbox.copy(msg.uid, 'INBOX/folder1')

    # MOVE all messages from current folder to folder2, *in bulk (implicit creation of uid list)
    mailbox.move(mailbox.fetch(), 'INBOX/folder2')

    # DELETE all messages from current folder, *in bulk (explicit creation of uid list)
    mailbox.delete([msg.uid for msg in mailbox.fetch()])

    # FLAG unseen messages in current folder as Answered and Flagged, *in bulk.
    flags = (imap_tools.MessageFlags.ANSWERED, imap_tools.MessageFlags.FLAGGED)
    mailbox.flag(mailbox.fetch('(UNSEEN)'), flags, True)

    # SEEN: mark all messages sent at 05.03.2007 in current folder as unseen, *in bulk
    mailbox.seen(mailbox.fetch("SENTON 05-Mar-2007"), False)
with MailBox('imap.mail.com').login('[email protected]', 'pwd') as mailbox:
    # LIST
    for folder_info in mailbox.folder.list('INBOX'):
        print(folder_info)  # {'name': 'INBOX|cats', 'delim': '|', 'flags': '\\Unmarked \\HasChildren'}
    # SET
    mailbox.folder.set('INBOX')
    # GET
    current_folder = mailbox.folder.get()
    # CREATE
    mailbox.folder.create('folder1')
    # EXISTS
    is_exists = mailbox.folder.exists('folder1')
    # RENAME
    mailbox.folder.rename('folder1', 'folder2')
    # DELETE
    mailbox.folder.delete('folder2')
    # STATUS
    folder_status = mailbox.folder.status('some_folder')
    print(folder_status)  # {'MESSAGES': 41, 'RECENT': 0, 'UIDNEXT': 11996, 'UIDVALIDITY': 1, 'UNSEEN': 5}
  • Excessive low level of imaplib library.
  • Other libraries contain various shortcomings or not convenient.
  • Open source projects makes world better.
release_notes.rst

If you found a bug or have a question, please let me know - create merge request or issue.

Thanks to:

imap_tools's People

Contributors

0xthiebaut avatar ikvk avatar jonnyarnold avatar parchd-1 avatar randomstrangerontheinternet avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.