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

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

Related

How to delete email from gmail using python IMAP?

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')
.

How do I set up email alerts for 500 errors using Flask?

I am using flask, marshmallow, and sql alchemy to make an API. I want to configure email alerts on 500 errors. The other error types I have the email alerts for work fine.
Error Handling Code
#app.errorhandler(ValidationError)
def handle_marshmallow_validation(err): # except ValidationError as err
return jsonify(err.messages), 400
#app.errorhandler(500)
def server_error(e):
if e == 500:
error_500_email()
Function called in the 500 error handler:
def error_500_email():
s = smtplib.SMTP(host='mailo2.uhc.com', port=25)
text = "There was an error"
msg = MIMEText(str(text))
msg['Subject'] = 'Prod SA Tool Error'
s.sendmail('sa_prod#optum.com', 'ian.christ#optum.com', msg.as_string())
s.quit()
I suspect the e == 500 condition always evaluates to False even in case of 500 error.
You could fix it, but why not remove it? What's the purpose of this test if you decorate the handler with #app.errorhandler(500)?

How to correctly end tornado requests?

I'm using Tornado as an API for a basic request to send an email.
Depending on the sending result, I'd like to finish the request accordingly.
This is what I've done:
def get_routes(tornado_config):
return [
(r"/send", EmailHandler, tornado_config)
]
This is inside EmailHandler, result is the return of sending email:
if result:
self.set_status(200)
self.finish(json.dumps({"status":"ok", "result":result}))
return ''
else:
self.set_status(500)
self.finish(json.dumps({"status": "error", "result":result }))
return ''
The problem is that self.set_status (500) doesn't seem to add the 500 header.
Other solution would be:
if result:
return "ok"
else:
return "Message not sent"
But this doesn't respect any standard, as it gets returned at a json {"status":"success", "data":false} or something like that, even when the sending of email fails.
Simply write:
self.write({"status":"ok", "result":result})
instead of self.finish.

Run python script at reception of email

I own a shared hosting which can run anacrontab. I would like to run a python script when I receive an email on that server.
Is anacrontab enough?
Or would using a client such as Gmail be better?
import imapclient, pyzmail, html2text
def latestMail():
imapObj = imapclient.IMAPClient('imap.yourServer.com', ssl=False)
imapObj.login('imapUser', 'imapPass')
imapObj.select_folder('Inbox', readonly=False)
UIDs = imapObj.search(criteria='ALL', charset=None)
rawMessages = imapObj.fetch(UIDs[0], ['BODY[]', 'FLAGS'])
message = pyzmail.PyzMessage.factory(rawMessages[UIDs[0]]['BODY[]'])
return message
def parser(message):
if message.text_part is not None and message.html_part is not None:
multipart = True
else:
multipart = False
if message.text_part is not None:
try:
body = message.text_part.get_payload().decode(message.text_part.charset)
except TypeError:
body = message.text_part.get_payload()
if message.html_part is not None and multipart is False:
try:
body = html2text.html2text(message.html_part.get_payload().decode(message.html_part.charset))
except Exception:
raise Systemexit
return body
try:
message = latestMail()
clean = parser(message)
print clean
except IndexError:
print "No messages left"
raise os._exit(0)
except Exception as e:
print e
Crontab config:
HOME=/var/www/html/whatever
* * * * * root /var/www/html/whatever/myMailChecker.py
Conclusion:
This will call your imap servers' Inbox every minute and parse trough your mail and parse it's content, you can do whatever you want after like create a new entry in your mysql table with the mail content etc.. or run another script if clean is not None etc.

How to get response on error in SUDS?

If i have some bad authorization data (for example wrong password) SUDS rises exception (400, u'Bad Request') from which i cant get anything, but in teh log is response, which contains data that password is wrong, but how to get this response? I tried like this:
except Exception as e:
print str(e)
print self._client.last_received()
It prints:
(400, u'Bad Request')
None
But in log there is long xml which contains <SOAP-ENV:Reason><SOAP-ENV:Text xml:lang="en">Sender not authorized</SOAP-ENV:Text></SOAP-ENV:Reason>
I am pulling this out of a comment and into an answer because of the code block.
import suds.client
try:
auth_url = "https://url.to.my.service/authenticator?wsdl"
auth_client = suds.client.Client(auth_url)
cookie = auth_client.service.authenticate(user,password)
except Exception as e:
print str(e)
print auth_client.last_received()
Using this code, I receive the appropriate response from my service if I pass an invalid password:
Server raised fault: 'error.pwd.incorrect'
None
And an appropriate response if I pass an invalid user id:
Server raised fault: 'error.uid.missing'
None
Something you may want to consider doing, is changing your except statement to catch suds.WebFault instead of the generic exception. There may be something else that is occurring and triggering your exception block.
One other thing that may help with your issue, is to pass faults=True in your Client() call.
The Client can be configured to throw web faults as WebFault or to
return a tuple (, )
The code I posted above would look like this:
auth_client = suds.client.Client(auth_url, faults=True)

Categories

Resources