diff --git a/.hgignore b/.hgignore index 81d4b47..f418d13 100644 --- a/.hgignore +++ b/.hgignore @@ -4,6 +4,7 @@ syntax: glob *.orig iambuttbot_password persist +config pep8.py .project -.pydevproject \ No newline at end of file +.pydevproject diff --git a/bot.py b/bot.py index 0fb66da..4a74297 100755 --- a/bot.py +++ b/bot.py @@ -1,9 +1,5 @@ #!/usr/bin/python -network = "localhost" -nick = "skybot" -channel = "#test" - import sys import os import Queue @@ -27,19 +23,28 @@ reload(init=True) print 'Connecting to IRC' -bot.nick = nick -bot.channel = channel -bot.network = network -bot.irc = irc(network, nick) -bot.irc.join(channel) +bot.conns = {} + +for connection in bot.config['connections']: + for name, conf in connection.iteritems(): + if name in bot.conns: + print 'ERROR: more than one connection named "%s"' % name + raise ValueError + bot.conns[name] = irc(conf['server'], conf['nick']) + for channel in conf.get('channels', []): + bot.conns[name].join(channel) + bot.persist_dir = os.path.abspath('persist') print 'Running main loop' while True: try: - out = bot.irc.out.get(timeout=1) - reload() - main(out) + reload() # these functions only do things + config() # if changes have occured + + for conn in bot.conns.itervalues(): + out = conn.out.get(timeout=1) + main(conn, out) except Queue.Empty: pass diff --git a/core/config.py b/core/config.py new file mode 100644 index 0000000..0fc8dee --- /dev/null +++ b/core/config.py @@ -0,0 +1,31 @@ +import os + +from util import yaml + +if not os.path.exists('config'): + conf = {'connections': [ + {'local irc': {'nick': 'skybot', + 'server': 'localhost', + 'channels': ["#test"]}}]} + yaml.dump(conf, open('config', 'w')) + del conf + +bot.config = yaml.load(open('config')) +bot._config_dirty = False +bot._config_mtime = os.stat('config').st_mtime + +def config_dirty(self): + "signals that config has changed and should be written to disk" + self._config_dirty = True + +bot.config_dirty = config_dirty + +def config(): + # reload config from file if file has changed + if bot._config_mtime != os.stat('config').st_mtime: + bot.config = yaml.load(open('config')) + bot._config_dirty = False + + # save config to file if config has changed + if bot._config_dirty: + yaml.dump(bot.config, open('config', 'w')) diff --git a/core/irc.py b/core/irc.py index e8a09dd..aa7a66f 100644 --- a/core/irc.py +++ b/core/irc.py @@ -62,13 +62,15 @@ class irc(object): "handles the IRC protocol" #see the docs/ folder for more information on the protocol - def __init__(self, network, nick, port=6667): - self.conn = crlf_tcp(network, port) + def __init__(self, server, nick, port=6667): + self.server = server + self.conn = crlf_tcp(server, port) thread.start_new_thread(self.conn.run, ()) self.out = Queue.Queue() #responses from the server are placed here # format: [rawline, prefix, command, params, # nick, user, host, paramlist, msg] - self.nick(nick) + self.nick = nick + self.set_nick(nick) self.cmd("USER", ["skybot v0.01", "0", "bot"]) thread.start_new_thread(self.parse_loop, ()) @@ -89,7 +91,7 @@ class irc(object): if command == "PING": self.cmd("PONG", [params]) - def nick(self, nick): + def set_nick(self, nick): self.cmd("NICK", [nick]) def join(self, channel): diff --git a/core/main.py b/core/main.py index 643fdad..23e3edd 100644 --- a/core/main.py +++ b/core/main.py @@ -3,8 +3,9 @@ import traceback class Input(object): - def __init__(self, raw, prefix, command, + def __init__(self, conn, raw, prefix, command, params, nick, user, host, paraml, msg): + self.conn = conn self.raw = raw self.prefix = prefix self.command = command @@ -15,7 +16,7 @@ class Input(object): self.paraml = paraml self.msg = msg self.chan = paraml[0] - if self.chan == bot.nick: + if self.chan == conn.nick: self.chan = nick elif command =='JOIN': self.chan = msg @@ -23,20 +24,21 @@ class Input(object): class FakeBot(object): - def __init__(self, bot, input, func): + def __init__(self, bot, conn, input, func): self.bot = bot + self.conn = conn self.persist_dir = bot.persist_dir - self.network = bot.network + self.server = conn.server self.input = input - self.msg = bot.irc.msg - self.cmd = bot.irc.cmd - self.join = bot.irc.join + self.msg = conn.msg + self.cmd = conn.cmd + self.join = conn.join self.func = func self.doreply = True self.chan = input.chan - + def say(self, msg): - self.bot.irc.msg(self.chan, msg) + self.conn.msg(self.chan, msg) def reply(self, msg): self.say(self.input.nick + ': ' + msg) @@ -53,9 +55,9 @@ class FakeBot(object): else: self.say(unicode(out)) -def main(out): +def main(conn, out): for csig, func, args in (bot.plugs['command'] + bot.plugs['event']): - input = Input(*out) + input = Input(conn, *out) for fsig, sieve in bot.plugs['sieve']: try: input = sieve(bot, input, func, args) @@ -67,4 +69,4 @@ def main(out): break if input == None: continue - thread.start_new_thread(FakeBot(bot, input, func).run, ()) + thread.start_new_thread(FakeBot(bot, conn, input, func).run, ()) diff --git a/plugins/log.py b/plugins/log.py index fb81b8f..aa1e68b 100644 --- a/plugins/log.py +++ b/plugins/log.py @@ -30,8 +30,8 @@ ctcp_formats = {'ACTION': '* %(nick)s %(ctcpmsg)s'} irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])') -def get_log_filename(dir, network, chan): - return os.path.join(dir, 'log', gmtime('%Y'), network, +def get_log_filename(dir, server, chan): + return os.path.join(dir, 'log', gmtime('%Y'), server, gmtime('%%s.%m-%d.log') % chan).lower() @@ -63,9 +63,9 @@ def beautify(input): return format % args -def get_log_fd(dir, network, chan): - fn = get_log_filename(dir, network, chan) - cache_key = '%s %s' % (network, chan) +def get_log_fd(dir, server, chan): + fn = get_log_filename(dir, server, chan) + cache_key = '%s %s' % (server, chan) filename, fd = log_fds.get(cache_key, ('', 0)) if fn != filename: # we need to open a file for writing @@ -86,7 +86,7 @@ def log(bot, input): with lock: timestamp = gmtime(timestamp_format) - fd = get_log_fd(bot.persist_dir, bot.network, 'raw') + fd = get_log_fd(bot.persist_dir, bot.server, 'raw') fd.write(timestamp + ' ' + input.raw + '\n') if input.command == 'QUIT': # these are temporary fixes until proper @@ -99,5 +99,5 @@ def log(bot, input): print '%s %s %s' % (timestamp, input.chan, beau) if input.chan: - fd = get_log_fd(bot.persist_dir, bot.network, input.chan) + fd = get_log_fd(bot.persist_dir, bot.server, input.chan) fd.write(timestamp + ' ' + beau + '\n') diff --git a/plugins/sieve.py b/plugins/sieve.py index 218f53b..bb3fde3 100644 --- a/plugins/sieve.py +++ b/plugins/sieve.py @@ -16,8 +16,9 @@ def sieve_suite(bot, input, func, args): hook = args.get('hook', r'(.*)') if args.get('prefix', True): + # add a prefix, unless it's a private message hook = (r'^(?:[.!]|' if input.chan != input.nick else r'^(?:[.!]?|') \ - + bot.nick +r'[:,]*\s*)' + hook + + input.conn.nick + r'[:,]*\s*)' + hook input.re = re.match(hook, input.msg, flags=re.I) if input.re is None: