fixbot/irc.py
changeset 21 aa6df8f9c44a
child 23 67e71e9170e5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fixbot/irc.py	Mon Sep 15 00:27:05 2008 +0300
@@ -0,0 +1,156 @@
+from twisted.words.protocols import irc
+from twisted.internet import protocol
+from twisted.python import log
+import traceback
+
+import buffer
+
+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(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:%d, version %s" % (module.name, addr.host, addr.port, module.version))
+
+    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 is version %d from %s:%d. Events: %s. See `commands %s' for a list of commands" % (module.name, module.version, addr.host, addr.port, ', '.join(module.event_types), 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
+