prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  API.AI Error when network is disconnected
  The following error should be caught and warned about cleanly.
  
  ```
  ERROR asyncio: Task exception was never retrieved
  future: <Task finished coro=<parse_apiai() done, defined at /Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py:34> exception=ClientConnectorError(8, 'Cannot connect to host api.api.ai:443 ssl:True [nodename nor servname provided, or not known]')>
  Traceback (most recent call last):
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py", line 375, in connect
      proto = yield from self._create_connection(req)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py", line 632, in _create_connection
      _, proto = yield from self._create_direct_connection(req)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py", line 643, in _create_direct_connection
      hosts = yield from self._resolve_host(req.url.raw_host, req.port)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py", line 615, in _resolve_host
      self._resolver.resolve(host, port, family=self._family)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/resolver.py", line 30, in resolve
      host, port, type=socket.SOCK_STREAM, family=family)
    File "/opt/boxen/homebrew/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/thread.py", line 55, in run
      result = self.fn(*self.args, **self.kwargs)
    File "/opt/boxen/homebrew/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 743, in getaddrinfo
      for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
  socket.gaierror: [Errno 8] nodename nor servname provided, or not known
  
  The above exception was the direct cause of the following exception:
  
  Traceback (most recent call last):
    File "/Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py", line 42, in parse_apiai
      result = await call_apiai(message, config)
    File "/Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py", line 27, in call_apiai
      headers=headers)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/client.py", line 621, in __await__
      resp = yield from self._coro
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/client.py", line 225, in _request
      conn = yield from self._connector.connect(req)
    File "/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py", line 380, in connect
      .format(key, exc.strerror)) from exc
  aiohttp.client_exceptions.ClientConnectorError: [Errno 8] Cannot connect to host api.api.ai:443 ssl:True [nodename nor servname provided, or not known]
  ```
  
  --- 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/apiai.py`
  Content:
  ```
  1 """A helper function for parsing and executing api.ai skills."""
  2 
  3 import logging
  4 import json
  5 
  6 import aiohttp
  7 
  8 
  9 _LOGGER = logging.getLogger(__name__)
  10 
  11 
  12 async def call_apiai(message, config):
  13     """Call the api.ai api and return the response."""
  14     async with aiohttp.ClientSession() as session:
  15         payload = {
  16             "v": "20150910",
  17             "lang": "en",
  18             "sessionId": message.connector.name,
  19             "query": message.text
  20         }
  21         headers = {
  22             "Authorization": "Bearer " + config['access-token'],
  23             "Content-Type": "application/json"
  24         }
  25         resp = await session.post("https://api.api.ai/v1/query",
  26                                   data=json.dumps(payload),
  27                                   headers=headers)
  28         result = await resp.json()
  29         _LOGGER.debug("api.ai response - " + json.dumps(result))
  30 
  31         return result
  32 
  33 
  34 async def parse_apiai(opsdroid, message, config):
  35     """Parse a message against all apiai skills."""
  36     # pylint: disable=broad-except
  37     # We want to catch all exceptions coming from a skill module and not
  38     # halt the application. If a skill throws an exception it just doesn't
  39     # give a response to the user, so an error response should be given.
  40     if 'access-token' in config:
  41 
  42         result = await call_apiai(message, config)
  43 
  44         if result["status"]["code"] >= 300:
  45             _LOGGER.error("api.ai error - " +
  46                           str(result["status"]["code"]) + " " +
  47                           result["status"]["errorType"])
  48             return
  49 
  50         if "min-score" in config and \
  51                 result["result"]["score"] < config["min-score"]:
  52             _LOGGER.debug("api.ai score lower than min-score")
  53             return
  54 
  55         if result:
  56             for skill in opsdroid.skills:
  57 
  58                 if "apiai_action" in skill or "apiai_intent" in skill:
  59                     if ("action" in result["result"] and
  60                             skill["apiai_action"] in
  61                             result["result"]["action"]) \
  62                             or ("intentName" in result["result"] and
  63                                 skill["apiai_intent"] in
  64                                 result["result"]["intentName"]):
  65                         message.apiai = result
  66                         try:
  67                             await skill["skill"](opsdroid, skill["config"],
  68                                                  message)
  69                         except Exception:
  70                             await message.respond(
  71                                 "Whoops there has been an error")
  72                             await message.respond(
  73                                 "Check the log for details")
  74                             _LOGGER.exception("Exception when parsing '" +
  75                                               message.text +
  76                                               "' against skill '" +
  77                                               result["result"]["action"] + "'")
  78
  ```
  --- 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/parsers/apiai.py b/opsdroid/parsers/apiai.py
  --- a/opsdroid/parsers/apiai.py
  +++ b/opsdroid/parsers/apiai.py
  @@ -38,8 +38,11 @@
       # halt the application. If a skill throws an exception it just doesn't
       # give a response to the user, so an error response should be given.
       if 'access-token' in config:
  -
  -        result = await call_apiai(message, config)
  +        try:
  +            result = await call_apiai(message, config)
  +        except aiohttp.ClientOSError:
  +            _LOGGER.error("No response from api.ai, check your network.")
  +            return
   
           if result["status"]["code"] >= 300:
               _LOGGER.error("api.ai error - " +
  
verification_info: {"golden_diff": "diff --git a/opsdroid/parsers/apiai.py b/opsdroid/parsers/apiai.py\n--- a/opsdroid/parsers/apiai.py\n+++ b/opsdroid/parsers/apiai.py\n@@ -38,8 +38,11 @@\n     # halt the application. If a skill throws an exception it just doesn't\n     # give a response to the user, so an error response should be given.\n     if 'access-token' in config:\n-\n-        result = await call_apiai(message, config)\n+        try:\n+            result = await call_apiai(message, config)\n+        except aiohttp.ClientOSError:\n+            _LOGGER.error(\"No response from api.ai, check your network.\")\n+            return\n \n         if result[\"status\"][\"code\"] >= 300:\n             _LOGGER.error(\"api.ai error - \" +\n", "issue": "API.AI Error when network is disconnected\nThe following error should be caught and warned about cleanly.\r\n\r\n```\r\nERROR asyncio: Task exception was never retrieved\r\nfuture: <Task finished coro=<parse_apiai() done, defined at /Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py:34> exception=ClientConnectorError(8, 'Cannot connect to host api.api.ai:443 ssl:True [nodename nor servname provided, or not known]')>\r\nTraceback (most recent call last):\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py\", line 375, in connect\r\n    proto = yield from self._create_connection(req)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py\", line 632, in _create_connection\r\n    _, proto = yield from self._create_direct_connection(req)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py\", line 643, in _create_direct_connection\r\n    hosts = yield from self._resolve_host(req.url.raw_host, req.port)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py\", line 615, in _resolve_host\r\n    self._resolver.resolve(host, port, family=self._family)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/resolver.py\", line 30, in resolve\r\n    host, port, type=socket.SOCK_STREAM, family=family)\r\n  File \"/opt/boxen/homebrew/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/concurrent/futures/thread.py\", line 55, in run\r\n    result = self.fn(*self.args, **self.kwargs)\r\n  File \"/opt/boxen/homebrew/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py\", line 743, in getaddrinfo\r\n    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):\r\nsocket.gaierror: [Errno 8] nodename nor servname provided, or not known\r\n\r\nThe above exception was the direct cause of the following exception:\r\n\r\nTraceback (most recent call last):\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py\", line 42, in parse_apiai\r\n    result = await call_apiai(message, config)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/opsdroid/parsers/apiai.py\", line 27, in call_apiai\r\n    headers=headers)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/client.py\", line 621, in __await__\r\n    resp = yield from self._coro\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/client.py\", line 225, in _request\r\n    conn = yield from self._connector.connect(req)\r\n  File \"/Users/jacob/Projects/opsdroid/opsdroid/venv/lib/python3.6/site-packages/aiohttp/connector.py\", line 380, in connect\r\n    .format(key, exc.strerror)) from exc\r\naiohttp.client_exceptions.ClientConnectorError: [Errno 8] Cannot connect to host api.api.ai:443 ssl:True [nodename nor servname provided, or not known]\r\n```\n", "before_files": [{"content": "\"\"\"A helper function for parsing and executing api.ai skills.\"\"\"\n\nimport logging\nimport json\n\nimport aiohttp\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nasync def call_apiai(message, config):\n    \"\"\"Call the api.ai api and return the response.\"\"\"\n    async with aiohttp.ClientSession() as session:\n        payload = {\n            \"v\": \"20150910\",\n            \"lang\": \"en\",\n            \"sessionId\": message.connector.name,\n            \"query\": message.text\n        }\n        headers = {\n            \"Authorization\": \"Bearer \" + config['access-token'],\n            \"Content-Type\": \"application/json\"\n        }\n        resp = await session.post(\"https://api.api.ai/v1/query\",\n                                  data=json.dumps(payload),\n                                  headers=headers)\n        result = await resp.json()\n        _LOGGER.debug(\"api.ai response - \" + json.dumps(result))\n\n        return result\n\n\nasync def parse_apiai(opsdroid, message, config):\n    \"\"\"Parse a message against all apiai skills.\"\"\"\n    # pylint: disable=broad-except\n    # We want to catch all exceptions coming from a skill module and not\n    # halt the application. If a skill throws an exception it just doesn't\n    # give a response to the user, so an error response should be given.\n    if 'access-token' in config:\n\n        result = await call_apiai(message, config)\n\n        if result[\"status\"][\"code\"] >= 300:\n            _LOGGER.error(\"api.ai error - \" +\n                          str(result[\"status\"][\"code\"]) + \" \" +\n                          result[\"status\"][\"errorType\"])\n            return\n\n        if \"min-score\" in config and \\\n                result[\"result\"][\"score\"] < config[\"min-score\"]:\n            _LOGGER.debug(\"api.ai score lower than min-score\")\n            return\n\n        if result:\n            for skill in opsdroid.skills:\n\n                if \"apiai_action\" in skill or \"apiai_intent\" in skill:\n                    if (\"action\" in result[\"result\"] and\n                            skill[\"apiai_action\"] in\n                            result[\"result\"][\"action\"]) \\\n                            or (\"intentName\" in result[\"result\"] and\n                                skill[\"apiai_intent\"] in\n                                result[\"result\"][\"intentName\"]):\n                        message.apiai = result\n                        try:\n                            await skill[\"skill\"](opsdroid, skill[\"config\"],\n                                                 message)\n                        except Exception:\n                            await message.respond(\n                                \"Whoops there has been an error\")\n                            await message.respond(\n                                \"Check the log for details\")\n                            _LOGGER.exception(\"Exception when parsing '\" +\n                                              message.text +\n                                              \"' against skill '\" +\n                                              result[\"result\"][\"action\"] + \"'\")\n", "path": "opsdroid/parsers/apiai.py"}], "after_files": [{"content": "\"\"\"A helper function for parsing and executing api.ai skills.\"\"\"\n\nimport logging\nimport json\n\nimport aiohttp\n\n\n_LOGGER = logging.getLogger(__name__)\n\n\nasync def call_apiai(message, config):\n    \"\"\"Call the api.ai api and return the response.\"\"\"\n    async with aiohttp.ClientSession() as session:\n        payload = {\n            \"v\": \"20150910\",\n            \"lang\": \"en\",\n            \"sessionId\": message.connector.name,\n            \"query\": message.text\n        }\n        headers = {\n            \"Authorization\": \"Bearer \" + config['access-token'],\n            \"Content-Type\": \"application/json\"\n        }\n        resp = await session.post(\"https://api.api.ai/v1/query\",\n                                  data=json.dumps(payload),\n                                  headers=headers)\n        result = await resp.json()\n        _LOGGER.debug(\"api.ai response - \" + json.dumps(result))\n\n        return result\n\n\nasync def parse_apiai(opsdroid, message, config):\n    \"\"\"Parse a message against all apiai skills.\"\"\"\n    # pylint: disable=broad-except\n    # We want to catch all exceptions coming from a skill module and not\n    # halt the application. If a skill throws an exception it just doesn't\n    # give a response to the user, so an error response should be given.\n    if 'access-token' in config:\n        try:\n            result = await call_apiai(message, config)\n        except aiohttp.ClientOSError:\n            _LOGGER.error(\"No response from api.ai, check your network.\")\n            return\n\n        if result[\"status\"][\"code\"] >= 300:\n            _LOGGER.error(\"api.ai error - \" +\n                          str(result[\"status\"][\"code\"]) + \" \" +\n                          result[\"status\"][\"errorType\"])\n            return\n\n        if \"min-score\" in config and \\\n                result[\"result\"][\"score\"] < config[\"min-score\"]:\n            _LOGGER.debug(\"api.ai score lower than min-score\")\n            return\n\n        if result:\n            for skill in opsdroid.skills:\n\n                if \"apiai_action\" in skill or \"apiai_intent\" in skill:\n                    if (\"action\" in result[\"result\"] and\n                            skill[\"apiai_action\"] in\n                            result[\"result\"][\"action\"]) \\\n                            or (\"intentName\" in result[\"result\"] and\n                                skill[\"apiai_intent\"] in\n                                result[\"result\"][\"intentName\"]):\n                        message.apiai = result\n                        try:\n                            await skill[\"skill\"](opsdroid, skill[\"config\"],\n                                                 message)\n                        except Exception:\n                            await message.respond(\n                                \"Whoops there has been an error\")\n                            await message.respond(\n                                \"Check the log for details\")\n                            _LOGGER.exception(\"Exception when parsing '\" +\n                                              message.text +\n                                              \"' against skill '\" +\n                                              result[\"result\"][\"action\"] + \"'\")\n", "path": "opsdroid/parsers/apiai.py"}]}