From 3f8060b5828f718c8b26bbe85dc01298d926b963 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Fri, 6 Mar 2026 12:20:19 -0500 Subject: [PATCH 1/5] fix(assistant): get_thread_context falls through to store.find() for user_message events --- .../get_thread_context/async_get_thread_context.py | 10 +++------- .../context/get_thread_context/get_thread_context.py | 10 +++------- tests/scenario_tests/test_events_assistant.py | 2 +- tests/scenario_tests_async/test_events_assistant.py | 2 +- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/slack_bolt/context/get_thread_context/async_get_thread_context.py b/slack_bolt/context/get_thread_context/async_get_thread_context.py index cb8683a10..a9e99bc59 100644 --- a/slack_bolt/context/get_thread_context/async_get_thread_context.py +++ b/slack_bolt/context/get_thread_context/async_get_thread_context.py @@ -31,14 +31,10 @@ async def __call__(self) -> Optional[AssistantThreadContext]: if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if thread is not None and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/slack_bolt/context/get_thread_context/get_thread_context.py b/slack_bolt/context/get_thread_context/get_thread_context.py index 0a77d2d9f..368afebcc 100644 --- a/slack_bolt/context/get_thread_context/get_thread_context.py +++ b/slack_bolt/context/get_thread_context/get_thread_context.py @@ -31,14 +31,10 @@ def __call__(self) -> Optional[AssistantThreadContext]: if self.thread_context_loaded is True: return self._thread_context - if self.payload.get("assistant_thread") is not None: + thread = self.payload.get("assistant_thread") + if thread is not None and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started - thread = self.payload["assistant_thread"] - self._thread_context = ( - AssistantThreadContext(thread["context"]) - if thread.get("context", {}).get("channel_id") is not None - else None - ) + self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 07f7ede53..76cdd0ce9 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -186,7 +186,7 @@ def build_payload(event: dict) -> dict: "channel": "D111", "event_ts": "1726133700.887259", "channel_type": "im", - "assistant_thread": {"XXX": "YYY"}, + "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, } ) diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index b131b4e38..4e294eabf 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -200,7 +200,7 @@ def build_payload(event: dict) -> dict: "channel": "D111", "event_ts": "1726133700.887259", "channel_type": "im", - "assistant_thread": {"XXX": "YYY"}, + "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, } ) From 68a0b647188c4cd9b1ad497b015fd3f1dd37f89a Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Mon, 9 Mar 2026 11:31:06 -0400 Subject: [PATCH 2/5] fix: keep old test cases alongside new ones --- tests/scenario_tests/test_events_assistant.py | 24 +++++++++++++++++ .../test_events_assistant.py | 26 ++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 76cdd0ce9..22b9e7b10 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -90,6 +90,11 @@ def handle_user_message(say: Say, set_status: SetStatus, context: BoltContext): assert response.status == 200 assert_target_called() + request = BoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") + response = app.dispatch(request) + assert response.status == 200 + assert_target_called() + request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 @@ -172,6 +177,25 @@ def build_payload(event: dict) -> dict: user_message_event_body_with_assistant_thread = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"XXX": "YYY"}, + } +) + +user_message_event_body_with_action_token = build_payload( { "user": "W222", "type": "message", diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index 4e294eabf..9e4fb2a99 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -104,6 +104,11 @@ async def handle_user_message(say: AsyncSay, set_status: AsyncSetStatus, context assert response.status == 200 await assert_target_called() + request = AsyncBoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") + response = await app.async_dispatch(request) + assert response.status == 200 + await assert_target_called() + request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 @@ -200,11 +205,30 @@ def build_payload(event: dict) -> dict: "channel": "D111", "event_ts": "1726133700.887259", "channel_type": "im", - "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, + "assistant_thread": {"XXX": "YYY"}, } ) +user_message_event_body_with_action_token = build_payload( + { + "user": "W222", + "type": "message", + "ts": "1726133700.887259", + "text": "When Slack was released?", + "team": "T111", + "user_team": "T111", + "source_team": "T222", + "user_profile": {}, + "thread_ts": "1726133698.626339", + "parent_user_id": "W222", + "channel": "D111", + "event_ts": "1726133700.887259", + "channel_type": "im", + "assistant_thread": {"action_token": "10647138185092.960436384805.afce3599"}, + } +) + message_changed_event_body = build_payload( { "type": "message", From d9b011791e7fdad253f3eb999634e038f910a590 Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:18:33 -0400 Subject: [PATCH 3/5] Update slack_bolt/context/get_thread_context/async_get_thread_context.py Co-authored-by: William Bergamin --- .../context/get_thread_context/async_get_thread_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slack_bolt/context/get_thread_context/async_get_thread_context.py b/slack_bolt/context/get_thread_context/async_get_thread_context.py index a9e99bc59..03f7c6076 100644 --- a/slack_bolt/context/get_thread_context/async_get_thread_context.py +++ b/slack_bolt/context/get_thread_context/async_get_thread_context.py @@ -32,7 +32,7 @@ async def __call__(self) -> Optional[AssistantThreadContext]: return self._thread_context thread = self.payload.get("assistant_thread") - if thread is not None and thread.get("context", {}).get("channel_id") is not None: + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed From 1ded1f44f64a7026fdcc4e1ad3d9174210ec8c3c Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:18:42 -0400 Subject: [PATCH 4/5] Update slack_bolt/context/get_thread_context/get_thread_context.py Co-authored-by: William Bergamin --- slack_bolt/context/get_thread_context/get_thread_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slack_bolt/context/get_thread_context/get_thread_context.py b/slack_bolt/context/get_thread_context/get_thread_context.py index 368afebcc..b9c9751e1 100644 --- a/slack_bolt/context/get_thread_context/get_thread_context.py +++ b/slack_bolt/context/get_thread_context/get_thread_context.py @@ -32,7 +32,7 @@ def __call__(self) -> Optional[AssistantThreadContext]: return self._thread_context thread = self.payload.get("assistant_thread") - if thread is not None and thread.get("context", {}).get("channel_id") is not None: + if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed From 99e38ac7c031220aa3cb2a3d1c8d06c9799d4646 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Mon, 23 Mar 2026 13:30:15 -0400 Subject: [PATCH 5/5] fix: replace undefined assert_target_called with listener_called Event pattern in tests --- tests/scenario_tests/test_events_assistant.py | 3 ++- tests/scenario_tests_async/test_events_assistant.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/scenario_tests/test_events_assistant.py b/tests/scenario_tests/test_events_assistant.py index 139ea1ea2..a1c3f1343 100644 --- a/tests/scenario_tests/test_events_assistant.py +++ b/tests/scenario_tests/test_events_assistant.py @@ -136,7 +136,8 @@ def handle_bot_message(): request = BoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") response = app.dispatch(request) assert response.status == 200 - assert_target_called() + assert listener_called.wait(timeout=0.1) is True + listener_called.clear() request = BoltRequest(body=message_changed_event_body, mode="socket_mode") response = app.dispatch(request) diff --git a/tests/scenario_tests_async/test_events_assistant.py b/tests/scenario_tests_async/test_events_assistant.py index c666c897b..9ccd80c11 100644 --- a/tests/scenario_tests_async/test_events_assistant.py +++ b/tests/scenario_tests_async/test_events_assistant.py @@ -160,7 +160,9 @@ async def handle_bot_message(): request = AsyncBoltRequest(body=user_message_event_body_with_action_token, mode="socket_mode") response = await app.async_dispatch(request) assert response.status == 200 - await assert_target_called() + await asyncio.sleep(0.1) + assert listener_called.is_set() + listener_called.clear() request = AsyncBoltRequest(body=message_changed_event_body, mode="socket_mode") response = await app.async_dispatch(request)