UNPKG

17.4 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11Object.defineProperty(exports, "__esModule", { value: true });
12exports.InspectionState = exports.InspectionMiddleware = void 0;
13/**
14 * @module botbuilder
15 */
16/**
17 * Copyright (c) Microsoft Corporation. All rights reserved.
18 * Licensed under the MIT License.
19 */
20const uuid_1 = require("uuid");
21const botframework_connector_1 = require("botframework-connector");
22const botbuilder_core_1 = require("botbuilder-core");
23const teamsActivityHelpers_1 = require("./teamsActivityHelpers");
24/** @private */
25class TraceActivity {
26 static makeCommandActivity(command) {
27 return {
28 type: botbuilder_core_1.ActivityTypes.Trace,
29 timestamp: new Date(),
30 name: 'Command',
31 label: 'Command',
32 value: command,
33 valueType: 'https://www.botframework.com/schemas/command',
34 };
35 }
36 static fromActivity(activity, name, label) {
37 return {
38 type: botbuilder_core_1.ActivityTypes.Trace,
39 timestamp: new Date(),
40 name: name,
41 label: label,
42 value: activity,
43 valueType: 'https://www.botframework.com/schemas/activity',
44 };
45 }
46 static fromState(botState) {
47 return {
48 type: botbuilder_core_1.ActivityTypes.Trace,
49 timestamp: new Date(),
50 name: 'BotState',
51 label: 'Bot State',
52 value: botState,
53 valueType: 'https://www.botframework.com/schemas/botState',
54 };
55 }
56 static fromConversationReference(conversationReference) {
57 return {
58 type: botbuilder_core_1.ActivityTypes.Trace,
59 timestamp: new Date(),
60 name: 'Deleted Message',
61 label: 'MessageDelete',
62 value: conversationReference,
63 valueType: 'https://www.botframework.com/schemas/conversationReference',
64 };
65 }
66 static fromError(errorMessage) {
67 return {
68 type: botbuilder_core_1.ActivityTypes.Trace,
69 timestamp: new Date(),
70 name: 'Turn Error',
71 label: 'TurnError',
72 value: errorMessage,
73 valueType: 'https://www.botframework.com/schemas/error',
74 };
75 }
76}
77/** @private */
78class InterceptionMiddleware {
79 /** Implement middleware signature
80 * @param context {TurnContext} An incoming TurnContext object.
81 * @param next {function} The next delegate function.
82 */
83 onTurn(turnContext, next) {
84 return __awaiter(this, void 0, void 0, function* () {
85 const { shouldForwardToApplication, shouldIntercept } = yield this.invokeInbound(turnContext, TraceActivity.fromActivity(turnContext.activity, 'ReceivedActivity', 'Received Activity'));
86 if (shouldIntercept) {
87 turnContext.onSendActivities((ctx, activities, nextSend) => __awaiter(this, void 0, void 0, function* () {
88 const traceActivities = [];
89 activities.forEach((activity) => {
90 traceActivities.push(TraceActivity.fromActivity(activity, 'SentActivity', 'Sent Activity'));
91 });
92 yield this.invokeOutbound(ctx, traceActivities);
93 return yield nextSend();
94 }));
95 turnContext.onUpdateActivity((ctx, activity, nextUpdate) => __awaiter(this, void 0, void 0, function* () {
96 const traceActivity = TraceActivity.fromActivity(activity, 'MessageUpdate', 'Updated Message');
97 yield this.invokeOutbound(ctx, [traceActivity]);
98 return yield nextUpdate();
99 }));
100 turnContext.onDeleteActivity((ctx, reference, nextDelete) => __awaiter(this, void 0, void 0, function* () {
101 const traceActivity = TraceActivity.fromConversationReference(reference);
102 yield this.invokeOutbound(ctx, [traceActivity]);
103 return yield nextDelete();
104 }));
105 }
106 if (shouldForwardToApplication) {
107 try {
108 yield next();
109 }
110 catch (err) {
111 const traceActivity = TraceActivity.fromError(err.toString());
112 yield this.invokeOutbound(turnContext, [traceActivity]);
113 throw err;
114 }
115 }
116 if (shouldIntercept) {
117 yield this.invokeTraceState(turnContext);
118 }
119 });
120 }
121 invokeInbound(turnContext, traceActivity) {
122 return __awaiter(this, void 0, void 0, function* () {
123 try {
124 return yield this.inbound(turnContext, traceActivity);
125 }
126 catch (err) {
127 console.warn(`Exception in inbound interception ${err}`);
128 return { shouldForwardToApplication: true, shouldIntercept: false };
129 }
130 });
131 }
132 invokeOutbound(turnContext, traceActivities) {
133 return __awaiter(this, void 0, void 0, function* () {
134 try {
135 yield this.outbound(turnContext, traceActivities);
136 }
137 catch (err) {
138 console.warn(`Exception in outbound interception ${err}`);
139 }
140 });
141 }
142 invokeTraceState(turnContext) {
143 return __awaiter(this, void 0, void 0, function* () {
144 try {
145 yield this.traceState(turnContext);
146 }
147 catch (err) {
148 console.warn(`Exception in state interception ${err}`);
149 }
150 });
151 }
152}
153/**
154 * InspectionMiddleware for emulator inspection of runtime Activities and BotState.
155 *
156 * @remarks
157 * InspectionMiddleware for emulator inspection of runtime Activities and BotState.
158 *
159 */
160class InspectionMiddleware extends InterceptionMiddleware {
161 /**
162 * Create the Inspection middleware for sending trace activities out to an emulator session
163 */
164 constructor(inspectionState, userState, conversationState, credentials) {
165 super();
166 this.inspectionState = inspectionState;
167 this.inspectionStateAccessor = inspectionState.createProperty('InspectionSessionByStatus');
168 this.userState = userState;
169 this.conversationState = conversationState;
170 credentials = Object.assign({ appId: '', appPassword: '' }, credentials);
171 this.credentials = new botframework_connector_1.MicrosoftAppCredentials(credentials.appId, credentials.appPassword);
172 }
173 /**
174 * Indentifies open and attach commands and calls the appropriate method.
175 * @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
176 * @returns True if the command is open or attached, otherwise false.
177 */
178 processCommand(turnContext) {
179 return __awaiter(this, void 0, void 0, function* () {
180 if (turnContext.activity.type == botbuilder_core_1.ActivityTypes.Message && turnContext.activity.text !== undefined) {
181 const originalText = turnContext.activity.text;
182 botbuilder_core_1.TurnContext.removeRecipientMention(turnContext.activity);
183 const command = turnContext.activity.text.trim().split(' ');
184 if (command.length > 1 && command[0] === InspectionMiddleware.command) {
185 if (command.length === 2 && command[1] === 'open') {
186 yield this.processOpenCommand(turnContext);
187 return true;
188 }
189 if (command.length === 3 && command[1] === 'attach') {
190 yield this.processAttachCommand(turnContext, command[2]);
191 return true;
192 }
193 }
194 turnContext.activity.text = originalText;
195 }
196 return false;
197 });
198 }
199 /**
200 * Processes inbound activities.
201 * @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
202 * @param traceActivity The trace activity.
203 */
204 inbound(turnContext, traceActivity) {
205 return __awaiter(this, void 0, void 0, function* () {
206 if (yield this.processCommand(turnContext)) {
207 return { shouldForwardToApplication: false, shouldIntercept: false };
208 }
209 const session = yield this.findSession(turnContext);
210 if (session !== undefined) {
211 if (yield this.invokeSend(turnContext, session, traceActivity)) {
212 return { shouldForwardToApplication: true, shouldIntercept: true };
213 }
214 else {
215 return { shouldForwardToApplication: true, shouldIntercept: false };
216 }
217 }
218 else {
219 return { shouldForwardToApplication: true, shouldIntercept: false };
220 }
221 });
222 }
223 /**
224 * Processes outbound activities.
225 * @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
226 * @param traceActivities A collection of trace activities.
227 */
228 outbound(turnContext, traceActivities) {
229 return __awaiter(this, void 0, void 0, function* () {
230 const session = yield this.findSession(turnContext);
231 if (session !== undefined) {
232 for (let i = 0; i < traceActivities.length; i++) {
233 const traceActivity = traceActivities[i];
234 if (!(yield this.invokeSend(turnContext, session, traceActivity))) {
235 break;
236 }
237 }
238 }
239 });
240 }
241 /**
242 * Processes the state management object.
243 * @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
244 */
245 traceState(turnContext) {
246 return __awaiter(this, void 0, void 0, function* () {
247 const session = yield this.findSession(turnContext);
248 if (session !== undefined) {
249 if (this.userState !== undefined) {
250 yield this.userState.load(turnContext, false);
251 }
252 if (this.conversationState != undefined) {
253 yield this.conversationState.load(turnContext, false);
254 }
255 const botState = {};
256 if (this.userState !== undefined) {
257 botState.userState = this.userState.get(turnContext);
258 }
259 if (this.conversationState !== undefined) {
260 botState.conversationState = this.conversationState.get(turnContext);
261 }
262 yield this.invokeSend(turnContext, session, TraceActivity.fromState(botState));
263 }
264 });
265 }
266 /**
267 * @private
268 */
269 processOpenCommand(turnContext) {
270 return __awaiter(this, void 0, void 0, function* () {
271 const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
272 const sessionId = this.openCommand(sessions, botbuilder_core_1.TurnContext.getConversationReference(turnContext.activity));
273 yield turnContext.sendActivity(TraceActivity.makeCommandActivity(`${InspectionMiddleware.command} attach ${sessionId}`));
274 yield this.inspectionState.saveChanges(turnContext, false);
275 });
276 }
277 /**
278 * @private
279 */
280 processAttachCommand(turnContext, sessionId) {
281 return __awaiter(this, void 0, void 0, function* () {
282 const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
283 if (this.attachCommand(this.getAttachId(turnContext.activity), sessions, sessionId)) {
284 yield turnContext.sendActivity('Attached to session, all traffic is being replicated for inspection.');
285 }
286 else {
287 yield turnContext.sendActivity(`Open session with id ${sessionId} does not exist.`);
288 }
289 yield this.inspectionState.saveChanges(turnContext, false);
290 });
291 }
292 /**
293 * @private
294 */
295 openCommand(sessions, conversationReference) {
296 const sessionId = uuid_1.v4();
297 sessions.openedSessions[sessionId] = conversationReference;
298 return sessionId;
299 }
300 /**
301 * @private
302 */
303 attachCommand(attachId, sessions, sessionId) {
304 const inspectionSessionState = sessions.openedSessions[sessionId];
305 if (inspectionSessionState !== undefined) {
306 sessions.attachedSessions[attachId] = inspectionSessionState;
307 delete sessions.openedSessions[sessionId];
308 return true;
309 }
310 return false;
311 }
312 /**
313 * @private
314 */
315 findSession(turnContext) {
316 return __awaiter(this, void 0, void 0, function* () {
317 const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
318 const conversationReference = sessions.attachedSessions[this.getAttachId(turnContext.activity)];
319 if (conversationReference !== undefined) {
320 return new InspectionSession(conversationReference, this.credentials);
321 }
322 return undefined;
323 });
324 }
325 /**
326 * @private
327 */
328 invokeSend(turnContext, session, activity) {
329 return __awaiter(this, void 0, void 0, function* () {
330 if (yield session.send(activity)) {
331 return true;
332 }
333 else {
334 yield this.cleanUpSession(turnContext);
335 return false;
336 }
337 });
338 }
339 /**
340 * @private
341 */
342 cleanUpSession(turnContext) {
343 return __awaiter(this, void 0, void 0, function* () {
344 const sessions = yield this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
345 delete sessions.attachedSessions[this.getAttachId(turnContext.activity)];
346 yield this.inspectionState.saveChanges(turnContext, false);
347 });
348 }
349 /**
350 * @private
351 */
352 getAttachId(activity) {
353 // If we are running in a Microsoft Teams Team the conversation Id will reflect a particular thread the bot is in.
354 // So if we are in a Team then we will associate the "attach" with the Team Id rather than the more restrictive conversation Id.
355 const teamId = teamsActivityHelpers_1.teamsGetTeamId(activity);
356 return teamId ? teamId : activity.conversation.id;
357 }
358}
359exports.InspectionMiddleware = InspectionMiddleware;
360InspectionMiddleware.command = '/INSPECT';
361/** @private */
362class InspectionSession {
363 constructor(conversationReference, credentials) {
364 this.conversationReference = conversationReference;
365 this.connectorClient = new botframework_connector_1.ConnectorClient(credentials, { baseUri: conversationReference.serviceUrl });
366 }
367 send(activity) {
368 return __awaiter(this, void 0, void 0, function* () {
369 botbuilder_core_1.TurnContext.applyConversationReference(activity, this.conversationReference);
370 try {
371 yield this.connectorClient.conversations.sendToConversation(activity.conversation.id, activity);
372 }
373 catch (err) {
374 return false;
375 }
376 return true;
377 });
378 }
379}
380/** @private */
381class InspectionSessionsByStatus {
382 constructor() {
383 this.openedSessions = {};
384 this.attachedSessions = {};
385 }
386}
387InspectionSessionsByStatus.DefaultValue = new InspectionSessionsByStatus();
388/**
389 * InspectionState for use by the InspectionMiddleware for emulator inspection of runtime Activities and BotState.
390 *
391 * @remarks
392 * InspectionState for use by the InspectionMiddleware for emulator inspection of runtime Activities and BotState.
393 *
394 */
395class InspectionState extends botbuilder_core_1.BotState {
396 /**
397 * Creates a new instance of the [InspectionState](xref:botbuilder.InspectionState) class.
398 * @param storage The [Storage](xref:botbuilder-core.Storage) layer this state management object will use to store and retrieve state.
399 */
400 constructor(storage) {
401 super(storage, (context) => {
402 return Promise.resolve(this.getStorageKey(context));
403 });
404 }
405 /**
406 * Gets the key to use when reading and writing state to and from storage.
407 * @param turnContext The [TurnContext](xref:botbuilder-core.TurnContext) for this turn.
408 */
409 getStorageKey(turnContext) {
410 return 'InspectionState';
411 }
412}
413exports.InspectionState = InspectionState;
414//# sourceMappingURL=inspectionMiddleware.js.map
\No newline at end of file