UNPKG

botbuilder-core

Version:

Core components for Microsoft Bot Builder. Components in this library can run either in a browser or on the server.

185 lines 8.27 kB
"use strict"; /** * @module botbuilder */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ 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.ConsoleTranscriptLogger = exports.TranscriptLoggerMiddleware = void 0; const botframework_schema_1 = require("botframework-schema"); const turnContext_1 = require("./turnContext"); /** * Logs incoming and outgoing activities to a TranscriptStore. */ class TranscriptLoggerMiddleware { /** * Middleware for logging incoming and outgoing activities to a transcript store. * * @param logger Transcript logger */ constructor(logger) { if (!logger) { throw new Error('TranscriptLoggerMiddleware requires a TranscriptLogger instance.'); } this.logger = logger; } /** * Initialization for middleware turn. * * @param context Context for the current turn of conversation with the user. * @param next Function to call at the end of the middleware chain. */ onTurn(context, next) { return __awaiter(this, void 0, void 0, function* () { const transcript = []; // log incoming activity at beginning of turn if (context.activity) { if (!context.activity.from.role) { context.activity.from.role = botframework_schema_1.RoleTypes.User; } this.logActivity(transcript, this.cloneActivity(context.activity)); } // hook up onSend pipeline context.onSendActivities((ctx, activities, next) => __awaiter(this, void 0, void 0, function* () { // Run full pipeline. const responses = yield next(); activities.forEach((activity, index) => { const clonedActivity = this.cloneActivity(activity); clonedActivity.id = responses && responses[index] ? responses[index].id : clonedActivity.id; // For certain channels, a ResourceResponse with an id is not always sent to the bot. // This fix uses the timestamp on the activity to populate its id for logging the transcript. // If there is no outgoing timestamp, the current time for the bot is used for the activity.id. // See https://github.com/microsoft/botbuilder-js/issues/1122 if (!clonedActivity.id) { const prefix = `g_${Math.random().toString(36).slice(2, 8)}`; if (clonedActivity.timestamp) { clonedActivity.id = `${prefix}${new Date(clonedActivity.timestamp).getTime().toString()}`; } else { clonedActivity.id = `${prefix}${new Date().getTime().toString()}`; } } this.logActivity(transcript, clonedActivity); }); return responses; })); // hook up update activity pipeline context.onUpdateActivity((ctx, activity, next) => __awaiter(this, void 0, void 0, function* () { // run full pipeline const response = yield next(); // add Message Update activity const updateActivity = this.cloneActivity(activity); updateActivity.type = botframework_schema_1.ActivityTypes.MessageUpdate; this.logActivity(transcript, updateActivity); return response; })); // hook up delete activity pipeline context.onDeleteActivity((ctx, reference, next) => __awaiter(this, void 0, void 0, function* () { // run full pipeline yield next(); // add MessageDelete activity // log as MessageDelete activity const deleteActivity = turnContext_1.TurnContext.applyConversationReference({ type: botframework_schema_1.ActivityTypes.MessageDelete, id: reference.activityId, }, reference, false); this.logActivity(transcript, this.cloneActivity(deleteActivity)); })); // process bot logic yield next(); // flush transcript at end of turn while (transcript.length) { try { // If the implementation of this.logger.logActivity() is asynchronous, we don't // await it as to not block processing of activities. // Because TranscriptLogger.logActivity() returns void or Promise<void>, we capture // the result and see if it is a Promise. const maybePromise = this.logger.logActivity(transcript.shift()); // If this.logger.logActivity() returns a Promise, a catch is added in case there // is no innate error handling in the method. This catch prevents // UnhandledPromiseRejectionWarnings from being thrown and prints the error to the // console. if (maybePromise instanceof Promise) { maybePromise.catch((err) => { this.transcriptLoggerErrorHandler(err); }); } } catch (err) { this.transcriptLoggerErrorHandler(err); } } }); } /** * Logs the Activity. * * @param transcript Array where the activity will be pushed. * @param activity Activity to log. */ logActivity(transcript, activity) { if (!activity.timestamp) { activity.timestamp = new Date(); } // We should not log ContinueConversation events used by skills to initialize the middleware. if (!(activity.type === botframework_schema_1.ActivityTypes.Event && activity.name === botframework_schema_1.ActivityEventNames.ContinueConversation)) { transcript.push(activity); } } /** * Clones the Activity entity. * * @param activity Activity to clone. * @returns The cloned activity. */ cloneActivity(activity) { return Object.assign({}, activity); } /** * Error logging helper function. * * @param err Error or object to console.error out. */ transcriptLoggerErrorHandler(err) { // tslint:disable:no-console if (err instanceof Error) { console.error(`TranscriptLoggerMiddleware logActivity failed: "${err.message}"`); console.error(err.stack); } else { console.error(`TranscriptLoggerMiddleware logActivity failed: "${JSON.stringify(err)}"`); } // tslint:enable:no-console } } exports.TranscriptLoggerMiddleware = TranscriptLoggerMiddleware; /** * ConsoleTranscriptLogger , writes activities to Console output. */ class ConsoleTranscriptLogger { /** * Log an activity to the transcript. * * @param activity Activity being logged. */ logActivity(activity) { if (!activity) { throw new Error('Activity is required.'); } // tslint:disable-next-line:no-console console.log('Activity Log:', activity); } } exports.ConsoleTranscriptLogger = ConsoleTranscriptLogger; //# sourceMappingURL=transcriptLogger.js.map