def send_welcome_email_to_participant( participant_email: str, incident: Incident, db_session: SessionLocal ): """Sends a welcome email to the participant.""" # we load the incident instance plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="email" ) if not plugin: log.warning("Participant welcome email not sent, not email plugin configured.") return message_kwargs = { "name": incident.name, "title": incident.title, "description": incident.description, "status": incident.status, "type": incident.incident_type.name, "type_description": incident.incident_type.description, "priority": incident.incident_priority.name, "priority_description": incident.incident_priority.description, "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "reporter_fullname": incident.reporter.individual.name, "reporter_team": incident.reporter.team, "reporter_weblink": incident.reporter.individual.weblink, "document_weblink": resolve_attr(incident, "incident_document.weblink"), "storage_weblink": resolve_attr(incident, "storage.weblink"), "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "conference_weblink": resolve_attr(incident, "conference.weblink"), "conference_challenge": resolve_attr(incident, "conference.conference_challenge"), "contact_fullname": incident.commander.individual.name, "contact_weblink": incident.commander.individual.weblink, } faq_doc = document_service.get_incident_faq_document(db_session=db_session) if faq_doc: message_kwargs.update({"faq_weblink": faq_doc.weblink}) conversation_reference = document_service.get_conversation_reference_document( db_session=db_session ) if conversation_reference: message_kwargs.update( {"conversation_commands_reference_document_weblink": conversation_reference.weblink} ) notification_text = "Incident Notification" plugin.instance.send( participant_email, notification_text, INCIDENT_PARTICIPANT_WELCOME_MESSAGE, MessageType.incident_participant_welcome, **message_kwargs, ) log.debug(f"Welcome email sent to {participant_email}.")
def send_incident_resources_ephemeral_message_to_participant( user_id: str, incident: Incident, db_session: SessionLocal ): """Sends the list of incident resources to the participant via an ephemeral message.""" plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="conversation" ) if not plugin: log.warning("Incident resource message not sent, no conversation plugin enabled.") return message_kwargs = { "title": incident.title, "description": incident.description, "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "reporter_fullname": incident.reporter.individual.name, "reporter_team": incident.reporter.team, "reporter_weblink": incident.reporter.individual.weblink, "document_weblink": resolve_attr(incident, "incident_document.weblink"), "storage_weblink": resolve_attr(incident, "storage.weblink"), "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "conference_weblink": resolve_attr(incident, "conference.weblink"), "conference_challenge": resolve_attr(incident, "conference.conference_challenge"), } if incident.incident_review_document: message_kwargs.update( {"review_document_weblink": incident.incident_review_document.weblink} ) faq_doc = document_service.get_incident_faq_document(db_session=db_session) if faq_doc: message_kwargs.update({"faq_weblink": faq_doc.weblink}) conversation_reference = document_service.get_conversation_reference_document( db_session=db_session ) if conversation_reference: message_kwargs.update( {"conversation_commands_reference_document_weblink": conversation_reference.weblink} ) # we send the ephemeral message plugin.instance.send_ephemeral( incident.conversation.channel_id, user_id, "Incident Resources Message", INCIDENT_RESOURCES_MESSAGE, MessageType.incident_resources_message, **message_kwargs, ) log.debug(f"List of incident resources sent to {user_id} via ephemeral message.")
def send_incident_created_notifications(incident: Incident, db_session: SessionLocal): """Sends incident created notifications.""" notification_template = INCIDENT_NOTIFICATION.copy() if incident.status != IncidentStatus.closed: notification_template.insert(0, INCIDENT_NAME_WITH_ENGAGEMENT) else: notification_template.insert(0, INCIDENT_NAME) notification_kwargs = { "name": incident.name, "title": incident.title, "description": incident.description, "status": incident.status, "type": incident.incident_type.name, "type_description": incident.incident_type.description, "priority": incident.incident_priority.name, "priority_description": incident.incident_priority.description, "reporter_fullname": incident.reporter.individual.name, "reporter_team": incident.reporter.team, "reporter_weblink": incident.reporter.individual.weblink, "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "document_weblink": resolve_attr(incident, "incident_document.weblink"), "storage_weblink": resolve_attr(incident, "storage.weblink"), "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "conference_weblink": resolve_attr(incident, "conference.weblink"), "conference_challenge": resolve_attr(incident, "conference.conference_challenge"), "contact_fullname": incident.commander.individual.name, "contact_weblink": incident.commander.individual.weblink, "incident_id": incident.id, } faq_doc = document_service.get_incident_faq_document(db_session=db_session) if faq_doc: notification_kwargs.update({"faq_weblink": faq_doc.weblink}) notification_params = { "text": "Incident Notification", "type": MessageType.incident_notification, "template": notification_template, "kwargs": notification_kwargs, } notification_service.filter_and_send( db_session=db_session, incident=incident, class_instance=incident, notification_params=notification_params, ) log.debug("Incident created notifications sent.")
def list_incidents(incident_id: int, command: dict = None, db_session=None): """Returns the list of current active and stable incidents, and closed incidents in the last 24 hours.""" incidents = [] # We fetch active incidents incidents = incident_service.get_all_by_status( db_session=db_session, status=IncidentStatus.active.value) # We fetch stable incidents incidents.extend( incident_service.get_all_by_status(db_session=db_session, status=IncidentStatus.stable.value)) # We fetch closed incidents in the last 24 hours incidents.extend( incident_service.get_all_last_x_hours_by_status( db_session=db_session, status=IncidentStatus.closed.value, hours=24)) blocks = [] blocks.append({ "type": "header", "text": { "type": "plain_text", "text": "List of Incidents" } }) if incidents: for incident in incidents: if incident.visibility == Visibility.open: ticket_weblink = resolve_attr(incident, "ticket.weblink") try: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": (f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Status*: {incident.status}\n" f"*Incident Commander*: <{incident.commander.individual.weblink}|{incident.commander.individual.name}>" ), }, }) except Exception as e: log.exception(e) dispatch_slack_service.send_ephemeral_message( slack_client, command["channel_id"], command["user_id"], "Incident List", blocks=blocks, )
def run_monitors(db_session, project, monitor_plugin, incidents, notify: bool = False): """Performs monitor run.""" for incident in incidents: for monitor in incident.monitors: # once an instance is complete we don't update it any more if not monitor.enabled: continue log.debug(f"Processing monitor. Monitor: {monitor.weblink}") monitor_status = monitor_plugin.instance.get_match_status( weblink=monitor.weblink, last_modified=monitor.updated_at, ) log.debug(f"Retrieved data from plugin. Data: {monitor_status}") if not monitor_status: continue monitor_status_old = monitor.status if monitor_status["state"] == monitor.status["state"]: continue monitor_service.update( db_session=db_session, monitor=monitor, monitor_in=MonitorUpdate( id=monitor.id, weblink=monitor.weblink, enabled=monitor.enabled, status=monitor_status, ), ) if notify: send_monitor_notification( project.id, incident.conversation.channel_id, INCIDENT_MONITOR_UPDATE_NOTIFICATION, db_session, monitor_state_old=monitor_status_old["state"], monitor_state_new=monitor.status["state"], weblink=monitor.weblink, monitor_creator_name=resolve_attr( monitor, "creator.individual.name"), )
def send_incident_report_reminder(incident: Incident, report_type: ReportTypes, db_session: SessionLocal): """Sends a direct message to the incident commander indicating that they should complete a report.""" message_text = f"Incident {report_type} Reminder" message_template = INCIDENT_REPORT_REMINDER command_name, message_type = get_report_reminder_settings(report_type) # check to see if there wasn't a recent report now = datetime.utcnow() if incident.last_tactical_report: last_reported_at = incident.last_tactical_report.created_at if now - last_reported_at < timedelta(hours=1): return plugin = plugin_service.get_active_instance(db_session=db_session, project_id=incident.project.id, plugin_type="conversation") if not plugin: log.warning( "Incident report reminder not sent, no conversation plugin enabled." ) return report_command = plugin.instance.get_command_name(command_name) ticket_weblink = resolve_attr(incident, "ticket.weblink") items = [{ "command": report_command, "name": incident.name, "report_type": report_type, "ticket_weblink": ticket_weblink, "title": incident.title, }] plugin.instance.send_direct( incident.commander.individual.email, message_text, message_template, message_type, items=items, ) log.debug( f"Incident report reminder sent to {incident.commander.individual.email}." )
def send_incident_update_notifications(incident: Incident, previous_incident: IncidentRead, db_session: SessionLocal): """Sends notifications about incident changes.""" notification_text = "Incident Notification" notification_type = MessageType.incident_notification notification_template = INCIDENT_NOTIFICATION_COMMON.copy() change = False if previous_incident.status != incident.status: change = True notification_template.append(INCIDENT_STATUS_CHANGE) if previous_incident.incident_type.name != incident.incident_type.name: change = True notification_template.append(INCIDENT_TYPE_CHANGE) if previous_incident.incident_priority.name != incident.incident_priority.name: change = True notification_template.append(INCIDENT_PRIORITY_CHANGE) if not change: # we don't need to notify log.debug("Incident updated notifications not sent.") return notification_template.append(INCIDENT_COMMANDER) # we send an update to the incident conversation if the incident is active or stable if incident.status != IncidentStatus.closed: incident_conversation_notification_template = notification_template.copy( ) incident_conversation_notification_template.insert(0, INCIDENT_NAME) convo_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="conversation") if convo_plugin: convo_plugin.instance.send( incident.conversation.channel_id, notification_text, incident_conversation_notification_template, notification_type, commander_fullname=incident.commander.individual.name, commander_team=incident.commander.team, commander_weblink=incident.commander.individual.weblink, incident_priority_new=incident.incident_priority.name, incident_priority_old=previous_incident.incident_priority.name, incident_status_new=incident.status, incident_status_old=previous_incident.status, incident_type_new=incident.incident_type.name, incident_type_old=previous_incident.incident_type.name, name=incident.name, ticket_weblink=incident.ticket.weblink, title=incident.title, ) else: log.debug( "Incident updated notification not sent to incident conversation. No conversation plugin enabled." ) # we send a notification to the notification conversations and emails fyi_notification_template = notification_template.copy() if incident.status != IncidentStatus.closed: fyi_notification_template.insert(0, INCIDENT_NAME_WITH_ENGAGEMENT) else: fyi_notification_template.insert(0, INCIDENT_NAME) notification_kwargs = { "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "contact_fullname": incident.commander.individual.name, "contact_weblink": incident.commander.individual.weblink, "incident_id": incident.id, "incident_priority_new": incident.incident_priority.name, "incident_priority_old": previous_incident.incident_priority.name, "incident_status_new": incident.status, "incident_status_old": previous_incident.status, "incident_type_new": incident.incident_type.name, "incident_type_old": previous_incident.incident_type.name, "organization_slug": incident.project.organization.slug, "name": incident.name, "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "title": incident.title, } notification_params = { "text": notification_text, "type": notification_type, "template": fyi_notification_template, "kwargs": notification_kwargs, } notification_service.filter_and_send( db_session=db_session, incident=incident, class_instance=incident, notification_params=notification_params, ) log.debug("Incident updated notifications sent.")
def list_incidents( user_id: str, user_email: str, channel_id: str, incident_id: int, config: SlackConversationConfiguration = None, command: dict = None, db_session=None, slack_client=None, ): """Returns the list of current active and stable incidents, and closed incidents in the last 24 hours.""" projects = [] incidents = [] args = command["text"].split(" ") # scopes reply to the current incident's project incident = incident_service.get(db_session=db_session, incident_id=incident_id) if incident: # command was run in an incident conversation projects.append(incident.project) else: # command was run in a non-incident conversation if len(args) == 2: project = project_service.get_by_name(db_session=db_session, name=args[1]) if project: projects.append() else: raise ValidationError( [ ErrorWrapper( NotFoundError( msg=f"Project name '{args[1]}' in organization '{args[0]}' not found. Check your spelling." ), loc="project", ) ], model=BaseModel, ) else: projects = project_service.get_all(db_session=db_session) for project in projects: # we fetch active incidents incidents.extend( incident_service.get_all_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.active ) ) # We fetch stable incidents incidents.extend( incident_service.get_all_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.stable, ) ) # We fetch closed incidents in the last 24 hours incidents.extend( incident_service.get_all_last_x_hours_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.closed, hours=24, ) ) blocks = [] blocks.append({"type": "header", "text": {"type": "plain_text", "text": "List of Incidents"}}) if incidents: for incident in incidents: if incident.visibility == Visibility.open: ticket_weblink = resolve_attr(incident, "ticket.weblink") try: blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Status*: {incident.status}\n" f"*Incident Commander*: <{incident.commander.individual.weblink}|{incident.commander.individual.name}>\n" f"*Project*: {incident.project.name}" ), }, } ) except Exception as e: log.exception(e) dispatch_slack_service.send_ephemeral_message( slack_client, channel_id, user_id, "Incident List", blocks=blocks, )
def list_incidents( user_id: str, user_email: str, channel_id: str, incident_id: int, command: dict = None, db_session=None, slack_client=None, ): """Returns the list of current active and stable incidents, and closed incidents in the last 24 hours.""" projects = [] incidents = [] # scopes reply to the current incident's project incident = incident_service.get(db_session=db_session, incident_id=incident_id) if incident: # command was run in an incident conversation projects.append(incident.project) else: # command was run in a non-incident conversation projects = project_service.get_all(db_session=db_session) for project in projects: # we fetch active incidents incidents.extend( incident_service.get_all_by_status(db_session=db_session, project_id=project.id, status=IncidentStatus.active)) # We fetch stable incidents incidents.extend( incident_service.get_all_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.stable, )) # We fetch closed incidents in the last 24 hours incidents.extend( incident_service.get_all_last_x_hours_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.closed, hours=24, )) blocks = [] blocks.append({ "type": "header", "text": { "type": "plain_text", "text": "List of Incidents" } }) if incidents: for incident in incidents: if incident.visibility == Visibility.open: ticket_weblink = resolve_attr(incident, "ticket.weblink") try: blocks.append({ "type": "section", "text": { "type": "mrkdwn", "text": (f"*<{ticket_weblink}|{incident.name}>*\n" f"*Title*: {incident.title}\n" f"*Type*: {incident.incident_type.name}\n" f"*Priority*: {incident.incident_priority.name}\n" f"*Status*: {incident.status}\n" f"*Incident Commander*: <{incident.commander.individual.weblink}|{incident.commander.individual.name}>\n" f"*Project*: {incident.project.name}"), }, }) except Exception as e: log.exception(e) dispatch_slack_service.send_ephemeral_message( slack_client, channel_id, user_id, "Incident List", blocks=blocks, )
def daily_report(db_session=None): """ Creates and sends incident daily reports based on notifications. """ for project in project_service.get_all(db_session=db_session): # we fetch all active, stable and closed incidents active_incidents = get_all_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.active.value) stable_incidents = get_all_last_x_hours_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.stable.value, hours=24, ) closed_incidents = get_all_last_x_hours_by_status( db_session=db_session, project_id=project.id, status=IncidentStatus.closed.value, hours=24, ) incidents = active_incidents + stable_incidents + closed_incidents # we map incidents to notification filters incidents_notification_filters_mapping = defaultdict( lambda: defaultdict(lambda: [])) notifications = notification_service.get_all_enabled( db_session=db_session, project_id=project.id) for incident in incidents: for notification in notifications: for search_filter in notification.filters: match = search_filter_service.match( db_session=db_session, filter_spec=search_filter.expression, class_instance=incident, ) if match: incidents_notification_filters_mapping[ notification.id][search_filter.id].append(incident) if not notification.filters: incidents_notification_filters_mapping[ notification.id][0].append(incident) # we create and send an incidents daily report for each notification filter for notification_id, search_filter_dict in incidents_notification_filters_mapping.items( ): for search_filter_id, incidents in search_filter_dict.items(): items_grouped = [] items_grouped_template = INCIDENT for idx, incident in enumerate(incidents): try: item = { "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "incident_id": incident.id, "name": incident.name, "priority": incident.incident_priority.name, "priority_description": incident.incident_priority.description, "status": incident.status, "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "title": incident.title, "type": incident.incident_type.name, "type_description": incident.incident_type.description, } if incident.status != IncidentStatus.closed.value: item.update({ "button_text": "Join Incident", "button_value": str(incident.id), "button_action": f"{ConversationButtonActions.invite_user.value}-{incident.status}-{idx}", }) items_grouped.append(item) except Exception as e: log.exception(e) notification_kwargs = { "contact_fullname": DISPATCH_HELP_EMAIL, "contact_weblink": DISPATCH_HELP_EMAIL, "items_grouped": items_grouped, "items_grouped_template": items_grouped_template, } notification_params = { "text": INCIDENT_DAILY_REPORT_TITLE, "type": MessageType.incident_daily_report, "template": INCIDENT_DAILY_REPORT, "kwargs": notification_kwargs, } notification = notification_service.get( db_session=db_session, notification_id=notification_id) notification_service.send( db_session=db_session, project_id=notification.project.id, notification=notification, notification_params=notification_params, )
def send_welcome_ephemeral_message_to_participant(participant_email: str, incident_id: int, db_session: SessionLocal): """Sends an ephemeral message to the participant.""" # we load the incident instance plugin = plugin_service.get_active(db_session=db_session, plugin_type="conversation") if not plugin: log.warning( "Incident welcome message not sent, not conversation plugin enabled." ) return incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we send the ephemeral message message_kwargs = { "name": incident.name, "title": incident.title, "description": incident.description, "status": incident.status, "type": incident.incident_type.name, "type_description": incident.incident_type.description, "priority": incident.incident_priority.name, "priority_description": incident.incident_priority.description, "commander_fullname": incident.commander.individual.name, "commander_team": incident.commander.team, "commander_weblink": incident.commander.individual.weblink, "reporter_fullname": incident.reporter.individual.name, "reporter_team": incident.reporter.team, "reporter_weblink": incident.reporter.individual.weblink, "document_weblink": resolve_attr(incident, "incident_document.weblink"), "storage_weblink": resolve_attr(incident, "storage.weblink"), "ticket_weblink": resolve_attr(incident, "ticket.weblink"), "conference_weblink": resolve_attr(incident, "conference.weblink"), "conference_challenge": resolve_attr(incident, "conference.conference_challenge"), } faq_doc = document_service.get_incident_faq_document(db_session=db_session) if faq_doc: message_kwargs.update({"faq_weblink": faq_doc.weblink}) conversation_reference = document_service.get_conversation_reference_document( db_session=db_session) if conversation_reference: message_kwargs.update({ "conversation_commands_reference_document_weblink": conversation_reference.weblink }) plugin.instance.send_ephemeral( incident.conversation.channel_id, participant_email, "Incident Welcome Message", INCIDENT_PARTICIPANT_WELCOME_MESSAGE, MessageType.incident_participant_welcome, **message_kwargs, ) log.debug(f"Welcome ephemeral message sent to {participant_email}.")