Twisted Python IRC Bot-如何在机器人运行命令时侦听命令



我有一个IRC bot,它是我使用Twisted Python 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循环)发送文件,Twisted是一个与系统其他部分协作并允许同时处理其他事件的工具。

这里有一个简短的例子(未经测试,有一些明显的问题(比如两个打印文件命令靠得太近时的糟糕行为),我不会在这里尝试解决):

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('rn')) 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的Deferred(以前,这些元素都是None,因为这是self.msg的返回值)。

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