Skip to content

Commit bf27882

Browse files
authored
Fix #842 Cannot pass thread_ts to respond() utility (#844)
1 parent 2452a92 commit bf27882

File tree

6 files changed

+361
-1
lines changed

6 files changed

+361
-1
lines changed

slack_bolt/context/respond/async_respond.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ async def __call__(
3434
delete_original: Optional[bool] = None,
3535
unfurl_links: Optional[bool] = None,
3636
unfurl_media: Optional[bool] = None,
37+
thread_ts: Optional[str] = None,
3738
) -> WebhookResponse:
3839
if self.response_url is not None:
3940
client = AsyncWebhookClient(
@@ -52,6 +53,7 @@ async def __call__(
5253
delete_original=delete_original,
5354
unfurl_links=unfurl_links,
5455
unfurl_media=unfurl_media,
56+
thread_ts=thread_ts,
5557
)
5658
return await client.send_dict(message)
5759
elif isinstance(text_or_whole_response, dict):

slack_bolt/context/respond/internals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def _build_message(
1515
delete_original: Optional[bool] = None,
1616
unfurl_links: Optional[bool] = None,
1717
unfurl_media: Optional[bool] = None,
18+
thread_ts: Optional[str] = None,
1819
) -> Dict[str, Any]:
1920
message = {"text": text}
2021
if blocks is not None and len(blocks) > 0:
@@ -31,4 +32,6 @@ def _build_message(
3132
message["unfurl_links"] = unfurl_links
3233
if unfurl_media is not None:
3334
message["unfurl_media"] = unfurl_media
35+
if thread_ts is not None:
36+
message["thread_ts"] = thread_ts
3437
return message

slack_bolt/context/respond/respond.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __call__(
3434
delete_original: Optional[bool] = None,
3535
unfurl_links: Optional[bool] = None,
3636
unfurl_media: Optional[bool] = None,
37+
thread_ts: Optional[str] = None,
3738
) -> WebhookResponse:
3839
if self.response_url is not None:
3940
client = WebhookClient(
@@ -53,6 +54,7 @@ def __call__(
5354
delete_original=delete_original,
5455
unfurl_links=unfurl_links,
5556
unfurl_media=unfurl_media,
57+
thread_ts=thread_ts,
5658
)
5759
return client.send_dict(message)
5860
elif isinstance(text_or_whole_response, dict):
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from slack_sdk import WebClient
2+
3+
from slack_bolt import BoltRequest, App, Say, Respond, Ack
4+
from tests.mock_web_api_server import (
5+
setup_mock_web_api_server,
6+
cleanup_mock_web_api_server,
7+
)
8+
from tests.utils import remove_os_env_temporarily, restore_os_env
9+
10+
11+
class TestBlockActionsRespond:
12+
signing_secret = "secret"
13+
valid_token = "xoxb-valid"
14+
mock_api_server_base_url = "http://localhost:8888"
15+
web_client = WebClient(
16+
token=valid_token,
17+
base_url=mock_api_server_base_url,
18+
)
19+
20+
def setup_method(self):
21+
self.old_os_env = remove_os_env_temporarily()
22+
setup_mock_web_api_server(self)
23+
24+
def teardown_method(self):
25+
cleanup_mock_web_api_server(self)
26+
restore_os_env(self.old_os_env)
27+
28+
def test_mock_server_is_running(self):
29+
resp = self.web_client.api_test()
30+
assert resp is not None
31+
32+
def test_success(self):
33+
app = App(client=self.web_client)
34+
35+
@app.event("app_mention")
36+
def handle_app_mention_events(say: Say):
37+
say(
38+
text="This is a section block with a button.",
39+
blocks=[
40+
{
41+
"type": "section",
42+
"text": {
43+
"type": "mrkdwn",
44+
"text": "This is a section block with a button.",
45+
},
46+
"accessory": {
47+
"type": "button",
48+
"text": {"type": "plain_text", "text": "Click Me"},
49+
"value": "clicked",
50+
"action_id": "button",
51+
},
52+
}
53+
],
54+
)
55+
56+
@app.action("button")
57+
def handle_button_clicks(body: dict, ack: Ack, respond: Respond):
58+
respond(
59+
text="hey!",
60+
thread_ts=body["message"]["ts"],
61+
response_type="in_channel",
62+
replace_original=False,
63+
)
64+
ack()
65+
66+
# app_mention event
67+
request = BoltRequest(
68+
mode="socket_mode",
69+
body={
70+
"team_id": "T0G9PQBBK",
71+
"api_app_id": "A111",
72+
"event": {
73+
"type": "app_mention",
74+
"text": "<@U111> hey",
75+
"user": "U222",
76+
"ts": "1678252212.229129",
77+
"blocks": [
78+
{
79+
"type": "rich_text",
80+
"block_id": "BCCO",
81+
"elements": [
82+
{
83+
"type": "rich_text_section",
84+
"elements": [
85+
{"type": "user", "user_id": "U111"},
86+
{"type": "text", "text": " hey"},
87+
],
88+
}
89+
],
90+
}
91+
],
92+
"team": "T0G9PQBBK",
93+
"channel": "C111",
94+
"event_ts": "1678252212.229129",
95+
},
96+
"type": "event_callback",
97+
"event_id": "Ev04SPP46R6J",
98+
"event_time": 1678252212,
99+
"authorizations": [
100+
{
101+
"enterprise_id": None,
102+
"team_id": "T0G9PQBBK",
103+
"user_id": "U111",
104+
"is_bot": True,
105+
"is_enterprise_install": False,
106+
}
107+
],
108+
"is_ext_shared_channel": False,
109+
"event_context": "4-xxx",
110+
},
111+
)
112+
response = app.dispatch(request)
113+
assert response.status == 200
114+
115+
# block_actions request
116+
request = BoltRequest(
117+
mode="socket_mode",
118+
body={
119+
"type": "block_actions",
120+
"user": {"id": "U111"},
121+
"api_app_id": "A111",
122+
"container": {
123+
"type": "message",
124+
"message_ts": "1678252213.679169",
125+
"channel_id": "C111",
126+
"is_ephemeral": False,
127+
},
128+
"trigger_id": "4916855695380.xxx.yyy",
129+
"team": {"id": "T0G9PQBBK"},
130+
"enterprise": None,
131+
"is_enterprise_install": False,
132+
"channel": {"id": "C111"},
133+
"message": {
134+
"bot_id": "B111",
135+
"type": "message",
136+
"text": "This is a section block with a button.",
137+
"user": "U222",
138+
"ts": "1678252213.679169",
139+
"app_id": "A111",
140+
"blocks": [
141+
{
142+
"type": "section",
143+
"block_id": "8KR",
144+
"text": {
145+
"type": "mrkdwn",
146+
"text": "This is a section block with a button.",
147+
"verbatim": False,
148+
},
149+
"accessory": {
150+
"type": "button",
151+
"action_id": "button",
152+
"text": {"type": "plain_text", "text": "Click Me"},
153+
"value": "clicked",
154+
},
155+
}
156+
],
157+
"team": "T0G9PQBBK",
158+
},
159+
"state": {"values": {}},
160+
"response_url": "http://localhost:8888/webhook",
161+
"actions": [
162+
{
163+
"action_id": "button",
164+
"block_id": "8KR",
165+
"text": {"type": "plain_text", "text": "Click Me"},
166+
"value": "clicked",
167+
"type": "button",
168+
"action_ts": "1678252216.469172",
169+
}
170+
],
171+
},
172+
)
173+
response = app.dispatch(request)
174+
assert response.status == 200

0 commit comments

Comments
 (0)