botbuilder
Version:
Bot Builder is a framework for building rich bots on virtually any platform.
429 lines • 18 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.InspectionState = exports.InspectionMiddleware = void 0;
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const uuid_1 = require("uuid");
const botframework_connector_1 = require("botframework-connector");
const botbuilder_core_1 = require("botbuilder-core");
const teamsActivityHelpers_1 = require("./teamsActivityHelpers");
/** @private */
class TraceActivity {
static makeCommandActivity(command) {
return {
type: botbuilder_core_1.ActivityTypes.Trace,
timestamp: new Date(),
name: 'Command',
label: 'Command',
value: command,
valueType: 'https://www.botframework.com/schemas/command',
};
}
static fromActivity(activity, name, label) {
return {
type: botbuilder_core_1.ActivityTypes.Trace,
timestamp: new Date(),
name: name,
label: label,
value: activity,
valueType: 'https://www.botframework.com/schemas/activity',
};
}
static fromState(botState) {
return {
type: botbuilder_core_1.ActivityTypes.Trace,
timestamp: new Date(),
name: 'BotState',
label: 'Bot State',
value: botState,
valueType: 'https://www.botframework.com/schemas/botState',
};
}
static fromConversationReference(conversationReference) {
return {
type: botbuilder_core_1.ActivityTypes.Trace,
timestamp: new Date(),
name: 'Deleted Message',
label: 'MessageDelete',
value: conversationReference,
valueType: 'https://www.botframework.com/schemas/conversationReference',
};
}
static fromError(errorMessage) {
return {
type: botbuilder_core_1.ActivityTypes.Trace,
timestamp: new Date(),
name: 'Turn Error',
label: 'TurnError',
value: errorMessage,
valueType: 'https://www.botframework.com/schemas/error',
};
}
}
/** @private */
class InterceptionMiddleware {
/**
* Implement middleware signature
*
* @param turnContext {TurnContext} An incoming TurnContext object.
* @param next {function} The next delegate function.
*/
onTurn(turnContext, next) {
return __awaiter(this, void 0, void 0, function* () {
const { shouldForwardToApplication, shouldIntercept } = yield this.invokeInbound(turnContext, TraceActivity.fromActivity(turnContext.activity, 'ReceivedActivity', 'Received Activity'));
if (shouldIntercept) {
turnContext.onSendActivities((ctx, activities, nextSend) => __awaiter(this, void 0, void 0, function* () {
const traceActivities = [];
activities.forEach((activity) => {
traceActivities.push(TraceActivity.fromActivity(activity, 'SentActivity', 'Sent Activity'));
});
yield this.invokeOutbound(ctx, traceActivities);
return yield nextSend();
}));
turnContext.onUpdateActivity((ctx, activity, nextUpdate) => __awaiter(this, void 0, void 0, function* () {
const traceActivity = TraceActivity.fromActivity(activity, 'MessageUpdate', 'Updated Message');
yield this.invokeOutbound(ctx, [traceActivity]);
return yield nextUpdate();
}));
turnContext.onDeleteActivity((ctx, reference, nextDelete) => __awaiter(this, void 0, void 0, function* () {
const traceActivity = TraceActivity.fromConversationReference(reference);
yield this.invokeOutbound(ctx, [traceActivity]);
return yield nextDelete();
}));
}
if (shouldForwardToApplication) {
try {
yield next();
}
catch (err) {
const traceActivity = TraceActivity.fromError(err.toString());
yield this.invokeOutbound(turnContext, [traceActivity]);
throw err;
}
}
if (shouldIntercept) {
yield this.invokeTraceState(turnContext);
}
});
}
invokeInbound(turnContext, traceActivity) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield this.inbound(turnContext, traceActivity);
}
catch (err) {
console.warn(`Exception in inbound interception ${err}`);
return { shouldForwardToApplication: true, shouldIntercept: false };
}
});
}
invokeOutbound(turnContext, traceActivities) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.outbound(turnContext, traceActivities);
}
catch (err) {
console.warn(`Exception in outbound interception ${err}`);
}
});
}
invokeTraceState(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.traceState(turnContext);
}
catch (err) {
console.warn(`Exception in state interception ${err}`);
}
});
}
}
/**
* InspectionMiddleware for emulator inspection of runtime Activities and BotState.
*
* @remarks
* InspectionMiddleware for emulator inspection of runtime Activities and BotState.
* @deprecated This class will be removed in a future version of the framework.
*/
class InspectionMiddleware extends InterceptionMiddleware {
/**
* Create the Inspection middleware for sending trace activities out to an emulator session
*
* @param inspectionState A state management object for inspection state.
* @param userState A state management object for user state.
* @param conversationState A state management object for conversation state.
* @param credentials The authentication credentials.
*/
constructor(inspectionState, userState, conversationState, credentials) {
super();
this.inspectionState = inspectionState;
this.inspectionStateAccessor = inspectionState.createProperty('InspectionSessionByStatus');
this.userState = userState;
this.conversationState = conversationState;
credentials = Object.assign({ appId: '', appPassword: '' }, credentials);
this.credentials = new botframework_connector_1.MicrosoftAppCredentials(credentials.appId, credentials.appPassword);
}
/**
* Indentifies open and attach commands and calls the appropriate method.
*
* @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
* @returns True if the command is open or attached, otherwise false.
*/
processCommand(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
if (turnContext.activity.type == botbuilder_core_1.ActivityTypes.Message && turnContext.activity.text !== undefined) {
const originalText = turnContext.activity.text;
botbuilder_core_1.TurnContext.removeRecipientMention(turnContext.activity);
const command = turnContext.activity.text.trim().split(' ');
if (command.length > 1 && command[0] === InspectionMiddleware.command) {
if (command.length === 2 && command[1] === 'open') {
yield this.processOpenCommand(turnContext);
return true;
}
if (command.length === 3 && command[1] === 'attach') {
yield this.processAttachCommand(turnContext, command[2]);
return true;
}
}
turnContext.activity.text = originalText;
}
return false;
});
}
/**
* Processes inbound activities.
*
* @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
* @param traceActivity The trace activity.
* @returns {Promise<any>} A promise representing the asynchronous operation.
*/
inbound(turnContext, traceActivity) {
return __awaiter(this, void 0, void 0, function* () {
if (yield this.processCommand(turnContext)) {
return { shouldForwardToApplication: false, shouldIntercept: false };
}
const session = yield this.findSession(turnContext);
if (session !== undefined) {
if (yield this.invokeSend(turnContext, session, traceActivity)) {
return { shouldForwardToApplication: true, shouldIntercept: true };
}
else {
return { shouldForwardToApplication: true, shouldIntercept: false };
}
}
else {
return { shouldForwardToApplication: true, shouldIntercept: false };
}
});
}
/**
* Processes outbound activities.
*
* @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
* @param traceActivities A collection of trace activities.
*/
outbound(turnContext, traceActivities) {
return __awaiter(this, void 0, void 0, function* () {
const session = yield this.findSession(turnContext);
if (session !== undefined) {
for (let i = 0; i < traceActivities.length; i++) {
const traceActivity = traceActivities[i];
if (!(yield this.invokeSend(turnContext, session, traceActivity))) {
break;
}
}
}
});
}
/**
* Processes the state management object.
*
* @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
*/
traceState(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
const session = yield this.findSession(turnContext);
if (session !== undefined) {
if (this.userState !== undefined) {
yield this.userState.load(turnContext, false);
}
if (this.conversationState != undefined) {
yield this.conversationState.load(turnContext, false);
}
const botState = {};
if (this.userState !== undefined) {
botState.userState = this.userState.get(turnContext);
}
if (this.conversationState !== undefined) {
botState.conversationState = this.conversationState.get(turnContext);
}
yield this.invokeSend(turnContext, session, TraceActivity.fromState(botState));
}
});
}
/**
* @private
*/
processOpenCommand(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
const sessionId = this.openCommand(sessions, botbuilder_core_1.TurnContext.getConversationReference(turnContext.activity));
yield turnContext.sendActivity(TraceActivity.makeCommandActivity(`${InspectionMiddleware.command} attach ${sessionId}`));
yield this.inspectionState.saveChanges(turnContext, false);
});
}
/**
* @private
*/
processAttachCommand(turnContext, sessionId) {
return __awaiter(this, void 0, void 0, function* () {
const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
if (this.attachCommand(this.getAttachId(turnContext.activity), sessions, sessionId)) {
yield turnContext.sendActivity('Attached to session, all traffic is being replicated for inspection.');
}
else {
yield turnContext.sendActivity(`Open session with id ${sessionId} does not exist.`);
}
yield this.inspectionState.saveChanges(turnContext, false);
});
}
/**
* @private
*/
openCommand(sessions, conversationReference) {
const sessionId = (0, uuid_1.v4)();
sessions.openedSessions[sessionId] = conversationReference;
return sessionId;
}
/**
* @private
*/
attachCommand(attachId, sessions, sessionId) {
const inspectionSessionState = sessions.openedSessions[sessionId];
if (inspectionSessionState !== undefined) {
sessions.attachedSessions[attachId] = inspectionSessionState;
delete sessions.openedSessions[sessionId];
return true;
}
return false;
}
/**
* @private
*/
findSession(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
const conversationReference = sessions.attachedSessions[this.getAttachId(turnContext.activity)];
if (conversationReference !== undefined) {
return new InspectionSession(conversationReference, this.credentials);
}
return undefined;
});
}
/**
* @private
*/
invokeSend(turnContext, session, activity) {
return __awaiter(this, void 0, void 0, function* () {
if (yield session.send(activity)) {
return true;
}
else {
yield this.cleanUpSession(turnContext);
return false;
}
});
}
/**
* @private
*/
cleanUpSession(turnContext) {
return __awaiter(this, void 0, void 0, function* () {
const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
delete sessions.attachedSessions[this.getAttachId(turnContext.activity)];
yield this.inspectionState.saveChanges(turnContext, false);
});
}
/**
* @private
*/
getAttachId(activity) {
// If we are running in a Microsoft Teams Team the conversation Id will reflect a particular thread the bot is in.
// So if we are in a Team then we will associate the "attach" with the Team Id rather than the more restrictive conversation Id.
const teamId = (0, teamsActivityHelpers_1.teamsGetTeamId)(activity);
return teamId ? teamId : activity.conversation.id;
}
}
exports.InspectionMiddleware = InspectionMiddleware;
InspectionMiddleware.command = '/INSPECT';
/** @private */
class InspectionSession {
constructor(conversationReference, credentials) {
this.conversationReference = conversationReference;
this.connectorClient = new botframework_connector_1.ConnectorClient(credentials, { baseUri: conversationReference.serviceUrl });
}
send(activity) {
return __awaiter(this, void 0, void 0, function* () {
botbuilder_core_1.TurnContext.applyConversationReference(activity, this.conversationReference);
try {
yield this.connectorClient.conversations.sendToConversation(activity.conversation.id, activity);
}
catch (_a) {
return false;
}
return true;
});
}
}
/** @private */
class InspectionSessionsByStatus {
constructor() {
this.openedSessions = {};
this.attachedSessions = {};
}
}
InspectionSessionsByStatus.DefaultValue = new InspectionSessionsByStatus();
/**
* InspectionState for use by the InspectionMiddleware for emulator inspection of runtime Activities and BotState.
*
* @remarks
* InspectionState for use by the InspectionMiddleware for emulator inspection of runtime Activities and BotState.
* @deprecated This class will be removed in a future version of the framework.
*/
class InspectionState extends botbuilder_core_1.BotState {
/**
* Creates a new instance of the [InspectionState](xref:botbuilder.InspectionState) class.
*
* @param storage The [Storage](xref:botbuilder-core.Storage) layer this state management object will use to store and retrieve state.
*/
constructor(storage) {
super(storage, (context) => {
return Promise.resolve(this.getStorageKey(context));
});
}
/**
* Gets the key to use when reading and writing state to and from storage.
*
* @param _turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
* @returns The storage key.
*/
getStorageKey(_turnContext) {
return 'InspectionState';
}
}
exports.InspectionState = InspectionState;
//# sourceMappingURL=inspectionMiddleware.js.map