prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  User configurable connection for mongo-based databases
  So the pymongo client has a multitude of ways for connecting to different mongo services
  
  So for MongoDB Atlas users the connection string is given as such 
  for python connections to the mongo db atlas 
  
  `mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/myFirstDatabase`
  
  In making the mongo connection to be user configurable we can specify different types of mongo services versus
  just asking for the basic connection arguments like port, user name,  pass, and also we can give users an easier way to connect versus making assumptions about the type of mongodb the kinds of credentials they might have. 
  
  As long as the pymongo client accepts the connection and connects the user to the database and the collection they want I think this would be great!
  
  Thanks again guys!
  
  
  --- 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/database/mongo/__init__.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 """A module for opsdroid to allow persist in mongo database."""
  3 import logging
  4 from contextlib import asynccontextmanager
  5 from motor.motor_asyncio import AsyncIOMotorClient
  6 from voluptuous import Any
  7 
  8 from opsdroid.database import Database
  9 
  10 _LOGGER = logging.getLogger(__name__)
  11 CONFIG_SCHEMA = {
  12     "host": str,
  13     "port": Any(int, str),
  14     "database": str,
  15     "user": str,
  16     "password": str,
  17     "collection": str,
  18 }
  19 
  20 
  21 class DatabaseMongo(Database):
  22     """A module for opsdroid to allow memory to persist in a mongo database."""
  23 
  24     def __init__(self, config, opsdroid=None):
  25         """Create the connection.
  26 
  27         Set some basic properties from the database config such as the name
  28         of this database.
  29 
  30         Args:
  31             config (dict): The config for this database specified in the
  32                            `configuration.yaml` file.
  33              opsdroid (OpsDroid): An instance of opsdroid.core.
  34 
  35         """
  36         super().__init__(config, opsdroid=opsdroid)
  37         _LOGGER.debug("Loaded mongo database connector.")
  38         self.name = "mongo"
  39         self.config = config
  40         self.client = None
  41         self.database = None
  42         self.collection = config.get("collection", "opsdroid")
  43 
  44     async def connect(self):
  45         """Connect to the database."""
  46         host = self.config.get("host", "localhost")
  47         port = self.config.get("port", "27017")
  48         database = self.config.get("database", "opsdroid")
  49         user = self.config.get("user")
  50         pwd = self.config.get("password")
  51         if user and pwd:
  52             path = "mongodb://{user}:{pwd}@{host}:{port}".format(
  53                 user=user, pwd=pwd, host=host, port=port
  54             )
  55         else:
  56             path = "mongodb://{host}:{port}".format(host=host, port=port)
  57         self.client = AsyncIOMotorClient(path)
  58         self.database = self.client[database]
  59         _LOGGER.info("Connected to MongoDB.")
  60 
  61     async def put(self, key, data):
  62         """Insert or replace an object into the database for a given key.
  63 
  64         Args:
  65             key (str): the key is the document lookup key.
  66             data (object): the data to be inserted or replaced
  67 
  68         """
  69         _LOGGER.debug("Putting %s into MongoDB collection %s", key, self.collection)
  70 
  71         if isinstance(data, str):
  72             data = {"value": data}
  73         if "key" not in data:
  74             data["key"] = key
  75 
  76         return await self.database[self.collection].update_one(
  77             {"key": data["key"]}, {"$set": data}, upsert=True
  78         )
  79 
  80     async def get(self, key):
  81         """Get a document from the database (key).
  82 
  83         Args:
  84             key (str): the key is the document lookup key.
  85 
  86         """
  87         _LOGGER.debug("Getting %s from MongoDB collection %s", key, self.collection)
  88 
  89         response = await self.database[self.collection].find_one(
  90             {"$query": {"key": key}, "$orderby": {"$natural": -1}}
  91         )
  92         if response.keys() == {"_id", "key", "value"}:
  93             response = response["value"]
  94         return response
  95 
  96     async def delete(self, key):
  97         """Delete a document from the database (key).
  98 
  99         Args:
  100             key (str): the key is the document lookup key.
  101 
  102         """
  103         _LOGGER.debug("Deleting %s from MongoDB collection %s.", key, self.collection)
  104 
  105         return await self.database[self.collection].delete_one({"key": key})
  106 
  107     @asynccontextmanager
  108     async def memory_in_collection(self, collection):
  109         """Use the specified collection rather than the default."""
  110         db_copy = DatabaseMongo(self.config, self.opsdroid)
  111         try:
  112             await db_copy.connect()
  113             db_copy.collection = collection
  114             yield db_copy
  115         finally:
  116             if db_copy.client:
  117                 db_copy.client.close()
  118
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/opsdroid/database/mongo/__init__.py b/opsdroid/database/mongo/__init__.py
  --- a/opsdroid/database/mongo/__init__.py
  +++ b/opsdroid/database/mongo/__init__.py
  @@ -44,17 +44,18 @@
       async def connect(self):
           """Connect to the database."""
           host = self.config.get("host", "localhost")
  +        protocol = self.config.get("protocol", "mongodb").replace("://", "")
           port = self.config.get("port", "27017")
  +        if port != "27017":
  +            host = f"{host}:{port}"
           database = self.config.get("database", "opsdroid")
           user = self.config.get("user")
           pwd = self.config.get("password")
           if user and pwd:
  -            path = "mongodb://{user}:{pwd}@{host}:{port}".format(
  -                user=user, pwd=pwd, host=host, port=port
  -            )
  +            self.db_url = f"{protocol}://{user}:{pwd}@{host}"
           else:
  -            path = "mongodb://{host}:{port}".format(host=host, port=port)
  -        self.client = AsyncIOMotorClient(path)
  +            self.db_url = f"{protocol}://{host}"
  +        self.client = AsyncIOMotorClient(self.db_url)
           self.database = self.client[database]
           _LOGGER.info("Connected to MongoDB.")
  
verification_info: {"golden_diff": "diff --git a/opsdroid/database/mongo/__init__.py b/opsdroid/database/mongo/__init__.py\n--- a/opsdroid/database/mongo/__init__.py\n+++ b/opsdroid/database/mongo/__init__.py\n@@ -44,17 +44,18 @@\n     async def connect(self):\n         \"\"\"Connect to the database.\"\"\"\n         host = self.config.get(\"host\", \"localhost\")\n+        protocol = self.config.get(\"protocol\", \"mongodb\").replace(\"://\", \"\")\n         port = self.config.get(\"port\", \"27017\")\n+        if port != \"27017\":\n+            host = f\"{host}:{port}\"\n         database = self.config.get(\"database\", \"opsdroid\")\n         user = self.config.get(\"user\")\n         pwd = self.config.get(\"password\")\n         if user and pwd:\n-            path = \"mongodb://{user}:{pwd}@{host}:{port}\".format(\n-                user=user, pwd=pwd, host=host, port=port\n-            )\n+            self.db_url = f\"{protocol}://{user}:{pwd}@{host}\"\n         else:\n-            path = \"mongodb://{host}:{port}\".format(host=host, port=port)\n-        self.client = AsyncIOMotorClient(path)\n+            self.db_url = f\"{protocol}://{host}\"\n+        self.client = AsyncIOMotorClient(self.db_url)\n         self.database = self.client[database]\n         _LOGGER.info(\"Connected to MongoDB.\")\n", "issue": "User configurable connection for mongo-based databases\nSo the pymongo client has a multitude of ways for connecting to different mongo services\r\n\r\nSo for MongoDB Atlas users the connection string is given as such \r\nfor python connections to the mongo db atlas \r\n\r\n`mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/myFirstDatabase`\r\n\r\nIn making the mongo connection to be user configurable we can specify different types of mongo services versus\r\njust asking for the basic connection arguments like port, user name,  pass, and also we can give users an easier way to connect versus making assumptions about the type of mongodb the kinds of credentials they might have. \r\n\r\nAs long as the pymongo client accepts the connection and connects the user to the database and the collection they want I think this would be great!\r\n\r\nThanks again guys!\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"A module for opsdroid to allow persist in mongo database.\"\"\"\nimport logging\nfrom contextlib import asynccontextmanager\nfrom motor.motor_asyncio import AsyncIOMotorClient\nfrom voluptuous import Any\n\nfrom opsdroid.database import Database\n\n_LOGGER = logging.getLogger(__name__)\nCONFIG_SCHEMA = {\n    \"host\": str,\n    \"port\": Any(int, str),\n    \"database\": str,\n    \"user\": str,\n    \"password\": str,\n    \"collection\": str,\n}\n\n\nclass DatabaseMongo(Database):\n    \"\"\"A module for opsdroid to allow memory to persist in a mongo database.\"\"\"\n\n    def __init__(self, config, opsdroid=None):\n        \"\"\"Create the connection.\n\n        Set some basic properties from the database config such as the name\n        of this database.\n\n        Args:\n            config (dict): The config for this database specified in the\n                           `configuration.yaml` file.\n             opsdroid (OpsDroid): An instance of opsdroid.core.\n\n        \"\"\"\n        super().__init__(config, opsdroid=opsdroid)\n        _LOGGER.debug(\"Loaded mongo database connector.\")\n        self.name = \"mongo\"\n        self.config = config\n        self.client = None\n        self.database = None\n        self.collection = config.get(\"collection\", \"opsdroid\")\n\n    async def connect(self):\n        \"\"\"Connect to the database.\"\"\"\n        host = self.config.get(\"host\", \"localhost\")\n        port = self.config.get(\"port\", \"27017\")\n        database = self.config.get(\"database\", \"opsdroid\")\n        user = self.config.get(\"user\")\n        pwd = self.config.get(\"password\")\n        if user and pwd:\n            path = \"mongodb://{user}:{pwd}@{host}:{port}\".format(\n                user=user, pwd=pwd, host=host, port=port\n            )\n        else:\n            path = \"mongodb://{host}:{port}\".format(host=host, port=port)\n        self.client = AsyncIOMotorClient(path)\n        self.database = self.client[database]\n        _LOGGER.info(\"Connected to MongoDB.\")\n\n    async def put(self, key, data):\n        \"\"\"Insert or replace an object into the database for a given key.\n\n        Args:\n            key (str): the key is the document lookup key.\n            data (object): the data to be inserted or replaced\n\n        \"\"\"\n        _LOGGER.debug(\"Putting %s into MongoDB collection %s\", key, self.collection)\n\n        if isinstance(data, str):\n            data = {\"value\": data}\n        if \"key\" not in data:\n            data[\"key\"] = key\n\n        return await self.database[self.collection].update_one(\n            {\"key\": data[\"key\"]}, {\"$set\": data}, upsert=True\n        )\n\n    async def get(self, key):\n        \"\"\"Get a document from the database (key).\n\n        Args:\n            key (str): the key is the document lookup key.\n\n        \"\"\"\n        _LOGGER.debug(\"Getting %s from MongoDB collection %s\", key, self.collection)\n\n        response = await self.database[self.collection].find_one(\n            {\"$query\": {\"key\": key}, \"$orderby\": {\"$natural\": -1}}\n        )\n        if response.keys() == {\"_id\", \"key\", \"value\"}:\n            response = response[\"value\"]\n        return response\n\n    async def delete(self, key):\n        \"\"\"Delete a document from the database (key).\n\n        Args:\n            key (str): the key is the document lookup key.\n\n        \"\"\"\n        _LOGGER.debug(\"Deleting %s from MongoDB collection %s.\", key, self.collection)\n\n        return await self.database[self.collection].delete_one({\"key\": key})\n\n    @asynccontextmanager\n    async def memory_in_collection(self, collection):\n        \"\"\"Use the specified collection rather than the default.\"\"\"\n        db_copy = DatabaseMongo(self.config, self.opsdroid)\n        try:\n            await db_copy.connect()\n            db_copy.collection = collection\n            yield db_copy\n        finally:\n            if db_copy.client:\n                db_copy.client.close()\n", "path": "opsdroid/database/mongo/__init__.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"A module for opsdroid to allow persist in mongo database.\"\"\"\nimport logging\nfrom contextlib import asynccontextmanager\nfrom motor.motor_asyncio import AsyncIOMotorClient\nfrom voluptuous import Any\n\nfrom opsdroid.database import Database\n\n_LOGGER = logging.getLogger(__name__)\nCONFIG_SCHEMA = {\n    \"host\": str,\n    \"port\": Any(int, str),\n    \"database\": str,\n    \"user\": str,\n    \"password\": str,\n    \"collection\": str,\n}\n\n\nclass DatabaseMongo(Database):\n    \"\"\"A module for opsdroid to allow memory to persist in a mongo database.\"\"\"\n\n    def __init__(self, config, opsdroid=None):\n        \"\"\"Create the connection.\n\n        Set some basic properties from the database config such as the name\n        of this database.\n\n        Args:\n            config (dict): The config for this database specified in the\n                           `configuration.yaml` file.\n             opsdroid (OpsDroid): An instance of opsdroid.core.\n\n        \"\"\"\n        super().__init__(config, opsdroid=opsdroid)\n        _LOGGER.debug(\"Loaded mongo database connector.\")\n        self.name = \"mongo\"\n        self.config = config\n        self.client = None\n        self.database = None\n        self.collection = config.get(\"collection\", \"opsdroid\")\n\n    async def connect(self):\n        \"\"\"Connect to the database.\"\"\"\n        host = self.config.get(\"host\", \"localhost\")\n        protocol = self.config.get(\"protocol\", \"mongodb\").replace(\"://\", \"\")\n        port = self.config.get(\"port\", \"27017\")\n        if port != \"27017\":\n            host = f\"{host}:{port}\"\n        database = self.config.get(\"database\", \"opsdroid\")\n        user = self.config.get(\"user\")\n        pwd = self.config.get(\"password\")\n        if user and pwd:\n            self.db_url = f\"{protocol}://{user}:{pwd}@{host}\"\n        else:\n            self.db_url = f\"{protocol}://{host}\"\n        self.client = AsyncIOMotorClient(self.db_url)\n        self.database = self.client[database]\n        _LOGGER.info(\"Connected to MongoDB.\")\n\n    async def put(self, key, data):\n        \"\"\"Insert or replace an object into the database for a given key.\n\n        Args:\n            key (str): the key is the document lookup key.\n            data (object): the data to be inserted or replaced\n\n        \"\"\"\n        _LOGGER.debug(\"Putting %s into MongoDB collection %s\", key, self.collection)\n\n        if isinstance(data, str):\n            data = {\"value\": data}\n        if \"key\" not in data:\n            data[\"key\"] = key\n\n        return await self.database[self.collection].update_one(\n            {\"key\": data[\"key\"]}, {\"$set\": data}, upsert=True\n        )\n\n    async def get(self, key):\n        \"\"\"Get a document from the database (key).\n\n        Args:\n            key (str): the key is the document lookup key.\n\n        \"\"\"\n        _LOGGER.debug(\"Getting %s from MongoDB collection %s\", key, self.collection)\n\n        response = await self.database[self.collection].find_one(\n            {\"$query\": {\"key\": key}, \"$orderby\": {\"$natural\": -1}}\n        )\n        if response.keys() == {\"_id\", \"key\", \"value\"}:\n            response = response[\"value\"]\n        return response\n\n    async def delete(self, key):\n        \"\"\"Delete a document from the database (key).\n\n        Args:\n            key (str): the key is the document lookup key.\n\n        \"\"\"\n        _LOGGER.debug(\"Deleting %s from MongoDB collection %s.\", key, self.collection)\n\n        return await self.database[self.collection].delete_one({\"key\": key})\n\n    @asynccontextmanager\n    async def memory_in_collection(self, collection):\n        \"\"\"Use the specified collection rather than the default.\"\"\"\n        db_copy = DatabaseMongo(self.config, self.opsdroid)\n        try:\n            await db_copy.connect()\n            db_copy.collection = collection\n            yield db_copy\n        finally:\n            if db_copy.client:\n                db_copy.client.close()\n", "path": "opsdroid/database/mongo/__init__.py"}]}