arcade-mcp/toolkits/slack/arcade_slack/tools/chat.py
Nate Barbettini ab703b75ef
Retryable errors (#20)
Co-authored-by: Sterling Dreyer <sdreyer@ucsc.edu>
2024-08-22 16:17:15 -07:00

122 lines
4.3 KiB
Python

import time
from typing import Annotated
from arcade.core.errors import ToolExecutionError, RetryableToolError
from arcade.core.schema import ToolContext
from arcade.sdk import tool
from arcade.sdk.auth import SlackUser
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
@tool(
requires_auth=SlackUser(
scope=["chat:write", "im:write", "users.profile:read", "users:read"],
)
)
def send_dm_to_user(
context: ToolContext,
user_name: Annotated[str, "The Slack username of the person you want to message"],
message: Annotated[str, "The message you want to send"],
):
"""Send a direct message to a user in Slack."""
slackClient = WebClient(token=context.authorization.token)
try:
# Step 1: Retrieve the user's Slack ID based on their username
userListResponse = slackClient.users_list()
user_id = None
for user in userListResponse["members"]:
if user["name"].lower() == user_name.lower():
user_id = user["id"]
break
if not user_id:
# does this end up as a developerMessage?
# does it end up in the LLM context?
# provide the dev an Error type that controls what ends up in the LLM context
# TODO make the sleep configurable and sent to the engine
time.sleep(0.5) # Wait for half a second
raise RetryableToolError(
"User not found",
developer_message=f"User with username '{user_name}' not found.",
additional_prompt_content=format_users(userListResponse),
)
# Step 2: Retrieve the DM channel ID with the user
im_response = slackClient.conversations_open(users=[user_id])
dm_channel_id = im_response["channel"]["id"]
# Step 3: Send the message as if it's from you (because we're using a user token)
slackClient.chat_postMessage(channel=dm_channel_id, text=message)
except SlackApiError as e:
raise ToolExecutionError(
f"Error sending message: {e.response['error']}",
developer_message="Error sending message",
)
def format_users(userListResponse: dict) -> str:
csv_string = "All active Slack users:\n\nid,name,real_name\n"
for user in userListResponse["members"]:
if not user.get("deleted", False):
user_id = user.get("id", "")
name = user.get("name", "")
real_name = user.get("profile", {}).get("real_name", "")
csv_string += f"{user_id},{name},{real_name}\n"
return csv_string.strip()
@tool(
requires_auth=SlackUser(
scope=["chat:write", "channels:read", "groups:read"],
)
)
def send_message_to_channel(
context: ToolContext,
channel_name: Annotated[
str, "The Slack channel name where you want to send the message"
],
message: Annotated[str, "The message you want to send"],
):
"""Send a message to a channel in Slack."""
slackClient = WebClient(token=context.authorization.token)
try:
# Step 1: Retrieve the list of channels
channels_response = slackClient.conversations_list()
channel_id = None
for channel in channels_response["channels"]:
if channel["name"].lower() == channel_name.lower():
channel_id = channel["id"]
break
if not channel_id:
time.sleep(0.5) # Wait for half a second
raise RetryableToolError(
"Channel not found",
developer_message=f"Channel with name '{channel_name}' not found.",
additional_prompt_content=format_channels(channels_response),
)
# Step 2: Send the message to the channel
slackClient.chat_postMessage(channel=channel_id, text=message)
except SlackApiError as e:
raise ToolExecutionError(
f"Error sending message: {e.response['error']}",
developer_message="Error sending message",
)
def format_channels(channels_response: dict) -> str:
csv_string = "All active Slack channels:\n\nid,name\n"
for channel in channels_response["channels"]:
if not channel.get("is_archived", False):
channel_id = channel.get("id", "")
name = channel.get("name", "")
csv_string += f"{channel_id},{name}\n"
return csv_string.strip()