mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-04-08 03:48:14 +02:00
Officially support Slack DMs to DanswerBot (#556)
This commit is contained in:
parent
fa460f4da1
commit
31d5fc6d31
@ -12,8 +12,11 @@ VALID_SLACK_FILTERS = [
|
||||
|
||||
|
||||
def get_slack_bot_config_for_channel(
|
||||
channel_name: str, db_session: Session
|
||||
channel_name: str | None, db_session: Session
|
||||
) -> SlackBotConfig | None:
|
||||
if not channel_name:
|
||||
return None
|
||||
|
||||
slack_bot_configs = fetch_slack_bot_configs(db_session=db_session)
|
||||
for config in slack_bot_configs:
|
||||
if channel_name in config.channel_config["channel_names"]:
|
||||
|
@ -103,7 +103,7 @@ def handle_message(
|
||||
if respond_team_member_list:
|
||||
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
|
||||
if respond_team_member_list and is_bot_msg:
|
||||
if sender_id:
|
||||
|
@ -22,6 +22,7 @@ from danswer.bots.slack.utils import get_channel_name_from_id
|
||||
from danswer.bots.slack.utils import respond_in_thread
|
||||
from danswer.configs.app_configs import DANSWER_BOT_RESPOND_EVERY_CHANNEL
|
||||
from danswer.configs.app_configs import DANSWER_REACT_EMOJI
|
||||
from danswer.configs.app_configs import NOTIFY_SLACKBOT_NO_ANSWER
|
||||
from danswer.connectors.slack.utils import make_slack_api_rate_limited
|
||||
from danswer.db.engine import get_sqlalchemy_engine
|
||||
from danswer.dynamic_configs.interface import ConfigNotFoundError
|
||||
@ -80,7 +81,9 @@ def prefilter_requests(req: SocketModeRequest, client: SocketModeClient) -> bool
|
||||
|
||||
if event_type == "message":
|
||||
bot_tag_id = client.web_client.auth_test().get("user_id")
|
||||
if bot_tag_id and bot_tag_id in msg:
|
||||
# DMs with the bot don't pick up the @DanswerBot so we have to keep the
|
||||
# caught events_api
|
||||
if bot_tag_id and bot_tag_id in msg and event.get("channel_type") != "im":
|
||||
# Let the tag flow handle this case, don't reply twice
|
||||
return False
|
||||
|
||||
@ -170,10 +173,13 @@ def build_request_details(
|
||||
tagged = event.get("type") == "app_mention"
|
||||
message_ts = event.get("ts")
|
||||
thread_ts = event.get("thread_ts")
|
||||
bot_tag_id = client.web_client.auth_test().get("user_id")
|
||||
# Might exist even if not tagged, specifically in the case of @DanswerBot
|
||||
# in DanswerBot DM channel
|
||||
msg = re.sub(rf"<@{bot_tag_id}>\s", "", msg)
|
||||
|
||||
if tagged:
|
||||
logger.info("User tagged DanswerBot")
|
||||
bot_tag_id = client.web_client.auth_test().get("user_id")
|
||||
msg = re.sub(rf"<@{bot_tag_id}>\s", "", msg)
|
||||
|
||||
return SlackMessageInfo(
|
||||
msg_content=msg,
|
||||
@ -248,8 +254,9 @@ def process_message(
|
||||
req: SocketModeRequest,
|
||||
client: SocketModeClient,
|
||||
respond_every_channel: bool = DANSWER_BOT_RESPOND_EVERY_CHANNEL,
|
||||
notify_no_answer: bool = NOTIFY_SLACKBOT_NO_ANSWER,
|
||||
) -> None:
|
||||
logger.info(f"Received Slack request of type: '{req.type}'")
|
||||
logger.debug(f"Received Slack request of type: '{req.type}'")
|
||||
|
||||
# Throw out requests that can't or shouldn't be handled
|
||||
if not prefilter_requests(req, client):
|
||||
@ -267,32 +274,37 @@ def process_message(
|
||||
channel_name=channel_name, db_session=db_session
|
||||
)
|
||||
|
||||
# Be careful about this default, don't want to accidentally spam every channel
|
||||
if slack_bot_config is None and not respond_every_channel:
|
||||
logger.info(
|
||||
"Skipping message since the channel is not configured to use DanswerBot"
|
||||
# Be careful about this default, don't want to accidentally spam every channel
|
||||
# Users should be able to DM slack bot in their private channels though
|
||||
if (
|
||||
slack_bot_config is None
|
||||
and not respond_every_channel
|
||||
# DMs are unnamed, don't filter those out
|
||||
and channel_name is not None
|
||||
# If @DanswerBot or /DanswerBot, always respond with the default configs
|
||||
and not (details.is_bot_msg or details.bipass_filters)
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
send_msg_ack_to_user(details, client)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Was not able to react to user message due to: {e}")
|
||||
|
||||
failed = handle_message(
|
||||
message_info=details,
|
||||
channel_config=slack_bot_config,
|
||||
client=client.web_client,
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
send_msg_ack_to_user(details, client)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Was not able to react to user message due to: {e}")
|
||||
# Skipping answering due to pre-filtering is not considered a failure
|
||||
if failed and notify_no_answer:
|
||||
apologize_for_fail(details, client)
|
||||
|
||||
failed = handle_message(
|
||||
message_info=details,
|
||||
channel_config=slack_bot_config,
|
||||
client=client.web_client,
|
||||
)
|
||||
|
||||
# Skipping answering due to pre-filtering is not considered a failure
|
||||
if failed:
|
||||
apologize_for_fail(details, client)
|
||||
|
||||
try:
|
||||
remove_react(details, client)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Failed to remove Reaction due to: {e}")
|
||||
try:
|
||||
remove_react(details, client)
|
||||
except SlackApiError as e:
|
||||
logger.error(f"Failed to remove Reaction due to: {e}")
|
||||
|
||||
|
||||
def acknowledge_message(req: SocketModeRequest, client: SocketModeClient) -> None:
|
||||
|
@ -8,6 +8,7 @@ from typing import cast
|
||||
|
||||
from retry import retry
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
from slack_sdk.models.blocks import Block
|
||||
from slack_sdk.models.metadata import Metadata
|
||||
|
||||
@ -170,8 +171,12 @@ def get_channel_from_id(client: WebClient, channel_id: str) -> dict[str, Any]:
|
||||
return response["channel"]
|
||||
|
||||
|
||||
def get_channel_name_from_id(client: WebClient, channel_id: str) -> str:
|
||||
return get_channel_from_id(client, channel_id)["name"]
|
||||
def get_channel_name_from_id(client: WebClient, channel_id: str) -> str | None:
|
||||
try:
|
||||
return get_channel_from_id(client, channel_id).get("name")
|
||||
except SlackApiError:
|
||||
# Private channels such as DMs don't have a name
|
||||
return None
|
||||
|
||||
|
||||
def fetch_userids_from_emails(user_emails: list[str], client: WebClient) -> list[str]:
|
||||
|
@ -238,12 +238,16 @@ DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER = os.environ.get(
|
||||
"DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER", ""
|
||||
).lower() not in ["false", ""]
|
||||
DANSWER_REACT_EMOJI = os.environ.get("DANSWER_REACT_EMOJI") or "eyes"
|
||||
|
||||
# Should DanswerBot send an apology message if it's not able to find an answer
|
||||
# That way the user isn't confused as to why DanswerBot reacted but then said nothing
|
||||
# Off by default to be less intrusive (don't want to give a notif that just says we couldnt help)
|
||||
NOTIFY_SLACKBOT_NO_ANSWER = (
|
||||
os.environ.get("NOTIFY_SLACKBOT_NO_ANSWER", "").lower() == "true"
|
||||
)
|
||||
# Default is only respond in channels that are included by a slack config set in the UI
|
||||
DANSWER_BOT_RESPOND_EVERY_CHANNEL = (
|
||||
os.environ.get("DANSWER_BOT_RESPOND_EVERY_CHANNEL", "").lower() == "true"
|
||||
)
|
||||
|
||||
# Add a second LLM call post Answer to verify if the Answer is valid
|
||||
# Throws out answers that don't directly or fully answer the user query
|
||||
# This is the default for all DanswerBot channels unless the bot is configured individually
|
||||
|
@ -284,7 +284,6 @@ def upsert_persona(
|
||||
persona.default_persona = default_persona
|
||||
else:
|
||||
persona = Persona(
|
||||
id=persona_id,
|
||||
name=name,
|
||||
retrieval_enabled=retrieval_enabled,
|
||||
system_text=system_text,
|
||||
|
@ -81,6 +81,7 @@ services:
|
||||
- DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER=${DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER:-}
|
||||
- DANSWER_BOT_DISPLAY_ERROR_MSGS=${DANSWER_BOT_DISPLAY_ERROR_MSGS:-}
|
||||
- DANSWER_BOT_RESPOND_EVERY_CHANNEL=${DANSWER_BOT_RESPOND_EVERY_CHANNEL:-}
|
||||
- NOTIFY_SLACKBOT_NO_ANSWER=${NOTIFY_SLACKBOT_NO_ANSWER:-}
|
||||
# Don't change the NLP model configs unless you know what you're doing
|
||||
- DOCUMENT_ENCODER_MODEL=${DOCUMENT_ENCODER_MODEL:-}
|
||||
- NORMALIZE_EMBEDDINGS=${NORMALIZE_EMBEDDINGS:-}
|
||||
|
Loading…
x
Reference in New Issue
Block a user