microsoft / botframework-sdk

Bot Framework provides the most comprehensive experience for building conversation applications.
MIT License
7.48k stars 2.44k forks source link

[Node.js SDK, Dialogs] Resumed dialog in last step automatically end all parent dialogs #3974

Closed sfratini closed 6 years ago

sfratini commented 6 years ago

Bot Info

Issue Description

I am trying to understand the concept or flow of what happens in the last step. I have a waterfall dialog that in the last step, begins a new dialog that request the user to confirm their choices. They can choose to cancel, confirm or modify. Confirm and cancel works fine.

In order to modify, I begin a new dialog, ask the user what they want to modify and then I want to show the confirmation promt again. The problem is that the whole stack seems to be ending all the dialogs in a waterfall. I understand this might be related to the fact I am starting a new dialog in my last step of the dialog, however I don't see how to avoid this.

Code Example

You can see in this log that as soon as I end the child dialog, the two parents also end:

1|Chatbot- | ..requestModify - Session.endDialogWithResult()
1|Chatbot- | .requestConfirm - Session.endDialogWithResult()
1|Chatbot- | request - Session.endDialogWithResult()

Expected Behavior

I would like to receive the result/information from the child dialog in the parent, so I can decide what to do before ending the dialog.

Code Examples

This is my requestConfirm dialog. When I click on the modify action callback, I have a regex recognizer that catches this and begins a new dialog. This is all a one step dialog because I found it easier to handle.

 bot.dialog("requestConfirm", [
        function(session, args, next){

            console.log('Confirm Stack: ' + JSON.stringify(session.dialogStack()));
            console.log('Confirm Message: ' + JSON.stringify(session.message));
            console.log('Confirm Args: ' + JSON.stringify(args));

            if (session.message.text == 'confirmar'){

                session.sendTyping();

                let user = session.privateConversationData.profile.id;
                let serv = session.privateConversationData.service.id;
                let staff = session.privateConversationData.staff.id;
                let loc = session.privateConversationData.location.id;
                let time = session.privateConversationData.time;

                self.client.book(user, loc, serv, staff, time).then((booking) => {

                    delete session.message.text;
                    delete session.message.value;

                    let text = util.format(session.localizer.gettext(session.preferredLocale(),"request_confirmed_info"),
                    session.privateConversationData.service.name,
                    session.privateConversationData.staff.name,
                    moment(session.privateConversationData.time).format('LLLL'),
                    session.privateConversationData.location.address
                    );

                    let cards = [];
                    let card = new builder.HeroCard(session)
                    .title("request_confirmed")
                    .text(text)
                    cards.push(card);
                    var msg = new builder.Message(session);
                    msg.attachments(cards);
                    session.send(msg)
                    //session.clearDialogStack();
                    cleanupChoices(session);
                    session.endDialogWithResult({confirmed: true});
                    return;

                })
                .catch(error => {
                    session.send("request_confirmed_error");
                });

            } else if (session.message.text == 'olvidar'){
                cleanupChoices(session);
                session.send("request_not_confirmed");
                session.endDialogWithResult({notconfirmed: true});
                return;
            } else {

                if (typeof session.message.text == 'undefined')
                    session.send('request_confirm_prompt');            
                else                 
                    session.send('request_confirm_not_understood');

                let title = session.localizer.gettext(session.preferredLocale(),"appointment_mine");

                let text = util.format(session.localizer.gettext(session.preferredLocale(),"request_confirm_prompt_text"),
                session.privateConversationData.profile.name,        
                session.privateConversationData.service.name,
                session.privateConversationData.location.title,        
                session.privateConversationData.staff.name,
                moment(session.privateConversationData.time).format('LLLL'),
                );

                let card = new builder.HeroCard(session)
                .title(title)
                .text(text)
                .buttons([
                    builder.CardAction.imBack(session, 'confirmar', "appointment_confirm"), 
                    builder.CardAction.imBack(session, 'modificar', "appointment_modify"),        
                    builder.CardAction.imBack(session, 'olvidar', "appointment_cancel")            
                ]);

                var msg = new builder.Message(session);
                let cards = []; cards.push(card);
                msg.attachments(cards);
                session.send(msg);

            }

        }
    ]);
sfratini commented 6 years ago

Well, I "solved" it by adding a second step in the requestConfim dialog and ending the child dialogs with {success:true}. This is my second step:

function(session, results){
            if (results.success){
                session.replaceDialog("requestConfirm");
            } else {
                session.endDialogWithResult({error: true});
            }
        }

It seems an overkill, having a step just for this but so far it works. I'd love some feedback about the solution tho. Thanks

torjussa commented 6 years ago

Was experiencing the same problem as @sfratini as it ended the entire dialog stack when ending child dialog. Did a similar workaround by adding an extra empty waterfall step at the end of the parent dialogue.

Did not work:

parent{
    beginDialog('child')
}
child{
    endDialog()
}

Works:

parent[
 {
    beginDialog('child')
 },
    ()=>{}
]
child{
    endDialog()
}

Pretty ugly fix.

EricDahlvang commented 6 years ago

It looks like you've found a valid workaround. v4 of the sdk handles this scenario better: https://github.com/Microsoft/BotBuilder-js


The Microsoft Bot Framework team prefers that how to questions be submitted on Stack Overflow. The official Bot Framework Github repo  is the preferred platform for submitting bug fixes and feature requests.