How to delete email from gmail using python IMAP? - python

I am trying to read otp from mail and after that I want to delete that email from gmail option. I have no problem in reading email but I am not able to delete mail. I tried some code from stackoverflow. below is my code.
def getOtpMail(vEmail, vPaasword):
connection = imaplib.IMAP4_SSL(IMAP_URL) # stablish connection with IMAP server
try:
connection.login(vEmail, vPaasword) # Login with userid password
except Exception as e:
print(e)
return
loopLock = True
while loopLock:
# fetch
connection.select('"INBOX"', readonly=True)
retCode, messages = connection.search(None, '(UNSEEN)')
print(messages[0])
latest = int(messages[0].split()[-1])
res, msg = connection.fetch(str(latest), "(RFC822)")
for response in msg:
if isinstance(response, tuple):
print('\n------------email--------------\n')
msg = email.message_from_bytes(response[1])
if SENDER_NAME in msg['From'] and KEYWORD in msg['Subject']:
loopLock = False
# fetch required information
for part in msg.walk():
body = part.get_payload()
word_list = body.split()
index = word_list.index('verification')
otp = word_list[index + 3].strip('.')
#delete mail - below two line not working
connection.store(str(latest), '+FLAGS', '"[Gmail]/Trash"')
print(connection.expunge())
return otp
else:
continue
I read documentation and print connection.expunge() so I got response as ('NO', [b'EXPUNGE attempt on READ-ONLY folder (Failure)']) . I think issue I have to establish connection in WRITE mode. I am not sure about it.

In this issue, I opened mail box in readonly mode. Hence my program not able to write and store in IMAP server.
I changed
connection.select('"INBOX"', readonly=True)
to
connection.select('"INBOX"', readonly=False)
also I changed command type and flag type in store method -
connection.store(str(latest), '+FLAGS', '"[Gmail]/Trash"')
to
connection.store(str(latest), '+FLAGS', '\\Deleted')
.

Related

Gmail API throws error while sending email, but still sends email

I create an email with the following.
def createGmailEmailNoAttachments(self, messageBody, subject, toEmail, fromEmail, html=False):
try:
newMessage = MIMEMultipart()
newMessage['to']=toEmail
newMessage['from'] = fromEmail
newMessage['subject'] = subject
if html:
msg= MIMEText(messageBody, 'html')
else:
msg= MIMEText(messageBody)
newMessage.attach(msg)
raw = base64.urlsafe_b64encode(newMessage.as_bytes())
raw = raw.decode()
body = {'raw': raw}
return body
except:
self.GLogger.error("An error was encountered while attempting to create gmail email")
tb = traceback.format_exc()
self.GLogger.exception(tb)
return False
I send an email with the following.
def gmailAPISendEmail(self, message, userID="me"):
try:
service = self.gmailAPIService
self.GLogger.info("Attempting to send email message")
request = service.users().messages().send(userId=userID, body=message)
response = self.executeGmailAPI_withretry(request)
if response is False:
return False
responseID = str(response['id'])
self.GLogger.info("Successfully sent email message with ID (" + responseID +")")
return responseID
except:
self.GLogger.error("Failed to send email message")
tb = traceback.format_exc()
self.GLogger.exception(tb)
return False
Where I execute the request in the function executeGmailAPI_withretry(request)
def executeGmailAPI_withretry(self, request, withHTTPObject = False):
try:
response_valid = False
num_retries = 0
while num_retries < 30:
try:
if withHTTPObject is True:
response = request.execute(http=self.http_toUse)
else:
response = request.execute()
response_valid = True
break
except socket.timeout:
num_retries = num_retries + 1
time.sleep(0.5*num_retries)
except:
self.GLogger.error("An error was encounrtered in executeGmailAPI_withretry")
try:
self.GLogger.error(f"The Method ID : {request.methodId}")
except:
pass
try:
self.GLogger.error(f"The uri : {request.uri}")
except:
pass
tb = traceback.format_exc()
self.GLogger.exception(tb)
num_retries = num_retries + 1
time.sleep(0.5*num_retries)
if response_valid is False:
self.GLogger.error(f"Could not resolve issue in 15 requests [{request}]")
return False
else:
return response
except:
self.GLogger.error("An error was encounrtered in executeGmailAPI_withretry")
tb = traceback.format_exc()
self.GLogger.exception(tb)
return False
The problem that I am encountering is as follows. Sometimes, when I want to send an email with these three functions, socket.timeout errors occur during execution of service.users().messages().send(userId=userID, body=message). My retry function will try to send it up to 30 times with some time delays in between. However, sometimes, when a socket.timeout error occurs, the email is still sent. This can result in several of the same emails being sent. From the code's perspective, only one email was sent, since service.users().messages().send(userId=userID, body=message) ran only once successfully without throwing an error.
So, for example, I had 4 identical emails being received, meaning that at least 3 send attempts had a socket.timeout errors in which Gmail actually did send the email and the 4th (or more) attempt executed without throwing the socket.timeout error.
Why does the Gmail API throw a socket.timeout error while sending an email, but still continue to send the email?
This creates a dilemma in the current situation.
If I handle the errors, then it ensures that emails that truly cannot be sent on the first try will be sent. However, it can result in multiple identical emails being sent, due to the false errors.
If I don't handle the error, then for certain, only one email at most will be sent. However, if the email truly cannot be sent, then it will certainly not be sent.
The ideal solution is that the Gmail API should only throw an error if the email truly cannot be sent.
You might want to try setting longer timeout for it to be in timeout mode. See socket.setdefaulttimeout(timeout).
This will force the script to wait until the specified timeout before raising a timeout exception. This way, you might be able to prevent premature timeout exceptions.
If it still doesn't work, maybe you could file it as a bug on issue tracker.
Reference:
socket.timeout with will cause api client to become unuseable
Socket Timeouts

Botframework send proactive message then string not empty

I have a problem sending proactive messages using the Bot Framework with Python.
First what I need is to get the message body from Outlook, and then the bot must send that as a message to all the chats where it was added.
To do that, first I created a new file and called it Email.py.
To read every incoming message body I simply used while true: and time.sleep()
Here is my code example:
import imaplib, email, getpass
from email import policy
import json
import time
imap_host = 'outlook.office365.com'
imap_user = 'xx#xx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(120)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
print(Message_for_bot)
After I successfully created a program to read and print all incoming messages, I tried to build my bot. I found a proactive message bot on the Internet and used it as an example.
First I thought to just run this file with os in the background, but then my bot wasn't running. So then I tried adding an async function in the bot file but it didn't work. My bot just ignores that function. (Then I found the async functions in activity_handler.py, but I didn't find any that could help me.)
Then I tried adding an on_message_activity function and thought maybe it will start working if I call the bot like "#bot hi" for example in Teams. For that idea I must always run the while cycle and never stop the bot, but then I just get a message, and if there's a new incoming message then the bot doesn't write it anymore, and it's not a solution because if the bot is used for multiple chats then it simply doesn't work this way.
Then I try include my code on on_members_added_activity it seems working on azure test in web chat perfectly, but in teams after 1-2 messages stopping to work.
my code
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
imap_host = 'outlook.office365.com'
imap_user = 'xxxxxx#xxxxxx.com'
# init imap connection
mail = imaplib.IMAP4_SSL(imap_host, 993)
rc, resp = mail.login(imap_user, 'xxxxxx')
while True:
# select only unread messages from inbox
mail.select('Inbox')
status, data = mail.search(None, '(UNSEEN)')
if not data[0].split():
time.sleep(5)
# Bot message variable
Message_for_bot = ''
# for each e-mail messages
for num in data[0].split():
# get a single message and parse it by policy.SMTP (RFC compliant)
status, data = mail.fetch(num, '(RFC822)')
email_msg = data[0][1]
email_msg = email.message_from_bytes(email_msg, policy=policy.SMTP)
# print only message parts that contain text data
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
for line in part.get_content().splitlines():
Message_for_bot += '\n' + line
await turn_context.send_activity(f"{Message_for_bot}")
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity(
"bot starting work..."
)
So maybe it's possible to send a message to wherever the bot is added (it needs to get this information somehow, maybe it's kept in the bot memory) whenever Message_for_bot is not empty.
All help will be appreciated.
As we have discussed some logic has to change
Move your code out of the on_members_added_activity function
Use Proactive concept to send the message
-Vinoth

How to parse credential args to a function handling imaplib protocols

I have a list of emails(mine) that I want to test against a list of passwords(All valid and some none valid of course) using imaplib library. Whenever I test the program ordinarily like in the code below, it works perfectly no errors.
import sys
import imaplib
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap_user = 'username#email'
imap_pass = 'RightPassword'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
login = imap.login(imap_user, imap_pass)
if login:
print login
except imaplib.IMAP4.error as error:
print error
#
But whenever I run the code such as to parsing credentials through a function to handle the authentication protocols such as the following code below, I get an error saying
"LOGIN command error: BAD ['Missing \'"\'']".
I have tried all sort of things I could find using google and non seem to handle it properly.
"""
E-mail Tester
NB: This is for educational purpose only.
"""
import sys
import imaplib
EMAILS_FILE = open('email_list.txt', 'r')
PASSWORD_FILE = open('pass_list.txt', 'r')
SUCCESS_FILE = open('success.txt', 'a')
EMAILS_FILE_LIST = []
def set_check(_emails):
email = str(_emails)
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords)
# connect to host using SSL
imap_host = 'imap.server.com'
imap_port = '993'
imap = imaplib.IMAP4_SSL(imap_host, imap_port)
## login to server
try:
# print "%s%s" % (email,password)
# print "I got here so far"
# sys.exit()
print "Testing <--> E-mail: %s - Password: %s" % (email, password)
login = imap.login("%s","%s" % (email, password))
if login:
print login
print "OK <---> E-mail: %s\nPassword: %s" % (email, password)
except imaplib.IMAP4.error as error:
print error
for emails in EMAILS_FILE:
EMAILS_FILE_LIST.append(emails)
for email_count in range(0, len(EMAILS_FILE_LIST)):
set_check(EMAILS_FILE_LIST[email_count])
I have tried all kind of suggestions I could find on the internet but non has worked thus far.
I expect imap.login to handle the authentication without the mysterious error output
"LOGIN command error: BAD ['Missing \'"\'']"
login = imap.login("%s","%s" % (email, password))
does not work. It throws an error in Python: TypeError: not all arguments converted during string formatting, because you're providing two strings to one %s.
Why don't you just use imap.login(email, password)? It has the same effect as what you're trying to do.
And what does your password file look like? What is it actually sending? Please provide the log line before it crashes. (anonymizing if necessary, but leaving any punctuation in for help diagnosing)
Okay, so I actually got this fixed by removing trail lines from my strings.
email = str(_emails).rstrip()
PASSWORD_FILE.seek(0)
for passwords in PASSWORD_FILE:
password = str(passwords).rstrip()
the error is caused by trail lines in the strings.

How to delete only one, specific message using IMAP in Python

I'm looking for one, specific message, and then, after found, I want to delete it from inbox. Just this one.
My code:
import email
import imaplib
def check_email(self, user, password, imap, port, message):
M = imaplib.IMAP4_SSL(imap, port)
M.login(user, password)
M.select()
type, message_numbers = M.search(None, '(ALL)')
subjects = []
for num in message_numbers[0].split():
type, data = M.fetch(num, '(RFC822)')
msg = email.message_from_bytes(data[0][1])
subjects.append(msg['Subject'])
if message in subjects:
M.store(num, '+FLAGS', '\\Deleted')
else:
raise FileNotFoundError('Ooops!')
M.close()
M.logout()
I want to find and delete only one mail by title, gven in the variable (message).
Can you help me?
You loop over all the messages, then delete the last one (which is what num ends up pointing to after the loop finishes) if any one of the messages has a subject which matches. You probably want to reindent the code so that the check takes place inside the loop, and probably abandon the rest of the loop once you found the one you want.
def check_email(self, user, password, imap, port, message):
M = imaplib.IMAP4_SSL(imap, port)
M.login(user, password)
M.select()
type, message_numbers = M.search(None, '(ALL)')
found = False
for num in message_numbers[0].split():
type, data = M.fetch(num, '(RFC822)')
msg = email.message_from_bytes(data[0][1])
# No need to collect all the subjects in a list
# Just examine the current one, then forget this message if it doesn't match
if message in msg['Subject']:
M.store(num, '+FLAGS', '\\Deleted')
found = True
break
# Don't raise an exception before cleaning up
M.close()
M.logout()
# Now finally
if not Found:
raise FileNotFoundError('Ooops!')

Get new e-mails mail folder multiple times

So I have this class that starts like this:
class emailreader():
def __init__(self, server, port, username, password):
self.imap_server = imaplib.IMAP4_SSL(server, int(port))
self.imap_server.login(username, password)
self.imap_server.select('INBOX')
def _get_raw_emails(self):
messages = []
typ, data = self.imap_server.search(None, 'UnSeen')
for num in data[0].split():
typ, data = self.imap_server.fetch(num, '(RFC822)')
messages.append(data[0][1])
return messages
It's working great for fetching messages like this:
mail = emailreader(server, port, username, password)
emails = mail._get_raw_emails()
But if I send a new message to the e-mail address I can't just run the last line again, the new mail won't show up until I delete the mail object and start all over again. Why is that? Do I have to reset the last search or something?
Edit: I think I found the solution. I had to do a imap_server.check() also...
I found the solution. I had to execute a method that's called check() from my imap object whenever new mails may have arrived. This is my "new" _get_raw_emails() method:
def _get_raw_emails(self):
messages = []
self.imap_server.check() # Fetch new mails since object is created.
typ, data = self.imap_server.search(None, 'UnSeen')
for num in data[0].split():
typ, data = self.imap_server.fetch(num, '(RFC822)')
messages.append(data[0][1])
return messages
It may be because I'm not a native english speaker, but when I looked att the documentation for imaplib it did'nt seem obvious what the check method did. It only says "Checkpoint mailbox on server." So I did'nt thought that it would solve my problem.

Categories

Resources