Repository: opsdroid/opsdroid

Issue:
Slack connector crashes after running dance several times
# Description
Slack connector crashes after running dance several times in a row

## Steps to Reproduce
Launch opsdroid with slack connector configured with bot token
go the channel connected in and run dance 3+ times in  a row

## Expected Functionality
Opsdroid keeps running fine


## Experienced Functionality
Opsdroid crashes with the following error:
```
ERROR slack.rtm.client: When calling '#process_message()' in the 'opsdroid.connector.slack' module the following error was raised: 'user'
Traceback (most recent call last):
  File "C:\Users\koe4945\desktop\git\personal\ops\venv\Scripts\opsdroid-script.py", line 11, in <module>
    load_entry_point('opsdroid', 'console_scripts', 'opsdroid')()
  File "c:\users\koe4945\desktop\git\personal\ops\venv\lib\site-packages\click\core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "c:\users\koe4945\desktop\git\personal\ops\venv\lib\site-packages\click\core.py", line 717, in main
    rv = self.invoke(ctx)
  File "c:\users\koe4945\desktop\git\personal\ops\venv\lib\site-packages\click\core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\users\koe4945\desktop\git\personal\ops\venv\lib\site-packages\click\core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\users\koe4945\desktop\git\personal\ops\venv\lib\site-packages\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "c:\users\koe4945\desktop\git\personal\ops\opsdroid\opsdroid\cli\start.py", line 31, in start
    opsdroid.run()
  File "c:\users\koe4945\desktop\git\personal\ops\opsdroid\opsdroid\core.py", line 161, in run
    self.eventloop.run_until_complete(asyncio.gather(*pending))
  File "C:\Users\koe4945\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 584, in run_until_complete
    return future.result()
  File "c:\users\koe4945\desktop\git\personal\ops\python-slackclient\slack\rtm\client.py", line 339, in _connect_and_read
    await self._read_messages()
  File "c:\users\koe4945\desktop\git\personal\ops\python-slackclient\slack\rtm\client.py", line 390, in _read_messages
    await self._dispatch_event(event, data=payload)
  File "c:\users\koe4945\desktop\git\personal\ops\python-slackclient\slack\rtm\client.py", line 437, in _dispatch_event
    rtm_client=self, web_client=self._web_client, data=data
  File "c:\users\koe4945\desktop\git\personal\ops\opsdroid\opsdroid\connector\slack\__init__.py", line 104, in process_message
    user_info = await self.lookup_username(message["user"])
KeyError: 'user'
ERROR: Unhandled exception in opsdroid, exiting...
```

## Versions
- **Opsdroid version:**  v0.16.0+62.g620590f
- **Python version:**  3.7.3
- **OS/Docker version:**  Windows 10

## Configuration File
Please include your version of the configuration file below.

```yaml
connectors:
  - name: slack
    # required
    api-token: "[...bot token...]"

```

## Additional Details
Any other details you wish to include such as screenshots, console messages, etc.



Discussion:

Resolving patch:
diff --git a/opsdroid/connector/slack/__init__.py b/opsdroid/connector/slack/__init__.py
--- a/opsdroid/connector/slack/__init__.py
+++ b/opsdroid/connector/slack/__init__.py
@@ -90,6 +90,10 @@ async def process_message(self, **payload):
         """Process a raw message and pass it to the parser."""
         message = payload["data"]
 
+        # Ignore message edits
+        if "subtype" in message and message["subtype"] == "message_changed":
+            return
+
         # Ignore own messages
         if (
             "subtype" in message


Test patch:
diff --git a/tests/test_connector_slack.py b/tests/test_connector_slack.py
--- a/tests/test_connector_slack.py
+++ b/tests/test_connector_slack.py
@@ -129,6 +129,12 @@ async def test_process_message(self):
         del message["subtype"]
         connector.bot_id = None
 
+        connector.opsdroid.parse.reset_mock()
+        message["subtype"] = "message_changed"
+        await connector.process_message(data=message)
+        self.assertFalse(connector.opsdroid.parse.called)
+        del message["subtype"]
+
         connector.opsdroid.parse.reset_mock()
         connector.lookup_username.side_effect = ValueError
         await connector.process_message(data=message)
@@ -181,7 +187,7 @@ async def test_react(self):
         connector = ConnectorSlack({"api-token": "abc123"})
         connector.slack.api_call = amock.CoroutineMock()
         prev_message = Message("test", "user", "room", connector, raw_event={"ts": 0})
-        with OpsDroid() as opsdroid:
+        with OpsDroid():
             await prev_message.respond(Reaction("😀"))
         self.assertTrue(connector.slack.api_call)
         self.assertEqual(
@@ -196,7 +202,7 @@ async def test_react_invalid_name(self):
             side_effect=slack.errors.SlackApiError("invalid_name", "invalid_name")
         )
         prev_message = Message("test", "user", "room", connector, raw_event={"ts": 0})
-        with OpsDroid() as opsdroid:
+        with OpsDroid():
             await prev_message.respond(Reaction("😀"))
         self.assertLogs("_LOGGER", "warning")
 
@@ -207,7 +213,7 @@ async def test_react_unknown_error(self):
         connector.slack.api_call = amock.CoroutineMock(
             side_effect=slack.errors.SlackApiError("unknown", "unknown")
         )
-        with self.assertRaises(slack.errors.SlackApiError), OpsDroid() as opsdroid:
+        with self.assertRaises(slack.errors.SlackApiError), OpsDroid():
             prev_message = Message(
                 "test", "user", "room", connector, raw_event={"ts": 0}
             )