Twisted Python - IRC客户端

时间:2012-09-21 05:45:48

标签: python client twisted irc

这里的第一个问题。

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

所以我采用扭曲的示例机器人给你。我已经设法将它连接到irc频道,然后记录。

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

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

调用我的raw_input()循环。

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

非常感谢任何帮助。我是一个新手python程序员,不知道太多web内容;像协议,端口和类似的东西,但我正在慢慢地接受它们。如果有人知道更简单的方法,请告诉我,我并不致力于使用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()

- TyrZaraki

1 个答案:

答案 0 :(得分:4)

  

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

实际上,没有必要为此使用两个线程。 Twisted的一个主要优势是在不使用线程的情况下进行I / O. question and answer Michael linked to讨论了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正在调用LogBot上的方法。使用StandardIO时不需要线程安全包装器,因为这样就不需要线程了。