botbuilder-dialogs-adaptive
Version:
Rule system for the Microsoft BotBuilder dialog system.
301 lines • 15.4 kB
JavaScript
"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