2

TwistedPythonIRCプロトコルを使用して作成した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

一度に2つのチャネルで実行したい場合!printfile、または実行中にprintfileコマンドを停止したい場合は、どうすればよいですか?

4

1 に答える 1

1

printfile コマンドを中断できない理由は、ファイルの内容全体に対するループが含まれているためです。つまり、privmsg関数は、ファイルからすべての行を読み取って送信するまで実行されます。その仕事が終わって初めて戻ってきます。

Twisted は、シングル スレッドの協調マルチタスク システムです。一度に実行できるのは、プログラムの一部だけです。irc サーバーからの次の入力行が irc ボットによって処理される前に、privmsg戻る必要があります。

ただし、Twisted はイベントの処理と並行性の管理にも優れています。したがって、この問題の解決策の 1 つは、(for ループの代わりに) Twisted に含まれるツールの 1 つを使用してファイルを送信することです。このツールは、システムの残りの部分と連携し、その間に他のイベントを処理できるようにするツールです。

以下に簡単な例を示します (テストされておらず、明らかな問題 (2 つの 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 からの新しいメッセージが処理されることを意味します。

ただし、考慮すべきもう 1 つの点は、通常、irc サーバーにはフラッド保護が含まれていることです。これは、多くの回線を非常に迅速に送信すると、ボットが切断される可能性があることを意味します。最良の場合でも、irc サーバーは回線をバッファリングし、それらをネットワーク全体にゆっくりと解放するだけです。ボットがすでに回線を送信しており、回線が irc サーバーのバッファーに残っている場合、ボットに停止するように指示しても、回線がネットワークに表示されないようにすることはできません (既に終了しているため)。さらに、このため、Twisted の irc クライアントにもバッファリング機能があるため、 を呼び出した後でも、実際にself.msgは行が送信されない場合があります。これは、irc サーバーがボットをキックするほど速く送信されるのを避けるために、irc クライアントが行をバッファリングしているためです。ネットワークから。私が書いたコードはを呼び出しself.msgも、すべての行がすでに irc クライアントのローカル バッファに入っている場合は、行の送信を実際に停止できない場合があります。

これらすべての問題に対する明らかな (おそらく理想的ではない) 解決策の 1 つは、 で使用されるイテレータを少し複雑にすることです。そこに_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 from になるようにイテレータを変更しましたdeferLater(以前は、要素は allでした。Noneそれが の戻り値だからですself.msg)。

cooperate遭遇すると、それが発火 Deferredするまでそのイテレータでの作業を停止します。このように使用されるのは、基本的に協調的なスリープ機能です。2 秒が経過するまで発火しないa を返します(その後、特に気にしない で発火します) 。発火後、イテレータの作業を再開します。そのため、2 秒ごとに 1 行だけを送信するようになりました。これは、停止コマンドで中断するのがはるかに簡単になります。DeferreddeferLaterDeferredNonecooperatecooperate_printfile

于 2012-08-02T11:23:22.383 に答える