Search code examples
javascriptbotframeworkprompt

Repeated ChoicePrompt clears UserState


I implemented a bot with the Microsoft BotFramework. For gathering user data, I use ChoicePrompts. When a user doesn't choose one of the proposed options, the ChoicePrompt repeats until the user enters a valid option (this is default behavior from the prompt method).

Unfortunately, after not choosing one of the valid choice options, the user state is refreshed. This means that I lose all the collected user data until that point.

Is this behavior intended or is there a way to prevent this?


Solution

  • You have a couple issues with your code on the link you provided. As I can't see your complete code, I've made a guess at some parts.

    1. Double check userState and userData are properly setup. My constructor looks like this:
    const DIALOG_STATE_PROPERTY = 'dialogState';
    const USER_PROFILE_PROPERTY = 'user';
    
    const EDUCATION_PROMPT = 'education_prompt';
    const MAJOR_PROMPT = 'major_prompt';
    
    constructor(conversationState, userState) {
        this.conversationState = conversationState;
        this.userState = userState;
    
        this.dialogState = this.conversationState.createProperty(DIALOG_STATE_PROPERTY);
        this.userData = this.userState.createProperty(USER_PROFILE_PROPERTY);
    
        this.dialogs = new DialogSet(this.dialogState);
    
        // Add prompts that will be used by the main dialogs.
        this.dialogs
            .add(new TextPrompt(NAME_PROMPT))
            .add(new TextPrompt(AGE_PROMPT))
            .add(new TextPrompt(GENDER_PROMPT))
            .add(new ChoicePrompt(EDUCATION_PROMPT))
            .add(new ChoicePrompt(MAJOR_PROMPT));
    
        // Create dialog for prompting user for profile data
        this.dialogs.add(new WaterfallDialog(START_DIALOG, [
            this.promptForName.bind(this),
            this.promptForAge.bind(this),
            this.promptForGender.bind(this),
            this.promptForEducation.bind(this),
            this.promptForMajor.bind(this),
            this.returnUser.bind(this)
        ]));
    
        this.majors = ['English', 'History', 'Computer Science'];
    }
    
    1. Remember that TextPrompt returns the value in step.result. ChoicePrompt returns the value as step.result.value

      I'm assuming that in your "promptForEducation" step where you assign the gender value to user that that value is coming from a choice prompt. If not, you will lose the value. Double check you are specifying the correct source.

    .add(new TextPrompt(GENDER_PROMPT))
    .add(new ChoicePrompt(EDUCATION_PROMPT))
    
    ...
    
    if (!user.gender) {
        user.gender = step.result;
        // Give user object back to UserState storage
        await this.userData.set(step.context, user);
        console.log(user);
    }
    
    1. In the "promptForMajor" step, the 2nd parameter in step.Prompt takes a string and represents the dialog portion of the choice. Your code should look like this and will produce the following output. In this instance, I assigned values to "this.majors" in the constructor (shown above).
    this.majors = ['English', 'History', 'Computer Science'];
    
    ...
    
    if (!user.major) {
        // Copy List of majors and add "Other" entry
        let majorsOther = this.majors.slice(0, this.majors.length);
        majorsOther.push('Einen anderen Studiengang');
        // return await step.prompt(MAJOR_PROMPT, this.userData.major, majorsOther);
        return await step.prompt(MAJOR_PROMPT, 'List of majors:', majorsOther);
    }
    

    enter image description here

    1. Check that you are saving state at the end of the "onTurn".
    // Save changes to the user state.
    await this.userState.saveChanges(turnContext);
    
    // End this turn by saving changes to the conversation state.
    await this.conversationState.saveChanges(turnContext);
    

    If you implement the above, you should be set. I was able to run with no issues and no loss of state. Additionally, repeatedly not answering the ChoicePrompt before finally providing a proper response did not break state.

    enter image description here

    Hope of help!