我有一个使用Twisted Python IRC协议编写的IRC机器人。我希望能够运行命令,同时仍允许该机器人同时侦听和执行其他命令。

例如,假设我有将大型文本文件打印到通道的命令。如果要在命令运行时通过在通道中输入“!stop”来停止该命令,我该怎么做?或假设我要在一个通道中执行“!print largefile”,然后转到另一个通道并键入“!print anotherfile”,并使其在完成第一个文件的打印之前将该文件打印至另一个通道。

我想为此使用线程?我不太确定

编辑(澄清):

def privmsg(self, user, channel, msg):
    nick = user.split('!', 1)[0]
    parts = msg.split(' ')
    trigger = parts[0]
    data = parts[1:]
    if trigger == '!printfile':
        myfile = open('/files/%s' % data[0], 'r')
        for line in myfile:
            line = line.strip('/r/n')
            self.msg(channel, line)
    if trigger == '!stop':
        CODE TO STOP THE CURRENTLY RUNNING PRINTFILE COMMAND


如果我想同时在两个通道中运行!printfile或在运行时停止printfile命令,该怎么办?

最佳答案

您不能中断printfile命令的原因是它包含一个遍历文件全部内容的循环。这意味着privmsg函数将一直运行,直到它已读取并发送了文件中的所有行。只有完成工作,它才会返回。

Twisted是单线程协作多任务系统。一次只能运行一部分程序。在您的irc机器人可以处理来自irc服务器的下一行输入之前,必须返回privmsg

但是,Twisted也擅长处理事件和管理并发。因此,解决此问题的一种方法是使用Twisted中包含的一种工具(而不是for循环)发送文件-该工具可与系统的其余部分配合使用,并允许同时处理其他事件。

这是一个简短的示例(未经测试,并存在一些明显的问题(例如,当两个printfile命令之间的距离太近时,行为不佳),我将不在此处进行修复):

from twisted.internet.task import cooperate

....

def privmsg(self, user, channel, msg):
    nick = user.split('!', 1)[0]
    parts = msg.split(' ')
    trigger = parts[0]
    data = parts[1:]
    if trigger == '!printfile':
        self._printfile(channel, data[0])
    if trigger == '!stop':
        self._stopprintfile()

def _printfile(self, channel, name):
    myfile = open('/files/%s' % (name,), 'r')
    self._printfiletask = cooperate(
        self.msg(channel, line.rstrip('\r\n')) for line in myfile)


def _stopprintfile(self):
    self._printfiletask.stop()


它使用twisted.internet.task.cooperate,它是一个帮助器函数,它接受迭代器(包括生成器)并以与应用程序其余部分协作的方式运行它们。为此,它会迭代几次迭代器,然后运行其他工作,然后返回迭代器,依此类推,直到迭代器耗尽为止。

这意味着即使在发送文件时,也会处理来自irc的新消息。

但是,要考虑的另一点是,irc服务器通常包括泛洪保护,这意味着非常快地向它们发送多条线路可能会使您的机器人断开连接。即使在最佳情况下,irc服务器也可以缓冲线路,并仅将它们缓慢释放到网络上。如果僵尸程序已经发送了线路并且它们位于irc服务器的缓冲区中,您将无法通过告诉僵尸程序停止(因为它已经完成)来阻止它们出现在网络上。进而,由于这个原因,Twisted的irc客户端也具有缓冲,因此即使您调用self.msg,也可能不会实际发送该行,因为irc客户端正在缓冲行,以便避免如此快地发送它们,从而使irc服务器将漫游器从网络上撤下。由于我编写的代码仅处理对self.msg的调用,因此,即使它们都已进入irc客户端的本地缓冲区,您实际上仍可能无法阻止其发送。

解决所有这些问题的一个显而易见的(也许不是理想的)解决方案是通过在其中插入新的延迟来稍微复杂化_printfile中使用的迭代器:

from twisted.internet import reactor
from twisted.internet.task import deferLater

def _printfileiterator(self, channel, myfile):
    for line in myfile:
        self.msg(channel, line)
        yield deferLater(reactor, 2, lambda: None)

def _printfile(self, channel, name):
    myfile = open('/files/%s' % (name,), 'r')
    self._printfiletask = cooperate(self._printfileiterator(channel, myfile))


在这里,我更改了迭代器,以使迭代器中的元素成为deferLater的Deferreds(以前,这些元素都是None,因为这是self.msg的返回值)。

cooperate遇到Deferred时,它将停止在该迭代器上工作,直到触发该Deferred。以这种方式使用的deferLater基本上是协作睡眠功能。它返回一个Deferred,直到2秒后才会触发(然后使用None触发,而cooperate并不特别在意)。触发后,cooperate将继续在迭代器上工作。因此,现在_printfile每两秒钟仅发送一行,这将更容易用stop命令中断。

关于python - Twisted Python IRC Bot-如何在bot运行命令时监听命令?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11752673/

10-12 21:04