Repository: opsdroid/opsdroid

Issue:
Message replies using Blocks in a thread end up outside of the thread even when start-thread: true
# Description
When start-thread: true in configuration.yaml, regular text replies do end up in the thread. Fine.

However, the same reply inside of Blocks() construct, end up outside of the thread in a new message in the channel.

This is not expected.

## Steps to Reproduce
1) Set start-thread: true in configuration.yaml
2) Write the first message, then, create a thread inside of this message saying "hi" with the following code:

message.respond here will reply in a thread as expected:

```python
import random

from opsdroid.matchers import match_regex
from opsdroid.skill import Skill


class HelloSkill(Skill):

    @match_regex(r'hi|hello|hey|hallo', case_sensitive=False)
    async def hello(self, message):
        await message.respond('Hello!')
```

message.respond here will NOT reply in a thread (just because we're. using "Blocks"?):

```python
import random

from opsdroid.matchers import match_regex
from opsdroid.skill import Skill


class HelloSkill(Skill):

    @match_regex(r'hi|hello|hey|hallo', case_sensitive=False)
    async def hello(self, message):
        await message.respond(Blocks([
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": "Hey! I'm opsdroid.\nI'm a natural language event driven automation bot framework.\n*What a mouthful!*"
                    },
                    "accessory": {
                        "type": "image",
                        "image_url": "https://raw.githubusercontent.com/opsdroid/style-guidelines/master/logos/logo-light.png",
                        "alt_text": "opsdroid logo"
                    }
                },
                {
                    "type": "actions",
                    "elements": [
                        {
                            "type": "button",
                            "text": {
                                "type": "plain_text",
                                "text": "Visit website",
                                "emoji": True
                            },
                            "url": "https://opsdroid.dev"
                        }
                    ]
                }
            ]
        ))
```

## Expected Functionality
message.respond(Blocks[...]) should reply in a thread when user types "hi" inside of a thread and start-thread: true is set in configuration.yaml



Discussion:
I started working on this, but I can't give you a date were I will have it done by.
Just wondering, would it make sense to add another config arg to set "start-thread-blocks" to true/false so users can stop opsdroid from replying to blocks only if they want?
If there's a use case for this then yeah but in my case the reason I set start-thread: true is because I want *ALL* replies within a thread. No exceptions. I'm not sure who would want a thread not to be started just because they're using blocks. However, if you feel like there's a good use case for this kind of implementation, then go for it :)

Resolving patch:
diff --git a/opsdroid/connector/slack/connector.py b/opsdroid/connector/slack/connector.py
--- a/opsdroid/connector/slack/connector.py
+++ b/opsdroid/connector/slack/connector.py
@@ -9,6 +9,7 @@
 import aiohttp
 import certifi
 from emoji import demojize
+
 from slack_sdk.errors import SlackApiError
 from slack_sdk.socket_mode.aiohttp import SocketModeClient
 from slack_sdk.socket_mode.request import SocketModeRequest
@@ -149,6 +150,35 @@ async def disconnect(self):
     async def listen(self):
         """Listen for and parse new messages."""
 
+    def _generate_base_data(self, event: opsdroid.events.Event) -> dict:
+        """Generate a base data dict to send to the slack API.
+
+        The data dictionary will always contain `channel`, `username`
+        and `icon_emoj` which can be derived from the event or the
+        slack config.
+
+        If we want to send messages in a thread, we need to include
+        thread_ts from the linked event (the message received by opsdroid) and the slack api will know that this new message
+        should be included in a thread and not in the channel.
+
+        """
+        data = {
+            "channel": event.target,
+            "username": self.bot_name,
+            "icon_emoji": self.icon_emoji,
+        }
+
+        if event.linked_event:
+            raw_event = event.linked_event.raw_event
+
+            if isinstance(raw_event, dict) and "thread_ts" in raw_event:
+                if event.linked_event.event_id != raw_event["thread_ts"]:
+                    # Linked Event is inside a thread
+                    data["thread_ts"] = raw_event["thread_ts"]
+            elif self.start_thread:
+                data["thread_ts"] = event.linked_event.event_id
+        return data
+
     async def event_handler(self, payload):
         """Handle different payload types and parse the resulting events"""
 
@@ -313,22 +343,9 @@ async def _send_message(self, message):
         _LOGGER.debug(
             _("Responding with: '%s' in room  %s."), message.text, message.target
         )
-        data = {
-            "channel": message.target,
-            "text": message.text,
-            "username": self.bot_name,
-            "icon_emoji": self.icon_emoji,
-        }
 
-        if message.linked_event:
-            raw_event = message.linked_event.raw_event
-
-            if isinstance(raw_event, dict) and "thread_ts" in raw_event:
-                if message.linked_event.event_id != raw_event["thread_ts"]:
-                    # Linked Event is inside a thread
-                    data["thread_ts"] = raw_event["thread_ts"]
-            elif self.start_thread:
-                data["thread_ts"] = message.linked_event.event_id
+        data = self._generate_base_data(message)
+        data["text"] = message.text
 
         return await self.slack_web_client.api_call(
             "chat.postMessage",
@@ -361,16 +378,10 @@ async def _send_blocks(self, blocks):
         _LOGGER.debug(
             _("Responding with interactive blocks in room %s."), blocks.target
         )
+        data = self._generate_base_data(blocks)
+        data["blocks"] = blocks.blocks
 
-        return await self.slack_web_client.api_call(
-            "chat.postMessage",
-            data={
-                "channel": blocks.target,
-                "username": self.bot_name,
-                "blocks": blocks.blocks,
-                "icon_emoji": self.icon_emoji,
-            },
-        )
+        return await self.slack_web_client.api_call("chat.postMessage", data=data)
 
     @register_event(EditedBlocks)
     async def _edit_blocks(self, blocks):


Test patch: