diff --git a/core/config.py b/core/config.py index 294d432..8a2831e 100644 --- a/core/config.py +++ b/core/config.py @@ -6,8 +6,12 @@ EarwigBot's XML 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.config import config" and access +config data from within that object. """ +from collections import defaultdict from os import makedirs, path from xml.dom import minidom from xml.parsers.expat import ExpatError @@ -16,15 +20,25 @@ script_dir = path.dirname(path.abspath(__file__)) root_dir = path.split(script_dir)[0] config_path = path.join(root_dir, "config.xml") -_config = None +_config = None # holds the parsed DOM object for our config file +config = None # holds an instance of Container() with our config data class ConfigParseException(Exception): """Base exception for when we could not parse the config file.""" class TypeMismatchException(ConfigParseException): - """A field does not fit to its expected type; e.g., an aribrary string + """A field does not fit to its expected type; e.g., an arbitrary string where we expected a boolean or integer.""" +class MissingElementException(ConfigParseException): + """An element in the config file is missing a required sub-element.""" + +class MissingAttributeException(ConfigParseException): + """An element is missing a required attribute to be parsed correctly.""" + +class Container(object): + """A class to hold information in a nice, accessable manner.""" + def _load_config(): """Load data from our XML config file (config.xml) into a DOM object.""" global _config @@ -42,6 +56,9 @@ def verify_config(): error) exit() else: + if not _config.getElementsByTagName("config"): + e = "Config file is missing a tag." + raise MissingElementException(e) return are_passwords_encrypted() else: print "You haven't configured the bot yet!" @@ -88,8 +105,55 @@ def attribute_to_bool(element, attribute, default=None): raise TypeMismatchException(e) def parse_config(key): - """Parse config data from a DOM object. The key is used to unencrypt - passwords stored in the config file.""" + """Parse config data from a DOM object into the 'config' global variable. + The key is used to unencrypt passwords stored in the XML config file.""" _load_config() # we might be re-loading unnecessarily here, but no harm in # that! data = _config.getElementsByTagName("config")[0] + + cfg = Container() + cfg.components = parse_components(data) + cfg.wiki = parse_wiki(data, key) + cfg.irc = parse_irc(data, key) + cfg.schedule = parse_schedule(data) + cfg.watcher = parse_watcher(data) + + global config + config = cfg + +def parse_components(data): + """Parse everything within the XML tag of our config file. + The components object here will exist as config.components, and is a dict + of our enabled components: components[name] = True if it is enabled, False + if it is disabled.""" + components = defaultdict(lambda: False) # all components are disabled by + # default + element = data.getElementsByTagName("components") + if not element: + e = " is missing a required tag." + raise MissingElementException(e) + element = element[0] # select the first tag out of our list + # of tags, even though we should only have one + + component_tags = element.getElementsByTagName("component") + for component in component_tags: + name = component.getAttribute("name") + if not name: + e = "A tag is missing the required attribute 'name'." + raise MissingAttributeException(e) + is_enabled = attribute_to_bool(component, "enabled", False) + components[name] = is_enabled + + return components + +def parse_wiki(data, key): + pass + +def parse_irc(data, key): + pass + +def parse_schedule(data): + pass + +def parse_watcher(data): + pass diff --git a/core/main.py b/core/main.py index b4e9729..e55d3b9 100644 --- a/core/main.py +++ b/core/main.py @@ -48,7 +48,7 @@ from core import config f_conn = None w_conn = None -def irc_watcher(f_conn): +def irc_watcher(f_conn=None): """Function to handle the IRC watcher as another thread (if frontend and/or scheduler is enabled), otherwise run as the main thread.""" global w_conn @@ -115,7 +115,7 @@ def run(): key = None config.parse_config(key) # load data from the config file and parse it # using the unlock key - components = None + components = config.config.components if components["irc_frontend"]: # make the frontend run on our primary irc_frontend(components) # thread if enabled, and enable additional @@ -138,6 +138,7 @@ def run(): else: # nothing is enabled! print "No bot parts are enabled; stopping..." + exit(1) if __name__ == "__main__": try: