UNPKG

botbuilder-dialogs-adaptive

Version:

Rule system for the Microsoft BotBuilder dialog system.

301 lines • 15.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BeginSkill = void 0; /** * @module botbuilder-dialogs-adaptive */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const botbuilder_1 = require("botbuilder"); const templates_1 = require("../templates"); const converters_1 = require("../converters"); const adaptiveEvents_1 = require("../adaptiveEvents"); const skillExtensions_1 = require("../skillExtensions"); const telemetryLoggerConstants_1 = require("../telemetryLoggerConstants"); const adaptive_expressions_1 = require("adaptive-expressions"); const botbuilder_dialogs_1 = require("botbuilder-dialogs"); /** * Begin a Skill. */ class BeginSkill extends botbuilder_dialogs_1.SkillDialog { /** * @param property The key of the conditional selector configuration. * @returns The converter for the selector configuration. */ getConverter(property) { switch (property) { case 'disabled': return new adaptive_expressions_1.BoolExpressionConverter(); case 'activityProcessed': return new adaptive_expressions_1.BoolExpressionConverter(); case 'resultProperty': return new adaptive_expressions_1.StringExpressionConverter(); case 'botId': return new adaptive_expressions_1.StringExpressionConverter(); case 'skillHostEndpoint': return new adaptive_expressions_1.StringExpressionConverter(); case 'skillAppId': return new adaptive_expressions_1.StringExpressionConverter(); case 'skillEndpoint': return new adaptive_expressions_1.StringExpressionConverter(); case 'activity': return new converters_1.ActivityTemplateConverter(); case 'connectionName': return new adaptive_expressions_1.StringExpressionConverter(); case 'allowInterruptions': return new adaptive_expressions_1.BoolExpressionConverter(); default: return undefined; } } /** * Creates a new `BeginSkillDialog instance. * * @param options Optional options used to configure the skill dialog. */ constructor(options) { super(Object.assign({ skill: {} }, options)); /** * Value indicating whether the new dialog should process the activity. * * @remarks * The default for this will be true, which means the new dialog should not look at the activity. * You can set this to false to dispatch the activity to the new dialog. */ this.activityProcessed = new adaptive_expressions_1.BoolExpression(true); /** * The Microsoft App ID that will be calling the skill. * * @remarks * Defauls to a value of `=settings.MicrosoftAppId` which retrievs the bots ID from settings. */ this.botId = new adaptive_expressions_1.StringExpression('=settings.MicrosoftAppId'); /** * The callback Url for the skill host. * * @remarks * Defauls to a value of `=settings.SkillHostEndpoint` which retrieves the endpoint from settings. */ this.skillHostEndpoint = new adaptive_expressions_1.StringExpression('=settings.SkillHostEndpoint'); // Used to cache DialogOptions for multi-turn calls across servers. this._dialogOptionsStateKey = `${this.constructor.name}.dialogOptionsData`; } /** * Called when the [Dialog](xref:botbuilder-dialogs.Dialog) is started and pushed onto the dialog stack. * * @param dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation. * @param options Optional. Initial information to pass to the dialog. * @returns A `Promise` representing the asynchronous operation. */ beginDialog(dc, options) { const _super = Object.create(null, { beginDialog: { get: () => super.beginDialog } }); return __awaiter(this, void 0, void 0, function* () { const dcState = dc.state; if (this.disabled && this.disabled.getValue(dcState)) { return yield dc.endDialog(); } // Setup the skill to call const botId = this.botId.getValue(dcState); const skillHostEndpoint = this.skillHostEndpoint.getValue(dcState); if (botId) { this.dialogOptions.botId = botId; } if (skillHostEndpoint) { this.dialogOptions.skillHostEndpoint = skillHostEndpoint; } if (this.skillAppId) { this.dialogOptions.skill.id = this.dialogOptions.skill.appId = this.skillAppId.getValue(dcState); } if (this.skillEndpoint) { this.dialogOptions.skill.skillEndpoint = this.skillEndpoint.getValue(dcState); } if (this.connectionName) { this.dialogOptions.connectionName = this.connectionName.getValue(dcState); } if (!this.dialogOptions.conversationState) { this.dialogOptions.conversationState = dc.context.turnState.get('ConversationState'); } if (!this.dialogOptions.skillClient) { this.dialogOptions.skillClient = dc.context.turnState.get(skillExtensions_1.skillClientKey); } if (!this.dialogOptions.conversationIdFactory) { this.dialogOptions.conversationIdFactory = dc.context.turnState.get(skillExtensions_1.skillConversationIdFactoryKey); } // Store the initialized dialogOptions in state so we can restore these values when the dialog is resumed. dc.activeDialog.state[this._dialogOptionsStateKey] = this.dialogOptions; const skipProperties = dc.context.turnState.get(botbuilder_1.CACHED_BOT_STATE_SKIP_PROPERTIES_HANDLER_KEY); const props = ['conversationIdFactory', 'conversationState']; skipProperties(this._dialogOptionsStateKey, props); // Get the activity to send to the skill. options = {}; if (this.activityProcessed.getValue(dcState) && this.activity) { // The parent consumed the activity in context, use the Activity property to start the skill. const activity = yield this.activity.bind(dc, dcState); this.telemetryClient.trackEvent({ name: telemetryLoggerConstants_1.TelemetryLoggerConstants.GeneratorResultEvent, properties: { template: this.activity, result: activity || '', }, }); options.activity = activity; } else { // Send the turn context activity to the skill (pass through). options.activity = dc.context.activity; } // Call the base to invoke the skill return yield _super.beginDialog.call(this, dc, options); }); } /** * Called when the [Dialog](xref:botbuilder-dialogs.Dialog) is _continued_, where it is the active dialog and the * user replies with a new activity. * * @param dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation. * @returns A `Promise` representing the asynchronous operation. */ continueDialog(dc) { const _super = Object.create(null, { continueDialog: { get: () => super.continueDialog } }); return __awaiter(this, void 0, void 0, function* () { this.loadDialogOptions(dc.context, dc.activeDialog); const activity = dc.context.activity; if (activity.type == botbuilder_1.ActivityTypes.EndOfConversation) { // Capture the result of the dialog if the property is set if (this.resultProperty && activity.value) { const dcState = dc.state; dc.state.setValue(this.resultProperty.getValue(dcState), activity.value); } } return yield _super.continueDialog.call(this, dc); }); } /** * Called when the [Dialog](xref:botbuilder-dialogs.Dialog) should re-prompt the user for input. * * @param turnContext [TurnContext](xref:botbuilder-core.TurnContext), the context object for this turn. * @param instance [DialogInstance](xref:botbuilder-dialogs.DialogInstance), state information for this dialog. * @returns A `Promise` representing the asynchronous operation. */ repromptDialog(turnContext, instance) { const _super = Object.create(null, { repromptDialog: { get: () => super.repromptDialog } }); return __awaiter(this, void 0, void 0, function* () { this.loadDialogOptions(turnContext, instance); return yield _super.repromptDialog.call(this, turnContext, instance); }); } /** * Called when a child [Dialog](xref:botbuilder-dialogs.Dialog) completed its turn, returning control to this dialog. * * @param dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation. * @param reason [DialogReason](xref:botbuilder-dialogs.DialogReason), reason why the dialog resumed. * @param result Optional. Value returned from the dialog that was called. The type * of the value returned is dependent on the child dialog. * @returns A `Promise` representing the asynchronous operation. */ resumeDialog(dc, reason, result) { const _super = Object.create(null, { resumeDialog: { get: () => super.resumeDialog } }); return __awaiter(this, void 0, void 0, function* () { this.loadDialogOptions(dc.context, dc.activeDialog); return yield _super.resumeDialog.call(this, dc, reason, result); }); } /** * Called when the [Dialog](xref:botbuilder-dialogs.Dialog) is ending. * * @param turnContext [TurnContext](xref:botbuilder-core.TurnContext), the context object for this turn. * @param instance [DialogInstance](xref:botbuilder-dialogs.DialogInstance), state information associated with the instance of this dialog on the dialog stack. * @param reason [DialogReason](xref:botbuilder-dialogs.DialogReason), reason why the dialog ended. * @returns A `Promise` representing the asynchronous operation. */ endDialog(turnContext, instance, reason) { const _super = Object.create(null, { endDialog: { get: () => super.endDialog } }); return __awaiter(this, void 0, void 0, function* () { this.loadDialogOptions(turnContext, instance); return yield _super.endDialog.call(this, turnContext, instance, reason); }); } /** * @protected * Builds the compute Id for the [Dialog](xref:botbuilder-dialogs.Dialog). * @returns A `string` representing the compute Id. */ onComputeId() { const appId = this.skillAppId ? this.skillAppId.toString() : ''; if (this.activity instanceof templates_1.ActivityTemplate) { return `BeginSkill['${appId}','${botbuilder_1.StringUtils.ellipsis(this.activity.template.trim(), 30)}']`; } return `BeginSkill['${appId}','${botbuilder_1.StringUtils.ellipsis(this.activity && this.activity.toString().trim(), 30)}']`; } onPreBubbleEvent(dc, e) { return __awaiter(this, void 0, void 0, function* () { if (e.name === botbuilder_dialogs_1.DialogEvents.activityReceived && dc.context.activity.type === botbuilder_1.ActivityTypes.Message) { // Ask parent to perform recognition. if (dc.parent) { yield dc.parent.emitEvent(adaptiveEvents_1.AdaptiveEvents.recognizeUtterance, dc.context.activity, false); } // Should we allow interruptions. let canInterrupt = true; if (this.allowInterruptions) { const { value: allowInterruptions, error } = this.allowInterruptions.tryGetValue(dc.state); canInterrupt = !error && allowInterruptions; } // Stop bubbling if interruptions are NOT allowed return !canInterrupt; } return false; }); } /** * @private * Regenerates the [SkillDialog.DialogOptions](xref:botbuilder-dialogs.SkillDialog.DialogOptions) based on the values used during the `BeginDialog` call. * @remarks The [Dialog](xref:botbuilder-dialogs.Dialog) can be resumed in another server or after redeploying the bot, this code ensure that the options used are the ones * used to call `BeginDialog`. * Also, if `ContinueConversation` or other methods are called on a server different than the one where `BeginDialog` was called, * `DialogOptions` will be empty and this code will make sure it has the right value. */ loadDialogOptions(context, instance) { const dialogOptions = instance.state[this._dialogOptionsStateKey]; this.dialogOptions.botId = dialogOptions.botId; this.dialogOptions.skillHostEndpoint = dialogOptions.skillHostEndpoint; this.dialogOptions.conversationIdFactory = context.turnState.get(skillExtensions_1.skillConversationIdFactoryKey); if (this.dialogOptions.conversationIdFactory == null) { throw new ReferenceError('Unable to locate skillConversationIdFactoryBase in HostContext.'); } this.dialogOptions.skillClient = context.turnState.get(skillExtensions_1.skillClientKey); if (this.dialogOptions.skillClient == null) { throw new ReferenceError('Unable to get an instance of skillHttpClient from turnState.'); } this.dialogOptions.conversationState = context.turnState.get('ConversationState'); if (this.dialogOptions.conversationState == null) { throw new ReferenceError('Unable to get an instance of conversationState from turnState.'); } this.dialogOptions.connectionName = dialogOptions.connectionName; // Set the skill to call. this.dialogOptions.skill = dialogOptions.skill; } } exports.BeginSkill = BeginSkill; BeginSkill.$kind = 'Microsoft.BeginSkill'; //# sourceMappingURL=beginSkill.js.map