coveralls: 
[![Coverage Status](https://coveralls.io/builds/11771359/badge)](https://coveralls.io/builds/11771359)

Coverage increased (+0.02%) to 95.876% when pulling **7372d076c962767f4d418fd0e84f96a6a2f02aa6 on jacobtomlinson:new-module-path** into **b6a6b803844b36e49146b468dc104be1e49235a5 on opsdroid:dev**.

diff --git a/opsdroid/const.py b/opsdroid/const.py
index be722ef..c12d2ae 100644
--- a/opsdroid/const.py
+++ b/opsdroid/const.py
@@ -4,5 +4,6 @@ __version__ = "0.7.1"
 
 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
index e494cda..34d1330 100644
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -8,7 +8,8 @@ import subprocess
 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 @@ class Loader:
     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 @@ class Loader:
         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 @@ class Loader:
         _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

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. 
diff --git a/tests/test_loader.py b/tests/test_loader.py
index 1724997..ae83378 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -153,20 +153,10 @@ class TestLoader(unittest.TestCase):
         with mock.patch.object(loader, '_install_module') as mockinstall, \
                 mock.patch.object(loader, 'import_module',
                                   mockedmodule) as mockimport:
+            loader.setup_modules_directory({})
             loader._load_modules(modules_type, modules)
-            mockinstall.assert_called_with({
-                'branch': 'master',
-                'module_path': 'modules.test.testmodule',
-                'name': 'testmodule',
-                'type': modules_type,
-                'install_path': 'modules/test/testmodule'})
-            mockimport.assert_called_with({
-                'module_path': 'modules.test.testmodule',
-                'name': 'testmodule',
-                'type': modules_type,
-                'branch': 'master',
-                'install_path':
-                'modules/test/testmodule'})
+            assert mockinstall.call_count
+            assert mockimport.call_count
 
     def test_install_existing_module(self):
         opsdroid, loader = self.setup()

pip3 install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov
apt-get update apt-get install -y git
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==1.3.5
arrow==0.10.0
async-timeout==4.0.2
attrs==22.2.0
certifi==2021.5.30
chardet==5.0.0
coverage==6.2
importlib-metadata==4.8.3
iniconfig==1.1.1
multidict==5.2.0
-e git+https://github.com/opsdroid/opsdroid.git@b6a6b803844b36e49146b468dc104be1e49235a5#egg=opsdroid
packaging==21.3
pluggy==1.0.0
py==1.11.0
pycron==0.40
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
PyYAML==3.12
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
yarl==0.9.8
zipp==3.6.0

name: opsdroid
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==1.3.5
      - arrow==0.10.0
      - async-timeout==4.0.2
      - attrs==22.2.0
      - chardet==5.0.0
      - coverage==6.2
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - multidict==5.2.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pycron==0.40
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - pyyaml==3.12
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - yarl==0.9.8
      - zipp==3.6.0
prefix: /opt/conda/envs/opsdroid

tests/test_loader.py::TestLoader::test_load_modules
tests/test_loader.py::TestLoader::test_build_module_path tests/test_loader.py::TestLoader::test_check_cache_leaves tests/test_loader.py::TestLoader::test_check_cache_removes_dir tests/test_loader.py::TestLoader::test_check_cache_removes_file tests/test_loader.py::TestLoader::test_git_clone tests/test_loader.py::TestLoader::test_import_module tests/test_loader.py::TestLoader::test_import_module_failure tests/test_loader.py::TestLoader::test_import_module_new tests/test_loader.py::TestLoader::test_install_default_remote_module tests/test_loader.py::TestLoader::test_install_existing_module tests/test_loader.py::TestLoader::test_install_local_module_dir tests/test_loader.py::TestLoader::test_install_local_module_failure tests/test_loader.py::TestLoader::test_install_local_module_file tests/test_loader.py::TestLoader::test_install_missing_local_module tests/test_loader.py::TestLoader::test_install_specific_local_git_module tests/test_loader.py::TestLoader::test_install_specific_local_path_module tests/test_loader.py::TestLoader::test_install_specific_remote_module tests/test_loader.py::TestLoader::test_load_broken_config_file tests/test_loader.py::TestLoader::test_load_config tests/test_loader.py::TestLoader::test_load_config_file tests/test_loader.py::TestLoader::test_load_empty_config tests/test_loader.py::TestLoader::test_load_non_existant_config_file tests/test_loader.py::TestLoader::test_pip_install_deps
opsdroid/const.py opsdroid/loader.py
opsdroid/const.py opsdroid/loader.py