Вложенные диалоги: сообщение отправляется дважды

У меня есть бот, в котором я делаю что-то вроде этого:

1) Приходит новое действие (сообщение).

2) Я отправляю сообщение в RootDialog.

3) В зависимости от некоторой логики RootDialog может:

 a) Call a LuisDialog (handling natural language)

 b) Call a CustomDialog (handles some business logic).

Но когда пользовательское состояние сбрасывается и поток приводит к намерению внутри LuisDialog, он дважды вызывает метод намерения. Просто первое время состояние пустое, потом работает нормально.

Позвольте мне показать вам код:

Контроллер сообщений:

   public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
            try
            {
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            catch (HttpRequestException e)
            {
             ...
            }
     }

Корневой диалог:

 public class RootDialog : IDialog<object>
 {
    public async Task StartAsync(IDialogContext context)
    {
        await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> awaitable)
    {
        bool value = DoSomeCustomLogic();
        if (value) 
        {
            string message = DoSomething();
            await context.PostAsync(message);
        } else {
            bool value2 = DoSomeCustomLogic2();
            if (value2) 
            {
                var answerValidationDialog = new ValidateAnswerWithUserDialog();
                context.Call(answerValidationDialog, ValidateAnswerWithUserDialogCompletedCallback);
            } else {
                var luisDialog = new LuisDialog();
                await context.Forward(luisDialog,LuisDialogCompletedCallback, context.Activity, CancellationToken.None);
            }
        }
    }

Обратные вызовы выполняют только context.Done(true);

И у LuisDialog есть намерение, которое звучит так:

  [LuisIntent(LuisUtils.INTENT_MENU_SALUTE)]
    public async Task SaluteOrMenu(IDialogContext context, LuisResult result)
    {
        if (LuisUtils.IntentScoreIsHighEnough(result))
        {
            string userName = context.Activity.From.Name;
            ContextHelper helper = new ContextHelper(MessageReceived);
            await helper.AskUserToDoSomeInitialAction(context, saluteWord, userName);
            context.Done(true);
        }
        else
        {
            await None(context, result);
        }
    }

И, наконец, класс ContextHelper:

public class ContextHelper
{
    private Func<IDialogContext, IAwaitable<IMessageActivity>, Task> MessageReceived;

    public ContextHelper(Func<IDialogContext, IAwaitable<IMessageActivity>, Task> messageReceived)
    {
        MessageReceived = messageReceived;
    }

    public async Task AskUserToDoSomeInitialAction(IDialogContext context, string saluteWord, string userName)
    {
        var reply = context.MakeMessage();
        List<CardAction> buttons = BuildInitialOptionActions();
        List<CardImage> images = BuildInitialOptionImages();
        string initialText = $"Hi stranger!"
        var card = new HeroCard
        {
            Title = "Hello!"
            Text = initialText,
            Buttons = buttons,
            Images = images
        };
        reply.Attachments = new List<Attachment> { card.ToAttachment() };
        await context.PostAsync(reply);
        context.Wait(AfterUserChoseOptionInSalute);
    }

    private async Task AfterUserChoseOptionInSalute(IDialogContext context, IAwaitable<IMessageActivity> result)
    {
        await ReDispatchMessageReceivedToDialog(context);
    }

    private async Task ReDispatchMessageReceivedToDialog(IDialogContext context)
    {
        await MessageReceived(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
    }

}

Намерение SaluteOrMenu вызывается дважды (только при первом взаимодействии с ботом или при удалении состояния. После отладки я увидел, что после выполнения context.Wait(AfterUserChoseOptionInSalute); бот вызывает эту функцию (вместо ожидания события для ее вызова)

Любые идеи?

Заранее спасибо.


person Gabriel Piffaretti    schedule 07.04.2017    source источник
comment
Можете ли вы создать небольшой образец, где это можно было бы воспроизвести?   -  person Ezequiel Jadib    schedule 08.04.2017
comment
0) Как я сказал в посте, это происходит, когда состояние бота пусто (первый раз, когда пользователь использует его или когда я делаю /deleteprofile). 1) пользователь набирает привет. 2) Логика, о которой я сказал, оценивается (в моем случае DoSomeLogic() вызывает службу QnA и в зависимости от оценки отвечает на вопрос или нет). Поскольку «привет» не является допустимым вопросом, он перенаправляет сообщение в LuisDialog. 3) Салют Интент вызывается и показывает маленькую карту героя. он заканчивается контекстом. Подождите (AfterUserChoseOptionInSalute); 4) Сам по себе AfterUserChoseOption вызывается и повторно отправляет действие (вызывая другой вызов).   -  person Gabriel Piffaretti    schedule 08.04.2017
comment
Да, я читал сообщение, но если вы не хотите, чтобы кто-то закодировал точно такой же сценарий, чтобы попытаться воспроизвести проблему, это будет сложно... диалоги переадресации/вызова работают, как и ожидалось; так что проблема в вашем коде.   -  person Ezequiel Jadib    schedule 08.04.2017
comment
Другое дело context.Done(true); в методе намерения выполняется, но также выполняется ожидание. Итак, либо вы ждете, либо завершаете диалог; вы не можете делать и то, и другое. помощник контекста ожидает, поэтому вы должны удалить контекст. Готово‹true›   -  person Ezequiel Jadib    schedule 08.04.2017


Ответы (1)


Я нашел строку, которая была неправильной. Это было в первом диалоге (RootDialog):

public async Task StartAsync(IDialogContext context)
{
   await MessageReceivedAsync(context, Awaitable.FromItem(context.Activity.AsMessageActivity()));
}

Это строка, которая повторно отправляет сообщение с входящей активностью. У меня это было где-то в каком-то фрагменте кода, и (не знаю почему) я подумал, что было бы неплохо использовать его в StartAsync. Итак, из-за этого произошло два звонка.

Тупой меня.

Я просто изменил его на это и работал:

public async Task StartAsync(IDialogContext context)
{
    context.Wait(MessageReceivedAsync);
}
person Gabriel Piffaretti    schedule 09.04.2017