We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Change default modules directory
Currently the default modules directory location is `./modules`. 

This makes a few assumptions:  
* Current directory is in the python path 
* There are no other python modules in the current directory
* There are no other modules named `modules`
* Current directory is writable

A better default location may be `~/.opsdroid/modules/opsdroid-modules`. This would be created if it doesn't exist and `~/.opsdroid/modules` could be added to the python path without fear of collision as `opsdroid-modules` is less generic. As it is in the home directory we can be fairly sure it is writable. 

Also when a user specifies a custom modules directory it should still be suffixed with `/opsdroid-modules` and the custom directory should be added to the python path. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opsdroid/loader.py`
Content:
```
1 """Class for loading in modules to OpsDroid."""
2 
3 import logging
4 import os
5 import sys
6 import shutil
7 import subprocess
8 import importlib
9 import yaml
10 from opsdroid.const import (
11     DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULE_BRANCH)
12 
13 
14 _LOGGER = logging.getLogger(__name__)
15 
16 
17 class Loader:
18     """Class to load in config and modules."""
19 
20     def __init__(self, opsdroid):
21         """Create object with opsdroid instance."""
22         self.opsdroid = opsdroid
23         self.modules_directory = MODULES_DIRECTORY
24         self.current_import_config = None
25         _LOGGER.debug("Loaded loader")
26 
27     @staticmethod
28     def import_module(config):
29         """Import module namespace as variable and return it."""
30         try:
31             module = importlib.import_module(
32                 config["module_path"] + "." + config["name"])
33             _LOGGER.debug("Loaded " + config["type"] + ": " +
34                           config["module_path"])
35             return module
36         except ImportError as error:
37             _LOGGER.debug("Failed to load " + config["type"] +
38                           " " + config["module_path"] + "." + config["name"])
39             _LOGGER.debug(error)
40 
41         try:
42             module = importlib.import_module(
43                 config["module_path"])
44             _LOGGER.debug("Loaded " + config["type"] + ": " +
45                           config["module_path"])
46             return module
47         except ImportError as error:
48             _LOGGER.debug("Failed to load " + config["type"] +
49                           " " + config["module_path"])
50             _LOGGER.debug(error)
51 
52         _LOGGER.error("Failed to load " + config["type"] +
53                       " " + config["module_path"])
54         return None
55 
56     @staticmethod
57     def check_cache(config):
58         """Remove module if 'no-cache' set in config."""
59         if "no-cache" in config \
60                 and config["no-cache"]:
61             _LOGGER.debug("'no-cache' set, removing " + config["install_path"])
62             if os.path.isdir(config["install_path"]):
63                 shutil.rmtree(config["install_path"])
64             if os.path.isfile(config["install_path"] + ".py"):
65                 os.remove(config["install_path"] + ".py")
66 
67     def build_module_path(self, path_type, config):
68         """Generate the module path from name and type."""
69         if path_type == "import":
70             return MODULES_DIRECTORY + "." + config["type"] + \
71                         "." + config["name"]
72         elif path_type == "install":
73             return self.modules_directory + "/" + config["type"] + \
74                         "/" + config["name"]
75 
76     @staticmethod
77     def git_clone(git_url, install_path, branch):
78         """Clone a git repo to a location and wait for finish."""
79         process = subprocess.Popen(["git", "clone", "-b", branch,
80                                     git_url, install_path], shell=False,
81                                    stdout=subprocess.PIPE,
82                                    stderr=subprocess.PIPE)
83         for output in process.communicate():
84             if output != "":
85                 for line in output.splitlines():
86                     _LOGGER.debug(str(line).strip())
87         process.wait()
88 
89     @staticmethod
90     def pip_install_deps(requirements_path):
91         """Pip install a requirements.txt file and wait for finish."""
92         process = subprocess.Popen(["pip", "install", "-r", requirements_path],
93                                    shell=False,
94                                    stdout=subprocess.PIPE,
95                                    stderr=subprocess.PIPE)
96         for output in process.communicate():
97             if output != "":
98                 for line in output.splitlines():
99                     _LOGGER.debug(str(line).strip())
100         process.wait()
101 
102     def load_config_file(self, config_paths):
103         """Load a yaml config file from path."""
104         config_path = ""
105         for possible_path in config_paths:
106             if not os.path.isfile(possible_path):
107                 _LOGGER.debug("Config file " + possible_path +
108                               " not found")
109             else:
110                 config_path = possible_path
111                 break
112 
113         if not config_path:
114             self.opsdroid.critical("No configuration files found", 1)
115 
116         try:
117             with open(config_path, 'r') as stream:
118                 _LOGGER.info("Loaded config from %s", config_path)
119                 return yaml.load(stream)
120         except yaml.YAMLError as error:
121             self.opsdroid.critical(error, 1)
122         except FileNotFoundError as error:
123             self.opsdroid.critical(str(error), 1)
124 
125     def load_modules_from_config(self, config):
126         """Load all module types based on config."""
127         _LOGGER.debug("Loading modules from config")
128 
129         if "module-path" in config:
130             sys.path.append(config["module-path"])
131             if not os.path.isdir(config["module-path"]):
132                 os.makedirs(config["module-path"], exist_ok=True)
133             self.modules_directory = os.path.join(config["module-path"],
134                                                   self.modules_directory)
135 
136         connectors, databases, skills = None, None, None
137 
138         if 'databases' in config.keys():
139             databases = self._load_modules('database', config['databases'])
140         else:
141             _LOGGER.warning("No databases in configuration")
142 
143         if 'skills' in config.keys():
144             skills = self._load_modules('skill', config['skills'])
145         else:
146             self.opsdroid.critical(
147                 "No skills in configuration, at least 1 required", 1)
148 
149         if 'connectors' in config.keys():
150             connectors = self._load_modules('connector', config['connectors'])
151         else:
152             self.opsdroid.critical(
153                 "No connectors in configuration, at least 1 required", 1)
154 
155         return connectors, databases, skills
156 
157     def _load_modules(self, modules_type, modules):
158         """Install and load modules."""
159         _LOGGER.debug("Loading " + modules_type + " modules")
160         loaded_modules = []
161 
162         # Create modules directory if doesn't exist
163         if not os.path.isdir(self.modules_directory):
164             os.makedirs(self.modules_directory)
165 
166         for module in modules:
167 
168             # Set up module config
169             config = module
170             config = {} if config is None else config
171             config["name"] = module["name"]
172             config["type"] = modules_type
173             config["module_path"] = self.build_module_path("import", config)
174             config["install_path"] = self.build_module_path("install", config)
175             if "branch" not in config:
176                 config["branch"] = DEFAULT_MODULE_BRANCH
177 
178             # Remove module for reinstall if no-cache set
179             self.check_cache(config)
180 
181             # Install module
182             self._install_module(config)
183 
184             # Import module
185             self.current_import_config = config
186             module = self.import_module(config)
187             if module is not None:
188                 loaded_modules.append({
189                     "module": module,
190                     "config": config})
191             else:
192                 _LOGGER.error(
193                     "Module " + config["name"] + " failed to import")
194 
195         return loaded_modules
196 
197     def _install_module(self, config):
198         # pylint: disable=R0201
199         """Install a module."""
200         _LOGGER.debug("Installing " + config["name"])
201 
202         if os.path.isdir(config["install_path"]) or \
203                 os.path.isfile(config["install_path"] + ".py"):
204             # TODO Allow for updating or reinstalling of modules
205             _LOGGER.debug("Module " + config["name"] +
206                           " already installed, skipping")
207             return
208 
209         if "path" in config:
210             self._install_local_module(config)
211         else:
212             self._install_git_module(config)
213 
214         if os.path.isdir(config["install_path"]):
215             _LOGGER.debug("Installed " + config["name"] +
216                           " to " + config["install_path"])
217         else:
218             _LOGGER.debug("Install of " + config["name"] + " failed")
219 
220         # Install module dependancies
221         if os.path.isfile(config["install_path"] + "/requirements.txt"):
222             self.pip_install_deps(config["install_path"] +
223                                   "/requirements.txt")
224 
225     def _install_git_module(self, config):
226         """Install a module from a git repository."""
227         if config is not None and "repo" in config:
228             git_url = config["repo"]
229         else:
230             git_url = DEFAULT_GIT_URL + config["type"] + \
231                         "-" + config["name"] + ".git"
232 
233         if any(prefix in git_url for prefix in ["http", "https", "ssh"]):
234             # TODO Test if url or ssh path exists
235             # TODO Handle github authentication
236             _LOGGER.debug("Cloning from remote repository")
237             self.git_clone(git_url, config["install_path"],
238                            config["branch"])
239         else:
240             if os.path.isdir(git_url):
241                 _LOGGER.debug("Cloning from local repository")
242                 self.git_clone(git_url, config["install_path"],
243                                config["branch"])
244             else:
245                 _LOGGER.debug("Could not find local git repo " + git_url)
246 
247     @staticmethod
248     def _install_local_module(config):
249         """Install a module from a local path."""
250         installed = False
251 
252         installdir, _ = os.path.split(config["install_path"])
253         if not os.path.isdir(installdir):
254             os.makedirs(installdir, exist_ok=True)
255 
256         if os.path.isdir(config["path"]):
257             shutil.copytree(config["path"], config["install_path"])
258             installed = True
259 
260         if os.path.isfile(config["path"]):
261             os.makedirs(config["install_path"], exist_ok=True)
262             shutil.copyfile(config["path"], config["install_path"] +
263                             "/__init__.py")
264             installed = True
265 
266         if not installed:
267             _LOGGER.error("Failed to install from " + config["path"])
268
```

Path: `opsdroid/const.py`
Content:
```
1 """Constants used by OpsDroid."""
2 
3 __version__ = "0.7.1"
4 
5 LOG_FILENAME = 'output.log'
6 DEFAULT_GIT_URL = "https://github.com/opsdroid/"
7 MODULES_DIRECTORY = "modules"
8 DEFAULT_MODULE_BRANCH = "master"
9
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/opsdroid/const.py b/opsdroid/const.py
--- a/opsdroid/const.py
+++ b/opsdroid/const.py
@@ -4,5 +4,6 @@
 
 LOG_FILENAME = 'output.log'
 DEFAULT_GIT_URL = "https://github.com/opsdroid/"
-MODULES_DIRECTORY = "modules"
+MODULES_DIRECTORY = "opsdroid-modules"
+DEFAULT_MODULES_PATH = "~/.opsdroid/modules"
 DEFAULT_MODULE_BRANCH = "master"
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -8,7 +8,8 @@
 import importlib
 import yaml
 from opsdroid.const import (
-    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULE_BRANCH)
+    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,
+    DEFAULT_MODULE_BRANCH)
 
 
 _LOGGER = logging.getLogger(__name__)
@@ -20,7 +21,7 @@
     def __init__(self, opsdroid):
         """Create object with opsdroid instance."""
         self.opsdroid = opsdroid
-        self.modules_directory = MODULES_DIRECTORY
+        self.modules_directory = None
         self.current_import_config = None
         _LOGGER.debug("Loaded loader")
 
@@ -122,16 +123,26 @@
         except FileNotFoundError as error:
             self.opsdroid.critical(str(error), 1)
 
+    def setup_modules_directory(self, config):
+        """Create and configure the modules directory."""
+        module_path = os.path.expanduser(
+            config.get("module-path", DEFAULT_MODULES_PATH))
+        sys.path.append(module_path)
+
+        if not os.path.isdir(module_path):
+            os.makedirs(module_path, exist_ok=True)
+
+        self.modules_directory = os.path.join(module_path, MODULES_DIRECTORY)
+
+        # Create modules directory if doesn't exist
+        if not os.path.isdir(self.modules_directory):
+            os.makedirs(self.modules_directory)
+
     def load_modules_from_config(self, config):
         """Load all module types based on config."""
         _LOGGER.debug("Loading modules from config")
 
-        if "module-path" in config:
-            sys.path.append(config["module-path"])
-            if not os.path.isdir(config["module-path"]):
-                os.makedirs(config["module-path"], exist_ok=True)
-            self.modules_directory = os.path.join(config["module-path"],
-                                                  self.modules_directory)
+        self.setup_modules_directory(config)
 
         connectors, databases, skills = None, None, None
 
@@ -159,10 +170,6 @@
         _LOGGER.debug("Loading " + modules_type + " modules")
         loaded_modules = []
 
-        # Create modules directory if doesn't exist
-        if not os.path.isdir(self.modules_directory):
-            os.makedirs(self.modules_directory)
-
         for module in modules:
 
             # Set up module config

{"golden_diff": "diff --git a/opsdroid/const.py b/opsdroid/const.py\n--- a/opsdroid/const.py\n+++ b/opsdroid/const.py\n@@ -4,5 +4,6 @@\n \n LOG_FILENAME = 'output.log'\n DEFAULT_GIT_URL = \"https://github.com/opsdroid/\"\n-MODULES_DIRECTORY = \"modules\"\n+MODULES_DIRECTORY = \"opsdroid-modules\"\n+DEFAULT_MODULES_PATH = \"~/.opsdroid/modules\"\n DEFAULT_MODULE_BRANCH = \"master\"\ndiff --git a/opsdroid/loader.py b/opsdroid/loader.py\n--- a/opsdroid/loader.py\n+++ b/opsdroid/loader.py\n@@ -8,7 +8,8 @@\n import importlib\n import yaml\n from opsdroid.const import (\n-    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULE_BRANCH)\n+    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,\n+    DEFAULT_MODULE_BRANCH)\n \n \n _LOGGER = logging.getLogger(__name__)\n@@ -20,7 +21,7 @@\n     def __init__(self, opsdroid):\n         \"\"\"Create object with opsdroid instance.\"\"\"\n         self.opsdroid = opsdroid\n-        self.modules_directory = MODULES_DIRECTORY\n+        self.modules_directory = None\n         self.current_import_config = None\n         _LOGGER.debug(\"Loaded loader\")\n \n@@ -122,16 +123,26 @@\n         except FileNotFoundError as error:\n             self.opsdroid.critical(str(error), 1)\n \n+    def setup_modules_directory(self, config):\n+        \"\"\"Create and configure the modules directory.\"\"\"\n+        module_path = os.path.expanduser(\n+            config.get(\"module-path\", DEFAULT_MODULES_PATH))\n+        sys.path.append(module_path)\n+\n+        if not os.path.isdir(module_path):\n+            os.makedirs(module_path, exist_ok=True)\n+\n+        self.modules_directory = os.path.join(module_path, MODULES_DIRECTORY)\n+\n+        # Create modules directory if doesn't exist\n+        if not os.path.isdir(self.modules_directory):\n+            os.makedirs(self.modules_directory)\n+\n     def load_modules_from_config(self, config):\n         \"\"\"Load all module types based on config.\"\"\"\n         _LOGGER.debug(\"Loading modules from config\")\n \n-        if \"module-path\" in config:\n-            sys.path.append(config[\"module-path\"])\n-            if not os.path.isdir(config[\"module-path\"]):\n-                os.makedirs(config[\"module-path\"], exist_ok=True)\n-            self.modules_directory = os.path.join(config[\"module-path\"],\n-                                                  self.modules_directory)\n+        self.setup_modules_directory(config)\n \n         connectors, databases, skills = None, None, None\n \n@@ -159,10 +170,6 @@\n         _LOGGER.debug(\"Loading \" + modules_type + \" modules\")\n         loaded_modules = []\n \n-        # Create modules directory if doesn't exist\n-        if not os.path.isdir(self.modules_directory):\n-            os.makedirs(self.modules_directory)\n-\n         for module in modules:\n \n             # Set up module config\n", "issue": "Change default modules directory\nCurrently the default modules directory location is `./modules`. \r\n\r\nThis makes a few assumptions:  \r\n* Current directory is in the python path \r\n* There are no other python modules in the current directory\r\n* There are no other modules named `modules`\r\n* Current directory is writable\r\n\r\nA better default location may be `~/.opsdroid/modules/opsdroid-modules`. This would be created if it doesn't exist and `~/.opsdroid/modules` could be added to the python path without fear of collision as `opsdroid-modules` is less generic. As it is in the home directory we can be fairly sure it is writable. \r\n\r\nAlso when a user specifies a custom modules directory it should still be suffixed with `/opsdroid-modules` and the custom directory should be added to the python path. \n", "before_files": [{"content": "\"\"\"Class for loading in modules to OpsDroid.\"\"\"\n\nimport logging\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport importlib\nimport yaml\nfrom opsdroid.const import (\n    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULE_BRANCH)\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass Loader:\n    \"\"\"Class to load in config and modules.\"\"\"\n\n    def __init__(self, opsdroid):\n        \"\"\"Create object with opsdroid instance.\"\"\"\n        self.opsdroid = opsdroid\n        self.modules_directory = MODULES_DIRECTORY\n        self.current_import_config = None\n        _LOGGER.debug(\"Loaded loader\")\n\n    @staticmethod\n    def import_module(config):\n        \"\"\"Import module namespace as variable and return it.\"\"\"\n        try:\n            module = importlib.import_module(\n                config[\"module_path\"] + \".\" + config[\"name\"])\n            _LOGGER.debug(\"Loaded \" + config[\"type\"] + \": \" +\n                          config[\"module_path\"])\n            return module\n        except ImportError as error:\n            _LOGGER.debug(\"Failed to load \" + config[\"type\"] +\n                          \" \" + config[\"module_path\"] + \".\" + config[\"name\"])\n            _LOGGER.debug(error)\n\n        try:\n            module = importlib.import_module(\n                config[\"module_path\"])\n            _LOGGER.debug(\"Loaded \" + config[\"type\"] + \": \" +\n                          config[\"module_path\"])\n            return module\n        except ImportError as error:\n            _LOGGER.debug(\"Failed to load \" + config[\"type\"] +\n                          \" \" + config[\"module_path\"])\n            _LOGGER.debug(error)\n\n        _LOGGER.error(\"Failed to load \" + config[\"type\"] +\n                      \" \" + config[\"module_path\"])\n        return None\n\n    @staticmethod\n    def check_cache(config):\n        \"\"\"Remove module if 'no-cache' set in config.\"\"\"\n        if \"no-cache\" in config \\\n                and config[\"no-cache\"]:\n            _LOGGER.debug(\"'no-cache' set, removing \" + config[\"install_path\"])\n            if os.path.isdir(config[\"install_path\"]):\n                shutil.rmtree(config[\"install_path\"])\n            if os.path.isfile(config[\"install_path\"] + \".py\"):\n                os.remove(config[\"install_path\"] + \".py\")\n\n    def build_module_path(self, path_type, config):\n        \"\"\"Generate the module path from name and type.\"\"\"\n        if path_type == \"import\":\n            return MODULES_DIRECTORY + \".\" + config[\"type\"] + \\\n                        \".\" + config[\"name\"]\n        elif path_type == \"install\":\n            return self.modules_directory + \"/\" + config[\"type\"] + \\\n                        \"/\" + config[\"name\"]\n\n    @staticmethod\n    def git_clone(git_url, install_path, branch):\n        \"\"\"Clone a git repo to a location and wait for finish.\"\"\"\n        process = subprocess.Popen([\"git\", \"clone\", \"-b\", branch,\n                                    git_url, install_path], shell=False,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.PIPE)\n        for output in process.communicate():\n            if output != \"\":\n                for line in output.splitlines():\n                    _LOGGER.debug(str(line).strip())\n        process.wait()\n\n    @staticmethod\n    def pip_install_deps(requirements_path):\n        \"\"\"Pip install a requirements.txt file and wait for finish.\"\"\"\n        process = subprocess.Popen([\"pip\", \"install\", \"-r\", requirements_path],\n                                   shell=False,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.PIPE)\n        for output in process.communicate():\n            if output != \"\":\n                for line in output.splitlines():\n                    _LOGGER.debug(str(line).strip())\n        process.wait()\n\n    def load_config_file(self, config_paths):\n        \"\"\"Load a yaml config file from path.\"\"\"\n        config_path = \"\"\n        for possible_path in config_paths:\n            if not os.path.isfile(possible_path):\n                _LOGGER.debug(\"Config file \" + possible_path +\n                              \" not found\")\n            else:\n                config_path = possible_path\n                break\n\n        if not config_path:\n            self.opsdroid.critical(\"No configuration files found\", 1)\n\n        try:\n            with open(config_path, 'r') as stream:\n                _LOGGER.info(\"Loaded config from %s\", config_path)\n                return yaml.load(stream)\n        except yaml.YAMLError as error:\n            self.opsdroid.critical(error, 1)\n        except FileNotFoundError as error:\n            self.opsdroid.critical(str(error), 1)\n\n    def load_modules_from_config(self, config):\n        \"\"\"Load all module types based on config.\"\"\"\n        _LOGGER.debug(\"Loading modules from config\")\n\n        if \"module-path\" in config:\n            sys.path.append(config[\"module-path\"])\n            if not os.path.isdir(config[\"module-path\"]):\n                os.makedirs(config[\"module-path\"], exist_ok=True)\n            self.modules_directory = os.path.join(config[\"module-path\"],\n                                                  self.modules_directory)\n\n        connectors, databases, skills = None, None, None\n\n        if 'databases' in config.keys():\n            databases = self._load_modules('database', config['databases'])\n        else:\n            _LOGGER.warning(\"No databases in configuration\")\n\n        if 'skills' in config.keys():\n            skills = self._load_modules('skill', config['skills'])\n        else:\n            self.opsdroid.critical(\n                \"No skills in configuration, at least 1 required\", 1)\n\n        if 'connectors' in config.keys():\n            connectors = self._load_modules('connector', config['connectors'])\n        else:\n            self.opsdroid.critical(\n                \"No connectors in configuration, at least 1 required\", 1)\n\n        return connectors, databases, skills\n\n    def _load_modules(self, modules_type, modules):\n        \"\"\"Install and load modules.\"\"\"\n        _LOGGER.debug(\"Loading \" + modules_type + \" modules\")\n        loaded_modules = []\n\n        # Create modules directory if doesn't exist\n        if not os.path.isdir(self.modules_directory):\n            os.makedirs(self.modules_directory)\n\n        for module in modules:\n\n            # Set up module config\n            config = module\n            config = {} if config is None else config\n            config[\"name\"] = module[\"name\"]\n            config[\"type\"] = modules_type\n            config[\"module_path\"] = self.build_module_path(\"import\", config)\n            config[\"install_path\"] = self.build_module_path(\"install\", config)\n            if \"branch\" not in config:\n                config[\"branch\"] = DEFAULT_MODULE_BRANCH\n\n            # Remove module for reinstall if no-cache set\n            self.check_cache(config)\n\n            # Install module\n            self._install_module(config)\n\n            # Import module\n            self.current_import_config = config\n            module = self.import_module(config)\n            if module is not None:\n                loaded_modules.append({\n                    \"module\": module,\n                    \"config\": config})\n            else:\n                _LOGGER.error(\n                    \"Module \" + config[\"name\"] + \" failed to import\")\n\n        return loaded_modules\n\n    def _install_module(self, config):\n        # pylint: disable=R0201\n        \"\"\"Install a module.\"\"\"\n        _LOGGER.debug(\"Installing \" + config[\"name\"])\n\n        if os.path.isdir(config[\"install_path\"]) or \\\n                os.path.isfile(config[\"install_path\"] + \".py\"):\n            # TODO Allow for updating or reinstalling of modules\n            _LOGGER.debug(\"Module \" + config[\"name\"] +\n                          \" already installed, skipping\")\n            return\n\n        if \"path\" in config:\n            self._install_local_module(config)\n        else:\n            self._install_git_module(config)\n\n        if os.path.isdir(config[\"install_path\"]):\n            _LOGGER.debug(\"Installed \" + config[\"name\"] +\n                          \" to \" + config[\"install_path\"])\n        else:\n            _LOGGER.debug(\"Install of \" + config[\"name\"] + \" failed\")\n\n        # Install module dependancies\n        if os.path.isfile(config[\"install_path\"] + \"/requirements.txt\"):\n            self.pip_install_deps(config[\"install_path\"] +\n                                  \"/requirements.txt\")\n\n    def _install_git_module(self, config):\n        \"\"\"Install a module from a git repository.\"\"\"\n        if config is not None and \"repo\" in config:\n            git_url = config[\"repo\"]\n        else:\n            git_url = DEFAULT_GIT_URL + config[\"type\"] + \\\n                        \"-\" + config[\"name\"] + \".git\"\n\n        if any(prefix in git_url for prefix in [\"http\", \"https\", \"ssh\"]):\n            # TODO Test if url or ssh path exists\n            # TODO Handle github authentication\n            _LOGGER.debug(\"Cloning from remote repository\")\n            self.git_clone(git_url, config[\"install_path\"],\n                           config[\"branch\"])\n        else:\n            if os.path.isdir(git_url):\n                _LOGGER.debug(\"Cloning from local repository\")\n                self.git_clone(git_url, config[\"install_path\"],\n                               config[\"branch\"])\n            else:\n                _LOGGER.debug(\"Could not find local git repo \" + git_url)\n\n    @staticmethod\n    def _install_local_module(config):\n        \"\"\"Install a module from a local path.\"\"\"\n        installed = False\n\n        installdir, _ = os.path.split(config[\"install_path\"])\n        if not os.path.isdir(installdir):\n            os.makedirs(installdir, exist_ok=True)\n\n        if os.path.isdir(config[\"path\"]):\n            shutil.copytree(config[\"path\"], config[\"install_path\"])\n            installed = True\n\n        if os.path.isfile(config[\"path\"]):\n            os.makedirs(config[\"install_path\"], exist_ok=True)\n            shutil.copyfile(config[\"path\"], config[\"install_path\"] +\n                            \"/__init__.py\")\n            installed = True\n\n        if not installed:\n            _LOGGER.error(\"Failed to install from \" + config[\"path\"])\n", "path": "opsdroid/loader.py"}, {"content": "\"\"\"Constants used by OpsDroid.\"\"\"\n\n__version__ = \"0.7.1\"\n\nLOG_FILENAME = 'output.log'\nDEFAULT_GIT_URL = \"https://github.com/opsdroid/\"\nMODULES_DIRECTORY = \"modules\"\nDEFAULT_MODULE_BRANCH = \"master\"\n", "path": "opsdroid/const.py"}], "after_files": [{"content": "\"\"\"Class for loading in modules to OpsDroid.\"\"\"\n\nimport logging\nimport os\nimport sys\nimport shutil\nimport subprocess\nimport importlib\nimport yaml\nfrom opsdroid.const import (\n    DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,\n    DEFAULT_MODULE_BRANCH)\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass Loader:\n    \"\"\"Class to load in config and modules.\"\"\"\n\n    def __init__(self, opsdroid):\n        \"\"\"Create object with opsdroid instance.\"\"\"\n        self.opsdroid = opsdroid\n        self.modules_directory = None\n        self.current_import_config = None\n        _LOGGER.debug(\"Loaded loader\")\n\n    @staticmethod\n    def import_module(config):\n        \"\"\"Import module namespace as variable and return it.\"\"\"\n        try:\n            module = importlib.import_module(\n                config[\"module_path\"] + \".\" + config[\"name\"])\n            _LOGGER.debug(\"Loaded \" + config[\"type\"] + \": \" +\n                          config[\"module_path\"])\n            return module\n        except ImportError as error:\n            _LOGGER.debug(\"Failed to load \" + config[\"type\"] +\n                          \" \" + config[\"module_path\"] + \".\" + config[\"name\"])\n            _LOGGER.debug(error)\n\n        try:\n            module = importlib.import_module(\n                config[\"module_path\"])\n            _LOGGER.debug(\"Loaded \" + config[\"type\"] + \": \" +\n                          config[\"module_path\"])\n            return module\n        except ImportError as error:\n            _LOGGER.debug(\"Failed to load \" + config[\"type\"] +\n                          \" \" + config[\"module_path\"])\n            _LOGGER.debug(error)\n\n        _LOGGER.error(\"Failed to load \" + config[\"type\"] +\n                      \" \" + config[\"module_path\"])\n        return None\n\n    @staticmethod\n    def check_cache(config):\n        \"\"\"Remove module if 'no-cache' set in config.\"\"\"\n        if \"no-cache\" in config \\\n                and config[\"no-cache\"]:\n            _LOGGER.debug(\"'no-cache' set, removing \" + config[\"install_path\"])\n            if os.path.isdir(config[\"install_path\"]):\n                shutil.rmtree(config[\"install_path\"])\n            if os.path.isfile(config[\"install_path\"] + \".py\"):\n                os.remove(config[\"install_path\"] + \".py\")\n\n    def build_module_path(self, path_type, config):\n        \"\"\"Generate the module path from name and type.\"\"\"\n        if path_type == \"import\":\n            return MODULES_DIRECTORY + \".\" + config[\"type\"] + \\\n                        \".\" + config[\"name\"]\n        elif path_type == \"install\":\n            return self.modules_directory + \"/\" + config[\"type\"] + \\\n                        \"/\" + config[\"name\"]\n\n    @staticmethod\n    def git_clone(git_url, install_path, branch):\n        \"\"\"Clone a git repo to a location and wait for finish.\"\"\"\n        process = subprocess.Popen([\"git\", \"clone\", \"-b\", branch,\n                                    git_url, install_path], shell=False,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.PIPE)\n        for output in process.communicate():\n            if output != \"\":\n                for line in output.splitlines():\n                    _LOGGER.debug(str(line).strip())\n        process.wait()\n\n    @staticmethod\n    def pip_install_deps(requirements_path):\n        \"\"\"Pip install a requirements.txt file and wait for finish.\"\"\"\n        process = subprocess.Popen([\"pip\", \"install\", \"-r\", requirements_path],\n                                   shell=False,\n                                   stdout=subprocess.PIPE,\n                                   stderr=subprocess.PIPE)\n        for output in process.communicate():\n            if output != \"\":\n                for line in output.splitlines():\n                    _LOGGER.debug(str(line).strip())\n        process.wait()\n\n    def load_config_file(self, config_paths):\n        \"\"\"Load a yaml config file from path.\"\"\"\n        config_path = \"\"\n        for possible_path in config_paths:\n            if not os.path.isfile(possible_path):\n                _LOGGER.debug(\"Config file \" + possible_path +\n                              \" not found\")\n            else:\n                config_path = possible_path\n                break\n\n        if not config_path:\n            self.opsdroid.critical(\"No configuration files found\", 1)\n\n        try:\n            with open(config_path, 'r') as stream:\n                _LOGGER.info(\"Loaded config from %s\", config_path)\n                return yaml.load(stream)\n        except yaml.YAMLError as error:\n            self.opsdroid.critical(error, 1)\n        except FileNotFoundError as error:\n            self.opsdroid.critical(str(error), 1)\n\n    def setup_modules_directory(self, config):\n        \"\"\"Create and configure the modules directory.\"\"\"\n        module_path = os.path.expanduser(\n            config.get(\"module-path\", DEFAULT_MODULES_PATH))\n        sys.path.append(module_path)\n\n        if not os.path.isdir(module_path):\n            os.makedirs(module_path, exist_ok=True)\n\n        self.modules_directory = os.path.join(module_path, MODULES_DIRECTORY)\n\n        # Create modules directory if doesn't exist\n        if not os.path.isdir(self.modules_directory):\n            os.makedirs(self.modules_directory)\n\n    def load_modules_from_config(self, config):\n        \"\"\"Load all module types based on config.\"\"\"\n        _LOGGER.debug(\"Loading modules from config\")\n\n        self.setup_modules_directory(config)\n\n        connectors, databases, skills = None, None, None\n\n        if 'databases' in config.keys():\n            databases = self._load_modules('database', config['databases'])\n        else:\n            _LOGGER.warning(\"No databases in configuration\")\n\n        if 'skills' in config.keys():\n            skills = self._load_modules('skill', config['skills'])\n        else:\n            self.opsdroid.critical(\n                \"No skills in configuration, at least 1 required\", 1)\n\n        if 'connectors' in config.keys():\n            connectors = self._load_modules('connector', config['connectors'])\n        else:\n            self.opsdroid.critical(\n                \"No connectors in configuration, at least 1 required\", 1)\n\n        return connectors, databases, skills\n\n    def _load_modules(self, modules_type, modules):\n        \"\"\"Install and load modules.\"\"\"\n        _LOGGER.debug(\"Loading \" + modules_type + \" modules\")\n        loaded_modules = []\n\n        for module in modules:\n\n            # Set up module config\n            config = module\n            config = {} if config is None else config\n            config[\"name\"] = module[\"name\"]\n            config[\"type\"] = modules_type\n            config[\"module_path\"] = self.build_module_path(\"import\", config)\n            config[\"install_path\"] = self.build_module_path(\"install\", config)\n            if \"branch\" not in config:\n                config[\"branch\"] = DEFAULT_MODULE_BRANCH\n\n            # Remove module for reinstall if no-cache set\n            self.check_cache(config)\n\n            # Install module\n            self._install_module(config)\n\n            # Import module\n            self.current_import_config = config\n            module = self.import_module(config)\n            if module is not None:\n                loaded_modules.append({\n                    \"module\": module,\n                    \"config\": config})\n            else:\n                _LOGGER.error(\n                    \"Module \" + config[\"name\"] + \" failed to import\")\n\n        return loaded_modules\n\n    def _install_module(self, config):\n        # pylint: disable=R0201\n        \"\"\"Install a module.\"\"\"\n        _LOGGER.debug(\"Installing \" + config[\"name\"])\n\n        if os.path.isdir(config[\"install_path\"]) or \\\n                os.path.isfile(config[\"install_path\"] + \".py\"):\n            # TODO Allow for updating or reinstalling of modules\n            _LOGGER.debug(\"Module \" + config[\"name\"] +\n                          \" already installed, skipping\")\n            return\n\n        if \"path\" in config:\n            self._install_local_module(config)\n        else:\n            self._install_git_module(config)\n\n        if os.path.isdir(config[\"install_path\"]):\n            _LOGGER.debug(\"Installed \" + config[\"name\"] +\n                          \" to \" + config[\"install_path\"])\n        else:\n            _LOGGER.debug(\"Install of \" + config[\"name\"] + \" failed\")\n\n        # Install module dependancies\n        if os.path.isfile(config[\"install_path\"] + \"/requirements.txt\"):\n            self.pip_install_deps(config[\"install_path\"] +\n                                  \"/requirements.txt\")\n\n    def _install_git_module(self, config):\n        \"\"\"Install a module from a git repository.\"\"\"\n        if config is not None and \"repo\" in config:\n            git_url = config[\"repo\"]\n        else:\n            git_url = DEFAULT_GIT_URL + config[\"type\"] + \\\n                        \"-\" + config[\"name\"] + \".git\"\n\n        if any(prefix in git_url for prefix in [\"http\", \"https\", \"ssh\"]):\n            # TODO Test if url or ssh path exists\n            # TODO Handle github authentication\n            _LOGGER.debug(\"Cloning from remote repository\")\n            self.git_clone(git_url, config[\"install_path\"],\n                           config[\"branch\"])\n        else:\n            if os.path.isdir(git_url):\n                _LOGGER.debug(\"Cloning from local repository\")\n                self.git_clone(git_url, config[\"install_path\"],\n                               config[\"branch\"])\n            else:\n                _LOGGER.debug(\"Could not find local git repo \" + git_url)\n\n    @staticmethod\n    def _install_local_module(config):\n        \"\"\"Install a module from a local path.\"\"\"\n        installed = False\n\n        installdir, _ = os.path.split(config[\"install_path\"])\n        if not os.path.isdir(installdir):\n            os.makedirs(installdir, exist_ok=True)\n\n        if os.path.isdir(config[\"path\"]):\n            shutil.copytree(config[\"path\"], config[\"install_path\"])\n            installed = True\n\n        if os.path.isfile(config[\"path\"]):\n            os.makedirs(config[\"install_path\"], exist_ok=True)\n            shutil.copyfile(config[\"path\"], config[\"install_path\"] +\n                            \"/__init__.py\")\n            installed = True\n\n        if not installed:\n            _LOGGER.error(\"Failed to install from \" + config[\"path\"])\n", "path": "opsdroid/loader.py"}, {"content": "\"\"\"Constants used by OpsDroid.\"\"\"\n\n__version__ = \"0.7.1\"\n\nLOG_FILENAME = 'output.log'\nDEFAULT_GIT_URL = \"https://github.com/opsdroid/\"\nMODULES_DIRECTORY = \"opsdroid-modules\"\nDEFAULT_MODULES_PATH = \"~/.opsdroid/modules\"\nDEFAULT_MODULE_BRANCH = \"master\"\n", "path": "opsdroid/const.py"}]}