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
JavaScript
/**
* @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
;