How to fetch last 10 messages from IMAP server? - python

I use imaplib2 library to search the last 10 messages with such command:
imap_client.search(None, '{}:{}'.format(last_uid, last_uid - 9))
But to get last_uid I need exec every time command like this:
imap_client.select("INBOX", readonly=True)
to get last UID.
Are the any ways to:
get last UID without select() command fetch last 10 messages
without last UID. Maybe there are any search criterias like 'LAST' or '-10:'?
I can not exec command like this client.search(None, 'ALL'), because IMAP server have more than 50K messages.

For any future travelers who seek an answer to this, I came up with code from the hint given by #arnt.
svr = imaplib.IMAP4_SSL(server)
if svr.login(user=user, password=password):
print('User ' + user + ' logged in successfully.')
else:
print('Login for the user ' + user + " was denied. Please check your credentials.")
x = svr.select('inbox', readonly=True)
num = x[1][0].decode('utf-8')
#from here you can start a loop of how many mails you want, if 10, then num-9 to num
resp, lst = svr.fetch(num, '(RFC822)')
body = lst[0][1]
email_message = email.message_from_bytes(body)
For me this was quite handy as I was accessing an email with more than 67000 emails in it.

You can get the last UID using the STATUS (UIDNEXT) command. However, you have to select the mailbox in order to retrieve messages, and when you issue SELECT, you'll get a message count back, which the Python imaplib's select returns.
So all you need is:
(status, response_text) = mailbox.select("inbox")
# response_text usually contains only one bytes element that denotes
# the message count in an ASCII string
message_count = int(response_text[0].decode("ascii"))
and then you can fetch the messages by index from message_count - 9 through message_count.
Note that messages are index starting at one.

Related

How to take multiple inputs on Twilio-Whatsapp?

I'm using Twilio with Whatsapp, I want to take multiple inputs from users on Whatsapp but I am only able to get the first input.
My first input is usually "hello" from user
then my query
and the second input I want
```
#app.route("/allr", methods=['POST'])
def start():
phonestring = request.form["From"]
phonenumber = int(re.search(r'\d+', phonestring).group(0))
print(phonenumber)
resp = MessagingResponse()
msg1 = request.form.get('Body') #usually hello or something to initialize
l1 = (db.search(User.Phone == phonenumber))
if len(l1)!=0:
aj=l1[0]
print(aj['Preference'])
pref=aj['Preference']
print("Done")
resp.message("{} News".format(pref))
else:
resp.message("Which updates would you like?\n1.National\n2.Regional")
pref = request.form.get('Body') # I need input but I get it as hello(the input above)
print(pref)
resp.message("You selected: {}".format(pref))
Item3 = {'Phone': phonenumber, 'Preference': pref}
db.insert(Item3)
return str(resp)
```
Expected: Regional/National
What I get: Hello
Twilio developer evangelist here.
It appears that when you receive the user's first message that you are not storing anything in the database. So, when you receive their second message, you look up the user by phone number but still don't find anything.
Also, it sounds like you are expecting to receive more than one message within this piece of code. Each webhook request from Twilio corresponds to one incoming message. If you are looking for the response to the second message, it will be in a second webhook.

Python IMAP select multiple folders

I'm trying to get the informations from all the folders but it seems that the code gives me the following error:
command SEARCH illegal in state AUTH, only allowed in states SELECTED
I've googled it but no results for me.
This is the code:
M = imaplib.IMAP4_SSL('',993)
M.login(user,password)
folders = M.list()
for folder in folders[1]:
for allfolders in re.findall('"\/"(.*)',folder):
finalfolders = allfolders.replace(" ",'')
M.select(finalfolders, readonly=True)
print finalfolders
typ, data = M.search(None, 'ALL')
for num in data[0].split():
typ, data = M.fetch(num, '(RFC822)')
email_message = email.message_from_string(data[0][1])
su = email_message['From']
allz = re.findall("<(.*)>",su)
for x in allz:
print x
results.write(x+'\n')
results.flush()
#print su
M.close()
M.logout()
Basically I'm trying to fetch "From", from all the folders founded into my email account.
You can only have one folder selected at any given time using one IMAP connection. This means that your code should EXAMINE or SELECT a mailbox at first, then FETCH whatever you need to download, then do not call CLOSE because it removes messages marked for deletion, and upon entering the next loop iteration, call EXAMINE or SELECT once again, and...

How to delete most recently mail sent in python?

Using python and imaplib, how can I delete the most recently sent mail?
I have this:
mail = imaplib.IMAP4_SSL('imap-mail.outlook.com')
mail.login('MYEMAIL#hotmail.com', 'MYPASS')
mail.select('Sent')
mail.search(None, "ALL") # Returns ('OK', ['1 2 ... N'])
# doubt
Thanks in advance!
You'll need to use the select method to open the appropriate folder with read and write permissions. If you do not want to mark your messages as seen, you need to use the examine method.
The sort command is available, but it is not guaranteed to be supported by the IMAP server. For example, Gmail does not support the SORT command.
To try the sort command, you would replace M.search(None, 'ALL') with M.sort(search_critera, 'UTF-8', 'ALL')
Then search_criteria would be a string like:
search_criteria = 'DATE' #Ascending, most recent email last
search_criteria = 'REVERSE DATE' #Descending, most recent email first
search_criteria = '[REVERSE] sort-key' #format for sorting
According to RFC5256 these are valid sort-key's:
"ARRIVAL" / "CC" / "DATE" / "FROM" / "SIZE" / "SUBJECT" / "TO"
I found a solution that worked for me. After get sent mailbox access I was needing to found a message with fetch() function and then delete the email message with expunge() function. From imaplib documentation:
IMAP4.expunge()
Permanently remove deleted items from selected
mailbox. Generates an EXPUNGE response for each deleted message.
Returned data contains a list of EXPUNGE message numbers in order
received.
My code:
mail = imaplib.IMAP4_SSL('imap-mail.outlook.com')
mail.login('MYEMAIL#hotmail.com', 'MYPASS')
mail.select('Sent')
typ, data = mail.search(None, 'ALL')
control = 0
tam = len(data[0].split())
while control < tam:
typ, data = mail.fetch(tam - control, '(RFC822)')
if str(data).find(msg['Subject']) and str(data).find(msg['To']) != -1:
print "Msg found! ", control + 1, "most recently message!"
mail.store(str(tam - control), '+FLAGS', '\\Deleted')
mail.expunge()
break
control = control + 1
mail.close()
mail.logout()

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.

imaplib.error: command FETCH illegal in state AUTH

I'm trying to download attachments from Gmail, using a combination of pieces of code I found online, and some editing from myself. However, the following code:
import email, getpass, imaplib, os, random, time
import oauth2 as oauth
import oauth2.clients.imap as imaplib
MY_EMAIL = 'example#gmail.com'
MY_TOKEN = "token"
MY_SECRET = "secret"
consumer = oauth.Consumer('anonymous', 'anonymous')
token = oauth.Token(MY_TOKEN, MY_SECRET)
url = "https://mail.google.com/mail/b/"+MY_EMAIL+"/imap/"
m = imaplib.IMAP4_SSL('imap.gmail.com')
m.authenticate(url, consumer, token)
m.select('INBOX')
items = m.select("UNSEEN");
items = items[0].split()
for emailid in items:
data = m.fetch(emailid, "(RFC822)")
returns this error:
imaplib.error: command FETCH illegal
in state AUTH
Why would Fetch be illegal while I'm authorized?
You're lacking error checking on your calls to select. Typically, this is how I'll structure the first parts of a connection to a mailbox:
# self.conn is an instance of IMAP4 connected to my server.
status, msgs = self.conn.select('INBOX')
if status != 'OK':
return # could be break, or continue, depending on surrounding code.
msgs = int(msgs[0])
Essentially, the trouble you're encountering is that you've selected a mailbox that doesn't exist, your status message is probably not "OK" as it should be, and the value you're iterating over isn't valid. Remember, select expects a mailbox name. It does not search based on a flag (which may be what you're attempting with "UNSEEN"). When you select a non-existent mail box you actually get this as a response:
('NO', ['The requested item could not be found.'])
In which case, for email id in items is not operating properly. Not what you're after in any way, unfortunately. What you'd get on a valid mailbox would be like this:
('OK', ['337'])
Hope that helps.
To address the question in comments, if you want to actually retrieve the unseen messages in the mailbox you'd use this:
status, msgs = self.conn.select('INBOX')
# returns ('OK', ['336'])
status, ids = self.conn.search(None, 'UNSEEN')
# returns ('OK', ['324 325 326 336'])
if status == 'OK':
ids = map(int, ids[0].split())
The response is going to be similar to the response from select, but instead of a single integer for the number of messages you'll get a list of ids.
Why would Fetch be illegal while I'm authorized?
IMAP has a state concept.
You can only fetch messages if you have selected a folder.
Here is a nice picture which shows this:
Image source: http://www.tcpipguide.com/free/t_IMAP4GeneralOperationClientServerCommunicationandS-2.htm

Categories