// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Schema; namespace Microsoft.Bot.Builder { /// /// Base class for Bot Framework protocol implementation. /// public abstract class ChannelServiceHandlerBase { /// /// Sends an activity to the end of a conversation. /// /// The authentication header. /// The conversation Id. /// The activity to send. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleSendToConversationAsync(string authHeader, string conversationId, Activity activity, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnSendToConversationAsync(claimsIdentity, conversationId, activity, cancellationToken).ConfigureAwait(false); } /// /// Sends a reply to an activity. /// /// The authentication header. /// The conversation Id. /// The activity Id the reply is to. /// The activity to send. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleReplyToActivityAsync(string authHeader, string conversationId, string activityId, Activity activity, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnReplyToActivityAsync(claimsIdentity, conversationId, activityId, activity, cancellationToken).ConfigureAwait(false); } /// /// Edits a previously sent existing activity. /// /// The authentication header. /// The conversation Id. /// The activity Id to update. /// The replacement activity. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleUpdateActivityAsync(string authHeader, string conversationId, string activityId, Activity activity, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnUpdateActivityAsync(claimsIdentity, conversationId, activityId, activity, cancellationToken).ConfigureAwait(false); } /// /// Deletes an existing activity. /// /// The authentication header. /// The conversation Id. /// The activity Id. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleDeleteActivityAsync(string authHeader, string conversationId, string activityId, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); await OnDeleteActivityAsync(claimsIdentity, conversationId, activityId, cancellationToken).ConfigureAwait(false); } /// /// Enumerates the members of an activity. /// /// The authentication header. /// The conversation Id. /// The activity Id. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task> HandleGetActivityMembersAsync(string authHeader, string conversationId, string activityId, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnGetActivityMembersAsync(claimsIdentity, conversationId, activityId, cancellationToken).ConfigureAwait(false); } /// /// Create a new Conversation. /// /// The authentication header. /// Parameters to create the conversation from. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleCreateConversationAsync(string authHeader, ConversationParameters parameters, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnCreateConversationAsync(claimsIdentity, parameters, cancellationToken).ConfigureAwait(false); } /// /// Lists the Conversations in which the bot has participated. /// /// The authentication header. /// The conversation Id. /// A skip or continuation token. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleGetConversationsAsync(string authHeader, string conversationId, string continuationToken = default, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnGetConversationsAsync(claimsIdentity, conversationId, continuationToken, cancellationToken).ConfigureAwait(false); } /// /// Enumerates the members of a conversation. /// /// The authentication header. /// The conversation Id. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task> HandleGetConversationMembersAsync(string authHeader, string conversationId, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnGetConversationMembersAsync(claimsIdentity, conversationId, cancellationToken).ConfigureAwait(false); } /// /// Gets the account of a single conversation member. /// /// The authentication header. /// The user id. /// The conversation Id. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleGetConversationMemberAsync(string authHeader, string userId, string conversationId, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnGetConversationMemberAsync(claimsIdentity, userId, conversationId, cancellationToken).ConfigureAwait(false); } /// /// Enumerates the members of a conversation one page at a time. /// /// The authentication header. /// The conversation Id. /// Suggested page size. /// A continuation token. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleGetConversationPagedMembersAsync(string authHeader, string conversationId, int? pageSize = default, string continuationToken = default, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnGetConversationPagedMembersAsync(claimsIdentity, conversationId, pageSize, continuationToken, cancellationToken).ConfigureAwait(false); } /// /// Deletes a member from a conversation. /// /// The authentication header. /// The conversation Id. /// Id of the member to delete from this conversation. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the asynchronous operation. public async Task HandleDeleteConversationMemberAsync(string authHeader, string conversationId, string memberId, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); await OnDeleteConversationMemberAsync(claimsIdentity, conversationId, memberId, cancellationToken).ConfigureAwait(false); } /// /// Uploads the historic activities of the conversation. /// /// The authentication header. /// The conversation Id. /// Transcript of activities. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleSendConversationHistoryAsync(string authHeader, string conversationId, Transcript transcript, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnSendConversationHistoryAsync(claimsIdentity, conversationId, transcript, cancellationToken).ConfigureAwait(false); } /// /// Stores data in a compliant store when dealing with enterprises. /// /// The authentication header. /// The conversation Id. /// Attachment data. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A representing the result of the asynchronous operation. public async Task HandleUploadAttachmentAsync(string authHeader, string conversationId, AttachmentData attachmentUpload, CancellationToken cancellationToken = default) { var claimsIdentity = await AuthenticateAsync(authHeader, cancellationToken).ConfigureAwait(false); return await OnUploadAttachmentAsync(claimsIdentity, conversationId, attachmentUpload, cancellationToken).ConfigureAwait(false); } /// /// Helper to authenticate the header token and extract the claims. /// /// The auth header containing JWT token. /// A cancellation token. /// A representing the claims associated with given header. internal abstract Task AuthenticateAsync(string authHeader, CancellationToken cancellationToken); /// /// SendToConversation() API for Skill. /// /// /// This method allows you to send an activity to the end of a conversation. /// /// This is slightly different from ReplyToActivity(). /// * SendToConversation(conversationId) - will append the activity to the end /// of the conversation according to the timestamp or semantics of the channel. /// * ReplyToActivity(conversationId,ActivityId) - adds the activity as a reply /// to another activity, if the channel supports it. If the channel does not /// support nested replies, ReplyToActivity falls back to SendToConversation. /// /// Use ReplyToActivity when replying to a specific activity in the /// conversation. /// /// Use SendToConversation in all other cases. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// conversationId. /// Activity to send. /// The cancellation token. /// task for a resource response. protected virtual Task OnSendToConversationAsync(ClaimsIdentity claimsIdentity, string conversationId, Activity activity, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// OnReplyToActivityAsync() API. /// /// /// Override this method allows to reply to an Activity. /// /// This is slightly different from SendToConversation(). /// * SendToConversation(conversationId) - will append the activity to the end /// of the conversation according to the timestamp or semantics of the channel. /// * ReplyToActivity(conversationId,ActivityId) - adds the activity as a reply /// to another activity, if the channel supports it. If the channel does not /// support nested replies, ReplyToActivity falls back to SendToConversation. /// /// Use ReplyToActivity when replying to a specific activity in the /// conversation. /// /// Use SendToConversation in all other cases. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// activityId the reply is to (OPTIONAL). /// Activity to send. /// The cancellation token. /// task for a resource response. protected virtual Task OnReplyToActivityAsync(ClaimsIdentity claimsIdentity, string conversationId, string activityId, Activity activity, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// OnUpdateActivityAsync() API. /// /// /// Override this method to edit a previously sent existing activity. /// /// Some channels allow you to edit an existing activity to reflect the new /// state of a bot conversation. /// /// For example, you can remove buttons after someone has clicked "Approve" /// button. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// activityId to update. /// replacement Activity. /// The cancellation token. /// task for a resource response. protected virtual Task OnUpdateActivityAsync(ClaimsIdentity claimsIdentity, string conversationId, string activityId, Activity activity, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// OnDeleteActivityAsync() API. /// /// /// Override this method to Delete an existing activity. /// /// Some channels allow you to delete an existing activity, and if successful /// this method will remove the specified activity. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// activityId to delete. /// The cancellation token. /// task for a resource response. protected virtual Task OnDeleteActivityAsync(ClaimsIdentity claimsIdentity, string conversationId, string activityId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// OnGetActivityMembersAsync() API. /// /// /// Override this method to enumerate the members of an activity. /// /// This REST API takes a ConversationId and a ActivityId, returning an array /// of ChannelAccount objects representing the members of the particular /// activity in the conversation. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// Activity ID. /// The cancellation token. /// task with result. protected virtual Task> OnGetActivityMembersAsync(ClaimsIdentity claimsIdentity, string conversationId, string activityId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// CreateConversation() API. /// /// /// Override this method to create a new Conversation. /// /// POST to this method with a /// * Bot being the bot creating the conversation /// * IsGroup set to true if this is not a direct message (default is false) /// * Array containing the members to include in the conversation /// /// The return value is a ResourceResponse which contains a conversation ID /// which is suitable for use /// in the message payload and REST API URIs. /// /// Most channels only support the semantics of bots initiating a direct /// message conversation. An example of how to do that would be: /// /// var resource = await connector.conversations.CreateConversation(new /// ConversationParameters(){ Bot = bot, members = new ChannelAccount[] { new /// ChannelAccount("user1") } ); /// await connect.Conversations.OnSendToConversationAsync(resource.Id, new /// Activity() ... ) ; /// /// end. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Parameters to create the conversation from. /// The cancellation token. /// task for a conversation resource response. protected virtual Task OnCreateConversationAsync(ClaimsIdentity claimsIdentity, ConversationParameters parameters, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// OnGetConversationsAsync() API for Skill. /// /// /// Override this method to list the Conversations in which this bot has participated. /// /// GET from this method with a skip token /// /// The return value is a ConversationsResult, which contains an array of /// ConversationMembers and a skip token. If the skip token is not empty, then /// there are further values to be returned. Call this method again with the /// returned token to get more values. /// /// Each ConversationMembers object contains the ID of the conversation and an /// array of ChannelAccounts that describe the members of the conversation. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// conversationId. /// skip or continuation token. /// The cancellation token. /// task for ConversationsResult. protected virtual Task OnGetConversationsAsync(ClaimsIdentity claimsIdentity, string conversationId, string continuationToken = default, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// GetConversationMembers() API for Skill. /// /// /// Override this method to enumerate the members of a conversation. /// /// This REST API takes a ConversationId and returns an array of ChannelAccount /// objects representing the members of the conversation. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// The cancellation token. /// task for a response. protected virtual Task> OnGetConversationMembersAsync(ClaimsIdentity claimsIdentity, string conversationId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// GetConversationMember() API for Skill. /// /// /// Override this method to get the account of a single conversation member. /// /// This REST API takes a ConversationId and UserId and returns the ChannelAccount /// objects representing the member of the conversation. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// User ID. /// Conversation ID. /// The cancellation token. /// task for a response. protected virtual Task OnGetConversationMemberAsync(ClaimsIdentity claimsIdentity, string userId, string conversationId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// GetConversationPagedMembers() API for Skill. /// /// /// Override this method to enumerate the members of a conversation one page at a time. /// /// This REST API takes a ConversationId. Optionally a pageSize and/or /// continuationToken can be provided. It returns a PagedMembersResult, which /// contains an array /// of ChannelAccounts representing the members of the conversation and a /// continuation token that can be used to get more values. /// /// One page of ChannelAccounts records are returned with each call. The number /// of records in a page may vary between channels and calls. The pageSize /// parameter can be used as /// a suggestion. If there are no additional results the response will not /// contain a continuation token. If there are no members in the conversation /// the Members will be empty or not present in the response. /// /// A response to a request that has a continuation token from a prior request /// may rarely return members from a previous request. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// Suggested page size. /// Continuation Token. /// The cancellation token. /// task for a response. protected virtual Task OnGetConversationPagedMembersAsync(ClaimsIdentity claimsIdentity, string conversationId, int? pageSize = default, string continuationToken = default, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// DeleteConversationMember() API for Skill. /// /// /// Override this method to deletes a member from a conversation. /// /// This REST API takes a ConversationId and a memberId (of type string) and /// removes that member from the conversation. If that member was the last /// member /// of the conversation, the conversation will also be deleted. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// ID of the member to delete from this conversation. /// The cancellation token. /// task. protected virtual Task OnDeleteConversationMemberAsync(ClaimsIdentity claimsIdentity, string conversationId, string memberId, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// SendConversationHistory() API for Skill. /// /// /// Override this method to this method allows you to upload the historic activities to the conversation. /// /// Sender must ensure that the historic activities have unique ids and /// appropriate timestamps. The ids are used by the client to deal with /// duplicate activities and the timestamps are used by the client to render /// the activities in the right order. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// Transcript of activities. /// The cancellation token. /// task for a resource response. protected virtual Task OnSendConversationHistoryAsync(ClaimsIdentity claimsIdentity, string conversationId, Transcript transcript, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// UploadAttachment() API. /// /// /// /// Override this method to store data in a compliant store when dealing with enterprises. /// /// The response is a ResourceResponse which contains an AttachmentId which is /// suitable for using with the attachments API. /// /// claimsIdentity for the bot, should have AudienceClaim, AppIdClaim and ServiceUrlClaim. /// Conversation ID. /// Attachment data. /// The cancellation token. /// task with result. protected virtual Task OnUploadAttachmentAsync(ClaimsIdentity claimsIdentity, string conversationId, AttachmentData attachmentUpload, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } } }