0

As part of a learning project I'm writing a simple IRC bot and I've implemented simple text functions like !auth, !join, !say, etc. In order to make it easy to add functions I've implemented each in python as bot_auth(), bot_join(), bot_say(), etc, with a fallthrough to a bot_NOCOMMAND() that logs an error.

The problem I'm having is that while most functions are found by getattr() as expected the join and part functions are not, and I can't figure out why.

Code:

def process_bot_command(self, prefix, params, trailing):
        # eg: !command some arguments
        try:
            bot_command,bot_args = trailing[1:].split(' ',1)
            bot_command = bot_command.lower().strip()
            bot_args = bot_args.strip()
        except ValueError:
            bot_command = trailing[1:].split(' ')[0].lower().strip()
            bot_args = ''

        try:
            function = getattr(self, 'bot_'+bot_command)
            function(prefix, params[0], bot_command, bot_args)
        except AttributeError:
            self.bot_NOCOMMAND(prefix, params[0], bot_command, bot_args)

def bot_NOCOMMAND(self, c_from, c_to, command, args):
    self.log('Undefined command: %s' % ','.join([c_from, c_to, command, args]), 1)

def bot_auth(self, c_from, c_to, command, args):
    user = self.parsePrefix(c_from)['name']
    if self.authenticate(user, args):
        self.irc_PRIVMSG(user, 'Authentication successful.')
        self.admins.append(c_from)
        self.log('Added %s to active admin list.' % c_from)
    else:
        self.irc_PRIVMSG(user, 'Authentication failed.')

# TODO: doesn't work for some reason
def bot_join(self, c_from, c_to, command, args):
    if not self.isAdmin(c_from):
        self.respond(c_from, c_to, self.str['unauth'])
        return
    channel = args.split(' ')[0].strip()
    self.irc_JOIN(channel)
    self.respond(c_from, c_to, 'Joining %s' % channel)
    
# TODO: doesn't work for some reason
def bot_part(self, c_from, c_to, command, args):
    if not self.isAdmin(c_from):
        self.respond(c_from, c_to, self.str['unauth'])
        return
    channel = args.split(' ')[0].strip()
    if channel in self.subscribed_channels:
        self.irc_PART(channel, 'Leaving channel')
        self.respond(c_from, c_to, 'Left %s' % channel)

def bot_say(self, c_from, c_to, command, args):
    if not self.isAdmin(c_from):
        self.respond(c_from, c_to, self.str['unauth'])
        return
    if args[0] == '#':
        channel,text = args.split(' ', 1)
        self.irc_PRIVMSG(channel, text)
    else:
        self.respond(c_from, c_to, args)

Basic flow:

  1. If the function that handles incoming PRIVMSGs finds a ! in the first character of the trailing data in sends the data to process_bot_command()
  2. process_bot_command() pops off the first portion of the trailing string, drops the !, prepends bot_, and calls getattr() to find a function with that name.
  3. If a function is found, the input is passed along to the function.
  4. If a function is not found, the input is passed to bot_NOCOMMAND() which dumps the input into the log as an error.

Example output:

> Sammitch!~Sammitch@hostname.tld PRIVMSG JerkfaceMcAssbot :!auth password
* Authentication request for user: Sammitch succeeded.
< PRIVMSG Sammitch :Authentication successful.
* Added Sammitch!~Sammitch@hostname.tld to active admin list.
> Sammitch!~Sammitch@hostname.tld PRIVMSG JerkfaceMcAssbot :!identify
< PRIVMSG nickserv :IDENTIFY password
< PRIVMSG Sammitch :Sent identify command.
> NickServ!NickServ@services. NOTICE JerkfaceMcAssbot :You are now identified for JerkfaceMcAssbot.
> Sammitch!~Sammitch@hostname.tld PRIVMSG JerkfaceMcAssbot :!join ##systemadmins
! Undefined command: Sammitch!~Sammitch@hostname.tld,JerkfaceMcAssbot,join,##systemadmins
> Sammitch!~Sammitch@hostname.tld PRIVMSG JerkfaceMcAssbot :!part KitchenCommandCenter
! Undefined command: Sammitch!~Sammitch@hostname.tld,JerkfaceMcAssbot,part,KitchenCommandCenter

eg:

! Undefined command: Sammitch!~Sammitch@hostname.tld,JerkfaceMcAssbot,join,##systemadmins

Means that get_attr() couldn't find a function named bot_join().

I can't figure out why only the bot_join() and bot_part() functions don't want to work while all of the others work just fine. Can anyone enlighten me as to what might be going wrong here?

Update

I commented out the try/catch from process_bot_command() to get it to die with an exception for you guys in the comments, and the join/part functions started working. So then I added it back to see if I could figure out why it was breaking, but the code continues to work.

I have no idea why, but I guess problem solved?

Community
  • 1
  • 1
Sammitch
  • 30,782
  • 7
  • 50
  • 77
  • 1
    Can you make this into a minimal example? If you're asking about `getattr` it seems odd to keep the code in the functions (they'll never get called) and any of the code after the part where it errors (it'll never get run). – Veedrac Sep 26 '14 at 00:49
  • 4
    What happens if you move `function(prefix, params[0], bot_command, bot_args)` into an `else` clause for the `try` block? Is it possible that the exception is being raised in there? – mgilson Sep 26 '14 at 00:50
  • 2
    Also, it would really help to log the actual exception. If we saw `AttributeError: 'SpamBot' object has no attribute 'bot_join'`, we'd know that mgilson's comment was irrelevant; on the other hand, `AttributeError: 'SpamBot' object has no attribute 'irc_JOIN'` would mean that comment is actually the answer. It would be better to know than to guess. – abarnert Sep 26 '14 at 01:02
  • The code has magically fixed itself, and I don't know why. See update in question. #heisenbug – Sammitch Sep 26 '14 at 17:47
  • Since you didn't include the actual, complete code in your example including indentation, it's totally possible that you had an indentation error that was causing ``bot_join`` to not be defined as a member of ``bot``, but rather as a standalone method. And then during your debugging, you fixed this indentation mistake. – aruisdante Sep 26 '14 at 18:01
  • @aruisdante but I didn't touch any part of the `bot_join()` function, not even the whitespace. All I did was to `bot_process_command()` where I commented out `try:` line, de-indented the rest of the `try` block, and commented out the `catch` block. The code then worked, I reversed the changes, and the code continued to work. – Sammitch Sep 26 '14 at 18:10

0 Answers0