Example #1
0
async def handle_menu(
        request: Request,
        response: Response,
        x_slack_request_timestamp: int = Header(None),
        x_slack_signature: str = Header(None),
        db_session: Session = Depends(get_db),
):
    """Handle all incoming Slack actions."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    request = json.loads(request_body_form.get("payload"))

    # 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 create an async Slack client
    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    return await handle_slack_menu(
        db_session=db_session,
        client=slack_async_client,
        request=request,
    )
Example #2
0
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 incoming 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)

    # We add the user-agent string to the response headers
    response.headers["X-Slack-Powered-By"] = create_ua_string()

    # Echo the URL verification challenge code back to Slack
    if event.challenge:
        return {"challenge": event.challenge}

    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    return await handle_slack_event(
        db_session=db_session,
        client=slack_async_client,
        event=event,
        background_tasks=background_tasks,
    )
Example #3
0
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 incoming Slack commands."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    request = 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()

    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    return await handle_slack_command(
        db_session=db_session,
        client=slack_async_client,
        request=request,
        background_tasks=background_tasks,
    )
Example #4
0
async def handle_command(
        request: Request,
        response: Response,
        organization: str,
        background_tasks: BackgroundTasks,
        x_slack_request_timestamp: int = Header(...),
        x_slack_signature: str = Header(...),
):
    """Handle all incoming Slack commands."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    request = request_body_form._dict

    # We verify the timestamp
    verify_timestamp(x_slack_request_timestamp)

    # We verify the signature
    current_configuration = verify_signature(organization, 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()

    slack_async_client = dispatch_slack_service.create_slack_client(
        config=current_configuration, run_async=True)

    body = await handle_slack_command(
        config=current_configuration,
        client=slack_async_client,
        request=request,
        background_tasks=background_tasks,
    )

    return JSONResponse(content=body)
Example #5
0
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

    if (event_body.type == "message"
            and event_body.subtype):  # We ignore messages that have a subtype
        # Parse the Event payload and emit the event to the event listener
        response.headers["X-Slack-Powered-By"] = create_ua_string()
        return {"ok"}

    user_id = event_body.user

    # Fetch conversation by channel id
    channel_id = (  # We ensure channel_id always has a value
        event_body.channel_id if event_body.channel_id else event_body.channel)
    conversation = get_by_channel_id(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)

    # We add the user-agent string to the response headers
    response.headers["X-Slack-Powered-By"] = create_ua_string()
    return {"ok"}
Example #6
0
async def handle_action(
        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 actions."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    action = json.loads(request_body_form.get("payload"))

    # 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 create an async Slack client
    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    # We resolve the user's email
    user_id = action["user"]["id"]
    user_email = await dispatch_slack_service.get_user_email_async(
        slack_async_client, user_id)

    # We resolve the action name based on the type
    action_name = get_action_name_by_action_type(action)

    # if the request was made as a form submission from slack then we skip getting the incident_id
    # the incident will be created in in the next step
    incident_id = 0
    if action_name != NewIncidentSubmission.form_slack_view:
        # we resolve the incident id based on the action type
        incident_id = get_incident_id_by_action_type(action, db_session)

    # Dispatch action functions to be executed in the background
    for f in action_functions(action_name):
        background_tasks.add_task(f, user_id, user_email, incident_id, action)

    # We add the user-agent string to the response headers
    response.headers["X-Slack-Powered-By"] = create_ua_string()

    # When there are no exceptions within the dialog submission, your app must respond with 200 OK with an empty body.
    response_body = {}
    if action_name == NewIncidentSubmission.form_slack_view:
        # For modals we set "response_action" to "clear" to close all views in the modal.
        # An empty body is currently not working.
        response_body = {"response_action": "clear"}

    return response_body
Example #7
0
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"}
Example #8
0
async def handle_action(
        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 actions."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    action = json.loads(request_body_form.get("payload"))

    # 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 create an async Slack client
    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    # We resolve the user's email
    user_id = action["user"]["id"]
    user_email = await dispatch_slack_service.get_user_email_async(
        slack_async_client, user_id)

    action["user"]["email"] = user_email

    # We add the user-agent string to the response headers
    # NOTE: I don't think this header ever gets sent? (kglisson)
    response.headers["X-Slack-Powered-By"] = create_ua_string()

    # When there are no exceptions within the dialog submission, your app must respond with 200 OK with an empty body.
    response_body = {}
    if action.get("view"):
        handle_modal_action(action, background_tasks)
        if action["type"] == "view_submission":
            # For modals we set "response_action" to "clear" to close all views in the modal.
            # An empty body is currently not working.
            response_body = {"response_action": "clear"}
    elif action["type"] == "dialog_submission":
        handle_dialog_action(action, background_tasks, db_session=db_session)
    elif action["type"] == "block_actions":
        handle_block_action(action, background_tasks)

    return response_body
Example #9
0
    def wrapper(*args, **kwargs):
        background = False

        if not kwargs.get("db_session"):
            db_session = SessionLocal()
            background = True
            kwargs["db_session"] = 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))

            # notify the user the interaction failed
            user_id = args[0]
            channel_id = args[2]
            message = f"Sorry, we've run into an unexpected error. For help, please reach out to the incident commander and provide them with the following token: {slack_interaction_guid}."
            dispatch_slack_service.send_ephemeral_message(
                kwargs["slack_client"], channel_id, user_id, message)

        finally:
            if background:
                kwargs["db_session"].close()
Example #10
0
async def handle_action(
        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 actions."""

    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    action = json.loads(request_body_form.get("payload"))

    # 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 create an async Slack client
    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)

    # We resolve the user's email
    user_id = action["user"]["id"]
    user_email = await dispatch_slack_service.get_user_email_async(
        slack_async_client, user_id)

    # We resolve the action name based on the type
    action_name = get_action_name_by_action_type(action)

    # we resolve the incident id based on the action type
    incident_id = get_incident_id_by_action_type(action, db_session)

    # Dispatch action functions to be executed in the background
    for f in action_functions(action_name):
        background_tasks.add_task(f, user_email, incident_id, action)

    # We add the user-agent string to the response headers
    response.headers["X-Slack-Powered-By"] = create_ua_string()

    # When there are no exceptions within the dialog submission, your app must respond with 200 OK with an empty body.
    # This will complete the dialog. (https://api.slack.com/dialogs#validation)
    return {}
Example #11
0
async def check_command_restrictions(command: str, user_id: str,
                                     incident_id: int,
                                     db_session: Session) -> bool:
    """Checks the current user's role to determine what commands they are allowed to run."""
    # some commands are sensitive and we only let non-participants execute them
    command_permissons = {
        SLACK_COMMAND_UPDATE_INCIDENT_SLUG: [
            ParticipantRoleType.incident_commander,
            ParticipantRoleType.scribe,
        ],
        SLACK_COMMAND_ASSIGN_ROLE_SLUG: [
            ParticipantRoleType.incident_commander,
            ParticipantRoleType.reporter,
            ParticipantRoleType.liaison,
            ParticipantRoleType.scribe,
        ],
        SLACK_COMMAND_REPORT_EXECUTIVE_SLUG: [
            ParticipantRoleType.incident_commander,
            ParticipantRoleType.scribe,
        ],
        SLACK_COMMAND_REPORT_TACTICAL_SLUG: [
            ParticipantRoleType.incident_commander,
            ParticipantRoleType.scribe,
        ],
    }

    # no permissions have been defined
    if command not in command_permissons.keys():
        return True

    slack_async_client = dispatch_slack_service.create_slack_client(
        run_async=True)
    user_email = await dispatch_slack_service.get_user_email_async(
        slack_async_client, user_id)
    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email)

    # if any required role is active, allow command
    for current_role in participant.current_roles:
        for allowed_role in command_permissons[command]:
            if current_role.role == allowed_role:
                return True
Example #12
0
async def handle_event(
        event: EventEnvelope,
        request: Request,
        response: Response,
        organization: str,
        background_tasks: BackgroundTasks,
        x_slack_request_timestamp: int = Header(...),
        x_slack_signature: str = Header(...),
):
    """Handle all incoming Slack events."""
    raw_request_body = bytes.decode(await request.body())

    # We verify the timestamp
    verify_timestamp(x_slack_request_timestamp)

    # We verify the signature
    current_configuration = verify_signature(organization, 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()

    # Echo the URL verification challenge code back to Slack
    if event.challenge:
        return JSONResponse(content={"challenge": event.challenge})

    slack_async_client = dispatch_slack_service.create_slack_client(
        config=current_configuration, run_async=True)

    body = await handle_slack_event(
        config=current_configuration,
        client=slack_async_client,
        event=event,
        background_tasks=background_tasks,
    )

    return JSONResponse(content=body)
Example #13
0
async def handle_menu(
        request: Request,
        response: Response,
        organization: str,
        x_slack_request_timestamp: int = Header(...),
        x_slack_signature: str = Header(...),
):
    """Handle all incoming Slack actions."""
    raw_request_body = bytes.decode(await request.body())
    request_body_form = await request.form()
    try:
        request = json.loads(request_body_form.get("payload"))
    except Exception:
        raise HTTPException(status_code=400, detail=[{"msg": "Bad Request"}])

    # We verify the timestamp
    verify_timestamp(x_slack_request_timestamp)

    # We verify the signature
    verify_signature(organization, raw_request_body, x_slack_request_timestamp,
                     x_slack_signature)
    current_configuration = verify_signature(organization, 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 create an async Slack client
    slack_async_client = dispatch_slack_service.create_slack_client(
        config=current_configuration, run_async=True)

    body = await handle_slack_menu(
        config=current_configuration,
        client=slack_async_client,
        request=request,
    )
    return JSONResponse(content=body)
Example #14
0
from dispatch.incident.enums import IncidentStatus
from dispatch.incident.models import IncidentUpdate, IncidentRead
from dispatch.plugins.dispatch_slack import service as dispatch_slack_service
from dispatch.report import flows as report_flows

from .config import (
    SLACK_COMMAND_ASSIGN_ROLE_SLUG,
    SLACK_COMMAND_ENGAGE_ONCALL_SLUG,
    SLACK_COMMAND_EXECUTIVE_REPORT_SLUG,
    SLACK_COMMAND_TACTICAL_REPORT_SLUG,
    SLACK_COMMAND_UPDATE_INCIDENT_SLUG,
)

from .service import get_user_email

slack_client = dispatch_slack_service.create_slack_client()


@background_task
def add_user_to_conversation(user_id: str,
                             user_email: str,
                             incident_id: int,
                             action: dict,
                             db_session=None):
    """Adds a user to a conversation."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    if incident.status == IncidentStatus.closed:
        message = f"Sorry, we cannot add you to a closed incident. Please reach out to the incident commander ({incident.commander.name}) for details."
        dispatch_slack_service.send_ephemeral_message(
Example #15
0
    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()
Example #16
0
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}")
Example #17
0
    def wrapper(*args, **kwargs):
        background = False

        metrics_provider.counter("function.call.counter",
                                 tags={
                                     "function": fullname(func),
                                     "slack": True
                                 })

        channel_id = kwargs["channel_id"]
        user_id = kwargs["user_id"]
        if not kwargs.get("db_session"):

            # 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_scope_from_channel_id(
                    channel_id=channel_id)
                if not scoped_db_session:
                    scoped_db_session = get_default_organization_scope()
            else:
                scoped_db_session = get_organization_scope_from_slug(
                    organization_slug)

            background = True
            kwargs["db_session"] = scoped_db_session

        if not kwargs.get("slack_client"):
            slack_client = dispatch_slack_service.create_slack_client(
                config=kwargs["config"])
            kwargs["slack_client"] = slack_client

        try:
            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 ValidationError as e:
            log.exception(e)
            message = f"Command Error: {e.errors()[0]['msg']}"

            dispatch_slack_service.send_ephemeral_message(
                client=kwargs["slack_client"],
                conversation_id=channel_id,
                user_id=user_id,
                text=message,
            )

        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))

            # we notify the user that the interaction failed
            message = f"""Sorry, we've run into an unexpected error. For help please reach out to your Dispatch admins \
and provide them with the following token: '{slack_interaction_guid}'."""

            dispatch_slack_service.send_ephemeral_message(
                client=kwargs["slack_client"],
                conversation_id=channel_id,
                user_id=user_id,
                text=message,
            )
        finally:
            if background:
                kwargs["db_session"].close()