We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Parser regex.py does not support identically named group in regex_matcher
# Description

When using the same group name in **match_regex**  decorator **regex.py** issue an error because the python re module used to parse messages does not support identical named group


## Steps to Reproduce
- Create a skill like (the goal is to have a multiple patterns using the same group name)

```python
from opsdroid.matchers import match_regex
from opsdroid.skill import Skill

PATTERNS = '|'.join([
    "say (?P<word>\w+)",
    "repeat (?P<word>\w+)",
    "say",
    "repeat"
])


class SaySkill(Skill):

    @match_regex(PATTERNS, case_sensitive=False, matching_condition='match')
    async def say(self, message):
        word = message.entities['word']['value'] or None
        if word is None:
            word = 'No word to say'

        await message.respond(word)

```

- Add it to configuration file
- using opsdroid shell or opsdroid desktop test it 
- Issue occurs

```
DEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.
ERROR aiohttp.server: Error handling request
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/lib64/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/connector/websocket/__init__.py", line 101, in websocket_handler
    await self.opsdroid.parse(message)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py", line 498, in parse
    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py", line 421, in get_ranked_skills
    ranked_skills += await parse_regex(self, skills, message)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py", line 40, in parse_regex
    regex = await match_regex(message.text, opts)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py", line 29, in match_regex
    regex = re.match(opts["expression"], text, is_case_sensitive())
  File "/usr/lib64/python3.6/re.py", line 172, in match
    return _compile(pattern, flags).match(string)
  File "/usr/lib64/python3.6/re.py", line 301, in _compile
    p = sre_compile.compile(pattern, flags)
  File "/usr/lib64/python3.6/sre_compile.py", line 562, in compile
    p = sre_parse.parse(p, flags)
  File "/usr/lib64/python3.6/sre_parse.py", line 855, in parse
    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
  File "/usr/lib64/python3.6/sre_parse.py", line 416, in _parse_sub
    not nested and not items))
  File "/usr/lib64/python3.6/sre_parse.py", line 759, in _parse
    raise source.error(err.msg, len(name) + 1) from None
sre_constants.error: redefinition of group name 'city' as group 2; was group 1 at position 42  <-------
```

## Expected Functionality
Handling multiple patterns with the same name group

```
DEBUG opsdroid.parsers.crontab: Running crontab skills at Fri May  1 19:03:00 2020.
DEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.
DEBUG opsdroid.core: Processing parsers...
DEBUG opsdroid.connector.websocket: Responding with: 'hello' in target 8d9c8b96-8bcd-11ea-b043-0050568f7a82    <----------
```
## Experienced Functionality

debug logs :

```
DEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.
ERROR aiohttp.server: Error handling request
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/lib64/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/connector/websocket/__init__.py", line 101, in websocket_handler
    await self.opsdroid.parse(message)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py", line 498, in parse
    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py", line 421, in get_ranked_skills
    ranked_skills += await parse_regex(self, skills, message)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py", line 40, in parse_regex
    regex = await match_regex(message.text, opts)
  File "/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py", line 29, in match_regex
    regex = re.match(opts["expression"], text, is_case_sensitive())
  File "/usr/lib64/python3.6/re.py", line 172, in match
    return _compile(pattern, flags).match(string)
  File "/usr/lib64/python3.6/re.py", line 301, in _compile
    p = sre_compile.compile(pattern, flags)
  File "/usr/lib64/python3.6/sre_compile.py", line 562, in compile
    p = sre_parse.parse(p, flags)
  File "/usr/lib64/python3.6/sre_parse.py", line 855, in parse
    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
  File "/usr/lib64/python3.6/sre_parse.py", line 416, in _parse_sub
    not nested and not items))
  File "/usr/lib64/python3.6/sre_parse.py", line 759, in _parse
    raise source.error(err.msg, len(name) + 1) from None
sre_constants.error: redefinition of group name 'city' as group 2; was group 1 at position 42  <-------
```

## Versions
- **Opsdroid version:*v0.18.0*
- **Python version:*3.6.7*

<!-- Love opsdroid? Please consider supporting our collective:
 +👉  https://opencollective.com/opsdroid/donate -->


--- 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/regex.py`
Content:
```
1 """A helper function for parsing and executing regex skills."""
2 
3 import logging
4 import re
5 
6 _LOGGER = logging.getLogger(__name__)
7 
8 
9 async def calculate_score(regex, score_factor):
10     """Calculate the score of a regex."""
11     # The score asymptotically approaches the max score
12     # based on the length of the expression.
13     return (1 - (1 / ((len(regex) + 1) ** 2))) * score_factor
14 
15 
16 async def match_regex(text, opts):
17     """Return False if matching does not need to be case sensitive."""
18 
19     def is_case_sensitive():
20         if opts["case_sensitive"]:
21             return False
22         return re.IGNORECASE
23 
24     if opts["matching_condition"].lower() == "search":
25         regex = re.search(opts["expression"], text, is_case_sensitive())
26     elif opts["matching_condition"].lower() == "fullmatch":
27         regex = re.fullmatch(opts["expression"], text, is_case_sensitive())
28     else:
29         regex = re.match(opts["expression"], text, is_case_sensitive())
30     return regex
31 
32 
33 async def parse_regex(opsdroid, skills, message):
34     """Parse a message against all regex skills."""
35     matched_skills = []
36     for skill in skills:
37         for matcher in skill.matchers:
38             if "regex" in matcher:
39                 opts = matcher["regex"]
40                 regex = await match_regex(message.text, opts)
41                 if regex:
42                     message.regex = regex
43                     for regroup, value in regex.groupdict().items():
44                         message.update_entity(regroup, value, None)
45                     matched_skills.append(
46                         {
47                             "score": await calculate_score(
48                                 opts["expression"], opts["score_factor"]
49                             ),
50                             "skill": skill,
51                             "config": skill.config,
52                             "message": message,
53                         }
54                     )
55     return matched_skills
56
```
--- 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/regex.py b/opsdroid/parsers/regex.py
--- a/opsdroid/parsers/regex.py
+++ b/opsdroid/parsers/regex.py
@@ -1,16 +1,16 @@
 """A helper function for parsing and executing regex skills."""
 
 import logging
-import re
+import regex
 
 _LOGGER = logging.getLogger(__name__)
 
 
-async def calculate_score(regex, score_factor):
+async def calculate_score(expression, score_factor):
     """Calculate the score of a regex."""
     # The score asymptotically approaches the max score
     # based on the length of the expression.
-    return (1 - (1 / ((len(regex) + 1) ** 2))) * score_factor
+    return (1 - (1 / ((len(expression) + 1) ** 2))) * score_factor
 
 
 async def match_regex(text, opts):
@@ -19,15 +19,15 @@
     def is_case_sensitive():
         if opts["case_sensitive"]:
             return False
-        return re.IGNORECASE
+        return regex.IGNORECASE
 
     if opts["matching_condition"].lower() == "search":
-        regex = re.search(opts["expression"], text, is_case_sensitive())
+        matched_regex = regex.search(opts["expression"], text, is_case_sensitive())
     elif opts["matching_condition"].lower() == "fullmatch":
-        regex = re.fullmatch(opts["expression"], text, is_case_sensitive())
+        matched_regex = regex.fullmatch(opts["expression"], text, is_case_sensitive())
     else:
-        regex = re.match(opts["expression"], text, is_case_sensitive())
-    return regex
+        matched_regex = regex.match(opts["expression"], text, is_case_sensitive())
+    return matched_regex
 
 
 async def parse_regex(opsdroid, skills, message):
@@ -37,10 +37,10 @@
         for matcher in skill.matchers:
             if "regex" in matcher:
                 opts = matcher["regex"]
-                regex = await match_regex(message.text, opts)
-                if regex:
-                    message.regex = regex
-                    for regroup, value in regex.groupdict().items():
+                matched_regex = await match_regex(message.text, opts)
+                if matched_regex:
+                    message.regex = matched_regex
+                    for regroup, value in matched_regex.groupdict().items():
                         message.update_entity(regroup, value, None)
                     matched_skills.append(
                         {

{"golden_diff": "diff --git a/opsdroid/parsers/regex.py b/opsdroid/parsers/regex.py\n--- a/opsdroid/parsers/regex.py\n+++ b/opsdroid/parsers/regex.py\n@@ -1,16 +1,16 @@\n \"\"\"A helper function for parsing and executing regex skills.\"\"\"\n \n import logging\n-import re\n+import regex\n \n _LOGGER = logging.getLogger(__name__)\n \n \n-async def calculate_score(regex, score_factor):\n+async def calculate_score(expression, score_factor):\n     \"\"\"Calculate the score of a regex.\"\"\"\n     # The score asymptotically approaches the max score\n     # based on the length of the expression.\n-    return (1 - (1 / ((len(regex) + 1) ** 2))) * score_factor\n+    return (1 - (1 / ((len(expression) + 1) ** 2))) * score_factor\n \n \n async def match_regex(text, opts):\n@@ -19,15 +19,15 @@\n     def is_case_sensitive():\n         if opts[\"case_sensitive\"]:\n             return False\n-        return re.IGNORECASE\n+        return regex.IGNORECASE\n \n     if opts[\"matching_condition\"].lower() == \"search\":\n-        regex = re.search(opts[\"expression\"], text, is_case_sensitive())\n+        matched_regex = regex.search(opts[\"expression\"], text, is_case_sensitive())\n     elif opts[\"matching_condition\"].lower() == \"fullmatch\":\n-        regex = re.fullmatch(opts[\"expression\"], text, is_case_sensitive())\n+        matched_regex = regex.fullmatch(opts[\"expression\"], text, is_case_sensitive())\n     else:\n-        regex = re.match(opts[\"expression\"], text, is_case_sensitive())\n-    return regex\n+        matched_regex = regex.match(opts[\"expression\"], text, is_case_sensitive())\n+    return matched_regex\n \n \n async def parse_regex(opsdroid, skills, message):\n@@ -37,10 +37,10 @@\n         for matcher in skill.matchers:\n             if \"regex\" in matcher:\n                 opts = matcher[\"regex\"]\n-                regex = await match_regex(message.text, opts)\n-                if regex:\n-                    message.regex = regex\n-                    for regroup, value in regex.groupdict().items():\n+                matched_regex = await match_regex(message.text, opts)\n+                if matched_regex:\n+                    message.regex = matched_regex\n+                    for regroup, value in matched_regex.groupdict().items():\n                         message.update_entity(regroup, value, None)\n                     matched_skills.append(\n                         {\n", "issue": "Parser regex.py does not support identically named group in regex_matcher\n# Description\r\n\r\nWhen using the same group name in **match_regex**  decorator **regex.py** issue an error because the python re module used to parse messages does not support identical named group\r\n\r\n\r\n## Steps to Reproduce\r\n- Create a skill like (the goal is to have a multiple patterns using the same group name)\r\n\r\n```python\r\nfrom opsdroid.matchers import match_regex\r\nfrom opsdroid.skill import Skill\r\n\r\nPATTERNS = '|'.join([\r\n    \"say (?P<word>\\w+)\",\r\n    \"repeat (?P<word>\\w+)\",\r\n    \"say\",\r\n    \"repeat\"\r\n])\r\n\r\n\r\nclass SaySkill(Skill):\r\n\r\n    @match_regex(PATTERNS, case_sensitive=False, matching_condition='match')\r\n    async def say(self, message):\r\n        word = message.entities['word']['value'] or None\r\n        if word is None:\r\n            word = 'No word to say'\r\n\r\n        await message.respond(word)\r\n\r\n```\r\n\r\n- Add it to configuration file\r\n- using opsdroid shell or opsdroid desktop test it \r\n- Issue occurs\r\n\r\n```\r\nDEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.\r\nERROR aiohttp.server: Error handling request\r\nTraceback (most recent call last):\r\n  File \"/usr/lib64/python3.6/site-packages/aiohttp/web_protocol.py\", line 418, in start\r\n    resp = await task\r\n  File \"/usr/lib64/python3.6/site-packages/aiohttp/web_app.py\", line 458, in _handle\r\n    resp = await handler(request)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/connector/websocket/__init__.py\", line 101, in websocket_handler\r\n    await self.opsdroid.parse(message)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py\", line 498, in parse\r\n    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py\", line 421, in get_ranked_skills\r\n    ranked_skills += await parse_regex(self, skills, message)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py\", line 40, in parse_regex\r\n    regex = await match_regex(message.text, opts)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py\", line 29, in match_regex\r\n    regex = re.match(opts[\"expression\"], text, is_case_sensitive())\r\n  File \"/usr/lib64/python3.6/re.py\", line 172, in match\r\n    return _compile(pattern, flags).match(string)\r\n  File \"/usr/lib64/python3.6/re.py\", line 301, in _compile\r\n    p = sre_compile.compile(pattern, flags)\r\n  File \"/usr/lib64/python3.6/sre_compile.py\", line 562, in compile\r\n    p = sre_parse.parse(p, flags)\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 855, in parse\r\n    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 416, in _parse_sub\r\n    not nested and not items))\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 759, in _parse\r\n    raise source.error(err.msg, len(name) + 1) from None\r\nsre_constants.error: redefinition of group name 'city' as group 2; was group 1 at position 42  <-------\r\n```\r\n\r\n## Expected Functionality\r\nHandling multiple patterns with the same name group\r\n\r\n```\r\nDEBUG opsdroid.parsers.crontab: Running crontab skills at Fri May  1 19:03:00 2020.\r\nDEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.\r\nDEBUG opsdroid.core: Processing parsers...\r\nDEBUG opsdroid.connector.websocket: Responding with: 'hello' in target 8d9c8b96-8bcd-11ea-b043-0050568f7a82    <----------\r\n```\r\n## Experienced Functionality\r\n\r\ndebug logs :\r\n\r\n```\r\nDEBUG opsdroid.core: Parsing input: <opsdroid.events.Message(text=say hello)>.\r\nERROR aiohttp.server: Error handling request\r\nTraceback (most recent call last):\r\n  File \"/usr/lib64/python3.6/site-packages/aiohttp/web_protocol.py\", line 418, in start\r\n    resp = await task\r\n  File \"/usr/lib64/python3.6/site-packages/aiohttp/web_app.py\", line 458, in _handle\r\n    resp = await handler(request)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/connector/websocket/__init__.py\", line 101, in websocket_handler\r\n    await self.opsdroid.parse(message)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py\", line 498, in parse\r\n    ranked_skills = await self.get_ranked_skills(unconstrained_skills, event)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/core.py\", line 421, in get_ranked_skills\r\n    ranked_skills += await parse_regex(self, skills, message)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py\", line 40, in parse_regex\r\n    regex = await match_regex(message.text, opts)\r\n  File \"/home/ibot/ibot-opsdroid/opsdroid/opsdroid/parsers/regex.py\", line 29, in match_regex\r\n    regex = re.match(opts[\"expression\"], text, is_case_sensitive())\r\n  File \"/usr/lib64/python3.6/re.py\", line 172, in match\r\n    return _compile(pattern, flags).match(string)\r\n  File \"/usr/lib64/python3.6/re.py\", line 301, in _compile\r\n    p = sre_compile.compile(pattern, flags)\r\n  File \"/usr/lib64/python3.6/sre_compile.py\", line 562, in compile\r\n    p = sre_parse.parse(p, flags)\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 855, in parse\r\n    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 416, in _parse_sub\r\n    not nested and not items))\r\n  File \"/usr/lib64/python3.6/sre_parse.py\", line 759, in _parse\r\n    raise source.error(err.msg, len(name) + 1) from None\r\nsre_constants.error: redefinition of group name 'city' as group 2; was group 1 at position 42  <-------\r\n```\r\n\r\n## Versions\r\n- **Opsdroid version:*v0.18.0*\r\n- **Python version:*3.6.7*\r\n\r\n<!-- Love opsdroid? Please consider supporting our collective:\r\n +\ud83d\udc49  https://opencollective.com/opsdroid/donate -->\r\n\n", "before_files": [{"content": "\"\"\"A helper function for parsing and executing regex skills.\"\"\"\n\nimport logging\nimport re\n\n_LOGGER = logging.getLogger(__name__)\n\n\nasync def calculate_score(regex, score_factor):\n    \"\"\"Calculate the score of a regex.\"\"\"\n    # The score asymptotically approaches the max score\n    # based on the length of the expression.\n    return (1 - (1 / ((len(regex) + 1) ** 2))) * score_factor\n\n\nasync def match_regex(text, opts):\n    \"\"\"Return False if matching does not need to be case sensitive.\"\"\"\n\n    def is_case_sensitive():\n        if opts[\"case_sensitive\"]:\n            return False\n        return re.IGNORECASE\n\n    if opts[\"matching_condition\"].lower() == \"search\":\n        regex = re.search(opts[\"expression\"], text, is_case_sensitive())\n    elif opts[\"matching_condition\"].lower() == \"fullmatch\":\n        regex = re.fullmatch(opts[\"expression\"], text, is_case_sensitive())\n    else:\n        regex = re.match(opts[\"expression\"], text, is_case_sensitive())\n    return regex\n\n\nasync def parse_regex(opsdroid, skills, message):\n    \"\"\"Parse a message against all regex skills.\"\"\"\n    matched_skills = []\n    for skill in skills:\n        for matcher in skill.matchers:\n            if \"regex\" in matcher:\n                opts = matcher[\"regex\"]\n                regex = await match_regex(message.text, opts)\n                if regex:\n                    message.regex = regex\n                    for regroup, value in regex.groupdict().items():\n                        message.update_entity(regroup, value, None)\n                    matched_skills.append(\n                        {\n                            \"score\": await calculate_score(\n                                opts[\"expression\"], opts[\"score_factor\"]\n                            ),\n                            \"skill\": skill,\n                            \"config\": skill.config,\n                            \"message\": message,\n                        }\n                    )\n    return matched_skills\n", "path": "opsdroid/parsers/regex.py"}], "after_files": [{"content": "\"\"\"A helper function for parsing and executing regex skills.\"\"\"\n\nimport logging\nimport regex\n\n_LOGGER = logging.getLogger(__name__)\n\n\nasync def calculate_score(expression, score_factor):\n    \"\"\"Calculate the score of a regex.\"\"\"\n    # The score asymptotically approaches the max score\n    # based on the length of the expression.\n    return (1 - (1 / ((len(expression) + 1) ** 2))) * score_factor\n\n\nasync def match_regex(text, opts):\n    \"\"\"Return False if matching does not need to be case sensitive.\"\"\"\n\n    def is_case_sensitive():\n        if opts[\"case_sensitive\"]:\n            return False\n        return regex.IGNORECASE\n\n    if opts[\"matching_condition\"].lower() == \"search\":\n        matched_regex = regex.search(opts[\"expression\"], text, is_case_sensitive())\n    elif opts[\"matching_condition\"].lower() == \"fullmatch\":\n        matched_regex = regex.fullmatch(opts[\"expression\"], text, is_case_sensitive())\n    else:\n        matched_regex = regex.match(opts[\"expression\"], text, is_case_sensitive())\n    return matched_regex\n\n\nasync def parse_regex(opsdroid, skills, message):\n    \"\"\"Parse a message against all regex skills.\"\"\"\n    matched_skills = []\n    for skill in skills:\n        for matcher in skill.matchers:\n            if \"regex\" in matcher:\n                opts = matcher[\"regex\"]\n                matched_regex = await match_regex(message.text, opts)\n                if matched_regex:\n                    message.regex = matched_regex\n                    for regroup, value in matched_regex.groupdict().items():\n                        message.update_entity(regroup, value, None)\n                    matched_skills.append(\n                        {\n                            \"score\": await calculate_score(\n                                opts[\"expression\"], opts[\"score_factor\"]\n                            ),\n                            \"skill\": skill,\n                            \"config\": skill.config,\n                            \"message\": message,\n                        }\n                    )\n    return matched_skills\n", "path": "opsdroid/parsers/regex.py"}]}