I am attempting to have a python script that constantly monitors a gmail account for new emails. Using IMAPClient, it opens two imap connections, one in idle mode. Whenever a new message is received, the connection in idle mode tells the other connection that it should fetch new mail. The non-idle connection will fetch the mail, perform some processing, then archive the email.
The problem comes when I have many emails arriving in a short period of time, more than a few in a minute. In this case, I get an AssertionError. Below is a minimal example to reproduce the error. In addition to the imap connection, it also opens an smtp connection so that it can send the emails to itself. It will usually fail with the AssertionError at some point after 5-7 emails have been sent. The AssertionError comes in the call to idle_check.
A few short comments on running the code. It does not use OAuth, and so gmail's must be set to allow less secure apps. The "username" and "password" fields at the bottom of the script must be set. The script will also archive any emails that are currently in the inbox, and so it should not be run on a primary email account.
#!/usr/bin/env python3
import smtplib
import imapclient
import email
import threading
import time
class Server(object):
def __init__(self,username,password):
self.username = username
self.password = password
def start(self):
self.stop_running = threading.Event()
self.has_mail = threading.Event()
threading.Thread(target=self._idle).start()
threading.Thread(target=self._poll).start()
print('Listening for messages now')
def _idle(self):
imap_idle = self.imap_connect()
while not self.stop_running.is_set():
imap_idle.idle()
for i in range(600):
try:
if imap_idle.idle_check(1):
self.has_mail.set()
except AssertionError as e:
self.stop_running.set()
raise
imap_idle.idle_done()
imap_idle.noop()
def _poll(self):
imap_poll = self.imap_connect()
self.process_unread(imap_poll)
while True:
if self.has_mail.wait(1):
self.has_mail.clear()
self.process_unread(imap_poll)
if self.stop_running.is_set():
return
def imap_connect(self):
imap = imapclient.IMAPClient('imap.gmail.com',use_uid=True,ssl=True)
imap.login(self.username,self.password)
imap.select_folder('INBOX')
return imap
def process_unread(self, imap):
imap.select_folder('INBOX')
messages = imap.search()
if messages:
imap.copy(messages,'[Gmail]/All Mail')
imap.delete_messages(messages)
def smtp_connect(self):
smtp = smtplib.SMTP('smtp.gmail.com',587)
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login(self.username,self.password)
return smtp
def send(self,recipient,subject='',body=''):
headers = ['from: ' + self.username,
'subject: ' + subject,
'to: ' + recipient,
'mime-version: 1.0',
'content-type: text/html']
headers = '\r\n'.join(headers)
self.smtp_connect().sendmail(self.username,recipient,headers+'\r\n\r\n'+body)
def main():
username = 'username#gmail.com'
password = 'password'
s = Server(username, password)
s.start()
for i in range(8):
if s.stop_running.is_set():
break
print('Sending message',i)
s.send(username,
'Test Message'.format(i),
'Body {}'.format(i))
time.sleep(15)
if __name__=='__main__':
main()
The error messsage given is as follows. The text variable at the time of error is sometimes b'XISTS' and sometimes b' FLAGS (\\Seen))'.
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
File "/usr/lib/python3.4/threading.py", line 868, in run
self._target(*self._args, **self._kwargs)
File "./emailer.py", line 31, in _idle
if imap_idle.idle_check(1):
File "/path/to/the/venv/lib/python3.4/site-packages/imapclient/imapclient.py", line 519, in idle_check
resps.append(_parse_untagged_response(line))
File "/path/to/the/venv/lib/python3.4/site-packages/imapclient/imapclient.py", line 1093, in _parse_untagged_response
assert text.startswith(b'* ')
AssertionError
I am running this with Python 3.4.0, using IMAPClient 0.13. This is being run in a clean virtualenv, with no other libraries installed. Any assistance would be quite appreciated.
Related
I'm making a simple python chatroom type server, and I am trying to implement server commands. Sending and receiving messages work fine but for some reason when I send a server command It gets received weird. I assume it's how the message is being sent that's causing it to act weird because the server sends server commands separately from how it sends regular messages. Here are the snippets that I think are the problem:
SERVER SIDE:
userData = "server".encode("utf-8")
userHeader = f"{len(userData):<{HEADER_LENGTH}}".encode("utf-8")
print(userHeader)
command = str(command)
messageData = f"cmd${command}".encode("utf-8")
messageHeader = f"{len(command):< {HEADER_LENGTH}}".encode("utf-8")
for client_socket in self.clients:
client_socket.send(userHeader + userData + messageHeader + messageData)
window.logData(f"sent server command {command}")
window.logChat("SERVER",command)
This gets called when a button is pressed and then some input is passed in as the command
Here's how the client receives data:
while True:
try:
username_header = self.client_socket.recv(HEADER_LENGTH)
if(not len(username_header)):
print("connection closed by server")
sys.exit()
print(username_header.decode("utf-8").strip())
username_length = int(username_header.decode("utf-8").strip())
username = self.client_socket.recv(username_lenght).decode("utf-8")
message_header = self.client_socket.recv(HEADER_LENGTH)
message_length = int(message_header.decode("utf-8").strip())
messageRaw = self.client_socket.recv(message_length).decode("utf-8")
type_, message = messageRaw.split("$")
if(type_ == "message"):
GUI.outgoing.insert(END, "")
GUI.incoming.insert(END, f"{username} >> {message}")
elif(type_ == "cmd"):
if(username == "sever"):
print("recieved server command")
except IOError as e:
if(e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK):
print("READ ERR",str(e))
sys.exit()
continue
I'm not getting any errors on the server side but I am on the client side so here's the output:
6
TEST
Exception in thread Thread-1:
Traceback (most recent call last):
File "/home/kali/anaconda3/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/home/kali/anaconda3/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "client.py", line 119, in main
username_lenght = int(username_header.decode("utf-8").strip())
ValueError: invalid literal for int() with base 10: 'TEST'
I'm sending the text "TEST" to see if the server is receiving data, where it prints the length of the username header it prints 6 which is the length "SERVER" but it also prints "TEST" which is where everything goes wrong.
If you want to play around with the code the github page is https://github.com/snakebite-382/Chatty.py/tree/unstable make sure you download the scripts from the unstable branch. The stable branch has the first release which doesn't have a GUI or the ability to send server commands.
I am running the Facebook Thrift service on my machine. I used a sample code to show its working:
import asyncio
from fbnet.command_runner.thrift_client import AsyncioThriftClient
# Import FCR Thrift Types
from fbnet.command_runner_asyncio.CommandRunner import ttypes as fcr_ttypes
# Import FCR Service Client
from fbnet.command_runner_asyncio.CommandRunner.Command import Client as FcrClient
import getpass
# Device Information
hostname = 'my_vm'
username = 'root'
password = getpass.getpass('%s Password: ' % username)
# Destination device
device = fcr_ttypes.Device(hostname=hostname, username=username, password=password)
async def run(cmd, device):
async with AsyncioThriftClient(FcrClient, 'x.x.x.x',22 ) as client:
res = await client.run(cmd, device)
# type of res is `struct CommandResult`
print(res.output)
loop = asyncio.get_event_loop()
loop.run_until_complete(run('uname -a', device))
However I am getting the following error:
Frame size 1397966893 too large for THeaderProtocol Traceback (most
recent call last):
File "pgm1.py", line 28, in
loop.run_until_complete(run('uname -a', device))
File "/usr/local/lib/python3.6/asyncio/base_events.py", line 467, in run_until_complete
return future.result()
File "pgm1.py", line 23, in run
res = await client.run(cmd, device) thrift.transport.TTransport.TTransportException: Connection closed
Any ideas on how to correct this?
#Kenster's comment indicates the real problem here.
0x5353482D is the four characters "SSH-", which happens to be the first data sent by an ssh server when something connects to it
There are some server implementations that require TFramedProtocol by design. In other words, it is mandatory, the client has to use it, simply because the server expects it that way.
The insight comes quickly to one who knows that TFramedProtocol adds a 4 byte header carrying the frame size of the data to follow. If the client does not use TFramedProtocol, the server will interpret the first four databytes as the frame size - hence the error message.
Solution
Add TFramedProtocol on the client side to the Thrift transport/protocol stack.
The following code just returns the output: "Failed to open WebSocket" Please help me correct it. I suspect the ws_url might be faulty, but still unsure.
from ws4py.client.threadedclient import WebSocketClient
import base64, time
class SpeechToTextClient(WebSocketClient):
def __init__(self):
ws_url = "wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize"
username = "your username"
password = "your password"
auth_string = "%s:%s" % (username, password)
base64string = base64.b64encode(auth_string.encode())
try:
WebSocketClient.__init__(self, ws_url,
headers=[("Authorization", "Basic %s" % base64string)])
self.connect()
except: print("Failed to open WebSocket.")
def opened(self):
self.send('{"action": "start", "content-type": "audio/l16;rate=16000"}')
def received_message(self, message):
print(message)
stt_client = SpeechToTextClient()
time.sleep(3)
stt_client.close()
Error message is provided below:ยจ
Here is the full error message:
Traceback (most recent call last):
File "C:\Users\Vetle\Desktop\Python projects\problem.py", line 27, in
stt_client.close()
File "C:\Users\Vetle\AppData\Local\Programs\Python\Python35\lib\site-packages\ws4py\client__init__.py", line 205, in close
self._write(self.stream.close(code=code, reason=reason).single(mask=True))
File "C:\Users\Vetle\AppData\Local\Programs\Python\Python35\lib\site-packages\ws4py\websocket.py", line 283, in _write
raise RuntimeError("Cannot send on a terminated websocket")
RuntimeError: Cannot send on a terminated websocket
As per the API documentation you need to pass in an authentication token and not the basic service credentials. The Documentation has sample web socket code - https://www.ibm.com/watson/developercloud/speech-to-text/api/v1/#recognize_audio_websockets
I am trying to send an email using SMTP via python.
This is my code:
import smtplib
from email.mime.text import MIMEText
textfile='msg.txt'
you='ashwin#gmail.com'
me='ashwin#gmail.com'
fp = open(textfile, 'rb')
msg = MIMEText(fp.read())
fp.close()
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
print "reached before s"
s = smtplib.SMTP('127.0.0.1',8000)
print "reached after s"
s.sendmail(me, [you], msg.as_string())
s.quit()
when I try and execute this code, "reached before s" is printed and then goes into an infinite loop or so, i.e. "reached after s" is not printed and the program is still running.
This is code for the server:
import sys
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
HandlerClass = SimpleHTTPRequestHandler
ServerClass = BaseHTTPServer.HTTPServer
Protocol = "HTTP/1.0"
if sys.argv[1:]:
port = int(sys.argv[1])
else:
port = 8000
server_address = ('127.0.0.1', port)
HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)
sa = httpd.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
httpd.serve_forever()
can someone figure out what is wrong?
I believe yagmail (disclaimer: I'm the maintainer) can be of great help to you.
Its goal is to make it easy to send emails with attachments.
import yagmail
yag = yagmail.SMTP('ashwin#gmail.com', 'yourpassword')
yag.send(to = 'ashwin#gmail.com', subject ='The contents of msg.txt', contents = 'msg.txt')
That's only three lines indeed.
Note that yagmail contents will try to load the string passed. If it cannot, it will send as text, if it can load it, it will attach it.
If you pass a list of strings, it will try to load each string as a file (or, again, just display the text).
Install with pip install yagmail or pip3 install yagmail for Python 3.
More info at github.
Here is a different approach that creates an email sender class with all the error handling taken care of:
import getpass
import smtplib
class EmailBot(object):
# Gets the username and password and sets up the Gmail connection
def __init__(self):
self.username = \
raw_input('Please enter your Gmail address and hit Enter: ')
self.password = \
getpass.getpass('Please enter the password for this email account and hit Enter: ')
self.server = 'smtp.gmail.com'
self.port = 587
print 'Connecting to the server. Please wait a moment...'
try:
self.session = smtplib.SMTP(self.server, self.port)
self.session.ehlo() # Identifying ourself to the server
self.session.starttls() # Puts the SMTP connection in TLS mode. All SMTP commands that follow will be encrypted
self.session.ehlo()
except smtplib.socket.gaierror:
print 'Error connecting. Please try running the program again later.'
sys.exit() # The program will cleanly exit in such an error
try:
self.session.login(self.username, self.password)
del self.password
except smtplib.SMTPAuthenticationError:
print 'Invalid username (%s) and/or password. Please try running the program again later.'\
% self.username
sys.exit() # program will cleanly exit in such an error
def send_message(self, subject, body):
try:
headers = ['From: ' + self.username, 'Subject: ' + subject,
'To: ' + self.username, 'MIME-Version: 1.0',
'Content-Type: text/html']
headers = '\r\n'.join(headers)
self.session.sendmail(self.username, self.username, headers + '''\r\r''' + body)
except smtplib.socket.timeout:
print 'Socket error while sending message. Please try running the program again later.'
sys.exit() # Program cleanly exits
except:
print "Couldn't send message. Please try running the program again later."
sys.exit() # Program cleanly exits
All you need to do is create an EmailBot instance and call the send_message method on it, with the appropriate details as inputs.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Send email with python
I'm trying so send an email with python but when I run the script it take a minute or two then I get this error:
Traceback (most recent call last):
File "./emailer", line 19, in <module>
server = smtplib.SMTP(SERVER)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/smtplib.py", line 239, in __init__
(code, msg) = self.connect(host, port)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/smtplib.py", line 295, in connect
self.sock = self._get_socket(host, port, self.timeout)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/smtplib.py", line 273, in _get_socket
return socket.create_connection((port, host), timeout)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 512, in create_connection
raise error, msg
socket.error: [Errno 60] Operation timed out
This is the script:
#!/usr/bin/env python
import smtplib
SERVER = 'addisonbean.com'
FROM = 'myemail#gmail.com'
TO = ['myemail#gmail.com']
SUBJECT = 'Hello!'
message = """\
Bla
Bla Bla
Bla Bla Bla
Bla Bla Bla Bla
"""
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
I also tried my site IP address as the server but that did the same thing.
Could someone tell me why it does this and how to fix this? Thanks!
Here's the key bit:
return socket.create_connection((port, host), timeout)
socket.error: [Errno 60] Operation timed out
Python's saying: I can't connect to that server, I've tried but it doesn't seem to respond.
Here's the second key bit:
SERVER = 'addisonbean.com'
That's not a mail server, is it?
While addisonbean.com does listen 25 port and answers 220 accra.dreamhost.com ESMTP - you seems to be behind proxy or some kind of firewall. Can you do telnet addisonbean.com 25 from your console?
It looks like you're hosting your page in dreamhost.com, a hosting provider.
When you set up your account, they probably gave you the chance to create email accounts ending with your domain (yaddayadda#addisonbean.com)
You may want to create one, get the information: host where the SMTP (the "mail server") is located, username, password... And you'll have to fill all that in your script.
I would recommend you start testing with another regular account (Gmail.com, Hotmail Outlook.com) and that you read (quite a bit) about what an SMTP server is (which is the server you'll have to talk to in order to have your email sent)
Here's a simple script that should send emails using a gmail account. Fill the information that is shown with asterisks with your data, see if it works:
#!/usr/bin/env python
import traceback
from smtplib import SMTP
from email.MIMEText import MIMEText
smtpHost = "smtp.gmail.com"
smtpPort = 587
smtpUsername = "***#gmail.com"
smtpPassword = "***"
sender = "***#gmail.com"
def sendEmail(to, subject, content):
retval = 1
if not(hasattr(to, "__iter__")):
to = [to]
destination = to
text_subtype = 'plain'
try:
msg = MIMEText(content, text_subtype)
msg['Subject'] = subject
msg['From'] = sender # some SMTP servers will do this automatically, not all
conn = SMTP(host=smtpHost, port=smtpPort)
conn.set_debuglevel(True)
#conn.login(smtpUsername, smtpPassword)
try:
if smtpUsername is not False:
conn.ehlo()
if smtpPort != 25:
conn.starttls()
conn.ehlo()
if smtpUsername and smtpPassword:
conn.login(smtpUsername, smtpPassword)
else:
print("::sendEmail > Skipping authentication information because smtpUsername: %s, smtpPassword: %s" % (smtpUsername, smtpPassword))
conn.sendmail(sender, destination, msg.as_string())
retval = 0
except Exception, e:
print("::sendEmail > Got %s %s. Showing traceback:\n%s" % (type(e), e, traceback.format_exc()))
retval = 1
finally:
conn.close()
except Exception, e:
print("::sendEmail > Got %s %s. Showing traceback:\n%s" % (type(e), e, traceback.format_exc()))
retval = 1
return retval
if __name__ == "__main__":
sendEmail("***#gmail.com", "Subject: Test", "This is a simple test")
Once you have the equivalent information for your domain (smtpHost, smtpPort, smtpUsername...) it MAY work as well (depends on the port they're using, it may be 25, which is the default for non-encrypted connections... or not... You'll have to check with dreamhost.com for that)
Be aware that (since you're using a hosting that probably shares its SMTP server with other people) your "sender" may be yaddayadda#addisonbean.com but the actual information to connect to the dreamhost.com SMTP servers may be different: I'm guessing the 'smtpUsername' may be the username you use to login in your site admin, the 'smtpHost' may change to something like smtp.dreamhost.com or such... That I don't really know.
You have a lot of resources on how to do that.
You also seem to be a designer or photographer... One of those dudes people concern on how things look on the screen and all... Then you may wanna investigate what MiME emails are. You know... so the email is not sent with text only, but you can put fancy HTML in it... You know what I'm sayin'?