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

Posted

技术标签:

【中文标题】Twisted Python IRC Bot - 如何在 bot 运行命令时监听命令?【英文标题】:Twisted Python IRC Bot - How to listen for commands while bot is running a command? 【发布时间】:2012-08-01 04:45:49 【问题描述】:

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

【问题讨论】:

这是一种开放式的方式。我可以告诉你,你不需要线程,但你真的从中学到了什么实用的东西吗?尝试问一个更具体的问题。例如,发布一些代码。您尝试编写的无法接受输入的 IRC 机器人在哪里?然后有人可以评论你应该改变什么。见sscce.org 我提出的问题就是我的问题,不管它是否可以回答。 【参考方案1】:

你不能中断你的 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 的延迟(以前,元素都是None,因为这是self.msg 的返回值)。

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

【讨论】:

以上是关于Twisted Python IRC Bot - 如何在 bot 运行命令时监听命令?的主要内容,如果未能解决你的问题,请参考以下文章

Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?

Twisted IRC Bot 与本地主机的连接反复丢失

Irc-bot 使用扭曲与期刊印刷

Python/Twisted IRC 机器人日志记录问题

Python twisted irc - 登录时的服务器密码

Python Twisted IRC,发送颜色信息