diff --git a/cloudbot.py b/cloudbot.py index 50ed779..f567315 100755 --- a/cloudbot.py +++ b/cloudbot.py @@ -32,8 +32,8 @@ def exit_gracefully(signum, frame): original_sigint = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, exit_gracefully) -# create a bot thread and start it -cloudbot = bot.Bot() +# create a bot master and start it +cloudbot = bot.CloudBot() cloudbot.start() # watch to see if the bot stops running or needs a restart diff --git a/core/bot.py b/core/bot.py index 5730efc..95d0dfe 100644 --- a/core/bot.py +++ b/core/bot.py @@ -46,30 +46,30 @@ def get_logger(): return logger -class Bot(threading.Thread): +class CloudBot(threading.Thread): def __init__(self): # basic variables self.start_time = time.time() self.running = True self.do_restart = False - self.connections = [] + + # stores each instance of the + self.instances = [] # set up config and logging self.setup() self.logger.debug("Bot setup completed.") - # start IRC connections - self.connect() - print(self.connections) + # start bot instances + self.create() - for conn in self.connections: - conn.permissions = PermissionManager(self, conn) - print(conn) + for instance in self.instances: + instance.permissions = PermissionManager(self, instance) # run plugin loader self.plugins = collections.defaultdict(list) - """ plugins format + """ self.plugins format {'PLUGIN_TYPE': [(, {PLUGIN_ARGS}), (, @@ -89,19 +89,19 @@ class Bot(threading.Thread): """recieves input from the IRC engine and processes it""" self.logger.info("Starting main thread.") while self.running: - for conn in self.connections: + for instance in self.instances: try: - incoming = conn.parsed_queue.get_nowait() + incoming = instance.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) + instance.connection.reconnect() + main.main(self, instance, incoming) except queue.Empty: pass # if no messages are in the incoming queue, sleep - while self.running and all(c.parsed_queue.empty() for c in self.connections): + while self.running and all(i.parsed_queue.empty() for i in self.instances): time.sleep(.1) def setup(self): @@ -126,26 +126,23 @@ class Bot(threading.Thread): self.db_session = scoped_session(db_factory) self.logger.debug("Database system initalised.") - def connect(self): - """connect to all the networks defined in the bot config""" - for conf in self.config['connections']: + def create(self): + """ Create a BotInstance for all the networks defined in the config """ + for conf in self.config['instances']: + # strip all spaces and capitalization from the connection name name = clean_name(conf['name']) nick = conf['nick'] server = conf['connection']['server'] port = conf['connection'].get('port', 6667) - self.logger.debug("({}) Creating connection to {}.".format(name, server)) + self.logger.debug("Creating BotInstance for {}.".format(name)) + + self.instances.append(irc.BotInstance(name, server, nick, config=conf, + port=port, logger=self.logger, channels=conf['channels'], + ssl=conf['connection'].get('ssl', False))) + self.logger.debug("({}) Created connection.".format(name)) - if conf['connection'].get('ssl'): - self.connections.append(irc.SSLIRC(name, server, nick, config=conf, - port=port, logger=self.logger, channels=conf['channels'], - ignore_certificate_errors=conf['connection'].get('ignore_cert', True))) - self.logger.debug("({}) Created SSL connection.".format(name)) - else: - self.connections.append(irc.IRC(name, server, nick, config=conf, - port=port, logger=self.logger, channels=conf['channels'])) - self.logger.debug("({}) Created connection.".format(name)) def stop(self, reason=None): """quits all networks and shuts the bot down""" diff --git a/core/config.py b/core/config.py index d4ad6a0..9d8d010 100644 --- a/core/config.py +++ b/core/config.py @@ -38,9 +38,9 @@ class Config(dict): self.logger.info("Config loaded from file.") # reload permissions - if self.bot.connections: - for conn in self.bot.connections: - conn.permissions.reload() + if self.bot.instances: + for instance in self.bot.instances: + instance.permissions.reload() def save_config(self): """saves the contents of the config dict to the config file""" diff --git a/core/irc.py b/core/irc.py index 32044dc..88357d5 100644 --- a/core/irc.py +++ b/core/irc.py @@ -202,13 +202,14 @@ class SSLIRCConnection(IRCConnection): CERT_REQUIRED) -class IRC(object): - """handles the IRC protocol""" +class BotInstance(object): + """ A BotInstance represents each connection the bot makes to an IRC server """ - def __init__(self, name, server, nick, port=6667, logger=None, channels=[], config={}): + def __init__(self, name, server, nick, port=6667, ssl=False, logger=None, channels=[], config={}): self.name = name self.channels = channels self.config = config + self.ssl = ssl self.server = server self.port = port self.logger = logger @@ -240,8 +241,12 @@ class IRC(object): self.parse_thread.start() def create_connection(self): - return IRCConnection(self.name, self.server, self.port, - self.input_queue, self.output_queue) + if self.ssl: + return SSLIRCConnection(self.name, self.server, self.port, self.input_queue, + self.output_queue, True) + else: + return IRCConnection(self.name, self.server, self.port, + self.input_queue, self.output_queue) def stop(self): self.connection.stop() @@ -287,15 +292,4 @@ class IRC(object): except: # if this doesn't work, no big deal pass - self.output_queue.put(string) - - -class SSLIRC(IRC): - def __init__(self, name, server, nick, port=6667, logger=None, channels=[], config={}, - ignore_certificate_errors=True): - self.ignore_cert_errors = ignore_certificate_errors - IRC.__init__(self, name, server, nick, port, logger, channels, config) - - def create_connection(self): - return SSLIRCConnection(self.name, self.server, self.port, self.input_queue, - self.output_queue, self.ignore_cert_errors) + self.output_queue.put(string) \ No newline at end of file diff --git a/core/loader.py b/core/loader.py index c934cda..2374041 100644 --- a/core/loader.py +++ b/core/loader.py @@ -39,7 +39,6 @@ class PluginLoader(object): self.observer.start() self.load_all() - pprint(bot.plugins) def stop(self): """shuts down the plugin reloader""" @@ -88,7 +87,6 @@ class PluginLoader(object): if hasattr(obj, '_hook'): # check for magic if obj._thread: self.bot.threads[obj] = main.Handler(self.bot, obj) - for plug_type, data in obj._hook: # add plugin to the plugin list self.bot.plugins[plug_type] += [data] diff --git a/core/main.py b/core/main.py index 4bdc300..da120ce 100644 --- a/core/main.py +++ b/core/main.py @@ -8,6 +8,7 @@ from sqlalchemy.orm import scoped_session _thread.stack_size(1024 * 512) # reduce vm size +#TODO: redesign this messy thing class Input(dict): def __init__(self, bot, conn, raw, prefix, command, params, nick, user, host, mask, paraml, msg): @@ -54,47 +55,27 @@ class Input(dict): def run(bot, func, input): - args = func._args + uses_db = True + # TODO: change to bot.get_db_session() + print(input) + if 'text' not in input: + input.text = input.paraml - uses_db = 'db' in args and 'db' not in input + if uses_db: + # create SQLAlchemy session + bot.logger.debug("Opened DB session for: {}".format(func._filename)) + input.db = input.bot.db_session() - if 'inp' not in input: - input.inp = input.paraml - - if args: + try: + out = func(input, input.conn) + except: + bot.logger.exception("Error in plugin {}:".format(func._filename)) + return + finally: if uses_db: - # create SQLAlchemy session - bot.logger.debug("Opened DB session for: {}".format(func._filename)) - input.db = input.bot.db_session() - if 'input' in args: - input.input = input - if 0 in args: - try: - out = func(input.inp, **input) - except: - bot.logger.exception("Error in plugin {}:".format(func._filename)) - return - finally: - if uses_db: - print("Close") - input.db.close() - else: - kw = dict((key, input[key]) for key in args if key in input) - try: - out = func(input.inp, **kw) - except: - bot.logger.exception("Error in plugin {}:".format(func._filename)) - return - finally: - if uses_db: - bot.logger.debug("Closed DB session for: {}".format(func._filename)) - input.db.close() - else: - try: - out = func(input.inp) - except: - bot.logger.exception("Error in plugin {}:".format(func._filename)) - return + bot.logger.debug("Closed DB session for: {}".format(func._filename)) + input.db.close() + if out is not None: input.reply(str(out)) @@ -117,25 +98,15 @@ class Handler(object): _thread.start_new_thread(self.start, ()) def start(self): - uses_db = 'db' in self.func._args + uses_db = True while True: input = self.input_queue.get() if input == StopIteration: break - if uses_db: - # self.bot.logger.debug("Opened ST DB session for: {}".format(self.func._filename)) - input.db = input.bot.db_session() + run(self.bot, self.func, input) - try: - run(self.bot, self.func, input) - except: - self.bot.logger.exception("Error in plugin {}:".format(self.func._filename)) - finally: - if uses_db: - # self.bot.logger.debug("Closed ST DB session for: {}".format(self.func._filename)) - input.db.close() def stop(self): self.input_queue.put(StopIteration) @@ -203,8 +174,8 @@ def main(bot, conn, out): elif command in bot.commands: input = Input(bot, conn, *out) input.trigger = trigger - input.inp_unstripped = m.group(2) - input.inp = input.inp_unstripped.strip() + input.text_unstripped = m.group(2) + input.text = input.text_unstripped.strip() func, args = bot.commands[command] dispatch(bot, input, "command", func, args, autohelp=True) @@ -214,6 +185,6 @@ def main(bot, conn, out): m = args['re'].search(inp.lastparam) if m: input = Input(bot, conn, *out) - input.inp = m + input.text = m dispatch(bot, input, "regex", func, args) diff --git a/plugins/eightball.py b/plugins/eightball.py index 1b8f5c1..4730bc4 100644 --- a/plugins/eightball.py +++ b/plugins/eightball.py @@ -14,10 +14,10 @@ with open("./data/8ball_responses.txt") as f: f.readlines() if not line.startswith("//")] -@hook.command('8ball') -def eightball(inp, action=None): +@hook.command() +def eightball(input, conn): """8ball -- The all knowing magic eight ball, in electronic form. Ask and it shall be answered!""" magic = text.multiword_replace(random.choice(responses), color_codes) - action("shakes the magic 8 ball... {}".format(magic)) + input.action("shakes the magic 8 ball... {}".format(magic)) diff --git a/plugins/namegen.py b/plugins/namegen.py index 7a1f0e6..5f5a169 100644 --- a/plugins/namegen.py +++ b/plugins/namegen.py @@ -14,12 +14,12 @@ def get_generator(_json): @hook.command(autohelp=False) -def namegen(inp, notice=None): +def namegen(input, instance, bot): """namegen [generator] -- Generates some names using the chosen generator. 'namegen list' will display a list of all generators.""" # clean up the input - inp = inp.strip().lower() + inp = input.text.strip().lower() # get a list of available name generators files = os.listdir(GEN_DIR) @@ -33,7 +33,7 @@ def namegen(inp, notice=None): if inp == "list": message = "Available generators: " message += text.get_text_list(all_modules, 'and') - notice(message) + input.notice(message) return if inp: diff --git a/util/hook.py b/util/hook.py index 98c65db..31e069b 100644 --- a/util/hook.py +++ b/util/hook.py @@ -10,30 +10,6 @@ def _hook_add(func, add, name=''): if not hasattr(func, '_filename'): func._filename = func.__code__.co_filename - if not hasattr(func, '_args'): - argspec = inspect.getargspec(func) - if name: - n_args = len(argspec.args) - if argspec.defaults: - n_args -= len(argspec.defaults) - if argspec.keywords: - n_args -= 1 - if argspec.varargs: - n_args -= 1 - if n_args != 1: - err = '%ss must take 1 non-keyword argument (%s)' % (name, - func.__name__) - raise ValueError(err) - - args = [] - if argspec.defaults: - end = bool(argspec.keywords) + bool(argspec.varargs) - args.extend(argspec.args[-len(argspec.defaults): - end if end else None]) - if argspec.keywords: - args.append(0) # means kwargs present - func._args = args - if not hasattr(func, '_thread'): # does function run in its own thread? func._thread = False