429 lines
No EOL
18 KiB
JavaScript
429 lines
No EOL
18 KiB
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
|