Skip to content
This repository was archived by the owner on Jun 30, 2022. It is now read-only.

Commit 3ee5239

Browse files
authored
[VA] Added "repeat last message" logic (#2484)
* added repeat logic to main * added repeat test
1 parent 51e676e commit 3ee5239

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

templates/Virtual-Assistant-Template/csharp/Sample/VirtualAssistantSample.Tests/InterruptionTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,22 @@ await GetTestFlow()
6666
.AssertReply(TemplateEngine.EvaluateTemplate("cancelledMessage"))
6767
.StartTestAsync();
6868
}
69+
70+
[TestMethod]
71+
public async Task Test_Repeat_Interruption()
72+
{
73+
await GetTestFlow()
74+
.Send(new Activity()
75+
{
76+
Type = ActivityTypes.ConversationUpdate,
77+
MembersAdded = new List<ChannelAccount>() { new ChannelAccount("user") }
78+
})
79+
.AssertReply(activity => Assert.AreEqual(1, activity.AsMessageActivity().Attachments.Count))
80+
.AssertReply(TemplateEngine.EvaluateTemplate("namePrompt"))
81+
.Send(GeneralUtterances.Repeat)
82+
.AssertReply(activity => Assert.AreEqual(1, activity.AsMessageActivity().Attachments.Count))
83+
.AssertReply(TemplateEngine.EvaluateTemplate("namePrompt"))
84+
.StartTestAsync();
85+
}
6986
}
7087
}

templates/Virtual-Assistant-Template/csharp/Sample/VirtualAssistantSample/Dialogs/MainDialog.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Globalization;
34
using System.Linq;
45
using System.Threading;
@@ -30,6 +31,7 @@ public class MainDialog : RouterDialog
3031
private OnboardingDialog _onboardingDialog;
3132
private IStatePropertyAccessor<SkillContext> _skillContext;
3233
private IStatePropertyAccessor<OnboardingState> _onboardingState;
34+
private IStatePropertyAccessor<List<Activity>> _previousResponseAccessor;
3335

3436
public MainDialog(
3537
IServiceProvider serviceProvider,
@@ -41,12 +43,18 @@ public MainDialog(
4143
_templateEngine = serviceProvider.GetService<TemplateEngine>();
4244
_langGenerator = serviceProvider.GetService<ILanguageGenerator>();
4345
_activityGenerator = serviceProvider.GetService<TextActivityGenerator>();
46+
_previousResponseAccessor = serviceProvider.GetService<IStatePropertyAccessor<List<Activity>>>();
4447
TelemetryClient = telemetryClient;
4548

49+
// Create user state properties
4650
var userState = serviceProvider.GetService<UserState>();
4751
_onboardingState = userState.CreateProperty<OnboardingState>(nameof(OnboardingState));
4852
_skillContext = userState.CreateProperty<SkillContext>(nameof(SkillContext));
4953

54+
// Create conversation state properties
55+
var conversationState = serviceProvider.GetService<ConversationState>();
56+
_previousResponseAccessor = conversationState.CreateProperty<List<Activity>>("previousResponse");
57+
5058
// Register dialogs
5159
_onboardingDialog = serviceProvider.GetService<OnboardingDialog>();
5260
AddDialog(_onboardingDialog);
@@ -59,6 +67,13 @@ public MainDialog(
5967
}
6068
}
6169

70+
protected override Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
71+
{
72+
// Set up response caching for "repeat" functionality.
73+
innerDc.Context.OnSendActivities(StoreOutgoingActivities);
74+
return base.OnContinueDialogAsync(innerDc, cancellationToken);
75+
}
76+
6277
protected override async Task<InterruptionAction> OnInterruptDialogAsync(DialogContext dc, CancellationToken cancellationToken)
6378
{
6479
var activity = dc.Context.Activity;
@@ -126,6 +141,21 @@ protected override async Task<InterruptionAction> OnInterruptDialogAsync(DialogC
126141
// Use this intent to send an event to your device that can turn off the microphone in speech scenarios.
127142
break;
128143
}
144+
145+
case GeneralLuis.Intent.Repeat:
146+
{
147+
// Sends the activities since the last user message again.
148+
var previousResponse = await _previousResponseAccessor.GetAsync(dc.Context, () => new List<Activity>());
149+
150+
foreach (var response in previousResponse)
151+
{
152+
// Reset id of original activity so it can be processed by the channel.
153+
response.Id = string.Empty;
154+
await dc.Context.SendActivityAsync(response);
155+
}
156+
157+
return InterruptionAction.Waiting;
158+
}
129159
}
130160
}
131161

@@ -298,6 +328,29 @@ private async Task CallQnAMaker(DialogContext innerDc, QnAMaker qnaMaker)
298328
}
299329
}
300330

331+
private async Task<ResourceResponse[]> StoreOutgoingActivities(ITurnContext turnContext, List<Activity> activities, Func<Task<ResourceResponse[]>> next)
332+
{
333+
var messageActivities = activities
334+
.Where(a => a.Type == ActivityTypes.Message)
335+
.ToList();
336+
337+
// If the bot is sending message activities to the user (as opposed to trace activities)
338+
if (messageActivities.Any())
339+
{
340+
var botResponse = await _previousResponseAccessor.GetAsync(turnContext, () => new List<Activity>());
341+
342+
// Get only the activities sent in response to last user message
343+
botResponse = botResponse
344+
.Concat(messageActivities)
345+
.Where(a => a.ReplyToId == turnContext.Activity.Id)
346+
.ToList();
347+
348+
await _previousResponseAccessor.SetAsync(turnContext, botResponse);
349+
}
350+
351+
return await next();
352+
}
353+
301354
private class Events
302355
{
303356
public const string Location = "VA.Location";

0 commit comments

Comments
 (0)