2 votes

Twisted Python - Client IRC

Première question ici.

Pour un club à l'école, nous travaillons sur la création d'un client IRC en Python et Twisted.

Donc, je prends l'exemple de robot que torsion vous donne. J'ai réussi à le connecter à un canal irc, et il s'enregistre.

Je sais que je dois probablement utiliser 2 threads pour avoir la lecture du serveur et l'entrée simultanée, ce que je peux faire, mais seulement si c'est une entrée en ligne de commande. Il faut savoir que les données du canal sont toujours enregistrées en même temps.

Pour ce faire, j'ai utilisé : d = threads.deferToThread(aSillyBlockingMethod)

Qui appelle mon raw_input() boucle.

Mon problème est que je n'arrive pas à comprendre comment passer de la simple saisie et impression de la ligne de commande à la possibilité d'envoyer des messages au serveur irc pour que d'autres personnes puissent les lire.

Toute aide serait grandement appréciée. Je suis un programmeur python débutant et je ne connais pas beaucoup de choses sur le web, comme les protocoles, les ports et autres, mais je les apprends petit à petit. Si quelqu'un connaît un moyen plus facile de faire cela, faites-le moi savoir, s'il vous plaît, je ne suis pas obligé d'utiliser Twisted.

Voici mon code, ou plutôt le bot modifié que je suis en train de bricoler :

# 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

4voto

Jean-Paul Calderone Points 27680

Je sais que je dois probablement utiliser 2 threads pour avoir la lecture du serveur et l'entrée simultanée, ce que je peux faire, mais seulement si c'est une entrée en ligne de commande. Il faut savoir que les données du canal sont toujours enregistrées en même temps.

En fait, il n'est pas nécessaire d'utiliser deux threads pour cela. L'une des forces majeures de Twisted est de faire des E/S sans utiliser de threads. Le site question et réponse Michael lié à parler du support non-threadé de Twisted pour interagir avec l'entrée et la sortie standard par le biais de twisted.internet.stdio.StandardIO .

Mon problème est que je n'arrive pas à comprendre comment passer de la simple saisie et impression en ligne de commande à la possibilité d'envoyer des messages au serveur irc pour que d'autres personnes puissent les lire.

IRCClient possède une méthode pour envoyer des messages au serveur IRC - l'exemple de code que vous avez inclus dans votre question utilise même déjà cette méthode, IRCClient.msg . Il suffit de l'appeler.

Votre code threadé pourrait être modifié de la manière suivante (encore une fois, les threads ne sont pas nécessaires, mais dans l'intérêt de la sécurité de l'utilisateur, il est possible de les utiliser). juste vous montrant comment envoyer un message à partir de votre code de gestion des entrées, je vais baser cette partie de la réponse sur les fils, afin d'éviter d'autres modifications du code qui pourraient rendre la réponse plus difficile à comprendre. Vous ne pas ont besoin de fils pour le faire) :

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)

Remarquez que tout ce qui se passe ici est que aSillyBlockingMethod appelle une méthode sur LogBot . Le wrapper thread-safe ne serait pas nécessaire si l'on utilisait StandardIO puisque cela élimine le besoin de fils.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X