-1

I have a need to reset or kill (using context.Done) the current IDialogContext instance and create a new instance and use it in a different function. How can I create such an instance, IDialogContext being an interface. Is it possible?

This is the function from which am tried to call a function "selfredirect"

private async Task ApprovalConfirm(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
xxxticket xxxticketobject = new xxxticket();
if (message.Text.Contains("yes"))
{
    xxxticketobject = context.ConversationData.Get<xxxticket>("xxxcard");
    if (xxxticketobject.action == "Approve")
        await context.PostAsync("Accepted");
    else
        await context.PostAsync("Rejected");
    context.ConversationData.RemoveValue("xxxfinalcard");
    context.Reset();// *This is what I want to do...clear context and then call the following function*
    await SelfRedirect(context, context.UserData.Get<string>("CurrentQuery"), "Approval");
}
else if (message.Text == "no")
{
    await context.PostAsync("Thank you");
    context.Done(context);
}
else
    await SelfRedirect(context, context.UserData.Get<string>("CurrentQuery"), null);

}

The selfredirect function

  public static async Task SelfRedirect(IDialogContext context, string msg, string intent=null)
    {
        try
        {
            DialogLuis LUISDialog = new DialogLuis();
            IMessageActivity message = context.MakeMessage();
            message.Text = msg;
            var tasks = LUISDialog.services.Select(s => s.QueryAsync(message.Text, context.CancellationToken)).ToArray();
            var results = await Task.WhenAll(tasks);
            var winners = from result in results.Select((value, index) => new { value, index })
                          let resultWinner = LUISDialog.BestIntentFrom(result.value)
                          where resultWinner != null
                          select new LuisServiceResult(result.value, resultWinner, LUISDialog.services[result.index]);
            if (intent != null)
            {
                winners = from result in results.Select((value, index) => new { value, index })
                          let resultWinner = new IntentRecommendation(intent)
                          where resultWinner != null
                          select new LuisServiceResult(result.value, resultWinner, LUISDialog.services[result.index]);
            }
            var winner = LUISDialog.BestResultFrom(winners);
            if (winner == null)
            {
                throw new InvalidOperationException("No winning intent selected from Luis results.");
            }
            if (winner.Result.Dialog?.Status == DialogResponse.DialogStatus.Question)
            {
                var childDialog = await LUISDialog.MakeLuisActionDialog(winner.LuisService,
                                                             winner.Result.Dialog.ContextId,
                                                             winner.Result.Dialog.Prompt);
                context.Call(childDialog, LUISDialog.LuisActionDialogFinished);
            }
            else
            {
                IAwaitable<IMessageActivity> item = null;
                await LUISDialog.DispatchToIntentHandler(context, item, winner.BestIntent, winner.Result);
            }
        }
        catch (Exception ex) { CommonMethods.LogTime("", ex, context); }
    }

But I cant use the redirect function after reset as the stack is empty. Please help

Let me elaborate why I had to use the selfredirect function instead of context.wait(MessageReceived) through this small function

private async Task ApprovalConfirm(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var message = await result;
        if (message.Text == "no")
        {
            await context.PostAsync("Thank you");
            context.Done(context);
        }
        else if (message.Text == "yes")
        {
            await context.PostAsync("yes");
            context.Done(context);
        }
        else
        {
            context.Wait(MessageReceived);
        }
    }

My use case is like, if the user types yes then bot should return yes, if user types no then bot should return thank you. and if the user types "Hi" the bot should find the answer for Hi through Luis which will hit on a new Greeting intent and return Hi user.

Here if I user context.Wait(MessageReceived) then the bot would simply wait for the next query from user

The structure of dialogs!

  [LuisIntent("Approval")]
    public async Task Approval(IDialogContext context, LuisResult result)
    {
        context.ConversationData.SetValue("currentIntent", "Approval");
        .
        .
        .
        PromptDialog.Choice<string>(context, ApprovalList, ...);
    }


private async Task ApprovalList(IDialogContext context, IAwaitable<string> result)
    {
        try
        {
            var request_type = await result;
            .
            .
            context.Wait(ApprovalResponse);
            }
        }
        catch (Exception ex)
        {
            CommonMethods.LogTime("ApprovalList", ex, context);
        }
    }
 private async Task ApprovalResponse(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var message = await result;
       .
       .
       .
            context.Wait(AfterApprovalComment);
        }
        catch (Exception ex)
        {
            CommonMethods.LogTime("ApprovalResponse", ex, context);
            await context.PostAsync("out of approvals...");
            context.Done(context);
        }
    }


private async Task ApprovalConfirm(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        var message = await result;
       .
       .
       .
    await SelfRedirect(context, context.UserData.Get<string>("CurrentQuery"), "Approval");
    }
Gp_1993
  • 89
  • 11
  • I want the instance created inside my current function,. because I am handling multiple dialogs and in the end of it, I need to redirect it to a different function which expects a Idialogcontext instance as well – Gp_1993 Apr 11 '17 at 09:48
  • Please show your code, otherwise it's really hard to understand what you are trying to do. – Ezequiel Jadib Apr 11 '17 at 10:26
  • @EzequielJadib Hi I have added the code..:) – Gp_1993 Apr 11 '17 at 12:00
  • Why are you trying to kill the context? And why you are not using a LuisDialog instead of copying the code in your method? – Ezequiel Jadib Apr 11 '17 at 13:11
  • Thats what i wanted to do. I want to redirect to another intent. Suppose there are 2 Intents A and B. While inside intent A at one condition i want to trigger another Intent. How to do that?. Also with present approach when i redirect to another intent internally new dialog is been pushed into the stack. Because of this when the present dialog exit it triggers the previous dialog which is not expected. – Gp_1993 Apr 12 '17 at 06:17
  • Make sure to update your question with all these details. So it seems you have a LuisDialog, with 2 intents. The message hit Intent A but you want to call Intent B (with the same message or with a different one?) – Ezequiel Jadib Apr 12 '17 at 10:02
  • with the same message :) – Gp_1993 Apr 12 '17 at 10:24
  • Mmmm.. something is not making sense here... someone sends a message, that hits your luis dialog, luis detect that it's intent A, but you want to call intent B without going to luis, because luis already detected intent B... so the question is do you want to call just a new method? and if not, and you want to go to intent B, why don't you just train luis to return intent B instead of intent A. It's not clear what you are trying to do – Ezequiel Jadib Apr 12 '17 at 13:11
  • I cant just train the luis to hit intent B.. because i want the intent A function itself to run, but when a particular condition inside intent A is met, I want to call intent B manually otherwise I want the intent A function to run – Gp_1993 Apr 13 '17 at 06:59
  • My mistake!! Its a different message – Gp_1993 Apr 13 '17 at 07:14
  • Ok. But the question is, do you want that new message to go back to LUIS or do you want to call an specific method? I assume is the first one. – Ezequiel Jadib Apr 13 '17 at 10:09
  • both are required. one where luis gives the method and the one where i give the method – Gp_1993 Apr 17 '17 at 09:02
  • I hope you are helping me with the reset thing itself :) – Gp_1993 Apr 17 '17 at 09:03
  • I'm still not following what you are trying to and why the reset is required. From intent A I would just call the MessageReceived method of the LuisDialog you are and that should make the trick. – Ezequiel Jadib Apr 17 '17 at 09:47
  • Lets just say, there are two intents A and B and there are two scenarios. One where, when I am inside an intent, I want to trigger a new intent through luis.(for this, I used the logic behind MessageReceived) Another one where, after entering intent A and satisfying a particular condition, I want to trigger intent B myself with the text I give(without going to luis). – Gp_1993 Apr 17 '17 at 10:10
  • Regarding the reset thing, I want to reset the stack of dialogs. and then use message received function logic or whatever to call a new intent manually without going to luis......But after reseting the stack this is not possible i think – Gp_1993 Apr 17 '17 at 10:25
  • You are not saying why you want to reset the stack. For the first scenario you need from call the MessageReceived method. You don't need to create a new method and copy the logic as in your SelfRedirect method. That's not required. For the second scenario just call the method with the LuisResult as null – Ezequiel Jadib Apr 17 '17 at 11:06
  • Please see the example I quoted in the question now on why I am not able to use context.wait(MessageReceived). :) – Gp_1993 Apr 17 '17 at 11:41
  • Please add the structure of your dialogs/ how many dialogs you have, how then are connected. – Ezequiel Jadib Apr 17 '17 at 11:43
  • I have added the dialogs as well...:) The reason why I want to reset the context is : suppose an exception occurs in between any of this dialog then the control will resume from that dialog only. – Gp_1993 Apr 18 '17 at 07:16
  • It's a single dialog with a prompt.. Ok. Who call ApprovalConfirm? In any case, you didn't get my comment of the MessageReceived. I didn't mention anything about doing context.Wait. I'm saying that you need to call the method base.MessageReceived(...,...) – Ezequiel Jadib Apr 18 '17 at 08:58
  • It is called from the function AfterApprovalComment as context.Wait(ApprovalConfirm)..I just skipped that function as it is similar – Gp_1993 Apr 18 '17 at 11:51
  • Yes, now I see it. Delete the SelfRedirect method and just call the MessageReceived one. You don't need to reset the stack – Ezequiel Jadib Apr 18 '17 at 11:56
  • I am sorry, I am still a newbie, Could you please tell me how you call it??? base.MessageReceived??????? – Gp_1993 Apr 18 '17 at 12:11
  • yes it's a method like any other...base because it's on the base class your are inheriting from (LuisDialog) – Ezequiel Jadib Apr 18 '17 at 14:09
  • @EzequielJadib Thanks a lotttt :) :) I was always calling the messagereceived function like context.wait(messagereceived)...Thanks man (y) – Gp_1993 Apr 21 '17 at 07:58
  • I will add the answer if you don't mind... – Ezequiel Jadib Apr 21 '17 at 09:53
  • yea sure add the answer(y) – Gp_1993 Apr 21 '17 at 13:02
  • added :) please set as answered/upvote – Ezequiel Jadib Apr 21 '17 at 14:24

1 Answers1

0

Based on the discussion we have in the comments to understand more your requirement, it seems that you don't need to reset the stack.

Instead, what you would need to do is leverage the MessageReceived method from the LuisDialog you are inheriting from. By doing that, you can directly call the method and force it to go to LUIS to resolve the new intent, without the need of a new input from the user.

Calling that method is exactly the same as calling any other one. The only caveat is that since the MessageReceived method is expecting an IAwaitable<IMessageActivity> you will have to create that. The good news is that is as easy as using Awaitable.FromItem

Ezequiel Jadib
  • 14,767
  • 2
  • 38
  • 43