2

这里的第一个问题。

因此,对于学校的一个俱乐部,我们正在努力用 Python 和 Twisted 制作一个 IRC 客户端。

因此,我以 twisted 为您提供的示例机器人为例。我已经设法将它连接到 irc 频道,并记录下来。

我知道我可能必须使用 2 个线程来同时从服务器读取和输入,这我可以实现,但前提是它必须是命令行输入。请注意,它仍在同时记录通道中的数据。

为此,我使用了:d = threads.deferToThread(aSillyBlockingMethod)

这叫我的raw_input()循环。

我的问题在于无法弄清楚如何从命令行中输入和打印来改变它;能够实际将消息发送到 irc 服务器以供其他人阅读。

任何帮助将不胜感激。我是一个新手python程序员,不太了解网络东西;像协议、端口和类似的东西,但我正在慢慢地把它们捡起来。如果有人知道更简单的方法,请告诉我,我不承诺使用 Twisted。

这是我的代码,或者更确切地说是我正在修改的修改后的机器人:

# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log
from twisted.internet import threads

# system imports
import time, sys

class MessageLogger:
    """
    An independent logger class (because separation of application
    and protocol logic is a good thing).
    """
    def __init__(self, file):
        self.file = file

    def log(self, message):
        """Write a message to the file."""
        timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time()))
        self.file.write('%s %s\n' % (timestamp, message))
        self.file.flush()

    def close(self):
        self.file.close()


class LogBot(irc.IRCClient):
    """A logging IRC bot."""

    nickname = "twistedbot"

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.logger = MessageLogger(open(self.factory.filename, "a"))
        self.logger.log("[connected at %s]" % 
                        time.asctime(time.localtime(time.time())))

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)
        self.logger.log("[disconnected at %s]" % 
                        time.asctime(time.localtime(time.time())))
        self.logger.close()


    # callbacks for events

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.join(self.factory.channel)

    def joined(self, channel):
        """This will get called when the bot joins the channel."""
        self.logger.log("[I have joined %s]" % channel)

    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        user = user.split('!', 1)[0]
        self.logger.log("<%s> %s" % (user, msg))

        # Check to see if they're sending me a private message
        if channel == self.nickname:
            msg = "It isn't nice to whisper!  Play nice with the group."
            self.msg(user, msg)
            return

        # Otherwise check to see if it is a message directed at me
        if msg.startswith(self.nickname + ":"):
            msg = "%s: I am a log bot" % user
            self.msg(channel, msg)
            self.logger.log("<%s> %s" % (self.nickname, msg))

    def action(self, user, channel, msg):
        """This will get called when the bot sees someone do an action."""
        user = user.split('!', 1)[0]
        self.logger.log("* %s %s" % (user, msg))

    # irc callbacks

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


    # For fun, override the method that determines how a nickname is changed on
    # collisions. The default method appends an underscore.
    def alterCollidedNick(self, nickname):
        """
        Generate an altered version of a nickname that caused a collision in an
        effort to create an unused related name for subsequent registration.
        """
        return nickname + '^'


def aSillyBlockingMethod(self):
        import time
        while True:
            msg = raw_input()
            print msg



class LogBotFactory(protocol.ClientFactory):
    """A factory for LogBots.

    A new protocol instance will be created each time we connect to the server.
    """

    def __init__(self, channel, filename):
        self.channel = channel
        self.filename = filename

    def buildProtocol(self, addr):
        p = LogBot()
        p.factory = self
        return p

    def clientConnectionLost(self, connector, reason):
        """If we get disconnected, reconnect to server."""
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "connection failed:", reason
        reactor.stop()



if __name__ == '__main__':
    # initialize logging
    log.startLogging(sys.stdout)

    # create factory protocol and application
    f = LogBotFactory("#goon.squad.dev", "test.txt")

    # connect factory to this host and port
    reactor.connectTCP("irc.freenode.net", 6667, f)

    #Use this to keep user input open
    d = threads.deferToThread(aSillyBlockingMethod)

    # run bot
    reactor.run()

——泰尔扎拉基

4

1 回答 1

4

我知道我可能必须使用 2 个线程来同时从服务器读取和输入,这我可以实现,但前提是它必须是命令行输入。请注意,它仍在同时记录通道中的数据。

实际上,没有必要为此使用两个线程。Twisted 的一个主要优势是在不使用线程的情况下进行 I/O。Michael 链接的问题和答案讨论了 Twisted 对通过twisted.internet.stdio.StandardIO.

我的问题在于无法弄清楚如何从命令行中输入和打印来改变它;能够实际将消息发送到 irc 服务器以供其他人阅读。

IRCClient有一种向 IRC 服务器发送消息的方法 - 您在问题中包含的示例代码甚至已经使用了这种方法,IRCClient.msg. 您需要做的就是调用它。

您的线程代码可能会像这样更改(再次,线程是不必要的,但为了向展示如何从您的输入处理代码发送消息,我将这部分答案基于线程,以避免对代码的其他更改可能会使答案更难理解。您不需要线程来执行此操作。):

def aSillyBlockingMethod(bot):
    import time
    while True:
        msg = raw_input()
        bot.threadSafeMsg("#bottest", msg)


class LogBot(irc.IRCClient):
    """A logging IRC bot."""

    nickname = "twistedbot"

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.logger = MessageLogger(open(self.factory.filename, "a"))
        self.logger.log("[connected at %s]" % 
                        time.asctime(time.localtime(time.time())))

        # The bot is now connected.  Start reading input here.
        # Pass a reference to this protocol instance, so that
        # messages can be sent to this protocol instance.
        deferToThread(aSillyBlockingMethod, self)


    # Define a helper function for aSillyBlockingMethod to use.
    # Since aSillyBlockingMethod runs in a thread, it cannot just call
    # IRCClient.msg, since that method - like almost all methods in Twisted -
    # is not thread-safe.  Instead it must call this thread-safe wrapper.
    def threadSafeMsg(self, channel, message):
        reactor.callFromThread(self.msg, channel, message)

请注意,这里发生的所有事情aSillyBlockingMethod都是调用 on 上的方法LogBot。使用 时不需要线程安全的包装器StandardIO,因为这消除了对线程的需求。

于 2012-09-21T11:16:40.607 回答