diff --git a/cloudbot.py b/cloudbot.py index f64b81c..0e94379 100644 --- a/cloudbot.py +++ b/cloudbot.py @@ -3,6 +3,7 @@ from core import bot import os import sys +import time import signal # check python version @@ -30,9 +31,15 @@ signal.signal(signal.SIGINT, exit_gracefully) cloudbot = bot.Bot() cloudbot.run() + +# wait for the bot loop to stop + if cloudbot.do_restart: # this kills the bot # TODO: make it not just kill the bot + time.sleep(2) sys.exit() else: + print "wtf" + time.sleep(2) sys.exit() diff --git a/core/bot.py b/core/bot.py index 4f46c72..90d4d3b 100644 --- a/core/bot.py +++ b/core/bot.py @@ -8,7 +8,9 @@ import collections from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy import create_engine -from core import config, irc, main, loader +from core import config, irc, main +from core.permissions import PermissionManager +from core.loader import PluginLoader def clean_name(n): @@ -49,29 +51,36 @@ class Bot(object): self.start_time = time.time() self.running = True self.do_restart = False + self.connections = [] # set up config and logging self.setup() self.logger.debug("Bot setup completed.") # start IRC connections - self.connections = {} self.connect() + print self.connections + + for conn in self.connections: + conn.permissions = PermissionManager(self, conn) + print conn # run plugin loader self.plugins = collections.defaultdict(list) self.threads = {} - self.loader = loader.PluginLoader(self) + + self.loader = PluginLoader(self) def run(self): """recieves input from the IRC engine and processes it""" self.logger.info("Starting main thread.") while self.running: - for conn in self.connections.itervalues(): + for conn in self.connections: try: incoming = conn.parsed_queue.get_nowait() if incoming == StopIteration: + print "StopIteration" # IRC engine has signalled timeout, so reconnect (ugly) conn.connection.reconnect() main.main(self, conn, incoming) @@ -79,7 +88,7 @@ class Bot(object): pass # if no messages are in the incoming queue, sleep - while all(connection.parsed_queue.empty() for connection in self.connections.itervalues()): + while self.running and all(c.parsed_queue.empty() for c in self.connections): time.sleep(.1) @@ -97,7 +106,7 @@ class Bot(object): self.logger.debug("Created data folder.") # config - self.config = config.Config(self.logger) + self.config = config.Config(self) self.logger.debug("Config object created.") # db @@ -118,13 +127,13 @@ class Bot(object): self.logger.debug("({}) Creating connection to {}.".format(name, server)) if conf['connection'].get('ssl'): - self.connections[name] = irc.SSLIRC(name, server, nick, conf = conf, + self.connections.append(irc.SSLIRC(name, server, nick, config = conf, port = port, channels = conf['channels'], - ignore_certificate_errors=conf['connection'].get('ignore_cert', True)) + ignore_certificate_errors=conf['connection'].get('ignore_cert', True))) self.logger.debug("({}) Created SSL connection.".format(name)) else: - self.connections[name] = irc.IRC(name, server, nick, conf = conf, - port = port, channels = conf['channels']) + self.connections.append(irc.IRC(name, server, nick, config = conf, + port = port, channels = conf['channels'])) self.logger.debug("({}) Created connection.".format(name)) @@ -153,9 +162,6 @@ class Bot(object): self.running = False - # wait for the bot loop to stop - time.sleep(1) - def restart(self, reason=None): """shuts the bot down and restarts it""" diff --git a/core/config.py b/core/config.py index 21494d2..7596d41 100755 --- a/core/config.py +++ b/core/config.py @@ -8,10 +8,11 @@ from watchdog.tricks import Trick class Config(dict): - def __init__(self, logger, *args, **kwargs): + def __init__(self, bot, *args, **kwargs): self.filename = "config.json" self.path = os.path.abspath(self.filename) - self.logger = logger + self.bot = bot + self.logger = bot.logger self.update(*args, **kwargs) # populate self with config data @@ -35,6 +36,12 @@ class Config(dict): self.update(json.load(f)) self.logger.info("Config loaded from file.") + # reload permissions + if self.bot.connections: + for conn in self.bot.connections: + conn.permissions.reload() + + def save_config(self): """saves the contents of the config dict to the config file""" json.dump(self, open(self.path, 'w'), sort_keys=True, indent=2) diff --git a/core/irc.py b/core/irc.py index a136d3a..39958bb 100755 --- a/core/irc.py +++ b/core/irc.py @@ -4,6 +4,8 @@ import time import threading import Queue +from core import permissions + from ssl import wrap_socket, CERT_NONE, CERT_REQUIRED, SSLError irc_prefix_rem = re.compile(r'(.*?) (.*?) (.*)').match @@ -203,10 +205,10 @@ class SSLIRCConnection(IRCConnection): class IRC(object): """handles the IRC protocol""" - def __init__(self, name, server, nick, port=6667, channels=[], conf={}): + def __init__(self, name, server, nick, port=6667, channels=[], config={}): self.name = name self.channels = channels - self.conf = conf + self.config = config self.server = server self.port = port self.nick = nick @@ -224,10 +226,10 @@ class IRC(object): self.connection = self.create_connection() self.connection.connect() - self.set_pass(self.conf.get('server_password')) + self.set_pass(self.config.get('server_password')) self.set_nick(self.nick) self.cmd("USER", - [self.conf.get('user', 'cloudbot'), "3", "*", self.conf.get('realname', + [self.config.get('user', 'cloudbot'), "3", "*", self.config.get('realname', 'CloudBot - http://git.io/cloudbot')]) self.parse_thread = ParseThread(self.input_queue, self.output_queue, @@ -235,6 +237,7 @@ class IRC(object): self.parse_thread.daemon = True self.parse_thread.start() + def create_connection(self): return IRCConnection(self.name, self.server, self.port, self.input_queue, self.output_queue) @@ -282,10 +285,10 @@ class IRC(object): class SSLIRC(IRC): - def __init__(self, name, server, nick, port=6667, channels=[], conf={}, + def __init__(self, name, server, nick, port=6667, channels=[], config={}, ignore_certificate_errors=True): self.ignore_cert_errors = ignore_certificate_errors - IRC.__init__(self, name, server, nick, port, channels, conf) + IRC.__init__(self, name, server, nick, port, channels, config) def create_connection(self): return SSLIRCConnection(self.name, self.server, self.port, self.input_queue, diff --git a/core/loader.py b/core/loader.py index 65269f0..365a3ec 100644 --- a/core/loader.py +++ b/core/loader.py @@ -64,7 +64,7 @@ class PluginLoader(object): namespace = {} eval(code, namespace) except Exception: - self.bot.logger.error("Error compiling {}.".format(filename)) + self.bot.logger.error("Error compiling {}:".format(filename)) self.bot.logger.error(traceback.format_exc()) return diff --git a/core/main.py b/core/main.py index 654933a..1cad6a3 100755 --- a/core/main.py +++ b/core/main.py @@ -132,7 +132,7 @@ def dispatch(bot, input, kind, func, args, autohelp=False): return if not (not autohelp or not args.get('autohelp', True) or input.inp or not (func.__doc__ is not None)): - input.notice(input.conn.conf["command_prefix"] + func.__doc__) + input.notice(input.conn.config["command_prefix"] + func.__doc__) return if func._thread: @@ -156,7 +156,7 @@ def match_command(bot, command): def main(bot, conn, out): inp = Input(bot, conn, *out) - command_prefix = conn.conf.get('command_prefix', '.') + command_prefix = conn.config.get('command_prefix', '.') # EVENTS for func, args in bot.events[inp.command] + bot.events['*']: diff --git a/core/permissions.py b/core/permissions.py new file mode 100644 index 0000000..9bf499a --- /dev/null +++ b/core/permissions.py @@ -0,0 +1,18 @@ +class PermissionManager(object): + def __init__(self, bot, conn): + self.logger = bot.logger + + self.logger.info("Creating permission manager for {}.".format(conn.name)) + + # stuff + self.bot = bot + self.conn = conn + self.config = conn.config + + self.group_perms = {} + + self.reload() + + def reload(self): + self.logger.error("reload perms stub") + pass \ No newline at end of file diff --git a/plugins/core_misc.py b/plugins/core_misc.py index 6255132..2f9da43 100755 --- a/plugins/core_misc.py +++ b/plugins/core_misc.py @@ -12,7 +12,7 @@ nick_re = re.compile(":(.+?)!") # Auto-join on Invite (Configurable, defaults to True) @hook.event('INVITE') def invite(paraml, conn=None): - invite_join = conn.conf.get('invite_join', True) + invite_join = conn.config.get('invite_join', True) if invite_join: conn.join(paraml[-1]) @@ -21,7 +21,7 @@ def invite(paraml, conn=None): @hook.event('004') def onjoin(paraml, conn=None, bot=None): bot.logger.info("ONJOIN hook triggered.") - nickserv = conn.conf.get('nickserv') + nickserv = conn.config.get('nickserv') if nickserv: nickserv_password = nickserv.get('nickserv_password', '') nickserv_name = nickserv.get('nickserv_name', 'nickserv') @@ -38,7 +38,7 @@ def onjoin(paraml, conn=None, bot=None): time.sleep(1) # Set bot modes - mode = conn.conf.get('mode') + mode = conn.config.get('mode') if mode: bot.logger.info('Setting bot mode: "{}"'.format(mode)) conn.cmd('MODE', [conn.nick, mode]) @@ -57,7 +57,7 @@ def onkick(paraml, conn=None, chan=None): # if the bot has been kicked, remove from the channel list if paraml[1] == conn.nick: conn.channels.remove(chan) - auto_rejoin = conn.conf.get('auto_rejoin', False) + auto_rejoin = conn.config.get('auto_rejoin', False) if auto_rejoin: conn.join(paraml[0]) @@ -74,7 +74,7 @@ def onnick(paraml, bot=None, conn=None, raw=None): @hook.singlethread @hook.event('004') def keep_alive(paraml, conn=None): - keepalive = conn.conf.get('keep_alive', False) + keepalive = conn.config.get('keep_alive', False) if keepalive: while True: conn.cmd('PING', [conn.nick]) diff --git a/plugins/core_sieve.py b/plugins/core_sieve.py index aeb04ab..aeb03f4 100755 --- a/plugins/core_sieve.py +++ b/plugins/core_sieve.py @@ -12,16 +12,16 @@ def sieve_suite(bot, input, func, kind, args): return None if kind == "command": - disabled_commands = conn.conf.get('disabled_plugins', []) + disabled_commands = conn.config.get('disabled_plugins', []) if input.trigger in disabled_commands: return None fn = re.match(r'^plugins.(.+).py$', func._filename) - disabled = conn.conf.get('disabled_plugins', []) + disabled = conn.config.get('disabled_plugins', []) if fn and fn.group(1).lower() in disabled: return None - acl = conn.conf.get('acls', {}).get(func.__name__) + acl = conn.config.get('acls', {}).get(func.__name__) if acl: if 'deny-except' in acl: allowed_channels = map(unicode.lower, acl['deny-except']) @@ -37,7 +37,7 @@ def sieve_suite(bot, input, func, kind, args): args["permissions"] = ["adminonly"] if args.get('permissions', False): - groups = conn.conf.get("permissions", []) + groups = conn.config.get("permissions", []) allowed_permissions = args.get('permissions', []) allowed_groups = [] @@ -58,7 +58,7 @@ def sieve_suite(bot, input, func, kind, args): mask = input.mask.lower() for group in allowed_groups: - group_users = conn.conf.get("permissions", {}).get(group, [])["users"] + group_users = conn.conig.get("permissions", {}).get(group, [])["users"] group_users = [_mask.lower() for _mask in group_users] for pattern in group_users: if fnmatch(mask, pattern):