960 строки
56 KiB
C#
960 строки
56 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Bot.Schema;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace Microsoft.Bot.Builder
|
|
{
|
|
/// <summary>
|
|
/// An implementation of the <see cref="IBot"/> interface, intended for further subclassing.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Derive from this class to plug in code to handle particular activity types.
|
|
/// Pre- and post-processing of <see cref="Activity"/> objects can be added by calling
|
|
/// the base class implementation from the derived class.
|
|
/// </remarks>
|
|
public class ActivityHandler : IBot
|
|
{
|
|
/// <summary>
|
|
/// Called by the adapter (for example, a <see cref="BotFrameworkAdapter"/>)
|
|
/// at runtime in order to process an inbound <see cref="Activity"/>.
|
|
/// </summary>
|
|
/// <param name="turnContext">The context object for this 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>
|
|
/// This method calls other methods in this class based on the type of the activity to
|
|
/// process, which allows a derived class to provide type-specific logic in a controlled way.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all activity types.
|
|
/// Add logic to apply before the type-specific logic before the call to the base class
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> method.
|
|
/// Add logic to apply after the type-specific logic after the call to the base class
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnMessageActivityAsync(ITurnContext{IMessageActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnMessageUpdateActivityAsync(ITurnContext{IMessageUpdateActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnMessageDeleteActivityAsync(ITurnContext{IMessageDeleteActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnUnrecognizedActivityTypeAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="Activity.Type"/>
|
|
/// <seealso cref="ActivityTypes"/>
|
|
public virtual async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
if (turnContext == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(turnContext));
|
|
}
|
|
|
|
if (turnContext.Activity == null)
|
|
{
|
|
throw new ArgumentException($"{nameof(turnContext)} must have non-null Activity.");
|
|
}
|
|
|
|
if (turnContext.Activity.Type == null)
|
|
{
|
|
throw new ArgumentException($"{nameof(turnContext)}.Activity must have non-null Type.");
|
|
}
|
|
|
|
switch (turnContext.Activity.Type)
|
|
{
|
|
case ActivityTypes.Message:
|
|
await OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.MessageUpdate:
|
|
await OnMessageUpdateActivityAsync(new DelegatingTurnContext<IMessageUpdateActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.MessageDelete:
|
|
await OnMessageDeleteActivityAsync(new DelegatingTurnContext<IMessageDeleteActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.ConversationUpdate:
|
|
await OnConversationUpdateActivityAsync(new DelegatingTurnContext<IConversationUpdateActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.MessageReaction:
|
|
await OnMessageReactionActivityAsync(new DelegatingTurnContext<IMessageReactionActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.Event:
|
|
await OnEventActivityAsync(new DelegatingTurnContext<IEventActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.Invoke:
|
|
var invokeResponse = await OnInvokeActivityAsync(new DelegatingTurnContext<IInvokeActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
|
|
// If OnInvokeActivityAsync has already sent an InvokeResponse, do not send another one.
|
|
if (invokeResponse != null && turnContext.TurnState.Get<Activity>(BotFrameworkAdapter.InvokeResponseKey) == null)
|
|
{
|
|
await turnContext.SendActivityAsync(new Activity { Value = invokeResponse, Type = ActivityTypesEx.InvokeResponse }, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
break;
|
|
|
|
case ActivityTypes.EndOfConversation:
|
|
await OnEndOfConversationActivityAsync(new DelegatingTurnContext<IEndOfConversationActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.Typing:
|
|
await OnTypingActivityAsync(new DelegatingTurnContext<ITypingActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.InstallationUpdate:
|
|
await OnInstallationUpdateActivityAsync(new DelegatingTurnContext<IInstallationUpdateActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.Command:
|
|
await OnCommandActivityAsync(new DelegatingTurnContext<ICommandActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
case ActivityTypes.CommandResult:
|
|
await OnCommandResultActivityAsync(new DelegatingTurnContext<ICommandResultActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
|
|
default:
|
|
await OnUnrecognizedActivityTypeAsync(turnContext, cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// An <see cref="InvokeResponse"/> factory that initializes the body to the parameter passed and status equal to OK.
|
|
/// </summary>
|
|
/// <param name="body">JSON serialized content from a POST response.</param>
|
|
/// <returns>A new <see cref="InvokeResponse"/> object.</returns>
|
|
protected static InvokeResponse CreateInvokeResponse(object body = null)
|
|
{
|
|
return new InvokeResponse { Status = (int)HttpStatusCode.OK, Body = body };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.Message"/> activities, such as the conversational logic.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.MessageUpdate"/> activities, such as the conversational logic.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message update activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnMessageUpdateActivityAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.MessageDelete"/> activities, such as the conversational logic.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message delete activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnMessageDeleteActivityAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when a conversation update activity is received from the channel when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// Conversation update activities are useful when it comes to responding to users being added to or removed from the conversation.
|
|
/// For example, a bot could respond to a user being added by greeting the user.
|
|
/// By default, this method will call <see cref="OnMembersAddedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// if any users have been added or <see cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// if any users have been removed. The method checks the member ID so that it only responds to updates regarding members other than the bot itself.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a conversation update activity, it calls this method.
|
|
/// If the conversation update activity indicates that members other than the bot joined the conversation, it calls
|
|
/// <see cref="OnMembersAddedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>.
|
|
/// If the conversation update activity indicates that members other than the bot left the conversation, it calls
|
|
/// <see cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all conversation update activities.
|
|
/// Add logic to apply before the member added or removed logic before the call to the base class
|
|
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> method.
|
|
/// Add logic to apply after the member added or removed logic after the call to the base class
|
|
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnMembersAddedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
protected virtual Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
if (turnContext.Activity.MembersAdded != null)
|
|
{
|
|
if (turnContext.Activity.MembersAdded.Any(m => m.Id != turnContext.Activity.Recipient?.Id))
|
|
{
|
|
return OnMembersAddedAsync(turnContext.Activity.MembersAdded, turnContext, cancellationToken);
|
|
}
|
|
}
|
|
else if (turnContext.Activity.MembersRemoved != null)
|
|
{
|
|
if (turnContext.Activity.MembersRemoved.Any(m => m.Id != turnContext.Activity.Recipient?.Id))
|
|
{
|
|
return OnMembersRemovedAsync(turnContext.Activity.MembersRemoved, turnContext, cancellationToken);
|
|
}
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic for when members other than the bot
|
|
/// join the conversation, such as your bot's welcome logic.
|
|
/// </summary>
|
|
/// <param name="membersAdded">A list of all the members added to the conversation, as
|
|
/// described by the conversation update activity.</param>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// method receives a conversation update activity that indicates one or more users other than the bot
|
|
/// are joining the conversation, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
protected virtual Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic for when members other than the bot
|
|
/// leave the conversation, such as your bot's good-bye logic.
|
|
/// </summary>
|
|
/// <param name="membersRemoved">A list of all the members removed from the conversation, as
|
|
/// described by the conversation update activity.</param>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// method receives a conversation update activity that indicates one or more users other than the bot
|
|
/// are leaving the conversation, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
protected virtual Task OnMembersRemovedAsync(IList<ChannelAccount> membersRemoved, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when an event activity is received from the connector when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
|
/// previously sent activity. Message reactions are only supported by a few channels.
|
|
/// The activity that the message reaction corresponds to is indicated in the replyToId property.
|
|
/// The value of this property is the activity id of a previously sent activity given back to the
|
|
/// bot as the response from a send call.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message reaction activity, it calls this method.
|
|
/// If the message reaction indicates that reactions were added to a message, it calls
|
|
/// <see cref="OnReactionsAddedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>.
|
|
/// If the message reaction indicates that reactions were removed from a message, it calls
|
|
/// <see cref="OnReactionsRemovedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all message reaction activities.
|
|
/// Add logic to apply before the reactions added or removed logic before the call to the base class
|
|
/// <see cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/> method.
|
|
/// Add logic to apply after the reactions added or removed logic after the call to the base class
|
|
/// <see cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/> method.
|
|
///
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnReactionsAddedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnReactionsRemovedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
protected virtual async Task OnMessageReactionActivityAsync(ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
if (turnContext.Activity.ReactionsAdded != null)
|
|
{
|
|
await OnReactionsAddedAsync(turnContext.Activity.ReactionsAdded, turnContext, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
if (turnContext.Activity.ReactionsRemoved != null)
|
|
{
|
|
await OnReactionsRemovedAsync(turnContext.Activity.ReactionsRemoved, turnContext, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic for when reactions to a previous activity
|
|
/// are added to the conversation.
|
|
/// </summary>
|
|
/// <param name="messageReactions">The list of reactions added.</param>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
|
/// previously sent message on the conversation. Message reactions are supported by only a few channels.
|
|
/// The activity that the message is in reaction to is identified by the activity's
|
|
/// <see cref="Activity.ReplyToId"/> property. The value of this property is the activity ID
|
|
/// of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
|
|
/// which is available in the <see cref="ResourceResponse.Id"/> of the result.
|
|
/// </remarks>
|
|
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
/// <seealso cref="Activity.Id"/>
|
|
/// <seealso cref="ITurnContext.SendActivityAsync(IActivity, CancellationToken)"/>
|
|
/// <seealso cref="ResourceResponse.Id"/>
|
|
protected virtual Task OnReactionsAddedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic for when reactions to a previous activity
|
|
/// are removed from the conversation.
|
|
/// </summary>
|
|
/// <param name="messageReactions">The list of reactions removed.</param>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
|
/// previously sent message on the conversation. Message reactions are supported by only a few channels.
|
|
/// The activity that the message is in reaction to is identified by the activity's
|
|
/// <see cref="Activity.ReplyToId"/> property. The value of this property is the activity ID
|
|
/// of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
|
|
/// which is available in the <see cref="ResourceResponse.Id"/> of the result.
|
|
/// </remarks>
|
|
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
/// <seealso cref="Activity.Id"/>
|
|
/// <seealso cref="ITurnContext.SendActivityAsync(IActivity, CancellationToken)"/>
|
|
/// <seealso cref="ResourceResponse.Id"/>
|
|
protected virtual Task OnReactionsRemovedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when an event activity is received from the connector when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// Event activities can be used to communicate many different things.
|
|
/// By default, this method will call <see cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/> if the
|
|
/// activity's name is <c>tokens/response</c> or <see cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/> otherwise.
|
|
/// A <c>tokens/response</c> event can be triggered by an <see cref="OAuthCard"/>.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives an event activity, it calls this method.
|
|
/// If the event <see cref="IEventActivity.Name"/> is `tokens/response`, it calls
|
|
/// <see cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>;
|
|
/// otherwise, it calls <see cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all event activities.
|
|
/// Add logic to apply before the specific event-handling logic before the call to the base class
|
|
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> method.
|
|
/// Add logic to apply after the specific event-handling logic after the call to the base class
|
|
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> method.
|
|
///
|
|
/// Event activities communicate programmatic information from a client or channel to a bot.
|
|
/// The meaning of an event activity is defined by the <see cref="IEventActivity.Name"/> property,
|
|
/// which is meaningful within the scope of a channel.
|
|
/// A `tokens/response` event can be triggered by an <see cref="OAuthCard"/> or an OAuth prompt.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
protected virtual Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
if (turnContext.Activity.Name == SignInConstants.TokenResponseEventName)
|
|
{
|
|
return OnTokenResponseEventAsync(turnContext, cancellationToken);
|
|
}
|
|
|
|
return OnEventAsync(turnContext, cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when a <c>tokens/response</c> event is received when the base behavior of
|
|
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> is used.
|
|
/// If using an <c>OAuthPrompt</c>, override this method to forward this <see cref="Activity"/> to the current dialog.
|
|
/// By default, this method does nothing.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// method receives an event with a <see cref="IEventActivity.Name"/> of `tokens/response`,
|
|
/// it calls this method.
|
|
///
|
|
/// If your bot uses the <c>OAuthPrompt</c>, forward the incoming <see cref="Activity"/> to
|
|
/// the current dialog.
|
|
/// </remarks>
|
|
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
protected virtual Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when an event other than <c>tokens/response</c> is received when the base behavior of
|
|
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> is used.
|
|
/// This method could optionally be overridden if the bot is meant to handle miscellaneous events.
|
|
/// By default, this method does nothing.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// method receives an event with a <see cref="IEventActivity.Name"/> other than `tokens/response`,
|
|
/// it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
protected virtual Task OnEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when an invoke activity is received from the connector when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// Invoke activities can be used to communicate many different things.
|
|
/// By default, this method will call <see cref="OnSignInInvokeAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/> if the
|
|
/// activity's name is <c>signin/verifyState</c> or <c>signin/tokenExchange</c>.
|
|
/// A <c>signin/verifyState</c> or <c>signin/tokenExchange</c> invoke can be triggered by an <see cref="OAuthCard"/>.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives an invoke activity, it calls this method.
|
|
/// If the event <see cref="IInvokeActivity.Name"/> is `signin/verifyState` or `signin/tokenExchange`, it calls
|
|
/// <see cref="OnSignInInvokeAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
/// Invoke activities communicate programmatic commands from a client or channel to a bot.
|
|
/// The meaning of an invoke activity is defined by the <see cref="IInvokeActivity.Name"/> property,
|
|
/// which is meaningful within the scope of a channel.
|
|
/// A `signin/verifyState` or `signin/tokenExchange` invoke can be triggered by an <see cref="OAuthCard"/> or an OAuth prompt.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
switch (turnContext.Activity.Name)
|
|
{
|
|
case "application/search":
|
|
var searchInvokeValue = GetSearchInvokeValue(turnContext.Activity);
|
|
return CreateInvokeResponse(await OnSearchInvokeAsync(turnContext, searchInvokeValue, cancellationToken).ConfigureAwait(false));
|
|
|
|
case "adaptiveCard/action":
|
|
var invokeValue = GetAdaptiveCardInvokeValue(turnContext.Activity);
|
|
return CreateInvokeResponse(await OnAdaptiveCardInvokeAsync(turnContext, invokeValue, cancellationToken).ConfigureAwait(false));
|
|
|
|
case SignInConstants.VerifyStateOperationName:
|
|
case SignInConstants.TokenExchangeOperationName:
|
|
await OnSignInInvokeAsync(turnContext, cancellationToken).ConfigureAwait(false);
|
|
return CreateInvokeResponse();
|
|
|
|
default:
|
|
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
|
|
}
|
|
}
|
|
catch (InvokeResponseException e)
|
|
{
|
|
return e.CreateInvokeResponse();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when a <c>signin/verifyState</c> or <c>signin/tokenExchange</c> event is received when the base behavior of
|
|
/// <see cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/> is used.
|
|
/// If using an <c>OAuthPrompt</c>, override this method to forward this <see cref="Activity"/> to the current dialog.
|
|
/// By default, this method does nothing.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
/// method receives an Invoke with a <see cref="IInvokeActivity.Name"/> of `tokens/response`,
|
|
/// it calls this method.
|
|
///
|
|
/// If your bot uses the <c>OAuthPrompt</c>, forward the incoming <see cref="Activity"/> to
|
|
/// the current dialog.
|
|
/// </remarks>
|
|
/// <seealso cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
protected virtual Task OnSignInInvokeAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when the bot is sent an Adaptive Card Action Execute.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
|
/// <param name="invokeValue">A strongly-typed object from the incoming activity's Value.</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>
|
|
/// When the <see cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
/// method receives an Invoke with a <see cref="IInvokeActivity.Name"/> of `adaptiveCard/action`,
|
|
/// it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
protected virtual Task<AdaptiveCardInvokeResponse> OnAdaptiveCardInvokeAsync(ITurnContext<IInvokeActivity> turnContext, AdaptiveCardInvokeValue invokeValue, CancellationToken cancellationToken)
|
|
{
|
|
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when the bot is sent an 'invoke' activity having name of 'application/search'.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
|
/// <param name="invokeValue">A strongly-typed object from the incoming activity's Value.</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>
|
|
/// When the <see cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
/// method receives an Invoke with a <see cref="IInvokeActivity.Name"/> of `application/search`,
|
|
/// it calls this method. The Activity.Value must be a well formed <see cref="SearchInvokeValue"/>.
|
|
/// </remarks>
|
|
/// <seealso cref="OnInvokeActivityAsync(ITurnContext{IInvokeActivity}, CancellationToken)"/>
|
|
protected virtual Task<SearchInvokeResponse> OnSearchInvokeAsync(ITurnContext<IInvokeActivity> turnContext, SearchInvokeValue invokeValue, CancellationToken cancellationToken)
|
|
{
|
|
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.EndOfConversation"/> activities, such as the conversational logic.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.Typing"/> activities, such as the conversational logic.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a message activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnTypingActivityAsync(ITurnContext<ITypingActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.InstallationUpdate"/> activities.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a installation update activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnInstallationUpdateActivityAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
switch (turnContext.Activity.Action)
|
|
{
|
|
case "add":
|
|
case "add-upgrade":
|
|
return OnInstallationUpdateAddAsync(turnContext, cancellationToken);
|
|
case "remove":
|
|
case "remove-upgrade":
|
|
return OnInstallationUpdateRemoveAsync(turnContext, cancellationToken);
|
|
default:
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.InstallationUpdate"/> activities with 'action' set to 'add'.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a installation update activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnInstallationUpdateAddAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this in a derived class to provide logic specific to
|
|
/// <see cref="ActivityTypes.InstallationUpdate"/> activities with 'action' set to 'remove'.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a installation update activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
protected virtual Task OnInstallationUpdateRemoveAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when a command activity is received when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// Commands are requests to perform an action and receivers typically respond with
|
|
/// one or more commandResult activities. Receivers are also expected to explicitly
|
|
/// reject unsupported command activities.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a command activity, it calls this method.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all comand activities.
|
|
/// Add logic to apply before the specific command-handling logic before the call to the base class
|
|
/// <see cref="OnCommandActivityAsync(ITurnContext{ICommandActivity}, CancellationToken)"/> method.
|
|
/// Add logic to apply after the specific command-handling logic after the call to the base class
|
|
/// <see cref="OnCommandActivityAsync(ITurnContext{ICommandActivity}, CancellationToken)"/> method.
|
|
///
|
|
/// Command activities communicate programmatic information from a client or channel to a bot.
|
|
/// The meaning of an command activity is defined by the <see cref="ICommandActivity.Name"/> property,
|
|
/// which is meaningful within the scope of a channel.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnCommandActivityAsync(ITurnContext{ICommandActivity}, CancellationToken)"/>
|
|
protected virtual Task OnCommandActivityAsync(ITurnContext<ICommandActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when a CommandResult activity is received when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// CommandResult activities can be used to communicate the result of a command execution.
|
|
/// </summary>
|
|
/// <param name="turnContext">A strongly-typed context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives a CommandResult activity, it calls this method.
|
|
///
|
|
/// In a derived class, override this method to add logic that applies to all comand activities.
|
|
/// Add logic to apply before the specific CommandResult-handling logic before the call to the base class
|
|
/// <see cref="OnCommandResultActivityAsync(ITurnContext{ICommandResultActivity}, CancellationToken)"/> method.
|
|
/// Add logic to apply after the specific CommandResult-handling logic after the call to the base class
|
|
/// <see cref="OnCommandResultActivityAsync(ITurnContext{ICommandResultActivity}, CancellationToken)"/> method.
|
|
///
|
|
/// CommandResult activities communicate programmatic information from a client or channel to a bot.
|
|
/// The meaning of an CommandResult activity is defined by the <see cref="ICommandResultActivity.Name"/> property,
|
|
/// which is meaningful within the scope of a channel.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnCommandResultActivityAsync(ITurnContext{ICommandResultActivity}, CancellationToken)"/>
|
|
protected virtual Task OnCommandResultActivityAsync(ITurnContext<ICommandResultActivity> turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked when an activity other than a message, conversation update, or event is received when the base behavior of
|
|
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
|
/// If overridden, this could potentially respond to any of the other activity types like
|
|
/// <see cref="ActivityTypes.ContactRelationUpdate"/> or <see cref="ActivityTypes.EndOfConversation"/>.
|
|
/// By default, this method does nothing.
|
|
/// </summary>
|
|
/// <param name="turnContext">The context object for this 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>
|
|
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// method receives an activity that is not a message, conversation update, message reaction,
|
|
/// or event activity, it calls this method.
|
|
/// </remarks>
|
|
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
|
/// <seealso cref="OnMessageActivityAsync(ITurnContext{IMessageActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
|
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
|
/// <seealso cref="Activity.Type"/>
|
|
/// <seealso cref="ActivityTypes"/>
|
|
protected virtual Task OnUnrecognizedActivityTypeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static SearchInvokeValue GetSearchInvokeValue(IInvokeActivity activity)
|
|
{
|
|
if (activity.Value == null)
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Missing value property for search");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
var obj = activity.Value as JObject;
|
|
if (obj == null)
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not properly formed for search");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
SearchInvokeValue invokeValue = null;
|
|
|
|
try
|
|
{
|
|
invokeValue = obj.ToObject<SearchInvokeValue>();
|
|
}
|
|
#pragma warning disable CA1031 // Do not catch general exception types
|
|
catch
|
|
#pragma warning restore CA1031 // Do not catch general exception types
|
|
{
|
|
var errorResponse = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not valid for search");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, errorResponse);
|
|
}
|
|
|
|
ValidateSearchInvokeValue(invokeValue, activity.ChannelId);
|
|
return invokeValue;
|
|
}
|
|
|
|
private static void ValidateSearchInvokeValue(SearchInvokeValue searchInvokeValue, string channelId)
|
|
{
|
|
string missingField = null;
|
|
|
|
if (string.IsNullOrEmpty(searchInvokeValue.Kind))
|
|
{
|
|
// Teams does not always send the 'kind' field. Default to 'search'.
|
|
if (Connector.Channels.Msteams.Equals(channelId, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
searchInvokeValue.Kind = SearchInvokeTypes.Search;
|
|
}
|
|
else
|
|
{
|
|
missingField = "kind";
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(searchInvokeValue.QueryText))
|
|
{
|
|
missingField = "queryText";
|
|
}
|
|
|
|
if (missingField != null)
|
|
{
|
|
var errorResponse = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", $"Missing {missingField} property for search");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, errorResponse);
|
|
}
|
|
}
|
|
|
|
private static AdaptiveCardInvokeValue GetAdaptiveCardInvokeValue(IInvokeActivity activity)
|
|
{
|
|
if (activity.Value == null)
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Missing value property");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
var obj = activity.Value as JObject;
|
|
if (obj == null)
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not properly formed");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
AdaptiveCardInvokeValue invokeValue = null;
|
|
|
|
try
|
|
{
|
|
invokeValue = obj.ToObject<AdaptiveCardInvokeValue>();
|
|
}
|
|
#pragma warning disable CA1031 // Do not catch general exception types
|
|
catch
|
|
#pragma warning restore CA1031 // Do not catch general exception types
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Value property is not properly formed");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
if (invokeValue.Action == null)
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "BadRequest", "Missing action property");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
if (invokeValue.Action.Type != "Action.Execute")
|
|
{
|
|
var response = CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode.BadRequest, "NotSupported", $"The action '{invokeValue.Action.Type}'is not supported.");
|
|
throw new InvokeResponseException(HttpStatusCode.BadRequest, response);
|
|
}
|
|
|
|
return invokeValue;
|
|
}
|
|
|
|
private static AdaptiveCardInvokeResponse CreateAdaptiveCardInvokeErrorResponse(HttpStatusCode statusCode, string code, string message)
|
|
{
|
|
return new AdaptiveCardInvokeResponse()
|
|
{
|
|
StatusCode = (int)statusCode,
|
|
Type = "application/vnd.microsoft.error",
|
|
Value = new Error()
|
|
{
|
|
Code = code,
|
|
Message = message
|
|
}
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// A custom exception for invoke response errors.
|
|
/// </summary>
|
|
#pragma warning disable CA1064 // Exceptions should be public (we can't change this without breaking binary compat, we may consider making this type public in the future)
|
|
protected class InvokeResponseException : Exception
|
|
#pragma warning restore CA1064 // Exceptions should be public
|
|
{
|
|
private readonly HttpStatusCode _statusCode;
|
|
private readonly object _body;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="InvokeResponseException"/> class.
|
|
/// </summary>
|
|
/// <param name="statusCode">The Http status code of the error.</param>
|
|
/// <param name="body">The body of the exception. Default is null.</param>
|
|
public InvokeResponseException(HttpStatusCode statusCode, object body = null)
|
|
{
|
|
_statusCode = statusCode;
|
|
_body = body;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="InvokeResponseException"/> class.
|
|
/// </summary>
|
|
public InvokeResponseException()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="InvokeResponseException"/> class.
|
|
/// </summary>
|
|
/// <param name="message">The message that explains the reason for the exception, or an empty string.</param>
|
|
public InvokeResponseException(string message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="InvokeResponseException"/> class.
|
|
/// </summary>
|
|
/// <param name="message">The message that explains the reason for the exception, or an empty string.</param>
|
|
/// <param name="innerException">Gets the System.Exception instance that caused the current exception.</param>
|
|
public InvokeResponseException(string message, Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// A factory method that creates a new <see cref="InvokeResponse"/> object with the status code and body of the current object..
|
|
/// </summary>
|
|
/// <returns>A new <see cref="InvokeResponse"/> object.</returns>
|
|
public InvokeResponse CreateInvokeResponse()
|
|
{
|
|
return new InvokeResponse
|
|
{
|
|
Status = (int)_statusCode,
|
|
Body = _body
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|