299 lines
No EOL
16 KiB
JavaScript
299 lines
No EOL
16 KiB
JavaScript
"use strict";
|
|
// Copyright (c) Microsoft Corporation.
|
|
// 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.CloudAdapterBase = void 0;
|
|
const botAdapter_1 = require("./botAdapter");
|
|
const turnContext_1 = require("./turnContext");
|
|
const activityHandlerBase_1 = require("./activityHandlerBase");
|
|
const botbuilder_stdlib_1 = require("botbuilder-stdlib");
|
|
const uuid_1 = require("uuid");
|
|
const botframework_connector_1 = require("botframework-connector");
|
|
const botframework_schema_1 = require("botframework-schema");
|
|
/**
|
|
* An adapter that implements the Bot Framework Protocol and can be hosted in different cloud environments both public and private.
|
|
*/
|
|
class CloudAdapterBase extends botAdapter_1.BotAdapter {
|
|
/**
|
|
* Create a new [CloudAdapterBase](xref:botbuilder.CloudAdapterBase) instance.
|
|
*
|
|
* @param botFrameworkAuthentication A [BotFrameworkAuthentication](xref:botframework-connector.BotFrameworkAuthentication) used for validating and creating tokens.
|
|
*/
|
|
constructor(botFrameworkAuthentication) {
|
|
super();
|
|
this.botFrameworkAuthentication = botFrameworkAuthentication;
|
|
this.ConnectorFactoryKey = Symbol('ConnectorFactory');
|
|
this.UserTokenClientKey = Symbol('UserTokenClient');
|
|
if (!botFrameworkAuthentication) {
|
|
throw new TypeError('`botFrameworkAuthentication` parameter required');
|
|
}
|
|
}
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
sendActivities(context, activities) {
|
|
var _a;
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!context) {
|
|
throw new TypeError('`context` parameter required');
|
|
}
|
|
if (!activities) {
|
|
throw new TypeError('`activities` parameter required');
|
|
}
|
|
if (!activities.length) {
|
|
throw new Error('Expecting one or more activities, but the array was empty.');
|
|
}
|
|
const responses = [];
|
|
for (const activity of activities) {
|
|
delete activity.id;
|
|
let response;
|
|
if (activity.type === 'delay') {
|
|
yield (0, botbuilder_stdlib_1.delay)(typeof activity.value === 'number' ? activity.value : 1000);
|
|
}
|
|
else if (activity.type === botframework_schema_1.ActivityTypes.InvokeResponse) {
|
|
context.turnState.set(activityHandlerBase_1.INVOKE_RESPONSE_KEY, activity);
|
|
}
|
|
else if (activity.type === botframework_schema_1.ActivityTypes.Trace && activity.channelId !== botframework_schema_1.Channels.Emulator) {
|
|
// no-op
|
|
}
|
|
else {
|
|
const connectorClient = context.turnState.get(this.ConnectorClientKey);
|
|
if (!connectorClient) {
|
|
throw new Error('Unable to extract ConnectorClient from turn context.');
|
|
}
|
|
if (activity.replyToId) {
|
|
response = yield connectorClient.conversations.replyToActivity(activity.conversation.id, activity.replyToId, activity);
|
|
}
|
|
else {
|
|
response = yield connectorClient.conversations.sendToConversation(activity.conversation.id, activity);
|
|
}
|
|
}
|
|
if (!response) {
|
|
response = { id: (_a = activity.id) !== null && _a !== void 0 ? _a : '' };
|
|
}
|
|
responses.push(response);
|
|
}
|
|
return responses;
|
|
});
|
|
}
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
updateActivity(context, activity) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!context) {
|
|
throw new TypeError('`context` parameter required');
|
|
}
|
|
if (!activity) {
|
|
throw new TypeError('`activity` parameter required');
|
|
}
|
|
const connectorClient = context.turnState.get(this.ConnectorClientKey);
|
|
if (!connectorClient) {
|
|
throw new Error('Unable to extract ConnectorClient from turn context.');
|
|
}
|
|
const response = yield connectorClient.conversations.updateActivity(activity.conversation.id, activity.id, activity);
|
|
return (response === null || response === void 0 ? void 0 : response.id) ? { id: response.id } : undefined;
|
|
});
|
|
}
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
deleteActivity(context, reference) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!context) {
|
|
throw new TypeError('`context` parameter required');
|
|
}
|
|
if (!reference) {
|
|
throw new TypeError('`reference` parameter required');
|
|
}
|
|
const connectorClient = context.turnState.get(this.ConnectorClientKey);
|
|
if (!connectorClient) {
|
|
throw new Error('Unable to extract ConnectorClient from turn context.');
|
|
}
|
|
yield connectorClient.conversations.deleteActivity(reference.conversation.id, reference.activityId);
|
|
});
|
|
}
|
|
/**
|
|
* @inheritdoc
|
|
* @deprecated
|
|
*/
|
|
continueConversation(_reference, _logic) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
throw new Error('`CloudAdapterBase.continueConversation` is deprecated, please use `CloudAdapterBase.continueConversationAsync`');
|
|
});
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
continueConversationAsync(botAppIdOrClaimsIdentity, reference, logicOrAudience, maybeLogic) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const botAppId = typeof botAppIdOrClaimsIdentity === 'string' ? botAppIdOrClaimsIdentity : undefined;
|
|
const claimsIdentity = typeof botAppIdOrClaimsIdentity !== 'string'
|
|
? botAppIdOrClaimsIdentity
|
|
: this.createClaimsIdentity(botAppId);
|
|
const audience = typeof logicOrAudience === 'string' ? logicOrAudience : undefined;
|
|
const logic = typeof logicOrAudience === 'function' ? logicOrAudience : maybeLogic;
|
|
return this.processProactive(claimsIdentity, botframework_schema_1.ActivityEx.getContinuationActivity(reference), audience, logic);
|
|
});
|
|
}
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
createConversationAsync(botAppId, channelId, serviceUrl, audience, conversationParameters, logic) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (typeof serviceUrl !== 'string' || !serviceUrl) {
|
|
throw new TypeError('`serviceUrl` must be a non-empty string');
|
|
}
|
|
if (!conversationParameters)
|
|
throw new TypeError('`conversationParameters` must be defined');
|
|
if (!logic)
|
|
throw new TypeError('`logic` must be defined');
|
|
// Create a ClaimsIdentity, to create the connector and for adding to the turn context.
|
|
const claimsIdentity = this.createClaimsIdentity(botAppId);
|
|
claimsIdentity.claims.push({ type: botframework_connector_1.AuthenticationConstants.ServiceUrlClaim, value: serviceUrl });
|
|
// Create the connector factory.
|
|
const connectorFactory = this.botFrameworkAuthentication.createConnectorFactory(claimsIdentity);
|
|
// Create the connector client to use for outbound requests.
|
|
const connectorClient = yield connectorFactory.create(serviceUrl, audience);
|
|
// Make the actual create conversation call using the connector.
|
|
const createConversationResult = yield connectorClient.conversations.createConversation(conversationParameters);
|
|
// Create the create activity to communicate the results to the application.
|
|
const createActivity = this.createCreateActivity(createConversationResult.id, channelId, serviceUrl, conversationParameters);
|
|
// Create a UserTokenClient instance for the application to use. (For example, in the OAuthPrompt.)
|
|
const userTokenClient = yield this.botFrameworkAuthentication.createUserTokenClient(claimsIdentity);
|
|
// Create a turn context and run the pipeline.
|
|
const context = this.createTurnContext(createActivity, claimsIdentity, undefined, connectorClient, userTokenClient, logic, connectorFactory);
|
|
// Run the pipeline.
|
|
yield this.runMiddleware(context, logic);
|
|
});
|
|
}
|
|
createCreateActivity(createdConversationId, channelId, serviceUrl, conversationParameters) {
|
|
// Create a conversation update activity to represent the result.
|
|
const activity = botframework_schema_1.ActivityEx.createEventActivity();
|
|
activity.name = botframework_schema_1.ActivityEventNames.CreateConversation;
|
|
activity.channelId = channelId;
|
|
activity.serviceUrl = serviceUrl;
|
|
activity.id = createdConversationId !== null && createdConversationId !== void 0 ? createdConversationId : (0, uuid_1.v4)();
|
|
activity.conversation = {
|
|
conversationType: undefined,
|
|
id: createdConversationId,
|
|
isGroup: conversationParameters.isGroup,
|
|
name: undefined,
|
|
tenantId: conversationParameters.tenantId,
|
|
};
|
|
activity.channelData = conversationParameters.channelData;
|
|
activity.recipient = conversationParameters.bot;
|
|
return activity;
|
|
}
|
|
/**
|
|
* The implementation for continue conversation.
|
|
*
|
|
* @param claimsIdentity The [ClaimsIdentity](xref:botframework-connector.ClaimsIdentity) for the conversation.
|
|
* @param continuationActivity The continuation [Activity](xref:botframework-schema.Activity) used to create the [TurnContext](xref:botbuilder-core.TurnContext).
|
|
* @param audience The audience for the call.
|
|
* @param logic The function to call for the resulting bot turn.
|
|
* @returns a Promise representing the async operation
|
|
*/
|
|
processProactive(claimsIdentity, continuationActivity, audience, logic) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Create the connector factory and the inbound request, extracting parameters and then create a connector for outbound requests.
|
|
const connectorFactory = this.botFrameworkAuthentication.createConnectorFactory(claimsIdentity);
|
|
// Create the connector client to use for outbound requests.
|
|
const connectorClient = yield connectorFactory.create(continuationActivity.serviceUrl, audience);
|
|
// Create a UserTokenClient instance for the application to use. (For example, in the OAuthPrompt.)
|
|
const userTokenClient = yield this.botFrameworkAuthentication.createUserTokenClient(claimsIdentity);
|
|
// Create a turn context and run the pipeline.
|
|
const context = this.createTurnContext(continuationActivity, claimsIdentity, audience, connectorClient, userTokenClient, logic, connectorFactory);
|
|
// Run the pipeline.
|
|
yield this.runMiddleware(context, logic);
|
|
});
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
processActivity(authHeaderOrAuthenticateRequestResult, activity, logic) {
|
|
var _a;
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Authenticate the inbound request, extracting parameters and create a ConnectorFactory for creating a Connector for outbound requests.
|
|
const authenticateRequestResult = typeof authHeaderOrAuthenticateRequestResult === 'string'
|
|
? yield this.botFrameworkAuthentication.authenticateRequest(activity, authHeaderOrAuthenticateRequestResult)
|
|
: authHeaderOrAuthenticateRequestResult;
|
|
// Set the callerId on the activity.
|
|
activity.callerId = authenticateRequestResult.callerId;
|
|
// Create the connector client to use for outbound requests.
|
|
const connectorClient = yield ((_a = authenticateRequestResult.connectorFactory) === null || _a === void 0 ? void 0 : _a.create(activity.serviceUrl, authenticateRequestResult.audience));
|
|
if (!connectorClient) {
|
|
throw new Error('Unable to extract ConnectorClient from turn context.');
|
|
}
|
|
// Create a UserTokenClient instance for the application to use. (For example, it would be used in a sign-in prompt.)
|
|
const userTokenClient = yield this.botFrameworkAuthentication.createUserTokenClient(authenticateRequestResult.claimsIdentity);
|
|
// Create a turn context and run the pipeline.
|
|
const context = this.createTurnContext(activity, authenticateRequestResult.claimsIdentity, authenticateRequestResult.audience, connectorClient, userTokenClient, logic, authenticateRequestResult.connectorFactory);
|
|
// Run the pipeline.
|
|
yield this.runMiddleware(context, logic);
|
|
// If there are any results they will have been left on the TurnContext.
|
|
return this.processTurnResults(context);
|
|
});
|
|
}
|
|
/**
|
|
* This is a helper to create the ClaimsIdentity structure from an appId that will be added to the TurnContext.
|
|
* It is intended for use in proactive and named-pipe scenarios.
|
|
*
|
|
* @param botAppId The bot's application id.
|
|
* @returns a [ClaimsIdentity](xref:botframework-connector.ClaimsIdentity) with the audience and appId claims set to the botAppId.
|
|
*/
|
|
createClaimsIdentity(botAppId = '') {
|
|
return new botframework_connector_1.ClaimsIdentity([
|
|
{
|
|
type: botframework_connector_1.AuthenticationConstants.AudienceClaim,
|
|
value: botAppId,
|
|
},
|
|
{
|
|
type: botframework_connector_1.AuthenticationConstants.AppIdClaim,
|
|
value: botAppId,
|
|
},
|
|
]);
|
|
}
|
|
createTurnContext(activity, claimsIdentity, oauthScope, connectorClient, userTokenClient, logic, connectorFactory) {
|
|
const context = new turnContext_1.TurnContext(this, activity);
|
|
context.turnState.set(this.BotIdentityKey, claimsIdentity);
|
|
context.turnState.set(this.ConnectorClientKey, connectorClient);
|
|
context.turnState.set(this.UserTokenClientKey, userTokenClient);
|
|
context.turnState.set(turnContext_1.BotCallbackHandlerKey, logic);
|
|
context.turnState.set(this.ConnectorFactoryKey, connectorFactory);
|
|
context.turnState.set(this.OAuthScopeKey, oauthScope);
|
|
return context;
|
|
}
|
|
processTurnResults(context) {
|
|
// Handle ExpectedReplies scenarios where all activities have been buffered and sent back at once in an invoke response.
|
|
if (context.activity.deliveryMode === botframework_schema_1.DeliveryModes.ExpectReplies) {
|
|
return {
|
|
status: botframework_schema_1.StatusCodes.OK,
|
|
body: {
|
|
activities: context.bufferedReplyActivities,
|
|
},
|
|
};
|
|
}
|
|
// Handle Invoke scenarios where the bot will return a specific body and return code.
|
|
if (context.activity.type === botframework_schema_1.ActivityTypes.Invoke) {
|
|
const activityInvokeResponse = context.turnState.get(activityHandlerBase_1.INVOKE_RESPONSE_KEY);
|
|
if (!activityInvokeResponse) {
|
|
return { status: botframework_schema_1.StatusCodes.NOT_IMPLEMENTED };
|
|
}
|
|
return activityInvokeResponse.value;
|
|
}
|
|
// No body to return.
|
|
return undefined;
|
|
}
|
|
}
|
|
exports.CloudAdapterBase = CloudAdapterBase;
|
|
//# sourceMappingURL=cloudAdapterBase.js.map
|