botbuilder
Advanced tools
Comparing version 4.0.0-m3.0 to 4.0.0-m4.0
@@ -8,6 +8,6 @@ /** | ||
*/ | ||
import { BotAdapter, TurnContext, Promiseable, Activity, ConversationReference, ResourceResponse } from 'botbuilder-core'; | ||
import { ConnectorClient, SimpleCredentialProvider, MicrosoftAppCredentials } from 'botframework-connector'; | ||
import { BotAdapter, TurnContext, Promiseable, Activity, ConversationReference, ResourceResponse, TokenResponse, ConversationsResult, ChannelAccount } from 'botbuilder-core'; | ||
import { ConnectorClient, SimpleCredentialProvider, MicrosoftAppCredentials, OAuthApiClient } from 'botframework-connector'; | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -22,3 +22,3 @@ * Express or Restify Request object. | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -32,3 +32,3 @@ * Express or Restify Response object. | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -41,2 +41,7 @@ * Bot Framework Adapter Settings. | ||
} | ||
/** | ||
* :package: **botbuilder** | ||
* | ||
* Response object expected to be sent in response to an `invoke` activity. | ||
*/ | ||
export interface InvokeResponse { | ||
@@ -47,3 +52,3 @@ status: number; | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -55,6 +60,11 @@ * ActivityAdapter class needed to communicate with a Bot Framework channel or the Emulator. | ||
* ```JavaScript | ||
* const { BotFrameworkAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new BotFrameworkAdapter({ | ||
* appId: process.env.MICROSOFT_APP_ID, | ||
* appPassword: process.env.MICROSOFT_APP_PASSWORD | ||
* }); | ||
* ``` | ||
*/ | ||
export declare class BotFrameworkAdapter extends BotAdapter { | ||
private readonly invokeResponses; | ||
protected readonly credentials: MicrosoftAppCredentials; | ||
@@ -68,11 +78,205 @@ protected readonly credentialsProvider: SimpleCredentialProvider; | ||
constructor(settings?: Partial<BotFrameworkAdapterSettings>); | ||
processActivity(req: WebRequest, res: WebResponse, logic: (context: TurnContext) => Promiseable<any>): Promise<void>; | ||
/** | ||
* Continues a conversation with a user. This is often referred to as the bots "Proactive Messaging" | ||
* flow as its lets the bot proactively send messages to a conversation or user that its already | ||
* communicated with. Scenarios like sending notifications or coupons to a user are enabled by this | ||
* method. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/notifyUser', async (req, res) => { | ||
* // Lookup previously saved conversation reference | ||
* const reference = await findReference(req.body.refId); | ||
* | ||
* // Proactively notify the user | ||
* if (reference) { | ||
* await adapter.continueConversation(reference, async (context) => { | ||
* await context.sendActivity(req.body.message); | ||
* }); | ||
* res.send(200); | ||
* } else { | ||
* res.send(404); | ||
* } | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
continueConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void>; | ||
/** | ||
* Starts a new conversation with a user. This is typically used to Direct Message (DM) a member | ||
* of a group. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* // Get group members conversation reference | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* | ||
* // Start a new conversation with the user | ||
* await adapter.createConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(`Hi (in private)`); | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` of the user to start a new conversation with. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
createConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void>; | ||
/** | ||
* Deletes an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.deleteActivity()` is the preferred way of deleting activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param reference Conversation reference information for the activity being deleted. | ||
*/ | ||
deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void>; | ||
/** | ||
* Deletes a member from the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param memberId ID of the member to delete from the conversation. | ||
*/ | ||
deleteConversationMember(context: TurnContext, memberId: string): Promise<void>; | ||
/** | ||
* Lists the members of a given activity. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activityId (Optional) activity ID to enumerate. If not specified the current activities ID will be used. | ||
*/ | ||
getActivityMembers(context: TurnContext, activityId?: string): Promise<ChannelAccount[]>; | ||
/** | ||
* Lists the members of the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
*/ | ||
getConversationMembers(context: TurnContext): Promise<ChannelAccount[]>; | ||
/** | ||
* Lists the Conversations in which this bot has participated for a given channel server. The | ||
* channel server returns results in pages and each page will include a `continuationToken` | ||
* that can be used to fetch the next page of results from the server. | ||
* @param serviceUrl The URL of the channel server to query. This can be retrieved from `context.activity.serviceUrl`. | ||
* @param continuationToken (Optional) token used to fetch the next page of results from the channel server. This should be left as `undefined` to retrieve the first page of results. | ||
*/ | ||
getConversations(serviceUrl: string, continuationToken?: string): Promise<ConversationsResult>; | ||
/** | ||
* Attempts to retrieve the token for a user that's in a logging flow. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
getUserToken(context: TurnContext, connectionName: string, magicCode?: string): Promise<TokenResponse>; | ||
/** | ||
* Signs the user out with the token server. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
signOutUser(context: TurnContext, connectionName: string): Promise<void>; | ||
/** | ||
* Processes an activity received by the bots web server. This includes any messages sent from a | ||
* user and is the method that drives what's often referred to as the bots "Reactive Messaging" | ||
* flow. | ||
* | ||
* The following steps will be taken to process the activity: | ||
* | ||
* - The identity of the sender will be verified to be either the Emulator or a valid Microsoft | ||
* server. The bots `appId` and `appPassword` will be used during this process and the request | ||
* will be rejected if the senders identity can't be verified. | ||
* - The activity will be parsed from the body of the incoming request. An error will be returned | ||
* if the activity can't be parsed. | ||
* - A `TurnContext` instance will be created for the received activity and wrapped with a | ||
* [Revocable Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable). | ||
* - The context will be routed through any middleware registered with the adapter using | ||
* [use()](#use). Middleware is executed in the order in which it's added and any middleware | ||
* can intercept or prevent further routing of the context by simply not calling the passed | ||
* in `next()` function. This is called the "Leading Edge" of the request and middleware will | ||
* get a second chance to run on the "Trailing Edge" of the request after the bots logic has run. | ||
* - Assuming the context hasn't been intercepted by a piece of middleware, the context will be | ||
* passed to the logic handler passed in. The bot may perform an additional routing or | ||
* processing at this time. Returning a promise (or providing an `async` handler) will cause the | ||
* adapter to wait for any asynchronous operations to complete. | ||
* - Once the bots logic completes the promise chain setup by the middleware stack will be resolved | ||
* giving middleware a second chance to run on the "Trailing Edge" of the request. | ||
* - After the middleware stacks promise chain has been fully resolved the context object will be | ||
* `revoked()` and any future calls to the context will result in a `TypeError: Cannot perform | ||
* 'set' on a proxy that has been revoked` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/messages', (req, res) => { | ||
* // Route received request to adapter for processing | ||
* adapter.processActivity(req, res, async (context) => { | ||
* // Process any messages received | ||
* if (context.activity.type === 'message') { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* }); | ||
* ``` | ||
* @param req An Express or Restify style Request object. | ||
* @param res An Express or Restify style Response object. | ||
* @param logic A function handler that will be called to perform the bots logic after the received activity has been pre-processed by the adapter and routed through any middleware for processing. | ||
*/ | ||
processActivity(req: WebRequest, res: WebResponse, logic: (context: TurnContext) => Promiseable<any>): Promise<void>; | ||
/** | ||
* Sends a set of activities to a channels server(s). The activities will be sent one after | ||
* another in the order in which they're received. A response object will be returned for each | ||
* sent activity. For `message` activities this will contain the ID of the delivered message. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* | ||
* The primary scenario for calling this method directly is when you want to explicitly bypass | ||
* going through any middleware. For instance, periodically sending a `typing` activity might | ||
* be a good reason to call this method directly as it would avoid any false signals from being | ||
* logged. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
sendActivities(context: TurnContext, activities: Partial<Activity>[]): Promise<ResourceResponse[]>; | ||
/** | ||
* Replaces an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.updateActivity()` is the preferred way of updating activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activity New activity to replace a current activity with. | ||
*/ | ||
updateActivity(context: TurnContext, activity: Partial<Activity>): Promise<void>; | ||
deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void>; | ||
/** | ||
* Allows for the overriding of authentication in unit tests. | ||
* @param request Received request. | ||
* @param authHeader Received authentication header. | ||
*/ | ||
protected authenticateRequest(request: Partial<Activity>, authHeader: string): Promise<void>; | ||
/** | ||
* Allows for mocking of the connector client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
protected createConnectorClient(serviceUrl: string): ConnectorClient; | ||
/** | ||
* Allows for mocking of the OAuth API Client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
protected createOAuthApiClient(serviceUrl: string): OAuthApiClient; | ||
/** | ||
* Allows for the overriding of the context object in unit tests and derived adapters. | ||
* @param request Received request. | ||
*/ | ||
protected createContext(request: Partial<Activity>): TurnContext; | ||
} |
@@ -12,4 +12,7 @@ "use strict"; | ||
const botframework_connector_1 = require("botframework-connector"); | ||
const pjson = require('../package.json'); | ||
const USER_AGENT = "Microsoft-BotFramework/3.1 (BotBuilder JS/" + pjson.version + ")"; | ||
const INVOKE_RESPONSE_KEY = Symbol('invokeResponse'); | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -21,2 +24,8 @@ * ActivityAdapter class needed to communicate with a Bot Framework channel or the Emulator. | ||
* ```JavaScript | ||
* const { BotFrameworkAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new BotFrameworkAdapter({ | ||
* appId: process.env.MICROSOFT_APP_ID, | ||
* appPassword: process.env.MICROSOFT_APP_PASSWORD | ||
* }); | ||
* ``` | ||
@@ -31,3 +40,2 @@ */ | ||
super(); | ||
this.invokeResponses = {}; | ||
this.settings = Object.assign({ appId: '', appPassword: '' }, settings); | ||
@@ -37,2 +45,289 @@ this.credentials = new botframework_connector_1.MicrosoftAppCredentials(this.settings.appId, this.settings.appPassword || ''); | ||
} | ||
/** | ||
* Continues a conversation with a user. This is often referred to as the bots "Proactive Messaging" | ||
* flow as its lets the bot proactively send messages to a conversation or user that its already | ||
* communicated with. Scenarios like sending notifications or coupons to a user are enabled by this | ||
* method. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/notifyUser', async (req, res) => { | ||
* // Lookup previously saved conversation reference | ||
* const reference = await findReference(req.body.refId); | ||
* | ||
* // Proactively notify the user | ||
* if (reference) { | ||
* await adapter.continueConversation(reference, async (context) => { | ||
* await context.sendActivity(req.body.message); | ||
* }); | ||
* res.send(200); | ||
* } else { | ||
* res.send(404); | ||
* } | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
continueConversation(reference, logic) { | ||
const request = botbuilder_core_1.TurnContext.applyConversationReference({}, reference, true); | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic); | ||
} | ||
/** | ||
* Starts a new conversation with a user. This is typically used to Direct Message (DM) a member | ||
* of a group. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* // Get group members conversation reference | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* | ||
* // Start a new conversation with the user | ||
* await adapter.createConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(`Hi (in private)`); | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` of the user to start a new conversation with. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
createConversation(reference, logic) { | ||
try { | ||
if (!reference.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.createConversation(): missing serviceUrl.`); | ||
} | ||
// Create conversation | ||
const parameters = { bot: reference.bot }; | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.createConversation(parameters).then((response) => { | ||
// Initialize request and copy over new conversation ID and updated serviceUrl. | ||
const request = botbuilder_core_1.TurnContext.applyConversationReference({}, reference, true); | ||
request.conversation = { id: response.id }; | ||
if (response.serviceUrl) { | ||
request.serviceUrl = response.serviceUrl; | ||
} | ||
// Create context and run middleware | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic); | ||
}); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Deletes an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.deleteActivity()` is the preferred way of deleting activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param reference Conversation reference information for the activity being deleted. | ||
*/ | ||
deleteActivity(context, reference) { | ||
try { | ||
if (!reference.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing serviceUrl`); | ||
} | ||
if (!reference.conversation || !reference.conversation.id) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing conversation or conversation.id`); | ||
} | ||
if (!reference.activityId) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing activityId`); | ||
} | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.deleteActivity(reference.conversation.id, reference.activityId); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Deletes a member from the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param memberId ID of the member to delete from the conversation. | ||
*/ | ||
deleteConversationMember(context, memberId) { | ||
try { | ||
if (!context.activity.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.deleteConversationMember(): missing serviceUrl`); | ||
} | ||
if (!context.activity.conversation || !context.activity.conversation.id) { | ||
throw new Error(`BotFrameworkAdapter.deleteConversationMember(): missing conversation or conversation.id`); | ||
} | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.deleteConversationMember(conversationId, memberId); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the members of a given activity. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activityId (Optional) activity ID to enumerate. If not specified the current activities ID will be used. | ||
*/ | ||
getActivityMembers(context, activityId) { | ||
try { | ||
if (!activityId) { | ||
activityId = context.activity.id; | ||
} | ||
if (!context.activity.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing serviceUrl`); | ||
} | ||
if (!context.activity.conversation || !context.activity.conversation.id) { | ||
throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing conversation or conversation.id`); | ||
} | ||
if (!activityId) { | ||
throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing both activityId and context.activity.id`); | ||
} | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getActivityMembers(conversationId, activityId); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the members of the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
*/ | ||
getConversationMembers(context) { | ||
try { | ||
if (!context.activity.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.getConversationMembers(): missing serviceUrl`); | ||
} | ||
if (!context.activity.conversation || !context.activity.conversation.id) { | ||
throw new Error(`BotFrameworkAdapter.getConversationMembers(): missing conversation or conversation.id`); | ||
} | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getConversationMembers(conversationId); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the Conversations in which this bot has participated for a given channel server. The | ||
* channel server returns results in pages and each page will include a `continuationToken` | ||
* that can be used to fetch the next page of results from the server. | ||
* @param serviceUrl The URL of the channel server to query. This can be retrieved from `context.activity.serviceUrl`. | ||
* @param continuationToken (Optional) token used to fetch the next page of results from the channel server. This should be left as `undefined` to retrieve the first page of results. | ||
*/ | ||
getConversations(serviceUrl, continuationToken) { | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getConversations(continuationToken ? { continuationToken: continuationToken } : undefined); | ||
} | ||
/** | ||
* Attempts to retrieve the token for a user that's in a logging flow. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
getUserToken(context, connectionName, magicCode) { | ||
try { | ||
if (!context.activity.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.getUserToken(): missing serviceUrl`); | ||
} | ||
if (!context.activity.from || !context.activity.from.id) { | ||
throw new Error(`BotFrameworkAdapter.getUserToken(): missing from or from.id`); | ||
} | ||
const serviceUrl = context.activity.serviceUrl; | ||
const userId = context.activity.from.id; | ||
const client = this.createOAuthApiClient(serviceUrl); | ||
return client.getUserToken(userId, connectionName, magicCode); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Signs the user out with the token server. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
signOutUser(context, connectionName) { | ||
try { | ||
if (!context.activity.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.signOutUser(): missing serviceUrl`); | ||
} | ||
if (!context.activity.from || !context.activity.from.id) { | ||
throw new Error(`BotFrameworkAdapter.signOutUser(): missing from or from.id`); | ||
} | ||
const serviceUrl = context.activity.serviceUrl; | ||
const userId = context.activity.from.id; | ||
const client = this.createOAuthApiClient(serviceUrl); | ||
return client.signOutUser(userId, connectionName); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Processes an activity received by the bots web server. This includes any messages sent from a | ||
* user and is the method that drives what's often referred to as the bots "Reactive Messaging" | ||
* flow. | ||
* | ||
* The following steps will be taken to process the activity: | ||
* | ||
* - The identity of the sender will be verified to be either the Emulator or a valid Microsoft | ||
* server. The bots `appId` and `appPassword` will be used during this process and the request | ||
* will be rejected if the senders identity can't be verified. | ||
* - The activity will be parsed from the body of the incoming request. An error will be returned | ||
* if the activity can't be parsed. | ||
* - A `TurnContext` instance will be created for the received activity and wrapped with a | ||
* [Revocable Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable). | ||
* - The context will be routed through any middleware registered with the adapter using | ||
* [use()](#use). Middleware is executed in the order in which it's added and any middleware | ||
* can intercept or prevent further routing of the context by simply not calling the passed | ||
* in `next()` function. This is called the "Leading Edge" of the request and middleware will | ||
* get a second chance to run on the "Trailing Edge" of the request after the bots logic has run. | ||
* - Assuming the context hasn't been intercepted by a piece of middleware, the context will be | ||
* passed to the logic handler passed in. The bot may perform an additional routing or | ||
* processing at this time. Returning a promise (or providing an `async` handler) will cause the | ||
* adapter to wait for any asynchronous operations to complete. | ||
* - Once the bots logic completes the promise chain setup by the middleware stack will be resolved | ||
* giving middleware a second chance to run on the "Trailing Edge" of the request. | ||
* - After the middleware stacks promise chain has been fully resolved the context object will be | ||
* `revoked()` and any future calls to the context will result in a `TypeError: Cannot perform | ||
* 'set' on a proxy that has been revoked` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/messages', (req, res) => { | ||
* // Route received request to adapter for processing | ||
* adapter.processActivity(req, res, async (context) => { | ||
* // Process any messages received | ||
* if (context.activity.type === 'message') { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* }); | ||
* ``` | ||
* @param req An Express or Restify style Request object. | ||
* @param res An Express or Restify style Response object. | ||
* @param logic A function handler that will be called to perform the bots logic after the received activity has been pre-processed by the adapter and routed through any middleware for processing. | ||
*/ | ||
processActivity(req, res, logic) { | ||
@@ -52,18 +347,11 @@ // Parse body of request | ||
if (request.type === botbuilder_core_1.ActivityTypes.Invoke) { | ||
const key = request.channelId + '/' + request.id; | ||
try { | ||
const invokeResponse = this.invokeResponses[key]; | ||
if (invokeResponse && invokeResponse.value) { | ||
const value = invokeResponse.value; | ||
res.send(value.status, value.body); | ||
res.end(); | ||
} | ||
else { | ||
throw new Error(`Bot failed to return a valid 'invokeResponse' activity.`); | ||
} | ||
// Retrieve cached invoke response. | ||
const invokeResponse = context.services.get(INVOKE_RESPONSE_KEY); | ||
if (invokeResponse && invokeResponse.value) { | ||
const value = invokeResponse.value; | ||
res.send(value.status, value.body); | ||
res.end(); | ||
} | ||
finally { | ||
if (this.invokeResponses.hasOwnProperty(key)) { | ||
delete this.invokeResponses[key]; | ||
} | ||
else { | ||
throw new Error(`Bot failed to return a valid 'invokeResponse' activity.`); | ||
} | ||
@@ -79,3 +367,3 @@ } | ||
// Reject response with error code | ||
console.warn(`BotFrameworkAdapter.processRequest(): ${errorCode} ERROR - ${err.toString()}`); | ||
console.warn(`BotFrameworkAdapter.processActivity(): ${errorCode} ERROR - ${err.toString()}`); | ||
res.send(errorCode, err.toString()); | ||
@@ -86,31 +374,18 @@ res.end(); | ||
} | ||
continueConversation(reference, logic) { | ||
const request = botbuilder_core_1.TurnContext.applyConversationReference({}, reference, true); | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic); | ||
} | ||
createConversation(reference, logic) { | ||
try { | ||
if (!reference.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.createConversation(): missing serviceUrl.`); | ||
} | ||
// Create conversation | ||
const parameters = { bot: reference.bot }; | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.createConversation(parameters).then((response) => { | ||
// Initialize request and copy over new conversation ID and updated serviceUrl. | ||
const request = botbuilder_core_1.TurnContext.applyConversationReference({}, reference, true); | ||
request.conversation = { id: response.id }; | ||
if (response.serviceUrl) { | ||
request.serviceUrl = response.serviceUrl; | ||
} | ||
// Create context and run middleware | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic); | ||
}); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Sends a set of activities to a channels server(s). The activities will be sent one after | ||
* another in the order in which they're received. A response object will be returned for each | ||
* sent activity. For `message` activities this will contain the ID of the delivered message. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* | ||
* The primary scenario for calling this method directly is when you want to explicitly bypass | ||
* going through any middleware. For instance, periodically sending a `typing` activity might | ||
* be a good reason to call this method directly as it would avoid any false signals from being | ||
* logged. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
sendActivities(context, activities) { | ||
@@ -132,4 +407,5 @@ return new Promise((resolve, reject) => { | ||
case 'invokeResponse': | ||
const key = activity.channelId + '/' + activity.replyToId; | ||
that.invokeResponses[key] = activity; | ||
// Cache response to context object. This will be retrieved when turn completes. | ||
context.services.set(INVOKE_RESPONSE_KEY, activity); | ||
responses.push({}); | ||
next(i + 1); | ||
@@ -146,3 +422,7 @@ break; | ||
const client = that.createConnectorClient(activity.serviceUrl); | ||
if (activity.replyToId) { | ||
if (activity.type === 'trace' && activity.channelId !== 'emulator') { | ||
// Just eat activity | ||
p = Promise.resolve({}); | ||
} | ||
else if (activity.replyToId) { | ||
p = client.conversations.replyToActivity(activity.conversation.id, activity.replyToId, activity); | ||
@@ -171,2 +451,11 @@ } | ||
} | ||
/** | ||
* Replaces an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.updateActivity()` is the preferred way of updating activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activity New activity to replace a current activity with. | ||
*/ | ||
updateActivity(context, activity) { | ||
@@ -190,26 +479,30 @@ try { | ||
} | ||
deleteActivity(context, reference) { | ||
try { | ||
if (!reference.serviceUrl) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing serviceUrl`); | ||
} | ||
if (!reference.conversation || !reference.conversation.id) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing conversation or conversation.id`); | ||
} | ||
if (!reference.activityId) { | ||
throw new Error(`BotFrameworkAdapter.deleteActivity(): missing activityId`); | ||
} | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.deleteActivity(reference.conversation.id, reference.activityId); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Allows for the overriding of authentication in unit tests. | ||
* @param request Received request. | ||
* @param authHeader Received authentication header. | ||
*/ | ||
authenticateRequest(request, authHeader) { | ||
return botframework_connector_1.JwtTokenValidation.assertValidActivity(request, authHeader, this.credentialsProvider); | ||
} | ||
/** | ||
* Allows for mocking of the connector client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
createConnectorClient(serviceUrl) { | ||
return new botframework_connector_1.ConnectorClient(this.credentials, serviceUrl); | ||
const client = new botframework_connector_1.ConnectorClient(this.credentials, serviceUrl); | ||
client.addUserAgentInfo(USER_AGENT); | ||
return client; | ||
} | ||
/** | ||
* Allows for mocking of the OAuth API Client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
createOAuthApiClient(serviceUrl) { | ||
return new botframework_connector_1.OAuthApiClient(this.createConnectorClient(serviceUrl)); | ||
} | ||
/** | ||
* Allows for the overriding of the context object in unit tests and derived adapters. | ||
* @param request Received request. | ||
*/ | ||
createContext(request) { | ||
@@ -216,0 +509,0 @@ return new botbuilder_core_1.TurnContext(this, request); |
@@ -12,3 +12,3 @@ /// <reference types="node" /> | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -20,2 +20,8 @@ * Lets a user communicate with a bot from a console window. | ||
* ```JavaScript | ||
* const { ConsoleAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new ConsoleAdapter(); | ||
* const closeFn = adapter.listen(async (context) => { | ||
* await context.sendActivity(`Hello World`); | ||
* }); | ||
* ``` | ||
@@ -26,15 +32,97 @@ */ | ||
private readonly reference; | ||
/** | ||
* Creates a new ConsoleAdapter instance. | ||
* @param reference (Optional) reference used to customize the address information of activites sent from the adapter. | ||
*/ | ||
constructor(reference?: ConversationReference); | ||
/** | ||
* Begins listening to console input. | ||
* Begins listening to console input. A function will be returned that can be used to stop the | ||
* bot listening and therefore end the process. | ||
* | ||
* Upon receiving input from the console the flow is as follows: | ||
* | ||
* - An 'message' activity will be created containing the users input text. | ||
* - A revokable `TurnContext` will be created for the activity. | ||
* - The context will be routed through any middleware registered with [use()](#use). | ||
* - The bots logic handler that was passed in will be executed. | ||
* - The promise chain setup by the middleware stack will be resolved. | ||
* - The context object will be revoked and any future calls to its members will result in a | ||
* `TypeError` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* const closeFn = adapter.listen(async (context) => { | ||
* const utterance = context.activity.text.toLowerCase(); | ||
* if (utterance.includes('goodbye')) { | ||
* await context.sendActivity(`Ok... Goodbye`); | ||
* closeFn(); | ||
* } else { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* ``` | ||
* @param logic Function which will be called each time a message is input by the user. | ||
*/ | ||
listen(logic: (context: TurnContext) => Promiseable<void>): Function; | ||
/** | ||
* Lets a bot proactively message the user. | ||
* | ||
* The processing steps for this method are very similar to [listen()](#listen) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* function delayedNotify(context, message, delay) { | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* setTimeout(() => { | ||
* adapter.continueConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(message); | ||
* }); | ||
* }, delay); | ||
* } | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
continueConversation(reference: ConversationReference, logic: (context: TurnContext) => Promiseable<void>): Promise<void>; | ||
/** | ||
* Logs a set of activities to the console. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
sendActivities(context: TurnContext, activities: Partial<Activity>[]): Promise<ResourceResponse[]>; | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.updateActivity()` | ||
* will result an error being returned. | ||
*/ | ||
updateActivity(context: TurnContext, activity: Partial<Activity>): Promise<void>; | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.deleteActivity()` | ||
* will result an error being returned. | ||
*/ | ||
deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void>; | ||
/** | ||
* Allows for mocking of the console interface in unit tests. | ||
* @param options Console interface options. | ||
*/ | ||
protected createInterface(options: readline.ReadLineOptions): readline.ReadLine; | ||
/** | ||
* Logs text to the console. | ||
* @param line Text to print. | ||
*/ | ||
protected print(line: string): void; | ||
/** | ||
* Logs an error to the console. | ||
* @param line Error text to print. | ||
*/ | ||
protected printError(line: string): void; | ||
} |
@@ -13,3 +13,3 @@ "use strict"; | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -21,5 +21,15 @@ * Lets a user communicate with a bot from a console window. | ||
* ```JavaScript | ||
* const { ConsoleAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new ConsoleAdapter(); | ||
* const closeFn = adapter.listen(async (context) => { | ||
* await context.sendActivity(`Hello World`); | ||
* }); | ||
* ``` | ||
*/ | ||
class ConsoleAdapter extends botbuilder_core_1.BotAdapter { | ||
/** | ||
* Creates a new ConsoleAdapter instance. | ||
* @param reference (Optional) reference used to customize the address information of activites sent from the adapter. | ||
*/ | ||
constructor(reference) { | ||
@@ -37,3 +47,28 @@ super(); | ||
/** | ||
* Begins listening to console input. | ||
* Begins listening to console input. A function will be returned that can be used to stop the | ||
* bot listening and therefore end the process. | ||
* | ||
* Upon receiving input from the console the flow is as follows: | ||
* | ||
* - An 'message' activity will be created containing the users input text. | ||
* - A revokable `TurnContext` will be created for the activity. | ||
* - The context will be routed through any middleware registered with [use()](#use). | ||
* - The bots logic handler that was passed in will be executed. | ||
* - The promise chain setup by the middleware stack will be resolved. | ||
* - The context object will be revoked and any future calls to its members will result in a | ||
* `TypeError` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* const closeFn = adapter.listen(async (context) => { | ||
* const utterance = context.activity.text.toLowerCase(); | ||
* if (utterance.includes('goodbye')) { | ||
* await context.sendActivity(`Ok... Goodbye`); | ||
* closeFn(); | ||
* } else { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* ``` | ||
* @param logic Function which will be called each time a message is input by the user. | ||
@@ -60,2 +95,26 @@ */ | ||
} | ||
/** | ||
* Lets a bot proactively message the user. | ||
* | ||
* The processing steps for this method are very similar to [listen()](#listen) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* function delayedNotify(context, message, delay) { | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* setTimeout(() => { | ||
* adapter.continueConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(message); | ||
* }); | ||
* }, delay); | ||
* } | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
continueConversation(reference, logic) { | ||
@@ -68,2 +127,11 @@ // Create context and run middleware pipe | ||
} | ||
/** | ||
* Logs a set of activities to the console. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
sendActivities(context, activities) { | ||
@@ -104,14 +172,34 @@ const that = this; | ||
} | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.updateActivity()` | ||
* will result an error being returned. | ||
*/ | ||
updateActivity(context, activity) { | ||
return Promise.reject(new Error(`ConsoleAdapter.updateActivity(): not supported.`)); | ||
} | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.deleteActivity()` | ||
* will result an error being returned. | ||
*/ | ||
deleteActivity(context, reference) { | ||
return Promise.reject(new Error(`ConsoleAdapter.deleteActivity(): not supported.`)); | ||
} | ||
/** | ||
* Allows for mocking of the console interface in unit tests. | ||
* @param options Console interface options. | ||
*/ | ||
createInterface(options) { | ||
return readline.createInterface(options); | ||
} | ||
/** | ||
* Logs text to the console. | ||
* @param line Text to print. | ||
*/ | ||
print(line) { | ||
console.log(line); | ||
} | ||
/** | ||
* Logs an error to the console. | ||
* @param line Error text to print. | ||
*/ | ||
printError(line) { | ||
@@ -118,0 +206,0 @@ console.error(line); |
@@ -12,3 +12,3 @@ /** | ||
* | ||
* A file based storage provider. | ||
* A file based storage provider. Items will be persisted to a folder on disk. | ||
* | ||
@@ -18,2 +18,6 @@ * **Usage Example** | ||
* ```JavaScript | ||
* const { FileStorage } = require('botbuilder'); | ||
* const path = require('path'); | ||
* | ||
* const storage = new FileStorage(path.join(__dirname, './state')); | ||
* ``` | ||
@@ -26,24 +30,8 @@ */ | ||
/** | ||
* Creates a new instance of the storage provider. | ||
* | ||
* @param path Root filesystem path for where the provider should store its objects. | ||
* Creates a new FileStorage instance. | ||
* @param path Root filesystem path for where the provider should store its items. | ||
*/ | ||
constructor(path: string); | ||
/** | ||
* Loads store items from storage | ||
* | ||
* @param keys Array of item keys to read from the store. | ||
**/ | ||
read(keys: string[]): Promise<StoreItems>; | ||
/** | ||
* Saves store items to storage. | ||
* | ||
* @param changes Map of items to write to storage. | ||
**/ | ||
write(changes: StoreItems): Promise<void>; | ||
/** | ||
* Removes store items from storage | ||
* | ||
* @param keys Array of item keys to remove from the store. | ||
**/ | ||
delete(keys: string[]): Promise<void>; | ||
@@ -50,0 +38,0 @@ private ensureFolder(); |
@@ -10,3 +10,3 @@ "use strict"; | ||
* | ||
* A file based storage provider. | ||
* A file based storage provider. Items will be persisted to a folder on disk. | ||
* | ||
@@ -16,2 +16,6 @@ * **Usage Example** | ||
* ```JavaScript | ||
* const { FileStorage } = require('botbuilder'); | ||
* const path = require('path'); | ||
* | ||
* const storage = new FileStorage(path.join(__dirname, './state')); | ||
* ``` | ||
@@ -21,5 +25,4 @@ */ | ||
/** | ||
* Creates a new instance of the storage provider. | ||
* | ||
* @param path Root filesystem path for where the provider should store its objects. | ||
* Creates a new FileStorage instance. | ||
* @param path Root filesystem path for where the provider should store its items. | ||
*/ | ||
@@ -29,7 +32,2 @@ constructor(path) { | ||
} | ||
/** | ||
* Loads store items from storage | ||
* | ||
* @param keys Array of item keys to read from the store. | ||
**/ | ||
read(keys) { | ||
@@ -52,7 +50,2 @@ return this.ensureFolder().then(() => { | ||
} | ||
/** | ||
* Saves store items to storage. | ||
* | ||
* @param changes Map of items to write to storage. | ||
**/ | ||
write(changes) { | ||
@@ -79,7 +72,2 @@ return this.ensureFolder().then(() => { | ||
; | ||
/** | ||
* Removes store items from storage | ||
* | ||
* @param keys Array of item keys to remove from the store. | ||
**/ | ||
delete(keys) { | ||
@@ -86,0 +74,0 @@ return this.ensureFolder().then(() => { |
@@ -5,3 +5,3 @@ { | ||
"description": "Services for Microsoft BotBuilder.", | ||
"version": "4.0.0-m3.0", | ||
"version": "4.0.0-m4.0", | ||
"license": "MIT", | ||
@@ -27,5 +27,5 @@ "keywords": [ | ||
"async-file": "^2.0.2", | ||
"botbuilder-core": "4.0.0-m3.0", | ||
"botbuilder-core-extensions": "4.0.0-m3.0", | ||
"botframework-connector": "4.0.0-m3.0", | ||
"botbuilder-core": "4.0.0-m4.0", | ||
"botbuilder-core-extensions": "4.0.0-m4.0", | ||
"botframework-connector": "4.0.0-m4.0", | ||
"filenamify": "^2.0.0", | ||
@@ -32,0 +32,0 @@ "readline": "^1.3.0" |
@@ -8,7 +8,11 @@ /** | ||
*/ | ||
import { BotAdapter, TurnContext, Promiseable, ActivityTypes, Activity, ConversationReference, ResourceResponse, ConversationResourceResponse, ConversationParameters, ConversationAccount } from 'botbuilder-core'; | ||
import { ConnectorClient, SimpleCredentialProvider, MicrosoftAppCredentials, JwtTokenValidation } from 'botframework-connector'; | ||
import { | ||
BotAdapter, TurnContext, Promiseable, ActivityTypes, Activity, ConversationReference, | ||
ResourceResponse, ConversationResourceResponse, ConversationParameters, ConversationAccount, | ||
TokenResponse, ConversationsResult, ChannelAccount | ||
} from 'botbuilder-core'; | ||
import { ConnectorClient, SimpleCredentialProvider, MicrosoftAppCredentials, JwtTokenValidation, OAuthApiClient } from 'botframework-connector'; | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -24,3 +28,3 @@ * Express or Restify Request object. | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -35,3 +39,3 @@ * Express or Restify Response object. | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -45,2 +49,7 @@ * Bot Framework Adapter Settings. | ||
/** | ||
* :package: **botbuilder** | ||
* | ||
* Response object expected to be sent in response to an `invoke` activity. | ||
*/ | ||
export interface InvokeResponse { | ||
@@ -51,4 +60,10 @@ status: number; | ||
const pjson: any = require('../package.json'); | ||
const USER_AGENT = "Microsoft-BotFramework/3.1 (BotBuilder JS/" + pjson.version + ")"; | ||
const INVOKE_RESPONSE_KEY = Symbol('invokeResponse'); | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -60,6 +75,11 @@ * ActivityAdapter class needed to communicate with a Bot Framework channel or the Emulator. | ||
* ```JavaScript | ||
* const { BotFrameworkAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new BotFrameworkAdapter({ | ||
* appId: process.env.MICROSOFT_APP_ID, | ||
* appPassword: process.env.MICROSOFT_APP_PASSWORD | ||
* }); | ||
* ``` | ||
*/ | ||
export class BotFrameworkAdapter extends BotAdapter { | ||
private readonly invokeResponses: { [key:string]: Partial<Activity>; } = {}; | ||
protected readonly credentials: MicrosoftAppCredentials; | ||
@@ -80,2 +100,259 @@ protected readonly credentialsProvider: SimpleCredentialProvider; | ||
/** | ||
* Continues a conversation with a user. This is often referred to as the bots "Proactive Messaging" | ||
* flow as its lets the bot proactively send messages to a conversation or user that its already | ||
* communicated with. Scenarios like sending notifications or coupons to a user are enabled by this | ||
* method. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/notifyUser', async (req, res) => { | ||
* // Lookup previously saved conversation reference | ||
* const reference = await findReference(req.body.refId); | ||
* | ||
* // Proactively notify the user | ||
* if (reference) { | ||
* await adapter.continueConversation(reference, async (context) => { | ||
* await context.sendActivity(req.body.message); | ||
* }); | ||
* res.send(200); | ||
* } else { | ||
* res.send(404); | ||
* } | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
public continueConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void> { | ||
const request = TurnContext.applyConversationReference({}, reference, true); | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic as any); | ||
} | ||
/** | ||
* Starts a new conversation with a user. This is typically used to Direct Message (DM) a member | ||
* of a group. | ||
* | ||
* The processing steps for this method are very similar to [processActivity()](#processactivity) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* // Get group members conversation reference | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* | ||
* // Start a new conversation with the user | ||
* await adapter.createConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(`Hi (in private)`); | ||
* }); | ||
* ``` | ||
* @param reference A `ConversationReference` of the user to start a new conversation with. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
public createConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void> { | ||
try { | ||
if (!reference.serviceUrl) { throw new Error(`BotFrameworkAdapter.createConversation(): missing serviceUrl.`) } | ||
// Create conversation | ||
const parameters = { bot: reference.bot } as ConversationParameters; | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.createConversation(parameters).then((response) => { | ||
// Initialize request and copy over new conversation ID and updated serviceUrl. | ||
const request = TurnContext.applyConversationReference({}, reference, true); | ||
request.conversation = { id: response.id } as ConversationAccount; | ||
if (response.serviceUrl) { request.serviceUrl = response.serviceUrl } | ||
// Create context and run middleware | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic as any); | ||
}); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Deletes an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.deleteActivity()` is the preferred way of deleting activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param reference Conversation reference information for the activity being deleted. | ||
*/ | ||
public deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void> { | ||
try { | ||
if (!reference.serviceUrl) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing serviceUrl`) } | ||
if (!reference.conversation || !reference.conversation.id) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing conversation or conversation.id`) } | ||
if (!reference.activityId) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing activityId`) } | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.deleteActivity(reference.conversation.id, reference.activityId); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Deletes a member from the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param memberId ID of the member to delete from the conversation. | ||
*/ | ||
public deleteConversationMember(context: TurnContext, memberId: string): Promise<void> { | ||
try { | ||
if (!context.activity.serviceUrl) { throw new Error(`BotFrameworkAdapter.deleteConversationMember(): missing serviceUrl`) } | ||
if (!context.activity.conversation || !context.activity.conversation.id) { throw new Error(`BotFrameworkAdapter.deleteConversationMember(): missing conversation or conversation.id`) } | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.deleteConversationMember(conversationId, memberId); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the members of a given activity. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activityId (Optional) activity ID to enumerate. If not specified the current activities ID will be used. | ||
*/ | ||
public getActivityMembers(context: TurnContext, activityId?: string): Promise<ChannelAccount[]> { | ||
try { | ||
if (!activityId) { activityId = context.activity.id } | ||
if (!context.activity.serviceUrl) { throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing serviceUrl`) } | ||
if (!context.activity.conversation || !context.activity.conversation.id) { throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing conversation or conversation.id`) } | ||
if (!activityId) { throw new Error(`BotFrameworkAdapter.getActivityMembers(): missing both activityId and context.activity.id`) } | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getActivityMembers(conversationId, activityId); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the members of the current conversation. | ||
* @param context Context for the current turn of conversation with the user. | ||
*/ | ||
public getConversationMembers(context: TurnContext): Promise<ChannelAccount[]> { | ||
try { | ||
if (!context.activity.serviceUrl) { throw new Error(`BotFrameworkAdapter.getConversationMembers(): missing serviceUrl`) } | ||
if (!context.activity.conversation || !context.activity.conversation.id) { throw new Error(`BotFrameworkAdapter.getConversationMembers(): missing conversation or conversation.id`) } | ||
const serviceUrl = context.activity.serviceUrl; | ||
const conversationId = context.activity.conversation.id; | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getConversationMembers(conversationId); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Lists the Conversations in which this bot has participated for a given channel server. The | ||
* channel server returns results in pages and each page will include a `continuationToken` | ||
* that can be used to fetch the next page of results from the server. | ||
* @param serviceUrl The URL of the channel server to query. This can be retrieved from `context.activity.serviceUrl`. | ||
* @param continuationToken (Optional) token used to fetch the next page of results from the channel server. This should be left as `undefined` to retrieve the first page of results. | ||
*/ | ||
public getConversations(serviceUrl: string, continuationToken?: string): Promise<ConversationsResult> { | ||
const client = this.createConnectorClient(serviceUrl); | ||
return client.conversations.getConversations(continuationToken ? { continuationToken: continuationToken } : undefined); | ||
} | ||
/** | ||
* Attempts to retrieve the token for a user that's in a logging flow. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
public getUserToken(context: TurnContext, connectionName: string, magicCode?: string): Promise<TokenResponse> { | ||
try { | ||
if (!context.activity.serviceUrl) { throw new Error(`BotFrameworkAdapter.getUserToken(): missing serviceUrl`) } | ||
if (!context.activity.from || !context.activity.from.id) { throw new Error(`BotFrameworkAdapter.getUserToken(): missing from or from.id`) } | ||
const serviceUrl = context.activity.serviceUrl; | ||
const userId = context.activity.from.id; | ||
const client = this.createOAuthApiClient(serviceUrl); | ||
return client.getUserToken(userId, connectionName, magicCode); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Signs the user out with the token server. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param connectionName Name of the auth connection to use. | ||
* @param magicCode (Optional) Optional user entered code to validate. | ||
*/ | ||
public signOutUser(context: TurnContext, connectionName: string): Promise<void> { | ||
try { | ||
if (!context.activity.serviceUrl) { throw new Error(`BotFrameworkAdapter.signOutUser(): missing serviceUrl`) } | ||
if (!context.activity.from || !context.activity.from.id) { throw new Error(`BotFrameworkAdapter.signOutUser(): missing from or from.id`) } | ||
const serviceUrl = context.activity.serviceUrl; | ||
const userId = context.activity.from.id; | ||
const client = this.createOAuthApiClient(serviceUrl); | ||
return client.signOutUser(userId, connectionName); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Processes an activity received by the bots web server. This includes any messages sent from a | ||
* user and is the method that drives what's often referred to as the bots "Reactive Messaging" | ||
* flow. | ||
* | ||
* The following steps will be taken to process the activity: | ||
* | ||
* - The identity of the sender will be verified to be either the Emulator or a valid Microsoft | ||
* server. The bots `appId` and `appPassword` will be used during this process and the request | ||
* will be rejected if the senders identity can't be verified. | ||
* - The activity will be parsed from the body of the incoming request. An error will be returned | ||
* if the activity can't be parsed. | ||
* - A `TurnContext` instance will be created for the received activity and wrapped with a | ||
* [Revocable Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable). | ||
* - The context will be routed through any middleware registered with the adapter using | ||
* [use()](#use). Middleware is executed in the order in which it's added and any middleware | ||
* can intercept or prevent further routing of the context by simply not calling the passed | ||
* in `next()` function. This is called the "Leading Edge" of the request and middleware will | ||
* get a second chance to run on the "Trailing Edge" of the request after the bots logic has run. | ||
* - Assuming the context hasn't been intercepted by a piece of middleware, the context will be | ||
* passed to the logic handler passed in. The bot may perform an additional routing or | ||
* processing at this time. Returning a promise (or providing an `async` handler) will cause the | ||
* adapter to wait for any asynchronous operations to complete. | ||
* - Once the bots logic completes the promise chain setup by the middleware stack will be resolved | ||
* giving middleware a second chance to run on the "Trailing Edge" of the request. | ||
* - After the middleware stacks promise chain has been fully resolved the context object will be | ||
* `revoked()` and any future calls to the context will result in a `TypeError: Cannot perform | ||
* 'set' on a proxy that has been revoked` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* server.post('/api/messages', (req, res) => { | ||
* // Route received request to adapter for processing | ||
* adapter.processActivity(req, res, async (context) => { | ||
* // Process any messages received | ||
* if (context.activity.type === 'message') { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* }); | ||
* ``` | ||
* @param req An Express or Restify style Request object. | ||
* @param res An Express or Restify style Response object. | ||
* @param logic A function handler that will be called to perform the bots logic after the received activity has been pre-processed by the adapter and routed through any middleware for processing. | ||
*/ | ||
public processActivity(req: WebRequest, res: WebResponse, logic: (context: TurnContext) => Promiseable<any>): Promise<void> { | ||
@@ -95,14 +372,10 @@ // Parse body of request | ||
if (request.type === ActivityTypes.Invoke) { | ||
const key = request.channelId + '/' + request.id; | ||
try { | ||
const invokeResponse = this.invokeResponses[key]; | ||
if (invokeResponse && invokeResponse.value) { | ||
const value = invokeResponse.value as InvokeResponse; | ||
res.send(value.status, value.body); | ||
res.end(); | ||
} else { | ||
throw new Error(`Bot failed to return a valid 'invokeResponse' activity.`); | ||
} | ||
} finally { | ||
if (this.invokeResponses.hasOwnProperty(key)) { delete this.invokeResponses[key] } | ||
// Retrieve cached invoke response. | ||
const invokeResponse = context.services.get(INVOKE_RESPONSE_KEY); | ||
if (invokeResponse && invokeResponse.value) { | ||
const value = invokeResponse.value as InvokeResponse; | ||
res.send(value.status, value.body); | ||
res.end(); | ||
} else { | ||
throw new Error(`Bot failed to return a valid 'invokeResponse' activity.`); | ||
} | ||
@@ -117,3 +390,3 @@ } else { | ||
// Reject response with error code | ||
console.warn(`BotFrameworkAdapter.processRequest(): ${errorCode} ERROR - ${err.toString()}`); | ||
console.warn(`BotFrameworkAdapter.processActivity(): ${errorCode} ERROR - ${err.toString()}`); | ||
res.send(errorCode, err.toString()); | ||
@@ -125,30 +398,18 @@ res.end(); | ||
public continueConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void> { | ||
const request = TurnContext.applyConversationReference({}, reference, true); | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic as any); | ||
} | ||
public createConversation(reference: Partial<ConversationReference>, logic: (context: TurnContext) => Promiseable<void>): Promise<void> { | ||
try { | ||
if (!reference.serviceUrl) { throw new Error(`BotFrameworkAdapter.createConversation(): missing serviceUrl.`) } | ||
// Create conversation | ||
const parameters = { bot: reference.bot } as ConversationParameters; | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.createConversation(parameters).then((response) => { | ||
// Initialize request and copy over new conversation ID and updated serviceUrl. | ||
const request = TurnContext.applyConversationReference({}, reference, true); | ||
request.conversation = { id: response.id } as ConversationAccount; | ||
if (response.serviceUrl) { request.serviceUrl = response.serviceUrl } | ||
// Create context and run middleware | ||
const context = this.createContext(request); | ||
return this.runMiddleware(context, logic as any); | ||
}); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Sends a set of activities to a channels server(s). The activities will be sent one after | ||
* another in the order in which they're received. A response object will be returned for each | ||
* sent activity. For `message` activities this will contain the ID of the delivered message. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* | ||
* The primary scenario for calling this method directly is when you want to explicitly bypass | ||
* going through any middleware. For instance, periodically sending a `typing` activity might | ||
* be a good reason to call this method directly as it would avoid any false signals from being | ||
* logged. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
public sendActivities(context: TurnContext, activities: Partial<Activity>[]): Promise<ResourceResponse[]> { | ||
@@ -170,4 +431,5 @@ return new Promise((resolve, reject) => { | ||
case 'invokeResponse': | ||
const key = activity.channelId + '/' + activity.replyToId; | ||
that.invokeResponses[key] = activity; | ||
// Cache response to context object. This will be retrieved when turn completes. | ||
context.services.set(INVOKE_RESPONSE_KEY, activity); | ||
responses.push({} as ResourceResponse); | ||
next(i + 1); | ||
@@ -180,3 +442,6 @@ break; | ||
const client = that.createConnectorClient(activity.serviceUrl); | ||
if (activity.replyToId) { | ||
if (activity.type === 'trace' && activity.channelId !== 'emulator') { | ||
// Just eat activity | ||
p = Promise.resolve({} as ResourceResponse); | ||
} else if (activity.replyToId) { | ||
p = client.conversations.replyToActivity( | ||
@@ -210,2 +475,11 @@ activity.conversation.id, | ||
/** | ||
* Replaces an activity that was previously sent to a channel. It should be noted that not all | ||
* channels support this feature. | ||
* | ||
* Calling `TurnContext.updateActivity()` is the preferred way of updating activities as that | ||
* will ensure that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activity New activity to replace a current activity with. | ||
*/ | ||
public updateActivity(context: TurnContext, activity: Partial<Activity>): Promise<void> { | ||
@@ -227,14 +501,7 @@ try { | ||
public deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void> { | ||
try { | ||
if (!reference.serviceUrl) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing serviceUrl`) } | ||
if (!reference.conversation || !reference.conversation.id) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing conversation or conversation.id`) } | ||
if (!reference.activityId) { throw new Error(`BotFrameworkAdapter.deleteActivity(): missing activityId`) } | ||
const client = this.createConnectorClient(reference.serviceUrl); | ||
return client.conversations.deleteActivity(reference.conversation.id, reference.activityId); | ||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
/** | ||
* Allows for the overriding of authentication in unit tests. | ||
* @param request Received request. | ||
* @param authHeader Received authentication header. | ||
*/ | ||
protected authenticateRequest(request: Partial<Activity>, authHeader: string): Promise<void> { | ||
@@ -244,6 +511,24 @@ return JwtTokenValidation.assertValidActivity(request as Activity, authHeader, this.credentialsProvider); | ||
/** | ||
* Allows for mocking of the connector client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
protected createConnectorClient(serviceUrl: string): ConnectorClient { | ||
return new ConnectorClient(this.credentials, serviceUrl); | ||
const client = new ConnectorClient(this.credentials, serviceUrl); | ||
client.addUserAgentInfo(USER_AGENT); | ||
return client; | ||
} | ||
/** | ||
* Allows for mocking of the OAuth API Client in unit tests. | ||
* @param serviceUrl Clients service url. | ||
*/ | ||
protected createOAuthApiClient(serviceUrl: string): OAuthApiClient { | ||
return new OAuthApiClient(this.createConnectorClient(serviceUrl)); | ||
} | ||
/** | ||
* Allows for the overriding of the context object in unit tests and derived adapters. | ||
* @param request Received request. | ||
*/ | ||
protected createContext(request: Partial<Activity>): TurnContext { | ||
@@ -250,0 +535,0 @@ return new TurnContext(this as any, request); |
@@ -12,3 +12,3 @@ /** | ||
/** | ||
* :package: **botbuilder-core** | ||
* :package: **botbuilder** | ||
* | ||
@@ -20,2 +20,8 @@ * Lets a user communicate with a bot from a console window. | ||
* ```JavaScript | ||
* const { ConsoleAdapter } = require('botbuilder'); | ||
* | ||
* const adapter = new ConsoleAdapter(); | ||
* const closeFn = adapter.listen(async (context) => { | ||
* await context.sendActivity(`Hello World`); | ||
* }); | ||
* ``` | ||
@@ -27,2 +33,6 @@ */ | ||
/** | ||
* Creates a new ConsoleAdapter instance. | ||
* @param reference (Optional) reference used to customize the address information of activites sent from the adapter. | ||
*/ | ||
constructor(reference?: ConversationReference) { | ||
@@ -40,3 +50,28 @@ super(); | ||
/** | ||
* Begins listening to console input. | ||
* Begins listening to console input. A function will be returned that can be used to stop the | ||
* bot listening and therefore end the process. | ||
* | ||
* Upon receiving input from the console the flow is as follows: | ||
* | ||
* - An 'message' activity will be created containing the users input text. | ||
* - A revokable `TurnContext` will be created for the activity. | ||
* - The context will be routed through any middleware registered with [use()](#use). | ||
* - The bots logic handler that was passed in will be executed. | ||
* - The promise chain setup by the middleware stack will be resolved. | ||
* - The context object will be revoked and any future calls to its members will result in a | ||
* `TypeError` being thrown. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* const closeFn = adapter.listen(async (context) => { | ||
* const utterance = context.activity.text.toLowerCase(); | ||
* if (utterance.includes('goodbye')) { | ||
* await context.sendActivity(`Ok... Goodbye`); | ||
* closeFn(); | ||
* } else { | ||
* await context.sendActivity(`Hello World`); | ||
* } | ||
* }); | ||
* ``` | ||
* @param logic Function which will be called each time a message is input by the user. | ||
@@ -65,2 +100,26 @@ */ | ||
/** | ||
* Lets a bot proactively message the user. | ||
* | ||
* The processing steps for this method are very similar to [listen()](#listen) | ||
* in that a `TurnContext` will be created which is then routed through the adapters middleware | ||
* before calling the passed in logic handler. The key difference being that since an activity | ||
* wasn't actually received it has to be created. The created activity will have its address | ||
* related fields populated but will have a `context.activity.type === undefined`. | ||
* | ||
* **Usage Example** | ||
* | ||
* ```JavaScript | ||
* function delayedNotify(context, message, delay) { | ||
* const reference = TurnContext.getConversationReference(context.activity); | ||
* setTimeout(() => { | ||
* adapter.continueConversation(reference, async (ctx) => { | ||
* await ctx.sendActivity(message); | ||
* }); | ||
* }, delay); | ||
* } | ||
* ``` | ||
* @param reference A `ConversationReference` saved during a previous message from a user. This can be calculated for any incoming activity using `TurnContext.getConversationReference(context.activity)`. | ||
* @param logic A function handler that will be called to perform the bots logic after the the adapters middleware has been run. | ||
*/ | ||
public continueConversation(reference: ConversationReference, logic: (context: TurnContext) => Promiseable<void>): Promise<void> { | ||
@@ -74,2 +133,11 @@ // Create context and run middleware pipe | ||
/** | ||
* Logs a set of activities to the console. | ||
* | ||
* Calling `TurnContext.sendActivities()` or `TurnContext.sendActivity()` is the preferred way of | ||
* sending activities as that will ensure that outgoing activities have been properly addressed | ||
* and that any interested middleware has been notified. | ||
* @param context Context for the current turn of conversation with the user. | ||
* @param activities List of activities to send. | ||
*/ | ||
public sendActivities(context: TurnContext, activities: Partial<Activity>[]): Promise<ResourceResponse[]> { | ||
@@ -109,2 +177,6 @@ const that = this; | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.updateActivity()` | ||
* will result an error being returned. | ||
*/ | ||
public updateActivity(context: TurnContext, activity: Partial<Activity>): Promise<void> { | ||
@@ -114,2 +186,6 @@ return Promise.reject(new Error(`ConsoleAdapter.updateActivity(): not supported.`)); | ||
/** | ||
* Not supported for the ConsoleAdapter. Calling this method or `TurnContext.deleteActivity()` | ||
* will result an error being returned. | ||
*/ | ||
public deleteActivity(context: TurnContext, reference: Partial<ConversationReference>): Promise<void> { | ||
@@ -119,2 +195,6 @@ return Promise.reject(new Error(`ConsoleAdapter.deleteActivity(): not supported.`)); | ||
/** | ||
* Allows for mocking of the console interface in unit tests. | ||
* @param options Console interface options. | ||
*/ | ||
protected createInterface(options: readline.ReadLineOptions): readline.ReadLine { | ||
@@ -124,2 +204,6 @@ return readline.createInterface(options); | ||
/** | ||
* Logs text to the console. | ||
* @param line Text to print. | ||
*/ | ||
protected print(line: string) { | ||
@@ -129,2 +213,6 @@ console.log(line); | ||
/** | ||
* Logs an error to the console. | ||
* @param line Error text to print. | ||
*/ | ||
protected printError(line: string) { | ||
@@ -131,0 +219,0 @@ console.error(line); |
@@ -18,3 +18,3 @@ /** | ||
* | ||
* A file based storage provider. | ||
* A file based storage provider. Items will be persisted to a folder on disk. | ||
* | ||
@@ -24,2 +24,6 @@ * **Usage Example** | ||
* ```JavaScript | ||
* const { FileStorage } = require('botbuilder'); | ||
* const path = require('path'); | ||
* | ||
* const storage = new FileStorage(path.join(__dirname, './state')); | ||
* ``` | ||
@@ -32,13 +36,7 @@ */ | ||
/** | ||
* Creates a new instance of the storage provider. | ||
* | ||
* @param path Root filesystem path for where the provider should store its objects. | ||
* Creates a new FileStorage instance. | ||
* @param path Root filesystem path for where the provider should store its items. | ||
*/ | ||
public constructor(protected readonly path: string) { } | ||
/** | ||
* Loads store items from storage | ||
* | ||
* @param keys Array of item keys to read from the store. | ||
**/ | ||
public read(keys: string[]): Promise<StoreItems> { | ||
@@ -63,7 +61,2 @@ return this.ensureFolder().then(() => { | ||
/** | ||
* Saves store items to storage. | ||
* | ||
* @param changes Map of items to write to storage. | ||
**/ | ||
public write(changes: StoreItems): Promise<void> { | ||
@@ -90,7 +83,2 @@ return this.ensureFolder().then(() => { | ||
/** | ||
* Removes store items from storage | ||
* | ||
* @param keys Array of item keys to remove from the store. | ||
**/ | ||
public delete(keys: string[]): Promise<void> { | ||
@@ -97,0 +85,0 @@ return this.ensureFolder().then(() => { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
163159
3024
+ Addedbotbuilder-core@4.0.0-m4.0(transitive)
+ Addedbotbuilder-core-extensions@4.0.0-m4.0(transitive)
+ Addedbotframework-connector@4.0.0-m4.0(transitive)
+ Addedbotframework-schema@4.0.0-m4.0(transitive)
- Removedbotbuilder-core@4.0.0-m3.0(transitive)
- Removedbotbuilder-core-extensions@4.0.0-m3.0(transitive)
- Removedbotframework-connector@4.0.0-m3.0(transitive)
- Removedbotframework-schema@4.0.0-m3.0(transitive)
Updatedbotbuilder-core@4.0.0-m4.0