0
votes

I made a bot with bot framework v4, using C#, and it's on a webpage, https://websitebotv2.azurewebsites.net/, if there's only 1 user it works fine but the moment I open it on a new tab it gives a IndexOutOfRangeException when I start the conversation.

What do I need to do to make it work with multiple tabs open?

When my bot stars it creates a waterfall dialog asking the name and greeting the user:

public dialogBotBot(dialogBotAccessors accessors, LuisRecognizer luis, QnAMaker qna)
    {
        // Set the _accessors 
        _accessors = accessors ?? throw new ArgumentNullException(nameof(accessors));
        // The DialogSet needs a DialogState accessor, it will call it when it has a turn context.
        _dialogs = new DialogSet(accessors.ConversationDialogState);

        // This array defines how the Waterfall will execute.
        var waterfallSteps = new WaterfallStep[] {
            NameStepAsync,
            NameConfirmStepAsync,
        };

        // The incoming luis variable is the LUIS Recognizer we added above.
        this.Recognizer = luis ?? throw new System.ArgumentNullException(nameof(luis));

        // The incoming QnA variable is the QnAMaker we added above.
        this.QnA = qna ?? throw new System.ArgumentNullException(nameof(qna));

        // Add named dialogs to the DialogSet. These names are saved in the dialog state.
        _dialogs.Add(new WaterfallDialog("details", waterfallSteps));
        _dialogs.Add(new TextPrompt("name"));

    }

Then I will save his name on UserProfile class, wich contains the field Name and Context, the Context has the purpose of saving the conversation.

This works the first time, but if I open a new tab or refresh the current tab for a new conversation the bot will fetch the first conversation data.

The Exception is thrown in Startup.cs in :

services.AddBot<dialogBotBot>(options =>
       {
           options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);

           // Catches any errors that occur during a conversation turn and logs them to currently
           // configured ILogger.
           ILogger logger = _loggerFactory.CreateLogger<dialogBotBot>();

           options.OnTurnError = async (context, exception) =>
           {
               logger.LogError($"Exception caught : {exception}");
               await context.SendActivityAsync(exception + "\nSorry, it looks like something went wrong.\n" + exception.Message);
           };


           // Create and add conversation state.
           var conversationState = new ConversationState(dataStore);
           options.State.Add(conversationState);

           // Create and add user state. 
           var userState = new UserState(dataStore);
           options.State.Add(userState);
       });

My onTurnAsync method is :

    public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    {

        // Handle Message activity type, which is the main activity type for shown within a conversational interface
        // Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
        // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
        if (turnContext.Activity.Type == ActivityTypes.Message)
        {
            //Get the current user profile
            userProfile = await _accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(), cancellationToken);

            userProfile.Contexto.Add(turnContext.Activity.Text);

            foreach (string s in userProfile.Contexto)
                await turnContext.SendActivityAsync(s);


            // Get the conversation state from the turn context.
            var state = await _accessors.CounterState.GetAsync(turnContext, () => new CounterState());

            // Bump the turn count for this conversation.
            state.TurnCount++;

            // Check LUIS model
            var recognizerResult = await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);
            var topIntent = recognizerResult?.GetTopScoringIntent();
            // Get the Intent as a string
            string strIntent = (topIntent != null) ? topIntent.Value.intent : "";
            // Get the IntentScore as a double
            double dblIntentScore = (topIntent != null) ? topIntent.Value.score : 0.0;
            // Only proceed with LUIS if there is an Intent 
            // and the score for the Intent is greater than 95
            if (strIntent != "" && (dblIntentScore > 2))
            {
                switch (strIntent)
                {
                    case "None":
                        //add the bot response to contexto
                        await turnContext.SendActivityAsync("Desculpa, não percebi.");
                        break;
                    case "Utilities_Help":
                        //add the bot response to contexto
                        await turnContext.SendActivityAsync("Quero-te ajudar!\nO que precisas?");
                        break;
                    default:
                        // Received an intent we didn't expect, so send its name and score.
                        //add the bot response to contexto
                        await turnContext.SendActivityAsync($"Intent: {topIntent.Value.intent} ({topIntent.Value.score}).");
                        break;
                }
            }
            else
            {
                if (userProfile.Name == null)
                {
                    // Run the DialogSet - let the framework identify the current state of the dialog from the dialog stack and figure out what (if any) is the active dialog.
                    var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
                    var results = await dialogContext.ContinueDialogAsync(cancellationToken);
                    // If the DialogTurnStatus is Empty we should start a new dialog.
                    if (results.Status == DialogTurnStatus.Empty)
                    {
                        await dialogContext.BeginDialogAsync("details", null, cancellationToken);
                    }
                }
                else
                {
                    var answers = await this.QnA.GetAnswersAsync(turnContext);
                    if (answers.Any() && answers[0].Score > 0.7)
                    {
                        // If the service produced one or more answers, send the first one.
                        await turnContext.SendActivityAsync(answers[0].Answer + "\n" + state.TurnCount);
                    }
                    else
                    {
                        var responseMessage = $"Ainda não sei a resposta mas vou averiguar\nPosso-te ajudar com mais alguma coisa?";

                        String connectionString = "Data Source=botdataserverv1.database.windows.net;" +
                                                 "Initial Catalog=botDataBase;" +
                                                 "User [email protected];" +
                                                 "Password=admin_123;";


                        SqlConnection connection = new SqlConnection(connectionString);

                        SqlDataAdapter adapter = new SqlDataAdapter();
                        SqlCommand command;

                        String sms = turnContext.Activity.Text;
                        float result = answers[0].Score;

                        String insertMessage = "insert into Mensagem(texto,contexto,grauCerteza)" +
                                               "values('" + sms + "', 'Falta apurar o contexto' ," + result + ")";

                        connection.Open();

                        command = new SqlCommand(insertMessage, connection);

                        adapter.InsertCommand = new SqlCommand(insertMessage, connection);
                        adapter.InsertCommand.ExecuteNonQuery();

                        command.Dispose();
                        connection.Close();


                        await turnContext.SendActivityAsync(responseMessage);
                    }
                }

                // Save the user profile updates into the user state.
                await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);

                // Set the property using the accessor.
                await _accessors.CounterState.SetAsync(turnContext, state);

                // Save the new turn count into the conversation state.
                await _accessors.ConversationState.SaveChangesAsync(turnContext);
            }


        }
    }
1
Sorry but without any details of your implementation, we can't help / understand / explain - Nicolas R
I edit my question with more information, hope that helps. I really don't know why that is happening - Pedro Rodrigues
I opened your bot in 5 different tabs and they all seemed to work. What are the steps to reproduce the error? What line of code is the error occurring on? If you can link to all of your bot code, that will help. I don't see anything in the code that you included that would cause this. - mdrichardson
If I were to guess, this occurs in OnMembersAddedAsync, or similar. - mdrichardson
I am not abble to install the github extension in Visual Studio right now, I will try again later. In my browser when I open the web page where the bot is hosted it works normally, but when I refresh the web page instead of restart the dialog it gives me that exception, although if I continue making questions from my QnA he answer me back right. The exception is thrown in the Startup.cs, I will put the code in the question. - Pedro Rodrigues

1 Answers

0
votes

Your problem is here:

float result = answers[0].Score;

You have:

// Check to see if we have any answers
if (answers.Any() && answers[0].Score > 0.7)
{
    [...]
    // This is fine
}
else // else, WE HAVE NO ANSWERS
{
    [...]
    // At this point, answers is an empty array, so answers[0] throws an IndexOutOfRangeException
    float result = answers[0].Score;

The reason this happens on refresh is because the new tab's user uses the same User Id. The bot already knows their name, so doesn't show the dialog, and when it calls await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);, the user hasn't entered anything in the new turnContext, so it returns an empty array.

Sidenote: You can set the userID in WebChat with this:

window.WebChat.renderWebChat(
      {
          directLine: directLine,
          userID: "USER_ID" // Make is use Math.random() or something if you want it to be random for each refresh
      },
      this.botWindowElement.nativeElement
  );