Response to prompt dialog not moving to the next waterfall step - c#

When I run the application for the first time, I am doing some initialization steps like asking for name and the communication mode(chat or speak), I used BeginDialogAsync to start the respective prompt dialog. But after responding to the prompt, it goes back to the MainDialog.cs I created, to check user input with LUIS, instead of going to the next waterfall step.
CODE
OptionsDialog.cs
private void InitializeWaterfallDialog()
{
var waterfallSteps = new WaterfallStep[]
{
InitialStepAsync,
FinalStepAsync
};
AddDialog(new WaterfallDialog($"{nameof(OptionsDialog)}.mainFlow", waterfallSteps));
AddDialog(new ChoicePrompt($"{nameof(OptionsDialog)}.communicationMode"));
InitialDialogId = $"{nameof(OptionsDialog)}.mainFlow";
}
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync($"{nameof(OptionsDialog)}.communicationMode",
new PromptOptions
{
Prompt = MessageFactory.Text("Please select how you want to continue interacting with me."),
Choices = ChoiceFactory.ToChoices(new List<string> { "Chat", "Speak" }),
}, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Attachment(attachments);
switch (((FoundChoice)stepContext.Result).Value)
{
case "Chat":
reply.Text = "Type something to start";
break;
case "Speak":
reply.Text = "This option is yet to be configured. Continuing with chat option. Type something to start";
break;
}
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
}
MainDialog.cs
private void InitializeWaterfallDialog()
{
var waterfallSteps = new WaterfallStep[]
{
InitialStepAsync,
FinalStepAsync
};
AddDialog(new OptionsDialog($"{nameof(MainDialog)}.options", _botStateService));
AddDialog(new GreetingDialog($"{nameof(MainDialog)}.greeting", _botStateService));
AddDialog(new RevenueDialog($"{nameof(MainDialog)}.revenue", _botStateService, _botServices, _financialServices, _currencyService));
AddDialog(new QuarterlyRevenueDialog($"{nameof(MainDialog)}.quarterlyRevenue", _botStateService, _botServices, _financialServices, _currencyService));
AddDialog(new ProfitDialog($"{nameof(MainDialog)}.profit", _botStateService, _botServices, _financialServices, _currencyService));
AddDialog(new QuarterlyProfitDialog($"{nameof(MainDialog)}.quarterlyProfit", _botStateService, _botServices, _financialServices, _currencyService));
AddDialog(new StockPriceDialog($"{nameof(MainDialog)}.stockPrice", _botStateService, _botServices, _financialServices, _currencyService));
AddDialog(new PersonNameDialog($"{nameof(MainDialog)}.personName", _botStateService, _botServices));
AddDialog(new FarewellDialog($"{nameof(MainDialog)}.farewell", _botStateService, _botServices));
AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.mainFlow", waterfallSteps));
InitialDialogId = $"{nameof(MainDialog)}.mainFlow";
}
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
try
{
UserProfile userProfile = await _botStateService.UserProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile());
var recognizerResult = await _botServices.Dispatch.RecognizeAsync(stepContext.Context, cancellationToken);
// Top intent tell us which cognitive service to use.
var topIntent = recognizerResult?.GetTopScoringIntent();
if(userProfile.HasRestartedConversation)
{
userProfile.HasRestartedConversation = false;
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.options", null, cancellationToken);
}
else
{
switch (topIntent?.intent)
{
case "GreetingIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.greeting", null, cancellationToken);
case "FindRevenueIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.revenue", null, cancellationToken);
case "FindQuarterlyRevenueIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.quarterlyRevenue", null, cancellationToken);
case "FindProfitIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.profit", null, cancellationToken);
case "FindQuarterlyProfitIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.quarterlyProfit", null, cancellationToken);
case "FindStockPriceIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.stockPrice", null, cancellationToken);
case "FindPersonNameIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.personName", null, cancellationToken);
case "FarewellIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.farewell", null, cancellationToken);
case "CommunicationSelectIntent":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.options", null, cancellationToken);
case null:
break;
default:
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Sorry, I don’t have an answer for that. " +
$"(Please try something like 'What is revenue of google?')"), cancellationToken);
break;
}
}
await _botStateService.UserProfileAccessor.SetAsync(stepContext.Context, userProfile);
}
catch (Exception e)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Sorry, there was an error sending your message. Please try again."), cancellationToken);
}
return await stepContext.NextAsync(null, cancellationToken);
}
After giving a response to the prompt in InitialStepAsync in OptionsDialog, it should go the next waterfall step, FinalStepAsync but it is going to the MainDialog and checking the response with LUIS.
The activity trigger code in the main bot program, DialogBot.cs:
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any state changes that might have occured during the turn.
await _botStateService.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
await _botStateService.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("Running dialog with Message Activity.");
// Run the Dialog with the new message Activity.
await _dialog.Run(turnContext, _botStateService.DialogStateAccessor, cancellationToken);
}
Run method:
public static async Task Run(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
{
var dialogSet = new DialogSet(accessor);
dialogSet.Add(dialog);
var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
if (results.Status == DialogTurnStatus.Empty)
{
await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
}
}
The issue here is that after responding to the prompt, it is going to BeginDialogAsync instead of ContinueDialogAsync which is only happening the first time I run the bot. If I trigger the OptionsDialog any time later, the waterfall steps are being run properly.

Related

Why does my dialog send twice? C# Azure CoreBot

This is my waterfall
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
UserStepAsync,
ActStepAsync,
FinalStepAsync,
}));
InitialDialogId = nameof(WaterfallDialog);
This is my code in the Final Step Async
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Restart the main dialog with a different message the second time around
var newMessage = "How else I can help you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, newMessage, cancellationToken);
}
This is my IntroStepAsync
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("NOTE: LUIS is not configured.", inputHint: InputHints.IgnoringInput), cancellationToken);
return await stepContext.NextAsync(null, cancellationToken);
}
// FinalStepAsync or default first time
var GreetText = stepContext.Options?.ToString() ?? "Hi there! I'm Saffi, your voice assistant. What is your name?";
var promptMessage = MessageFactory.Text(GreetText, GreetText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
This is how my bot looks like
Why does the repeat happen and how do I get rid of it?

How could I cancel task with different threads?

I'm writing an own TelegramBot. In that Bot there is a function subscribe/unsubscribe. When user starts "Subscribtion" TelegramBot should send a message "1111" each three seconds. But after unsubscription message keep sending. Could someone help me with that issue?
Method for start subscription:
private async Task OnStartSubscribeAsync(string userName, long userId,
ITelegramBotClient _client, long chatId)
{
var user = new UserDTO
{
UserId = userId.ToString(),
UserName = userName
};
await _userService.StartSubscribeAsync(user);
await _client.SendTextMessageAsync(chatId, "You subscribed successfully ");
try
{
await CheckTick(_client, chatId);
}
catch (OperationCanceledException e)
{
await _client.SendTextMessageAsync(chatId, "STOPPED");
}
finally
{
tokenSource.Dispose();
}
var articles = await ReturnNewArticles();
foreach (var item in articles)
{
var linkButton = KeyboardGoOver("Перейти", (EncodeUrl(item.Href)));
await _client.SendPhotoAsync(chatId: chatId, photo: item.Image,
caption: $"*{item.Title}*",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown,
replyMarkup: linkButton);
}
}
Method for sending message with delay:
private Task CheckTick(ITelegramBotClient _client, long chatId)
{
return Task.Run(async () =>
{
tokenSource.Token.ThrowIfCancellationRequested();
while (true)
{
await Task.Delay(3000);
await _client.SendTextMessageAsync(chatId, "1111");
if (tokenSource.Token.IsCancellationRequested)
{
tokenSource.Token.ThrowIfCancellationRequested();
}
}
}, tokenSource.Token);
}
Method for unsubscribe:
private async Task OnStopSubscibeAsync(string userName, long userId,
ITelegramBotClient _client, long chatId)
{
var user = new UserDTO()
{
UserId = userId.ToString(),
UserName = userName
};
await _userService.StopSubscribeAsync(user);
tokenSource.Cancel();
await _client.SendTextMessageAsync(chatId, "You unsubscribed successfully");
}
Definition of tokenSource:
private CancellationTokenSource tokenSource = new();
I think there are some issues with CancelationToken with threads. When I tried to debug, I didn't hit to block "catch".

Why does my extension method does not cancel my task?

In UWP I wrote the following method in the model view:
private async Task RFIDRead()
{
try
{
await nur_reader.SetupRFIDReader();
string RFID_tag = await ProdSysCommon.ExtensionMethods.TimeoutAfter(nur_reader.PerformRFIDReading(), TimeSpan.FromSeconds(15));
}
catch (TimeoutException)
{
var dialog = new MessageDialog("No RFID found in time, please scan again");
dialog.Title = "Timeout Warning";
await dialog.ShowAsync();
}
The extension method that I use is the following:
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
return await task; // Very important in order to propagate exceptions
}
else
{
throw new TimeoutException("The operation has timed out.");
}
}
}
After I run this, I catch the timeout exception, but the nur_reader.PerformRFIDReading() keeps running. What do I do wrong with my extension method? How shall I change it, so it cancels the task?

C# Task Run Cancellation Token cleanup after cancellation

How do I do some cleanup after checking if the cancellation token has been canceled but what I've found is if I try to do anything it the below code, everything works as expected except the cancellation, as soon as await LeaveGroup(SIGNALR_NETWORK_TEST_GROUP); is called the thread ends and nothing after it happens I've tried several different calls and it doesn't matter it just ends immediately instead of waiting for me to clean things up before I return.
What am I missing here? I even found a couple of examples from Microsoft that lead me to believe I'm on the right track but it doesn't let me execute anything else after I've checked if it's been canceled.
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
https://learn.microsoft.com/en-us/dotnet/standard/threading/how-to-listen-for-cancellation-requests-by-polling
static void Main()
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var signalRClient = new SignalRClient();
Task.Run(async () => await signalRClient.Start(cancellationToken), cancellationToken);
Console.ReadKey();
cancellationTokenSource.Cancel();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public async Task Start(CancellationToken cancellationToken)
{
var signalRConnectionInfo = await GetConnectionInfo();
hubConnection = new HubConnectionBuilder()
.WithUrl(signalRConnectionInfo.Url, options =>
{
options.AccessTokenProvider = () => Task.FromResult(signalRConnectionInfo.AccessToken);
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("OnUpdate", param =>
{
Console.WriteLine(param);
});
await hubConnection.StartAsync();
if (hubConnection.State == HubConnectionState.Connected)
await JoinGroup(SIGNALR_NETWORK_TEST_GROUP);
while (hubConnection.State == HubConnectionState.Connected)
{
if (cancellationToken.IsCancellationRequested)
{
Debug.WriteLine("Cancellation Requested!");
await LeaveGroup(SIGNALR_NETWORK_TEST_GROUP);
throw new OperationCanceledException("SignalR connection cancelled!", cancellationToken);
}
}

Dialog is not continuing from where it waited for user input

This code is using botframework 4
I have rootdialog which is calling an another dialog called choicedialog .From the choicedialog i am returning the DialogturnResult(waiting) from BeginDialogAsync method.After that the bot wait for the input from the user.
when the user enter something it should call the ContinueAsync Method in the ChoiceDialog.But the bot is calling the Rootdailog ContinueAsync .
what is the reason for this ?How can i solve this?
in the Controller onTurnAync Method
async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
var dc = await Dialogs.CreateContextAsync(turnContext, cancellationToken);
var dialogResult = await dc.ContinueDialogAsync();
if (!dc.Context.Responded)
{
// examine results from active dialog
switch (dialogResult.Status)
{
case DialogTurnStatus.Empty:
await dc.BeginDialogAsync(nameof(RootDialog));
break;
case DialogTurnStatus.Waiting:
// The active dialog is waiting for a response from the user, so do nothing.
break;
case DialogTurnStatus.Complete:
await dc.EndDialogAsync();
break;
default:
await dc.CancelAllDialogsAsync();
break;
}
}
}
}
rootDialog
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext context, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
var activity = context.Context.Activity;
context.SendTyping(activity);
var response = DataFromService();
if (response == null || response.StatusCode != 1)
{
await context.PostAsync(Messages.StandardErrorMessage);
}
if (response.Data != null)
{
return await dialog.BeginDialogAsync(context);
}
else
{
return new DialogTurnResult(DialogTurnStatus.Waiting);
}
}
ChoiceDialog
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext context, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
choiceStep.SaveEntityDataInContext(context: context);
IList<IMessageActivity> messages = GenerateMessageActivity(context);
if (messages.IsCollectionValid())
{
foreach (var message in messages)
{
await context.PostActivityToUser(message);
}
}
var dialogResult = new DialogTurnResult(DialogTurnStatus.Waiting);
return dialogResult;
}
I don't see you saving the changes to DialogState anywhere. Usually this is done at the end of OnTurnAsync. You could possibly be using AutoSaveStateMiddleware, but you didn't mention that (and, frankly, I wouldn't recommend it).
Specifically you need to call SaveChangesAsync on the BotState instance that you called CreateProperty<DialogState>. Usually this is ConversationState.
If you don't do this, every turn will start with an empty dialog stack, thus the behavior you're describing where the root dialog is always being run.

Categories