mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-26 03:48:49 +02:00
Group support for Slack Followup (#951)
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
"""Add api_key table
|
"""Add api_key table
|
||||||
|
|
||||||
Revision ID: 79acd316403a
|
Revision ID: 79acd316403a
|
||||||
Revises: 904e5138fffb
|
Revises: 904e5138fffb
|
||||||
Create Date: 2024-01-11 17:56:37.934381
|
Create Date: 2024-01-11 17:56:37.934381
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import fastapi_users_db_sqlalchemy
|
import fastapi_users_db_sqlalchemy
|
||||||
|
@@ -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])
|
tag_str = " ".join([f"<@{tag}>" for tag in tag_ids])
|
||||||
if tag_str:
|
if tag_str:
|
||||||
tag_str += " "
|
tag_str += " "
|
||||||
|
|
||||||
|
group_str = " ".join([f"<!subteam^{group}>" for group in group_ids])
|
||||||
|
if group_str:
|
||||||
|
group_str += " "
|
||||||
|
|
||||||
text = (
|
text = (
|
||||||
tag_str
|
tag_str
|
||||||
|
+ group_str
|
||||||
+ "Someone has requested more help.\n\n:point_down:Please mark this resolved after answering!"
|
+ "Someone has requested more help.\n\n:point_down:Please mark this resolved after answering!"
|
||||||
)
|
)
|
||||||
text_block = SectionBlock(text=text)
|
text_block = SectionBlock(text=text)
|
||||||
|
@@ -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.constants import VIEW_DOC_FEEDBACK_ID
|
||||||
from danswer.danswerbot.slack.utils import build_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 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 fetch_userids_from_emails
|
||||||
from danswer.danswerbot.slack.utils import get_channel_name_from_id
|
from danswer.danswerbot.slack.utils import get_channel_name_from_id
|
||||||
from danswer.danswerbot.slack.utils import respond_in_thread
|
from danswer.danswerbot.slack.utils import respond_in_thread
|
||||||
@@ -140,7 +141,8 @@ def handle_followup_button(
|
|||||||
client=client.web_client,
|
client=client.web_client,
|
||||||
)
|
)
|
||||||
|
|
||||||
tag_ids = []
|
tag_ids: list[str] = []
|
||||||
|
group_ids: list[str] = []
|
||||||
with Session(get_sqlalchemy_engine()) as db_session:
|
with Session(get_sqlalchemy_engine()) as db_session:
|
||||||
channel_name, is_dm = get_channel_name_from_id(
|
channel_name, is_dm = get_channel_name_from_id(
|
||||||
client=client.web_client, channel_id=channel_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
|
channel_name=channel_name, db_session=db_session
|
||||||
)
|
)
|
||||||
if slack_bot_config:
|
if slack_bot_config:
|
||||||
emails = slack_bot_config.channel_config.get("follow_up_tags")
|
tag_names = slack_bot_config.channel_config.get("follow_up_tags")
|
||||||
if emails:
|
remaining = None
|
||||||
tag_ids = fetch_userids_from_emails(emails, client.web_client)
|
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(
|
respond_in_thread(
|
||||||
client=client.web_client,
|
client=client.web_client,
|
||||||
|
@@ -152,7 +152,7 @@ def handle_message(
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if respond_team_member_list:
|
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
|
# If configured to respond to team members only, then cannot be used with a /DanswerBot command
|
||||||
# which would just respond to the sender
|
# which would just respond to the sender
|
||||||
|
@@ -251,22 +251,48 @@ def get_channel_name_from_id(
|
|||||||
raise e
|
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] = []
|
user_ids: list[str] = []
|
||||||
|
failed_to_find: list[str] = []
|
||||||
for email in user_emails:
|
for email in user_emails:
|
||||||
try:
|
try:
|
||||||
user = client.users_lookupByEmail(email=email)
|
user = client.users_lookupByEmail(email=email)
|
||||||
user_ids.append(user.data["user"]["id"]) # type: ignore
|
user_ids.append(user.data["user"]["id"]) # type: ignore
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error(f"Was not able to find slack user by email: {email}")
|
logger.error(f"Was not able to find slack user by email: {email}")
|
||||||
|
failed_to_find.append(email)
|
||||||
|
|
||||||
if not user_ids:
|
return user_ids, failed_to_find
|
||||||
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
|
|
||||||
|
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:
|
def fetch_user_semantic_id_from_id(user_id: str, client: WebClient) -> str | None:
|
||||||
|
@@ -216,8 +216,12 @@ export const SlackBotCreationForm = ({
|
|||||||
subtext={
|
subtext={
|
||||||
<div>
|
<div>
|
||||||
The full email addresses of the Slack users we should
|
The full email addresses of the Slack users we should
|
||||||
tag if the user clicks the "Still need help?" button.
|
tag if the user clicks the "Still need help?"
|
||||||
For example, 'mark@acme.com'.
|
button. For example, 'mark@acme.com'.
|
||||||
|
<br />
|
||||||
|
Or provide a user group by either the name or the
|
||||||
|
handle. For example, 'Danswer Team' or
|
||||||
|
'danswer-team'.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
If no emails are provided, we will not tag anyone and
|
If no emails are provided, we will not tag anyone and
|
||||||
|
Reference in New Issue
Block a user