Working with email and mailbox using IMAP protocol.
Project description
Working with email and mailbox using IMAP protocol.
Python version |
3.3+ |
License |
MIT |
PyPI |
|
IMAP |
VERSION 4rev1 - https://tools.ietf.org/html/rfc3501 |
Features
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
Installation
$ pip install imap_tools
Guide
Basic
from imap_tools import MailBox, Q
# 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()]
# OR the same otherwise
mailbox = MailBox('imap.mail.com')
mailbox.login('test@mail.com', '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 uids 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
Email attributes
# NOTE: All message properties 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, 'sender@ya.ru'
message.to # tuple, ('iam@goo.ru', 'friend@ya.ru', )
message.cc # tuple, ('cc@mail.ru', )
message.bcc # tuple, ('bcc@mail.ru', )
message.date # datetime.datetime, 1900-1-1 for unparsed, may be naive or with tzinfo
message.text # str, 'hi'
message.html # str, '<b>hi</b>'
message.flags # tuple, ('SEEN', 'FLAGGED', 'ENCRYPTED')
message.headers # dict, {'Received': ('from 1.m.net', 'from 2.m.net'), 'AntiVirus-Status': ('Clean',)}
message.attachments # [(str, bytes)], 'cat.jpg', b'\xff\xd8\xff\xe0\'
message.obj # original email.message.Message object
message.from_values # dict or None, {'email': 'sender@ya.ru', 'name': 'Ivan', 'full': 'Ivan <sender@ya.ru>'}
message.to_values # tuple, ({'email': '', 'name': '', 'full': ''},)
message.cc_values # tuple, ({'email': '', 'name': '', 'full': ''},)
message.bcc_values # tuple, ({'email': '', 'name': '', 'full': ''},)
message.date_str # original date str, 'Tue, 03 Jan 2017 22:26:59 +0500'
Search criteria
Implemented the search logic described in rfc3501.
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.
If the “charset” argument is specified in MailBox.fetch, the search string will be encoded to this encoding.
You can change this behaviour by overriding MailBox._criteria_encoder or pass criteria as bytes in desired encoding.
from imap_tools import Q, AND, OR, NOT
# base
mailbox.fetch('TEXT "hello"') # str
mailbox.fetch(b'TEXT "\xd1\x8f"') # bytes
mailbox.fetch(Q(subject='weather')) # query, the str-like object
# 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:
# 'TO "to@ya.ru" (OR FROM "from@ya.ru" TEXT "\\"the text\\"") (NOT (OR UNANSWERED NEW))')
Q(OR(from_='from@ya.ru', text='"the text"'), NOT(OR(Q(answered=False), Q(new=True))), to='to@ya.ru')
# encoding
mailbox.fetch(Q(subject='привет'), charset='utf8') # 'привет' will be encoded by MailBox._criteria_encoder
Python syntax limitations:
# you can't do: Q(to='one@mail.ru', to='two@mail.ru'), instead you can:
Q(AND(to='one@mail.ru'), AND(to='two@mail.ru')) # 'TO "one@mail.ru" TO "two@mail.ru"'
# you can't do: Q(subject='two', NOT(subject='one')), use kwargs after args (after logic classes):
Q(NOT(subject='one'), subject='two')
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 15-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 |
(str, str) |
HEADER “AntiSpam” “5.8” |
have a header that contains the specified str in the text |
* - 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:
“by one” - Perform IMAP operation for each message separately per N commands
“in bulk” - Perform IMAP operation for message set per 1 command
Result of MailBox.fetch generator will be implicitly converted to uid list
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.StandardMessageFlags.ANSWERED, imap_tools.StandardMessageFlags.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)
Actions with mailbox folders
# LIST
for folder in mailbox.folder.list('INBOX'):
print(folder['flags'], folder['delim'], folder['name'])
# 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
for status_key, status_val in mailbox.folder.status('some_folder').items():
print(status_key, status_val)
Reasons
Excessive low level of imaplib library
Other libraries contain various shortcomings or not convenient
Open source projects makes world better
Release notes
Thanks to
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
Built Distribution
File details
Details for the file imap_tools-0.9.0.tar.gz
.
File metadata
- Download URL: imap_tools-0.9.0.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 87d850f88940048bae90ec647de5cdcba73773d151a8c765689c457987afeb34 |
|
MD5 | 8db64bb29b8d93299bee97d2dfd4dd3a |
|
BLAKE2b-256 | 5b80e423fe8afafc73e356396574c847001ab272c421c56e31d5b8b04a2e29a9 |
File details
Details for the file imap_tools-0.9.0-py3-none-any.whl
.
File metadata
- Download URL: imap_tools-0.9.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.4.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a9d95826083172fa0a199b31e47ad549fce0b38ca40a16bad2bb1de6dbe00e90 |
|
MD5 | 355827a9943a0842754dc3fe54eedae6 |
|
BLAKE2b-256 | 708d77d5612e2043596b0a79ad4ccc3ace9d70e7c9d40d4b4632c76dce11a2ba |