I'm using imaplib and attempting to parse messages from a gmail acct. My code has worked for months, and now all of the sudden it's failing miserably. I have no idea what to attribute this to.
The following works around 1/3 of the time. By 'works', I mean succeeds in printing something other than 'no new messages' when I receive an email. Would anyone have any suggestions of more robust ways to attempt this? Or maybe suggestions of now to configure gmail accounts to get this to work more reliably?
I'm also generally interested if the way I've coded this seems like good practice.
Thanks for any and all help...
def check_email(interval):
while True:
server.select('INBOX')
status, ids = server.search(None, 'UnSeen')
if not ids or ids[0] is '':
print 'no new messages'
else:
print 'found a message; attempting to parse...'
latest_id = ids[0]
status, msg_data = server.fetch(latest_id, '(UID BODY[TEXT])')
raw_data = msg_data[0][1]
char_array = list(raw_data)
print 'message result: ', char_array
time.sleep(interval)
EDIT1: I am now getting the following error:
"imaplib.error: FETCH command error: BAD ['Could not parse command']"
Does anyone know what I can attribute this to? It's apparently a result of the line
status, msg_data = server.fetch(latest_id, '(UID BODY[TEXT])')
EDIT2: I found out that I can log into the gmail account, click on the 'more' tab, then click on 'mark all as read' and all of the sudden the code works as expected. Is there a way to mark all messages as read remotely with imaplib??
Related
trying to delete emails in my yahoo account using imaplib. i'm new to python, figured out most of the code but unable to find anything that works relating to this error.
imap = imaplib.IMAP4_SSL(imap_server)
imap.login(email_address, password)
imap.select("Learn", readonly=False)
con = imaplib.IMAP4_SSL('imap.mail.yahoo.com',993)
con.login(email_address, password)
con.select('Learn',readonly=False)
imap.select('"Learn"', "(UNSEEN)")
for i in '1':
typ, msg_data = imap.fetch('1', '(RFC822)')
for response_part in msg_data:
if isinstance(response_part, tuple):
msg = email.message_from_bytes(response_part[1])
for header in [ 'from' ]:
print('%-8s: %s' % (header.upper(), msg[header]))
imap.store(i, "+FLAGS", "\\Deleted")
#tried commented codes below and same error
#imap.expunge()
#result, data = imap.uid('STORE', str(i) , '+FLAGS', '(\\Deleted)')
#imap.uid('STORE', i, '+X-GM-LABELS', '\\Trash')
con.close()
con.logout()
i get the error below
STORE command error: BAD [b'[CANNOT] STORE failed - Mailbox has read-only access']
any help would be greatly appreciated
imap.select('"Learn"', "(UNSEEN)")
Select does not take a search criterion. The second parameter is “readonly”, so this is the same as:
imap.select('"Learn"', readonly="(UNSEEN)")
Which as a non-empty string is the same as:
imap.select('"Learn"', readonly=True)
Which is why you can’t make any changes to that mailbox. Delete the second parameter:
imap.select('"Learn"')
You appear to be wanting to do a search for unseen messages. Use search for this.
im trying to query in mailbox with python3 imap_tools, cant uderstand how to extract emails with sender email and unseen flag. Trying like this
for msg in [msg for msg in mailbox.fetch(Q(from_=sender, seen=False))]
And always get same error
imap_tools.utils.UnexpectedCommandStatusError: Response status for command "box.search" == "NO", "OK" expected, data: [b'[CANNOT] Unsupported search criterion: FROM "ZOYA1608#YANDEX.RU" UNSEEN']
Can somebody explain where my mistake,
full code below
with open("..\\conf\\conf.yaml", mode="r") as stream:
for conf in yaml.safe_load_all(stream):
with MailBox(conf['host']).login(conf['mailbox'], conf['password']) as mailbox:
for sender in conf['senders']['from']:
for msg in [msg for msg in mailbox.fetch(Q(from_=sender, seen=False))]:
for att in msg.attachments:
with open(get_path(file_name=att.filename, store_paths=conf['store_paths']), 'w+b') as f:
f.write(att.payload)
yandex server support From queries
A(from_="ZOYA1608#YANDEX.RU", seen=False) works, I checked
Do not forget update lib sometimes
Most likely: your server doesn't fully support the IMAP search specification.
Regards, imap_tools author.
I have 2 Gmail accounts, and I use one ("gmail1") to forward all e-mail to the other one ("gmail2"). This works fine, but I discovered recently (after years!) that in fact, Gmail does not forward all my e-mail, but only the e-mail it considers not to be spam. Therefore, checking my spam folder in gmail2 for missing e-mails, I have often blamed senders, when really the e-mail had gotten lost on gmail1. I want to solve this programatically, by regularly checking for spam e-mails in gmail1 and importing them into gmail2 (then I'm fine with gmail2 categorizing as spam or not, as long as the mail makes its way there).
I'm only a very amateur programmer, and so far thanks to some samples from the gmail API docs, I've managed to log in to my accounts, and retrieve some messages corresponding to a query, including for example recent spam e-mails. However, I'm struggling with the "import" concept and how to use it. There are no Python examples for this, and I couldn't solve the problems I'm facing.
Where I am right now:
- if I "get" a message from gmail1 and attempt an import_ call on gmail2, using the message I just retrieved, I get an error because threadId is not allowed
- if I do the same and then "del message['threadId']" then the error becomes error 400 : 'raw' RFC822 payload message string or uploading message via /upload/* URL required. I've seen that there are some situations where upload is required, but I am completely lost as to what I should do to make this work.
Here's what I have so far (sorry for the very hacky style):
# skipping imports
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/gmail.modify']
def getMessage(service, user_id, msg_id):
"""from gmail API examples"""
try:
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
return message
except errors.HttpError as error:
print ('An error occurred:' , error)
def listMessagesMatchingQuery(service, user_id, query=''):
"""from gmail API examples"""
# skipping some code
return messages
def login(accountId):
"""from gmail API examples, adapted to handle 2 accounts"""
# skipping some code
return build('gmail', 'v1', credentials=creds)
def importMessage(service, user_id, msg):
"""my daring attempt at using import, without any Python sample to use as a basis"""
try:
message = service.users().messages().import_(userId=user_id, body=msg).execute()
return message
except errors.HttpError as error:
print ('An error occurred:' , error)
if __name__ == '__main__':
service_gmail = login('gmail2')
service_dnt = login('gmail1')
messages = listMessagesMatchingQuery(service_dnt,"me","in:spam is:unread after:" + str(int((datetime.now() - timedelta(hours=12)).timestamp())))
# this gets me some recent unread spam messages
m=getMessage(service_dnt,"me",messages[0]['id'])
# now I have a full message - I'm just investigating for now so the first message is enough
del m['threadId']
# if I don't do that, the error I get is that threadId is not allowed here, so I remove it
imported = importMessage(service_gmail,"me",m)
# this now gives me error 400 : 'raw' RFC822 payload message string or uploading message via /upload/* URL required
I'd like to find the way to make this work, so that the e-mail appears in gmail2 as if it had been received by gmail2 directly (though I would like to keep the To: address, as I use a catch-all on gmail1 and want to know which e-mail address the e-mail was directed to). But right now, I get only errors about having to use upload; I'm not sure if that's what I really should be doing, and if it is , I have no idea how.
Thanks a lot in advance for any help!
In the end I managed. I had missed the fact that there are 2 ways to "get" messages, a simple one and a "raw" one. With the "raw" way to access the message, I can use the import_ function easily:
message = service_dnt.users().messages().get(userId="me", id=messageId,format='raw').execute()
imported = importMessage(service_gmail,"me",{'raw': message['raw']})
whereby the importMessage function is unchanged vs. the original code I posted.
I tried to fetch a message content in the following way
result, data = m.uid('fetch', num, "( FLAGS BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)] BODYSTRUCTURE)")
It worked well when I was connecting to a private mail server "mail.example.com"
But it returns exception when I used "imap.gmail.com"
error: UID command error: BAD ['Could not parse command']
I think gmail doesnot support detailed search like HEADER.FIELDS....
So I tried the following option for gmail server and it worked really well
result, data = m.uid('fetch', num, "(FLAGS BODY.PEEK[HEADER] BODYSTRUCTURE)")
I am using this with Gmail's SMTP server, and I would like to search via IMAP for emails either sent to or received from an address.
This is what I have:
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('user', 'pass')
mail.list()
mail.select("[Gmail]/All Mail")
status, email_ids = mail.search(None, 'TO "tech163#fusionswift.com" OR FROM "tech163#fusionswift.com"')
The last line of the error is: imaplib.error: SEARCH command error: BAD ['Could not parse command']
Not sure how I'm supposed to do that kind of OR statement within python's imaplib. If someone can quickly explain what's wrong or point me in the right direction, it'd be greatly appreciated.
The error you are receiving is generated from the server because it can't parse the search query correctly. In order to generate a valid query follow the RFC 3501, in page 49 it is explained in detail the structure.
For example your search string to be correct should be:
'(OR (TO "tech163#fusionswift.com") (FROM "tech163#fusionswift.com"))'
Try to use IMAP query builder from https://github.com/ikvk/imap_tools
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')
Of course you can use all library tools