0
# Advanced Features
1
2
Slack Bolt provides advanced functionality for AI Assistant integration, workflow steps, custom functions, and conversation state management for complex Slack application scenarios.
3
4
## Capabilities
5
6
### AI Assistant Integration
7
8
Build AI-powered Slack assistants with thread context management and user interaction handling.
9
10
```typescript { .api }
11
/**
12
* AI Assistant class for building intelligent Slack applications
13
* @param config - Assistant configuration with event handlers
14
*/
15
class Assistant {
16
constructor(config: AssistantConfig);
17
}
18
19
interface AssistantConfig {
20
/** Custom thread context store (defaults to in-memory store) */
21
threadContextStore?: AssistantThreadContextStore;
22
/** Handler(s) for when assistant threads are started */
23
threadStarted: AssistantThreadStartedMiddleware | AssistantThreadStartedMiddleware[];
24
/** Handler(s) for when thread context changes (optional) */
25
threadContextChanged?: AssistantThreadContextChangedMiddleware | AssistantThreadContextChangedMiddleware[];
26
/** Handler(s) for user messages in assistant threads */
27
userMessage: AssistantUserMessageMiddleware | AssistantUserMessageMiddleware[];
28
}
29
30
type AssistantThreadStartedMiddleware = (
31
args: AssistantUtilityArgs & AllMiddlewareArgs
32
) => Promise<void>;
33
34
type AssistantThreadContextChangedMiddleware = (
35
args: AssistantUtilityArgs & AllMiddlewareArgs & {
36
previousThreadContext?: AssistantThreadContext;
37
}
38
) => Promise<void>;
39
40
type AssistantUserMessageMiddleware = (
41
args: AssistantUtilityArgs & SlackEventMiddlewareArgs<'message'> & {
42
message: AssistantMessage;
43
}
44
) => Promise<void>;
45
```
46
47
**Usage Examples:**
48
49
```typescript
50
import { App, Assistant } from "@slack/bolt";
51
52
const app = new App({
53
token: process.env.SLACK_BOT_TOKEN,
54
signingSecret: process.env.SLACK_SIGNING_SECRET
55
});
56
57
// Configure assistant
58
const assistant = new Assistant({
59
threadStarted: async ({ say, setStatus, setSuggestedPrompts }) => {
60
await say("Hello! I'm your AI assistant. How can I help you today?");
61
62
await setStatus("Ready to assist");
63
64
await setSuggestedPrompts({
65
prompts: [
66
{ title: "Help me write code", message: "Can you help me write some code?" },
67
{ title: "Explain a concept", message: "Can you explain how webhooks work?" },
68
{ title: "Review my work", message: "Can you review this document?" }
69
],
70
title: "Popular requests"
71
});
72
},
73
74
userMessage: async ({
75
message,
76
say,
77
getThreadContext,
78
saveThreadContext,
79
setStatus
80
}) => {
81
await setStatus("Thinking...");
82
83
// Get conversation context
84
const context = await getThreadContext();
85
86
// Process user message with AI
87
const response = await processWithAI(message.text, context);
88
89
// Update context with new information
90
context.conversationHistory.push({
91
user: message.text,
92
assistant: response
93
});
94
95
// Save updated context
96
await saveThreadContext();
97
98
await say(response);
99
await setStatus("Ready");
100
}
101
});
102
103
// Register assistant with app
104
app.assistant(assistant);
105
```
106
107
### Assistant Thread Context
108
109
Manage conversation state and context across assistant interactions.
110
111
```typescript { .api }
112
interface AssistantThreadContext {
113
/** Channel ID where the thread exists */
114
channelId: string;
115
/** Thread timestamp identifier */
116
threadTs: string;
117
/** Custom context data */
118
[key: string]: any;
119
}
120
121
interface AssistantThreadContextStore {
122
/** Retrieve context for a specific thread */
123
get(channelId: string, threadTs: string): Promise<AssistantThreadContext>;
124
/** Save context for a specific thread */
125
save(context: AssistantThreadContext): Promise<void>;
126
/** Remove context for a specific thread */
127
remove(channelId: string, threadTs: string): Promise<void>;
128
}
129
130
class DefaultThreadContextStore implements AssistantThreadContextStore {
131
constructor();
132
133
async get(channelId: string, threadTs: string): Promise<AssistantThreadContext>;
134
async save(context: AssistantThreadContext): Promise<void>;
135
async remove(channelId: string, threadTs: string): Promise<void>;
136
}
137
```
138
139
### Assistant Utilities
140
141
Utility functions available to assistant middleware for managing thread state and interactions.
142
143
```typescript { .api }
144
interface AssistantUtilityArgs {
145
/** Get current thread context */
146
getThreadContext: GetThreadContextUtilFn;
147
/** Save current thread context */
148
saveThreadContext: SaveThreadContextUtilFn;
149
/** Send message in current thread */
150
say: SayFn;
151
/** Set assistant status */
152
setStatus: SetStatusFn;
153
/** Set suggested prompts for the thread */
154
setSuggestedPrompts: SetSuggestedPromptsFn;
155
/** Set thread title */
156
setTitle: SetTitleFn;
157
}
158
159
type GetThreadContextUtilFn = () => Promise<AssistantThreadContext>;
160
type SaveThreadContextUtilFn = () => Promise<void>;
161
type SetStatusFn = (status: string) => Promise<AssistantThreadsSetStatusResponse>;
162
type SetSuggestedPromptsFn = (
163
params: SetSuggestedPromptsArguments
164
) => Promise<AssistantThreadsSetSuggestedPromptsResponse>;
165
type SetTitleFn = (title: string) => Promise<AssistantThreadsSetTitleResponse>;
166
167
interface SetSuggestedPromptsArguments {
168
/** Prompt suggestions that appear when opening assistant thread */
169
prompts: [AssistantPrompt, ...AssistantPrompt[]];
170
/** Title for the prompts */
171
title: string;
172
}
173
174
interface AssistantPrompt {
175
/** Display title for the prompt */
176
title: string;
177
/** Message text when prompt is selected */
178
message: string;
179
}
180
```
181
182
### Workflow Steps (Deprecated)
183
184
Legacy workflow step functionality for workflow apps (deprecated in favor of custom functions).
185
186
```typescript { .api }
187
/**
188
* @deprecated Steps from Apps are no longer supported and support will be removed in next major version
189
* Workflow step builder for creating custom workflow steps
190
*/
191
class WorkflowStep {
192
constructor(config: WorkflowStepConfig);
193
}
194
195
/**
196
* @deprecated Configuration for workflow steps
197
*/
198
interface WorkflowStepConfig {
199
/** Callback ID for the workflow step */
200
callback_id: string;
201
/** Handler for step edit action */
202
edit: WorkflowStepEditMiddleware | WorkflowStepEditMiddleware[];
203
/** Handler for step save action */
204
save: WorkflowStepSaveMiddleware | WorkflowStepSaveMiddleware[];
205
/** Handler for step execution */
206
execute: WorkflowStepExecuteMiddleware | WorkflowStepExecuteMiddleware[];
207
}
208
209
type WorkflowStepEditMiddleware = (
210
args: SlackActionMiddlewareArgs<WorkflowStepEdit> & {
211
step: WorkflowStepEdit;
212
configure: (config: StepConfigureArguments) => Promise<ViewsOpenResponse>;
213
}
214
) => Promise<void>;
215
216
type WorkflowStepSaveMiddleware = (
217
args: SlackViewMiddlewareArgs & {
218
step: WorkflowStepEdit;
219
update: (config: StepUpdateArguments) => Promise<WorkflowsUpdateStepResponse>;
220
}
221
) => Promise<void>;
222
223
type WorkflowStepExecuteMiddleware = (
224
args: SlackEventMiddlewareArgs<'workflow_step_execute'> & {
225
step: WorkflowStepExecuteEvent;
226
complete: (outputs?: StepCompletionArguments) => Promise<WorkflowsStepCompletedResponse>;
227
fail: (error: StepFailureArguments) => Promise<WorkflowsStepFailedResponse>;
228
}
229
) => Promise<void>;
230
```
231
232
### Custom Functions
233
234
Create custom functions for Slack workflows with input validation and execution handling.
235
236
```typescript { .api }
237
/**
238
* Custom function builder for Slack workflows
239
*/
240
class CustomFunction {
241
constructor(callbackId: string, config: CustomFunctionConfig);
242
}
243
244
interface CustomFunctionConfig {
245
/** Handler for function execution */
246
execute: CustomFunctionExecuteMiddleware | CustomFunctionExecuteMiddleware[];
247
}
248
249
type CustomFunctionExecuteMiddleware = (
250
args: SlackCustomFunctionMiddlewareArgs & {
251
inputs: FunctionInputs;
252
complete: FunctionCompleteFn;
253
fail: FunctionFailFn;
254
}
255
) => Promise<void>;
256
257
type FunctionCompleteFn = (outputs: { [key: string]: any }) => Promise<void>;
258
type FunctionFailFn = (error: string) => Promise<void>;
259
260
interface SlackCustomFunctionMiddlewareArgs extends AllMiddlewareArgs {
261
/** Function execution payload */
262
payload: {
263
type: 'function_executed';
264
function: {
265
callback_id: string;
266
type: 'app';
267
};
268
inputs: FunctionInputs;
269
function_execution_id: string;
270
workflow: {
271
id: string;
272
};
273
event: {
274
type: 'function_executed';
275
};
276
};
277
}
278
```
279
280
**Usage Examples:**
281
282
```typescript
283
import { App, CustomFunction } from "@slack/bolt";
284
285
const app = new App({
286
token: process.env.SLACK_BOT_TOKEN,
287
signingSecret: process.env.SLACK_SIGNING_SECRET
288
});
289
290
// Define custom function
291
const processDataFunction = new CustomFunction("process_data", {
292
execute: async ({ inputs, complete, fail, client }) => {
293
try {
294
const { data_source, format } = inputs;
295
296
// Validate inputs
297
if (!data_source) {
298
await fail("data_source is required");
299
return;
300
}
301
302
// Process the data
303
const result = await processData(data_source, format);
304
305
// Return outputs
306
await complete({
307
processed_data: result.data,
308
record_count: result.count,
309
status: "success"
310
});
311
312
} catch (error) {
313
await fail(`Processing failed: ${error.message}`);
314
}
315
}
316
});
317
318
// Register function with app
319
app.function("process_data", processDataFunction);
320
```
321
322
### Conversation Store
323
324
Manage conversation state and context across multiple interactions.
325
326
```typescript { .api }
327
/**
328
* Interface for storing conversation state
329
*/
330
interface ConversationStore {
331
/** Store conversation data */
332
set(conversationId: string, value: any, expiresAt?: number): Promise<void>;
333
/** Retrieve conversation data */
334
get<T = any>(conversationId: string): Promise<T | undefined>;
335
/** Remove conversation data */
336
delete(conversationId: string): Promise<void>;
337
}
338
339
/**
340
* In-memory implementation of ConversationStore
341
*/
342
class MemoryStore implements ConversationStore {
343
constructor();
344
345
async set(conversationId: string, value: any, expiresAt?: number): Promise<void>;
346
async get<T = any>(conversationId: string): Promise<T | undefined>;
347
async delete(conversationId: string): Promise<void>;
348
}
349
350
/**
351
* Middleware for adding conversation context to requests
352
* @param store - Conversation store instance
353
* @param options - Context options
354
*/
355
function conversationContext(
356
store: ConversationStore,
357
options?: ConversationContextOptions
358
): Middleware<AnyMiddlewareArgs>;
359
360
interface ConversationContextOptions {
361
/** Custom function to extract conversation ID */
362
getConversationId?: (args: AnyMiddlewareArgs) => string | undefined;
363
/** Context property name (defaults to 'conversation') */
364
contextKey?: string;
365
}
366
```
367
368
**Usage Examples:**
369
370
```typescript
371
import { App, MemoryStore, conversationContext } from "@slack/bolt";
372
373
const app = new App({
374
token: process.env.SLACK_BOT_TOKEN,
375
signingSecret: process.env.SLACK_SIGNING_SECRET
376
});
377
378
// Create conversation store
379
const convoStore = new MemoryStore();
380
381
// Add conversation context middleware
382
app.use(conversationContext(convoStore));
383
384
// Use conversation context in listeners
385
app.message("start survey", async ({ message, context, say }) => {
386
// Initialize conversation state
387
await context.conversation.set("survey_step", 1);
388
await context.conversation.set("responses", {});
389
390
await say("Let's start the survey! What's your name?");
391
});
392
393
app.message(async ({ message, context, say }) => {
394
const step = await context.conversation.get("survey_step");
395
const responses = await context.conversation.get("responses") || {};
396
397
if (step === 1) {
398
responses.name = message.text;
399
await context.conversation.set("survey_step", 2);
400
await context.conversation.set("responses", responses);
401
402
await say("Thanks! What's your favorite color?");
403
} else if (step === 2) {
404
responses.color = message.text;
405
await context.conversation.set("survey_step", 3);
406
await context.conversation.set("responses", responses);
407
408
await say(`Great! So your name is ${responses.name} and your favorite color is ${responses.color}. Survey complete!`);
409
410
// Clear conversation state
411
await context.conversation.delete("survey_step");
412
await context.conversation.delete("responses");
413
}
414
});
415
```