Skip to content

Commit

Permalink
4.17.2 Patch Update - Reversion of breaking change (#6467)
Browse files Browse the repository at this point in the history
* Revert "[#6430] Adaptive ForEachElement does not exit when child action CancelAllDialogs is called (#6452)" (#6466)

This reverts commit c572be3.

Co-authored-by: Tracy Boehrer <[email protected]>

* Add cancellation token to TeamsOperation class (#6458)

* Fix typing indicator shown after bot has replied (#6460)

Co-authored-by: Tracy Boehrer <[email protected]>
Co-authored-by: Joel Mut <[email protected]>
  • Loading branch information
3 people authored Sep 7, 2022
1 parent 764590c commit 8307e64
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ private DialogContext CreateChildContext(DialogContext dc, DialogState childDial

private bool ShouldEndDialog(DialogTurnResult turnResult, out DialogTurnResult finalTurnResult)
{
DialogTurnStatus[] endedDialog = { DialogTurnStatus.Complete, DialogTurnStatus.Cancelled };
finalTurnResult = null;

// Insure BreakLoop ends the dialog
Expand All @@ -280,12 +279,12 @@ private bool ShouldEndDialog(DialogTurnResult turnResult, out DialogTurnResult f
// the result will be nested.
while (finalTurnResult.Result != null
&& finalTurnResult.Result is DialogTurnResult dtr
&& dtr.ParentEnded && endedDialog.Contains(dtr.Status))
&& dtr.ParentEnded && dtr.Status == DialogTurnStatus.Complete)
{
finalTurnResult = dtr;
}

return finalTurnResult.ParentEnded && endedDialog.Contains(finalTurnResult.Status);
return finalTurnResult.ParentEnded && finalTurnResult.Status == DialogTurnStatus.Complete;
}

private void UpdateActionScopeState(DialogContext dc, DialogState state)
Expand Down
86 changes: 67 additions & 19 deletions libraries/Microsoft.Bot.Builder/ShowTypingMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the MIT License.

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
Expand All @@ -22,6 +24,7 @@ public class ShowTypingMiddleware : IMiddleware
{
private readonly TimeSpan _delay;
private readonly TimeSpan _period;
private readonly ConcurrentDictionary<string, (Task, CancellationTokenSource)> _tasks = new ConcurrentDictionary<string, (Task, CancellationTokenSource)>();

/// <summary>
/// Initializes a new instance of the <see cref="ShowTypingMiddleware"/> class.
Expand Down Expand Up @@ -58,29 +61,29 @@ public ShowTypingMiddleware(int delay = 500, int period = 2000)
/// <seealso cref="Bot.Schema.IActivity"/>
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
{
using (var cts = new CancellationTokenSource())
turnContext.OnSendActivities(async (ctx, activities, nextSend) =>
{
Task typingTask = null;
try
var containsMessage = activities.Any(e => e.Type == ActivityTypes.Message);
if (containsMessage)
{
// Start a timer to periodically send the typing activity (bots running as skills should not send typing activity)
if (!IsSkillBot(turnContext) && turnContext.Activity.Type == ActivityTypes.Message)
{
// do not await task - we want this to run in the background and we will cancel it when its done
typingTask = SendTypingAsync(turnContext, _delay, _period, cts.Token);
}

await next(cancellationToken).ConfigureAwait(false);
}
finally
{
if (typingTask != null && !typingTask.IsCanceled)
{
// Cancel the typing loop.
cts.Cancel();
}
await FinishTypingTaskAsync(ctx).ConfigureAwait(false);
}
return await nextSend().ConfigureAwait(false);
});

// Start a timer to periodically send the typing activity (bots running as skills should not send typing activity)
if (!IsSkillBot(turnContext) && turnContext.Activity.Type == ActivityTypes.Message)
{
// Override the typing background task.
await FinishTypingTaskAsync(turnContext).ConfigureAwait(false);
StartTypingTask(turnContext);
}

await next(cancellationToken).ConfigureAwait(false);

// Ensures there are no Tasks left running.
await FinishTypingTaskAsync(turnContext).ConfigureAwait(false);
}

private static bool IsSkillBot(ITurnContext turnContext)
Expand Down Expand Up @@ -129,5 +132,50 @@ private static async Task SendTypingActivityAsync(ITurnContext turnContext, Canc
// make sure to send the Activity directly on the Adapter rather than via the TurnContext
await turnContext.Adapter.SendActivitiesAsync(turnContext, new Activity[] { typingActivity }, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Starts the typing background task for the current conversation.
/// </summary>
/// <param name="turnContext">The context object for this turn.</param>
private void StartTypingTask(ITurnContext turnContext)
{
if (string.IsNullOrEmpty(turnContext?.Activity?.Conversation?.Id) &&
_tasks.ContainsKey(turnContext.Activity.Conversation.Id))
{
return;
}

var cts = new CancellationTokenSource();

// do not await task - we want this to run in the background and we will cancel it when its done
var typingTask = SendTypingAsync(turnContext, _delay, _period, cts.Token);
_tasks.TryAdd(turnContext.Activity.Conversation.Id, (typingTask, cts));
}

/// <summary>
/// Finishes the typing background task for the current conversation.
/// </summary>
/// <param name="turnContext">The context object for this turn.</param>
private async Task FinishTypingTaskAsync(ITurnContext turnContext)
{
if (string.IsNullOrEmpty(turnContext?.Activity?.Conversation?.Id) &&
!_tasks.ContainsKey(turnContext.Activity.Conversation.Id))
{
return;
}

// Cancel the typing loop.
_tasks.TryGetValue(turnContext.Activity.Conversation.Id, out var item);
var (typingTask, cts) = item;
cts?.Cancel();
cts?.Dispose();
if (typingTask != null)
{
await typingTask.ConfigureAwait(false);
typingTask.Dispose();
}

_tasks.TryRemove(turnContext.Activity.Conversation.Id, out _);
}
}
}
8 changes: 4 additions & 4 deletions libraries/Microsoft.Bot.Connector/Teams/TeamsOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public TeamsOperations(TeamsConnectorClient client)
var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v3/teams/{teamId}/conversations").ToString();
url = url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));

return await GetResponseAsync<ConversationList>(url, shouldTrace, invocationId).ConfigureAwait(false);
return await GetResponseAsync<ConversationList>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -151,7 +151,7 @@ public TeamsOperations(TeamsConnectorClient client)
var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v3/teams/{teamId}").ToString();
url = url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));

return await GetResponseAsync<TeamDetails>(url, shouldTrace, invocationId).ConfigureAwait(false);
return await GetResponseAsync<TeamDetails>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -205,7 +205,7 @@ public TeamsOperations(TeamsConnectorClient client)
var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v1/meetings/{meetingId}").ToString();
url = url.Replace("{meetingId}", System.Uri.EscapeDataString(meetingId));

return await GetResponseAsync<MeetingInfo>(url, shouldTrace, invocationId, customHeaders).ConfigureAwait(false);
return await GetResponseAsync<MeetingInfo>(url, shouldTrace, invocationId, customHeaders, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -284,7 +284,7 @@ public TeamsOperations(TeamsConnectorClient client)
url = url.Replace("{participantId}", System.Uri.EscapeDataString(participantId));
url = url.Replace("{tenantId}", System.Uri.EscapeDataString(tenantId));

return await GetResponseAsync<TeamsMeetingParticipant>(url, shouldTrace, invocationId).ConfigureAwait(false);
return await GetResponseAsync<TeamsMeetingParticipant>(url, shouldTrace, invocationId, cancellationToken: cancellationToken).ConfigureAwait(false);
}

private async Task<HttpOperationResponse<T>> GetResponseAsync<T>(string url, bool shouldTrace, string invocationId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,6 @@ public async Task Action_Foreach_Nested()
await TestUtils.RunTestScript(_resourceExplorerFixture.ResourceExplorer);
}

[Fact]
public async Task Action_Foreach_Nested_WithCancel()
{
await TestUtils.RunTestScript(_resourceExplorerFixture.ResourceExplorer);
}

[Fact]
public async Task Action_Foreach_Object()
{
Expand Down

This file was deleted.

0 comments on commit 8307e64

Please sign in to comment.