# -*- coding: utf-8 -*- """ EarwigBot's JSON Config File Parser This handles all tasks involving reading and writing to our config file, including encrypting and decrypting passwords and making a new config file from scratch at the inital bot run. Usually you'll just want to do "from core import config" and access config data from within config's three global variables and one function: * config.components - a list of enabled components * config.wiki - a dict of config information for wiki-editing * config.irc - a dict of config information for IRC * config.schedule() - returns a list of tasks scheduled to run at a given time """ import json from os import path import blowfish script_dir = path.dirname(path.abspath(__file__)) root_dir = path.split(script_dir)[0] config_path = path.join(root_dir, "config.json") _config = None # holds data loaded from our config file # set our three easy-config-access global variables to None components, wiki, irc = (None, None, None) def is_config_loaded(): """Return True if our config file has already been loaded, and False if it hasn't.""" if _config is not None: return True return False def load_config(): """Load data from our JSON config file (config.json) into _config.""" global _config with open(config_path, 'r') as fp: try: _config = json.load(fp) except ValueError as error: print "Error parsing config file {0}:".format(config_path) print error exit(1) def verify_config(): """Check to see if we have a valid config file, and if not, notify the user. If there is no config file at all, offer to make one; otherwise, exit. If everything goes well, return True if stored passwords are encrypted in the file, or False if they are not.""" if path.exists(config_path): load_config() try: return _config["encryptPasswords"] # are passwords encrypted? except KeyError: return False # assume passwords are not encrypted by default else: print "You haven't configured the bot yet!" choice = raw_input("Would you like to do this now? [y/n] ") if choice.lower().startswith("y"): return make_new_config() else: exit(1) def parse_config(key): """Store data from our config file in three global variables for easy access, and use the key to unencrypt passwords. Catch password decryption errors and report them to the user.""" global components, wiki, irc load_config() # we might be re-loading unnecessarily here, but no harm in # that! try: components = _config["components"] except KeyError: components = [] try: wiki = _config["wiki"] except KeyError: wiki = {} try: irc = _config["irc"] except KeyError: irc = {} try: try: if _config["encryptPasswords"]: decrypt(key, "wiki['password']") decrypt(key, "irc['frontend']['nickservPassword']") decrypt(key, "irc['watcher']['nickservPassword']") except KeyError: pass except blowfish.BlowfishError as error: print "\nError decrypting passwords:" print "{0}: {1}.".format(error.__class__.__name__, error) exit(1) def decrypt(key, item): """Decrypt 'item' with blowfish.decrypt() using the given key and set it to the decrypted result. 'item' should be a string, like decrypt(key, "wiki['password']"), NOT decrypt(key, wiki['password'), because that won't work.""" global irc, wiki try: result = blowfish.decrypt(key, eval(item)) except KeyError: return exec "{0} = result".format(item) def schedule(minute, hour, month_day, month, week_day): """Return a list of tasks that are scheduled to run at the time specified by the function arguments. The schedule data comes from our config file's 'schedule' field, which is stored as _config["schedule"]. Call this function with config.schedule(args).""" tasks = [] # tasks to run this turn, each as a tuple of either (task_name, # kwargs), or just task_name now = {"minute": minute, "hour": hour, "month_day": month_day, "month": month, "week_day": week_day} try: data = _config["schedule"] except KeyError: return [] # nothing is in our schedule for event in data: do = True for key, value in now.items(): try: requirement = event[key] except KeyError: continue if requirement != value: do = False break if do: try: tasks.extend(event["tasks"]) except KeyError: pass return tasks def make_new_config(): """Make a new config file based on the user's input.""" encrypt = raw_input("Would you like to encrypt passwords stored in " + "config.json? [y/n] ") if encrypt.lower().startswith("y"): is_encrypted = True else: is_encrypted = False return is_encrypted