#!/usr/bin/env python from twisted.words.protocols import irc from twisted.internet import reactor, protocol from twisted.python import log from twisted.protocols.basic import LineReceiver import os from ConfigParser import ConfigParser import login config = ConfigParser() def load_config(): print 'Loading config...' config.read([os.path.expanduser('~/.fot')]) print 'Done!' load_config() modules = {} def load_modules(): print 'Loading modules...' for m in config.options('modules'): if config.getboolean('modules', m): try: if modules.has_key(m): reload(modules[m]) else: modules[m] = __import__('modules.' + m, globals(), locals(), ['Module']) mod = modules[m] mod.config = config print '%s by %s' % (mod.info['title'], mod.info['author']) del mod except: print 'Failed to load', m raise else: print 'Not loading', m print 'Done!' load_modules() bots = [] def refresh_modules(): print 'Re-applying modules to bots...' for bot in bots: bot.apply_modules() print 'Done!' class Bot(irc.IRCClient): def __init__(self): bots.append(self) self.modules = {} self.keywords = {} def __repr__(self): return '' % (self.nickname, self.factory.server) def register_keyword(self, kw, mod): if kw in self.keywords and self.keywords[kw] != mod: raise Exception('Keyword "%s" already in use' % kw) self.keywords[kw] = mod def apply_modules(self): # Call stop() for each module if neccessary (can't rely on __del__ here) for m in self.modules.itervalues(): if hasattr(m, 'stop'): m.stop() self.modules = {} for m in modules.keys(): if config.has_option(self.factory.server, m) and config.get(self.factory.server, m).strip(): self.modules[m] = modules[m].Module(self) def connectionMade(self): self.apply_modules() self.nickname = config.get(self.factory.server, 'nickname') irc.IRCClient.connectionMade(self) def signedOn(self): for chan in config.get(self.factory.server, 'channels').split(' '): self.join(chan) def privmsg(self, nick, channel, msg): msg = msg.split(None, 1) if not len(msg): return elif len(msg) == 1: kw, msg = msg[0], '' else: kw, msg = msg if kw in self.keywords: self.keywords[kw].keyword(nick, channel, kw, msg) def kickedFrom(self, channel, kicker, message): self.join(channel) def connectionLost(self, reason): for m in self.modules.itervalues(): if hasattr(m, 'stop'): m.stop() self.modules = {} irc.IRCClient.connectionLost(self, reason) def reconnect(self): self.quit('reconnecting') class BotFactory(protocol.ReconnectingClientFactory): protocol = Bot def __init__(self, server, nickname): self.server = server self.nickname = nickname self.maxDelay = 120 def buildProtocol(self, addr): self.resetDelay() return protocol.ReconnectingClientFactory.buildProtocol(self, addr) def startedConnecting(self, connector): print 'Connecting to', connector.host def clientConnectionFailed(self, connector, reason): print 'Connection failed:', reason.getErrorMessage() protocol.ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) def clientConnectionLost(self, connector, reason): print 'Connection lost:', reason.getErrorMessage() protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason) def start_server(server): if not config.has_option(server,'host') or not config.has_option(server, 'port') \ or not config.has_option(server, 'channels') or config.has_option(server, 'disabled'): return channels = [] server_modules = [(m, config.get(server, m).split()) for m in modules.keys() if config.has_option(server, m)] for channel in config.get(server, 'channels').split(): channel_modules = [x[0] for x in server_modules if channel in x[1]] channels.append('%s (%s)' % (channel, ' '.join(channel_modules) if len(channel_modules) else 'No modules')) print '%s: %s' % (server, ' '.join(channels) if len(channels) else 'No channels') factory = BotFactory(server, config.get(server, 'nickname')) reactor.connectTCP(config.get(server, 'host'), config.getint(server, 'port'), factory) print 'Starting per-network instances...' for server in (server for server in config.sections() if server.startswith('server/')): start_server(server) loginfactory = login.getManholeFactory(globals(), os.path.expanduser('~/.fot.users')) reactor.listenTCP(3333, loginfactory) reactor.run()