Search code examples
c#botframeworkbots

how does turnContext Respond work using Waterfalldialogs?


I'm having a Problem with my TaskOnTurn-Method. When I start the bot, it sends the welcome message and a new Dialog is started, because of the

if(!turnContext.Responded)

Now while im in the Dialog it jumps again into the last if-statement and a new Dialog is started while im already in one. How does the !turnContext.Responded work? I then tried to start the Dialog in if (turnContext.Activity.MembersAdded != null) under await SendWecomeMessage. That didn't work. It then sent 1 Welcome message and then started 2 Dialogs. This also confused me.

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (turnContext == null)
            {
                throw new ArgumentNullException(nameof(turnContext));
            }

            var activity = turnContext.Activity;
            var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
            if (turnContext.Activity.Type == ActivityTypes.Message)
            {
                await dc.ContinueDialogAsync(cancellationToken);
            }
            else if (turnContext.Activity.Type == ActivityTypes.ConversationUpdate)
            {
                if (turnContext.Activity.MembersAdded != null)
                {
                    await SendWelcomeMessageAsync(turnContext, cancellationToken);
                }
            }
            else
            {
                await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected", cancellationToken: cancellationToken);
            }

            if (!turnContext.Responded)
            {
                await dc.BeginDialogAsync(ReservationDialog, cancellationToken);
            }
        }

Solution

  • TurnContext.Responded indicates whether at least one response was sent for the current turn. OnTurnAsync fires between each Waterfall step, so if ReservationDialog has a prompt, as soon as the user answers the prompt, OnTurnAsync is fired and since the bot hasn't responded within that StepContext, TurnContext.Responded is false. This is why you're getting a Dialog in the middle of another Dialog.

    There's a LOT of different routes you can go within OnTurnAsync. I definitely recommend checking out some of the samples to see how they do things--most of them are pretty well-commented.

    public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        var activity = turnContext.Activity;
    
        var dc = await Dialogs.CreateContextAsync(turnContext);
    
        // Execute on incoming messages
        if (activity.Type == ActivityTypes.Message)
        {
            if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
            {
                activity.Text = JsonConvert.SerializeObject(activity.Value);
            }
        }
    
        var dialogResult = await dc.ContinueDialogAsync();
    
        // Execute based on different situations within a Dialog. See BasicBot for examples:
        // https://github.com/Microsoft/BotBuilder-Samples/blob/master/samples/csharp_dotnetcore/13.basic-bot/BasicBot.cs#L112
        if (!dc.Context.Responded)
        {
            switch (dialogResult.Status)
            {
                case DialogTurnStatus.Empty:
                case DialogTurnStatus.Waiting:
                    break;
                case DialogTurnStatus.Complete:
                    await dc.EndDialogAsync();
                    break;
                default:
                    await dc.CancelAllDialogsAsync();
                    break;
    
            }
        }
    
        // Here's where we show welcome messages
        if (activity.Type == ActivityTypes.ConversationUpdate)
        {
            if (activity.MembersAdded != null)
            {
                foreach (var member in activity.MembersAdded)
                {
                    // This makes sure the new user isn't the bot. It's a little different from some of the samples
                    // because code has changed in the SDK and emulator
                    if (member.Name != "Bot" && member.Name != null)
                    {
                        await SendWelcomeMessageAsync(turnContext, cancellationToken);
                        await dc.BeginDialogAsync(ReservationDialog, cancellationToken);
                    }
                }
            }
        }
    
        // Be sure you're saving ConversationState at the end since DialogContext derives from it
        await _conversationState.SaveChangesAsync(turnContext);
    }