2018-02-10 02:08:12 +03:00
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
// Licensed under the MIT License.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2018-03-08 02:37:08 +03:00
|
|
|
|
using System.Linq;
|
2018-07-02 20:15:59 +03:00
|
|
|
|
using System.Threading;
|
2018-02-10 02:08:12 +03:00
|
|
|
|
using System.Threading.Tasks;
|
2018-03-08 02:37:08 +03:00
|
|
|
|
using Microsoft.Bot.Schema;
|
2018-02-10 02:08:12 +03:00
|
|
|
|
|
|
|
|
|
namespace Microsoft.Bot.Builder
|
|
|
|
|
{
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides context for a turn of a bot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Context provides information needed to process an incoming activity.
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// The context object is created by a <see cref="BotAdapter"/> and persists for the
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// length of the turn.</remarks>
|
|
|
|
|
/// <seealso cref="IBot"/>
|
|
|
|
|
/// <seealso cref="IMiddleware"/>
|
2018-04-11 23:30:52 +03:00
|
|
|
|
public class TurnContext : ITurnContext, IDisposable
|
2018-02-10 02:08:12 +03:00
|
|
|
|
{
|
2018-03-08 02:37:08 +03:00
|
|
|
|
private readonly IList<SendActivitiesHandler> _onSendActivities = new List<SendActivitiesHandler>();
|
|
|
|
|
private readonly IList<UpdateActivityHandler> _onUpdateActivity = new List<UpdateActivityHandler>();
|
|
|
|
|
private readonly IList<DeleteActivityHandler> _onDeleteActivity = new List<DeleteActivityHandler>();
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// Initializes a new instance of the <see cref="TurnContext"/> class.
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="adapter">The adapter creating the context.</param>
|
|
|
|
|
/// <param name="activity">The incoming activity for the turn;
|
|
|
|
|
/// or <c>null</c> for a turn for a proactive message.</param>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="activity"/> or
|
|
|
|
|
/// <paramref name="adapter"/> is <c>null</c>.</exception>
|
|
|
|
|
/// <remarks>For use by bot adapter implementations only.</remarks>
|
2018-03-23 00:20:04 +03:00
|
|
|
|
public TurnContext(BotAdapter adapter, Activity activity)
|
2018-02-10 02:08:12 +03:00
|
|
|
|
{
|
2018-06-28 01:33:22 +03:00
|
|
|
|
Adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
|
|
|
|
Activity = activity ?? throw new ArgumentNullException(nameof(activity));
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the bot adapter that created this context object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public BotAdapter Adapter { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the services registered on this context object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TurnContextServiceCollection Services { get; } = new TurnContextServiceCollection();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the activity associated with this turn; or <c>null</c> when processing
|
|
|
|
|
/// a proactive message.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Activity Activity { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates whether at least one response was sent for the current turn.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value><c>true</c> if at least one response was sent for the current turn.</value>
|
|
|
|
|
public bool Responded
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
private set;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a response handler for send activity operations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="handler">The handler to add to the context object.</param>
|
|
|
|
|
/// <returns>The updated context object.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="handler"/> is <c>null</c>.</exception>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <remarks>When the context's <see cref="SendActivity(IActivity)"/>
|
|
|
|
|
/// or <see cref="SendActivities(IActivity[])"/> methods are called,
|
|
|
|
|
/// the adapter calls the registered handlers in the order in which they were
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// added to the context object.
|
|
|
|
|
/// </remarks>
|
2018-03-21 03:17:11 +03:00
|
|
|
|
public ITurnContext OnSendActivities(SendActivitiesHandler handler)
|
2018-03-08 04:44:26 +03:00
|
|
|
|
{
|
|
|
|
|
if (handler == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 04:44:26 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(handler));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 04:44:26 +03:00
|
|
|
|
|
2018-03-09 05:15:47 +03:00
|
|
|
|
_onSendActivities.Add(handler);
|
2018-03-08 04:44:26 +03:00
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a response handler for update activity operations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="handler">The handler to add to the context object.</param>
|
|
|
|
|
/// <returns>The updated context object.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="handler"/> is <c>null</c>.</exception>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <remarks>When the context's <see cref="UpdateActivity(IActivity)"/> is called,
|
|
|
|
|
/// the adapter calls the registered handlers in the order in which they were
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// added to the context object.
|
|
|
|
|
/// </remarks>
|
2018-03-21 03:17:11 +03:00
|
|
|
|
public ITurnContext OnUpdateActivity(UpdateActivityHandler handler)
|
2018-03-08 04:44:26 +03:00
|
|
|
|
{
|
|
|
|
|
if (handler == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 04:44:26 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(handler));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 04:44:26 +03:00
|
|
|
|
|
|
|
|
|
_onUpdateActivity.Add(handler);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a response handler for delete activity operations.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="handler">The handler to add to the context object.</param>
|
|
|
|
|
/// <returns>The updated context object.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="handler"/> is <c>null</c>.</exception>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <remarks>When the context's <see cref="DeleteActivity(string)"/> is called,
|
|
|
|
|
/// the adapter calls the registered handlers in the order in which they were
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// added to the context object.
|
|
|
|
|
/// </remarks>
|
2018-03-21 03:17:11 +03:00
|
|
|
|
public ITurnContext OnDeleteActivity(DeleteActivityHandler handler)
|
2018-03-08 04:44:26 +03:00
|
|
|
|
{
|
|
|
|
|
if (handler == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 04:44:26 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(handler));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 04:44:26 +03:00
|
|
|
|
|
|
|
|
|
_onDeleteActivity.Add(handler);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends a message activity to the sender of the incoming activity.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="textReplyToSend">The text of the message to send.</param>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <param name="speak">Optional, text to be spoken by your bot on a speech-enabled
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// channel.</param>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <param name="inputHint">Optional, indicates whether your bot is accepting,
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// expecting, or ignoring user input after the message is delivered to the client.
|
|
|
|
|
/// One of: "acceptingInput", "ignoringInput", or "expectingInput".
|
|
|
|
|
/// Default is null.</param>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException">
|
|
|
|
|
/// <paramref name="textReplyToSend"/> is <c>null</c> or whitespace.</exception>
|
|
|
|
|
/// <remarks>If the activity is successfully sent, the task result contains
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// channel assigned to the activity.
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <para>See the channel's documentation for limits imposed upon the contents of
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// <paramref name="textReplyToSend"/>.</para>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <para>To control various characteristics of your bot's speech such as voice,
|
|
|
|
|
/// rate, volume, pronunciation, and pitch, specify <paramref name="speak"/> in
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// Speech Synthesis Markup Language (SSML) format.</para>
|
|
|
|
|
/// </remarks>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public async Task<ResourceResponse> SendActivityAsync(string textReplyToSend, string speak = null, string inputHint = null, CancellationToken cancellationToken = default(CancellationToken))
|
2018-03-09 05:15:47 +03:00
|
|
|
|
{
|
2018-03-20 01:53:56 +03:00
|
|
|
|
if (string.IsNullOrWhiteSpace(textReplyToSend))
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-29 22:39:47 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(textReplyToSend));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-29 22:39:47 +03:00
|
|
|
|
|
2018-03-20 01:53:56 +03:00
|
|
|
|
var activityToSend = new Activity(ActivityTypes.Message) { Text = textReplyToSend };
|
|
|
|
|
|
2018-03-30 01:51:05 +03:00
|
|
|
|
if (!string.IsNullOrEmpty(speak))
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-30 01:51:05 +03:00
|
|
|
|
activityToSend.Speak = speak;
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-30 01:51:05 +03:00
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(inputHint))
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-30 01:51:05 +03:00
|
|
|
|
activityToSend.InputHint = inputHint;
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-30 01:51:05 +03:00
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
return await SendActivityAsync(activityToSend, cancellationToken).ConfigureAwait(false);
|
2018-03-20 01:53:56 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends an activity to the sender of the incoming activity.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="activity">The activity to send.</param>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="activity"/> is <c>null</c>.</exception>
|
|
|
|
|
/// <remarks>If the activity is successfully sent, the task result contains
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// channel assigned to the activity.</remarks>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public async Task<ResourceResponse> SendActivityAsync(IActivity activity, CancellationToken cancellationToken = default(CancellationToken))
|
2018-03-20 01:53:56 +03:00
|
|
|
|
{
|
2018-06-29 01:20:11 +03:00
|
|
|
|
BotAssert.ActivityNotNull(activity);
|
2018-03-20 01:53:56 +03:00
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
ResourceResponse[] responses = await SendActivitiesAsync(new[] { activity }, cancellationToken).ConfigureAwait(false);
|
2018-03-20 01:53:56 +03:00
|
|
|
|
if (responses == null || responses.Length == 0)
|
2018-03-09 05:15:47 +03:00
|
|
|
|
{
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// It's possible an interceptor prevented the activity from having been sent.
|
|
|
|
|
// Just return an empty response in that case.
|
2018-03-20 01:53:56 +03:00
|
|
|
|
return new ResourceResponse();
|
2018-03-09 05:15:47 +03:00
|
|
|
|
}
|
2018-03-20 01:53:56 +03:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return responses[0];
|
2018-03-29 22:39:47 +03:00
|
|
|
|
}
|
2018-03-09 05:15:47 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// Sends a set of activities to the sender of the incoming activity.
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="activities">The activities to send.</param>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <remarks>If the activities are successfully sent, the task result contains
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// an array of <see cref="ResourceResponse"/> objects containing the IDs that
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// the receiving channel assigned to the activities.</remarks>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public Task<ResourceResponse[]> SendActivitiesAsync(IActivity[] activities, CancellationToken cancellationToken = default(CancellationToken))
|
2018-02-10 02:08:12 +03:00
|
|
|
|
{
|
2018-05-26 00:40:32 +03:00
|
|
|
|
if (activities == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-05-26 00:40:32 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(activities));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-05-26 00:40:32 +03:00
|
|
|
|
|
|
|
|
|
if (activities.Length == 0)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-05-26 00:40:32 +03:00
|
|
|
|
throw new ArgumentException("Expecting one or more activities, but the array was empty.", nameof(activities));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-05-26 00:40:32 +03:00
|
|
|
|
|
2018-06-28 03:25:09 +03:00
|
|
|
|
var conversationReference = this.Activity.GetConversationReference();
|
2018-05-24 00:50:35 +03:00
|
|
|
|
|
2018-05-24 23:39:04 +03:00
|
|
|
|
var bufferedActivities = new List<Activity>(activities.Length);
|
2018-05-24 00:50:35 +03:00
|
|
|
|
|
2018-05-24 23:39:04 +03:00
|
|
|
|
for (var index = 0; index < activities.Length; index++)
|
2018-03-29 22:39:47 +03:00
|
|
|
|
{
|
2018-05-24 23:39:04 +03:00
|
|
|
|
// Buffer the incoming activities into a List<T> since we allow the set to be manipulated by the callbacks
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// Bind the relevant Conversation Reference properties, such as URLs and
|
2018-05-24 00:50:35 +03:00
|
|
|
|
// ChannelId's, to the activity we're about to send
|
2018-06-28 03:25:09 +03:00
|
|
|
|
bufferedActivities.Add(activities[index].ApplyConversationReference(conversationReference));
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
// If there are no callbacks registered, bypass the overhead of invoking them and send directly to the adapter
|
|
|
|
|
if (_onSendActivities.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
return SendActivitiesThroughAdapter();
|
|
|
|
|
}
|
2018-03-13 21:40:33 +03:00
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
// Send through the full callback pipeline
|
|
|
|
|
return SendActivitiesThroughCallbackPipeline();
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
Task<ResourceResponse[]> SendActivitiesThroughCallbackPipeline(int nextCallbackIndex = 0)
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
2018-05-24 00:50:35 +03:00
|
|
|
|
// If we've executed the last callback, we now send straight to the adapter
|
|
|
|
|
if (nextCallbackIndex == _onSendActivities.Count)
|
2018-04-14 01:09:44 +03:00
|
|
|
|
{
|
2018-05-24 00:50:35 +03:00
|
|
|
|
return SendActivitiesThroughAdapter();
|
2018-04-14 01:09:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
return _onSendActivities[nextCallbackIndex].Invoke(this, bufferedActivities, () => SendActivitiesThroughCallbackPipeline(nextCallbackIndex + 1));
|
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
async Task<ResourceResponse[]> SendActivitiesThroughAdapter()
|
|
|
|
|
{
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// Send from the list which may have been manipulated via the event handlers.
|
2018-03-20 01:53:56 +03:00
|
|
|
|
// Note that 'responses' was captured from the root of the call, and will be
|
2018-03-29 22:39:47 +03:00
|
|
|
|
// returned to the original caller.
|
2018-07-04 01:52:45 +03:00
|
|
|
|
var responses = await Adapter.SendActivitiesAsync(this, bufferedActivities.ToArray(), cancellationToken).ConfigureAwait(false);
|
2018-05-24 00:50:35 +03:00
|
|
|
|
var sentNonTraceActivity = false;
|
2018-04-11 23:31:44 +03:00
|
|
|
|
|
2018-05-24 03:02:07 +03:00
|
|
|
|
for (var index = 0; index < responses.Length; index++)
|
2018-04-11 23:31:44 +03:00
|
|
|
|
{
|
2018-05-24 03:02:07 +03:00
|
|
|
|
var activity = bufferedActivities[index];
|
|
|
|
|
|
|
|
|
|
activity.Id = responses[index].Id;
|
2018-05-24 00:50:35 +03:00
|
|
|
|
|
|
|
|
|
sentNonTraceActivity |= activity.Type != ActivityTypes.Trace;
|
2018-04-11 23:31:44 +03:00
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-05-24 00:50:35 +03:00
|
|
|
|
if (sentNonTraceActivity)
|
2018-04-14 01:09:44 +03:00
|
|
|
|
{
|
2018-06-28 01:33:22 +03:00
|
|
|
|
Responded = true;
|
2018-04-14 01:09:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-11 23:31:44 +03:00
|
|
|
|
return responses;
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-08 02:37:08 +03:00
|
|
|
|
/// <summary>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// Replaces an existing activity.
|
2018-03-08 02:37:08 +03:00
|
|
|
|
/// </summary>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <param name="activity">New replacement activity.</param>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <exception cref="Microsoft.Bot.Schema.ErrorResponseException">
|
|
|
|
|
/// The HTTP operation failed and the response contained additional information.</exception>
|
|
|
|
|
/// <exception cref="System.AggregateException">
|
|
|
|
|
/// One or more exceptions occurred during the operation.</exception>
|
|
|
|
|
/// <remarks>If the activity is successfully sent, the task result contains
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// 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>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public async Task<ResourceResponse> UpdateActivityAsync(IActivity activity, CancellationToken cancellationToken = default(CancellationToken))
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
2018-03-20 01:53:56 +03:00
|
|
|
|
Activity a = (Activity)activity;
|
2018-03-13 21:40:33 +03:00
|
|
|
|
|
2018-04-11 23:31:44 +03:00
|
|
|
|
async Task<ResourceResponse> ActuallyUpdateStuff()
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
2018-07-04 01:52:45 +03:00
|
|
|
|
return await Adapter.UpdateActivityAsync(this, a, cancellationToken).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
return await UpdateActivityInternalAsync(a, _onUpdateActivity, ActuallyUpdateStuff, cancellationToken).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Deletes an existing activity.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="activityId">The ID of the activity to delete.</param>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
2018-03-29 22:39:47 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <exception cref="Microsoft.Bot.Schema.ErrorResponseException">
|
|
|
|
|
/// The HTTP operation failed and the response contained additional information.</exception>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public async Task DeleteActivityAsync(string activityId, CancellationToken cancellationToken = default(CancellationToken))
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(activityId))
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 02:37:08 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(activityId));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-07-03 04:47:02 +03:00
|
|
|
|
var cr = Activity.GetConversationReference();
|
2018-03-08 02:37:08 +03:00
|
|
|
|
cr.ActivityId = activityId;
|
|
|
|
|
|
|
|
|
|
async Task ActuallyDeleteStuff()
|
|
|
|
|
{
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await Adapter.DeleteActivityAsync(this, cr, cancellationToken).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await DeleteActivityInternalAsync(cr, _onDeleteActivity, ActuallyDeleteStuff, cancellationToken).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Deletes an existing activity.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="conversationReference">The conversation containing the activity to delete.</param>
|
2018-07-03 04:47:02 +03:00
|
|
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// <returns>A task that represents the work queued to execute.</returns>
|
|
|
|
|
/// <exception cref="Microsoft.Bot.Schema.ErrorResponseException">
|
|
|
|
|
/// The HTTP operation failed and the response contained additional information.</exception>
|
2018-07-03 01:13:07 +03:00
|
|
|
|
/// <remarks>The conversation reference's <see cref="ConversationReference.ActivityId"/>
|
2018-04-02 18:57:04 +03:00
|
|
|
|
/// indicates the activity in the conversation to delete.</remarks>
|
2018-07-04 01:52:45 +03:00
|
|
|
|
public async Task DeleteActivityAsync(ConversationReference conversationReference, CancellationToken cancellationToken = default(CancellationToken))
|
2018-03-30 02:41:59 +03:00
|
|
|
|
{
|
|
|
|
|
if (conversationReference == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-30 02:41:59 +03:00
|
|
|
|
throw new ArgumentNullException(nameof(conversationReference));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-04-11 23:31:44 +03:00
|
|
|
|
|
2018-03-30 02:41:59 +03:00
|
|
|
|
async Task ActuallyDeleteStuff()
|
|
|
|
|
{
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await Adapter.DeleteActivityAsync(this, conversationReference, cancellationToken).ConfigureAwait(false);
|
2018-03-30 02:41:59 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await DeleteActivityInternalAsync(conversationReference, _onDeleteActivity, ActuallyDeleteStuff, cancellationToken).ConfigureAwait(false);
|
2018-03-30 02:41:59 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-03 01:13:07 +03:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
Services.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
private async Task<ResourceResponse> UpdateActivityInternalAsync(
|
2018-07-03 01:13:07 +03:00
|
|
|
|
Activity activity,
|
2018-03-08 02:37:08 +03:00
|
|
|
|
IEnumerable<UpdateActivityHandler> updateHandlers,
|
2018-07-02 20:15:59 +03:00
|
|
|
|
Func<Task<ResourceResponse>> callAtBottom,
|
|
|
|
|
CancellationToken cancellationToken)
|
2018-02-25 21:44:04 +03:00
|
|
|
|
{
|
|
|
|
|
BotAssert.ActivityNotNull(activity);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
if (updateHandlers == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 02:37:08 +03:00
|
|
|
|
throw new ArgumentException(nameof(updateHandlers));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-07-03 04:47:02 +03:00
|
|
|
|
// No middleware to run.
|
|
|
|
|
if (updateHandlers.Count() == 0)
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
if (callAtBottom != null)
|
|
|
|
|
{
|
2018-06-29 01:20:11 +03:00
|
|
|
|
return await callAtBottom().ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-11 23:31:44 +03:00
|
|
|
|
return null;
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
// Default to "No more Middleware after this".
|
2018-07-04 01:52:45 +03:00
|
|
|
|
async Task<ResourceResponse> Next()
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
// Remove the first item from the list of middleware to call,
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// so that the next call just has the remaining items to worry about.
|
2018-03-08 02:37:08 +03:00
|
|
|
|
IEnumerable<UpdateActivityHandler> remaining = updateHandlers.Skip(1);
|
2018-07-04 01:52:45 +03:00
|
|
|
|
var result = await UpdateActivityInternalAsync(activity, remaining, callAtBottom, cancellationToken).ConfigureAwait(false);
|
2018-04-11 23:31:44 +03:00
|
|
|
|
activity.Id = result.Id;
|
|
|
|
|
return result;
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// Grab the current middleware, which is the 1st element in the array, and execute it
|
2018-03-08 02:37:08 +03:00
|
|
|
|
UpdateActivityHandler toCall = updateHandlers.First();
|
2018-07-04 01:52:45 +03:00
|
|
|
|
return await toCall(this, activity, Next).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 01:52:45 +03:00
|
|
|
|
private async Task DeleteActivityInternalAsync(
|
2018-07-03 01:13:07 +03:00
|
|
|
|
ConversationReference cr,
|
|
|
|
|
IEnumerable<DeleteActivityHandler> updateHandlers,
|
|
|
|
|
Func<Task> callAtBottom,
|
|
|
|
|
CancellationToken cancellationToken)
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
BotAssert.ConversationReferenceNotNull(cr);
|
2018-06-29 01:20:11 +03:00
|
|
|
|
|
2018-03-08 02:37:08 +03:00
|
|
|
|
if (updateHandlers == null)
|
2018-07-03 04:47:02 +03:00
|
|
|
|
{
|
2018-03-08 02:37:08 +03:00
|
|
|
|
throw new ArgumentException(nameof(updateHandlers));
|
2018-07-03 04:47:02 +03:00
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
|
2018-07-03 04:47:02 +03:00
|
|
|
|
// No middleware to run.
|
|
|
|
|
if (updateHandlers.Count() == 0)
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
if (callAtBottom != null)
|
|
|
|
|
{
|
2018-06-29 01:20:11 +03:00
|
|
|
|
await callAtBottom().ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
// Default to "No more Middleware after this".
|
2018-07-04 01:52:45 +03:00
|
|
|
|
async Task Next()
|
2018-03-08 02:37:08 +03:00
|
|
|
|
{
|
|
|
|
|
// Remove the first item from the list of middleware to call,
|
2018-07-03 01:13:07 +03:00
|
|
|
|
// so that the next call just has the remaining items to worry about.
|
2018-03-08 02:37:08 +03:00
|
|
|
|
IEnumerable<DeleteActivityHandler> remaining = updateHandlers.Skip(1);
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await DeleteActivityInternalAsync(cr, remaining, callAtBottom, cancellationToken).ConfigureAwait(false);
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 22:39:47 +03:00
|
|
|
|
// Grab the current middleware, which is the 1st element in the array, and execute it.
|
2018-03-08 02:37:08 +03:00
|
|
|
|
DeleteActivityHandler toCall = updateHandlers.First();
|
2018-07-04 01:52:45 +03:00
|
|
|
|
await toCall(this, cr, Next).ConfigureAwait(false);
|
2018-02-25 21:44:04 +03:00
|
|
|
|
}
|
2018-03-08 02:37:08 +03:00
|
|
|
|
}
|
2018-02-10 02:08:12 +03:00
|
|
|
|
}
|