Twisted Python IRC Bot - 如何在bot运行命令时监听命令?

时间:2012-08-01 04:45:49

标签: python twisted irc

我有一个使用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命令,我该怎么办?

1 个答案:

答案 0 :(得分:1)

您无法中断printfile命令的原因是它包含一个文件整个内容的循环。这意味着privmsg函数将一直运行,直到它读取并发送文件中的所有行。只有在完成工作后才会返回。

Twisted是一种单线程协同多任务系统。一次只能运行程序的一部分。在irc服务器的下一行输入可由irc bot处理之前,privmsg必须返回。

但是,Twisted也善于处理事件和管理并发性。因此,这个问题的一个解决方案是使用Twisted(而不是for循环)中包含的工具之一发送文件 - 这是一个与系统其余部分协作并允许同时处理其他事件的工具。 / p>

这是一个简短的例子(未经测试,并且有一些明显的问题(例如当两个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客户端的本地缓冲区,你实际上仍然无法阻止发送行。< / p>

对所有这些问题的一个明显的(可能不是理想的)解决方案是通过在_printfile插入新的延迟来稍微复杂化{/ 1>}中使用的迭代器:

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的延迟(之前,元素都是None,因为这是{{的返回值1}})。

self.msg遇到cooperate时,它会停止在该迭代器上工作,直到Deferred触发为止。以这种方式使用的Deferred基本上是一种合作睡眠功能。它返回一个deferLater,直到2秒后才会触发(然后用Deferred触发,None并不特别关心。在它触发后,cooperate将继续在迭代器上工作。所以现在cooperate每两秒只发送一行,使用停止命令更容易中断。