UNPKG

botbuilder-dialogs-adaptive

Version:

Rule system for the Microsoft BotBuilder dialog system.

142 lines (127 loc) 5.49 kB
/** * @module botbuilder-dialogs-adaptive */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { ArrayProperty, StringProperty } from '../properties'; import { SendActivity, SendActivityConfiguration } from '../actions/sendActivity'; import { ArrayExpression, ArrayExpressionConverter, StringExpression, StringExpressionConverter, } from 'adaptive-expressions'; import { Converter, ConverterFactory, DialogContext, DialogEvent, DialogPath, DialogTurnResult, DialogTurnStatus, TurnPath, } from 'botbuilder-dialogs'; import { StringUtils } from 'botbuilder'; import { ActivityTemplate } from '..'; export interface AskConfiguration extends SendActivityConfiguration { expectedProperties?: ArrayProperty<string>; defaultOperation?: StringProperty; } /** * Ask for an open-ended response. * This sends an activity and then terminates the turn with `DialogTurnStatus.completeAndWait`. * The next activity from the user will then be handled by the parent adaptive dialog. * It also builds in a model of the properties that are expected in response through `DialogPath.expectedProperties`. * `DialogPath.retries` is updated as the same question is asked multiple times. */ export class Ask extends SendActivity implements AskConfiguration { static $kind = 'Microsoft.Ask'; /** *Initializes a new instance of the [Ask](xref:botbuilder-dialogs-adaptive.Ask) class. * * @param text Optional, text value. * @param expectedProperties Optional, [ArrayExpression](xref:adaptive-expressions.ArrayExpression) of expected properties. */ constructor(text?: string, expectedProperties?: ArrayExpression<string>) { super(text); this.expectedProperties = expectedProperties; } /** * Gets or sets properties expected to be filled by response. */ expectedProperties: ArrayExpression<string>; /** * Gets or sets the default operation that will be used when no operation is recognized. */ defaultOperation: StringExpression; /** * Called when the [Dialog](xref:botbuilder-dialogs.Dialog) is started and pushed onto the dialog stack. * * @param property The key of the conditional selector configuration. * @returns The converter for the selector configuration. */ getConverter(property: keyof AskConfiguration): Converter | ConverterFactory { switch (property) { case 'expectedProperties': return new ArrayExpressionConverter<string>(); case 'defaultOperation': return new StringExpressionConverter(); default: return super.getConverter(property); } } /** * Called when the dialog is started and pushed onto the dialog stack. * * @summary * If the task is successful, the result indicates whether the dialog is still * active after the turn has been processed by the dialog. * * You can use the [options](#options) parameter to include the QnA Maker context data, * which represents context from the previous query. To do so, the value should include a * `context` property of type [QnAResponseContext](#QnAResponseContext). * * @param {DialogContext} dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation. * @param {object} options (Optional) Initial information to pass to the dialog. * @returns {Promise<DialogTurnResult>} A promise resolving to the turn result */ async beginDialog(dc: DialogContext, options?: object): Promise<DialogTurnResult> { // get number of retries from memory let retries: number = dc.state.getValue<number>(DialogPath.retries, 0); const expected: string[] = this.expectedProperties ? this.expectedProperties.getValue(dc.state) : undefined; const trigger: DialogEvent = dc.state.getValue<DialogEvent>(TurnPath.dialogEvent); const lastExpectedProperties: string[] = dc.state.getValue<string[]>(DialogPath.expectedProperties); const lastTrigger: DialogEvent = dc.state.getValue<DialogEvent>(DialogPath.lastTriggerEvent); if ( expected && lastExpectedProperties && lastTrigger && !expected.some( (prop: string): boolean => !lastExpectedProperties.some((lastProp: string): boolean => lastProp === prop), ) && !lastExpectedProperties.some( (lastProp: string): boolean => !expected.some((prop: string): boolean => prop === lastProp), ) && lastTrigger.name === trigger.name ) { retries++; } else { retries = 0; } dc.state.setValue(DialogPath.retries, retries); dc.state.setValue(DialogPath.lastTriggerEvent, trigger); dc.state.setValue(DialogPath.expectedProperties, expected); const result = await super.beginDialog(dc, options); result.status = DialogTurnStatus.completeAndWait; return result; } protected onComputeId(): string { if (this.activity instanceof ActivityTemplate) { return `Ask[${StringUtils.ellipsis(this.activity.template.trim(), 30)}]`; } return `Ask[${StringUtils.ellipsis(this.activity && this.activity.toString().trim(), 30)}]`; } }