We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add a config option to disable registering a route for /
In the web server by default a route is registered for the base / path. This should be configurable in case a user wants to register their own.

--- 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/web.py`
Content:
```
1 """Submodule to handle web requests in opsdroid."""
2 
3 import json
4 import logging
5 import ssl
6 
7 from aiohttp import web
8 
9 from opsdroid import __version__
10 
11 
12 _LOGGER = logging.getLogger(__name__)
13 
14 
15 class Web:
16     """Create class for opsdroid Web server."""
17 
18     def __init__(self, opsdroid):
19         """Create web object."""
20         self.opsdroid = opsdroid
21         try:
22             self.config = self.opsdroid.config["web"]
23         except KeyError:
24             self.config = {}
25         self.base_url = self.config.get("base-url")
26         if not self.base_url:
27             self.base_url = "{proto}://{host}{port}".format(
28                 proto="http" if self.get_ssl_context is None else "https",
29                 host=self.get_host,
30                 port=":{}".format(self.get_port)
31                 if self.get_port not in (80, 443)
32                 else "",
33             )
34         self.web_app = web.Application()
35         self.runner = web.AppRunner(self.web_app)
36         self.site = None
37         self.web_app.router.add_get("/", self.web_index_handler)
38         self.web_app.router.add_get("", self.web_index_handler)
39         self.web_app.router.add_get("/stats", self.web_stats_handler)
40         self.web_app.router.add_get("/stats/", self.web_stats_handler)
41 
42     @property
43     def get_port(self):
44         """Return port from config or the default.
45 
46         Args:
47             self: instance method
48 
49         Returns:
50             int: returns value of port being used, config or default
51 
52         """
53         try:
54             port = self.config["port"]
55         except KeyError:
56             if self.get_ssl_context is not None:
57                 port = 8443
58             else:
59                 port = 8080
60         return port
61 
62     @property
63     def get_host(self):
64         """Return host from config or the default.
65 
66         Args:
67             self: instance method
68 
69         Returns:
70             string: returns address of host being used, config or default
71 
72         """
73         try:
74             host = self.config["host"]
75         except KeyError:
76             host = "0.0.0.0"
77         return host
78 
79     @property
80     def get_ssl_context(self):
81         """Return the ssl context or None.
82 
83         Args:
84             self: instance method
85 
86         Returns:
87             string (or NoneType): returns ssl context of None.
88 
89         """
90         try:
91             ssl_config = self.config["ssl"]
92             sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
93             sslcontext.load_cert_chain(ssl_config["cert"], ssl_config["key"])
94             return sslcontext
95         except FileNotFoundError:
96             _LOGGER.error(_("Cannot find ssl cert or key."))
97             return None
98         except KeyError:
99             return None
100 
101     async def start(self):
102         """Start web servers."""
103         _LOGGER.info(_(f"Started web server on {self.base_url}"))
104         await self.runner.setup()
105         self.site = web.TCPSite(
106             self.runner,
107             host=self.get_host,
108             port=self.get_port,
109             ssl_context=self.get_ssl_context,
110         )
111         await self.site.start()
112 
113     async def stop(self):
114         """Stop the web server."""
115         await self.runner.cleanup()
116 
117     @staticmethod
118     def build_response(status, result):
119         """Build a json response object to power the bot reponses.
120 
121         Args:
122             result: serialize obj as a JSON formated stream
123 
124         Returns:
125             json: returns json object with list of responses for the bot
126 
127         """
128         return web.Response(text=json.dumps(result), status=status)
129 
130     def register_skill(self, opsdroid, skill, webhook):
131         """Register a new skill in the web app router."""
132 
133         async def wrapper(req, opsdroid=opsdroid, config=skill.config):
134             """Wrap up the aiohttp handler."""
135             webhook_token = self.config.get("webhook-token", None)
136             authorization_header = []
137             if req is not None:
138                 authorization_header = req.headers.get("Authorization", "").split()
139 
140             if webhook_token is not None:
141                 if not (
142                     len(authorization_header) == 2
143                     and authorization_header[0] == "Bearer"
144                     and authorization_header[1] == webhook_token
145                 ):
146                     _LOGGER.error(
147                         _("Unauthorized to run skill %s via webhook"), webhook
148                     )
149                     return Web.build_response(403, {"called_skill": webhook})
150 
151             _LOGGER.info(_("Running skill %s via webhook."), webhook)
152             opsdroid.stats["webhooks_called"] = opsdroid.stats["webhooks_called"] + 1
153             resp = await opsdroid.run_skill(skill, config, req)
154             if isinstance(resp, web.Response):
155                 return resp
156             return Web.build_response(200, {"called_skill": webhook})
157 
158         self.web_app.router.add_post(
159             "/skill/{}/{}".format(skill.config["name"], webhook), wrapper
160         )
161         self.web_app.router.add_post(
162             "/skill/{}/{}/".format(skill.config["name"], webhook), wrapper
163         )
164 
165     def setup_webhooks(self, skills):
166         """Add the webhooks for the webhook skills to the router."""
167         for skill in skills:
168             for matcher in skill.matchers:
169                 if "webhook" in matcher:
170                     self.register_skill(self.opsdroid, skill, matcher["webhook"])
171 
172     async def web_index_handler(self, request):
173         """Handle root web request to opsdroid API.
174 
175         Args:
176             request: web request to the root (index)
177 
178         Returns:
179             dict: returns successful status code and greeting for the root page
180 
181         """
182         return self.build_response(200, {"message": "Welcome to the opsdroid API"})
183 
184     async def web_stats_handler(self, request):
185         """Handle stats request.
186 
187         Args:
188             request: web request to render opsdroid stats
189 
190         Returns:
191             dict: returns successful status code and dictionary with
192                   stats requested
193 
194         """
195         stats = self.opsdroid.stats
196         try:
197             stats["average_response_time"] = (
198                 stats["total_response_time"] / stats["total_responses"]
199             )
200         except ZeroDivisionError:
201             stats["average_response_time"] = 0
202 
203         return self.build_response(
204             200,
205             {
206                 "version": __version__,
207                 "messages": {
208                     "total_parsed": stats["messages_parsed"],
209                     "webhooks_called": stats["webhooks_called"],
210                     "total_response_time": stats["total_response_time"],
211                     "total_responses": stats["total_responses"],
212                     "average_response_time": stats["average_response_time"],
213                 },
214                 "modules": {
215                     "skills": len(self.opsdroid.skills),
216                     "connectors": len(self.opsdroid.connectors),
217                     "databases": len(self.opsdroid.memory.databases),
218                 },
219             },
220         )
221
```
--- 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/web.py b/opsdroid/web.py
--- a/opsdroid/web.py
+++ b/opsdroid/web.py
@@ -34,8 +34,10 @@
         self.web_app = web.Application()
         self.runner = web.AppRunner(self.web_app)
         self.site = None
-        self.web_app.router.add_get("/", self.web_index_handler)
-        self.web_app.router.add_get("", self.web_index_handler)
+        if not self.config.get("disable_web_index_handler_in_root", False):
+            self.web_app.router.add_get("/", self.web_index_handler)
+            self.web_app.router.add_get("", self.web_index_handler)
+
         self.web_app.router.add_get("/stats", self.web_stats_handler)
         self.web_app.router.add_get("/stats/", self.web_stats_handler)

{"golden_diff": "diff --git a/opsdroid/web.py b/opsdroid/web.py\n--- a/opsdroid/web.py\n+++ b/opsdroid/web.py\n@@ -34,8 +34,10 @@\n         self.web_app = web.Application()\n         self.runner = web.AppRunner(self.web_app)\n         self.site = None\n-        self.web_app.router.add_get(\"/\", self.web_index_handler)\n-        self.web_app.router.add_get(\"\", self.web_index_handler)\n+        if not self.config.get(\"disable_web_index_handler_in_root\", False):\n+            self.web_app.router.add_get(\"/\", self.web_index_handler)\n+            self.web_app.router.add_get(\"\", self.web_index_handler)\n+\n         self.web_app.router.add_get(\"/stats\", self.web_stats_handler)\n         self.web_app.router.add_get(\"/stats/\", self.web_stats_handler)\n", "issue": "Add a config option to disable registering a route for /\nIn the web server by default a route is registered for the base / path. This should be configurable in case a user wants to register their own.\n", "before_files": [{"content": "\"\"\"Submodule to handle web requests in opsdroid.\"\"\"\n\nimport json\nimport logging\nimport ssl\n\nfrom aiohttp import web\n\nfrom opsdroid import __version__\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass Web:\n    \"\"\"Create class for opsdroid Web server.\"\"\"\n\n    def __init__(self, opsdroid):\n        \"\"\"Create web object.\"\"\"\n        self.opsdroid = opsdroid\n        try:\n            self.config = self.opsdroid.config[\"web\"]\n        except KeyError:\n            self.config = {}\n        self.base_url = self.config.get(\"base-url\")\n        if not self.base_url:\n            self.base_url = \"{proto}://{host}{port}\".format(\n                proto=\"http\" if self.get_ssl_context is None else \"https\",\n                host=self.get_host,\n                port=\":{}\".format(self.get_port)\n                if self.get_port not in (80, 443)\n                else \"\",\n            )\n        self.web_app = web.Application()\n        self.runner = web.AppRunner(self.web_app)\n        self.site = None\n        self.web_app.router.add_get(\"/\", self.web_index_handler)\n        self.web_app.router.add_get(\"\", self.web_index_handler)\n        self.web_app.router.add_get(\"/stats\", self.web_stats_handler)\n        self.web_app.router.add_get(\"/stats/\", self.web_stats_handler)\n\n    @property\n    def get_port(self):\n        \"\"\"Return port from config or the default.\n\n        Args:\n            self: instance method\n\n        Returns:\n            int: returns value of port being used, config or default\n\n        \"\"\"\n        try:\n            port = self.config[\"port\"]\n        except KeyError:\n            if self.get_ssl_context is not None:\n                port = 8443\n            else:\n                port = 8080\n        return port\n\n    @property\n    def get_host(self):\n        \"\"\"Return host from config or the default.\n\n        Args:\n            self: instance method\n\n        Returns:\n            string: returns address of host being used, config or default\n\n        \"\"\"\n        try:\n            host = self.config[\"host\"]\n        except KeyError:\n            host = \"0.0.0.0\"\n        return host\n\n    @property\n    def get_ssl_context(self):\n        \"\"\"Return the ssl context or None.\n\n        Args:\n            self: instance method\n\n        Returns:\n            string (or NoneType): returns ssl context of None.\n\n        \"\"\"\n        try:\n            ssl_config = self.config[\"ssl\"]\n            sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)\n            sslcontext.load_cert_chain(ssl_config[\"cert\"], ssl_config[\"key\"])\n            return sslcontext\n        except FileNotFoundError:\n            _LOGGER.error(_(\"Cannot find ssl cert or key.\"))\n            return None\n        except KeyError:\n            return None\n\n    async def start(self):\n        \"\"\"Start web servers.\"\"\"\n        _LOGGER.info(_(f\"Started web server on {self.base_url}\"))\n        await self.runner.setup()\n        self.site = web.TCPSite(\n            self.runner,\n            host=self.get_host,\n            port=self.get_port,\n            ssl_context=self.get_ssl_context,\n        )\n        await self.site.start()\n\n    async def stop(self):\n        \"\"\"Stop the web server.\"\"\"\n        await self.runner.cleanup()\n\n    @staticmethod\n    def build_response(status, result):\n        \"\"\"Build a json response object to power the bot reponses.\n\n        Args:\n            result: serialize obj as a JSON formated stream\n\n        Returns:\n            json: returns json object with list of responses for the bot\n\n        \"\"\"\n        return web.Response(text=json.dumps(result), status=status)\n\n    def register_skill(self, opsdroid, skill, webhook):\n        \"\"\"Register a new skill in the web app router.\"\"\"\n\n        async def wrapper(req, opsdroid=opsdroid, config=skill.config):\n            \"\"\"Wrap up the aiohttp handler.\"\"\"\n            webhook_token = self.config.get(\"webhook-token\", None)\n            authorization_header = []\n            if req is not None:\n                authorization_header = req.headers.get(\"Authorization\", \"\").split()\n\n            if webhook_token is not None:\n                if not (\n                    len(authorization_header) == 2\n                    and authorization_header[0] == \"Bearer\"\n                    and authorization_header[1] == webhook_token\n                ):\n                    _LOGGER.error(\n                        _(\"Unauthorized to run skill %s via webhook\"), webhook\n                    )\n                    return Web.build_response(403, {\"called_skill\": webhook})\n\n            _LOGGER.info(_(\"Running skill %s via webhook.\"), webhook)\n            opsdroid.stats[\"webhooks_called\"] = opsdroid.stats[\"webhooks_called\"] + 1\n            resp = await opsdroid.run_skill(skill, config, req)\n            if isinstance(resp, web.Response):\n                return resp\n            return Web.build_response(200, {\"called_skill\": webhook})\n\n        self.web_app.router.add_post(\n            \"/skill/{}/{}\".format(skill.config[\"name\"], webhook), wrapper\n        )\n        self.web_app.router.add_post(\n            \"/skill/{}/{}/\".format(skill.config[\"name\"], webhook), wrapper\n        )\n\n    def setup_webhooks(self, skills):\n        \"\"\"Add the webhooks for the webhook skills to the router.\"\"\"\n        for skill in skills:\n            for matcher in skill.matchers:\n                if \"webhook\" in matcher:\n                    self.register_skill(self.opsdroid, skill, matcher[\"webhook\"])\n\n    async def web_index_handler(self, request):\n        \"\"\"Handle root web request to opsdroid API.\n\n        Args:\n            request: web request to the root (index)\n\n        Returns:\n            dict: returns successful status code and greeting for the root page\n\n        \"\"\"\n        return self.build_response(200, {\"message\": \"Welcome to the opsdroid API\"})\n\n    async def web_stats_handler(self, request):\n        \"\"\"Handle stats request.\n\n        Args:\n            request: web request to render opsdroid stats\n\n        Returns:\n            dict: returns successful status code and dictionary with\n                  stats requested\n\n        \"\"\"\n        stats = self.opsdroid.stats\n        try:\n            stats[\"average_response_time\"] = (\n                stats[\"total_response_time\"] / stats[\"total_responses\"]\n            )\n        except ZeroDivisionError:\n            stats[\"average_response_time\"] = 0\n\n        return self.build_response(\n            200,\n            {\n                \"version\": __version__,\n                \"messages\": {\n                    \"total_parsed\": stats[\"messages_parsed\"],\n                    \"webhooks_called\": stats[\"webhooks_called\"],\n                    \"total_response_time\": stats[\"total_response_time\"],\n                    \"total_responses\": stats[\"total_responses\"],\n                    \"average_response_time\": stats[\"average_response_time\"],\n                },\n                \"modules\": {\n                    \"skills\": len(self.opsdroid.skills),\n                    \"connectors\": len(self.opsdroid.connectors),\n                    \"databases\": len(self.opsdroid.memory.databases),\n                },\n            },\n        )\n", "path": "opsdroid/web.py"}], "after_files": [{"content": "\"\"\"Submodule to handle web requests in opsdroid.\"\"\"\n\nimport json\nimport logging\nimport ssl\n\nfrom aiohttp import web\n\nfrom opsdroid import __version__\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nclass Web:\n    \"\"\"Create class for opsdroid Web server.\"\"\"\n\n    def __init__(self, opsdroid):\n        \"\"\"Create web object.\"\"\"\n        self.opsdroid = opsdroid\n        try:\n            self.config = self.opsdroid.config[\"web\"]\n        except KeyError:\n            self.config = {}\n        self.base_url = self.config.get(\"base-url\")\n        if not self.base_url:\n            self.base_url = \"{proto}://{host}{port}\".format(\n                proto=\"http\" if self.get_ssl_context is None else \"https\",\n                host=self.get_host,\n                port=\":{}\".format(self.get_port)\n                if self.get_port not in (80, 443)\n                else \"\",\n            )\n        self.web_app = web.Application()\n        self.runner = web.AppRunner(self.web_app)\n        self.site = None\n        if not self.config.get(\"disable_web_index_handler_in_root\", False):\n            self.web_app.router.add_get(\"/\", self.web_index_handler)\n            self.web_app.router.add_get(\"\", self.web_index_handler)\n\n        self.web_app.router.add_get(\"/stats\", self.web_stats_handler)\n        self.web_app.router.add_get(\"/stats/\", self.web_stats_handler)\n\n    @property\n    def get_port(self):\n        \"\"\"Return port from config or the default.\n\n        Args:\n            self: instance method\n\n        Returns:\n            int: returns value of port being used, config or default\n\n        \"\"\"\n        try:\n            port = self.config[\"port\"]\n        except KeyError:\n            if self.get_ssl_context is not None:\n                port = 8443\n            else:\n                port = 8080\n        return port\n\n    @property\n    def get_host(self):\n        \"\"\"Return host from config or the default.\n\n        Args:\n            self: instance method\n\n        Returns:\n            string: returns address of host being used, config or default\n\n        \"\"\"\n        try:\n            host = self.config[\"host\"]\n        except KeyError:\n            host = \"0.0.0.0\"\n        return host\n\n    @property\n    def get_ssl_context(self):\n        \"\"\"Return the ssl context or None.\n\n        Args:\n            self: instance method\n\n        Returns:\n            string (or NoneType): returns ssl context of None.\n\n        \"\"\"\n        try:\n            ssl_config = self.config[\"ssl\"]\n            sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)\n            sslcontext.load_cert_chain(ssl_config[\"cert\"], ssl_config[\"key\"])\n            return sslcontext\n        except FileNotFoundError:\n            _LOGGER.error(_(\"Cannot find ssl cert or key.\"))\n            return None\n        except KeyError:\n            return None\n\n    async def start(self):\n        \"\"\"Start web servers.\"\"\"\n        _LOGGER.info(_(f\"Started web server on {self.base_url}\"))\n        await self.runner.setup()\n        self.site = web.TCPSite(\n            self.runner,\n            host=self.get_host,\n            port=self.get_port,\n            ssl_context=self.get_ssl_context,\n        )\n        await self.site.start()\n\n    async def stop(self):\n        \"\"\"Stop the web server.\"\"\"\n        await self.runner.cleanup()\n\n    @staticmethod\n    def build_response(status, result):\n        \"\"\"Build a json response object to power the bot reponses.\n\n        Args:\n            result: serialize obj as a JSON formated stream\n\n        Returns:\n            json: returns json object with list of responses for the bot\n\n        \"\"\"\n        return web.Response(text=json.dumps(result), status=status)\n\n    def register_skill(self, opsdroid, skill, webhook):\n        \"\"\"Register a new skill in the web app router.\"\"\"\n\n        async def wrapper(req, opsdroid=opsdroid, config=skill.config):\n            \"\"\"Wrap up the aiohttp handler.\"\"\"\n            webhook_token = self.config.get(\"webhook-token\", None)\n            authorization_header = []\n            if req is not None:\n                authorization_header = req.headers.get(\"Authorization\", \"\").split()\n\n            if webhook_token is not None:\n                if not (\n                    len(authorization_header) == 2\n                    and authorization_header[0] == \"Bearer\"\n                    and authorization_header[1] == webhook_token\n                ):\n                    _LOGGER.error(\n                        _(\"Unauthorized to run skill %s via webhook\"), webhook\n                    )\n                    return Web.build_response(403, {\"called_skill\": webhook})\n\n            _LOGGER.info(_(\"Running skill %s via webhook.\"), webhook)\n            opsdroid.stats[\"webhooks_called\"] = opsdroid.stats[\"webhooks_called\"] + 1\n            resp = await opsdroid.run_skill(skill, config, req)\n            if isinstance(resp, web.Response):\n                return resp\n            return Web.build_response(200, {\"called_skill\": webhook})\n\n        self.web_app.router.add_post(\n            \"/skill/{}/{}\".format(skill.config[\"name\"], webhook), wrapper\n        )\n        self.web_app.router.add_post(\n            \"/skill/{}/{}/\".format(skill.config[\"name\"], webhook), wrapper\n        )\n\n    def setup_webhooks(self, skills):\n        \"\"\"Add the webhooks for the webhook skills to the router.\"\"\"\n        for skill in skills:\n            for matcher in skill.matchers:\n                if \"webhook\" in matcher:\n                    self.register_skill(self.opsdroid, skill, matcher[\"webhook\"])\n\n    async def web_index_handler(self, request):\n        \"\"\"Handle root web request to opsdroid API.\n\n        Args:\n            request: web request to the root (index)\n\n        Returns:\n            dict: returns successful status code and greeting for the root page\n\n        \"\"\"\n        return self.build_response(200, {\"message\": \"Welcome to the opsdroid API\"})\n\n    async def web_stats_handler(self, request):\n        \"\"\"Handle stats request.\n\n        Args:\n            request: web request to render opsdroid stats\n\n        Returns:\n            dict: returns successful status code and dictionary with\n                  stats requested\n\n        \"\"\"\n        stats = self.opsdroid.stats\n        try:\n            stats[\"average_response_time\"] = (\n                stats[\"total_response_time\"] / stats[\"total_responses\"]\n            )\n        except ZeroDivisionError:\n            stats[\"average_response_time\"] = 0\n\n        return self.build_response(\n            200,\n            {\n                \"version\": __version__,\n                \"messages\": {\n                    \"total_parsed\": stats[\"messages_parsed\"],\n                    \"webhooks_called\": stats[\"webhooks_called\"],\n                    \"total_response_time\": stats[\"total_response_time\"],\n                    \"total_responses\": stats[\"total_responses\"],\n                    \"average_response_time\": stats[\"average_response_time\"],\n                },\n                \"modules\": {\n                    \"skills\": len(self.opsdroid.skills),\n                    \"connectors\": len(self.opsdroid.connectors),\n                    \"databases\": len(self.opsdroid.memory.databases),\n                },\n            },\n        )\n", "path": "opsdroid/web.py"}]}