From bc9b4e4f4574c0b522a424e1e2a16124a12502be Mon Sep 17 00:00:00 2001 From: rkuo-danswer Date: Wed, 26 Mar 2025 14:55:04 -0700 Subject: [PATCH] use slack's built in rate limit handler for the bot (#4362) Co-authored-by: Richard Kuo (Onyx) --- .../onyxbot/slack/handlers/handle_buttons.py | 4 +-- backend/onyx/onyxbot/slack/listener.py | 16 ++++++++- backend/onyx/onyxbot/slack/utils.py | 33 ++++++++++--------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/backend/onyx/onyxbot/slack/handlers/handle_buttons.py b/backend/onyx/onyxbot/slack/handlers/handle_buttons.py index 9d2693d65aa9..757c93f0f4e5 100644 --- a/backend/onyx/onyxbot/slack/handlers/handle_buttons.py +++ b/backend/onyx/onyxbot/slack/handlers/handle_buttons.py @@ -15,7 +15,6 @@ from onyx.configs.constants import MessageType from onyx.configs.constants import SearchFeedbackType from onyx.configs.onyxbot_configs import DANSWER_FOLLOWUP_EMOJI from onyx.connectors.slack.utils import expert_info_from_slack_id -from onyx.connectors.slack.utils import make_slack_api_rate_limited from onyx.context.search.models import SavedSearchDoc from onyx.db.chat import get_chat_message from onyx.db.chat import translate_db_message_to_chat_message_detail @@ -553,8 +552,7 @@ def handle_followup_resolved_button( # Delete the message with the option to mark resolved if not immediate: - slack_call = make_slack_api_rate_limited(client.web_client.chat_delete) - response = slack_call( + response = client.web_client.chat_delete( channel=channel_id, ts=message_ts, ) diff --git a/backend/onyx/onyxbot/slack/listener.py b/backend/onyx/onyxbot/slack/listener.py index e6ac548a29df..20041d1b5c9e 100644 --- a/backend/onyx/onyxbot/slack/listener.py +++ b/backend/onyx/onyxbot/slack/listener.py @@ -18,6 +18,9 @@ from prometheus_client import start_http_server from redis.lock import Lock from slack_sdk import WebClient from slack_sdk.errors import SlackApiError +from slack_sdk.http_retry import ConnectionErrorRetryHandler +from slack_sdk.http_retry import RateLimitErrorRetryHandler +from slack_sdk.http_retry import RetryHandler from slack_sdk.socket_mode.request import SocketModeRequest from slack_sdk.socket_mode.response import SocketModeResponse from sqlalchemy.orm import Session @@ -944,10 +947,21 @@ def _get_socket_client( ) -> TenantSocketModeClient: # For more info on how to set this up, checkout the docs: # https://docs.onyx.app/slack_bot_setup + + # use the retry handlers built into the slack sdk + connection_error_retry_handler = ConnectionErrorRetryHandler() + rate_limit_error_retry_handler = RateLimitErrorRetryHandler(max_retry_count=7) + slack_retry_handlers: list[RetryHandler] = [ + connection_error_retry_handler, + rate_limit_error_retry_handler, + ] + return TenantSocketModeClient( # This app-level token will be used only for establishing a connection app_token=slack_bot_tokens.app_token, - web_client=WebClient(token=slack_bot_tokens.bot_token), + web_client=WebClient( + token=slack_bot_tokens.bot_token, retry_handlers=slack_retry_handlers + ), tenant_id=tenant_id, slack_bot_id=slack_bot_id, ) diff --git a/backend/onyx/onyxbot/slack/utils.py b/backend/onyx/onyxbot/slack/utils.py index ee942fa99cee..ad7dbe2796d5 100644 --- a/backend/onyx/onyxbot/slack/utils.py +++ b/backend/onyx/onyxbot/slack/utils.py @@ -30,7 +30,6 @@ from onyx.configs.onyxbot_configs import ( from onyx.configs.onyxbot_configs import ( DANSWER_BOT_RESPONSE_LIMIT_TIME_PERIOD_SECONDS, ) -from onyx.connectors.slack.utils import make_slack_api_rate_limited from onyx.connectors.slack.utils import SlackTextCleaner from onyx.db.engine import get_session_with_current_tenant from onyx.db.users import get_user_by_email @@ -125,13 +124,18 @@ def update_emote_react( ) return - func = client.reactions_remove if remove else client.reactions_add - slack_call = make_slack_api_rate_limited(func) # type: ignore - slack_call( - name=emoji, - channel=channel, - timestamp=message_ts, - ) + if remove: + client.reactions_remove( + name=emoji, + channel=channel, + timestamp=message_ts, + ) + else: + client.reactions_add( + name=emoji, + channel=channel, + timestamp=message_ts, + ) except SlackApiError as e: if remove: logger.error(f"Failed to remove Reaction due to: {e}") @@ -200,9 +204,8 @@ def respond_in_thread_or_channel( message_ids: list[str] = [] if not receiver_ids: - slack_call = make_slack_api_rate_limited(client.chat_postMessage) try: - response = slack_call( + response = client.chat_postMessage( channel=channel, text=text, blocks=blocks, @@ -224,7 +227,7 @@ def respond_in_thread_or_channel( blocks_without_urls.append(_build_error_block(str(e))) # Try again wtihout blocks containing url - response = slack_call( + response = client.chat_postMessage( channel=channel, text=text, blocks=blocks_without_urls, @@ -236,11 +239,9 @@ def respond_in_thread_or_channel( message_ids.append(response["message_ts"]) else: - slack_call = make_slack_api_rate_limited(client.chat_postEphemeral) - for receiver in receiver_ids: try: - response = slack_call( + response = client.chat_postEphemeral( channel=channel, user=receiver, text=text, @@ -263,7 +264,7 @@ def respond_in_thread_or_channel( blocks_without_urls.append(_build_error_block(str(e))) # Try again wtihout blocks containing url - response = slack_call( + response = client.chat_postEphemeral( channel=channel, user=receiver, text=text, @@ -500,7 +501,7 @@ def fetch_user_semantic_id_from_id( if not user_id: return None - response = make_slack_api_rate_limited(client.users_info)(user=user_id) + response = client.users_info(user=user_id) if not response["ok"]: return None