#!/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 = {} def __del__(self): bots.remove(self) def __repr__(self): return '' % (self.nickname, self.factory.server) 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): for m in self.modules.keys(): if (config.has_option(self.factory.server, m) and channel in config.get(self.factory.server, m)) or (channel == self.nickname and nick.split('!')[0] != self.nickname): self.modules[m](nick, channel, 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) 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 protocol.ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) def clientConnectionLost(self, connector, reason): print 'Connection lost:', reason protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason) print 'Starting per-network instances...' for server in (server for server in config.sections() if server.startswith('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'): continue channels = [] ms = [(m, config.get(server, m)) for m in modules.keys() if config.has_option(server, m)] for c in config.get(server, 'channels').split(' '): ch = [x[0] for x in ms if c in x[1]] channels.append('%s (%s)' % (c, ' '.join(ch))) print '%s: %s' % (server, ' '.join(channels)) del channels, ms, c, ch, x factory = BotFactory(server, config.get(server, 'nickname')) reactor.connectTCP(config.get(server, 'host'), config.getint(server, 'port'), factory) loginfactory = login.getManholeFactory(globals(), os.path.expanduser('~/.fot.users')) reactor.listenTCP(3333, loginfactory) reactor.run()