Skip to main content

Working with email and mailbox using IMAP protocol.

Project description

Working with email and mailbox using IMAP protocol.

  • 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

Python version

3.3+

License

Apache-2.0

PyPI

https://pypi.python.org/pypi/imap_tools/

IMAP RFC

VERSION 4rev1 - https://tools.ietf.org/html/rfc3501

EMAIL RFC

Internet Message Format - https://tools.ietf.org/html/rfc2822

Installation

$ pip install imap_tools

Guide

Basic

from imap_tools import MailBox, AND

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

# get list of email subjects from INBOX folder - equivalent verbose version
mailbox = MailBox('imap.mail.com')
mailbox.login('test@mail.com', 'password', initial_folder='INBOX')  # or mailbox.folder.set instead 3d arg
subjects = [msg.subject for msg in mailbox.fetch(AND(all=True))]
mailbox.logout()

MailBox(BaseMailBox), MailBoxUnencrypted(BaseMailBox) - for create mailbox instance.

BaseMailBox.login, MailBox.xoauth2 - authentication functions

BaseMailBox.fetch - email message generator, first searches email nums by criteria, then fetch and yields Message:

  • criteria = ‘ALL’, message search criteria, query builder

  • 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

  • headers_only = False, get only email headers (without text, html, attachments)

  • bulk = False, False - fetch each message separately per N commands - low memory consumption, slow; True - fetch all messages per 1 command - high memory consumption, fast

BaseMailBox.<action> - copy, move, delete, flag, seen

BaseMailBox.folder - folder manager

BaseMailBox.search - search mailbox for matching message numbers (this is not uids)

BaseMailBox.box - imaplib.IMAP4/IMAP4_SSL client instance.

Email attributes

Message and Attachment public attributes are cached by functools.lru_cache

for msg in mailbox.fetch():
    msg.uid              # str or None: '123'
    msg.subject          # str: 'some subject 你 привет'
    msg.from_            # str: 'sender@ya.ru'
    msg.to               # tuple: ('iam@goo.ru', 'friend@ya.ru', )
    msg.cc               # tuple: ('cc@mail.ru', )
    msg.bcc              # tuple: ('bcc@mail.ru', )
    msg.reply_to         # tuple: ('reply_to@mail.ru', )
    msg.date             # datetime.datetime: 1900-1-1 for unparsed, may be naive or with tzinfo
    msg.date_str         # str: original date - 'Tue, 03 Jan 2017 22:26:59 +0500'
    msg.text             # str: 'Hello 你 Привет'
    msg.html             # str: '<b>Hello 你 Привет</b>'
    msg.flags            # tuple: ('SEEN', 'FLAGGED', 'ENCRYPTED')
    msg.headers          # dict: {'Received': ('from 1.m.ru', 'from 2.m.ru'), 'AntiVirus': ('Clean',)}

    for att in msg.attachments:  # list: [Attachment]
        att.filename             # str: 'cat.jpg'
        att.payload              # bytes: b'\xff\xd8\xff\xe0\'
        att.content_id           # str: 'part45.06020801.00060008@mail.ru'
        att.content_type         # str: 'image/jpeg'
        att.content_disposition  # str: 'inline'
        att.part                 # email.message.Message: original object

    msg.obj              # email.message.Message: original object
    msg.from_values      # dict or None: {'email': 'im@ya.ru', 'name': 'Ya 你', 'full': 'Ya 你 <im@ya.ru>'}
    msg.to_values        # tuple: ({'email': '', 'name': '', 'full': ''},)
    msg.cc_values        # tuple: ({'email': '', 'name': '', 'full': ''},)
    msg.bcc_values       # tuple: ({'email': '', 'name': '', 'full': ''},)
    msg.reply_to_values  # tuple: ({'email': '', 'name': '', 'full': ''},)

Search criteria

This chapter about “criteria” and “charset” arguments of MailBox.fetch.

You can use 3 approaches to build search criteria:

from imap_tools import AND, OR, NOT

mailbox.fetch(AND(subject='weather'))  # query, the str-like object
mailbox.fetch('TEXT "hello"')          # str
mailbox.fetch(b'TEXT "\xd1\x8f"')      # bytes, *charset arg is ignored

The “charset” is argument used for encode criteria to this encoding. You can pass criteria as bytes in desired encoding - charset will be ignored.

Query builder implements all search logic described in rfc3501. See query examples.

Class

Alias

Usage

Arguments

AND

A

combines keys by logical “AND” condition

Search keys (see below) | str

OR

O

combines keys by logical “OR” condition

Search keys (see below) | str

NOT

N

invert the result of a logical expression

AND/OR instances | str

Header

H

for search by headers

name: str, value: str

from imap_tools import A, AND, OR, NOT
# AND
A(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
A(OR(from_='from@ya.ru', text='"the text"'), NOT(OR(A(answered=False), A(new=True))), to='to@ya.ru')
# encoding
mailbox.fetch(A(subject='привет'), charset='utf8')
# python note: you can't do: A(text='two', NOT(subject='one'))
A(NOT(subject='one'), text='two')  # use kwargs after logic classes (args)

Search key table. Key types 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 “from@ya.ru”

contain specified str in envelope struct’s FROM field

to

str*

TO “to@ya.ru”

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 “bcc@ya.ru”

contain specified str in envelope struct’s BCC field

cc

str*

CC “cc@ya.ru”

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

gmail_label

str*

X-GM-LABELS “label1”

have this gmail label.

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.

Actions with emails in folder

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

MailBox.fetch generator instance passed as the first argument to any action will be implicitly converted to uid list.

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

with MailBox('imap.mail.com').login('test@mail.com', '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.MailMessageFlags.ANSWERED, imap_tools.MailMessageFlags.FLAGGED)
    mailbox.flag(mailbox.fetch(AND(seen=False)), 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)

Actions with mailbox folders

with MailBox('imap.mail.com').login('test@mail.com', '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}

Exceptions

Custom lib exceptions here: errors.py.

Release notes

History of important changes: release_notes.rst

Contribute

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

Reasons

  • Excessive low level of imaplib library.

  • Other libraries contain various shortcomings or not convenient.

  • Open source projects make world better.

Thanks

Big thanks to people who helped develop this library:

shilkazx, somepad, 0xThiebaut, TpyoKnig, parchd-1, dojasoncom, RandomStrangerOnTheInternet, jonnyarnold, Mitrich3000, audemed44, mkalioby, atlas0fd00m, unqx, daitangio, upils, Foosec, frispete, PH89, amarkham09, nixCodeX, backelj, ohayak, mwherman95926, andyfensham, mike-code, aknrdureegaesr

💰 You may thank me, if this library helped you.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

imap_tools-0.24.0.tar.gz (76.3 kB view details)

Uploaded Source

Built Distribution

imap_tools-0.24.0-py3-none-any.whl (59.8 kB view details)

Uploaded Python 3

File details

Details for the file imap_tools-0.24.0.tar.gz.

File metadata

  • Download URL: imap_tools-0.24.0.tar.gz
  • Upload date:
  • Size: 76.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/50.3.0 requests-toolbelt/0.8.0 tqdm/4.29.1 CPython/3.6.5

File hashes

Hashes for imap_tools-0.24.0.tar.gz
Algorithm Hash digest
SHA256 057e6b24560ac1cf299e91d3d79dccd336203095cc323e18b836fb166dce9e6e
MD5 4bf5ac080f42dffb16c5a1ae474188db
BLAKE2b-256 c05daf73d47732200c0cc7df6d72d8ee6ce64ad63a890a01d24ea046a043a425

See more details on using hashes here.

File details

Details for the file imap_tools-0.24.0-py3-none-any.whl.

File metadata

  • Download URL: imap_tools-0.24.0-py3-none-any.whl
  • Upload date:
  • Size: 59.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/50.3.0 requests-toolbelt/0.8.0 tqdm/4.29.1 CPython/3.6.5

File hashes

Hashes for imap_tools-0.24.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1a2c25c9b287ab08827bf80462e0ef9ba3c1f547f51caad525e825f50ffa635d
MD5 206a1cf5105a35ffafbe3c19159e18cb
BLAKE2b-256 1e271aa5267e6637c61a9c0d37e7c982667c22f7ee1e1977af8a15fa6c00d7fd

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page