3

我正在使用的下一个脚本用于使用 IMAP IDLE 侦听 IMAP 连接,并且严重依赖于线程。对我来说消除踏板调用并只使用主线程的最简单方法是什么?作为一个新的python开发人员,我尝试了编辑def __init__(self, conn):方法,但得到了越来越多的错误

代码示例对我有很大帮助

#!/usr/local/bin/python2.7
print "Content-type: text/html\r\n\r\n";

import socket, ssl, json, struct, re
import imaplib2, time
from threading import *

# enter gmail login details here
USER="username@gmail.com"
PASSWORD="password"
# enter device token here
deviceToken = 'my device token x x x x x'
deviceToken = deviceToken.replace(' ','').decode('hex')
currentBadgeNum = -1

def getUnseen():
    (resp, data) = M.status("INBOX", '(UNSEEN)')
    print data
    return int(re.findall("UNSEEN (\d)*\)", data[0])[0])    

def sendPushNotification(badgeNum):
    global currentBadgeNum, deviceToken
    if badgeNum != currentBadgeNum:
        currentBadgeNum = badgeNum
        thePayLoad = {
             'aps': {
                  'alert':'Hello world!',
                  'sound':'',
                  'badge': badgeNum,
                  },
             'test_data': { 'foo': 'bar' },
             }
        theCertfile = 'certif.pem'
        theHost = ('gateway.push.apple.com', 2195)

        data = json.dumps(thePayLoad)
        theFormat = '!BH32sH%ds' % len(data)
        theNotification = struct.pack(theFormat, 0, 32, 
          deviceToken, len(data), data)

        ssl_sock = ssl.wrap_socket(socket.socket(socket.AF_INET, 
          socket.SOCK_STREAM), certfile=theCertfile)
        ssl_sock.connect(theHost)
        ssl_sock.write(theNotification)
        ssl_sock.close()
        print "Sent Push alert."

# 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!"
        numUnseen = getUnseen()
        sendPushNotification(numUnseen)

# Had to do this stuff in a try-finally, since some testing 
# went a little wrong.....
while True:
    try:
        # Set the following two lines to your creds and server
        M = imaplib2.IMAP4_SSL("imap.gmail.com")
        M.login(USER, PASSWORD)
        M.debug = 4
        # We need to get out of the AUTH state, so we just select 
        # the INBOX.
        M.select("INBOX")
        numUnseen = getUnseen()
        sendPushNotification(numUnseen)

        typ, data = M.fetch(1, '(RFC822)')
        raw_email = data[0][1]

        import email
        email_message = email.message_from_string(raw_email)
        print email_message['Subject']

        #print M.status("INBOX", '(UNSEEN)')
        # Start the Idler thread
        idler = Idler(M)
        idler.start()


        # Sleep forever, one minute at a time
        while True:
            time.sleep(60)
    except imaplib2.IMAP4.abort:
      print("Disconnected.  Trying again.")   
    finally:
        # Clean up.
        #idler.stop() #Commented out to see the real error
        #idler.join() #Commented out to see the real error
        #M.close()    #Commented out to see the real error
        # This is important!
        M.logout()
4

2 回答 2

1

据我所知,这段代码非常混乱,因为作者使用了“imaplib2”项目库,该库强制使用该代码从未使用过的线程模型。

只创建了一个线程,它不需要是线程,但可以选择imaplib2. 但是,正如imaplib2 文档所述:

该模块提供了与标准 python 库模块 imaplib 提供的几乎相同的 API,主要区别在于该版本允许在 IMAP4 服务器上并行执行命令,并实现 IMAP4rev1 IDLE 扩展。(imaplib2 可以在现有客户端中替换 imaplib,无需更改代码,但请参阅下面的警告。)

这使您看起来应该能够扔掉很多东西class Idler而只使用连接M。我建议您在查看官方文档之前先查看Doug Hellman 的优秀 Python Module Of The Week for module imaplib。您需要对代码进行逆向工程以找出其意图,但在我看来,它像:

  1. 打开与 GMail 的连接
  2. 检查收件箱中未查看的邮件
  3. 计算来自 (2) 的未见消息
  4. 向 gateway.push.apple.com 上的某个服务发送虚拟消息
  5. 等待通知,转到(2)

这段代码最有趣的地方可能是它似乎没有做任何事情,尽管sendPushNotification(第 4 步)所做的是一个谜,并且其中一行使用了 imaplib2 特定服务:

self.M.idle(callback=callback)

使用我在模块文档中看不到的命名参数。你知道这段代码是否真的运行过吗?

除了不必要的复杂性之外,还有另一个放弃的原因imaplib2:它独立存在于sourceforgePyPi上,两年前一位维护者声称“将尝试使其与原始版本保持同步”。你有哪一个?你会安装哪个?

于 2013-05-31T21:49:25.177 回答
0

不要这样做

由于您尝试删除 Thread 使用仅仅是因为您没有找到如何处理来自服务器的异常,因此我不建议删除 Thread 使用,因为库本身的异步性质 - Idler 处理它更多比一个线程更顺利。

解决方案

您需要self.M.idle(callback=callback)用 try-except 包装它,然后在主线程中重新提升它。然后通过在主线程中重新运行代码以重新启动连接来处理异常。

您可以在此答案中找到解决方案的更多详细信息和可能的原因:https ://stackoverflow.com/a/50163971/1544154

完整的解决方案在这里:https ://www.github.com/Elijas/email-notifier

于 2018-05-05T08:29:35.600 回答