We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Unhandled exception in wit.ai matcher (wrong wit.ai response parsing)
# Description
Opsdroid doesn't understand wit.ai time range response


## Steps to Reproduce
Please also include relevant information and steps to reproduce the bug/issue.
1. Connect with wit.ai app
2. Create new indent
3. Train indent to understand `wit/datetime`
4. Send message like `aws cost since december`
5. Opsdroid will fail to understand wit.ai api response

```
INFO opsdroid.parsers.witai.call_witai(): wit.ai response - {"_text": "aws cost since december", "entities": {"intent": [{"confidence": 0.99986626506986, "value": "aws_cost"}], "datetime": [{"confidence": 0.9995, "type": "interval", "from": {"grain": "month", "value": "2022-12-01T00:00:00.000-08:00"}, "values": [{"type": "interval", "from": {"grain": "month", "value": "2022-12-01T00:00:00.000-08:00"}}, {"type": "interval", "from": {"grain": "month", "value": "2023-12-01T00:00:00.000-08:00"}}, {"type": "interval", "from": {"grain": "month", "value": "2024-12-01T00:00:00.000-08:00"}}]}]}, "WARNING": "DEPRECATED", "msg_id": "0eanvH01TmiwU0Era"}.
ERROR aiohttp.server.log_exception(): Error handling request
Traceback (most recent call last):
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\aiohttp\web_protocol.py", line 435, in _handle_request
    resp = await request_handler(request)
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\aiohttp\web_app.py", line 504, in _handle
    resp = await handler(request)
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\opsdroid\connector\websocket\__init__.py", line 99, in websocket_handler
    await self.opsdroid.parse(message)
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\opsdroid\core.py", line 627, in parse
    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\opsdroid\core.py", line 516, in get_ranked_skills
    ranked_skills += await parse_witai(self, skills, message, witai)
  File "C:\Users\jakub\Documents\venvs\chatops\lib\site-packages\opsdroid\parsers\witai.py", line 72, in parse_witai
    key, entity[0]["value"], entity[0]["confidence"]
KeyError: 'value'
```

## Expected Functionality
There should be no exception


## Experienced Functionality
Thrown exception

## Versions
- **Opsdroid version:** 0.25.0
- **Python version:** 3.9.5 
- **OS/Docker version:** Windows 10

## Configuration File
Please include your version of the configuration file below.

```yaml
# Your code goes here.
parsers:
  - name: witai
    access-token: [REDACTED]
    min-score: 0.6
```

## Additional Details

Raw wit.ai response for `aws cost since december`

```json
{
  "_text": "aws cost since december",
  "entities": {
    "intent": [
      {
        "confidence": 0.99965322126667,
        "value": "aws_cost"
      }
    ],
    "datetime": [
      {
        "confidence": 0.9995,
        "type": "interval",
        "from": {
          "grain": "month",
          "value": "2022-12-01T00:00:00.000-08:00"
        },
        "values": [
          {
            "type": "interval",
            "from": {
              "grain": "month",
              "value": "2022-12-01T00:00:00.000-08:00"
            }
          },
          {
            "type": "interval",
            "from": {
              "grain": "month",
              "value": "2023-12-01T00:00:00.000-08:00"
            }
          },
          {
            "type": "interval",
            "from": {
              "grain": "month",
              "value": "2024-12-01T00:00:00.000-08:00"
            }
          }
        ]
      }
    ]
  },
  "WARNING": "DEPRECATED",
  "msg_id": "051qg0BBGn4O7xZDj"
}

```

Here we can see that wit.ai sends `values` in `datetime[0]` dict. 

Opsdroid expects it to be `value` (without **s**):

https://github.com/opsdroid/opsdroid/blob/c5dad210fe3d9068c75cd4fac9762fcc353335d3/opsdroid/parsers/witai.py#L69-L73

Which is fine for simple response without any matched intents (excluded by `if` in L70):

```json
{
  "_text": "aws",
  "entities": {
    "intent": [
      {
        "confidence": 0.99692494474705,
        "value": "aws_cost"
      }
    ]
  },
  "WARNING": "DEPRECATED",
  "msg_id": "0lbTZJcwDL5RoT2Wi"
}
```

Simple query `aws cost today`. If there is only one field in `values` wit.ai will rewrite it to short version to `values`

```json
{
  "_text": "aws cost today",
  "entities": {
    "intent": [
      {
        "confidence": 0.99965536553564,
        "value": "aws_cost"
      }
    ],
    "datetime": [
      {
        "confidence": 0.9995,
        "type": "value",
        "grain": "day",
        "value": "2022-04-16T00:00:00.000-07:00",
        "values": [
          {
            "type": "value",
            "grain": "day",
            "value": "2022-04-16T00:00:00.000-07:00"
          }
        ]
      }
    ]
  },
  "WARNING": "DEPRECATED",
  "msg_id": "05vACT9WHhDUmAy9u"
}
```



--- 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/parsers/witai.py`
Content:
```
1 """A helper function for parsing and executing wit.ai skills."""
2 
3 import logging
4 import json
5 
6 import aiohttp
7 
8 from voluptuous import Required
9 
10 from opsdroid.const import WITAI_DEFAULT_VERSION
11 from opsdroid.const import WITAI_API_ENDPOINT
12 
13 _LOGGER = logging.getLogger(__name__)
14 CONFIG_SCHEMA = {Required("token"): str, "min-score": float}
15 
16 
17 async def call_witai(message, config):
18     """Call the wit.ai api and return the response."""
19     async with aiohttp.ClientSession(trust_env=True) as session:
20         headers = {"Authorization": "Bearer " + config["token"]}
21         payload = {"v": WITAI_DEFAULT_VERSION, "q": message.text}
22         resp = await session.get(
23             WITAI_API_ENDPOINT + "v={}&q={}".format(payload["v"], payload["q"]),
24             headers=headers,
25         )
26         result = await resp.json()
27         _LOGGER.info(_("wit.ai response - %s."), json.dumps(result))
28         return result
29 
30 
31 async def parse_witai(opsdroid, skills, message, config):
32     """Parse a message against all witai skills."""
33     matched_skills = []
34     if "token" in config:
35         try:
36             result = await call_witai(message, config)
37         except aiohttp.ClientOSError:
38             _LOGGER.error(_("No response from wit.ai, check your network."))
39             return matched_skills
40 
41         if "code" in result:
42             _LOGGER.error(
43                 _("wit.ai error - %s %s"), str(result["code"]), str(result["error"])
44             )
45             return matched_skills
46 
47         if result["entities"] == {}:
48             _LOGGER.error(
49                 _("wit.ai error - No intent found. Did you forget to create one?")
50             )
51             return matched_skills
52 
53         try:
54             confidence = result["entities"]["intent"][0]["confidence"]
55         except KeyError:
56             confidence = 0.0
57         if "min-score" in config and confidence < config["min-score"]:
58             _LOGGER.info(_("wit.ai score lower than min-score."))
59             return matched_skills
60 
61         if result:
62             for skill in skills:
63                 for matcher in skill.matchers:
64                     if "witai_intent" in matcher:
65                         if matcher["witai_intent"] in [
66                             i["value"] for i in result["entities"]["intent"]
67                         ]:
68                             message.witai = result
69                             for key, entity in result["entities"].items():
70                                 if key != "intent":
71                                     message.update_entity(
72                                         key, entity[0]["value"], entity[0]["confidence"]
73                                     )
74                             matched_skills.append(
75                                 {
76                                     "score": confidence,
77                                     "skill": skill,
78                                     "config": skill.config,
79                                     "message": message,
80                                 }
81                             )
82     return matched_skills
83
```
--- 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/parsers/witai.py b/opsdroid/parsers/witai.py
--- a/opsdroid/parsers/witai.py
+++ b/opsdroid/parsers/witai.py
@@ -68,8 +68,16 @@
                             message.witai = result
                             for key, entity in result["entities"].items():
                                 if key != "intent":
+                                    witai_entity_value = ""
+                                    if "value" in entity[0]:
+                                        witai_entity_value = entity[0]["value"]
+                                    elif "values" in entity[0]:
+                                        # we never know which data are important for user,
+                                        # so we return list with all values
+                                        witai_entity_value = entity[0]["values"]
+
                                     message.update_entity(
-                                        key, entity[0]["value"], entity[0]["confidence"]
+                                        key, witai_entity_value, entity[0]["confidence"]
                                     )
                             matched_skills.append(
                                 {

{"golden_diff": "diff --git a/opsdroid/parsers/witai.py b/opsdroid/parsers/witai.py\n--- a/opsdroid/parsers/witai.py\n+++ b/opsdroid/parsers/witai.py\n@@ -68,8 +68,16 @@\n                             message.witai = result\n                             for key, entity in result[\"entities\"].items():\n                                 if key != \"intent\":\n+                                    witai_entity_value = \"\"\n+                                    if \"value\" in entity[0]:\n+                                        witai_entity_value = entity[0][\"value\"]\n+                                    elif \"values\" in entity[0]:\n+                                        # we never know which data are important for user,\n+                                        # so we return list with all values\n+                                        witai_entity_value = entity[0][\"values\"]\n+\n                                     message.update_entity(\n-                                        key, entity[0][\"value\"], entity[0][\"confidence\"]\n+                                        key, witai_entity_value, entity[0][\"confidence\"]\n                                     )\n                             matched_skills.append(\n                                 {\n", "issue": "Unhandled exception in wit.ai matcher (wrong wit.ai response parsing)\n# Description\r\nOpsdroid doesn't understand wit.ai time range response\r\n\r\n\r\n## Steps to Reproduce\r\nPlease also include relevant information and steps to reproduce the bug/issue.\r\n1. Connect with wit.ai app\r\n2. Create new indent\r\n3. Train indent to understand `wit/datetime`\r\n4. Send message like `aws cost since december`\r\n5. Opsdroid will fail to understand wit.ai api response\r\n\r\n```\r\nINFO opsdroid.parsers.witai.call_witai(): wit.ai response - {\"_text\": \"aws cost since december\", \"entities\": {\"intent\": [{\"confidence\": 0.99986626506986, \"value\": \"aws_cost\"}], \"datetime\": [{\"confidence\": 0.9995, \"type\": \"interval\", \"from\": {\"grain\": \"month\", \"value\": \"2022-12-01T00:00:00.000-08:00\"}, \"values\": [{\"type\": \"interval\", \"from\": {\"grain\": \"month\", \"value\": \"2022-12-01T00:00:00.000-08:00\"}}, {\"type\": \"interval\", \"from\": {\"grain\": \"month\", \"value\": \"2023-12-01T00:00:00.000-08:00\"}}, {\"type\": \"interval\", \"from\": {\"grain\": \"month\", \"value\": \"2024-12-01T00:00:00.000-08:00\"}}]}]}, \"WARNING\": \"DEPRECATED\", \"msg_id\": \"0eanvH01TmiwU0Era\"}.\r\nERROR aiohttp.server.log_exception(): Error handling request\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\aiohttp\\web_protocol.py\", line 435, in _handle_request\r\n    resp = await request_handler(request)\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\aiohttp\\web_app.py\", line 504, in _handle\r\n    resp = await handler(request)\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\opsdroid\\connector\\websocket\\__init__.py\", line 99, in websocket_handler\r\n    await self.opsdroid.parse(message)\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\opsdroid\\core.py\", line 627, in parse\r\n    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\opsdroid\\core.py\", line 516, in get_ranked_skills\r\n    ranked_skills += await parse_witai(self, skills, message, witai)\r\n  File \"C:\\Users\\jakub\\Documents\\venvs\\chatops\\lib\\site-packages\\opsdroid\\parsers\\witai.py\", line 72, in parse_witai\r\n    key, entity[0][\"value\"], entity[0][\"confidence\"]\r\nKeyError: 'value'\r\n```\r\n\r\n## Expected Functionality\r\nThere should be no exception\r\n\r\n\r\n## Experienced Functionality\r\nThrown exception\r\n\r\n## Versions\r\n- **Opsdroid version:** 0.25.0\r\n- **Python version:** 3.9.5 \r\n- **OS/Docker version:** Windows 10\r\n\r\n## Configuration File\r\nPlease include your version of the configuration file below.\r\n\r\n```yaml\r\n# Your code goes here.\r\nparsers:\r\n  - name: witai\r\n    access-token: [REDACTED]\r\n    min-score: 0.6\r\n```\r\n\r\n## Additional Details\r\n\r\nRaw wit.ai response for `aws cost since december`\r\n\r\n```json\r\n{\r\n  \"_text\": \"aws cost since december\",\r\n  \"entities\": {\r\n    \"intent\": [\r\n      {\r\n        \"confidence\": 0.99965322126667,\r\n        \"value\": \"aws_cost\"\r\n      }\r\n    ],\r\n    \"datetime\": [\r\n      {\r\n        \"confidence\": 0.9995,\r\n        \"type\": \"interval\",\r\n        \"from\": {\r\n          \"grain\": \"month\",\r\n          \"value\": \"2022-12-01T00:00:00.000-08:00\"\r\n        },\r\n        \"values\": [\r\n          {\r\n            \"type\": \"interval\",\r\n            \"from\": {\r\n              \"grain\": \"month\",\r\n              \"value\": \"2022-12-01T00:00:00.000-08:00\"\r\n            }\r\n          },\r\n          {\r\n            \"type\": \"interval\",\r\n            \"from\": {\r\n              \"grain\": \"month\",\r\n              \"value\": \"2023-12-01T00:00:00.000-08:00\"\r\n            }\r\n          },\r\n          {\r\n            \"type\": \"interval\",\r\n            \"from\": {\r\n              \"grain\": \"month\",\r\n              \"value\": \"2024-12-01T00:00:00.000-08:00\"\r\n            }\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  },\r\n  \"WARNING\": \"DEPRECATED\",\r\n  \"msg_id\": \"051qg0BBGn4O7xZDj\"\r\n}\r\n\r\n```\r\n\r\nHere we can see that wit.ai sends `values` in `datetime[0]` dict. \r\n\r\nOpsdroid expects it to be `value` (without **s**):\r\n\r\nhttps://github.com/opsdroid/opsdroid/blob/c5dad210fe3d9068c75cd4fac9762fcc353335d3/opsdroid/parsers/witai.py#L69-L73\r\n\r\nWhich is fine for simple response without any matched intents (excluded by `if` in L70):\r\n\r\n```json\r\n{\r\n  \"_text\": \"aws\",\r\n  \"entities\": {\r\n    \"intent\": [\r\n      {\r\n        \"confidence\": 0.99692494474705,\r\n        \"value\": \"aws_cost\"\r\n      }\r\n    ]\r\n  },\r\n  \"WARNING\": \"DEPRECATED\",\r\n  \"msg_id\": \"0lbTZJcwDL5RoT2Wi\"\r\n}\r\n```\r\n\r\nSimple query `aws cost today`. If there is only one field in `values` wit.ai will rewrite it to short version to `values`\r\n\r\n```json\r\n{\r\n  \"_text\": \"aws cost today\",\r\n  \"entities\": {\r\n    \"intent\": [\r\n      {\r\n        \"confidence\": 0.99965536553564,\r\n        \"value\": \"aws_cost\"\r\n      }\r\n    ],\r\n    \"datetime\": [\r\n      {\r\n        \"confidence\": 0.9995,\r\n        \"type\": \"value\",\r\n        \"grain\": \"day\",\r\n        \"value\": \"2022-04-16T00:00:00.000-07:00\",\r\n        \"values\": [\r\n          {\r\n            \"type\": \"value\",\r\n            \"grain\": \"day\",\r\n            \"value\": \"2022-04-16T00:00:00.000-07:00\"\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  },\r\n  \"WARNING\": \"DEPRECATED\",\r\n  \"msg_id\": \"05vACT9WHhDUmAy9u\"\r\n}\r\n```\r\n\r\n\n", "before_files": [{"content": "\"\"\"A helper function for parsing and executing wit.ai skills.\"\"\"\n\nimport logging\nimport json\n\nimport aiohttp\n\nfrom voluptuous import Required\n\nfrom opsdroid.const import WITAI_DEFAULT_VERSION\nfrom opsdroid.const import WITAI_API_ENDPOINT\n\n_LOGGER = logging.getLogger(__name__)\nCONFIG_SCHEMA = {Required(\"token\"): str, \"min-score\": float}\n\n\nasync def call_witai(message, config):\n    \"\"\"Call the wit.ai api and return the response.\"\"\"\n    async with aiohttp.ClientSession(trust_env=True) as session:\n        headers = {\"Authorization\": \"Bearer \" + config[\"token\"]}\n        payload = {\"v\": WITAI_DEFAULT_VERSION, \"q\": message.text}\n        resp = await session.get(\n            WITAI_API_ENDPOINT + \"v={}&q={}\".format(payload[\"v\"], payload[\"q\"]),\n            headers=headers,\n        )\n        result = await resp.json()\n        _LOGGER.info(_(\"wit.ai response - %s.\"), json.dumps(result))\n        return result\n\n\nasync def parse_witai(opsdroid, skills, message, config):\n    \"\"\"Parse a message against all witai skills.\"\"\"\n    matched_skills = []\n    if \"token\" in config:\n        try:\n            result = await call_witai(message, config)\n        except aiohttp.ClientOSError:\n            _LOGGER.error(_(\"No response from wit.ai, check your network.\"))\n            return matched_skills\n\n        if \"code\" in result:\n            _LOGGER.error(\n                _(\"wit.ai error - %s %s\"), str(result[\"code\"]), str(result[\"error\"])\n            )\n            return matched_skills\n\n        if result[\"entities\"] == {}:\n            _LOGGER.error(\n                _(\"wit.ai error - No intent found. Did you forget to create one?\")\n            )\n            return matched_skills\n\n        try:\n            confidence = result[\"entities\"][\"intent\"][0][\"confidence\"]\n        except KeyError:\n            confidence = 0.0\n        if \"min-score\" in config and confidence < config[\"min-score\"]:\n            _LOGGER.info(_(\"wit.ai score lower than min-score.\"))\n            return matched_skills\n\n        if result:\n            for skill in skills:\n                for matcher in skill.matchers:\n                    if \"witai_intent\" in matcher:\n                        if matcher[\"witai_intent\"] in [\n                            i[\"value\"] for i in result[\"entities\"][\"intent\"]\n                        ]:\n                            message.witai = result\n                            for key, entity in result[\"entities\"].items():\n                                if key != \"intent\":\n                                    message.update_entity(\n                                        key, entity[0][\"value\"], entity[0][\"confidence\"]\n                                    )\n                            matched_skills.append(\n                                {\n                                    \"score\": confidence,\n                                    \"skill\": skill,\n                                    \"config\": skill.config,\n                                    \"message\": message,\n                                }\n                            )\n    return matched_skills\n", "path": "opsdroid/parsers/witai.py"}], "after_files": [{"content": "\"\"\"A helper function for parsing and executing wit.ai skills.\"\"\"\n\nimport logging\nimport json\n\nimport aiohttp\n\nfrom voluptuous import Required\n\nfrom opsdroid.const import WITAI_DEFAULT_VERSION\nfrom opsdroid.const import WITAI_API_ENDPOINT\n\n_LOGGER = logging.getLogger(__name__)\nCONFIG_SCHEMA = {Required(\"token\"): str, \"min-score\": float}\n\n\nasync def call_witai(message, config):\n    \"\"\"Call the wit.ai api and return the response.\"\"\"\n    async with aiohttp.ClientSession(trust_env=True) as session:\n        headers = {\"Authorization\": \"Bearer \" + config[\"token\"]}\n        payload = {\"v\": WITAI_DEFAULT_VERSION, \"q\": message.text}\n        resp = await session.get(\n            WITAI_API_ENDPOINT + \"v={}&q={}\".format(payload[\"v\"], payload[\"q\"]),\n            headers=headers,\n        )\n        result = await resp.json()\n        _LOGGER.info(_(\"wit.ai response - %s.\"), json.dumps(result))\n        return result\n\n\nasync def parse_witai(opsdroid, skills, message, config):\n    \"\"\"Parse a message against all witai skills.\"\"\"\n    matched_skills = []\n    if \"token\" in config:\n        try:\n            result = await call_witai(message, config)\n        except aiohttp.ClientOSError:\n            _LOGGER.error(_(\"No response from wit.ai, check your network.\"))\n            return matched_skills\n\n        if \"code\" in result:\n            _LOGGER.error(\n                _(\"wit.ai error - %s %s\"), str(result[\"code\"]), str(result[\"error\"])\n            )\n            return matched_skills\n\n        if result[\"entities\"] == {}:\n            _LOGGER.error(\n                _(\"wit.ai error - No intent found. Did you forget to create one?\")\n            )\n            return matched_skills\n\n        try:\n            confidence = result[\"entities\"][\"intent\"][0][\"confidence\"]\n        except KeyError:\n            confidence = 0.0\n        if \"min-score\" in config and confidence < config[\"min-score\"]:\n            _LOGGER.info(_(\"wit.ai score lower than min-score.\"))\n            return matched_skills\n\n        if result:\n            for skill in skills:\n                for matcher in skill.matchers:\n                    if \"witai_intent\" in matcher:\n                        if matcher[\"witai_intent\"] in [\n                            i[\"value\"] for i in result[\"entities\"][\"intent\"]\n                        ]:\n                            message.witai = result\n                            for key, entity in result[\"entities\"].items():\n                                if key != \"intent\":\n                                    witai_entity_value = \"\"\n                                    if \"value\" in entity[0]:\n                                        witai_entity_value = entity[0][\"value\"]\n                                    elif \"values\" in entity[0]:\n                                        # we never know which data are important for user,\n                                        # so we return list with all values\n                                        witai_entity_value = entity[0][\"values\"]\n\n                                    message.update_entity(\n                                        key, witai_entity_value, entity[0][\"confidence\"]\n                                    )\n                            matched_skills.append(\n                                {\n                                    \"score\": confidence,\n                                    \"skill\": skill,\n                                    \"config\": skill.config,\n                                    \"message\": message,\n                                }\n                            )\n    return matched_skills\n", "path": "opsdroid/parsers/witai.py"}]}