Skip to content

Commit 5c8e2f2

Browse files
eddygseratch
authored andcommitted
Extract user_id from 'message_changed' event
1 parent 87dccda commit 5c8e2f2

File tree

5 files changed

+470
-9
lines changed

5 files changed

+470
-9
lines changed

slack_bolt/request/internals.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ def extract_is_enterprise_install(payload: Dict[str, Any]) -> Optional[bool]:
5252

5353

5454
def extract_enterprise_id(payload: Dict[str, Any]) -> Optional[str]:
55-
if payload.get("enterprise") is not None:
56-
org = payload.get("enterprise")
55+
org = payload.get("enterprise")
56+
if org is not None:
5757
if isinstance(org, str):
5858
return org
5959
elif "id" in org:
60-
return org.get("id") # type: ignore
60+
return org.get("id")
6161
if payload.get("authorizations") is not None and len(payload["authorizations"]) > 0:
6262
# To make Events API handling functioning also for shared channels,
6363
# we should use .authorizations[0].enterprise_id over .enterprise_id
@@ -103,26 +103,32 @@ def extract_team_id(payload: Dict[str, Any]) -> Optional[str]:
103103

104104

105105
def extract_user_id(payload: Dict[str, Any]) -> Optional[str]:
106-
if payload.get("user") is not None:
107-
user = payload.get("user")
106+
user = payload.get("user")
107+
if user is not None:
108108
if isinstance(user, str):
109109
return user
110110
elif "id" in user:
111-
return user.get("id") # type: ignore
111+
return user.get("id")
112112
if "user_id" in payload:
113113
return payload.get("user_id")
114114
if payload.get("event") is not None:
115115
return extract_user_id(payload["event"])
116+
if payload.get("message") is not None:
117+
# message_changed: body["event"]["message"]
118+
return extract_user_id(payload["message"])
119+
if payload.get("previous_message") is not None:
120+
# message_deleted: body["event"]["previous_message"]
121+
return extract_user_id(payload["previous_message"])
116122
return None
117123

118124

119125
def extract_channel_id(payload: Dict[str, Any]) -> Optional[str]:
120-
if payload.get("channel") is not None:
121-
channel = payload.get("channel")
126+
channel = payload.get("channel")
127+
if channel is not None:
122128
if isinstance(channel, str):
123129
return channel
124130
elif "id" in channel:
125-
return channel.get("id") # type: ignore
131+
return channel.get("id")
126132
if "channel_id" in payload:
127133
return payload.get("channel_id")
128134
if payload.get("event") is not None:
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import json
2+
import time
3+
4+
from slack_sdk.signature import SignatureVerifier
5+
from slack_sdk.web import WebClient
6+
7+
from slack_bolt.app import App
8+
from slack_bolt.request import BoltRequest
9+
from tests.mock_web_api_server import (
10+
setup_mock_web_api_server,
11+
cleanup_mock_web_api_server,
12+
)
13+
from tests.utils import remove_os_env_temporarily, restore_os_env
14+
15+
16+
class TestMessageChanged:
17+
signing_secret = "secret"
18+
valid_token = "xoxb-valid"
19+
mock_api_server_base_url = "http://localhost:8888"
20+
signature_verifier = SignatureVerifier(signing_secret)
21+
web_client = WebClient(
22+
token=valid_token,
23+
base_url=mock_api_server_base_url,
24+
)
25+
26+
def setup_method(self):
27+
self.old_os_env = remove_os_env_temporarily()
28+
setup_mock_web_api_server(self)
29+
30+
def teardown_method(self):
31+
cleanup_mock_web_api_server(self)
32+
restore_os_env(self.old_os_env)
33+
34+
def generate_signature(self, body: str, timestamp: str):
35+
return self.signature_verifier.generate_signature(
36+
body=body,
37+
timestamp=timestamp,
38+
)
39+
40+
def build_headers(self, timestamp: str, body: str):
41+
return {
42+
"content-type": ["application/json"],
43+
"x-slack-signature": [self.generate_signature(body, timestamp)],
44+
"x-slack-request-timestamp": [timestamp],
45+
}
46+
47+
def build_request(self, event_payload: dict) -> BoltRequest:
48+
timestamp, body = str(int(time.time())), json.dumps(event_payload)
49+
return BoltRequest(body=body, headers=self.build_headers(timestamp, body))
50+
51+
def test_user_and_channel_id_in_context(self):
52+
app = App(client=self.web_client, signing_secret=self.signing_secret, process_before_response=True)
53+
54+
@app.event({"type": "message", "subtype": "message_changed"})
55+
def handle_message_changed(context):
56+
assert context.channel_id == "C111"
57+
assert context.user_id == "U111"
58+
59+
request = self.build_request(event_payload)
60+
response = app.dispatch(request)
61+
assert response.status == 200
62+
63+
64+
event_payload = {
65+
"token": "verification-token",
66+
"team_id": "T111",
67+
"enterprise_id": "E111",
68+
"api_app_id": "A111",
69+
"event": {
70+
"type": "message",
71+
"subtype": "message_changed",
72+
"message": {
73+
"type": "message",
74+
"text": "updated message",
75+
"user": "U111",
76+
"team": "T111",
77+
"edited": {"user": "U111", "ts": "1665102362.000000"},
78+
"blocks": [
79+
{
80+
"type": "rich_text",
81+
"block_id": "xwvU3",
82+
"elements": [{"type": "rich_text_section", "elements": [{"type": "text", "text": "updated message"}]}],
83+
}
84+
],
85+
},
86+
"previous_message": {
87+
"type": "message",
88+
"text": "original message",
89+
"user": "U222",
90+
"team": "T111",
91+
"ts": "1665102338.901939",
92+
"blocks": [
93+
{
94+
"type": "rich_text",
95+
"block_id": "URf",
96+
"elements": [{"type": "rich_text_section", "elements": [{"type": "text", "text": "original message"}]}],
97+
}
98+
],
99+
},
100+
"channel": "C111",
101+
"hidden": True,
102+
"ts": "1665102362.013600",
103+
"event_ts": "1665102362.013600",
104+
"channel_type": "channel",
105+
},
106+
"type": "event_callback",
107+
"event_id": "Ev111",
108+
"event_time": 1665102362,
109+
"authorizations": [
110+
{
111+
"enterprise_id": "E111",
112+
"team_id": "T111",
113+
"user_id": "U111",
114+
"is_bot": True,
115+
"is_enterprise_install": False,
116+
}
117+
],
118+
"is_ext_shared_channel": False,
119+
"event_context": "1-message-T111-C111",
120+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import json
2+
import time
3+
4+
from slack_sdk.signature import SignatureVerifier
5+
from slack_sdk.web import WebClient
6+
7+
from slack_bolt.app import App
8+
from slack_bolt.request import BoltRequest
9+
from tests.mock_web_api_server import (
10+
setup_mock_web_api_server,
11+
cleanup_mock_web_api_server,
12+
)
13+
from tests.utils import remove_os_env_temporarily, restore_os_env
14+
15+
16+
class TestMessageDeleted:
17+
signing_secret = "secret"
18+
valid_token = "xoxb-valid"
19+
mock_api_server_base_url = "http://localhost:8888"
20+
signature_verifier = SignatureVerifier(signing_secret)
21+
web_client = WebClient(
22+
token=valid_token,
23+
base_url=mock_api_server_base_url,
24+
)
25+
26+
def setup_method(self):
27+
self.old_os_env = remove_os_env_temporarily()
28+
setup_mock_web_api_server(self)
29+
30+
def teardown_method(self):
31+
cleanup_mock_web_api_server(self)
32+
restore_os_env(self.old_os_env)
33+
34+
def generate_signature(self, body: str, timestamp: str):
35+
return self.signature_verifier.generate_signature(
36+
body=body,
37+
timestamp=timestamp,
38+
)
39+
40+
def build_headers(self, timestamp: str, body: str):
41+
return {
42+
"content-type": ["application/json"],
43+
"x-slack-signature": [self.generate_signature(body, timestamp)],
44+
"x-slack-request-timestamp": [timestamp],
45+
}
46+
47+
def build_request(self, event_payload: dict) -> BoltRequest:
48+
timestamp, body = str(int(time.time())), json.dumps(event_payload)
49+
return BoltRequest(body=body, headers=self.build_headers(timestamp, body))
50+
51+
def test_user_and_channel_id_in_context(self):
52+
app = App(client=self.web_client, signing_secret=self.signing_secret, process_before_response=True)
53+
54+
@app.event({"type": "message", "subtype": "message_deleted"})
55+
def handle_message_deleted(context):
56+
assert context.channel_id == "C111"
57+
assert context.user_id == "U111"
58+
59+
request = self.build_request(event_payload)
60+
response = app.dispatch(request)
61+
assert response.status == 200
62+
63+
64+
event_payload = {
65+
"token": "verification-token",
66+
"team_id": "T111",
67+
"enterprise_id": "E111",
68+
"api_app_id": "A111",
69+
"event": {
70+
"type": "message",
71+
"subtype": "message_deleted",
72+
"previous_message": {
73+
"type": "message",
74+
"text": "Delete this message",
75+
"user": "U111",
76+
"team": "T111",
77+
"ts": "1665368619.804829",
78+
},
79+
"channel": "C111",
80+
"hidden": True,
81+
"deleted_ts": "1665368619.804829",
82+
"event_ts": "1665368629.007100",
83+
"ts": "1665368629.007100",
84+
"channel_type": "channel",
85+
},
86+
"type": "event_callback",
87+
"event_id": "Ev111",
88+
"event_time": 1665368629,
89+
"authorizations": [
90+
{
91+
"enterprise_id": "E111",
92+
"team_id": "T111",
93+
"user_id": "U111",
94+
"is_bot": True,
95+
"is_enterprise_install": False,
96+
}
97+
],
98+
"is_ext_shared_channel": False,
99+
"event_context": "1-message-T111-C111",
100+
}

0 commit comments

Comments
 (0)