2

I'm trying to make an IRC bot using the twisted.words.protocols.irc module.

The bot will parse messages from a channel and parse them for command strings.

Everything works fine except when I need the bot to identify a nick by sending a whois command. The whois reply will not be handled until the privmsg method (the method from which I'm doing the parsing) returns.
example:

from twisted.words.protocols import irc

class MyBot(irc.IRClient):

..........

    def privmsg(self, user, channel, msg):
        """This method is called when the client recieves a message"""
        if msg.startswith(':whois '):
            nick = msg.split()[1]
            self.whois(nick)
            print(self.whoislist)

    def irc_RPL_WHOISCHANNELS(self, prefix, params):
        """This method is called when the client recieves a reply for whois"""
        self.whoislist[prefix] = params

Is there a way to somehow make the bot wait for a reply after self.whois(nick)?

Perhaps use a thread (I don't have any experience with those).

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
axujen
  • 49
  • 3

2 Answers2

2

Deferred is a core concept in Twisted, you must be familiar with it to use Twisted.

Basically, your whois checking function should return a Deferred that will be fired when you receive whois-reply.

kirelagin
  • 13,248
  • 2
  • 42
  • 57
  • i think maybe i didn't give this question the correct title, or maybe im not understanding the documentation correctly, irc_RPL_WHOISCHANNELS is the method that returns the data, not whois, however irc_RPL_WHOISCHANNELS will never execute unless if privmsg, the method that calls whois, exits. So to reformat my question, how do i run privmsg and still let irc_RPL_WHOISCHANNELS run at the same time? – axujen Jun 01 '13 at 19:13
  • 1
    OK, let's forget `Deferred`s for now. You could just have a field in your class that will contain a flag “received message, waiting for whois reply” and when you receive a whois reply you'll check this flag. – kirelagin Jun 01 '13 at 19:33
-2

I managed to fix this by running all handler methods as threads, and then setting a field, following kirelagin's suggestion, before running a whois query, and modifying the method that recieves the data to change the field when it recieves a reply. Its not the most elegant solution but it works.

Modified code:

class MyBot(irc.IRClient):
..........

    def privmsg(self, user, channel, msg):
        """This method is called when the client recieves a message"""
        if msg.startswith(':whois '):
            nick = msg.split()[1]

            self.whois_status = 'REQUEST'
            self.whois(nick)
            while not self.whois_status == 'ACK':
                sleep(1)

            print(self.whoislist)

    def irc_RPL_WHOISCHANNELS(self, prefix, params):
        """This method is called when the client recieves a reply for whois"""
        self.whoislist[prefix] = params

    def handleCommand(self, command, prefix, params):
        """Determine the function to call for the given command and call
        it with the given arguments.
        """
        method = getattr(self, "irc_%s" % command, None)
        try:
            # all handler methods are now threaded.
            if method is not None:
                thread.start_new_thread(method, (prefix, params))
            else:
                thread.start_new_thread(self.irc_unknown, (prefix, command, params))
        except:
            irc.log.deferr()

    def irc_RPL_WHOISCHANNELS(self, prefix, params):
        """docstring for irc_RPL_WHOISCHANNELS"""
        self.whoislist[prefix] = params

    def irc_RPL_ENDOFWHOIS(self, prefix, params):
        self.whois_status = 'ACK'
axujen
  • 49
  • 3
  • Oh, `sleep(1)` is a no-no-no-no. – kirelagin Jun 01 '13 at 20:57
  • And running in threads when the whole Twisted is about _avoiding_ threads… okay… – kirelagin Jun 01 '13 at 20:58
  • i know its not the best solution, but its best method i could come up with. But now you know what im trying to do why dont you show me the best way to do it with twisted? and about sleep(1), that was just an example, with the actual bot its higher and also has a timeout so it won't run forever. – axujen Jun 01 '13 at 21:11
  • This is not a solution at all. It is very broken and you should get rid of it immediately. – Jean-Paul Calderone Jun 01 '13 at 21:12
  • how about only threading privmsg with reactor.callInThread? this works just aswell for me since its the one doing the blocking. – axujen Jun 01 '13 at 21:59
  • The whole point of Twisted is to enable asynchronous programming. The solution you came up with blocks, which defeats the purpose of using Twisted. – pcurry Jun 23 '13 at 06:36