Repository: opsdroid/opsdroid

Issue:
Generate default config automatically
Instead of running `opsdroid --gen-config` and piping the output into a file opsdroid should simply check for the existence of a config file and if it can't find one it should create one in `~/.opsdroid` using the default config file.

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

Coverage decreased (-1.1%) to 94.764% when pulling **94188fe0a3bc45018ef05f76be87a396347e41ee on jacobtomlinson:auto-gen-conf** into **a3e93bda15e1debbc368f5c947a10ad5f1027fbe on opsdroid:master**.

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

Coverage decreased (-0.1%) to 95.777% when pulling **1435962988a19c2d69c8deff1984acf4772c9441 on jacobtomlinson:auto-gen-conf** into **a3e93bda15e1debbc368f5c947a10ad5f1027fbe on opsdroid:master**.

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

Coverage decreased (-0.1%) to 95.777% when pulling **1435962988a19c2d69c8deff1984acf4772c9441 on jacobtomlinson:auto-gen-conf** into **a3e93bda15e1debbc368f5c947a10ad5f1027fbe on opsdroid:master**.

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

Coverage increased (+0.2%) to 96.115% when pulling **6c3aa2538aefb511db794be759ebdaf290131a77 on jacobtomlinson:auto-gen-conf** into **a3e93bda15e1debbc368f5c947a10ad5f1027fbe on opsdroid:master**.


Patch:
diff --git a/README.md b/README.md
index 098e647..7b9ad7b 100644
--- a/README.md
+++ b/README.md
@@ -10,16 +10,14 @@ An open source python chat-ops bot framework.
 
 ```
 pip3 install opsdroid
-mkdir ~/.opsdroid
-opsdroid --gen-config > ~/.opsdroid/configuration.yaml
 opsdroid
 ```
 
 ## Configuration
 
-Configuration is done in a yaml file called `configuration.yaml`. See the [full reference](http://opsdroid.readthedocs.io/en/latest/configuration-reference/).
+Configuration is done in a yaml file called `configuration.yaml`. This will be created automatically for you in `~/.opsdroid`. See the [full reference](http://opsdroid.readthedocs.io/en/latest/configuration-reference/).
 
-Example:
+Example config:
 
 ```yaml
 ##                      _           _     _
diff --git a/docs/getting-started.md b/docs/getting-started.md
index f9b2692..1f7ebb7 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -22,6 +22,8 @@ For configuration you simply need to create a single YAML file named `configurat
  * `~/.opsdroid/configuration.yaml`
  * `/etc/opsdroid/configuration.yaml`
 
+ If none are found then `~/.opsdroid/configuration.yaml` will be created for you.
+
 The opsdroid project itself is very simple and requires modules to give it functionality. In your configuration file you must specify the connector, skill and database* modules you wish to use and any options they may require.
 
 **Connectors** are modules for connecting opsdroid to your specific chat service. **Skills** are modules which define what actions opsdroid should perform based on different chat messages. **Database** modules connect opsdroid to your chosen database and allows skills to store information between messages.
diff --git a/opsdroid/__main__.py b/opsdroid/__main__.py
index 423fcaa..5513e8f 100644
--- a/opsdroid/__main__.py
+++ b/opsdroid/__main__.py
@@ -1,12 +1,11 @@
 """Starts opsdroid."""
 
 import sys
-import os
 import logging
 import argparse
 
 from opsdroid.core import OpsDroid
-from opsdroid.const import LOG_FILENAME
+from opsdroid.const import LOG_FILENAME, EXAMPLE_CONFIG_FILE
 from opsdroid.web import Web
 
 
@@ -88,10 +87,7 @@ def main():
     args = parse_args(sys.argv[1:])
 
     if args.gen_config:
-        path = os.path.join(
-            os.path.dirname(os.path.abspath(__file__)),
-            "configuration/example_configuration.yaml")
-        with open(path, 'r') as conf:
+        with open(EXAMPLE_CONFIG_FILE, 'r') as conf:
             print(conf.read())
         sys.exit(0)
 
diff --git a/opsdroid/configuration/example_configuration.yaml b/opsdroid/configuration/example_configuration.yaml
index 8b3d6af..abb841c 100644
--- a/opsdroid/configuration/example_configuration.yaml
+++ b/opsdroid/configuration/example_configuration.yaml
@@ -37,6 +37,7 @@
 ## Connector modules
 connectors:
   - name: shell
+  - name: websocket
 
 ## Database modules (optional)
 # databases:
diff --git a/opsdroid/const.py b/opsdroid/const.py
index c826aed..3d5b8b4 100644
--- a/opsdroid/const.py
+++ b/opsdroid/const.py
@@ -1,9 +1,14 @@
 """Constants used by OpsDroid."""
+import os
 
 __version__ = "0.8.0"
 
 LOG_FILENAME = 'output.log'
 DEFAULT_GIT_URL = "https://github.com/opsdroid/"
 MODULES_DIRECTORY = "opsdroid-modules"
-DEFAULT_MODULES_PATH = "~/.opsdroid/modules"
+DEFAULT_ROOT_PATH = os.path.join(os.path.expanduser("~"), ".opsdroid")
+DEFAULT_MODULES_PATH = os.path.join(DEFAULT_ROOT_PATH, "modules")
+DEFAULT_CONFIG_PATH = os.path.join(DEFAULT_ROOT_PATH, "configuration.yaml")
 DEFAULT_MODULE_BRANCH = "master"
+EXAMPLE_CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                                   "configuration/example_configuration.yaml")
diff --git a/opsdroid/core.py b/opsdroid/core.py
index 0f43afd..5e3b545 100644
--- a/opsdroid/core.py
+++ b/opsdroid/core.py
@@ -4,7 +4,6 @@ import logging
 import sys
 import weakref
 import asyncio
-import os.path
 
 from opsdroid.memory import Memory
 from opsdroid.connector import Connector
@@ -13,6 +12,7 @@ from opsdroid.loader import Loader
 from opsdroid.parsers.regex import parse_regex
 from opsdroid.parsers.apiai import parse_apiai
 from opsdroid.parsers.crontab import parse_crontab
+from opsdroid.const import DEFAULT_CONFIG_PATH
 
 
 _LOGGER = logging.getLogger(__name__)
@@ -89,8 +89,7 @@ class OpsDroid():
         """Load configuration."""
         self.config = self.loader.load_config_file([
             "./configuration.yaml",
-            os.path.join(os.path.expanduser("~"),
-                         ".opsdroid/configuration.yaml"),
+            DEFAULT_CONFIG_PATH,
             "/etc/opsdroid/configuration.yaml"
             ])
 
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
index 34d1330..0f6e1a8 100644
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -9,7 +9,7 @@ import importlib
 import yaml
 from opsdroid.const import (
     DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,
-    DEFAULT_MODULE_BRANCH)
+    DEFAULT_MODULE_BRANCH, DEFAULT_CONFIG_PATH, EXAMPLE_CONFIG_FILE)
 
 
 _LOGGER = logging.getLogger(__name__)
@@ -100,6 +100,16 @@ class Loader:
                     _LOGGER.debug(str(line).strip())
         process.wait()
 
+    @staticmethod
+    def create_default_config(config_path):
+        """Create a default config file based on the included example."""
+        _LOGGER.info("Creating %s.", config_path)
+        config_dir, _ = os.path.split(config_path)
+        if not os.path.isdir(config_dir):
+            os.makedirs(config_dir)
+        shutil.copyfile(EXAMPLE_CONFIG_FILE, config_path)
+        return config_path
+
     def load_config_file(self, config_paths):
         """Load a yaml config file from path."""
         config_path = ""
@@ -112,7 +122,8 @@ class Loader:
                 break
 
         if not config_path:
-            self.opsdroid.critical("No configuration files found", 1)
+            _LOGGER.info("No configuration files found.")
+            config_path = self.create_default_config(DEFAULT_CONFIG_PATH)
 
         try:
             with open(config_path, 'r') as stream:


Test patch:
diff --git a/tests/test_loader.py b/tests/test_loader.py
index ae83378..155e262 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -29,10 +29,28 @@ class TestLoader(unittest.TestCase):
         config = loader.load_config_file(["tests/configs/minimal.yaml"])
         self.assertIsNotNone(config)
 
+    def test_create_default_config(self):
+        test_config_path = "/tmp/test_config_path/configuration.yaml"
+        opsdroid, loader = self.setup()
+
+        self.assertEqual(loader.create_default_config(test_config_path),
+                         test_config_path)
+        self.assertTrue(os.path.isfile(test_config_path))
+        shutil.rmtree(os.path.split(test_config_path)[0])
+
+    def test_generate_config_if_none_exist(self):
+        opsdroid, loader = self.setup()
+        loader.create_default_config = mock.Mock(
+            return_value="tests/configs/minimal.yaml")
+        loader.load_config_file(["file_which_does_not_exist"])
+        self.assertTrue(loader.create_default_config.called)
+
     def test_load_non_existant_config_file(self):
         opsdroid, loader = self.setup()
-        loader.opsdroid.critical = mock.Mock()
+        loader.create_default_config = mock.Mock(
+            return_value="/tmp/my_nonexistant_config")
         loader.load_config_file(["file_which_does_not_exist"])
+        self.assertTrue(loader.create_default_config.called)
         self.assertTrue(loader.opsdroid.critical.called)
 
     def test_load_broken_config_file(self):