Skip to content

Commit ddbe801

Browse files
committed
Set assistant status to provide the user with waiting message
1 parent f73d909 commit ddbe801

File tree

4 files changed

+55
-25
lines changed

4 files changed

+55
-25
lines changed

integrations/slack/src/actions/inferUserIntent.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,38 @@ export async function inferUserIntentAndTriggerAction(
4545

4646
logger.debug('Infering user intent from message', userMessage);
4747

48+
const installation = await getIntegrationInstallationForTeam(context, teamId);
49+
if (!installation) {
50+
throw new Error('Installation not found');
51+
}
52+
const installationConfiguration = installation.configuration as SlackInstallationConfiguration;
53+
const accessToken = installationConfiguration.oauth_credentials?.access_token;
54+
const accessTokenScopes = installationConfiguration.oauth_credentials?.requested_scopes || [];
55+
56+
// Installations created before the Docs Agents feature was released may not have a token with the
57+
// required scope to call this API. Since this is non-blocking, we skip the API call if the token lacks
58+
// the necessary permission.
59+
if (accessTokenScopes.includes('assistant:write')) {
60+
await slackAPI(
61+
context,
62+
{
63+
method: 'POST',
64+
path: 'assistant.threads.setStatus',
65+
payload: {
66+
channel_id: channelId,
67+
thread_ts: threadTs,
68+
status: 'is working on your request...',
69+
loading_messages: [
70+
'Reading your message…',
71+
'Understanding your request…',
72+
'Determining the best action…',
73+
],
74+
},
75+
},
76+
{ accessToken },
77+
);
78+
}
79+
4880
const result = await generateText({
4981
model: openai('gpt-5-nano'),
5082
messages: [
@@ -104,13 +136,6 @@ You are a Slack assistant designed to help users with their product documentatio
104136
.filter((content) => content && content.type === 'text');
105137
const lastAssistantMessage = assistantMessages.pop();
106138

107-
const installation = await getIntegrationInstallationForTeam(context, teamId);
108-
if (!installation) {
109-
throw new Error('Installation not found');
110-
}
111-
const accessToken = (installation.configuration as SlackInstallationConfiguration)
112-
.oauth_credentials?.access_token;
113-
114139
await slackAPI(
115140
context,
116141
{

integrations/slack/src/configuration.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { RuntimeContext, RuntimeEnvironment } from '@gitbook/runtime';
33
export interface SlackInstallationConfiguration {
44
oauth_credentials?: {
55
access_token: string;
6+
requested_scopes?: string[];
67
};
78

89
default_channel?: string;

integrations/slack/src/handlers/handlers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,20 @@ export async function messageEventHandler(eventPayload: any, context: SlackRunti
8080
*/
8181
export async function appMentionEventHandler(eventPayload: any, context: SlackRuntimeContext) {
8282
// pull out required params from the slashEvent for queryAskAI
83-
const { type, text, thread_ts, channel, user, team, response_url } = eventPayload.event;
83+
const { type, text, channel, ts, thread_ts, user, team } = eventPayload.event;
8484

8585
// check for bot_id so that the bot doesn't trigger itself
8686
if (['message', 'app_mention'].includes(type) && isAllowedToRespond(eventPayload)) {
8787
// strip out the bot-name in the mention and account for user mentions within the query
8888
// @ts-ignore
8989
const parsedMessage = stripBotName(text, eventPayload.authorizations[0]?.user_id);
90+
logger.info('Received payload', JSON.stringify(eventPayload));
9091

9192
context.waitUntil(
9293
inferUserIntentAndTriggerAction(
9394
{
9495
channelId: channel,
95-
threadTs: thread_ts,
96+
threadTs: thread_ts ?? ts,
9697
userId: user,
9798
teamId: team,
9899
userMessage: parsedMessage,

integrations/slack/src/router.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,6 @@ export const handleFetchEvent: FetchEventCallback = async (request, context) =>
4040
).pathname,
4141
});
4242

43-
const encodedScopes = encodeURIComponent(
44-
[
45-
'app_mentions:read',
46-
'chat:write',
47-
'channels:join',
48-
'channels:read',
49-
'groups:read',
50-
'links:read',
51-
'links:write',
52-
'commands',
53-
'channels:history',
54-
'im:history',
55-
].join(' '),
56-
);
57-
5843
/**
5944
* Handle integration tasks
6045
*/
@@ -88,6 +73,21 @@ export const handleFetchEvent: FetchEventCallback = async (request, context) =>
8873
});
8974
});
9075

76+
const requestedScopes = [
77+
'app_mentions:read',
78+
'chat:write',
79+
'channels:join',
80+
'channels:read',
81+
'groups:read',
82+
'links:read',
83+
'links:write',
84+
'commands',
85+
'channels:history',
86+
'im:history',
87+
'assistant:write',
88+
];
89+
const encodedScopes = encodeURIComponent(requestedScopes.join(' '));
90+
9191
/*
9292
* Authenticate the user using OAuth.
9393
*/
@@ -115,7 +115,10 @@ export const handleFetchEvent: FetchEventCallback = async (request, context) =>
115115
return {
116116
externalIds: [response.team.id],
117117
configuration: {
118-
oauth_credentials: { access_token: response.access_token },
118+
oauth_credentials: {
119+
access_token: response.access_token,
120+
requested_scopes: requestedScopes,
121+
},
119122
},
120123
};
121124
},

0 commit comments

Comments
 (0)