Python twisted:函数调用不正确?

时间:2011-11-24 23:01:02

标签: python client twisted irc

我在设置连接到“分发服务器”服务器以发送某些数据的客户端时遇到问题。 服务器的目的是从客户端获取数据,然后将数据发送到所有连接的客户端。服务器没有任何问题。 主客户端也应该作为IRC机器人工作。 这是一个文本示例,说明它应该如何工作:

(IRC)约翰:你好!

1。 IRC客户收到了消息,我们现在需要将其发送给经销商。

2。经销商应该得到这个“约翰:你好!”字符串并将其发送回所有连接的客户端。

3。如果其他客户端向分发服务器发送数据,这将向所有客户端广播,则IRC客户端应该将接收到的数据输出到指定的通道

以下代码是IRC bot客户端(ircbot.py):

import sys
import socket
import time
import traceback
from twisted.words.protocols import irc
from twisted.internet import reactor
from twisted.internet import protocol

VERBOSE = True
f = None

class IRCBot(irc.IRCClient):
    def _get_nickname(self):
        return self.factory.nickname
    nickname = property(_get_nickname)

    def signedOn(self):
        self.msg("NickServ", "id <password_removed>") # Identify the bot
        time.sleep(0.1) # Wait a little...
        self.join(self.factory.channel) # Join channel #chantest
        print "Signed on as %s." % (self.nickname,)

    def joined(self, channel):
        print "Joined %s." % (channel,)

    def privmsg(self, user, channel, msg):
        name = user.split('!', 1)[0]
        prefix = "%s: %s" % (name, msg)
        print prefix
        if not user:
            return
        if self.nickname in msg:
            msg = re.compile(self.nickname + "[:,]* ?", re.I).sub('', msg)
            print msg
        else:
            prefix = ''
        if msg.startswith("!"):
            if name.lower() == "longdouble":
                self.msg(channel, "Owner command") # etc just testing stuff
            else:
                self.msg(channel, "Command")
        if channel == "#testchan" and name != "BotName":
            EchoClient().sendData('IRC:'+' '.join(map(str, [name, msg])))
            # This should make the bot send chat data to the distributor server (NOT IRC server)

    def irc_NICK(self, prefix, params):
        """Called when an IRC user changes their nickname."""
        old_nick = prefix.split('!')[0]
        new_nick = params[0]
        self.msg(, "%s is now known as %s" % (old_nick, new_nick))

    def alterCollidedNick(self, nickname):
        return nickname + '1'

class BotFactory(protocol.ClientFactory):
    protocol = IRCBot    
    def __init__(self, channel, nickname='BotName'):
        self.channel = channel
        self.nickname = nickname

    def clientConnectionLost(self, connector, reason):
        print "Lost connection (%s), reconnecting." % (reason,)
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "Could not connect: %s" % (reason,)


class EchoClient(protocol.Protocol):
    def connectionMade(self):
        pass

    def sendData(self, data):
            self.transport.write(data)

    def dataReceived(self, data):
        if VERBOSE:
            print "RECV:", data
        IRC.msg("#chantest", data)
        #This one should send the received data from the distributor to the IRC channel

        def connectionLost(self, reason):
            print "Connection was lost."

class EchoFactory(protocol.ClientFactory):
    def startedConnecting(self, connector):
        print 'Started to connect.'

    def buildProtocol(self, addr):
        print 'Connected to the Distributor'
        return EchoClient()
    def clientConnectionFailed(self, connector, reason):
        print "Cannot connect to distributor! Check all settings!"
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print "Distributor Lost connection!!"
        reactor.stop()


if __name__ == "__main__":
    IRC = BotFactory('#chantest')
    reactor.connectTCP('irc.rizon.net', 6667, IRC) # Our IRC connection
    f = EchoFactory()
    reactor.connectTCP("localhost", 8000, f) # Connection to the Distributor server
    reactor.run()

以下代码是分发服务器(distributor.py):

(这个工作正常,但可能对进一步参考有用)

from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor


class MultiEcho(Protocol):
    def __init__(self, factory):
        self.factory = factory

    def connectionMade(self):
        print "Client connected:",self
        self.factory.echoers.append(self)
        self.factory.clients = self.factory.clients+1
        #self.transport.write("Welcome to the server! There are currently "+`self.factory.clients`+" clients connected.")

    def dataReceived(self, data):
        print "RECV:",data
        for echoer in self.factory.echoers:
            echoer.transport.write(data)

    def connectionLost(self, reason):
        print "Client disconnected:",self
        self.factory.echoers.remove(self)
        self.factory.clients = self.factory.clients-1

class MultiEchoFactory(Factory):
    def __init__(self):
        self.clients = 0
        self.names = []
        self.echoers = []

    def buildProtocol(self, addr):
        return MultiEcho(self)

if __name__ == '__main__':
    print "Running..."
    reactor.listenTCP(8000, MultiEchoFactory())
    reactor.run()

我希望客户端将所有传入的聊天数据从IRC服务器输出到“分发服务器”服务器,并输出来自“分发服务器”的传入数据。 但是,我得到这样的错误:

对于ircbot.py中的以下行,

EchoClient().sendData('IRC'+' '.join(map(str, [name, msg])))

我收到以下错误:

Joined #chantest.
Longdouble: test
Traceback (most recent call last):
  File "C:\Python\lib\site-packages\twisted\internet\tcp.py", line 460, in doRea
d
    return self.protocol.dataReceived(data)
  File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2277,
in dataReceived
    basic.LineReceiver.dataReceived(self, data.replace('\r', ''))
  File "C:\Python\lib\site-packages\twisted\protocols\basic.py", line 564, in da
taReceived
    why = self.lineReceived(line)
  File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2285,
in lineReceived
    self.handleCommand(command, prefix, params)
--- <exception caught here> ---
  File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 2329,
in handleCommand
    method(prefix, params)
  File "C:\Python\lib\site-packages\twisted\words\protocols\irc.py", line 1813,
in irc_PRIVMSG
    self.privmsg(user, channel, message)
  File "C:\Python\Traance\kwlbot\ircbot.py", line 51, in privmsg
    EchoClient().sendData('IRC'+' '.join(map(str, [name, msg])))
  File "C:\Python\Traance\kwlbot\ircbot.py", line 90, in sendData
    self.transport.write(data)
exceptions.AttributeError: 'NoneType' object has no attribute 'write'

在同一个ircbot.py

中这一行也是如此
IRC.msg("#chantest", data)

- &GT;

RECV: Hello from Distributor Server
Traceback (most recent call last):
  File "C:\Python\Traance\kwlbot\ircbot.py", line 96, in dataReceived
    IRC.msg("#chantest", data)
AttributeError: BotFactory instance has no attribute 'msg'

我做错了什么?如何从IRCbot类调用正确的函数使其将数据发送到分发服务器,并从分发服务器接收的数据通过IRC在指定的通道中输出?

欢迎任何建议和可能的解决方案。

如果我错过了其他任何细节,请告诉我。

感谢您的时间!

2 个答案:

答案 0 :(得分:4)

你应该避免编写这样的阻止代码:

def signedOn(self):
    self.msg("NickServ", "id <password_removed>") # Identify the bot
    time.sleep(0.1) # Wait a little...
    self.join(self.factory.channel) # Join channel #chantest
    print "Signed on as %s." % (self.nickname,)

有关详细信息,请参阅Tail -f log on server, process data, then serve to client via twisted

除此之外,这里的主要问题是您尝试在没有连接的情况下发送数据。当你写下这样的东西时:

EchoClient().sendData('IRC'+' '.join(map(str, [name, msg])))

您正在创建一个协议实例,负责处理连接,然后尝试使用它,但您创建连接。发送数据的尝试失败,因为协议尚未附加到任何传输。

您的代码段已经演示了创建连接的正确方法,实际上是两次:

IRC = BotFactory('#chantest')
reactor.connectTCP('irc.rizon.net', 6667, IRC) # Our IRC connection
f = EchoFactory()
reactor.connectTCP("localhost", 8000, f) # Connection to the Distributor server

错误是创建一个新的EchoClient实例,一个没有连接的实例。 reactor.connectTCP调用会创建一个新连接和一个新的EchoClient实例,并将它们相互关联。

您希望使用工厂创建的EchoClient().sendData(...)实例,而不是EchoClient

def buildProtocol(self, addr):
    print 'Connected to the Distributor'
    return EchoClient()

您的buildProtocol实现会创建实例,但缺少的只是保存实例,以便您的IRC机器人可以使用它。

考虑这样的事情:

def buildProtocol(self, addr):
    print 'Connected to the Distributor'
    self.connection = EchoClient()
    return self.connection

然后,您的IRC客户端可以使用已保存的EchoClient实例:

    if channel == "#testchan" and name != "BotName":
        f.connection.sendData('IRC:'+' '.join(map(str, [name, msg])))
        # This should make the bot send chat data to the distributor server (NOT IRC server)

请注意,我在这里给出的具体代码是一种非常粗略的方法。它使用全局变量f来查找EchoFactory实例。与大多数全局变量使用一样,这使代码有点难以理解。此外,我还没有添加任何代码来处理connectionLost事件以清除connection属性。这意味着当连接已经丢失时,您可能会认为您正在向分布式服务器发送数据。同样地,无法保证在IRC客户端首次尝试使用它时创建与分布式服务器的连接,因此在尝试使用AttributeError时可能会有f.connection.sendData

然而,修复这些并不需要太大的飞跃。通过将参数传递给函数,将对象保存为对其他对象的引用等来修复全局变量的使用,等等。通过处理它来修复可能的AttributeError,或者直到你之后不创建IRC连接。已创建分布式连接等。通过将属性值重置为None或其他一些标记来处理丢失的连接,并在尝试使用分布式客户端连接发送任何内容之前在IRC代码中注意这种情况数据

答案 1 :(得分:0)

TFM永远不会在您的代码中定义,所以我不知道这笔交易是什么。

另一个错误是您正在实例化客户端,但从未将其连接到任何内容,例如reactor.connectTCP(...)endpoint.connect(...)transport属性将为None,直到由某事设置为止。

(对你来说这个代码的更简单版本是完整的并且不包括所有打印日志消息等不必要的细节会很有帮助。这使得查看真正的问题变得更加困难。)