-
-
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() |
Ok, I've found the bug's root cause. Ubuntu18.*'s pip3 repos don't have the correct pakages for imaplib2, so the "pip3 install imaplib2" couldn't install the correct package. I've changed to the following command to install it:
sudo apt install python3-imaplib2
Then it works fine!!!
Still couldn't pass the first line code, it's still in disaster:
>>> M = imaplib2.IMAP4_SSL("imap-mail.outlook.com")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
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
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.
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
How to install imaplib2 in python3 env? I've installed it as:
pip3 install imaplib2
but when executing:
M = imaplib2.IMAP4_SSL("mail.example.com")
It report error as: