diff --git a/backend/alembic/versions/79acd316403a_add_api_key_table.py b/backend/alembic/versions/79acd316403a_add_api_key_table.py index e8b0885fd..b3617883f 100644 --- a/backend/alembic/versions/79acd316403a_add_api_key_table.py +++ b/backend/alembic/versions/79acd316403a_add_api_key_table.py @@ -1,7 +1,9 @@ """Add api_key table + Revision ID: 79acd316403a Revises: 904e5138fffb Create Date: 2024-01-11 17:56:37.934381 + """ from alembic import op import fastapi_users_db_sqlalchemy diff --git a/backend/danswer/danswerbot/slack/blocks.py b/backend/danswer/danswerbot/slack/blocks.py index f9f2ffcdd..3a84c18aa 100644 --- a/backend/danswer/danswerbot/slack/blocks.py +++ b/backend/danswer/danswerbot/slack/blocks.py @@ -288,12 +288,20 @@ def build_follow_up_block(message_id: int | None) -> ActionsBlock: ) -def build_follow_up_resolved_blocks(tag_ids: list[str]) -> list[Block]: +def build_follow_up_resolved_blocks( + tag_ids: list[str], group_ids: list[str] +) -> list[Block]: tag_str = " ".join([f"<@{tag}>" for tag in tag_ids]) if tag_str: tag_str += " " + + group_str = " ".join([f"" for group in group_ids]) + if group_str: + group_str += " " + text = ( tag_str + + group_str + "Someone has requested more help.\n\n:point_down:Please mark this resolved after answering!" ) text_block = SectionBlock(text=text) diff --git a/backend/danswer/danswerbot/slack/handlers/handle_buttons.py b/backend/danswer/danswerbot/slack/handlers/handle_buttons.py index d0640c0ef..fa0ece3e8 100644 --- a/backend/danswer/danswerbot/slack/handlers/handle_buttons.py +++ b/backend/danswer/danswerbot/slack/handlers/handle_buttons.py @@ -17,6 +17,7 @@ from danswer.danswerbot.slack.constants import LIKE_BLOCK_ACTION_ID from danswer.danswerbot.slack.constants import VIEW_DOC_FEEDBACK_ID from danswer.danswerbot.slack.utils import build_feedback_id from danswer.danswerbot.slack.utils import decompose_action_id +from danswer.danswerbot.slack.utils import fetch_groupids_from_names from danswer.danswerbot.slack.utils import fetch_userids_from_emails from danswer.danswerbot.slack.utils import get_channel_name_from_id from danswer.danswerbot.slack.utils import respond_in_thread @@ -140,7 +141,8 @@ def handle_followup_button( client=client.web_client, ) - tag_ids = [] + tag_ids: list[str] = [] + group_ids: list[str] = [] with Session(get_sqlalchemy_engine()) as db_session: channel_name, is_dm = get_channel_name_from_id( client=client.web_client, channel_id=channel_id @@ -149,11 +151,16 @@ def handle_followup_button( channel_name=channel_name, db_session=db_session ) if slack_bot_config: - emails = slack_bot_config.channel_config.get("follow_up_tags") - if emails: - tag_ids = fetch_userids_from_emails(emails, client.web_client) + tag_names = slack_bot_config.channel_config.get("follow_up_tags") + remaining = None + if tag_names: + tag_ids, remaining = fetch_userids_from_emails( + tag_names, client.web_client + ) + if remaining: + group_ids, _ = fetch_groupids_from_names(remaining, client.web_client) - blocks = build_follow_up_resolved_blocks(tag_ids=tag_ids) + blocks = build_follow_up_resolved_blocks(tag_ids=tag_ids, group_ids=group_ids) respond_in_thread( client=client.web_client, diff --git a/backend/danswer/danswerbot/slack/handlers/handle_message.py b/backend/danswer/danswerbot/slack/handlers/handle_message.py index 81dbc93be..a52a26bcc 100644 --- a/backend/danswer/danswerbot/slack/handlers/handle_message.py +++ b/backend/danswer/danswerbot/slack/handlers/handle_message.py @@ -152,7 +152,7 @@ def handle_message( return False if respond_team_member_list: - send_to = fetch_userids_from_emails(respond_team_member_list, client) + send_to, _ = fetch_userids_from_emails(respond_team_member_list, client) # If configured to respond to team members only, then cannot be used with a /DanswerBot command # which would just respond to the sender diff --git a/backend/danswer/danswerbot/slack/utils.py b/backend/danswer/danswerbot/slack/utils.py index 8577379bb..4088f704b 100644 --- a/backend/danswer/danswerbot/slack/utils.py +++ b/backend/danswer/danswerbot/slack/utils.py @@ -251,22 +251,48 @@ def get_channel_name_from_id( raise e -def fetch_userids_from_emails(user_emails: list[str], client: WebClient) -> list[str]: +def fetch_userids_from_emails( + user_emails: list[str], client: WebClient +) -> tuple[list[str], list[str]]: user_ids: list[str] = [] + failed_to_find: list[str] = [] for email in user_emails: try: user = client.users_lookupByEmail(email=email) user_ids.append(user.data["user"]["id"]) # type: ignore except Exception: logger.error(f"Was not able to find slack user by email: {email}") + failed_to_find.append(email) - if not user_ids: - raise RuntimeError( - "Was not able to find any Slack users to respond to. " - "No email was parsed into a valid slack account." - ) + return user_ids, failed_to_find - return user_ids + +def fetch_groupids_from_names( + names: list[str], client: WebClient +) -> tuple[list[str], list[str]]: + group_ids: set[str] = set() + failed_to_find: list[str] = [] + + try: + response = client.usergroups_list() + if response.get("ok") and "usergroups" in response.data: + all_groups_dicts = response.data["usergroups"] # type: ignore + name_id_map = {d["name"]: d["id"] for d in all_groups_dicts} + handle_id_map = {d["handle"]: d["id"] for d in all_groups_dicts} + for group in names: + if group in name_id_map: + group_ids.add(name_id_map[group]) + elif group in handle_id_map: + group_ids.add(handle_id_map[group]) + else: + failed_to_find.append(group) + else: + # Most likely a Slack App scope issue + logger.error("Error fetching user groups") + except Exception as e: + logger.error(f"Error fetching user groups: {str(e)}") + + return list(group_ids), failed_to_find def fetch_user_semantic_id_from_id(user_id: str, client: WebClient) -> str | None: diff --git a/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx b/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx index 23c78cbb9..8006ccf43 100644 --- a/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx +++ b/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx @@ -216,8 +216,12 @@ export const SlackBotCreationForm = ({ subtext={