Skip to content

Commit 6d3c554

Browse files
committed
React to messages in thread where bot is participating
1 parent d9ab87a commit 6d3c554

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

src/redis_release/github_client_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ async def trigger_workflow(
274274

275275
async def identify_workflow(
276276
self, repo: str, workflow_file: str, workflow_uuid: str
277-
) -> WorkflowRun | None:
277+
) -> Optional[WorkflowRun]:
278278

279279
logger.debug(
280280
f"[blue]Searching for workflow run with UUID:[/blue] [cyan]{workflow_uuid}[/cyan]"

src/redis_release/slack_bot.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,16 @@ def start_conversation(
135135
def _register_handlers(self) -> None:
136136
"""Register Slack event handlers."""
137137

138-
@self.app.event("app_mention")
139-
async def handle_app_mention( # pyright: ignore[reportUnusedFunction]
140-
event: Dict[str, Any], say: AsyncSay, logger: logging.Logger
138+
async def process_message(
139+
event: Dict[str, Any], logger: logging.Logger, is_mention: bool = False
141140
) -> None:
142-
"""Handle app mentions by processing through conversation tree."""
141+
"""Common message processing logic for both mentions and thread replies.
142+
143+
Args:
144+
event: Slack event data
145+
logger: Logger instance
146+
is_mention: Whether this is an explicit mention (True) or thread reply (False)
147+
"""
143148
try:
144149
text = event.get("text", "")
145150
channel = event.get("channel")
@@ -162,7 +167,7 @@ async def handle_app_mention( # pyright: ignore[reportUnusedFunction]
162167
assert isinstance(thread_ts, str)
163168

164169
logger.info(
165-
f"Received mention from user {user} in channel {channel}: {text}"
170+
f"Received {'mention' if is_mention else 'thread message'} from user {user} in channel {channel}: {text}"
166171
)
167172

168173
# Get slack thread messages
@@ -187,7 +192,7 @@ async def handle_app_mention( # pyright: ignore[reportUnusedFunction]
187192
self.start_conversation(args)
188193

189194
except Exception as e:
190-
logger.error(f"Error handling app mention: {e}", exc_info=True)
195+
logger.error(f"Error handling message: {e}", exc_info=True)
191196
channel = event.get("channel")
192197
if channel:
193198
await self._send_reply(
@@ -196,6 +201,57 @@ async def handle_app_mention( # pyright: ignore[reportUnusedFunction]
196201
f"Sorry, I encountered an error: {str(e)}",
197202
)
198203

204+
@self.app.event("app_mention")
205+
async def handle_app_mention( # pyright: ignore[reportUnusedFunction]
206+
event: Dict[str, Any], say: AsyncSay, logger: logging.Logger
207+
) -> None:
208+
"""Handle app mentions by processing through conversation tree."""
209+
await process_message(event, logger, is_mention=True)
210+
211+
@self.app.event("message")
212+
async def handle_message( # pyright: ignore[reportUnusedFunction]
213+
event: Dict[str, Any], logger: logging.Logger
214+
) -> None:
215+
"""Handle messages in threads where bot is participating."""
216+
logger.debug(f"Received message event: {event}")
217+
218+
# Ignore messages that are not in threads
219+
if "thread_ts" not in event:
220+
logger.debug("Ignoring non-thread message")
221+
return
222+
223+
# Ignore bot's own messages
224+
if event.get("bot_id"):
225+
logger.debug("Ignoring bot's own message")
226+
return
227+
228+
# Ignore subtypes (like message_changed, message_deleted, etc.)
229+
if event.get("subtype"):
230+
logger.debug(f"Ignoring message with subtype: {event.get('subtype')}")
231+
return
232+
233+
channel = event.get("channel")
234+
thread_ts = event.get("thread_ts")
235+
236+
if not channel or not thread_ts:
237+
logger.debug("Missing channel or thread_ts")
238+
return
239+
240+
logger.debug(f"Checking if bot is participating in thread {thread_ts}")
241+
242+
# Check if bot has participated in this thread
243+
is_participating = await self._is_bot_in_thread(channel, thread_ts)
244+
245+
logger.debug(f"Bot participating in thread: {is_participating}")
246+
247+
if is_participating:
248+
logger.info(
249+
f"Processing thread message in channel {channel}, thread {thread_ts}"
250+
)
251+
await process_message(event, logger, is_mention=False)
252+
else:
253+
logger.debug("Bot not participating in this thread, ignoring message")
254+
199255
async def _get_thread_messages(self, channel: str, thread_ts: str) -> List[str]:
200256
"""Get all messages from a thread.
201257
@@ -225,6 +281,54 @@ async def _get_thread_messages(self, channel: str, thread_ts: str) -> List[str]:
225281
logger.error(f"Error getting thread messages: {e}", exc_info=True)
226282
return []
227283

284+
async def _is_bot_in_thread(self, channel: str, thread_ts: str) -> bool:
285+
"""Check if the bot has participated in a thread.
286+
287+
Args:
288+
channel: Slack channel ID
289+
thread_ts: Thread timestamp
290+
291+
Returns:
292+
True if bot has sent messages in the thread, False otherwise
293+
"""
294+
try:
295+
# Get bot's user ID
296+
auth_result = await self.app.client.auth_test()
297+
bot_user_id = auth_result.get("user_id")
298+
299+
if not bot_user_id:
300+
logger.warning("Could not get bot user ID")
301+
return False
302+
303+
logger.debug(f"Bot user ID: {bot_user_id}")
304+
305+
# Get thread messages
306+
result = await self.app.client.conversations_replies(
307+
channel=channel, ts=thread_ts
308+
)
309+
310+
messages = result.get("messages", [])
311+
logger.debug(f"Found {len(messages)} messages in thread {thread_ts}")
312+
313+
# Check if any message is from the bot
314+
for msg in messages:
315+
msg_user = msg.get("user")
316+
msg_bot_id = msg.get("bot_id")
317+
logger.debug(f"Message from user={msg_user}, bot_id={msg_bot_id}")
318+
319+
if msg_user == bot_user_id or msg_bot_id:
320+
logger.debug(f"Found bot message in thread")
321+
return True
322+
323+
logger.debug(f"No bot messages found in thread")
324+
return False
325+
326+
except Exception as e:
327+
logger.error(
328+
f"Error checking bot participation in thread: {e}", exc_info=True
329+
)
330+
return False
331+
228332
def create_queue_listener(
229333
self,
230334
queue: janus.Queue[str],

0 commit comments

Comments
 (0)