Skip to content

Commit 6544f9e

Browse files
committed
Add feedback button handlers and unify message handler
1 parent 236cb99 commit 6544f9e

File tree

5 files changed

+130
-27
lines changed

5 files changed

+130
-27
lines changed

packages/slackBotFunction/app/slack/slack_events.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ def process_async_slack_event(slack_event_data):
3737
raw_text = event["text"]
3838
user_id = event["user"]
3939
channel = event["channel"]
40-
thread_ts = event.get("thread_ts", event["ts"])
41-
4240
# figure out if this is a DM or channel thread
4341
if event.get("channel_type") == "im":
4442
conversation_key = f"dm#{channel}"
4543
context_type = "DM"
44+
thread_ts = event.get("thread_ts", event["ts"])
4645
else:
47-
conversation_key = f"thread#{channel}#{thread_ts}"
46+
thread_root = event.get("thread_ts", event["ts"])
47+
conversation_key = f"thread#{channel}#{thread_root}"
4848
context_type = "thread"
49+
thread_ts = thread_root
4950

5051
# clean up the user's message
5152
user_query = re.sub(r"<@[UW][A-Z0-9]+(\|[^>]+)?>", "", raw_text).strip()
@@ -94,7 +95,7 @@ def process_async_slack_event(slack_event_data):
9495
{"type": "section", "text": {"type": "mrkdwn", "text": response_text}},
9596
{
9697
"type": "section",
97-
"text": {"type": "plain_text", "text": BOT_MESSAGES.get("feedback_prompt", "Was this helpful?")},
98+
"text": {"type": "plain_text", "text": BOT_MESSAGES["feedback_prompt"]},
9899
},
99100
{
100101
"type": "actions",
@@ -109,7 +110,6 @@ def process_async_slack_event(slack_event_data):
109110
{
110111
"type": "button",
111112
"text": {"type": "plain_text", "text": "No"},
112-
"style": "danger",
113113
"action_id": "feedback_no",
114114
"value": feedback_value,
115115
},

packages/slackBotFunction/app/slack/slack_handlers.py

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def app_mention_handler(event, ack, body, client):
112112
try:
113113
client.chat_postMessage(
114114
channel=channel_id,
115-
text=BOT_MESSAGES.get("feedback_thanks", "Thank you for your feedback."),
115+
text=BOT_MESSAGES["feedback_thanks"],
116116
thread_ts=thread_root,
117117
)
118118
except Exception as e:
@@ -124,18 +124,14 @@ def app_mention_handler(event, ack, body, client):
124124
trigger_async_processing({"event": event, "event_id": event_id, "bot_token": bot_token})
125125

126126

127-
def dm_message_handler(event, ack, body, client):
127+
def dm_message_handler(event, event_id, body, client):
128128
"""
129129
Direct messages:
130130
- 'feedback:' prefix -> store as conversation-scoped additional feedback (no model call).
131131
- otherwise -> forward to async processing (Q&A).
132132
"""
133133
if event.get("channel_type") != "im":
134134
return # not a DM; the channel handler will evaluate it
135-
ack()
136-
event_id = _gate_common(event, body)
137-
if not event_id:
138-
return
139135

140136
text = (event.get("text") or "").strip()
141137
channel_id = event["channel"]
@@ -161,7 +157,7 @@ def dm_message_handler(event, ack, body, client):
161157
try:
162158
client.chat_postMessage(
163159
channel=channel_id,
164-
text=BOT_MESSAGES.get("feedback_thanks", "Thank you for your feedback."),
160+
text=BOT_MESSAGES["feedback_thanks"],
165161
thread_ts=thread_root,
166162
)
167163
except Exception as e:
@@ -172,7 +168,7 @@ def dm_message_handler(event, ack, body, client):
172168
trigger_async_processing({"event": event, "event_id": event_id, "bot_token": bot_token})
173169

174170

175-
def channel_message_handler(event, ack, body, client):
171+
def channel_message_handler(event, event_id, body, client):
176172
"""
177173
Channel messages:
178174
- Ignore top-level messages (policy: require @mention to start).
@@ -182,10 +178,6 @@ def channel_message_handler(event, ack, body, client):
182178
"""
183179
if event.get("channel_type") == "im":
184180
return # handled in the DM handler
185-
ack()
186-
event_id = _gate_common(event, body)
187-
if not event_id:
188-
return
189181

190182
text = (event.get("text") or "").strip()
191183
channel_id = event["channel"]
@@ -197,7 +189,9 @@ def channel_message_handler(event, ack, body, client):
197189
try:
198190
resp = table.get_item(Key={"pk": conversation_key, "sk": "session"})
199191
if "Item" not in resp:
192+
logger.info(f"No session found for thread: {conversation_key}")
200193
return # not a bot-owned thread; ignore
194+
logger.info(f"Found session for thread: {conversation_key}")
201195
except Exception as e:
202196
logger.error(f"Error checking thread session: {e}")
203197
return
@@ -222,7 +216,7 @@ def channel_message_handler(event, ack, body, client):
222216
try:
223217
client.chat_postMessage(
224218
channel=channel_id,
225-
text=BOT_MESSAGES.get("feedback_thanks", "Thank you for your feedback."),
219+
text=BOT_MESSAGES["feedback_thanks"],
226220
thread_ts=thread_root,
227221
)
228222
except Exception as e:
@@ -233,6 +227,68 @@ def channel_message_handler(event, ack, body, client):
233227
trigger_async_processing({"event": event, "event_id": event_id, "bot_token": bot_token})
234228

235229

230+
def unified_message_handler(event, ack, body, client):
231+
"""Handle all message events - DMs and channel messages"""
232+
ack()
233+
event_id = _gate_common(event, body)
234+
if not event_id:
235+
return
236+
237+
# Route to appropriate handler based on message type
238+
if event.get("channel_type") == "im":
239+
# DM handling
240+
dm_message_handler(event, event_id, body, client)
241+
else:
242+
# Channel message handling
243+
channel_message_handler(event, event_id, body, client)
244+
245+
246+
def feedback_yes_handler(ack, body, client):
247+
"""Handle positive feedback button clicks."""
248+
ack()
249+
try:
250+
feedback_data = json.loads(body["actions"][0]["value"])
251+
store_feedback(
252+
feedback_data["ck"],
253+
None,
254+
"positive",
255+
body["user"]["id"],
256+
feedback_data["ch"],
257+
feedback_data.get("tt"),
258+
feedback_data.get("mt"),
259+
)
260+
client.chat_postMessage(
261+
channel=feedback_data["ch"],
262+
text=BOT_MESSAGES["feedback_positive_thanks"],
263+
thread_ts=feedback_data.get("tt"),
264+
)
265+
except Exception as e:
266+
logger.error(f"Error handling positive feedback: {e}")
267+
268+
269+
def feedback_no_handler(ack, body, client):
270+
"""Handle negative feedback button clicks."""
271+
ack()
272+
try:
273+
feedback_data = json.loads(body["actions"][0]["value"])
274+
store_feedback(
275+
feedback_data["ck"],
276+
None,
277+
"negative",
278+
body["user"]["id"],
279+
feedback_data["ch"],
280+
feedback_data.get("tt"),
281+
feedback_data.get("mt"),
282+
)
283+
client.chat_postMessage(
284+
channel=feedback_data["ch"],
285+
text=BOT_MESSAGES["feedback_negative_thanks"],
286+
thread_ts=feedback_data.get("tt"),
287+
)
288+
except Exception as e:
289+
logger.error(f"Error handling negative feedback: {e}")
290+
291+
236292
# ================================================================
237293
# Registration (kept minimal to satisfy complexity checkers)
238294
# ================================================================
@@ -241,8 +297,9 @@ def channel_message_handler(event, ack, body, client):
241297
def setup_handlers(app):
242298
"""Register handlers. Intentionally minimal—no branching here."""
243299
app.event("app_mention")(app_mention_handler)
244-
app.event("message")(dm_message_handler)
245-
app.event("message")(channel_message_handler)
300+
app.event("message")(unified_message_handler)
301+
app.action("feedback_yes")(feedback_yes_handler)
302+
app.action("feedback_no")(feedback_no_handler)
246303

247304

248305
# ================================================================

packages/slackBotFunction/tests/test_feedback.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,28 @@ def test_feedback_storage_with_additional_text(mock_store_feedback, mock_env):
5252

5353
def test_feedback_message_empty_text(mock_env):
5454
"""Test that empty feedback doesn't crash"""
55-
# This test just ensures no crash occurs with empty feedback
56-
assert True # Placeholder test
55+
from app.slack.slack_handlers import _gate_common, _strip_mentions, _conversation_key_and_root
56+
from app.slack.slack_events import get_conversation_session
57+
58+
# Test _gate_common helper functions
59+
result = _gate_common({}, {})
60+
assert result is None
61+
62+
result = _gate_common({"bot_id": "B123"}, {"event_id": "evt123"})
63+
assert result is None
64+
65+
# Test strip mentions
66+
result = _strip_mentions("<@U123> hello world")
67+
assert result == "hello world"
68+
69+
# Test conversation key for DM
70+
event = {"channel": "D123", "ts": "456", "channel_type": "im"}
71+
key, root = _conversation_key_and_root(event)
72+
assert key == "dm#D123"
73+
assert root == "456"
74+
75+
# Test error handling
76+
with patch("app.core.config.table") as mock_table:
77+
mock_table.get_item.side_effect = Exception("DB error")
78+
result = get_conversation_session("test-conv")
79+
assert result is None

packages/slackBotFunction/tests/test_slack_handlers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_setup_handlers_registers_correctly(mock_env):
3232
setup_handlers(mock_app)
3333

3434
# Verify all handlers are registered
35-
assert mock_app.event.call_count == 3 # app_mention and 2 message handlers
35+
assert mock_app.event.call_count == 2 # app_mention and unified message handler
3636

3737

3838
def test_app_mention_handler(mock_env):
@@ -199,7 +199,7 @@ def decorator(func):
199199
mock_ack = Mock()
200200
mock_body = {
201201
"user": {"id": "U123"},
202-
"actions": [{"value": "conv-key|test query"}],
202+
"actions": [{"value": '{"ck": "conv-key", "ch": "C123", "tt": "123", "mt": "456"}'}],
203203
"channel": {"id": "C123"},
204204
"message": {"ts": "123"},
205205
}
@@ -209,7 +209,7 @@ def decorator(func):
209209
yes_handler(mock_ack, mock_body, mock_client)
210210

211211
mock_ack.assert_called_once()
212-
mock_store.assert_called_once_with("conv-key", "test query", "positive", "U123")
212+
mock_store.assert_called_once_with("conv-key", None, "positive", "U123", "C123", "123", "456")
213213
mock_client.chat_postMessage.assert_called_once()
214214

215215

@@ -242,7 +242,7 @@ def decorator(func):
242242
mock_ack = Mock()
243243
mock_body = {
244244
"user": {"id": "U123"},
245-
"actions": [{"value": "conv-key|test query"}],
245+
"actions": [{"value": '{"ck": "conv-key", "ch": "C123", "tt": "123", "mt": "456"}'}],
246246
"channel": {"id": "C123"},
247247
"message": {"ts": "123"},
248248
}
@@ -252,7 +252,7 @@ def decorator(func):
252252
no_handler(mock_ack, mock_body, mock_client)
253253

254254
mock_ack.assert_called_once()
255-
mock_store.assert_called_once_with("conv-key", "test query", "negative", "U123")
255+
mock_store.assert_called_once_with("conv-key", None, "negative", "U123", "C123", "123", "456")
256256
mock_client.chat_postMessage.assert_called_once()
257257

258258

packages/slackBotFunction/tests/test_state_management.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,26 @@ def test_trigger_async_processing_error(mock_boto_client, mock_boto_resource, mo
136136

137137
mock_boto_client.assert_called_once_with("lambda")
138138
mock_lambda_client.invoke.assert_called_once()
139+
140+
141+
@patch("slack_bolt.App")
142+
@patch("aws_lambda_powertools.utilities.parameters.get_parameter")
143+
@patch("boto3.resource")
144+
def test_handler_missing_slack_event(mock_boto_resource, mock_get_parameter, mock_app, mock_env):
145+
"""Test handler with missing slack_event in async processing"""
146+
mock_get_parameter.side_effect = [
147+
json.dumps({"token": "test-token"}),
148+
json.dumps({"secret": "test-secret"}),
149+
]
150+
mock_boto_resource.return_value.Table.return_value = Mock()
151+
152+
if "app.handler" in sys.modules:
153+
del sys.modules["app.handler"]
154+
155+
from app.handler import handler
156+
157+
event = {"async_processing": True}
158+
context = Mock()
159+
160+
result = handler(event, context)
161+
assert result["statusCode"] == 400

0 commit comments

Comments
 (0)