botbuilder-dotnet/libraries/Microsoft.Bot.Builder/BotAdapter.cs

362 строки
21 KiB
C#
Исходник Постоянная ссылка Ответственный История

Этот файл содержит неоднозначные символы Юникода!

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Globalization;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
namespace Microsoft.Bot.Builder
{
/// <summary>
/// Represents a bot adapter that can connect a bot to a service endpoint.
/// This class is abstract.
/// </summary>
/// <remarks>The bot adapter encapsulates authentication processes and sends
/// activities to and receives activities from the Bot Connector Service. When your
/// bot receives an activity, the adapter creates a context object, passes it to your
/// bot's application logic, and sends responses back to the user's channel.
/// <para>Use <see cref="Use(IMiddleware)"/> to add <see cref="IMiddleware"/> objects
/// to your adapters middleware collection. The adapter processes and directs
/// incoming activities in through the bot middleware pipeline to your bots logic
/// and then back out again. As each activity flows in and out of the bot, each piece
/// of middleware can inspect or act upon the activity, both before and after the bot
/// logic runs.</para>
/// </remarks>
/// <seealso cref="ITurnContext"/>
/// <seealso cref="IActivity"/>
/// <seealso cref="IBot"/>
/// <seealso cref="IMiddleware"/>
public abstract class BotAdapter
{
/// <summary>
/// The key value for any InvokeResponseActivity that would be on the TurnState.
/// </summary>
public const string InvokeResponseKey = "BotFrameworkAdapter.InvokeResponse";
/// <summary>
/// The string value for the bot identity key.
/// </summary>
public const string BotIdentityKey = "BotIdentity";
/// <summary>
/// The string value for the OAuth scope key.
/// </summary>
public const string OAuthScopeKey = "Microsoft.Bot.Builder.BotAdapter.OAuthScope";
/// <summary>
/// Initializes a new instance of the <see cref="BotAdapter"/> class.
/// </summary>
public BotAdapter()
: base()
{
}
/// <summary>
/// Gets or sets an error handler that can catch exceptions in the middleware or application.
/// </summary>
/// <value>An error handler that can catch exceptions in the middleware or application.</value>
public Func<ITurnContext, Exception, Task> OnTurnError { get; set; }
/// <summary>
/// Gets the collection of middleware in the adapter's pipeline.
/// </summary>
/// <value>The middleware collection for the pipeline.</value>
public MiddlewareSet MiddlewareSet { get; } = new MiddlewareSet();
/// <summary>
/// Adds middleware to the adapter's pipeline.
/// </summary>
/// <param name="middleware">The middleware to add.</param>
/// <returns>The updated adapter object.</returns>
/// <remarks>Middleware is added to the adapter at initialization time.
/// For each turn, the adapter calls middleware in the order in which you added it.
/// </remarks>
public BotAdapter Use(IMiddleware middleware)
{
MiddlewareSet.Use(middleware);
return this;
}
/// <summary>
/// When overridden in a derived class, sends activities to the conversation.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="activities">The activities to send.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the activities are successfully sent, the task result contains
/// an array of <see cref="ResourceResponse"/> objects containing the IDs that
/// the receiving channel assigned to the activities.</remarks>
/// <seealso cref="ITurnContext.OnSendActivities(SendActivitiesHandler)"/>
public abstract Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken);
/// <summary>
/// When overridden in a derived class, replaces an existing activity in the
/// conversation.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="activity">New replacement activity.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the activity is successfully sent, the task result contains
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
/// channel assigned to the activity.
/// <para>Before calling this, set the ID of the replacement activity to the ID
/// of the activity to replace.</para></remarks>
/// <seealso cref="ITurnContext.OnUpdateActivity(UpdateActivityHandler)"/>
public abstract Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken);
/// <summary>
/// When overridden in a derived class, deletes an existing activity in the
/// conversation.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="reference">Conversation reference for the activity to delete.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>The <see cref="ConversationReference.ActivityId"/> of the conversation
/// reference identifies the activity to delete.</remarks>
/// <seealso cref="ITurnContext.OnDeleteActivity(DeleteActivityHandler)"/>
public abstract Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken);
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="botId">The application ID of the bot. This parameter is ignored in
/// single tenant the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
/// which is multi-tenant aware. </param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(string botId, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
using (var context = new TurnContext(this, reference.GetContinuationActivity()))
{
return RunPipelineAsync(context, callback, cancellationToken);
}
}
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="audience">A value signifying the recipient of the proactive message.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, string audience, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="botId">The application ID of the bot. This parameter is ignored in
/// single tenant the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
/// which is multi-tenant aware. </param>
/// <param name="continuationActivity">An <see cref="Activity"/> with the appropriate <see cref="ConversationReference"/> with which to continue the conversation.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(string botId, Activity continuationActivity, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="continuationActivity">An <see cref="Activity"/> with the appropriate <see cref="ConversationReference"/> with which to continue the conversation.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, Activity continuationActivity, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Sends a proactive message to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="continuationActivity">An <see cref="Activity"/> with the appropriate <see cref="ConversationReference"/> with which to continue the conversation.</param>
/// <param name="audience">A value signifying the recipient of the proactive message.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initiate a conversation with a bot
/// before the bot can send activities to the user.</remarks>
/// <seealso cref="RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public virtual Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, Activity continuationActivity, string audience, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a conversation on the specified channel.
/// </summary>
/// <param name="botAppId">TThe application ID of the bot.</param>
/// <param name="channelId">The ID for the channel.</param>
/// <param name="serviceUrl">The channel's service URL endpoint.</param>
/// <param name="audience">The audience for the connector.</param>
/// <param name="conversationParameters">The conversation information to use to
/// create the conversation.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>To start a conversation, your bot must know its account information
/// and the user's account information on that channel.
/// Most _channels only support initiating a direct message (non-group) conversation.
/// <para>The adapter attempts to create a new conversation on the channel, and
/// then sends a <c>conversationUpdate</c> activity through its middleware pipeline
/// to the <paramref name="callback"/> method.</para>
/// <para>If the conversation is established with the
/// specified users, the ID of the activity's <see cref="IActivity.Conversation"/>
/// will contain the ID of the new conversation.</para>
/// </remarks>
public virtual Task CreateConversationAsync(string botAppId, string channelId, string serviceUrl, string audience, ConversationParameters conversationParameters, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Creates a turn context and runs the middleware pipeline for an incoming TRUSTED activity.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the request.</param>
/// <param name="activity">The incoming activity.</param>
/// <param name="callback">The code to run at the end of the adapter's middleware pipeline.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public virtual Task<InvokeResponse> ProcessActivityAsync(ClaimsIdentity claimsIdentity, Activity activity, BotCallbackHandler callback, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Starts activity processing for the current bot turn.
/// </summary>
/// <param name="turnContext">The turn's context object.</param>
/// <param name="callback">A callback method to run at the end of the pipeline.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="turnContext"/> is null.</exception>
/// <remarks>The adapter calls middleware in the order in which you added it.
/// The adapter passes in the context object for the turn and a next delegate,
/// and the middleware calls the delegate to pass control to the next middleware
/// in the pipeline. Once control reaches the end of the pipeline, the adapter calls
/// the <paramref name="callback"/> method. If a middleware component doesnt call
/// the next delegate, the adapter does not call any of the subsequent middlewares
/// <see cref="IMiddleware.OnTurnAsync(ITurnContext, NextDelegate, CancellationToken)"/>
/// methods or the callback method, and the pipeline short circuits.
/// <para>When the turn is initiated by a user activity (reactive messaging), the
/// callback method will be a reference to the bot's
/// <see cref="IBot.OnTurnAsync(ITurnContext, CancellationToken)"/> method. When the turn is
/// initiated by a call to <see cref="ContinueConversationAsync(string, ConversationReference, BotCallbackHandler, CancellationToken)"/>
/// (proactive messaging), the callback method is the callback method that was provided in the call.</para>
/// </remarks>
protected async Task RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
{
BotAssert.ContextNotNull(turnContext);
// Call any registered Middleware Components looking for ReceiveActivityAsync()
if (turnContext.Activity != null)
{
if (turnContext.Activity.Locale != null)
{
try
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(turnContext.Activity.Locale);
if (turnContext is TurnContext ctx)
{
ctx.Locale = turnContext.Activity.Locale;
}
}
catch (CultureNotFoundException)
{
// if turnContext.Activity.Locale is invalid, then TurnContext.Locale will set to Thread.CurrentThread.CurrentCulture.Name as default.
(turnContext as TurnContext).Locale = Thread.CurrentThread.CurrentCulture.Name;
}
}
try
{
await MiddlewareSet.ReceiveActivityWithStatusAsync(turnContext, callback, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
if (OnTurnError != null)
{
await OnTurnError.Invoke(turnContext, e).ConfigureAwait(false);
}
else
{
throw;
}
}
}
else
{
// call back to caller on proactive case
if (callback != null)
{
await callback(turnContext, cancellationToken).ConfigureAwait(false);
}
}
}
}
}