From 0ae0bec1711fcd32cc57b25fefb4aa460364a421 Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Fri, 15 Nov 2019 11:25:40 +0000 Subject: [PATCH 1/4] Better summary and impact updates Prior to this change, the summary and impact commands would overwrite whatever was currently set for the incident. In most cases, our understanding of these develops over time, so we'd like to be update and refine what we've previously set. This change introduces actions and dialogs to update the summary and impact from what was previously set, making it easier for us to develop meaningful and thorough statements over time. --- response/slack/incident_commands/__init__.py | 5 + response/slack/incident_commands/impact.py | 104 ++++++++++++++++++ .../incident_commands.py | 16 --- response/slack/incident_commands/summary.py | 104 ++++++++++++++++++ 4 files changed, 213 insertions(+), 16 deletions(-) create mode 100644 response/slack/incident_commands/__init__.py create mode 100644 response/slack/incident_commands/impact.py rename response/slack/{ => incident_commands}/incident_commands.py (88%) create mode 100644 response/slack/incident_commands/summary.py diff --git a/response/slack/incident_commands/__init__.py b/response/slack/incident_commands/__init__.py new file mode 100644 index 00000000..f5ba999b --- /dev/null +++ b/response/slack/incident_commands/__init__.py @@ -0,0 +1,5 @@ +from . import ( + summary, + impact, + incident_commands, +) diff --git a/response/slack/incident_commands/impact.py b/response/slack/incident_commands/impact.py new file mode 100644 index 00000000..51aa334c --- /dev/null +++ b/response/slack/incident_commands/impact.py @@ -0,0 +1,104 @@ +import logging +import json + +from response.core.models import Incident +from response.slack.decorators.incident_command import __default_incident_command +from response.slack.models import CommsChannel +from response.slack import block_kit, dialog_builder +from response.slack.decorators import action_handler, dialog_handler, ActionContext + + +logger = logging.getLogger(__name__) + +UPDATE_CURRENT_IMPACT_ACTION = 'update-current-impact-action' +SET_NEW_IMPACT_ACTION = 'set-new-impact-action' +PROPOSED_MESSAGE_BLOCK_ID = 'proposed' +NO_IMPACT_TEXT = "The impact of this incicent hasn't been set yet." +CURRENT_TITLE = '*Current impact:*\n' +PROPOSED_TITLE = '*Proposed impact:*\n' +UPDATE_IMPACT_DIALOG = 'update-impact-dialog' +IMPACT_UPDATED_TITLE = '*Impact updated to:*\n' + + +@__default_incident_command(["impact"], helptext="Explain the impact of this") +def update_impact(incident: Incident, user_id: str, message: str): + # Easy case. No impact currently and one has been provided + if message and not incident.impact: + incident.impact = message + incident.save() + return True, f'{IMPACT_UPDATED_TITLE}{message}' + + # Either no new impact has been provided, or one already exists + msg = block_kit.Message() + msg.add_block( + block_kit.Section( + block_id="update", + text=block_kit.Text(f"{CURRENT_TITLE}{incident.impact or NO_IMPACT_TEXT}"), + accessory=block_kit.Button( + "Update", UPDATE_CURRENT_IMPACT_ACTION, value=incident.pk + ), + ) + ) + + # if the user has supplied a message, provide the option for them to set it without + # retyping in the dialog + if message: + msg.add_block( + block_kit.Section( + block_id=PROPOSED_MESSAGE_BLOCK_ID, + text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), + accessory=block_kit.Button( + "Set to this", SET_NEW_IMPACT_ACTION, value=incident.pk + ) + ) + ) + + comms_channel = CommsChannel.objects.get(incident=incident) + msg.send(comms_channel.channel_id) + return True, None + + +@action_handler(SET_NEW_IMPACT_ACTION) +def handle_set_new_impact(action_context: ActionContext): + for block in action_context.message['blocks']: + print('Looking at block', block) + if block['block_id'] == PROPOSED_MESSAGE_BLOCK_ID: + impact = block['text']['text'].replace(PROPOSED_TITLE, '') + action_context.incident.impact = impact + action_context.incident.save() + + comms_channel = CommsChannel.objects.get(incident=action_context.incident) + comms_channel.post_in_channel(f'{IMPACT_UPDATED_TITLE}{impact}') + return + + +@action_handler(UPDATE_CURRENT_IMPACT_ACTION) +def handle_open_impact_dialog(action_context: ActionContext): + dialog = dialog_builder.Dialog( + title="Update Impact", + submit_label="Update", + state=action_context.incident.pk, + elements=[ + dialog_builder.TextArea( + label="Impact", + name="impact", + optional=False, + value=action_context.incident.impact, + ), + ], + ) + + dialog.send_open_dialog(UPDATE_IMPACT_DIALOG, action_context.trigger_id) + + +@dialog_handler(UPDATE_IMPACT_DIALOG) +def update_status_page( + user_id: str, channel_id: str, submission: json, response_url: str, state: json +): + incident_id = state + incident = Incident.objects.get(pk=incident_id) + incident.impact = submission["impact"] + incident.save() + + comms_channel = CommsChannel.objects.get(incident=incident) + comms_channel.post_in_channel(f'{IMPACT_UPDATED_TITLE}{submission["impact"]}') diff --git a/response/slack/incident_commands.py b/response/slack/incident_commands/incident_commands.py similarity index 88% rename from response/slack/incident_commands.py rename to response/slack/incident_commands/incident_commands.py index 0991b39e..b5d0e4be 100644 --- a/response/slack/incident_commands.py +++ b/response/slack/incident_commands/incident_commands.py @@ -19,22 +19,6 @@ def send_help_text(incident: Incident, user_id: str, message: str): return True, get_help() -@__default_incident_command( - ["summary"], helptext="Provide a summary of what's going on" -) -def update_summary(incident: Incident, user_id: str, message: str): - incident.summary = message - incident.save() - return True, None - - -@__default_incident_command(["impact"], helptext="Explain the impact of this") -def update_impact(incident: Incident, user_id: str, message: str): - incident.impact = message - incident.save() - return True, None - - @__default_incident_command(["lead"], helptext="Assign someone as the incident lead") def set_incident_lead(incident: Incident, user_id: str, message: str): assignee = reference_to_id(message) or user_id diff --git a/response/slack/incident_commands/summary.py b/response/slack/incident_commands/summary.py new file mode 100644 index 00000000..a3450d32 --- /dev/null +++ b/response/slack/incident_commands/summary.py @@ -0,0 +1,104 @@ +import logging +import json + +from response.core.models import Incident +from response.slack.decorators.incident_command import __default_incident_command +from response.slack.models import CommsChannel +from response.slack import block_kit, dialog_builder +from response.slack.decorators import action_handler, dialog_handler, ActionContext + + +logger = logging.getLogger(__name__) + +UPDATE_CURRENT_SUMMARY_ACTION = 'update-current-summary-action' +SET_NEW_SUMMARY_ACTION = 'set-new-summary-action' +PROPOSED_MESSAGE_BLOCK_ID = 'proposed' +NO_SUMMARY_TEXT = "This incident doesn't have a summary yet." +CURRENT_TITLE = '*Current summary:*\n' +PROPOSED_TITLE = '*Proposed summary:*\n' +UPDATE_SUMMARY_DIALOG = 'update-summary-dialog' +SUMMARY_UPDATED_TITLE = '*Summary updated to:*\n' + + +@__default_incident_command(["summary"], helptext="Provide a summary of what's going on") +def update_summary(incident: Incident, user_id: str, message: str): + # Easy case. No summary currently and one has been provided + if message and not incident.summary: + incident.summary = message + incident.save() + return True, f'{SUMMARY_UPDATED_TITLE}{message}' + + # Either no new summary has been provided, or one already exists + msg = block_kit.Message() + msg.add_block( + block_kit.Section( + block_id="update", + text=block_kit.Text(f"{CURRENT_TITLE}{incident.summary or NO_SUMMARY_TEXT}"), + accessory=block_kit.Button( + "Update", UPDATE_CURRENT_SUMMARY_ACTION, value=incident.pk + ), + ) + ) + + # if the user has supplied a message, provide the option for them to set it without + # retyping in the dialog + if message: + msg.add_block( + block_kit.Section( + block_id=PROPOSED_MESSAGE_BLOCK_ID, + text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), + accessory=block_kit.Button( + "Set to this", SET_NEW_SUMMARY_ACTION, value=incident.pk + ) + ) + ) + + comms_channel = CommsChannel.objects.get(incident=incident) + msg.send(comms_channel.channel_id) + return True, None + + +@action_handler(SET_NEW_SUMMARY_ACTION) +def handle_set_new_summary(action_context: ActionContext): + for block in action_context.message['blocks']: + print('Looking at block', block) + if block['block_id'] == PROPOSED_MESSAGE_BLOCK_ID: + summary = block['text']['text'].replace(PROPOSED_TITLE, '') + action_context.incident.summary = summary + action_context.incident.save() + + comms_channel = CommsChannel.objects.get(incident=action_context.incident) + comms_channel.post_in_channel(f'{SUMMARY_UPDATED_TITLE}{summary}') + return + + +@action_handler(UPDATE_CURRENT_SUMMARY_ACTION) +def handle_open_summary_dialog(action_context: ActionContext): + dialog = dialog_builder.Dialog( + title="Update Summary", + submit_label="Update", + state=action_context.incident.pk, + elements=[ + dialog_builder.TextArea( + label="Summary", + name="summary", + optional=False, + value=action_context.incident.summary, + ), + ], + ) + + dialog.send_open_dialog(UPDATE_SUMMARY_DIALOG, action_context.trigger_id) + + +@dialog_handler(UPDATE_SUMMARY_DIALOG) +def update_status_page( + user_id: str, channel_id: str, submission: json, response_url: str, state: json +): + incident_id = state + incident = Incident.objects.get(pk=incident_id) + incident.summary = submission["summary"] + incident.save() + + comms_channel = CommsChannel.objects.get(incident=incident) + comms_channel.post_in_channel(f'{SUMMARY_UPDATED_TITLE}{submission["summary"]}') From 03c991ac8d22194efe413099c0f39c30c55ab6fb Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Fri, 15 Nov 2019 12:00:26 +0000 Subject: [PATCH 2/4] Fixing linting --- response/slack/incident_commands/__init__.py | 8 ++-- response/slack/incident_commands/impact.py | 37 ++++++++-------- response/slack/incident_commands/summary.py | 45 +++++++++++--------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/response/slack/incident_commands/__init__.py b/response/slack/incident_commands/__init__.py index f5ba999b..735d7c9d 100644 --- a/response/slack/incident_commands/__init__.py +++ b/response/slack/incident_commands/__init__.py @@ -1,5 +1,3 @@ -from . import ( - summary, - impact, - incident_commands, -) +from . import impact, incident_commands, summary + +__all__ = (impact, incident_commands, summary) diff --git a/response/slack/incident_commands/impact.py b/response/slack/incident_commands/impact.py index 51aa334c..04aa2363 100644 --- a/response/slack/incident_commands/impact.py +++ b/response/slack/incident_commands/impact.py @@ -1,23 +1,22 @@ -import logging import json +import logging from response.core.models import Incident +from response.slack import block_kit, dialog_builder +from response.slack.decorators import ActionContext, action_handler, dialog_handler from response.slack.decorators.incident_command import __default_incident_command from response.slack.models import CommsChannel -from response.slack import block_kit, dialog_builder -from response.slack.decorators import action_handler, dialog_handler, ActionContext - logger = logging.getLogger(__name__) -UPDATE_CURRENT_IMPACT_ACTION = 'update-current-impact-action' -SET_NEW_IMPACT_ACTION = 'set-new-impact-action' -PROPOSED_MESSAGE_BLOCK_ID = 'proposed' +UPDATE_CURRENT_IMPACT_ACTION = "update-current-impact-action" +SET_NEW_IMPACT_ACTION = "set-new-impact-action" +PROPOSED_MESSAGE_BLOCK_ID = "proposed" NO_IMPACT_TEXT = "The impact of this incicent hasn't been set yet." -CURRENT_TITLE = '*Current impact:*\n' -PROPOSED_TITLE = '*Proposed impact:*\n' -UPDATE_IMPACT_DIALOG = 'update-impact-dialog' -IMPACT_UPDATED_TITLE = '*Impact updated to:*\n' +CURRENT_TITLE = "*Current impact:*\n" +PROPOSED_TITLE = "*Proposed impact:*\n" +UPDATE_IMPACT_DIALOG = "update-impact-dialog" +IMPACT_UPDATED_TITLE = "*Impact updated to:*\n" @__default_incident_command(["impact"], helptext="Explain the impact of this") @@ -26,7 +25,7 @@ def update_impact(incident: Incident, user_id: str, message: str): if message and not incident.impact: incident.impact = message incident.save() - return True, f'{IMPACT_UPDATED_TITLE}{message}' + return True, f"{IMPACT_UPDATED_TITLE}{message}" # Either no new impact has been provided, or one already exists msg = block_kit.Message() @@ -49,7 +48,7 @@ def update_impact(incident: Incident, user_id: str, message: str): text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), accessory=block_kit.Button( "Set to this", SET_NEW_IMPACT_ACTION, value=incident.pk - ) + ), ) ) @@ -60,15 +59,15 @@ def update_impact(incident: Incident, user_id: str, message: str): @action_handler(SET_NEW_IMPACT_ACTION) def handle_set_new_impact(action_context: ActionContext): - for block in action_context.message['blocks']: - print('Looking at block', block) - if block['block_id'] == PROPOSED_MESSAGE_BLOCK_ID: - impact = block['text']['text'].replace(PROPOSED_TITLE, '') + for block in action_context.message["blocks"]: + print("Looking at block", block) + if block["block_id"] == PROPOSED_MESSAGE_BLOCK_ID: + impact = block["text"]["text"].replace(PROPOSED_TITLE, "") action_context.incident.impact = impact action_context.incident.save() comms_channel = CommsChannel.objects.get(incident=action_context.incident) - comms_channel.post_in_channel(f'{IMPACT_UPDATED_TITLE}{impact}') + comms_channel.post_in_channel(f"{IMPACT_UPDATED_TITLE}{impact}") return @@ -84,7 +83,7 @@ def handle_open_impact_dialog(action_context: ActionContext): name="impact", optional=False, value=action_context.incident.impact, - ), + ) ], ) diff --git a/response/slack/incident_commands/summary.py b/response/slack/incident_commands/summary.py index a3450d32..d0bff926 100644 --- a/response/slack/incident_commands/summary.py +++ b/response/slack/incident_commands/summary.py @@ -1,39 +1,42 @@ -import logging import json +import logging from response.core.models import Incident +from response.slack import block_kit, dialog_builder +from response.slack.decorators import ActionContext, action_handler, dialog_handler from response.slack.decorators.incident_command import __default_incident_command from response.slack.models import CommsChannel -from response.slack import block_kit, dialog_builder -from response.slack.decorators import action_handler, dialog_handler, ActionContext - logger = logging.getLogger(__name__) -UPDATE_CURRENT_SUMMARY_ACTION = 'update-current-summary-action' -SET_NEW_SUMMARY_ACTION = 'set-new-summary-action' -PROPOSED_MESSAGE_BLOCK_ID = 'proposed' +UPDATE_CURRENT_SUMMARY_ACTION = "update-current-summary-action" +SET_NEW_SUMMARY_ACTION = "set-new-summary-action" +PROPOSED_MESSAGE_BLOCK_ID = "proposed" NO_SUMMARY_TEXT = "This incident doesn't have a summary yet." -CURRENT_TITLE = '*Current summary:*\n' -PROPOSED_TITLE = '*Proposed summary:*\n' -UPDATE_SUMMARY_DIALOG = 'update-summary-dialog' -SUMMARY_UPDATED_TITLE = '*Summary updated to:*\n' +CURRENT_TITLE = "*Current summary:*\n" +PROPOSED_TITLE = "*Proposed summary:*\n" +UPDATE_SUMMARY_DIALOG = "update-summary-dialog" +SUMMARY_UPDATED_TITLE = "*Summary updated to:*\n" -@__default_incident_command(["summary"], helptext="Provide a summary of what's going on") +@__default_incident_command( + ["summary"], helptext="Provide a summary of what's going on" +) def update_summary(incident: Incident, user_id: str, message: str): # Easy case. No summary currently and one has been provided if message and not incident.summary: incident.summary = message incident.save() - return True, f'{SUMMARY_UPDATED_TITLE}{message}' + return True, f"{SUMMARY_UPDATED_TITLE}{message}" # Either no new summary has been provided, or one already exists msg = block_kit.Message() msg.add_block( block_kit.Section( block_id="update", - text=block_kit.Text(f"{CURRENT_TITLE}{incident.summary or NO_SUMMARY_TEXT}"), + text=block_kit.Text( + f"{CURRENT_TITLE}{incident.summary or NO_SUMMARY_TEXT}" + ), accessory=block_kit.Button( "Update", UPDATE_CURRENT_SUMMARY_ACTION, value=incident.pk ), @@ -49,7 +52,7 @@ def update_summary(incident: Incident, user_id: str, message: str): text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), accessory=block_kit.Button( "Set to this", SET_NEW_SUMMARY_ACTION, value=incident.pk - ) + ), ) ) @@ -60,15 +63,15 @@ def update_summary(incident: Incident, user_id: str, message: str): @action_handler(SET_NEW_SUMMARY_ACTION) def handle_set_new_summary(action_context: ActionContext): - for block in action_context.message['blocks']: - print('Looking at block', block) - if block['block_id'] == PROPOSED_MESSAGE_BLOCK_ID: - summary = block['text']['text'].replace(PROPOSED_TITLE, '') + for block in action_context.message["blocks"]: + print("Looking at block", block) + if block["block_id"] == PROPOSED_MESSAGE_BLOCK_ID: + summary = block["text"]["text"].replace(PROPOSED_TITLE, "") action_context.incident.summary = summary action_context.incident.save() comms_channel = CommsChannel.objects.get(incident=action_context.incident) - comms_channel.post_in_channel(f'{SUMMARY_UPDATED_TITLE}{summary}') + comms_channel.post_in_channel(f"{SUMMARY_UPDATED_TITLE}{summary}") return @@ -84,7 +87,7 @@ def handle_open_summary_dialog(action_context: ActionContext): name="summary", optional=False, value=action_context.incident.summary, - ), + ) ], ) From 3b89b95f57e98e71594f395c1197da8d70d30226 Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Fri, 15 Nov 2019 15:17:05 +0000 Subject: [PATCH 3/4] Changed wording to improve the UX in the summary/impact flow --- response/slack/incident_commands/impact.py | 15 +++++++++------ response/slack/incident_commands/summary.py | 17 +++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/response/slack/incident_commands/impact.py b/response/slack/incident_commands/impact.py index 04aa2363..83c5d2d5 100644 --- a/response/slack/incident_commands/impact.py +++ b/response/slack/incident_commands/impact.py @@ -12,11 +12,14 @@ UPDATE_CURRENT_IMPACT_ACTION = "update-current-impact-action" SET_NEW_IMPACT_ACTION = "set-new-impact-action" PROPOSED_MESSAGE_BLOCK_ID = "proposed" -NO_IMPACT_TEXT = "The impact of this incicent hasn't been set yet." -CURRENT_TITLE = "*Current impact:*\n" -PROPOSED_TITLE = "*Proposed impact:*\n" UPDATE_IMPACT_DIALOG = "update-impact-dialog" -IMPACT_UPDATED_TITLE = "*Impact updated to:*\n" + +NO_IMPACT_TEXT = "The impact of this incicent hasn't been set yet." +CURRENT_TITLE = "*This is the current impact:*\n" +PROPOSED_TITLE = "*Or would you like to update the impact to this?*\n" +IMPACT_UPDATED_TITLE = "*The impact has been updated to:*\n" +CHANGE_BUTTON_TEXT = "Change" +ACCEPT_PROPOSED_TEXT = "Yes" @__default_incident_command(["impact"], helptext="Explain the impact of this") @@ -34,7 +37,7 @@ def update_impact(incident: Incident, user_id: str, message: str): block_id="update", text=block_kit.Text(f"{CURRENT_TITLE}{incident.impact or NO_IMPACT_TEXT}"), accessory=block_kit.Button( - "Update", UPDATE_CURRENT_IMPACT_ACTION, value=incident.pk + CHANGE_BUTTON_TEXT, UPDATE_CURRENT_IMPACT_ACTION, value=incident.pk ), ) ) @@ -47,7 +50,7 @@ def update_impact(incident: Incident, user_id: str, message: str): block_id=PROPOSED_MESSAGE_BLOCK_ID, text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), accessory=block_kit.Button( - "Set to this", SET_NEW_IMPACT_ACTION, value=incident.pk + ACCEPT_PROPOSED_TEXT, SET_NEW_IMPACT_ACTION, value=incident.pk ), ) ) diff --git a/response/slack/incident_commands/summary.py b/response/slack/incident_commands/summary.py index d0bff926..28388739 100644 --- a/response/slack/incident_commands/summary.py +++ b/response/slack/incident_commands/summary.py @@ -12,11 +12,14 @@ UPDATE_CURRENT_SUMMARY_ACTION = "update-current-summary-action" SET_NEW_SUMMARY_ACTION = "set-new-summary-action" PROPOSED_MESSAGE_BLOCK_ID = "proposed" -NO_SUMMARY_TEXT = "This incident doesn't have a summary yet." -CURRENT_TITLE = "*Current summary:*\n" -PROPOSED_TITLE = "*Proposed summary:*\n" UPDATE_SUMMARY_DIALOG = "update-summary-dialog" -SUMMARY_UPDATED_TITLE = "*Summary updated to:*\n" + +NO_SUMMARY_TEXT = "This incident doesn't have a summary yet." +CURRENT_TITLE = "*This is the current summary:*\n" +PROPOSED_TITLE = "*Or would you like to update the summary to this?*\n" +SUMMARY_UPDATED_TITLE = "*The summary has been updated to:*\n" +CHANGE_BUTTON_TEXT = "Change" +ACCEPT_PROPOSED_TEXT = "Yes" @__default_incident_command( @@ -31,6 +34,7 @@ def update_summary(incident: Incident, user_id: str, message: str): # Either no new summary has been provided, or one already exists msg = block_kit.Message() + msg.add_block( block_kit.Section( block_id="update", @@ -38,7 +42,7 @@ def update_summary(incident: Incident, user_id: str, message: str): f"{CURRENT_TITLE}{incident.summary or NO_SUMMARY_TEXT}" ), accessory=block_kit.Button( - "Update", UPDATE_CURRENT_SUMMARY_ACTION, value=incident.pk + CHANGE_BUTTON_TEXT, UPDATE_CURRENT_SUMMARY_ACTION, value=incident.pk ), ) ) @@ -46,12 +50,13 @@ def update_summary(incident: Incident, user_id: str, message: str): # if the user has supplied a message, provide the option for them to set it without # retyping in the dialog if message: + msg.add_block(block_kit.Divider()) msg.add_block( block_kit.Section( block_id=PROPOSED_MESSAGE_BLOCK_ID, text=block_kit.Text(f"{PROPOSED_TITLE}{message}"), accessory=block_kit.Button( - "Set to this", SET_NEW_SUMMARY_ACTION, value=incident.pk + ACCEPT_PROPOSED_TEXT, SET_NEW_SUMMARY_ACTION, value=incident.pk ), ) ) From 4da8eaa79ae103a2661c8437ddfc071ff94a003a Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Fri, 15 Nov 2019 15:26:01 +0000 Subject: [PATCH 4/4] Typo --- response/slack/incident_commands/impact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/response/slack/incident_commands/impact.py b/response/slack/incident_commands/impact.py index 83c5d2d5..8d123ec8 100644 --- a/response/slack/incident_commands/impact.py +++ b/response/slack/incident_commands/impact.py @@ -14,7 +14,7 @@ PROPOSED_MESSAGE_BLOCK_ID = "proposed" UPDATE_IMPACT_DIALOG = "update-impact-dialog" -NO_IMPACT_TEXT = "The impact of this incicent hasn't been set yet." +NO_IMPACT_TEXT = "The impact of this incident hasn't been set yet." CURRENT_TITLE = "*This is the current impact:*\n" PROPOSED_TITLE = "*Or would you like to update the impact to this?*\n" IMPACT_UPDATED_TITLE = "*The impact has been updated to:*\n"