-
-
Save thomaswieland/3cac92843896040b11c4635f7bf61cfb to your computer and use it in GitHub Desktop.
import imaplib2, time | |
from threading import * | |
# This is the threading object that does all the waiting on | |
# the event | |
class Idler(object): | |
def __init__(self, conn): | |
self.thread = Thread(target=self.idle) | |
self.M = conn | |
self.event = Event() | |
def start(self): | |
self.thread.start() | |
def stop(self): | |
# This is a neat trick to make thread end. Took me a | |
# while to figure that one out! | |
self.event.set() | |
def join(self): | |
self.thread.join() | |
def idle(self): | |
# Starting an unending loop here | |
while True: | |
# This is part of the trick to make the loop stop | |
# when the stop() command is given | |
if self.event.isSet(): | |
return | |
self.needsync = False | |
# A callback method that gets called when a new | |
# email arrives. Very basic, but that's good. | |
def callback(args): | |
if not self.event.isSet(): | |
self.needsync = True | |
self.event.set() | |
# Do the actual idle call. This returns immediately, | |
# since it's asynchronous. | |
self.M.idle(callback=callback) | |
# This waits until the event is set. The event is | |
# set by the callback, when the server 'answers' | |
# the idle call and the callback function gets | |
# called. | |
self.event.wait() | |
# Because the function sets the needsync variable, | |
# this helps escape the loop without doing | |
# anything if the stop() is called. Kinda neat | |
# solution. | |
if self.needsync: | |
self.event.clear() | |
self.dosync() | |
# The method that gets called when a new email arrives. | |
# Replace it with something better. | |
def dosync(self): | |
print "Got an event!" | |
# Had to do this stuff in a try-finally, since some testing | |
# went a little wrong..... | |
try: | |
# Set the following two lines to your creds and server | |
M = imaplib2.IMAP4_SSL("mail.example.com") | |
M.login("mylogin","mypassword") | |
# We need to get out of the AUTH state, so we just select | |
# the INBOX. | |
M.select("INBOX") | |
# Start the Idler thread | |
idler = Idler(M) | |
idler.start() | |
# Because this is just an example, exit after 1 minute. | |
time.sleep(1*60) | |
finally: | |
# Clean up. | |
idler.stop() | |
idler.join() | |
M.close() | |
# This is important! | |
M.logout() |
Could you explain in a little more detail what steps you've followed? I'm trying to get imaplib2 working as well and am coming across the same problem. I've followed the steps you've mentioned and am seeing the same error.
Traceback (most recent call last):
File "./test_imap.py", line 48, in <module>
M = imaplib2.IMAP4_SSL(address)
File "/usr/lib/python3/dist-packages/imaplib2.py", line 2192, in __init__
IMAP4.__init__(self, host, port, debug, debug_file, identifier, timeout, debug_buf_lvl)
File "/usr/lib/python3/dist-packages/imaplib2.py", line 400, in __init__
self.welcome = self._request_push(name='welcome', tag='continuation').get_response('IMAP4 protocol error: %s')[1]
File "/usr/lib/python3/dist-packages/imaplib2.py", line 201, in get_response
raise typ(exc_fmt % str(val))
imaplib2.error: IMAP4 protocol error: program error: <class 'TypeError'> - cannot use a bytes pattern on a string-like object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./test_imap.py", line 57, in <module>
idler.stop()
NameError: name 'idler' is not defined
I went to the link and copied imaplib2.py3 as imaplib2.py to the library path. Is that the same path your're referring to by PYTHONPATH?
@jaynery , No, I didn't use pip to install the imaplib2, I just use its project source tree locally, and set the PYTHONPATH to point to its path. I don't rely on the pip installation, because sometimes the pip pakcages' authors couldn't handle the environment dependent factors correctly. Especially these days, the python2 will disappear, and we must switch all python packages from python2 to python3, some authors already don't want to touch their python project which still only support python2. This package, imaplib2 is a good example for this situation.
@clockzhong Thanks so much! Was able to get the library working.
@thomaswieland After an event is received, how would you go about starting the IDLE over again? I have the script set up so that it executes a function from another script. After dosync() is executed the script goes into sleep. Any further events won't trigger the dosync(). Do you have any ideas for a work around?
@jaynery I have written my own, less polished code for my own use case and at the beginning of the equivalent of dosync() I do idle_done(), at the end I do idle()
def get_unseen_messages(self) -> list:
typ, data = self.M.search(None, 'UNSEEN')
if typ == u'OK':
return data[0].split()
raise ValueError("Get unseen messages Error")
def get_message(self, message_number):
typ, data = self.M.fetch(message_number, 'RFC822')
if typ == 'OK':
return email.message_from_bytes(data[0][1])
raise Exception
def dosync(self):
message_numbers = self.get_unseen_messages()
message_numbers.reverse()
for message_number in message_numbers:
mail_obj = self.get_message(message_number)
print(mail_obj)
If you can use a library, this one has built-in IDLE support:
https://imapclient.readthedocs.io/en/2.3.1/advanced.html
Ok, got the root cause of the new error. The pip3 repos also makes serious mistakes on the installing package. It's not for python3, it uses the python2's source codes as the python3's platform.
I've used the source codes provided here:
https://github.com/imaplib2/imaplib2/tree/master/imaplib2
and copied the imaplib2.py3 as imaplib2.py into my PYTHONPATH, then everything works fine.