Skip to content
This repository was archived by the owner on Sep 3, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/dispatch/conversation/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,14 @@ def get_topic_text(subject: Subject) -> str:
"""Returns the topic details based on subject"""
if isinstance(subject, Incident):
return (
f":helmet_with_white_cross: {subject.commander.individual.name}, {subject.commander.team} | "
f"⛑️ {subject.commander.individual.name}, {subject.commander.team} | "
f"Status: {subject.status} | "
f"Type: {subject.incident_type.name} | "
f"Severity: {subject.incident_severity.name} | "
f"Priority: {subject.incident_priority.name}"
)
return (
f":helmet_with_white_cross: {subject.assignee.individual.name}, {subject.assignee.team} | "
f"⛑️ {subject.assignee.individual.name}, {subject.assignee.team} | "
f"Status: {subject.status} | "
f"Type: {subject.case_type.name} | "
f"Severity: {subject.case_severity.name} | "
Expand Down
53 changes: 34 additions & 19 deletions src/dispatch/messaging/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ class MessageType(DispatchEnum):


INCIDENT_NAME_WITH_ENGAGEMENT = {
"title": ":rotating_light: {{name}} Incident Notification",
"title": "🚨 {{name}} Incident Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
"buttons": [
Expand Down Expand Up @@ -469,7 +469,7 @@ class MessageType(DispatchEnum):
}

INCIDENT_NAME_WITH_ENGAGEMENT_NO_SELF_JOIN = {
"title": ":rotating_light: {{name}} Incident Notification",
"title": "🚨 {{name}} Incident Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
"buttons": [
Expand All @@ -482,13 +482,13 @@ class MessageType(DispatchEnum):
}

CASE_NAME = {
"title": ":briefcase: {{name}} Case Notification",
"title": "💼 {{name}} Case Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
}

CASE_NAME_WITH_ENGAGEMENT = {
"title": ":briefcase: {{name}} Case Notification",
"title": "💼 {{name}} Case Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
"buttons": [
Expand All @@ -514,32 +514,39 @@ class MessageType(DispatchEnum):
}

CASE_NAME_WITH_ENGAGEMENT_NO_SELF_JOIN = {
"title": ":briefcase: {{name}} Case Notification",
"title": "💼 {{name}} Case Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
}


CASE_STATUS_CHANGE = {
"title": "*{% set status_emojis = {'Closed': ':white_check_mark:', 'New': ':new:', 'Triage': ':mag:', 'Stable': ':shield:', 'Escalated': ':arrow_up:'} %}{{ status_emojis.get(case_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ case_status_old }} → {{ case_status_new }}",
"title": "{% set status_emojis = {'Closed': '✅', 'New': '🆕', 'Triage': '🔍', 'Stable': '🛡️', 'Escalated': '⬆️'} %}{{ status_emojis.get(case_status_new, '🔄') }} Status Change",
"text": "{{ case_status_old }} → {{ case_status_new }}",
}

CASE_TYPE_CHANGE = {"title": "*:label: Case Type Change:* {{ case_type_old }} → {{ case_type_new }}"}
CASE_TYPE_CHANGE = {
"title": "🏷️ Case Type Change",
"text": "{{ case_type_old }} → {{ case_type_new }}",
}

CASE_SEVERITY_CHANGE = {
"title": "*{% if case_severity_old.view_order < case_severity_new.view_order %}:arrow_up:{% elif case_severity_old.view_order > case_severity_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Severity Change:* {{ case_severity_old.name }} → {{ case_severity_new.name }}",
"title": "{% if case_severity_old.view_order < case_severity_new.view_order %}⬆️{% elif case_severity_old.view_order > case_severity_new.view_order %}⬇️{% else %}↔️{% endif %} Severity Change",
"text": "{{ case_severity_old.name }} → {{ case_severity_new.name }}",
}

CASE_PRIORITY_CHANGE = {
"title": "*{% if case_priority_old.view_order < case_priority_new.view_order %}:arrow_up:{% elif case_priority_old.view_order > case_priority_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Priority Change:* {{ case_priority_old.name }} → {{ case_priority_new.name }}",
"title": "{% if case_priority_old.view_order < case_priority_new.view_order %}⬆️{% elif case_priority_old.view_order > case_priority_new.view_order %}⬇️{% else %}↔️{% endif %} Priority Change",
"text": "{{ case_priority_old.name }} → {{ case_priority_new.name }}",
}

CASE_VISIBILITY_CHANGE = {
"title": "*{% set visibility_emojis = {'Open': ':unlock:', 'Restricted': ':lock:'} %}{{ visibility_emojis.get(case_visibility_new, ':eye:') }} Visibility Change:* {{ case_visibility_old }} → {{ case_visibility_new }}",
"title": "{% set visibility_emojis = {'Open': '🔓', 'Restricted': '🔒'} %}{{ visibility_emojis.get(case_visibility_new, '👁️') }} Visibility Change",
"text": "{{ case_visibility_old }} → {{ case_visibility_new }}",
}

INCIDENT_NAME = {
"title": ":rotating_light: {{name}} Incident Notification",
"title": "🚨 {{name}} Incident Notification",
"title_link": "{{ticket_weblink}}",
"text": NOTIFICATION_PURPOSES_FYI,
}
Expand All @@ -552,9 +559,9 @@ class MessageType(DispatchEnum):

INCIDENT_SUMMARY = {"title": "Summary", "text": "{{summary}}"}

INCIDENT_TITLE = {"title": "*:memo: Title:* {{title}}"}
INCIDENT_TITLE = {"title": "📝 Title", "text": "{{title}}"}

CASE_TITLE = {"title": "*:memo: Title:* {{title}}"}
CASE_TITLE = {"title": "📝 Title", "text": "{{title}}"}

CASE_STATUS = {
"title": "Status - {{status}}",
Expand Down Expand Up @@ -615,7 +622,8 @@ class MessageType(DispatchEnum):
}

INCIDENT_COMMANDER = {
"title": ":firefighter: Commander: <{{commander_weblink}}|{{commander_fullname}}, {{commander_team}}>",
"title": "🧑‍🚒 Commander - {{commander_fullname}}, {{commander_team}}",
"title_link": "{{commander_weblink}}",
"text": INCIDENT_COMMANDER_DESCRIPTION,
}

Expand Down Expand Up @@ -662,17 +670,23 @@ class MessageType(DispatchEnum):
}

INCIDENT_STATUS_CHANGE = {
"title": "*{% set status_emojis = {'Closed': ':white_check_mark:', 'Stable': ':shield:', 'Active': ':fire:'} %}{{ status_emojis.get(incident_status_new, ':arrows_counterclockwise:') }} Status Change:* {{ incident_status_old }} → {{ incident_status_new }}",
"title": "{% set status_emojis = {'Closed': '✅', 'Stable': '🛡️', 'Active': '🔥'} %}{{ status_emojis.get(incident_status_new, '🔄') }} Status Change",
"text": "{{ incident_status_old }} → {{ incident_status_new }}",
}

INCIDENT_TYPE_CHANGE = {"title": "*:label: Incident Type Change:* {{ incident_type_old }} → {{ incident_type_new }}"}
INCIDENT_TYPE_CHANGE = {
"title": "🏷️ Incident Type Change",
"text": "{{ incident_type_old }} → {{ incident_type_new }}",
}

INCIDENT_SEVERITY_CHANGE = {
"title": "*{% if incident_severity_old.view_order < incident_severity_new.view_order %}:arrow_up:{% elif incident_severity_old.view_order > incident_severity_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Severity Change:* {{ incident_severity_old.name }} → {{ incident_severity_new.name }}",
"title": "{% if incident_severity_old.view_order < incident_severity_new.view_order %}⬆️{% elif incident_severity_old.view_order > incident_severity_new.view_order %}⬇️{% else %}↔️{% endif %} Severity Change",
"text": "{{ incident_severity_old.name }} → {{ incident_severity_new.name }}",
}

INCIDENT_PRIORITY_CHANGE = {
"title": "*{% if incident_priority_old.view_order < incident_priority_new.view_order %}:arrow_up:{% elif incident_priority_old.view_order > incident_priority_new.view_order %}:arrow_down:{% else %}:left_right_arrow:{% endif %} Priority Change:* {{ incident_priority_old.name }} → {{ incident_priority_new.name }}",
"title": "{% if incident_priority_old.view_order < incident_priority_new.view_order %}⬆️{% elif incident_priority_old.view_order > incident_priority_new.view_order %}⬇️{% else %}↔️{% endif %} Priority Change",
"text": "{{ incident_priority_old.name }} → {{ incident_priority_new.name }}",
}

INCIDENT_PARTICIPANT_SUGGESTED_READING_ITEM = {
Expand Down Expand Up @@ -853,7 +867,8 @@ class MessageType(DispatchEnum):
}

CASE_ASSIGNEE = {
"title": ":female-detective: Assignee: <{{assignee_weblink}}|{{assignee_fullname}}, {{assignee_team}}>",
"title": "🕵️‍♀️ Assignee - {{assignee_fullname}}, {{assignee_team}}",
"title_link": "{{assignee_weblink}}",
"text": CASE_ASSIGNEE_DESCRIPTION,
}

Expand Down
22 changes: 15 additions & 7 deletions src/dispatch/plugins/dispatch_slack/messaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,25 @@ def build_unexpected_error_message(guid: str) -> str:
def format_default_text(item: dict):
"""Creates the correct Slack text string based on the item context."""
if item.get("title_link"):
text_part = f"\n{item['text']}" if item.get('text') else ""
return f"*<{item['title_link']}|{item['title']}>*{text_part}"
return f"*<{item['title_link']}|{item['title']}>*\n{item['text']}"
Comment thread
whitdog47 marked this conversation as resolved.
if item.get("datetime"):
return f"*{item['title']}*\n <!date^{int(item['datetime'].timestamp())}^ {{date}} | {item['datetime']}"
if item.get("title"):
text_part = f"\n{item['text']}" if item.get('text') else ""
# Check if title already has formatting (contains asterisks)
if '*' in item['title']:
return f"{item['title']}{text_part}"
# Titles that should be combined on a single line with ": "
single_line_titles = {
"📝 Title",
}

if item.get('text'):
# Check if this title should be on a single line or text contains → (e.g. a state transition)
if item['title'] in single_line_titles or '→' in item['text']:
text_part = f": {item['text']}"
else:
text_part = f"\n{item['text']}"
else:
return f"*{item['title']}*{text_part}"
text_part = ""

return f"*{item['title']}*{text_part}"
return item.get("text", "")


Expand Down
2 changes: 1 addition & 1 deletion src/dispatch/plugins/dispatch_slack/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ def create_genai_message_metadata_blocks(
message = json_to_slack_format(message)

# Truncate the text if it exceeds Block Kit's maximum length of 3000 characters
text = f":magic_wand: *{title}*\n\n{message}"
text = f"🪄 *{title}*\n\n{message}"
text = f"{text[:2997]}..." if len(text) > 3000 else text
blocks.append(
Section(text=text),
Expand Down
30 changes: 15 additions & 15 deletions tests/ai/test_ai_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -152,7 +152,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -194,7 +194,7 @@ def test_generate_read_in_summary_cache_hit(
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -248,7 +248,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand All @@ -275,7 +275,7 @@ def test_generate_read_in_summary_no_ai_plugin(self, session, mock_subject, mock
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -313,7 +313,7 @@ def test_generate_read_in_summary_no_conversation_plugin(
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -361,7 +361,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -412,7 +412,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -442,7 +442,7 @@ def test_generate_read_in_summary_event_query_incident(
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand All @@ -465,7 +465,7 @@ def test_generate_read_in_summary_event_query_case(self, session, mock_subject,
subject=mock_subject,
project=mock_project,
channel_id="test-channel",
important_reaction=":white_check_mark:",
important_reaction="",
participant_email="test@example.com",
)

Expand Down Expand Up @@ -541,7 +541,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
db_session=session,
incident=mock_incident,
project=mock_project,
important_reaction=":fire:",
important_reaction="🔥",
)

# Assertions
Expand Down Expand Up @@ -579,7 +579,7 @@ def test_generate_tactical_report_no_ai_plugin(self, session, mock_incident, moc
db_session=session,
incident=mock_incident,
project=mock_project,
important_reaction=":fire:",
important_reaction="🔥",
)
print(type(result))
assert isinstance(result, TacticalReportResponse)
Expand All @@ -606,7 +606,7 @@ def test_generate_tactical_report_no_conversation_plugin(
db_session=session,
incident=mock_incident,
project=mock_project,
important_reaction=":fire:",
important_reaction="🔥",
)

assert isinstance(result, TacticalReportResponse)
Expand Down Expand Up @@ -642,7 +642,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
db_session=session,
incident=mock_incident,
project=mock_project,
important_reaction=":fire:",
important_reaction="🔥",
)

assert isinstance(result, TacticalReportResponse)
Expand Down Expand Up @@ -681,7 +681,7 @@ def get_plugin_side_effect(db_session, plugin_type, project_id):
db_session=session,
incident=mock_incident,
project=mock_project,
important_reaction=":fire:",
important_reaction="🔥",
)

assert isinstance(result, TacticalReportResponse)
Expand Down
Loading