扭曲的Python的IRC机器人 - 如何监听命令,而机器人运行命令?(Twisted Python

2019-09-20 14:06发布

我有一个IRC bot,我使用了Python的扭曲IRC协议中写道。 我希望能够运行命令,同时仍然允许机器人倾听并同时执行其他命令。

举例来说,假设我有命令,将打印大量文本文件的通道。 如果我想通过输入停止命令,而它运行!“停止”进入通道,我怎么能做到这一点? 还是让说,我想在一个频道做“!打印largefile”,然后去一个不同的频道和类型“!打印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命令在运行时,我该怎么办?

Answer 1:

你不能打断你的printfile命令的原因是,它包含了一个文件的全部内容的循环。 这意味着privmsg功能将运行至具有读取和发送文件中的所有行。 它仅完成后的工作将其返回。

Twisted是一个单线程的协作式多任务系统。 只有一个你的计划的一部分,可以同时运行。 之前输入的来自IRC服务器的下一行可以通过你的IRC bot的处理, privmsg必须返回。

然而,扭曲还善于处理事件和管理并发性。 所以,一个解决这个问题是发送使用包含在扭曲的工具之一文件(而不是一个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客户端的本地缓存。

一个明显的(也许不是理想的)解决所有这些问题是稍微使用的iterator复杂_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))

在这里,我已经改变了迭代器,这样说出来它的元素是Deferreds从deferLater (以前的元素都None ,因为这是的返回值self.msg )。

cooperate遇到一个Deferred ,它停止工作对迭代器,直到后Deferred火灾。 deferLater以这种方式使用基本上是一个合作的睡眠功能。 它返回一个Deferred ,这将不会触发直到凌晨2秒钟后(然后将它与触发None ,其cooperate并不特别在意)。 它触发后, cooperate将继续在迭代器工作虽然。 所以,现在_printfile只发送每两秒钟一个线,东西,这将是更容易与停止命令中断。



文章来源: Twisted Python IRC Bot - How to listen for commands while bot is running a command?