Python GMail IMAP doesn't return UIDs - python

I am using imaplib with Python to fetch the contents of my inbox or GMail labels.
My problem is: imaplib returns the UIDs when I'm querying the inbox, but not my email labels.
If I query the inbox, I get UIDs:
inbox EMAIL_UIDS: 24408 24599 25193 25224 25237 25406 25411 25412 25413 25415
But if I query my label "New" (which contains two of the messages in the inbox, thus should contain two of the above UIDs), I get only the ordered indices:
New EMAIL_UIDS: 1 2
My code is:
#Main file
if folder_name is None:
email_uids = obtain_inbox_email_uids(mail)
else:
email_uids = obtain_folder_email_uids(folder_name,mail)
email_uids = list(email_uids)
and:
#Email utilities file
def obtain_folder_email_uids(folder_name, mail):
"""
Given an IMAP instance,
return the UIDs of the emails in a specific folder.
"""
mail.select(folder_name)
result, data = mail.uid('search', None, "ALL")
print "RESULT, DATA",result,data
email_uids = data[0]
print folder_name,"EMAIL_UIDS:",email_uids
email_uids = email_uids.split(" ")
email_uids = reversed(email_uids)
return email_uids
def obtain_inbox_email_uids(mail):
"""
Given an IMAP instance,
return the UIDs of the inbox emails.
"""
return obtain_folder_email_uids('inbox', mail)
Does anybody know why imaplib returns UIDs for the inbox but ordered indices for the specific labels, and how can I get it to return the UIDs?
Thank you

Found out the problem. The UIDs were being returned correctly.
My problem was: I was using a function to manually fetch an email based on its UID. So in the case of the inbox, I obtained the UIDs, then used this function to obtain each mail in specific. With each label, the same.
However, each specific email was being returned for the inbox, but in the case of a label, I wasn't able to fetch each mail. I therefore assumed I wasn't passing the correct UID to the function.
My mistake was not related. Inside the function to fetch a specific mail, instead of fetching it from the label in question, I was always fetching from the inbox.
So in this case, for the inbox I would obtain a list of UIDs, then use this function to successfully obtain each mail in specific, but in the case of a specific GMail label I would obtain the list of UIDs, then fail to fetch each specific mail because I was performing select on the inbox.
Changed the select from the inbox to the specific label inside the function that fetches each specific mail, and now works perfect.

IMAP UIDs are unique per mailbox. If you just created the mailbox, 1 and 2 are almost certainly the correct UIDs for those two messages. Why do you think they aren't?

Related

How to send an e-mail if a particular word is present in subject using Python

I have sent an automated e-mail using python with the following subject: "Daily Test Results are completed and please find the Test Results". Once this is done, I should wait and search for the reply for this email with the following subject: "Daily Test Results are completed and please find the Test Results - CHECK OK/VERIFIED/SEND/PASS_SEND"
Once I receive the mail with the new subject, then through python I must share the original mail with the first subject to multiple users.
I am not sure on two points:
For subject ending with CHECK OK/VERIFIED/SEND/PASS_SEND, I am not able to give a list and instead give only one keyword
If I give one keyword and search, I am able to get the mail, but I am not sure how to share the original mail to remaining participants
Can some one please help me with this
Code:
import win32com.client as win32
from datetime import date
def mail_check():
Outlook = win32.Dispatch('Outlook.Application').GetNameSpace('MAPI')
inbox = Outlook.GetDefaultFolder(6)
sub = [message for message in inbox.Items if 'CHECK OK' in message.Subject]
for mssg in sub:
print(mssg)
if __name__ == '__main__':
mail_check()

Python IMAP - Read Gmail with '+' in email address

I've previously used imaplib in Python 3to extract emails from gmail. However I would want to generate a script to differentiate emails to the same address with different strings after a plus sign. For example, the base email address can be:
example#gmail.com
Then I would want to separately read all emails with the addresses:
example+test1#gmail.com,
example+test2#gmail.com,
example#gmail.com.
Therefore I would wind up with a dictionary of lists containing the specific emails. This only works for example#gmail.com. For example:
{'example':[],
'example_test':[],
'example_test2':[]}
Currently I can retrieve the emails that I need with this function from a class:
def get_emails(self):
"""Retrieve emails"""
self.M = imaplib.IMAP4_SSL(self.server)
self.M.login(self.emailaddress,self.password)
self.M.select(readonly=1)
self.M.select('INBOX', readonly=True)
#Yesterdays date
date = (datetime.date.today() - datetime.timedelta(self.daysback)).strftime("%d-%b-%Y")
print("Selecting email messages since %s" % date)
#Retrieve all emails from yesterday on
result,data = self.M.uid('search', None, '(SENTSINCE {date})'.format(date=date))
return result,data
You should directly use the exact mail address you want in the IMAP search request. For example it could be something like :
result,data = self.M.uid('search', None, '(SENTSINCE {date})'.format(date=date),
('TO example+test1#gmail.com'))

how to sign request tokens?

I am currently trying to write a script to send off a request token, I have the header, and the claimset, but I don't understand the signature! OAuth requires my private key to be encrypted with SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function), but the closest I could find was RSAES-PKCS1-v1_5 (has RSA, and the SHA-256 hash). I followed the example, and tweaked it, so I could get it set, but heres my dillema:
signature = ""
h = SHA.new (signature)
key = RSA.importKey(open('C:\Users\Documents\Library\KEY\My Project 905320c6324f.json').read())
cipher = PKCS1_v1_5.new(key)
ciphertext = cipher.encrypt(message+h.digest())
print(ciphertext)
I'm a bit lost, the JSON file I was given has both public key, and private, do I copy and paste the private key into the signature variable (it gave me a invalid syntax)? Or do I past the directory again? I am so lost, and way over my head haha. I am currently running Python 3.4, with pyCrypto for the signature.
Based on what you've said below about wanting to write a command system using gmail, I wrote a simple script to do this using IMAP. I think this is probably simpler than trying to use Google APIs for a single user, unless you were wanting to do that simply for the exercise.
import imaplib, logging
from time import sleep
USERNAME = 'YOUR_USERNAME_HERE' # For gmail, this is your full email address.
PASSWORD = 'YOUR_PASSWORD_HERE'
CHECK_DELAY = 60 # In seconds
LOGGING_FORMAT = '%(asctime)s %(message)s'
logging.basicConfig(filename='imapTest.log', format=LOGGING_FORMAT, level=logging.INFO)
logging.info("Connecting to IMAP server...")
imap = imaplib.IMAP4_SSL('imap.gmail.com')
imap.login(USERNAME, PASSWORD)
logging.info("Connected to IMAP server.")
def get_command_messages():
logging.info("Checking for new commands.")
imap.check()
# Search the inbox (server-side) for messages containing the subject 'COMMAND' and which are from you.
# Substitute USERNAME below for the sending email address if it differs.
typ, data = imap.search(None, '(FROM "%s" SUBJECT "COMMAND")' %(USERNAME))
return data[0]
def delete_messages(message_nums):
logging.info("Deleting old commands.")
for message in message_nums.split():
imap.store(message, '+FLAGS', '\\DELETED')
imap.expunge()
# Select the inbox
imap.select()
# Delete any messages left over that match commands, so we are starting 'clean'.
# This probably isn't the nicest way to do this, but saves checking the DATE header.
message_nums = get_command_messages()
delete_messages(message_nums)
try:
while True:
sleep(CHECK_DELAY)
# Get the message body and sent time. Use BODY.PEEK instead of BODY if you don't want to mark the message as read, but we're deleting it anyway below.
message_nums = get_command_messages()
if message_nums:
# search returns space-separated message IDs, but we need them comma-separated for fetch.
typ, messages = imap.fetch(message_nums.replace(' ', ','), '(BODY[TEXT])')
logging.info("Found %d commands" %(len(messages[0])))
for message in messages[0]:
# You now have the message body in the message variable.
# From here, you can check against it to perform commands, e.g:
if 'shutdown' in message:
print("I got a shutdown command!")
# Do stuff
delete_messages(message_nums)
finally:
try:
imap.close()
except:
pass
imap.logout()
If you're set on using the Gmail API, though, Google strongly encourage you to use their existing Python library rather than attempt to do full authentication etc. yourself as you appear to be. With that, it should - more or less - be a case of replacing the imap calls above with the relevant Gmail API ones.

Get sender email address with Python IMAP

I have this python IMAP script, but my problem is that, every time I want to get the sender's email address, (From), I always get the sender's first name followed by their email address:
Example:
Souleiman Benhida <souleb#gmail.com>
How can i just extract the email address (souleb#gmail.com)
I did this before, in PHP:
$headerinfo = imap_headerinfo($connection, $count)
or die("Couldn't get header for message " . $count . " : " . imap_last_error());
$from = $headerinfo->fromaddress;
But, in python I can only get the full name w/address, how can I get the address alone? I currently use this:
typ, data = M.fetch(num, '(RFC822)')
mail = email.message_from_string(data[0][1])
headers = HeaderParser().parsestr(data[0][1])
message = parse_message(mail) #body
org = headers['From']
Thanks!
Just one more step, using email.utils:
email.utils.parseaddr(address)
Parse address – which should be the value of some address-containing field such as To or Cc – into its constituent realname and email address parts. Returns a tuple of that information, unless the parse fails, in which case a 2-tuple of ('', '') is returned.
Note: originally referenced rfc822, which is now deprecated.
to = email.utils.parseaddr(msg['cc'])
This works for me.
My external lib https://github.com/ikvk/imap_tools
let you work with mail instead read IMAP specifications.
from imap_tools import MailBox, A
# get all emails from INBOX folder
with MailBox('imap.mail.com').login('test#mail.com', 'pwd', 'INBOX') as mailbox:
for msg in mailbox.fetch(A(all=True)):
print(msg.date, msg.from_, msg.to, len(msg.text or msg.html))
msg.from_, msg.to - parsed addresses, like: 'Sender#ya.ru'
I didn't like the existing solutions so I decided to make a sister library for my email sender called Red Box.
Here is how to search and process emails including getting the from address:
from redbox import EmailBox
# Create email box instance
box = EmailBox(
host="imap.example.com",
port=993,
username="me#example.com",
password="<PASSWORD>"
)
# Select an email folder
inbox = box["INBOX"]
# Search and process messages
for msg in inbox.search(unseen=True):
# Process the message
print(msg.from_)
print(msg.to)
print(msg.subject)
print(msg.text_body)
print(msg.html_body)
# Flag the email as read/seen
msg.read()
I also wrote extensive documentation for it. It also has query language that fully supports nested logical operations.

How to receive mail using python

I would like to receive email using python. So far I have been able to get the subject but not the body. Here is the code I have been using:
import poplib
from email import parser
pop_conn = poplib.POP3_SSL('pop.gmail.com')
pop_conn.user('myusername')
pop_conn.pass_('mypassword')
#Get messages from server:
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]
# Concat message pieces:
messages = ["\n".join(mssg[1]) for mssg in messages]
#Parse message intom an email object:
messages = [parser.Parser().parsestr(mssg) for mssg in messages]
for message in messages:
print message['subject']
print message['body']
pop_conn.quit()
My issue is that when I run this code it properly returns the Subject but not the body. So if I send an email with the subject "Tester" and the body "This is a test message" it looks like this in IDLE.
>>>>Tester >>>>None
So it appears to be accurately assessing the subject but not the body, I think it is in the parsing method right? The issue is that I don't know enough about these libraries to figure out how to change it so that it returns both a subject and a body.
The object message does not have a body, you will need to parse the multiple parts, like this:
for part in message.walk():
if part.get_content_type():
body = part.get_payload(decode=True)
The walk() function iterates depth-first through the parts of the email, and you are looking for the parts that have a content-type. The content types can be either text/plain or text/html, and sometimes one e-mail can contain both (if the message content_type is set to multipart/alternative).
The email parser returns an email.message.Message object, which does not contain a body key, as you'll see if you run
print message.keys()
What you want is the get_payload() method:
for message in messages:
print message['subject']
print message.get_payload()
pop_conn.quit()
But this gets complicated when it comes to multi-part messages; get_payload() returns a list of parts, each of which is a Message object. You can get a particular part of the multipart message by using get_payload(i), which returns the ith part, raises an IndexError if i is out of range, or raises a TypeError if the message is not multipart.
As Gustavo Costa De Oliveir points out, you can use the walk() method to get the parts in order -- it does a depth-first traversal of the parts and subparts of the message.
There's more about the email.parser module at http://docs.python.org/library/email.message.html#email.message.Message.
it also good return data in correct encoding in message contains some multilingual content
charset = part.get_content_charset()
content = part.get_payload(decode=True)
content = content.decode(charset).encode('utf-8')
Here is how I solved the problem using python 3 new capabilities:
import imaplib
import email
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(username, password)
mail.select(readonly=True) # refresh inbox
status, message_ids = mail.search(None, 'ALL') # get all emails
for message_id in message_ids[0].split(): # returns all message ids
# for every id get the actual email
status, message_data = mail.fetch(message_id, '(RFC822)')
actual_message = email.message_from_bytes(message_data[0][1])
# extract the needed fields
email_date = actual_message["Date"]
subject = actual_message["Subject"]
message_body = get_message_body(actual_message)
Now get_message_body is actually pretty tricky due to MIME format. I used the function suggested in this answer.
This particular example works with Gmail, but IMAP is a standard protocol, so it should work for other email providers as well, possibly with minor changes.
if u want to use IMAP4. Use outlook python library, download here : https://github.com/awangga/outlook
to retrieve unread email from your inbox :
import outlook
mail = outlook.Outlook()
mail.login('emailaccount#live.com','yourpassword')
mail.inbox()
print mail.unread()
to retrive email element :
print mail.mailbody()
print mail.mailsubject()
print mail.mailfrom()
print mail.mailto()

Categories