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**.

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:

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.
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):

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y git gcc
3.9
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==5.0.1
chardet==5.2.0
coverage==7.8.0
exceptiongroup==1.2.2
execnet==2.1.1
iniconfig==2.1.0
multidict==6.2.0
-e git+https://github.com/opsdroid/opsdroid.git@a3e93bda15e1debbc368f5c947a10ad5f1027fbe#egg=opsdroid
packaging==24.2
pluggy==1.5.0
pycron==0.40
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
PyYAML==3.12
six==1.17.0
tomli==2.2.1
typing_extensions==4.13.0
yarl==0.9.8

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
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - 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=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==1.3.5
      - arrow==0.10.0
      - async-timeout==5.0.1
      - chardet==5.2.0
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - iniconfig==2.1.0
      - multidict==6.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pycron==0.40
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - pyyaml==3.12
      - six==1.17.0
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - yarl==0.9.8
prefix: /opt/conda/envs/opsdroid

tests/test_loader.py::TestLoader::test_create_default_config tests/test_loader.py::TestLoader::test_generate_config_if_none_exist tests/test_loader.py::TestLoader::test_load_non_existant_config_file
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_modules tests/test_loader.py::TestLoader::test_pip_install_deps
opsdroid/__main__.py opsdroid/const.py opsdroid/configuration/example_configuration.yaml opsdroid/core.py README.md docs/getting-started.md opsdroid/loader.py
opsdroid/__main__.py opsdroid/const.py opsdroid/configuration/example_configuration.yaml opsdroid/core.py README.md docs/getting-started.md opsdroid/loader.py