2012-08-01 98 views
2

我有一个使用Twisted Python IRC协议编写的IRC bot。我希望能够运行命令,同时仍允许机器人同时监听和执行其他命令。Twisted Python IRC Bot - 如何在bot运行命令时监听命令?

例如,假设我有一个命令将打印一个大文本文件到一个通道。如果我想通过在通道中输入“!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命令,我该怎么办?

+3

这是开放式的。我可以告诉你,你不需要线程,但是你真的从中学到了什么实际用途吗?试着问一个更具体的问题。例如,发布一些代码。你试图写IRC机器人在哪里?*不能*接受输入?然后有人可以评论你应该改变什么。见http://sscce.org/ – 2012-08-01 12:03:54

+0

我提出的问题是我有的问题,不管它是否可以回答。 – user1567543 2012-08-02 01:07:38

+0

我的回答有帮助吗? – 2012-11-11 22:27:32

回答

1

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

Twisted是一个单线程协作式多任务系统。一次只能运行一部分程序。在来自irc服务器的下一行输入可以由irc bot处理之前,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使用的iterator复杂:

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,因为这是self.msg的返回值)的延迟。

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