UNPKG

18.3 kBTypeScriptView Raw
1/**
2 * @module botkit
3 */
4/**
5 * Copyright (c) Microsoft Corporation. All rights reserved.
6 * Licensed under the MIT License.
7 */
8import { Botkit, BotkitMessage } from './core';
9import { BotWorker } from './botworker';
10import { BotkitDialogWrapper } from './dialogWrapper';
11import { Dialog, DialogContext, DialogReason, DialogTurnStatus } from 'botbuilder-dialogs';
12/**
13 * Definition of the handler functions used to handle .ask and .addQuestion conditions
14 */
15interface BotkitConvoHandler {
16 (answer: string, convo: BotkitDialogWrapper, bot: BotWorker, message: BotkitMessage): Promise<any>;
17}
18/**
19 * Definition of the trigger pattern passed into .ask or .addQuestion
20 */
21interface BotkitConvoTrigger {
22 type?: string;
23 pattern?: string | RegExp;
24 handler: BotkitConvoHandler;
25 default?: boolean;
26}
27/**
28 * Template for definiting a BotkitConversation template
29 */
30interface BotkitMessageTemplate {
31 text: ((template: any, vars: any) => string) | string[];
32 action?: string;
33 execute?: {
34 script: string;
35 thread?: string;
36 };
37 quick_replies?: ((template: any, vars: any) => any[]) | any[];
38 attachments?: ((template: any, vars: any) => any[]) | any[];
39 blocks?: ((template: any, vars: any) => any[]) | any[];
40 attachment?: ((template: any, vars: any) => any) | any;
41 attachmentLayout?: string;
42 channelData?: any;
43 collect: {
44 key?: string;
45 options?: BotkitConvoTrigger[];
46 };
47}
48export interface BotkitConversationStep {
49 /**
50 * The number pointing to the current message in the current thread in this dialog's script
51 */
52 index: number;
53 /**
54 * The name of the current thread
55 */
56 thread: string;
57 /**
58 * The length of the current thread
59 */
60 threadLength: number;
61 /**
62 * A pointer to the current dialog state
63 */
64 state: any;
65 /**
66 * A pointer to any options passed into the dialog when it began
67 */
68 options: any;
69 /**
70 * The reason for this step being called
71 */
72 reason: DialogReason;
73 /**
74 * The results of the previous turn
75 */
76 result: any;
77 /**
78 * A pointer directly to state.values
79 */
80 values: any;
81 /**
82 * A function to call when the step is completed.
83 */
84 next: (stepResult: any) => Promise<any>;
85}
86/**
87 * An extension on the [BotBuilder Dialog Class](https://docs.microsoft.com/en-us/javascript/api/botbuilder-dialogs/dialog?view=botbuilder-ts-latest) that provides a Botkit-friendly interface for
88 * defining and interacting with multi-message dialogs. Dialogs can be constructed using `say()`, `ask()` and other helper methods.
89 *
90 * ```javascript
91 * // define the structure of your dialog...
92 * const convo = new BotkitConversation('foo', controller);
93 * convo.say('Hello!');
94 * convo.ask('What is your name?', async(answer, convo, bot) => {
95 * await bot.say('Your name is ' + answer);
96 * });
97 * controller.dialogSet.add(convo);
98 *
99 * // later on, trigger this dialog by its id
100 * controller.on('event', async(bot, message) => {
101 * await bot.beginDialog('foo');
102 * })
103 * ```
104 */
105export declare class BotkitConversation<O extends object = {}> extends Dialog<O> {
106 /**
107 * A map of every message in the dialog, broken into threads
108 */
109 script: any;
110 private _prompt;
111 private _beforeHooks;
112 private _afterHooks;
113 private _changeHooks;
114 private _controller;
115 /**
116 * Create a new BotkitConversation object
117 * @param dialogId A unique identifier for this dialog, used to later trigger this dialog
118 * @param controller A pointer to the main Botkit controller
119 */
120 constructor(dialogId: string, controller: Botkit);
121 /**
122 * Add a non-interactive message to the default thread.
123 * Messages added with `say()` and `addMessage()` will _not_ wait for a response, will be sent one after another without a pause.
124 *
125 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
126 *
127 * ```javascript
128 * let conversation = new BotkitConversation('welcome', controller);
129 * conversation.say('Hello! Welcome to my app.');
130 * conversation.say('Let us get started...');
131 * ```
132 *
133 * @param message Message template to be sent
134 */
135 say(message: Partial<BotkitMessageTemplate> | string): BotkitConversation;
136 /**
137 * An an action to the conversation timeline. This can be used to go to switch threads or end the dialog.
138 *
139 * When provided the name of another thread in the conversation, this will cause the bot to go immediately
140 * to that thread.
141 *
142 * Otherwise, use one of the following keywords:
143 * * `stop`
144 * * `repeat`
145 * * `complete`
146 * * `timeout`
147 *
148 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
149 *
150 * ```javascript
151 *
152 * // go to a thread called "next_thread"
153 * convo.addAction('next_thread');
154 *
155 * // end the conversation and mark as successful
156 * convo.addAction('complete');
157 * ```
158 * @param action An action or thread name
159 * @param thread_name The name of the thread to which this action is added. Defaults to `default`
160 */
161 addAction(action: string, thread_name?: string): BotkitConversation;
162 /**
163 * Cause the dialog to call a child dialog, wait for it to complete,
164 * then store the results in a variable and resume the parent dialog.
165 * Use this to [combine multiple dialogs into bigger interactions.](../conversations.md#composing-dialogs)
166 *
167 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
168 * ```javascript
169 * // define a profile collection dialog
170 * let profileDialog = new BotkitConversation('PROFILE_DIALOG', controller);
171 * profileDialog.ask('What is your name?', async(res, convo, bot) => {}, {key: 'name'});
172 * profileDialog.ask('What is your age?', async(res, convo, bot) => {}, {key: 'age'});
173 * profileDialog.ask('What is your location?', async(res, convo, bot) => {}, {key: 'location'});
174 * controller.addDialog(profileDialog);
175 *
176 * let onboard = new BotkitConversation('ONBOARDING', controller);
177 * onboard.say('Hello! It is time to collect your profile data.');
178 * onboard.addChildDialog('PROFILE_DIALOG', 'profile');
179 * onboard.say('Hello, {{vars.profile.name}}! Onboarding is complete.');
180 * ```
181 *
182 * @param dialog_id the id of another dialog
183 * @param key_name the variable name in which to store the results of the child dialog. if not provided, defaults to dialog_id.
184 * @param thread_name the name of a thread to which this call should be added. defaults to 'default'
185 */
186 addChildDialog(dialog_id: string, key_name?: string, thread_name?: string): BotkitConversation;
187 /**
188 * Cause the current dialog to handoff to another dialog.
189 * The parent dialog will not resume when the child dialog completes. However, the afterDialog event will not fire for the parent dialog until all child dialogs complete.
190 * Use this to [combine multiple dialogs into bigger interactions.](../conversations.md#composing-dialogs)
191 *
192 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
193 * ```javascript
194 * let parent = new BotkitConversation('parent', controller);
195 * let child = new BotkitConversation('child', controller);
196 * parent.say('Moving on....');
197 * parent.addGotoDialog('child');
198 * ```
199 *
200 * @param dialog_id the id of another dialog
201 * @param thread_name the name of a thread to which this call should be added. defaults to 'default'
202 */
203 addGotoDialog(dialog_id: string, thread_name?: string): BotkitConversation;
204 /**
205 * Add a message template to a specific thread.
206 * Messages added with `say()` and `addMessage()` will be sent one after another without a pause.
207 *
208 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
209 * ```javascript
210 * let conversation = new BotkitConversation('welcome', controller);
211 * conversation.say('Hello! Welcome to my app.');
212 * conversation.say('Let us get started...');
213 * // pass in a message with an action that will cause gotoThread to be called...
214 * conversation.addAction('continuation');
215 *
216 * conversation.addMessage('This is a different thread completely', 'continuation');
217 * ```
218 *
219 * @param message Message template to be sent
220 * @param thread_name Name of thread to which message will be added
221 */
222 addMessage(message: Partial<BotkitMessageTemplate> | string, thread_name: string): BotkitConversation;
223 /**
224 * Add a question to the default thread.
225 * In addition to a message template, receives either a single handler function to call when an answer is provided,
226 * or an array of handlers paired with trigger patterns. When providing multiple conditions to test, developers may also provide a
227 * handler marked as the default choice.
228 *
229 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
230 * ```javascript
231 * // ask a question, handle the response with a function
232 * convo.ask('What is your name?', async(response, convo, bot, full_message) => {
233 * await bot.say('Oh your name is ' + response);
234 * }, {key: 'name'});
235 *
236 * // ask a question, evaluate answer, take conditional action based on response
237 * convo.ask('Do you want to eat a taco?', [
238 * {
239 * pattern: 'yes',
240 * type: 'string',
241 * handler: async(response_text, convo, bot, full_message) => {
242 * return await convo.gotoThread('yes_taco');
243 * }
244 * },
245 * {
246 * pattern: 'no',
247 * type: 'string',
248 * handler: async(response_text, convo, bot, full_message) => {
249 * return await convo.gotoThread('no_taco');
250 * }
251 * },
252 * {
253 * default: true,
254 * handler: async(response_text, convo, bot, full_message) => {
255 * await bot.say('I do not understand your response!');
256 * // start over!
257 * return await convo.repeat();
258 * }
259 * }
260 * ], {key: 'tacos'});
261 * ```
262 *
263 * @param message a message that will be used as the prompt
264 * @param handlers one or more handler functions defining possible conditional actions based on the response to the question.
265 * @param key name of variable to store response in.
266 */
267 ask(message: Partial<BotkitMessageTemplate> | string, handlers: BotkitConvoHandler | BotkitConvoTrigger[], key: {
268 key: string;
269 } | string | null): BotkitConversation;
270 /**
271 * Identical to [ask()](#ask), but accepts the name of a thread to which the question is added.
272 *
273 * [Learn more about building conversations &rarr;](../conversations.md#build-a-conversation)
274 * @param message A message that will be used as the prompt
275 * @param handlers One or more handler functions defining possible conditional actions based on the response to the question
276 * @param key Name of variable to store response in.
277 * @param thread_name Name of thread to which message will be added
278 */
279 addQuestion(message: Partial<BotkitMessageTemplate> | string, handlers: BotkitConvoHandler | BotkitConvoTrigger[], key: {
280 key: string;
281 } | string | null, thread_name: string): BotkitConversation;
282 /**
283 * Register a handler function that will fire before a given thread begins.
284 * Use this hook to set variables, call APIs, or change the flow of the conversation using `convo.gotoThread`
285 *
286 * ```javascript
287 * convo.addMessage('This is the foo thread: var == {{vars.foo}}', 'foo');
288 * convo.before('foo', async(convo, bot) => {
289 * // set a variable here that can be used in the message template
290 * convo.setVar('foo','THIS IS FOO');
291 *
292 * });
293 * ```
294 *
295 * @param thread_name A valid thread defined in this conversation
296 * @param handler A handler function in the form async(convo, bot) => { ... }
297 */
298 before(thread_name: string, handler: (convo: BotkitDialogWrapper, bot: BotWorker) => Promise<any>): void;
299 /**
300 * This private method is called before a thread begins, and causes any bound handler functions to be executed.
301 * @param thread_name the thread about to begin
302 * @param dc the current DialogContext
303 * @param step the current step object
304 */
305 private runBefore;
306 /**
307 * Bind a function to run after the dialog has completed.
308 * The first parameter to the handler will include a hash of all variables set and values collected from the user during the conversation.
309 * The second parameter to the handler is a BotWorker object that can be used to start new dialogs or take other actions.
310 *
311 * [Learn more about handling end of conversation](../conversations.md#handling-end-of-conversation)
312 * ```javascript
313 * let convo = new BotkitConversation(MY_CONVO, controller);
314 * convo.ask('What is your name?', [], 'name');
315 * convo.ask('What is your age?', [], 'age');
316 * convo.ask('What is your favorite color?', [], 'color');
317 * convo.after(async(results, bot) => {
318 *
319 * // handle results.name, results.age, results.color
320 *
321 * });
322 * controller.addDialog(convo);
323 * ```
324 *
325 * @param handler in the form async(results, bot) { ... }
326 */
327 after(handler: (results: any, bot: BotWorker) => void): void;
328 /**
329 * This private method is called at the end of the conversation, and causes any bound handler functions to be executed.
330 * @param context the current dialog context
331 * @param results an object containing the final results of the dialog
332 */
333 private runAfter;
334 /**
335 * Bind a function to run whenever a user answers a specific question. Can be used to validate input and take conditional actions.
336 *
337 * ```javascript
338 * convo.ask('What is your name?', [], 'name');
339 * convo.onChange('name', async(response, convo, bot) => {
340 *
341 * // user changed their name!
342 * // do something...
343 *
344 * });
345 * ```
346 * @param variable name of the variable to watch for changes
347 * @param handler a handler function that will fire whenever a user's response is used to change the value of the watched variable
348 */
349 onChange(variable: string, handler: (response: any, convo: any, bot: any) => Promise<any>): void;
350 /**
351 * This private method is responsible for firing any bound onChange handlers when a variable changes
352 * @param variable the name of the variable that is changing
353 * @param value the new value of the variable
354 * @param dc the current DialogContext
355 * @param step the current step object
356 */
357 private runOnChange;
358 /**
359 * Called automatically when a dialog begins. Do not call this directly!
360 * @ignore
361 * @param dc the current DialogContext
362 * @param options an object containing initialization parameters passed to the dialog. may include `thread` which will cause the dialog to begin with that thread instead of the `default` thread.
363 */
364 beginDialog(dc: DialogContext, options: any): Promise<any>;
365 /**
366 * Called automatically when an already active dialog is continued. Do not call this directly!
367 * @ignore
368 * @param dc the current DialogContext
369 */
370 continueDialog(dc: DialogContext): Promise<any>;
371 /**
372 * Called automatically when a dialog moves forward a step. Do not call this directly!
373 * @ignore
374 * @param dc The current DialogContext
375 * @param reason Reason for resuming the dialog
376 * @param result Result of previous step
377 */
378 resumeDialog(dc: any, reason: any, result: any): Promise<any>;
379 /**
380 * Called automatically to process the turn, interpret the script, and take any necessary actions based on that script. Do not call this directly!
381 * @ignore
382 * @param dc The current dialog context
383 * @param step The current step object
384 */
385 private onStep;
386 /**
387 * Run a dialog step, based on the index and thread_name passed in.
388 * @param dc The current DialogContext
389 * @param index The index of the current step
390 * @param thread_name The name of the current thread
391 * @param reason The reason given for running this step
392 * @param result The result of the previous turn if any
393 */
394 private runStep;
395 /**
396 * Automatically called when the the dialog ends and causes any handlers bound using `after()` to fire. Do not call this directly!
397 * @ignore
398 * @param dc The current DialogContext
399 * @param value The final value collected by the dialog.
400 */
401 end(dc: DialogContext): Promise<DialogTurnStatus>;
402 /**
403 * Translates a line from the dialog script into an Activity. Responsible for doing token replacement.
404 * @param line a message template from the script
405 * @param vars an object containing key/value pairs used to do token replacement on fields in the message template
406 */
407 private makeOutgoing;
408 /**
409 * Responsible for doing token replacements recursively in attachments and other multi-field properties of the message.
410 * @param attachments some object or array containing values for which token replacements should be made.
411 * @param vars an object defining key/value pairs used for the token replacements
412 */
413 private parseTemplatesRecursive;
414 /**
415 * Handle the scripted "gotothread" action - requires an additional call to runStep.
416 * @param thread The name of the thread to jump to
417 * @param dc The current DialogContext
418 * @param step The current step object
419 */
420 private gotoThreadAction;
421 /**
422 * Accepts a Botkit script action, and performs that action
423 * @param path A conditional path in the form {action: 'some action', handler?: some handler function, maybe_other_fields}
424 * @param dc The current DialogContext
425 * @param step The current stpe object
426 */
427 private handleAction;
428}
429export {};