arcade-slack 0.4.6__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arcade_slack/constants.py +7 -2
- arcade_slack/conversation_retrieval.py +74 -0
- arcade_slack/custom_types.py +4 -4
- arcade_slack/exceptions.py +0 -20
- arcade_slack/message_retrieval.py +76 -0
- arcade_slack/models.py +165 -1
- arcade_slack/tools/chat.py +675 -538
- arcade_slack/tools/users.py +59 -47
- arcade_slack/user_retrieval.py +214 -0
- arcade_slack/utils.py +238 -79
- {arcade_slack-0.4.6.dist-info → arcade_slack-0.5.1.dist-info}/METADATA +1 -1
- arcade_slack-0.5.1.dist-info/RECORD +17 -0
- arcade_slack-0.4.6.dist-info/RECORD +0 -14
- {arcade_slack-0.4.6.dist-info → arcade_slack-0.5.1.dist-info}/WHEEL +0 -0
- {arcade_slack-0.4.6.dist-info → arcade_slack-0.5.1.dist-info}/licenses/LICENSE +0 -0
arcade_slack/tools/chat.py
CHANGED
|
@@ -1,137 +1,515 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from datetime import datetime, timezone
|
|
3
1
|
from typing import Annotated, cast
|
|
4
2
|
|
|
5
3
|
from arcade_tdk import ToolContext, tool
|
|
6
4
|
from arcade_tdk.auth import Slack
|
|
7
|
-
from arcade_tdk.errors import
|
|
5
|
+
from arcade_tdk.errors import ToolExecutionError
|
|
8
6
|
from slack_sdk.errors import SlackApiError
|
|
9
7
|
from slack_sdk.web.async_client import AsyncWebClient
|
|
10
8
|
|
|
11
|
-
from arcade_slack.constants import
|
|
12
|
-
from arcade_slack.
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
from arcade_slack.constants import MAX_PAGINATION_SIZE_LIMIT
|
|
10
|
+
from arcade_slack.conversation_retrieval import (
|
|
11
|
+
get_channel_by_name,
|
|
12
|
+
get_conversation_by_id,
|
|
15
13
|
)
|
|
14
|
+
from arcade_slack.message_retrieval import retrieve_messages_in_conversation
|
|
16
15
|
from arcade_slack.models import (
|
|
17
16
|
ConversationType,
|
|
18
|
-
SlackUserList,
|
|
19
17
|
)
|
|
20
|
-
from arcade_slack.
|
|
18
|
+
from arcade_slack.user_retrieval import (
|
|
19
|
+
get_users_by_id,
|
|
20
|
+
get_users_by_id_username_or_email,
|
|
21
|
+
)
|
|
21
22
|
from arcade_slack.utils import (
|
|
22
23
|
async_paginate,
|
|
23
|
-
convert_conversation_type_to_slack_name,
|
|
24
|
-
convert_datetime_to_unix_timestamp,
|
|
25
|
-
convert_relative_datetime_to_unix_timestamp,
|
|
26
|
-
enrich_message_datetime,
|
|
27
24
|
extract_conversation_metadata,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
retrieve_conversations_by_user_ids,
|
|
25
|
+
populate_users_in_messages,
|
|
26
|
+
raise_for_users_not_found,
|
|
31
27
|
)
|
|
32
28
|
|
|
33
29
|
|
|
34
30
|
@tool(
|
|
35
31
|
requires_auth=Slack(
|
|
36
32
|
scopes=[
|
|
33
|
+
"channels:read",
|
|
34
|
+
"groups:read",
|
|
35
|
+
"mpim:read",
|
|
36
|
+
"im:read",
|
|
37
|
+
"users:read",
|
|
38
|
+
"users:read.email",
|
|
37
39
|
"chat:write",
|
|
40
|
+
# "mpim:write",
|
|
38
41
|
"im:write",
|
|
39
|
-
|
|
42
|
+
],
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
async def send_message(
|
|
46
|
+
context: ToolContext,
|
|
47
|
+
message: Annotated[str, "The content of the message to send."],
|
|
48
|
+
channel_name: Annotated[
|
|
49
|
+
str | None,
|
|
50
|
+
"The channel name to send the message to. Prefer providing a conversation_id, "
|
|
51
|
+
"when available, since the performance is better.",
|
|
52
|
+
] = None,
|
|
53
|
+
conversation_id: Annotated[str | None, "The conversation ID to send the message to."] = None,
|
|
54
|
+
user_ids: Annotated[list[str] | None, "The Slack user IDs of the people to message."] = None,
|
|
55
|
+
emails: Annotated[list[str] | None, "The emails of the people to message."] = None,
|
|
56
|
+
usernames: Annotated[
|
|
57
|
+
list[str] | None,
|
|
58
|
+
"The Slack usernames of the people to message. Prefer providing user_ids and/or emails, "
|
|
59
|
+
"when available, since the performance is better.",
|
|
60
|
+
] = None,
|
|
61
|
+
) -> Annotated[dict, "The response from the Slack API"]:
|
|
62
|
+
"""Send a message to a Channel, Direct Message (IM/DM), or Multi-Person (MPIM) conversation
|
|
63
|
+
|
|
64
|
+
Provide exactly one of:
|
|
65
|
+
- channel_name; or
|
|
66
|
+
- conversation_id; or
|
|
67
|
+
- any combination of user_ids, usernames, and/or emails.
|
|
68
|
+
|
|
69
|
+
In case multiple user_ids, usernames, and/or emails are provided, the tool will open a
|
|
70
|
+
multi-person conversation with the specified people and send the message to it.
|
|
71
|
+
"""
|
|
72
|
+
if conversation_id and any([channel_name, user_ids, usernames, emails]):
|
|
73
|
+
raise ToolExecutionError(
|
|
74
|
+
"Provide exactly one of: channel_name, OR conversation_id, OR any combination of "
|
|
75
|
+
"user_ids, usernames, and/or emails."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if not conversation_id:
|
|
79
|
+
conversation = await get_conversation_metadata(
|
|
80
|
+
context=context,
|
|
81
|
+
channel_name=channel_name,
|
|
82
|
+
user_ids=user_ids,
|
|
83
|
+
usernames=usernames,
|
|
84
|
+
emails=emails,
|
|
85
|
+
)
|
|
86
|
+
conversation_id = conversation["id"]
|
|
87
|
+
|
|
88
|
+
slack_client = AsyncWebClient(token=context.get_auth_token_or_empty())
|
|
89
|
+
response = await slack_client.chat_postMessage(channel=cast(str, conversation_id), text=message)
|
|
90
|
+
return {"success": True, "data": response.data}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@tool(
|
|
94
|
+
requires_auth=Slack(
|
|
95
|
+
scopes=[
|
|
96
|
+
"channels:read",
|
|
97
|
+
"groups:read",
|
|
98
|
+
"im:read",
|
|
99
|
+
"mpim:read",
|
|
40
100
|
"users:read",
|
|
101
|
+
"users:read.email",
|
|
41
102
|
],
|
|
42
103
|
)
|
|
43
104
|
)
|
|
44
|
-
async def
|
|
105
|
+
async def get_users_in_conversation(
|
|
45
106
|
context: ToolContext,
|
|
46
|
-
|
|
47
|
-
|
|
107
|
+
conversation_id: Annotated[str | None, "The ID of the conversation to get users in."] = None,
|
|
108
|
+
channel_name: Annotated[
|
|
109
|
+
str | None,
|
|
110
|
+
"The name of the channel to get users in. Prefer providing a conversation_id, "
|
|
111
|
+
"when available, since the performance is better.",
|
|
112
|
+
] = None,
|
|
113
|
+
# The user object is relatively small, so we allow a higher limit.
|
|
114
|
+
limit: Annotated[
|
|
115
|
+
int, "The maximum number of users to return. Defaults to 200. Maximum is 500."
|
|
116
|
+
] = 200,
|
|
117
|
+
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
118
|
+
) -> Annotated[dict, "Information about each user in the conversation"]:
|
|
119
|
+
"""Get the users in a Slack conversation (Channel, DM/IM, or MPIM) by its ID or by channel name.
|
|
120
|
+
|
|
121
|
+
Provide exactly one of conversation_id or channel_name. Prefer providing a conversation_id,
|
|
122
|
+
when available, since the performance is better.
|
|
123
|
+
"""
|
|
124
|
+
limit = max(1, min(limit, 500))
|
|
125
|
+
|
|
126
|
+
if sum({bool(conversation_id), bool(channel_name)}) != 1:
|
|
127
|
+
raise ToolExecutionError("Provide exactly one of conversation_id OR channel_name.")
|
|
128
|
+
|
|
129
|
+
auth_token = context.get_auth_token_or_empty()
|
|
130
|
+
|
|
131
|
+
if not conversation_id:
|
|
132
|
+
channel = await get_channel_by_name(auth_token, cast(str, channel_name))
|
|
133
|
+
conversation_id = channel["id"]
|
|
134
|
+
|
|
135
|
+
slack_client = AsyncWebClient(token=auth_token)
|
|
136
|
+
user_ids, next_cursor = await async_paginate(
|
|
137
|
+
func=slack_client.conversations_members,
|
|
138
|
+
response_key="members",
|
|
139
|
+
limit=limit,
|
|
140
|
+
next_cursor=next_cursor,
|
|
141
|
+
channel=conversation_id,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
response = await get_users_by_id(auth_token, user_ids)
|
|
145
|
+
|
|
146
|
+
await raise_for_users_not_found(context, [response])
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
"users": [user for user in response["users"] if not user.get("is_bot")],
|
|
150
|
+
"next_cursor": next_cursor,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@tool(
|
|
155
|
+
requires_auth=Slack(
|
|
156
|
+
scopes=[
|
|
157
|
+
"channels:read",
|
|
158
|
+
"groups:read",
|
|
159
|
+
"mpim:read",
|
|
160
|
+
"im:read",
|
|
161
|
+
"users:read",
|
|
162
|
+
"users:read.email",
|
|
163
|
+
"channels:history",
|
|
164
|
+
"groups:history",
|
|
165
|
+
"mpim:history",
|
|
166
|
+
"im:history",
|
|
167
|
+
]
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
async def get_messages(
|
|
171
|
+
context: ToolContext,
|
|
172
|
+
conversation_id: Annotated[
|
|
173
|
+
str | None,
|
|
174
|
+
"The ID of the conversation to get messages from. Provide exactly one of conversation_id "
|
|
175
|
+
"OR any combination of user_ids, usernames, and/or emails.",
|
|
176
|
+
] = None,
|
|
177
|
+
channel_name: Annotated[
|
|
178
|
+
str | None,
|
|
179
|
+
"The name of the channel to get messages from. Prefer providing a conversation_id, "
|
|
180
|
+
"when available, since the performance is better.",
|
|
181
|
+
] = None,
|
|
182
|
+
user_ids: Annotated[
|
|
183
|
+
list[str] | None, "The IDs of the users in the conversation to get messages from."
|
|
184
|
+
] = None,
|
|
185
|
+
usernames: Annotated[
|
|
186
|
+
list[str] | None,
|
|
187
|
+
"The usernames of the users in the conversation to get messages from. Prefer providing"
|
|
188
|
+
"user_ids and/or emails, when available, since the performance is better.",
|
|
189
|
+
] = None,
|
|
190
|
+
emails: Annotated[
|
|
191
|
+
list[str] | None,
|
|
192
|
+
"The emails of the users in the conversation to get messages from.",
|
|
193
|
+
] = None,
|
|
194
|
+
oldest_relative: Annotated[
|
|
195
|
+
str | None,
|
|
48
196
|
(
|
|
49
|
-
"The
|
|
50
|
-
"
|
|
197
|
+
"The oldest message to include in the results, specified as a time offset from the "
|
|
198
|
+
"current time in the format 'DD:HH:MM'"
|
|
51
199
|
),
|
|
52
|
-
],
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
200
|
+
] = None,
|
|
201
|
+
latest_relative: Annotated[
|
|
202
|
+
str | None,
|
|
203
|
+
(
|
|
204
|
+
"The latest message to include in the results, specified as a time offset from the "
|
|
205
|
+
"current time in the format 'DD:HH:MM'"
|
|
206
|
+
),
|
|
207
|
+
] = None,
|
|
208
|
+
oldest_datetime: Annotated[
|
|
209
|
+
str | None,
|
|
210
|
+
(
|
|
211
|
+
"The oldest message to include in the results, specified as a datetime object in the "
|
|
212
|
+
"format 'YYYY-MM-DD HH:MM:SS'"
|
|
213
|
+
),
|
|
214
|
+
] = None,
|
|
215
|
+
latest_datetime: Annotated[
|
|
216
|
+
str | None,
|
|
217
|
+
(
|
|
218
|
+
"The latest message to include in the results, specified as a datetime object in the "
|
|
219
|
+
"format 'YYYY-MM-DD HH:MM:SS'"
|
|
220
|
+
),
|
|
221
|
+
] = None,
|
|
222
|
+
limit: Annotated[
|
|
223
|
+
# The message object can be relatively large, so we limit maximum to 100
|
|
224
|
+
# to preserve LLM's context window and reduce the likelihood of hallucinations.
|
|
225
|
+
int, "The maximum number of messages to return. Defaults to 20. Maximum is 100."
|
|
226
|
+
] = 20,
|
|
227
|
+
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
228
|
+
) -> Annotated[
|
|
229
|
+
dict,
|
|
230
|
+
"The messages in a Slack Channel, DM (direct message) or MPIM (multi-person) conversation.",
|
|
231
|
+
]:
|
|
232
|
+
"""Get messages in a Slack Channel, DM (direct message) or MPIM (multi-person) conversation.
|
|
233
|
+
|
|
234
|
+
Provide exactly one of:
|
|
235
|
+
- conversation_id; or
|
|
236
|
+
- channel_name; or
|
|
237
|
+
- any combination of user_ids, usernames, and/or emails.
|
|
238
|
+
|
|
239
|
+
To filter messages by an absolute datetime, use 'oldest_datetime' and/or 'latest_datetime'. If
|
|
240
|
+
only 'oldest_datetime' is provided, it will return messages from the oldest_datetime to the
|
|
241
|
+
current time. If only 'latest_datetime' is provided, it will return messages since the
|
|
242
|
+
beginning of the conversation to the latest_datetime.
|
|
243
|
+
|
|
244
|
+
To filter messages by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
245
|
+
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it will
|
|
246
|
+
return messages from the oldest_relative to the current time. If only 'latest_relative' is
|
|
247
|
+
provided, it will return messages from the current time to the latest_relative.
|
|
248
|
+
|
|
249
|
+
Do not provide both 'oldest_datetime' and 'oldest_relative' or both 'latest_datetime' and
|
|
250
|
+
'latest_relative'.
|
|
56
251
|
|
|
57
|
-
|
|
58
|
-
|
|
252
|
+
Leave all arguments with the default None to get messages without date/time filtering"""
|
|
253
|
+
limit = max(1, min(limit, 100))
|
|
254
|
+
|
|
255
|
+
if not conversation_id:
|
|
256
|
+
conversation = await get_conversation_metadata(
|
|
257
|
+
context=context,
|
|
258
|
+
channel_name=channel_name,
|
|
259
|
+
user_ids=user_ids,
|
|
260
|
+
usernames=usernames,
|
|
261
|
+
emails=emails,
|
|
262
|
+
)
|
|
263
|
+
conversation_id = conversation["id"]
|
|
264
|
+
|
|
265
|
+
response = await retrieve_messages_in_conversation(
|
|
266
|
+
auth_token=context.get_auth_token_or_empty(),
|
|
267
|
+
conversation_id=cast(str, conversation_id),
|
|
268
|
+
oldest_relative=oldest_relative,
|
|
269
|
+
latest_relative=latest_relative,
|
|
270
|
+
oldest_datetime=oldest_datetime,
|
|
271
|
+
latest_datetime=latest_datetime,
|
|
272
|
+
limit=limit,
|
|
273
|
+
next_cursor=next_cursor,
|
|
59
274
|
)
|
|
60
|
-
|
|
275
|
+
|
|
276
|
+
response["messages"] = await populate_users_in_messages(
|
|
277
|
+
auth_token=context.get_auth_token_or_empty(),
|
|
278
|
+
messages=response["messages"],
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
return cast(dict, response)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@tool(
|
|
285
|
+
requires_auth=Slack(
|
|
286
|
+
scopes=[
|
|
287
|
+
"channels:read",
|
|
288
|
+
"groups:read",
|
|
289
|
+
"mpim:read",
|
|
290
|
+
"im:read",
|
|
291
|
+
"users:read",
|
|
292
|
+
"users:read.email",
|
|
293
|
+
],
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
async def get_conversation_metadata(
|
|
297
|
+
context: ToolContext,
|
|
298
|
+
conversation_id: Annotated[str | None, "The ID of the conversation to get metadata for"] = None,
|
|
299
|
+
channel_name: Annotated[
|
|
300
|
+
str | None,
|
|
301
|
+
"The name of the channel to get metadata for. Prefer providing a conversation_id, "
|
|
302
|
+
"when available, since the performance is better.",
|
|
303
|
+
] = None,
|
|
304
|
+
usernames: Annotated[
|
|
305
|
+
list[str] | None,
|
|
306
|
+
"The usernames of the users to get the conversation metadata. "
|
|
307
|
+
"Prefer providing user_ids and/or emails, when available, since the performance is better.",
|
|
308
|
+
] = None,
|
|
309
|
+
emails: Annotated[
|
|
310
|
+
list[str] | None,
|
|
311
|
+
"The emails of the users to get the conversation metadata.",
|
|
312
|
+
] = None,
|
|
313
|
+
user_ids: Annotated[
|
|
314
|
+
list[str] | None,
|
|
315
|
+
"The IDs of the users to get the conversation metadata.",
|
|
316
|
+
] = None,
|
|
317
|
+
) -> Annotated[
|
|
318
|
+
dict | None,
|
|
319
|
+
"The conversation metadata.",
|
|
320
|
+
]:
|
|
321
|
+
"""Get metadata of a Channel, a Direct Message (IM / DM) or a Multi-Person (MPIM) conversation.
|
|
322
|
+
|
|
323
|
+
Use this tool to retrieve metadata about a conversation with a conversation_id, a channel name,
|
|
324
|
+
or by the user_id(s), username(s), and/or email(s) of the user(s) in the conversation.
|
|
325
|
+
|
|
326
|
+
This tool does not return the messages in a conversation. To get the messages, use the
|
|
327
|
+
'Slack.GetMessages' tool instead.
|
|
328
|
+
|
|
329
|
+
Provide exactly one of:
|
|
330
|
+
- conversation_id; or
|
|
331
|
+
- channel_name; or
|
|
332
|
+
- any combination of user_ids, usernames, and/or emails.
|
|
333
|
+
"""
|
|
334
|
+
if bool(conversation_id) + bool(channel_name) + any([user_ids, usernames, emails]) > 1:
|
|
335
|
+
raise ToolExecutionError(
|
|
336
|
+
"Provide exactly one of: conversation_id, OR channel_name, OR any combination of "
|
|
337
|
+
"user_ids, usernames, and/or emails."
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
auth_token = context.get_auth_token_or_empty()
|
|
341
|
+
|
|
342
|
+
if conversation_id:
|
|
343
|
+
return await get_conversation_by_id(auth_token, conversation_id)
|
|
344
|
+
|
|
345
|
+
elif channel_name:
|
|
346
|
+
return await get_channel_by_name(auth_token, channel_name)
|
|
347
|
+
|
|
348
|
+
user_ids_list = user_ids if isinstance(user_ids, list) else []
|
|
349
|
+
|
|
350
|
+
slack_client = AsyncWebClient(token=auth_token)
|
|
61
351
|
|
|
62
352
|
try:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
retry_after_ms=500, # Play nice with Slack API rate limits
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Step 2: Retrieve the DM channel ID with the user
|
|
83
|
-
im_response = await slackClient.conversations_open(users=[user_id])
|
|
84
|
-
dm_channel_id = im_response["channel"]["id"]
|
|
85
|
-
|
|
86
|
-
# Step 3: Send the message as if it's from you (because we're using a user token)
|
|
87
|
-
response = await slackClient.chat_postMessage(channel=dm_channel_id, text=message)
|
|
353
|
+
current_user = await slack_client.auth_test()
|
|
354
|
+
except SlackApiError as e:
|
|
355
|
+
message = "Failed to get currently authenticated user's info."
|
|
356
|
+
developer_message = f"{message} Slack error: '{e.response.get('error', 'unknown_error')}'"
|
|
357
|
+
raise ToolExecutionError(message, developer_message)
|
|
358
|
+
|
|
359
|
+
if current_user["user_id"] not in user_ids_list:
|
|
360
|
+
user_ids_list.append(current_user["user_id"])
|
|
361
|
+
|
|
362
|
+
if usernames or emails:
|
|
363
|
+
other_users = await get_users_by_id_username_or_email(
|
|
364
|
+
context=context,
|
|
365
|
+
usernames=usernames,
|
|
366
|
+
emails=emails,
|
|
367
|
+
)
|
|
368
|
+
user_ids_list.extend([user["id"] for user in other_users])
|
|
88
369
|
|
|
370
|
+
try:
|
|
371
|
+
response = await slack_client.conversations_open(users=user_ids_list, return_im=True)
|
|
372
|
+
return dict(**extract_conversation_metadata(response["channel"]))
|
|
89
373
|
except SlackApiError as e:
|
|
90
|
-
|
|
374
|
+
message = "Failed to retrieve conversation metadata."
|
|
375
|
+
slack_error = e.response.get("error", "unknown_error")
|
|
91
376
|
raise ToolExecutionError(
|
|
92
|
-
|
|
93
|
-
developer_message=f"Slack
|
|
377
|
+
message=message,
|
|
378
|
+
developer_message=f"{message} Slack error: '{slack_error}'",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@tool(
|
|
383
|
+
requires_auth=Slack(
|
|
384
|
+
scopes=["channels:read", "groups:read", "im:read", "mpim:read"],
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
async def list_conversations(
|
|
388
|
+
context: ToolContext,
|
|
389
|
+
conversation_types: Annotated[
|
|
390
|
+
list[ConversationType] | None,
|
|
391
|
+
"Optionally filter by the type(s) of conversations. Defaults to None (all types).",
|
|
392
|
+
] = None,
|
|
393
|
+
# The conversation object is relatively small, so we allow a higher limit.
|
|
394
|
+
limit: Annotated[
|
|
395
|
+
int,
|
|
396
|
+
f"The maximum number of conversations to list. Defaults to {MAX_PAGINATION_SIZE_LIMIT}. "
|
|
397
|
+
"Maximum is 500.",
|
|
398
|
+
] = MAX_PAGINATION_SIZE_LIMIT,
|
|
399
|
+
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
400
|
+
) -> Annotated[dict, "The list of conversations found with metadata"]:
|
|
401
|
+
"""List metadata for Slack conversations (channels, DMs, MPIMs) the user is a member of.
|
|
402
|
+
|
|
403
|
+
This tool does not return the messages in a conversation. To get the messages, use the
|
|
404
|
+
'Slack.GetMessages' tool instead. Calling this tool when the user is asking for messages
|
|
405
|
+
will release too much CO2 in the atmosphere and contribute to global warming.
|
|
406
|
+
"""
|
|
407
|
+
limit = max(1, min(limit, 500))
|
|
408
|
+
|
|
409
|
+
if conversation_types:
|
|
410
|
+
conversation_types_filter = ",".join(
|
|
411
|
+
conversation_type.to_slack_name_str() for conversation_type in conversation_types
|
|
94
412
|
)
|
|
95
413
|
else:
|
|
96
|
-
|
|
414
|
+
conversation_types_filter = None
|
|
415
|
+
|
|
416
|
+
slack_client = AsyncWebClient(token=context.get_auth_token_or_empty())
|
|
417
|
+
|
|
418
|
+
results, next_cursor = await async_paginate(
|
|
419
|
+
slack_client.conversations_list,
|
|
420
|
+
"channels",
|
|
421
|
+
limit=limit,
|
|
422
|
+
next_cursor=next_cursor,
|
|
423
|
+
types=conversation_types_filter,
|
|
424
|
+
exclude_archived=True,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
"conversations": [
|
|
429
|
+
dict(**extract_conversation_metadata(conversation))
|
|
430
|
+
for conversation in results
|
|
431
|
+
if conversation.get("is_im") or conversation.get("is_member")
|
|
432
|
+
],
|
|
433
|
+
"next_cursor": next_cursor,
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
##################################################################################
|
|
438
|
+
# NOTE: The tools below are kept here for backwards compatibility. Prefer using: #
|
|
439
|
+
# - send_message
|
|
440
|
+
# - get_messages
|
|
441
|
+
# - get_conversation_metadata
|
|
442
|
+
# - get_users_in_conversation
|
|
443
|
+
# - list_conversations
|
|
444
|
+
##################################################################################
|
|
97
445
|
|
|
98
446
|
|
|
99
447
|
@tool(
|
|
100
448
|
requires_auth=Slack(
|
|
101
449
|
scopes=[
|
|
102
|
-
"chat:write",
|
|
103
450
|
"channels:read",
|
|
104
451
|
"groups:read",
|
|
452
|
+
"mpim:read",
|
|
453
|
+
"im:read",
|
|
454
|
+
"users:read",
|
|
455
|
+
"users:read.email",
|
|
456
|
+
"chat:write",
|
|
457
|
+
# "mpim:write",
|
|
458
|
+
"im:write",
|
|
105
459
|
],
|
|
106
460
|
)
|
|
107
461
|
)
|
|
108
|
-
async def
|
|
462
|
+
async def send_dm_to_user(
|
|
109
463
|
context: ToolContext,
|
|
110
|
-
|
|
464
|
+
user_name: Annotated[
|
|
465
|
+
str,
|
|
466
|
+
(
|
|
467
|
+
"The Slack username of the person you want to message. "
|
|
468
|
+
"Slack usernames are ALWAYS lowercase."
|
|
469
|
+
),
|
|
470
|
+
],
|
|
111
471
|
message: Annotated[str, "The message you want to send"],
|
|
112
472
|
) -> Annotated[dict, "The response from the Slack API"]:
|
|
113
|
-
"""Send a message to a
|
|
473
|
+
"""Send a direct message to a user in Slack.
|
|
114
474
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
475
|
+
This tool is deprecated. Use `Slack.SendMessage` instead.
|
|
476
|
+
"""
|
|
477
|
+
return await send_message( # type: ignore[no-any-return]
|
|
478
|
+
context=context,
|
|
479
|
+
usernames=[user_name],
|
|
480
|
+
message=message,
|
|
481
|
+
)
|
|
121
482
|
|
|
122
|
-
channel = await get_channel_metadata_by_name(context=context, channel_name=channel_name)
|
|
123
|
-
channel_id = channel["id"]
|
|
124
483
|
|
|
125
|
-
|
|
484
|
+
@tool(
|
|
485
|
+
requires_auth=Slack(
|
|
486
|
+
scopes=[
|
|
487
|
+
"channels:read",
|
|
488
|
+
"groups:read",
|
|
489
|
+
"mpim:read",
|
|
490
|
+
"im:read",
|
|
491
|
+
"users:read",
|
|
492
|
+
"users:read.email",
|
|
493
|
+
"chat:write",
|
|
494
|
+
# "mpim:write",
|
|
495
|
+
"im:write",
|
|
496
|
+
],
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
async def send_message_to_channel(
|
|
500
|
+
context: ToolContext,
|
|
501
|
+
channel_name: Annotated[str, "The Slack channel name where you want to send the message. "],
|
|
502
|
+
message: Annotated[str, "The message you want to send"],
|
|
503
|
+
) -> Annotated[dict, "The response from the Slack API"]:
|
|
504
|
+
"""Send a message to a channel in Slack.
|
|
126
505
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return {"response": response.data}
|
|
506
|
+
This tool is deprecated. Use `Slack.SendMessage` instead.
|
|
507
|
+
"""
|
|
508
|
+
return await send_message( # type: ignore[no-any-return]
|
|
509
|
+
context=context,
|
|
510
|
+
channel_name=channel_name,
|
|
511
|
+
message=message,
|
|
512
|
+
)
|
|
135
513
|
|
|
136
514
|
|
|
137
515
|
@tool(
|
|
@@ -152,46 +530,19 @@ async def get_members_in_conversation_by_id(
|
|
|
152
530
|
limit: Annotated[int | None, "The maximum number of members to return."] = None,
|
|
153
531
|
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
154
532
|
) -> Annotated[dict, "Information about each member in the conversation"]:
|
|
155
|
-
"""Get the members of a conversation in Slack by the conversation's ID.
|
|
156
|
-
token = (
|
|
157
|
-
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
158
|
-
)
|
|
159
|
-
slackClient = AsyncWebClient(token=token)
|
|
533
|
+
"""Get the members of a conversation in Slack by the conversation's ID.
|
|
160
534
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
available_conversations = ", ".join(
|
|
173
|
-
f"{conversation['id']} ({conversation['name']})"
|
|
174
|
-
for conversation in conversations["conversations"]
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
raise RetryableToolError(
|
|
178
|
-
"Conversation not found",
|
|
179
|
-
developer_message=f"Conversation with ID '{conversation_id}' not found.",
|
|
180
|
-
additional_prompt_content=f"Available conversations: {available_conversations}",
|
|
181
|
-
retry_after_ms=500,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
# Get the members' info
|
|
185
|
-
# TODO: This will probably hit rate limits. We should probably call list_users() and
|
|
186
|
-
# then filter the results instead.
|
|
187
|
-
members = await asyncio.gather(*[
|
|
188
|
-
get_user_info_by_id(context, member_id) for member_id in member_ids
|
|
189
|
-
])
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
"members": [member for member in members if not member.get("is_bot")],
|
|
193
|
-
"next_cursor": next_cursor,
|
|
194
|
-
}
|
|
535
|
+
This tool is deprecated. Use the `Slack.GetUsersInConversation` tool instead.
|
|
536
|
+
"""
|
|
537
|
+
response = await get_users_in_conversation(
|
|
538
|
+
context=context,
|
|
539
|
+
conversation_id=conversation_id,
|
|
540
|
+
limit=limit,
|
|
541
|
+
next_cursor=next_cursor,
|
|
542
|
+
)
|
|
543
|
+
response["members"] = response["users"]
|
|
544
|
+
del response["users"]
|
|
545
|
+
return cast(dict, response)
|
|
195
546
|
|
|
196
547
|
|
|
197
548
|
@tool(
|
|
@@ -212,28 +563,38 @@ async def get_members_in_channel_by_name(
|
|
|
212
563
|
limit: Annotated[int | None, "The maximum number of members to return."] = None,
|
|
213
564
|
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
214
565
|
) -> Annotated[dict, "The channel members' IDs and Names"]:
|
|
215
|
-
"""Get the members of a conversation in Slack by the conversation's name.
|
|
216
|
-
channel = await get_channel_metadata_by_name(context=context, channel_name=channel_name)
|
|
566
|
+
"""Get the members of a conversation in Slack by the conversation's name.
|
|
217
567
|
|
|
218
|
-
|
|
568
|
+
This tool is deprecated. Use the `Slack.GetUsersInConversation` tool instead.
|
|
569
|
+
"""
|
|
570
|
+
response = await get_users_in_conversation(
|
|
219
571
|
context=context,
|
|
220
|
-
|
|
572
|
+
channel_name=channel_name,
|
|
221
573
|
limit=limit,
|
|
222
574
|
next_cursor=next_cursor,
|
|
223
575
|
)
|
|
576
|
+
response["members"] = response["users"]
|
|
577
|
+
del response["users"]
|
|
578
|
+
return cast(dict, response)
|
|
224
579
|
|
|
225
580
|
|
|
226
|
-
# TODO: make the function accept a current unix timestamp argument to allow testing without
|
|
227
|
-
# mocking. Have to wait until arcade.core.annotations.Inferrable is implemented, so that we
|
|
228
|
-
# can avoid exposing this arg to the LLM.
|
|
229
581
|
@tool(
|
|
230
582
|
requires_auth=Slack(
|
|
231
|
-
scopes=[
|
|
583
|
+
scopes=[
|
|
584
|
+
"channels:history",
|
|
585
|
+
"channels:read",
|
|
586
|
+
"groups:history",
|
|
587
|
+
"groups:read",
|
|
588
|
+
"im:history",
|
|
589
|
+
"im:read",
|
|
590
|
+
"mpim:history",
|
|
591
|
+
"mpim:read",
|
|
592
|
+
],
|
|
232
593
|
)
|
|
233
594
|
)
|
|
234
|
-
async def
|
|
595
|
+
async def get_messages_in_channel_by_name(
|
|
235
596
|
context: ToolContext,
|
|
236
|
-
|
|
597
|
+
channel_name: Annotated[str, "The name of the channel"],
|
|
237
598
|
oldest_relative: Annotated[
|
|
238
599
|
str | None,
|
|
239
600
|
(
|
|
@@ -267,105 +628,48 @@ async def get_messages_in_conversation_by_id(
|
|
|
267
628
|
) -> Annotated[
|
|
268
629
|
dict,
|
|
269
630
|
(
|
|
270
|
-
"The messages in a
|
|
631
|
+
"The messages in a channel and next cursor for paginating results (when "
|
|
271
632
|
"there are additional messages to retrieve)."
|
|
272
633
|
),
|
|
273
634
|
]:
|
|
274
|
-
"""Get the messages in a
|
|
635
|
+
"""Get the messages in a channel by the channel's name.
|
|
275
636
|
|
|
276
|
-
|
|
637
|
+
This tool is deprecated. Use the `Slack.GetMessages` tool instead.
|
|
277
638
|
|
|
278
|
-
To filter by an absolute datetime, use 'oldest_datetime' and/or 'latest_datetime'. If
|
|
279
|
-
only 'oldest_datetime' is provided, it
|
|
280
|
-
current time. If only 'latest_datetime' is provided, it
|
|
281
|
-
beginning of the
|
|
639
|
+
To filter messages by an absolute datetime, use 'oldest_datetime' and/or 'latest_datetime'. If
|
|
640
|
+
only 'oldest_datetime' is provided, it will return messages from the oldest_datetime to the
|
|
641
|
+
current time. If only 'latest_datetime' is provided, it will return messages since the
|
|
642
|
+
beginning of the channel to the latest_datetime.
|
|
282
643
|
|
|
283
|
-
To filter by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
284
|
-
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it
|
|
285
|
-
messages from the oldest_relative to the current time. If only 'latest_relative' is
|
|
286
|
-
it
|
|
644
|
+
To filter messages by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
645
|
+
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it will
|
|
646
|
+
return messages from the oldest_relative to the current time. If only 'latest_relative' is
|
|
647
|
+
provided, it will return messages from the current time to the latest_relative.
|
|
287
648
|
|
|
288
649
|
Do not provide both 'oldest_datetime' and 'oldest_relative' or both 'latest_datetime' and
|
|
289
650
|
'latest_relative'.
|
|
290
651
|
|
|
291
652
|
Leave all arguments with the default None to get messages without date/time filtering"""
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if error_message:
|
|
300
|
-
raise ToolExecutionError(error_message, developer_message=error_message)
|
|
301
|
-
|
|
302
|
-
current_unix_timestamp = int(datetime.now(timezone.utc).timestamp())
|
|
303
|
-
|
|
304
|
-
if latest_relative:
|
|
305
|
-
latest_timestamp = convert_relative_datetime_to_unix_timestamp(
|
|
306
|
-
latest_relative, current_unix_timestamp
|
|
307
|
-
)
|
|
308
|
-
elif latest_datetime:
|
|
309
|
-
latest_timestamp = convert_datetime_to_unix_timestamp(latest_datetime)
|
|
310
|
-
else:
|
|
311
|
-
latest_timestamp = None
|
|
312
|
-
|
|
313
|
-
if oldest_relative:
|
|
314
|
-
oldest_timestamp = convert_relative_datetime_to_unix_timestamp(
|
|
315
|
-
oldest_relative, current_unix_timestamp
|
|
316
|
-
)
|
|
317
|
-
elif oldest_datetime:
|
|
318
|
-
oldest_timestamp = convert_datetime_to_unix_timestamp(oldest_datetime)
|
|
319
|
-
else:
|
|
320
|
-
oldest_timestamp = None
|
|
321
|
-
|
|
322
|
-
token = (
|
|
323
|
-
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
324
|
-
)
|
|
325
|
-
slackClient = AsyncWebClient(token=token)
|
|
326
|
-
|
|
327
|
-
datetime_args = {}
|
|
328
|
-
if oldest_timestamp:
|
|
329
|
-
datetime_args["oldest"] = oldest_timestamp
|
|
330
|
-
if latest_timestamp:
|
|
331
|
-
datetime_args["latest"] = latest_timestamp
|
|
332
|
-
|
|
333
|
-
response, next_cursor = await async_paginate(
|
|
334
|
-
slackClient.conversations_history,
|
|
335
|
-
"messages",
|
|
653
|
+
return await get_messages( # type: ignore[no-any-return]
|
|
654
|
+
context=context,
|
|
655
|
+
channel_name=channel_name,
|
|
656
|
+
oldest_relative=oldest_relative,
|
|
657
|
+
latest_relative=latest_relative,
|
|
658
|
+
oldest_datetime=oldest_datetime,
|
|
659
|
+
latest_datetime=latest_datetime,
|
|
336
660
|
limit=limit,
|
|
337
661
|
next_cursor=next_cursor,
|
|
338
|
-
channel=conversation_id,
|
|
339
|
-
include_all_metadata=True,
|
|
340
|
-
inclusive=True, # Include messages at the start and end of the time range
|
|
341
|
-
**datetime_args,
|
|
342
662
|
)
|
|
343
663
|
|
|
344
|
-
messages = [enrich_message_datetime(message) for message in response]
|
|
345
|
-
|
|
346
|
-
return {"messages": messages, "next_cursor": next_cursor}
|
|
347
|
-
|
|
348
664
|
|
|
349
|
-
# TODO: make the function accept a current unix timestamp argument to allow testing without
|
|
350
|
-
# mocking. Have to wait until arcade.core.annotations.Inferrable is implemented, so that we
|
|
351
|
-
# can avoid exposing this arg to the LLM.
|
|
352
665
|
@tool(
|
|
353
666
|
requires_auth=Slack(
|
|
354
|
-
scopes=[
|
|
355
|
-
"channels:history",
|
|
356
|
-
"channels:read",
|
|
357
|
-
"groups:history",
|
|
358
|
-
"groups:read",
|
|
359
|
-
"im:history",
|
|
360
|
-
"im:read",
|
|
361
|
-
"mpim:history",
|
|
362
|
-
"mpim:read",
|
|
363
|
-
],
|
|
667
|
+
scopes=["channels:history", "groups:history", "im:history", "mpim:history"],
|
|
364
668
|
)
|
|
365
669
|
)
|
|
366
|
-
async def
|
|
670
|
+
async def get_messages_in_conversation_by_id(
|
|
367
671
|
context: ToolContext,
|
|
368
|
-
|
|
672
|
+
conversation_id: Annotated[str, "The ID of the conversation to get history for"],
|
|
369
673
|
oldest_relative: Annotated[
|
|
370
674
|
str | None,
|
|
371
675
|
(
|
|
@@ -399,31 +703,33 @@ async def get_messages_in_channel_by_name(
|
|
|
399
703
|
) -> Annotated[
|
|
400
704
|
dict,
|
|
401
705
|
(
|
|
402
|
-
"The messages in a
|
|
706
|
+
"The messages in a conversation and next cursor for paginating results (when "
|
|
403
707
|
"there are additional messages to retrieve)."
|
|
404
708
|
),
|
|
405
709
|
]:
|
|
406
|
-
"""Get the messages in a
|
|
710
|
+
"""Get the messages in a conversation by the conversation's ID.
|
|
407
711
|
|
|
408
|
-
|
|
409
|
-
only 'oldest_datetime' is provided, it will return messages from the oldest_datetime to the
|
|
410
|
-
current time. If only 'latest_datetime' is provided, it will return messages since the
|
|
411
|
-
beginning of the channel to the latest_datetime.
|
|
712
|
+
This tool is deprecated. Use the 'Slack.GetMessages' tool instead.
|
|
412
713
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
provided, it
|
|
714
|
+
A conversation can be a channel, a DM, or a group DM.
|
|
715
|
+
|
|
716
|
+
To filter by an absolute datetime, use 'oldest_datetime' and/or 'latest_datetime'. If
|
|
717
|
+
only 'oldest_datetime' is provided, it returns messages from the oldest_datetime to the
|
|
718
|
+
current time. If only 'latest_datetime' is provided, it returns messages since the
|
|
719
|
+
beginning of the conversation to the latest_datetime.
|
|
720
|
+
|
|
721
|
+
To filter by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
722
|
+
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it returns
|
|
723
|
+
messages from the oldest_relative to the current time. If only 'latest_relative' is provided,
|
|
724
|
+
it returns messages from the current time to the latest_relative.
|
|
417
725
|
|
|
418
726
|
Do not provide both 'oldest_datetime' and 'oldest_relative' or both 'latest_datetime' and
|
|
419
727
|
'latest_relative'.
|
|
420
728
|
|
|
421
729
|
Leave all arguments with the default None to get messages without date/time filtering"""
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return await get_messages_in_conversation_by_id( # type: ignore[no-any-return]
|
|
730
|
+
return await get_messages( # type: ignore[no-any-return]
|
|
425
731
|
context=context,
|
|
426
|
-
conversation_id=
|
|
732
|
+
conversation_id=conversation_id,
|
|
427
733
|
oldest_relative=oldest_relative,
|
|
428
734
|
latest_relative=latest_relative,
|
|
429
735
|
oldest_datetime=oldest_datetime,
|
|
@@ -433,7 +739,7 @@ async def get_messages_in_channel_by_name(
|
|
|
433
739
|
)
|
|
434
740
|
|
|
435
741
|
|
|
436
|
-
@tool(requires_auth=Slack(scopes=["im:history", "im:read"]))
|
|
742
|
+
@tool(requires_auth=Slack(scopes=["im:history", "im:read", "users:read", "users:read.email"]))
|
|
437
743
|
async def get_messages_in_direct_message_conversation_by_username(
|
|
438
744
|
context: ToolContext,
|
|
439
745
|
username: Annotated[str, "The username of the user to get messages from"],
|
|
@@ -476,27 +782,11 @@ async def get_messages_in_direct_message_conversation_by_username(
|
|
|
476
782
|
]:
|
|
477
783
|
"""Get the messages in a direct conversation by the user's name.
|
|
478
784
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
beginning of the conversation to the latest_datetime.
|
|
483
|
-
|
|
484
|
-
To filter messages by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
485
|
-
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it will
|
|
486
|
-
return messages from the oldest_relative to the current time. If only 'latest_relative' is
|
|
487
|
-
provided, it will return messages from the current time to the latest_relative.
|
|
488
|
-
|
|
489
|
-
Do not provide both 'oldest_datetime' and 'oldest_relative' or both 'latest_datetime' and
|
|
490
|
-
'latest_relative'.
|
|
491
|
-
|
|
492
|
-
Leave all arguments with the default None to get messages without date/time filtering"""
|
|
493
|
-
direct_conversation = await get_direct_message_conversation_metadata_by_username(
|
|
494
|
-
context=context, username=username
|
|
495
|
-
)
|
|
496
|
-
|
|
497
|
-
return await get_messages_in_conversation_by_id( # type: ignore[no-any-return]
|
|
785
|
+
This tool is deprecated. Use the `Slack.GetMessages` tool instead.
|
|
786
|
+
"""
|
|
787
|
+
return await get_messages( # type: ignore[no-any-return]
|
|
498
788
|
context=context,
|
|
499
|
-
|
|
789
|
+
usernames=[username],
|
|
500
790
|
oldest_relative=oldest_relative,
|
|
501
791
|
latest_relative=latest_relative,
|
|
502
792
|
oldest_datetime=oldest_datetime,
|
|
@@ -506,7 +796,7 @@ async def get_messages_in_direct_message_conversation_by_username(
|
|
|
506
796
|
)
|
|
507
797
|
|
|
508
798
|
|
|
509
|
-
@tool(requires_auth=Slack(scopes=["im:history", "im:read"]))
|
|
799
|
+
@tool(requires_auth=Slack(scopes=["im:history", "im:read", "users:read", "users:read.email"]))
|
|
510
800
|
async def get_messages_in_multi_person_dm_conversation_by_usernames(
|
|
511
801
|
context: ToolContext,
|
|
512
802
|
usernames: Annotated[list[str], "The usernames of the users to get messages from"],
|
|
@@ -549,27 +839,11 @@ async def get_messages_in_multi_person_dm_conversation_by_usernames(
|
|
|
549
839
|
]:
|
|
550
840
|
"""Get the messages in a multi-person direct message conversation by the usernames.
|
|
551
841
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
beginning of the conversation to the latest_datetime.
|
|
556
|
-
|
|
557
|
-
To filter messages by a relative datetime (e.g. 3 days ago, 1 hour ago, etc.), use
|
|
558
|
-
'oldest_relative' and/or 'latest_relative'. If only 'oldest_relative' is provided, it will
|
|
559
|
-
return messages from the oldest_relative to the current time. If only 'latest_relative' is
|
|
560
|
-
provided, it will return messages from the current time to the latest_relative.
|
|
561
|
-
|
|
562
|
-
Do not provide both 'oldest_datetime' and 'oldest_relative' or both 'latest_datetime' and
|
|
563
|
-
'latest_relative'.
|
|
564
|
-
|
|
565
|
-
Leave all arguments with the default None to get messages without date/time filtering"""
|
|
566
|
-
direct_conversation = await get_multi_person_dm_conversation_metadata_by_usernames(
|
|
567
|
-
context=context, usernames=usernames
|
|
568
|
-
)
|
|
569
|
-
|
|
570
|
-
return await get_messages_in_conversation_by_id( # type: ignore[no-any-return]
|
|
842
|
+
This tool is deprecated. Use the `Slack.GetMessages` tool instead.
|
|
843
|
+
"""
|
|
844
|
+
return await get_messages( # type: ignore[no-any-return]
|
|
571
845
|
context=context,
|
|
572
|
-
|
|
846
|
+
usernames=usernames,
|
|
573
847
|
oldest_relative=oldest_relative,
|
|
574
848
|
latest_relative=latest_relative,
|
|
575
849
|
oldest_datetime=oldest_datetime,
|
|
@@ -579,237 +853,6 @@ async def get_messages_in_multi_person_dm_conversation_by_usernames(
|
|
|
579
853
|
)
|
|
580
854
|
|
|
581
855
|
|
|
582
|
-
@tool(
|
|
583
|
-
requires_auth=Slack(
|
|
584
|
-
scopes=["channels:read", "groups:read", "im:read", "mpim:read"],
|
|
585
|
-
)
|
|
586
|
-
)
|
|
587
|
-
async def get_conversation_metadata_by_id(
|
|
588
|
-
context: ToolContext,
|
|
589
|
-
conversation_id: Annotated[str, "The ID of the conversation to get metadata for"],
|
|
590
|
-
) -> Annotated[dict, "The conversation metadata"]:
|
|
591
|
-
"""Get the metadata of a conversation in Slack searching by its ID.
|
|
592
|
-
|
|
593
|
-
This tool does not return the messages in a conversation. To get the messages, use the
|
|
594
|
-
`get_messages_in_conversation_by_id` tool."""
|
|
595
|
-
token = (
|
|
596
|
-
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
597
|
-
)
|
|
598
|
-
slackClient = AsyncWebClient(token=token)
|
|
599
|
-
|
|
600
|
-
try:
|
|
601
|
-
response = await slackClient.conversations_info(
|
|
602
|
-
channel=conversation_id,
|
|
603
|
-
include_locale=True,
|
|
604
|
-
include_num_members=True,
|
|
605
|
-
)
|
|
606
|
-
|
|
607
|
-
except SlackApiError as e:
|
|
608
|
-
if e.response.get("error") == "channel_not_found":
|
|
609
|
-
conversations = await list_conversations_metadata(context)
|
|
610
|
-
available_conversations = ", ".join(
|
|
611
|
-
f"{conversation['id']} ({conversation['name']})"
|
|
612
|
-
for conversation in conversations["conversations"]
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
raise RetryableToolError(
|
|
616
|
-
"Conversation not found",
|
|
617
|
-
developer_message=f"Conversation with ID '{conversation_id}' not found.",
|
|
618
|
-
additional_prompt_content=f"Available conversations: {available_conversations}",
|
|
619
|
-
retry_after_ms=500,
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
raise
|
|
623
|
-
|
|
624
|
-
return dict(**extract_conversation_metadata(response["channel"]))
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
@tool(requires_auth=Slack(scopes=["channels:read", "groups:read"]))
|
|
628
|
-
async def get_channel_metadata_by_name(
|
|
629
|
-
context: ToolContext,
|
|
630
|
-
channel_name: Annotated[str, "The name of the channel to get metadata for"],
|
|
631
|
-
next_cursor: Annotated[
|
|
632
|
-
str | None,
|
|
633
|
-
"The cursor to use for pagination, if continuing from a previous search.",
|
|
634
|
-
] = None,
|
|
635
|
-
) -> Annotated[dict, "The channel metadata"]:
|
|
636
|
-
"""Get the metadata of a channel in Slack searching by its name.
|
|
637
|
-
|
|
638
|
-
This tool does not return the messages in a channel. To get the messages, use the
|
|
639
|
-
`get_messages_in_channel_by_name` tool."""
|
|
640
|
-
channel_names: list[str] = []
|
|
641
|
-
|
|
642
|
-
async def find_channel() -> dict:
|
|
643
|
-
nonlocal channel_names, channel_name, next_cursor
|
|
644
|
-
should_continue = True
|
|
645
|
-
|
|
646
|
-
while should_continue:
|
|
647
|
-
response = await list_conversations_metadata(
|
|
648
|
-
context=context,
|
|
649
|
-
conversation_types=[
|
|
650
|
-
ConversationType.PUBLIC_CHANNEL,
|
|
651
|
-
ConversationType.PRIVATE_CHANNEL,
|
|
652
|
-
],
|
|
653
|
-
next_cursor=next_cursor,
|
|
654
|
-
)
|
|
655
|
-
next_cursor = response.get("next_cursor")
|
|
656
|
-
|
|
657
|
-
for channel in response["conversations"]:
|
|
658
|
-
response_channel_name = (
|
|
659
|
-
"" if not isinstance(channel.get("name"), str) else channel["name"].lower()
|
|
660
|
-
)
|
|
661
|
-
if response_channel_name == channel_name.lower():
|
|
662
|
-
return channel # type: ignore[no-any-return]
|
|
663
|
-
channel_names.append(channel["name"])
|
|
664
|
-
|
|
665
|
-
if not next_cursor:
|
|
666
|
-
should_continue = False
|
|
667
|
-
|
|
668
|
-
raise ItemNotFoundError()
|
|
669
|
-
|
|
670
|
-
try:
|
|
671
|
-
return await asyncio.wait_for(find_channel(), timeout=MAX_PAGINATION_TIMEOUT_SECONDS)
|
|
672
|
-
except ItemNotFoundError:
|
|
673
|
-
raise RetryableToolError(
|
|
674
|
-
"Channel not found",
|
|
675
|
-
developer_message=f"Channel with name '{channel_name}' not found.",
|
|
676
|
-
additional_prompt_content=f"Available channel names: {channel_names}",
|
|
677
|
-
retry_after_ms=500,
|
|
678
|
-
)
|
|
679
|
-
except TimeoutError:
|
|
680
|
-
raise RetryableToolError(
|
|
681
|
-
"Channel not found, search timed out.",
|
|
682
|
-
developer_message=(
|
|
683
|
-
f"Channel with name '{channel_name}' not found. "
|
|
684
|
-
f"Search timed out after {MAX_PAGINATION_TIMEOUT_SECONDS} seconds."
|
|
685
|
-
),
|
|
686
|
-
additional_prompt_content=(
|
|
687
|
-
f"Other channel names found are: {channel_names}. "
|
|
688
|
-
"The list is potentially non-exhaustive, since the search process timed out. "
|
|
689
|
-
f"Use the '{list_conversations_metadata.__tool_name__}' tool to get"
|
|
690
|
-
"a comprehensive list of channels."
|
|
691
|
-
),
|
|
692
|
-
retry_after_ms=500,
|
|
693
|
-
)
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
@tool(requires_auth=Slack(scopes=["im:read"]))
|
|
697
|
-
async def get_direct_message_conversation_metadata_by_username(
|
|
698
|
-
context: ToolContext,
|
|
699
|
-
username: Annotated[str, "The username of the user/person to get messages with"],
|
|
700
|
-
next_cursor: Annotated[
|
|
701
|
-
str | None,
|
|
702
|
-
"The cursor to use for pagination, if continuing from a previous search.",
|
|
703
|
-
] = None,
|
|
704
|
-
) -> Annotated[
|
|
705
|
-
dict | None,
|
|
706
|
-
"The direct message conversation metadata.",
|
|
707
|
-
]:
|
|
708
|
-
"""Get the metadata of a direct message conversation in Slack by the username.
|
|
709
|
-
|
|
710
|
-
This tool does not return the messages in a conversation. To get the messages, use the
|
|
711
|
-
`get_messages_in_direct_message_conversation_by_username` tool."""
|
|
712
|
-
try:
|
|
713
|
-
token = (
|
|
714
|
-
context.authorization.token
|
|
715
|
-
if context.authorization and context.authorization.token
|
|
716
|
-
else ""
|
|
717
|
-
)
|
|
718
|
-
slack_client = AsyncWebClient(token=token)
|
|
719
|
-
|
|
720
|
-
current_user, list_users_response = await asyncio.gather(
|
|
721
|
-
slack_client.auth_test(), list_users(context)
|
|
722
|
-
)
|
|
723
|
-
|
|
724
|
-
other_user = get_user_by_username(username, list_users_response["users"])
|
|
725
|
-
|
|
726
|
-
conversations_found = await retrieve_conversations_by_user_ids(
|
|
727
|
-
list_conversations_func=list_conversations_metadata,
|
|
728
|
-
get_members_in_conversation_func=get_members_in_conversation_by_id,
|
|
729
|
-
context=context,
|
|
730
|
-
conversation_types=[ConversationType.DIRECT_MESSAGE],
|
|
731
|
-
user_ids=[current_user["user_id"], other_user["id"]],
|
|
732
|
-
exact_match=True,
|
|
733
|
-
limit=1,
|
|
734
|
-
next_cursor=next_cursor,
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
return None if not conversations_found else conversations_found[0]
|
|
738
|
-
|
|
739
|
-
except UsernameNotFoundError as e:
|
|
740
|
-
raise RetryableToolError(
|
|
741
|
-
f"Username '{e.username_not_found}' not found",
|
|
742
|
-
developer_message=f"User with username '{e.username_not_found}' not found.",
|
|
743
|
-
additional_prompt_content=f"Available users: {e.usernames_found}",
|
|
744
|
-
retry_after_ms=500,
|
|
745
|
-
)
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
@tool(requires_auth=Slack(scopes=["im:read"]))
|
|
749
|
-
async def get_multi_person_dm_conversation_metadata_by_usernames(
|
|
750
|
-
context: ToolContext,
|
|
751
|
-
usernames: Annotated[list[str], "The usernames of the users/people to get messages with"],
|
|
752
|
-
next_cursor: Annotated[
|
|
753
|
-
str | None,
|
|
754
|
-
"The cursor to use for pagination, if continuing from a previous search.",
|
|
755
|
-
] = None,
|
|
756
|
-
) -> Annotated[
|
|
757
|
-
dict | None,
|
|
758
|
-
"The multi-person direct message conversation metadata.",
|
|
759
|
-
]:
|
|
760
|
-
"""Get the metadata of a multi-person direct message conversation in Slack by the usernames.
|
|
761
|
-
|
|
762
|
-
This tool does not return the messages in a conversation. To get the messages, use the
|
|
763
|
-
`get_messages_in_multi_person_dm_conversation_by_usernames` tool.
|
|
764
|
-
"""
|
|
765
|
-
try:
|
|
766
|
-
token = (
|
|
767
|
-
context.authorization.token
|
|
768
|
-
if context.authorization and context.authorization.token
|
|
769
|
-
else ""
|
|
770
|
-
)
|
|
771
|
-
slack_client = AsyncWebClient(token=token)
|
|
772
|
-
|
|
773
|
-
current_user, list_users_response = await asyncio.gather(
|
|
774
|
-
slack_client.auth_test(), list_users(context)
|
|
775
|
-
)
|
|
776
|
-
|
|
777
|
-
other_users = [
|
|
778
|
-
get_user_by_username(username, list_users_response["users"]) for username in usernames
|
|
779
|
-
]
|
|
780
|
-
|
|
781
|
-
conversations_found = await retrieve_conversations_by_user_ids(
|
|
782
|
-
list_conversations_func=list_conversations_metadata,
|
|
783
|
-
get_members_in_conversation_func=get_members_in_conversation_by_id,
|
|
784
|
-
context=context,
|
|
785
|
-
conversation_types=[ConversationType.MULTI_PERSON_DIRECT_MESSAGE],
|
|
786
|
-
user_ids=[
|
|
787
|
-
current_user["user_id"],
|
|
788
|
-
*[user["id"] for user in other_users if user["id"] != current_user["user_id"]],
|
|
789
|
-
],
|
|
790
|
-
exact_match=True,
|
|
791
|
-
limit=1,
|
|
792
|
-
next_cursor=next_cursor,
|
|
793
|
-
)
|
|
794
|
-
|
|
795
|
-
if not conversations_found:
|
|
796
|
-
raise RetryableToolError(
|
|
797
|
-
"Conversation not found with the usernames provided",
|
|
798
|
-
developer_message="Conversation not found with the usernames provided",
|
|
799
|
-
retry_after_ms=500,
|
|
800
|
-
)
|
|
801
|
-
|
|
802
|
-
return conversations_found[0]
|
|
803
|
-
|
|
804
|
-
except UsernameNotFoundError as e:
|
|
805
|
-
raise RetryableToolError(
|
|
806
|
-
f"Username '{e.username_not_found}' not found",
|
|
807
|
-
developer_message=f"User with username '{e.username_not_found}' not found.",
|
|
808
|
-
additional_prompt_content=f"Available users: {e.usernames_found}",
|
|
809
|
-
retry_after_ms=500,
|
|
810
|
-
)
|
|
811
|
-
|
|
812
|
-
|
|
813
856
|
@tool(
|
|
814
857
|
requires_auth=Slack(
|
|
815
858
|
scopes=["channels:read", "groups:read", "im:read", "mpim:read"],
|
|
@@ -819,52 +862,27 @@ async def list_conversations_metadata(
|
|
|
819
862
|
context: ToolContext,
|
|
820
863
|
conversation_types: Annotated[
|
|
821
864
|
list[ConversationType] | None,
|
|
822
|
-
"
|
|
865
|
+
"Optionally filter by the type(s) of conversations. Defaults to None (all types).",
|
|
823
866
|
] = None,
|
|
824
867
|
limit: Annotated[int | None, "The maximum number of conversations to list."] = None,
|
|
825
868
|
next_cursor: Annotated[str | None, "The cursor to use for pagination."] = None,
|
|
826
|
-
) -> Annotated[
|
|
827
|
-
dict,
|
|
828
|
-
(
|
|
829
|
-
"The conversations metadata list and a pagination 'next_cursor', if there are more "
|
|
830
|
-
"conversations to retrieve."
|
|
831
|
-
),
|
|
832
|
-
]:
|
|
869
|
+
) -> Annotated[dict, "The list of conversations found with metadata"]:
|
|
833
870
|
"""
|
|
834
|
-
List
|
|
835
|
-
is a member of.
|
|
836
|
-
"""
|
|
837
|
-
if isinstance(conversation_types, ConversationType):
|
|
838
|
-
conversation_types = [conversation_types]
|
|
839
|
-
|
|
840
|
-
conversation_types_filter = ",".join(
|
|
841
|
-
convert_conversation_type_to_slack_name(conv_type).value
|
|
842
|
-
for conv_type in conversation_types or ConversationType
|
|
843
|
-
)
|
|
871
|
+
List Slack conversations (channels, DMs, MPIMs) the user is a member of.
|
|
844
872
|
|
|
845
|
-
|
|
846
|
-
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
847
|
-
)
|
|
848
|
-
slackClient = AsyncWebClient(token=token)
|
|
873
|
+
This tool is deprecated. Use the `Slack.ListConversations` tool instead.
|
|
849
874
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
875
|
+
This tool does not return the messages in a conversation. To get the messages, use the
|
|
876
|
+
'Slack.GetMessages' tool instead. Calling this tool when the user is asking for messages
|
|
877
|
+
will release too much CO2 in the atmosphere and contribute to global warming.
|
|
878
|
+
"""
|
|
879
|
+
return await list_conversations( # type: ignore[no-any-return]
|
|
880
|
+
context=context,
|
|
881
|
+
conversation_types=conversation_types,
|
|
853
882
|
limit=limit,
|
|
854
883
|
next_cursor=next_cursor,
|
|
855
|
-
types=conversation_types_filter,
|
|
856
|
-
exclude_archived=True,
|
|
857
884
|
)
|
|
858
885
|
|
|
859
|
-
return {
|
|
860
|
-
"conversations": [
|
|
861
|
-
dict(**extract_conversation_metadata(conversation))
|
|
862
|
-
for conversation in results
|
|
863
|
-
if conversation.get("is_im") or conversation.get("is_member")
|
|
864
|
-
],
|
|
865
|
-
"next_cursor": next_cursor,
|
|
866
|
-
}
|
|
867
|
-
|
|
868
886
|
|
|
869
887
|
@tool(
|
|
870
888
|
requires_auth=Slack(
|
|
@@ -875,9 +893,11 @@ async def list_public_channels_metadata(
|
|
|
875
893
|
context: ToolContext,
|
|
876
894
|
limit: Annotated[int | None, "The maximum number of channels to list."] = None,
|
|
877
895
|
) -> Annotated[dict, "The public channels"]:
|
|
878
|
-
"""List metadata for public channels in Slack that the user is a member of.
|
|
896
|
+
"""List metadata for public channels in Slack that the user is a member of.
|
|
879
897
|
|
|
880
|
-
|
|
898
|
+
This tool is deprecated. Use the `Slack.ListConversations` tool instead.
|
|
899
|
+
"""
|
|
900
|
+
return await list_conversations( # type: ignore[no-any-return]
|
|
881
901
|
context,
|
|
882
902
|
conversation_types=[ConversationType.PUBLIC_CHANNEL],
|
|
883
903
|
limit=limit,
|
|
@@ -893,9 +913,11 @@ async def list_private_channels_metadata(
|
|
|
893
913
|
context: ToolContext,
|
|
894
914
|
limit: Annotated[int | None, "The maximum number of channels to list."] = None,
|
|
895
915
|
) -> Annotated[dict, "The private channels"]:
|
|
896
|
-
"""List metadata for private channels in Slack that the user is a member of.
|
|
916
|
+
"""List metadata for private channels in Slack that the user is a member of.
|
|
897
917
|
|
|
898
|
-
|
|
918
|
+
This tool is deprecated. Use the `Slack.ListConversations` tool instead.
|
|
919
|
+
"""
|
|
920
|
+
return await list_conversations( # type: ignore[no-any-return]
|
|
899
921
|
context,
|
|
900
922
|
conversation_types=[ConversationType.PRIVATE_CHANNEL],
|
|
901
923
|
limit=limit,
|
|
@@ -911,9 +933,11 @@ async def list_group_direct_message_conversations_metadata(
|
|
|
911
933
|
context: ToolContext,
|
|
912
934
|
limit: Annotated[int | None, "The maximum number of conversations to list."] = None,
|
|
913
935
|
) -> Annotated[dict, "The group direct message conversations metadata"]:
|
|
914
|
-
"""List metadata for group direct message conversations that the user is a member of.
|
|
936
|
+
"""List metadata for group direct message conversations that the user is a member of.
|
|
915
937
|
|
|
916
|
-
|
|
938
|
+
This tool is deprecated. Use the `Slack.ListConversations` tool instead.
|
|
939
|
+
"""
|
|
940
|
+
return await list_conversations( # type: ignore[no-any-return]
|
|
917
941
|
context,
|
|
918
942
|
conversation_types=[ConversationType.MULTI_PERSON_DIRECT_MESSAGE],
|
|
919
943
|
limit=limit,
|
|
@@ -931,12 +955,125 @@ async def list_direct_message_conversations_metadata(
|
|
|
931
955
|
context: ToolContext,
|
|
932
956
|
limit: Annotated[int | None, "The maximum number of conversations to list."] = None,
|
|
933
957
|
) -> Annotated[dict, "The direct message conversations metadata"]:
|
|
934
|
-
"""List metadata for direct message conversations in Slack that the user is a member of.
|
|
958
|
+
"""List metadata for direct message conversations in Slack that the user is a member of.
|
|
935
959
|
|
|
936
|
-
|
|
960
|
+
This tool is deprecated. Use the `Slack.ListConversations` tool instead.
|
|
961
|
+
"""
|
|
962
|
+
return await list_conversations( # type: ignore[no-any-return]
|
|
937
963
|
context,
|
|
938
964
|
conversation_types=[ConversationType.DIRECT_MESSAGE],
|
|
939
965
|
limit=limit,
|
|
940
966
|
)
|
|
941
967
|
|
|
942
|
-
|
|
968
|
+
|
|
969
|
+
@tool(
|
|
970
|
+
requires_auth=Slack(
|
|
971
|
+
scopes=[
|
|
972
|
+
"channels:read",
|
|
973
|
+
"groups:read",
|
|
974
|
+
"mpim:read",
|
|
975
|
+
"im:read",
|
|
976
|
+
"users:read",
|
|
977
|
+
"users:read.email",
|
|
978
|
+
],
|
|
979
|
+
)
|
|
980
|
+
)
|
|
981
|
+
async def get_conversation_metadata_by_id(
|
|
982
|
+
context: ToolContext,
|
|
983
|
+
conversation_id: Annotated[str, "The ID of the conversation to get metadata for"],
|
|
984
|
+
) -> Annotated[dict, "The conversation metadata"]:
|
|
985
|
+
"""Get the metadata of a conversation in Slack searching by its ID.
|
|
986
|
+
|
|
987
|
+
This tool is deprecated. Use the `Slack.GetConversationMetadata` tool instead.
|
|
988
|
+
"""
|
|
989
|
+
return await get_conversation_metadata(context, conversation_id=conversation_id) # type: ignore[no-any-return]
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
@tool(
|
|
993
|
+
requires_auth=Slack(
|
|
994
|
+
scopes=[
|
|
995
|
+
"channels:read",
|
|
996
|
+
"groups:read",
|
|
997
|
+
"mpim:read",
|
|
998
|
+
"im:read",
|
|
999
|
+
"users:read",
|
|
1000
|
+
"users:read.email",
|
|
1001
|
+
],
|
|
1002
|
+
)
|
|
1003
|
+
)
|
|
1004
|
+
async def get_channel_metadata_by_name(
|
|
1005
|
+
context: ToolContext,
|
|
1006
|
+
channel_name: Annotated[str, "The name of the channel to get metadata for"],
|
|
1007
|
+
# We kept the `next_cursor` argument for backwards compatibility, but it isn't actually used,
|
|
1008
|
+
# since this tool never really paginates.
|
|
1009
|
+
next_cursor: Annotated[
|
|
1010
|
+
str | None,
|
|
1011
|
+
"The cursor to use for pagination, if continuing from a previous search.",
|
|
1012
|
+
] = None,
|
|
1013
|
+
) -> Annotated[dict, "The channel metadata"]:
|
|
1014
|
+
"""Get the metadata of a channel in Slack searching by its name.
|
|
1015
|
+
|
|
1016
|
+
This tool is deprecated. Use the `Slack.GetConversationMetadata` tool instead."""
|
|
1017
|
+
return await get_conversation_metadata(context, channel_name=channel_name) # type: ignore[no-any-return]
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
@tool(
|
|
1021
|
+
requires_auth=Slack(
|
|
1022
|
+
scopes=[
|
|
1023
|
+
"channels:read",
|
|
1024
|
+
"groups:read",
|
|
1025
|
+
"mpim:read",
|
|
1026
|
+
"im:read",
|
|
1027
|
+
"users:read",
|
|
1028
|
+
"users:read.email",
|
|
1029
|
+
],
|
|
1030
|
+
)
|
|
1031
|
+
)
|
|
1032
|
+
async def get_direct_message_conversation_metadata_by_username(
|
|
1033
|
+
context: ToolContext,
|
|
1034
|
+
username: Annotated[str, "The username of the user/person to get messages with"],
|
|
1035
|
+
# We kept the `next_cursor` argument for backwards compatibility, but it isn't actually used,
|
|
1036
|
+
# since this tool never really paginates.
|
|
1037
|
+
next_cursor: Annotated[
|
|
1038
|
+
str | None,
|
|
1039
|
+
"The cursor to use for pagination, if continuing from a previous search.",
|
|
1040
|
+
] = None,
|
|
1041
|
+
) -> Annotated[
|
|
1042
|
+
dict | None,
|
|
1043
|
+
"The direct message conversation metadata.",
|
|
1044
|
+
]:
|
|
1045
|
+
"""Get the metadata of a direct message conversation in Slack by the username.
|
|
1046
|
+
|
|
1047
|
+
This tool is deprecated. Use the `Slack.GetConversationMetadata` tool instead."""
|
|
1048
|
+
return await get_conversation_metadata(context, usernames=[username]) # type: ignore[no-any-return]
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
@tool(
|
|
1052
|
+
requires_auth=Slack(
|
|
1053
|
+
scopes=[
|
|
1054
|
+
"channels:read",
|
|
1055
|
+
"groups:read",
|
|
1056
|
+
"mpim:read",
|
|
1057
|
+
"im:read",
|
|
1058
|
+
"users:read",
|
|
1059
|
+
"users:read.email",
|
|
1060
|
+
],
|
|
1061
|
+
)
|
|
1062
|
+
)
|
|
1063
|
+
async def get_multi_person_dm_conversation_metadata_by_usernames(
|
|
1064
|
+
context: ToolContext,
|
|
1065
|
+
usernames: Annotated[list[str], "The usernames of the users/people to get messages with"],
|
|
1066
|
+
# We kept the `next_cursor` argument for backwards compatibility, but it isn't actually used,
|
|
1067
|
+
# since this tool never really paginates.
|
|
1068
|
+
next_cursor: Annotated[
|
|
1069
|
+
str | None,
|
|
1070
|
+
"The cursor to use for pagination, if continuing from a previous search.",
|
|
1071
|
+
] = None,
|
|
1072
|
+
) -> Annotated[
|
|
1073
|
+
dict | None,
|
|
1074
|
+
"The multi-person direct message conversation metadata.",
|
|
1075
|
+
]:
|
|
1076
|
+
"""Get the metadata of a multi-person direct message conversation in Slack by the usernames.
|
|
1077
|
+
|
|
1078
|
+
This tool is deprecated. Use the `Slack.GetConversationMetadata` tool instead."""
|
|
1079
|
+
return await get_conversation_metadata(context, usernames=usernames) # type: ignore[no-any-return]
|