diff --git a/src/dispatch/ai/service.py b/src/dispatch/ai/service.py index 8ca232c26498..25622b706db1 100644 --- a/src/dispatch/ai/service.py +++ b/src/dispatch/ai/service.py @@ -636,7 +636,7 @@ def generate_read_in_summary( return ReadInSummaryResponse(error_message=message) conversation = conversation_plugin.instance.get_conversation( - conversation_id=channel_id, important_reaction=important_reaction + conversation_id=channel_id, include_user_details=True, important_reaction=important_reaction ) if not conversation: message = f"Read-in summary not generated for {subject.name}. No conversation found." @@ -739,7 +739,7 @@ def generate_tactical_report( return TacticalReportResponse(error_message=message) conversation = conversation_plugin.instance.get_conversation( - conversation_id=incident.conversation.channel_id, important_reaction=important_reaction + conversation_id=incident.conversation.channel_id, include_user_details=True, important_reaction=important_reaction ) if not conversation: message = f"Tactical report not generated for {incident.name}. No conversation found." diff --git a/src/dispatch/plugins/dispatch_slack/plugin.py b/src/dispatch/plugins/dispatch_slack/plugin.py index 906fb5b6bf09..8b47fe63d15d 100644 --- a/src/dispatch/plugins/dispatch_slack/plugin.py +++ b/src/dispatch/plugins/dispatch_slack/plugin.py @@ -450,7 +450,7 @@ def fetch_events( raise def get_conversation( - self, conversation_id: str, oldest: str = "0", important_reaction: str | None = None + self, conversation_id: str, oldest: str = "0", include_user_details = False, important_reaction: str | None = None ) -> list: """ Fetches the top-level posts from a Slack conversation. @@ -458,6 +458,8 @@ def get_conversation( Args: conversation_id (str): The ID of the Slack conversation. oldest (str): The oldest timestamp to fetch messages from. + include_user_details (bool): Whether to resolve user name and email information. + important_reaction (str): Emoji reaction indicating important messages. Returns: list: A list of tuples containing the timestamp and user ID of each message. @@ -468,6 +470,7 @@ def get_conversation( conversation_id, oldest, include_message_text=True, + include_user_details=include_user_details, important_reaction=important_reaction, ) diff --git a/src/dispatch/plugins/dispatch_slack/service.py b/src/dispatch/plugins/dispatch_slack/service.py index 41fb529ee104..b028d92fbfd7 100644 --- a/src/dispatch/plugins/dispatch_slack/service.py +++ b/src/dispatch/plugins/dispatch_slack/service.py @@ -1,6 +1,7 @@ import functools import heapq import logging +import re from datetime import datetime from blockkit.surfaces import Block @@ -610,16 +611,39 @@ def get_channel_activity( conversation_id: str, oldest: str = "0", include_message_text: bool = False, + include_user_details: bool = False, important_reaction: str | None = None, ) -> list: """Gets all top-level messages for a given Slack channel. + Args: + client (WebClient): Slack client responsible for API calls + conversation_id (str): Channel ID to reference + oldest (int): Oldest timestamp to fetch messages from + include_message_text (bool): Include message text (in addition to datetime and user id) + include_user_details (bool): Include user name and email information + important_reaction (str): Optional emoji reaction designating important messages + Returns: A sorted list of tuples (utc_dt, user_id) of each message in the channel, or (utc_dt, user_id, message_text), depending on include_message_text. """ result = [] cursor = None + + def mention_resolver(user_match): + """ + Helper function to extract user informations from @ mentions in messages. + """ + user_id = user_match.group(1) + try: + user_info = get_user_info_by_id(client, user_id) + return user_info.get('real_name', f"{user_id} (name not found)") + except SlackApiError as e: + log.warning(f"Error resolving mentioned Slack user: {e}") + # fall back on id + return user_id + while True: response = make_call( client, @@ -640,13 +664,28 @@ def get_channel_activity( if "user" in message: user_id = resolve_user(client, message["user"])["id"] utc_dt = datetime.utcfromtimestamp(float(message["ts"])) + + message_result = [utc_dt, user_id] + if include_message_text: message_text = message.get("text", "") if has_important_reaction(message, important_reaction): message_text = f"IMPORTANT!: {message_text}" - heapq.heappush(result, (utc_dt, user_id, message_text)) - else: - heapq.heappush(result, (utc_dt, user_id)) + + if include_user_details: # attempt to resolve mentioned users + message_text = re.sub(r'<@(\w+)>', mention_resolver, message_text) + + message_result.append(message_text) + + if include_user_details: + user_details = get_user_info_by_id(client, user_id) + user_name = user_details.get('real_name', "Name not found") + user_profile = user_details.get('profile', {}) + user_display_name = user_profile.get('display_name_normalized', "DisplayName not found") + user_email = user_profile.get('email', "Email not found") + message_result.extend([user_name, user_display_name, user_email]) + + heapq.heappush(result, tuple(message_result)) if not response["has_more"]: break