Skip to content

Commit 1e1c167

Browse files
committed
feat: attach ts to listener context
1 parent 956f5f5 commit 1e1c167

File tree

5 files changed

+51
-5
lines changed

5 files changed

+51
-5
lines changed

slack_bolt/context/base_context.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class BaseContext(dict):
1818
"actor_team_id",
1919
"actor_user_id",
2020
"channel_id",
21+
"ts",
2122
"thread_ts",
2223
"response_url",
2324
"matches",
@@ -110,6 +111,11 @@ def channel_id(self) -> Optional[str]:
110111
"""The conversation ID associated with this request."""
111112
return self.get("channel_id")
112113

114+
@property
115+
def ts(self) -> Optional[str]:
116+
"""The message timestamp associated with this request."""
117+
return self.get("ts")
118+
113119
@property
114120
def thread_ts(self) -> Optional[str]:
115121
"""The conversation thread's ID associated with this request."""

slack_bolt/kwargs_injection/async_utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,18 @@ def build_async_required_kwargs(
8989
if "agent" in required_arg_names:
9090
from slack_bolt.agent.async_agent import AsyncBoltAgent
9191

92+
# For thread_ts, we check multiple sources:
93+
# 1. context.thread_ts - populated for assistant events
94+
# 2. event.thread_ts - for non-assistant events in a thread (e.g., app_mention in thread)
95+
# 3. context.ts - fallback to the message timestamp
96+
# We read from event directly to avoid changing context.thread_ts which would affect say() behavior
97+
event = request.body.get("event", {})
98+
thread_ts = request.context.thread_ts or event.get("thread_ts") or request.context.ts
99+
92100
all_available_args["agent"] = AsyncBoltAgent(
93101
client=request.context.client,
94102
channel_id=request.context.channel_id,
95-
thread_ts=request.context.thread_ts,
103+
thread_ts=thread_ts,
96104
team_id=request.context.team_id,
97105
user_id=request.context.user_id,
98106
)

slack_bolt/kwargs_injection/utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,18 @@ def build_required_kwargs(
8888
if "agent" in required_arg_names:
8989
from slack_bolt.agent.agent import BoltAgent
9090

91+
# For thread_ts, we check multiple sources:
92+
# 1. context.thread_ts - populated for assistant events
93+
# 2. event.thread_ts - for non-assistant events in a thread (e.g., app_mention in thread)
94+
# 3. context.ts - fallback to the message timestamp
95+
# We read from event directly to avoid changing context.thread_ts which would affect say() behavior
96+
event = request.body.get("event", {})
97+
thread_ts = request.context.thread_ts or event.get("thread_ts") or request.context.ts
98+
9199
all_available_args["agent"] = BoltAgent(
92100
client=request.context.client,
93101
channel_id=request.context.channel_id,
94-
thread_ts=request.context.thread_ts,
102+
thread_ts=thread_ts,
95103
team_id=request.context.team_id,
96104
user_id=request.context.user_id,
97105
)

slack_bolt/request/async_internals.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
extract_actor_team_id,
1616
extract_actor_user_id,
1717
extract_thread_ts,
18+
extract_ts,
1819
)
1920

2021

@@ -45,6 +46,9 @@ def build_async_context(
4546
channel_id = extract_channel_id(body)
4647
if channel_id:
4748
context["channel_id"] = channel_id
49+
ts = extract_ts(body)
50+
if ts:
51+
context["ts"] = ts
4852
thread_ts = extract_thread_ts(body)
4953
if thread_ts:
5054
context["thread_ts"] = thread_ts

slack_bolt/request/internals.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,14 @@ def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]:
215215

216216

217217
def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]:
218-
# This utility initially supports only the use cases for AI assistants, but it may be fine to add more patterns.
219-
# That said, note that thread_ts is always required for assistant threads, but it's not for channels.
220-
# Thus, blindly setting this thread_ts to say utility can break existing apps' behaviors.
218+
# This utility only extracts thread_ts for assistant events to avoid breaking existing say() behavior.
219+
# For non-assistant events, thread_ts is intentionally NOT extracted into context because:
220+
# - say() uses context.thread_ts to decide where to post messages
221+
# - Existing apps may expect say() to post to the channel, not the thread
222+
# - Changing this would be a breaking change for existing apps
223+
#
224+
# The BoltAgent class handles non-assistant thread_ts separately by reading from the event directly,
225+
# allowing it to work correctly without affecting say() behavior.
221226
if is_assistant_event(payload):
222227
event = payload["event"]
223228
if (
@@ -242,6 +247,18 @@ def extract_thread_ts(payload: Dict[str, Any]) -> Optional[str]:
242247
return None
243248

244249

250+
def extract_ts(payload: Dict[str, Any]) -> Optional[str]:
251+
"""Extract the message timestamp from an event payload."""
252+
event = payload.get("event", {})
253+
# Direct ts on the event (e.g., app_mention, message)
254+
if event.get("ts") is not None:
255+
return event["ts"]
256+
# message_changed events have ts in the message
257+
if event.get("message", {}).get("ts") is not None:
258+
return event["message"]["ts"]
259+
return None
260+
261+
245262
def extract_function_execution_id(payload: Dict[str, Any]) -> Optional[str]:
246263
if payload.get("function_execution_id") is not None:
247264
return payload.get("function_execution_id")
@@ -292,6 +309,9 @@ def build_context(context: BoltContext, body: Dict[str, Any]) -> BoltContext:
292309
channel_id = extract_channel_id(body)
293310
if channel_id:
294311
context["channel_id"] = channel_id
312+
ts = extract_ts(body)
313+
if ts:
314+
context["ts"] = ts
295315
thread_ts = extract_thread_ts(body)
296316
if thread_ts:
297317
context["thread_ts"] = thread_ts

0 commit comments

Comments
 (0)