Скрученный Python: Функции не вызываются должным образом? - PullRequest
3 голосов
/ 25 ноября 2011

У меня проблема с настройкой клиента, который подключается к серверу «дистрибьютора» для отправки определенных данных. Цель сервера - получить данные от клиента и затем отправить эти данные всем подключенным клиентам. Сервер работает без проблем. Основной клиент также должен работать как бот IRC. Вот текстовый пример того, как это должно работать следующим образом:

(IRC) Джон: Привет!

1. IRC-клиент получил сообщение, нам нужно отправить его дистрибьютору.

2. Дистрибьютор должен получить это "Джон: Привет!" и отправьте его всем подключенным клиентам.

3. Если другие клиенты отправляют данные дистрибьютору, который будет транслировать их всем клиентам, IRC-клиент должен по очереди выводить полученные данные в указанный канал

Следующий код является клиентом бота IRC (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)

->

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 ]

4 голосов
/ 27 ноября 2011

Вы должны избегать написания кода блокировки следующим образом:

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 на сервере, обработка данных, а затем передача клиенту через витую .

Кроме того, основная проблема заключается в том, что вы пытаетесь отправить данные без подключения. Когда вы пишете что-то вроде:

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)

Обратите внимание, что конкретный код, который я здесь привожу, является очень грубым подходом. Для поиска экземпляра EchoFactory используется глобальная переменная f. Как и в случае использования большинства глобальных переменных, это делает код немного сложным для понимания. Кроме того, я не добавил никакого кода для обработки событий connectionLost, чтобы очистить атрибут connection. Это означает, что вы можете подумать, что отправляете данные на распределенный сервер, когда соединение уже потеряно. И точно так же нет гарантии, что соединение с распределенным сервером будет создано к тому времени, когда IRC-клиент впервые попытается его использовать, поэтому у вас может быть AttributeError, когда он пытается использовать f.connection.sendData.

Однако их исправление не требует большого скачка. Исправьте использование глобальной переменной так же, как и любые другие - передавая аргументы функциям, сохраняя объекты в виде ссылок на другие объекты и т. Д. Исправьте возможное значение AttributeError, обрабатывая его или не создавая соединение IRC до тех пор, пока вы не создадите распределенное соединение и т. д. И обрабатывать потерянные соединения, сбрасывая значение атрибута в None или некоторый другой страж и обращая внимание на такой случай в коде IRC, прежде чем пытаться использовать распределенное клиентское соединение для отправки каких-либо данных.

0 голосов
/ 26 ноября 2011

TFM никогда не определяется в вашем коде, поэтому я не знаю, в чем здесь заключается сделка.

Другая ошибка заключается в том, что вы создаете экземпляр клиента, но никогда не подключаете его ни к чему, как с reactor.connectTCP(...) или endpoint.connect(...). Атрибут transport будет None до тех пор, пока он не будет установлен чем-либо.

(Для вас было бы полезно придумать более простую версию этого кода, которая является полной и не содержит ненужных деталей, таких как все печатные сообщения журнала. Это затрудняет понимание реальных проблем.)

...