fixbot/irc.py
author Tero Marttila <terom@fixme.fi>
Sat, 20 Feb 2010 23:06:43 +0200
changeset 64 8574aeff9b36
parent 58 31a17b0b5159
permissions -rw-r--r--
blind error handling tweaks
from twisted.words.protocols import irc
from twisted.internet import protocol
from twisted.python import log
import traceback

class ReplyException (Exception) :
    def __init__ (self, reply) :
        self.reply = reply

class Client (irc.IRCClient, object) :
    """
        Fixme IRC bot
    """
    
    def __init__ (self) :
        # can't config yet
        pass

    # housekeeping
    def connectionMade (self) :
        self.nexus = self.factory.nexus

        self.nickname = self.factory.nickname
        self.username = self.factory.username
        self.channel = self.factory.channel
        
        log.msg("IRC Client connected, using username=%s, nickname=%s" % (self.username, self.nickname))
        
        super(Client, self).connectionMade()

    def connectionLost (self, reason) :
        log.msg("Connection lost: %s" % reason)
        super(Client, self).connectionLost(reason)

    def signedOn (self) :
        log.msg("Signed on, joining channel %s" % self.channel)
        self.join(self.channel)

    def joined (self, channel) :
        log.msg("Joined channel %s" % channel)
        
        self.factory.connected(self)
    
    # our actual functionality
    def send (self, msg) :
        msg = str(msg)

        if len(msg) > 480 :
            log.msg("truncating: %s" % msg)
            msg = msg[:480] + "..."

        msg = msg.replace("\n", "\\n").replace("\r", "\\r").replace("\0", "\\0")

        self.notice(self.channel, msg)

    def sendEvent (self, event) :
        self.send("[%s.%s] %s" % (event.module.name, event.type, event.msg))

    def moduleConnected (self, module, addr) :
        self.send("{modules.%s} connected from %s" % (module.name, addr))

    def moduleDisconnected (self, module, reason) :
        self.send("{modules.%s} disconnected: %s" % (module.name, reason))
    
    class _noDefault : pass

    def _lookupCommand (self, command, default=_noDefault) :
        if '.' in command :
            raise ReplyException("No support for module commands yet :P")
        else :
            method = getattr(self, "cmd_%s" % command, None)

        if method :
            return method
        elif default is self._noDefault :
            raise ReplyException("No such command '%s'. See `help commands'" % command)
        else :
            return default

    def privmsg (self, user, channel, message) :
        if message.lower().startswith(self.nickname.lower() + ':'):
            me, command = message.split(":", 1)

            args = command.strip().split()
            command = args.pop(0)
            
            try :
                method = self._lookupCommand(command)

                reply = method(*args)

                if reply :
                    self.send(reply)
            
            except ReplyException, e :
                self.send(e.reply)

            except Exception, e :
                self.send("Error: %s: %s" % (e.__class__.__name__, e))
                traceback.print_exc()

    def cmd_help (self, cmd="help") :
        """help <command|module> - Display help about the given command or module"""

        method = self._lookupCommand(cmd, None)
        
        if method :
            return method.__doc__
        else :
            try :
                module, addr = self.nexus.getModuleInfo(cmd)

                return "%s from %s. See `commands %s' for a list of commands" % (module.name, addr, module.name)

            except KeyError :
                raise ReplyException("No command/module called `%s'. See `help commands'" % cmd)

    def cmd_commands (self, module=None) :
        """commands [<module>] - Show primary commands, or commands in the given module (see `help modules')"""

        if module :
            raise ReplyException("No support for module commands yet :P")
        else :
            return "Commands: %s" % ', '.join(
                attr_name.split('_', 1)[1]
                for attr_name in Client.__dict__.iterkeys()
                if attr_name.startswith("cmd_")
            )

    def cmd_modules (self) :
        """modules - Show a list of connected modules"""

        return "Modules: %s" % ', '.join(
            module.name
            for module in self.nexus.getModules()
        )   

class Factory (protocol.ClientFactory) :
    protocol = Client

    def __init__ (self, nexus, nickname, username, channel) :
        self.nexus = nexus

        # config
        self.nickname = nickname
        self.username = username
        self.channel = channel
        
        # don't have a connection yet
        self.connection = None
    
    def connected (self, connection) :
        self.connection = connection