async def handle_slack_event(*, client, event, background_tasks): """Handles slack event message.""" user_id = event.event.user channel_id = get_channel_id_from_event(event) if user_id and channel_id: db_session = get_organization_from_channel_id(channel_id=channel_id) if not db_session: log.info( f"Unable to determine organization associated with channel id. ChannelId: {channel_id}" ) return {"ok": ""} conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) if conversation and dispatch_slack_service.is_user(user_id): # We resolve the user's email user_email = await dispatch_slack_service.get_user_email_async( client, user_id) # Dispatch event functions to be executed in the background for f in event_functions(event): background_tasks.add_task( f, user_id, user_email, channel_id, conversation.incident_id, event=event, ) return {"ok": ""}
def handle_dialog_action(config: SlackConversationConfiguration, action: dict, background_tasks: BackgroundTasks): """Handles all dialog actions.""" channel_id = action["channel"]["id"] db_session = get_organization_scope_from_channel_id(channel_id=channel_id) conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) incident_id = conversation.incident_id user_id = action["user"]["id"] user_email = action["user"]["email"] action_id = action["callback_id"] for f in dialog_action_functions(config, action_id): background_tasks.add_task( f, user_id=user_id, user_email=user_email, config=config, channel_id=channel_id, incident_id=incident_id, action=action, )
async def handle_slack_command(*, db_session, client, request, background_tasks): """Handles slack command message.""" # We fetch conversation by channel id channel_id = request.get("channel_id") conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) # We get the name of command that was run command = request.get("command") incident_id = 0 if conversation: incident_id = conversation.incident_id else: if command not in [ SLACK_COMMAND_REPORT_INCIDENT_SLUG, SLACK_COMMAND_LIST_INCIDENTS_SLUG ]: # We let the user know that incident-specific commands # can only be run in incident conversations return create_command_run_in_nonincident_conversation_message( command) # We get the list of public and private conversations the Slack bot is a member of ( public_conversations, private_conversations, ) = await dispatch_slack_service.get_conversations_by_user_id_async( client, SLACK_APP_USER_SLUG) # We get the name of conversation where the command was run conversation_id = request.get("channel_id") conversation_name = await dispatch_slack_service.get_conversation_name_by_id_async( client, conversation_id) if (not conversation_name or conversation_name not in public_conversations + private_conversations): # We let the user know in which public conversations they can run the command return create_command_run_in_conversation_where_bot_not_present_message( command, public_conversations) user_id = request.get("user_id") user_email = await dispatch_slack_service.get_user_email_async( client, user_id) # some commands are sensitive and we only let non-participants execute them allowed = check_command_restrictions(command=command, user_email=user_email, incident_id=incident_id, db_session=db_session) if not allowed: return create_command_run_by_non_privileged_user_message(command) for f in command_functions(command): background_tasks.add_task(f, incident_id, command=request) return INCIDENT_CONVERSATION_COMMAND_MESSAGE.get( command, f"Running... Command: {command}")
def get_plugin_configuration_from_channel_id(db_session: SessionLocal, channel_id: str) -> Plugin: """Fetches the currently slack plugin configuration for this incident channel.""" conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session, channel_id) if conversation: plugin_instance = plugin_service.get_active_instance( db_session=db_session, plugin_type="conversation", project_id=conversation.incident.project.id, ) return plugin_instance.configuration
async def handle_event( event: EventEnvelope, request: Request, response: Response, background_tasks: BackgroundTasks, x_slack_request_timestamp: int = Header(None), x_slack_signature: str = Header(None), db_session: Session = Depends(get_db), ): """Handle all incomming Slack events.""" raw_request_body = bytes.decode(await request.body()) # We verify the timestamp verify_timestamp(x_slack_request_timestamp) # We verify the signature verify_signature(raw_request_body, x_slack_request_timestamp, x_slack_signature) # Echo the URL verification challenge code back to Slack if event.challenge: return {"challenge": event.challenge} event_body = event.event user_id = event_body.user channel_id = get_channel_id_from_event(event_body) if user_id and channel_id: conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) if conversation and dispatch_slack_service.is_user(user_id): # We create an async Slack client slack_async_client = dispatch_slack_service.create_slack_client( run_async=True) # We resolve the user's email user_email = await dispatch_slack_service.get_user_email_async( slack_async_client, user_id) # Dispatch event functions to be executed in the background for f in event_functions(event): background_tasks.add_task(f, user_email, conversation.incident_id, event=event) # We add the user-agent string to the response headers response.headers["X-Slack-Powered-By"] = create_ua_string() return {"ok"}
def handle_dialog_action(action: dict, background_tasks: BackgroundTasks, db_session: SessionLocal): """Handles all dialog actions.""" channel_id = action["channel"]["id"] conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id ) incident_id = conversation.incident_id user_id = action["user"]["id"] user_email = action["user"]["email"] action_id = action["callback_id"] for f in dialog_action_functions(action_id): background_tasks.add_task(f, user_id, user_email, incident_id, action)
async def handle_incident_conversation_commands(config, client, request, background_tasks): """Handles all commands that are issued from an incident conversation.""" channel_id = request.get("channel_id") command = request.get("command") db_session = get_organization_scope_from_channel_id(channel_id=channel_id) if not db_session: # We let the user know that incident-specific commands # can only be run in incident conversations return create_command_run_in_nonincident_conversation_message(command) conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id ) user_id = request.get("user_id") user_email = await dispatch_slack_service.get_user_email_async(client, user_id) # some commands are sensitive and we only let non-participants execute them allowed = check_command_restrictions( config=config, command=command, user_email=user_email, incident_id=conversation.incident.id, db_session=db_session, ) if not allowed: return create_command_run_by_non_privileged_user_message(command) for f in command_functions(config, command): background_tasks.add_task( f, user_id=user_id, user_email=user_email, channel_id=channel_id, config=config, incident_id=conversation.incident.id, command=request, ) return get_incident_conversation_command_message(config, command)
async def handle_command( request: Request, response: Response, background_tasks: BackgroundTasks, x_slack_request_timestamp: int = Header(None), x_slack_signature: str = Header(None), db_session: Session = Depends(get_db), ): """Handle all incomming Slack commands.""" raw_request_body = bytes.decode(await request.body()) request_body_form = await request.form() command = request_body_form._dict # We verify the timestamp verify_timestamp(x_slack_request_timestamp) # We verify the signature verify_signature(raw_request_body, x_slack_request_timestamp, x_slack_signature) # We add the user-agent string to the response headers response.headers["X-Slack-Powered-By"] = create_ua_string() # Fetch conversation by channel id channel_id = command.get("channel_id") conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id ) incident_id = 0 if conversation: incident_id = conversation.incident_id else: if command.get("command") != SLACK_COMMAND_REPORT_INCIDENT_SLUG: return render_non_incident_conversation_command_error_message(command.get("command")) for f in command_functions(command.get("command")): background_tasks.add_task(f, incident_id, command=command) return INCIDENT_CONVERSATION_COMMAND_MESSAGE.get( command.get("command"), f"Running... Command: {command.get('command')}" )
def get_organization_scope_from_channel_id(channel_id: str) -> SessionLocal: """Iterate all organizations looking for a relevant channel_id.""" db_session = SessionLocal() organization_slugs = [ o.slug for o in organization_service.get_all(db_session=db_session) ] db_session.close() for slug in organization_slugs: schema_engine = engine.execution_options( schema_translate_map={ None: f"dispatch_organization_{slug}", }) scoped_db_session = sessionmaker(bind=schema_engine)() conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=scoped_db_session, channel_id=channel_id) if conversation: return scoped_db_session scoped_db_session.close()
async def handle_slack_event(*, db_session, client, event, background_tasks): """Handles slack event message.""" user_id = event.event.user channel_id = get_channel_id_from_event(event) if user_id and channel_id: conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) if conversation and dispatch_slack_service.is_user(user_id): # We resolve the user's email user_email = await dispatch_slack_service.get_user_email_async( client, user_id) # Dispatch event functions to be executed in the background for f in event_functions(event): background_tasks.add_task(f, user_email, conversation.incident_id, event=event) return {"ok": ""}
async def handle_command( request: Request, response: Response, background_tasks: BackgroundTasks, x_slack_request_timestamp: int = Header(None), x_slack_signature: str = Header(None), db_session: Session = Depends(get_db), ): """Handle all incomming Slack commands.""" raw_request_body = bytes.decode(await request.body()) request_body_form = await request.form() command_details = request_body_form._dict # We verify the timestamp verify_timestamp(x_slack_request_timestamp) # We verify the signature verify_signature(raw_request_body, x_slack_request_timestamp, x_slack_signature) # We add the user-agent string to the response headers response.headers["X-Slack-Powered-By"] = create_ua_string() # We fetch conversation by channel id channel_id = command_details.get("channel_id") conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=db_session, channel_id=channel_id) # We get the name of command that was run command = command_details.get("command") incident_id = 0 if conversation: incident_id = conversation.incident_id else: if command not in [ SLACK_COMMAND_REPORT_INCIDENT_SLUG, SLACK_COMMAND_LIST_INCIDENTS_SLUG ]: # We let the user know that incident-specific commands # can only be run in incident conversations return create_command_run_in_nonincident_conversation_message( command) # We create an async Slack client slack_async_client = dispatch_slack_service.create_slack_client( run_async=True) # We get the list of public and private conversations the Slack bot is a member of ( public_conversations, private_conversations, ) = await dispatch_slack_service.get_conversations_by_user_id_async( slack_async_client, SLACK_APP_USER_SLUG) # We get the name of conversation where the command was run conversation_name = command_details.get("channel_name") if conversation_name not in public_conversations + private_conversations: # We let the user know in which public conversations they can run the command return create_command_run_in_conversation_where_bot_not_present_message( command, public_conversations) for f in command_functions(command): background_tasks.add_task(f, incident_id, command=command_details) return INCIDENT_CONVERSATION_COMMAND_MESSAGE.get( command, f"Running... Command: {command}")
def wrapper(*args, **kwargs): background = False if not kwargs.get("db_session"): channel_id = args[2] # slug passed directly is prefered over just having a channel_id organization_slug = kwargs.pop("organization_slug", None) if not organization_slug: scoped_db_session = get_organization_from_channel_id( channel_id=channel_id) if not scoped_db_session: raise Exception( f"Could not find an organization associated with channel_id. ChannelId: {channel_id}" ) else: schema_engine = engine.execution_options( schema_translate_map={ None: f"dispatch_organization_{organization_slug}", }) scoped_db_session = sessionmaker(bind=schema_engine)() background = True kwargs["db_session"] = scoped_db_session if not kwargs.get("slack_client"): slack_client = dispatch_slack_service.create_slack_client() kwargs["slack_client"] = slack_client try: metrics_provider.counter("function.call.counter", tags={ "function": fullname(func), "slack": True }) start = time.perf_counter() result = func(*args, **kwargs) elapsed_time = time.perf_counter() - start metrics_provider.timer( "function.elapsed.time", value=elapsed_time, tags={ "function": fullname(func), "slack": True }, ) return result except Exception as e: # we generate our own guid for now, maybe slack provides us something we can use? slack_interaction_guid = str(uuid.uuid4()) log.exception( e, extra=dict(slack_interaction_guid=slack_interaction_guid)) user_id = args[0] channel_id = args[2] conversation = conversation_service.get_by_channel_id_ignoring_channel_type( db_session=kwargs["db_session"], channel_id=channel_id) # we notify the user that the interaction failed message = ( f"Sorry, we've run into an unexpected error. For help, please reach out to {conversation.incident.commander.individual.name}", f" and provide them with the following token: {slack_interaction_guid}.", ) if conversation.incident.status != IncidentStatus.closed: dispatch_slack_service.send_ephemeral_message( kwargs["slack_client"], channel_id, user_id, message) else: dispatch_slack_service.send_message( client=kwargs["slack_client"], conversation_id=user_id, text=message, ) finally: if background: kwargs["db_session"].close()