【问题标题】:Microsoft Bot Framework Proactive Message which continues the current dialog继续当前对话框的 Microsoft Bot Framework 主动消息
【发布时间】:2019-07-20 05:49:00
【问题描述】:

微软bot框架中有主动消息的概念->https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp

我在我的解决方案中使用多个不同的对话框,我将一些数据存储在数据库中,这些数据每次都会加载。根据来自数据库的数据,状态对象被修改,并且根据该数据,对话框继续。

在我的情况下,用户 A 启动对话,系统响应“我将你放入队列”然后一段时间后 B 启动他的对话并询问他是否应该与 A 配对。用户 B 确认后,对话来自用户 A 应该继续。

我可以给他写一条如下所示的简单消息,但我不知道如何简单地为匹配的用户强制进行新的“转向”,以便对话继续。

public class BasicBot : IBot
{
    // some properties

    public BasicBot(CustomBotState botState, BotServices services, UserState userState, ConversationState conversationState, ILoggerFactory loggerFactory, EndpointService endpointService)
    {
        // set some properties

        _conversationReferenceAccessor = _botState.CreateProperty<Dictionary<string, ConversationReference>>(nameof(MatchConversationReference));
        _dialogStateAccessor = _conversationState.CreateProperty<DialogState>(nameof(DialogState));
        _matchStateAccessor = _userState.CreateProperty<MatchState>(nameof(MatchState));

        var appId = string.IsNullOrWhiteSpace(endpointService.AppId) ? "1" : endpointService.AppId;

        Dialogs = new DialogSet(_dialogStateAccessor);
        Dialogs.Add(new MatchDialog(_matchStateAccessor, loggerFactory, services, appId, _conversationReferenceAccessor));
    }        

    private DialogSet Dialogs { get; set; }

    public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        var dialogContext = await Dialogs.CreateContextAsync(turnContext);

        if (turnContext.Activity.Type == ActivityTypes.Message)
        {
            var dialogResult = await dialogContext.ContinueDialogAsync();

            if (!dialogContext.Context.Responded)
            {
                var match = LoadMatchFromDatabase();
                await dialogContext.BeginDialogAsync(nameof(MatchDialog), match);
            }
        }

        // save the state of conversation, user, bot
    }
}
public class MatchDialog : ComponentDialog
{
    // some properties

    public MatchDialog(IStatePropertyAccessor<MatchState> stateAccessor, ILoggerFactory loggerFactory, BotServices services, string appId, IStatePropertyAccessor<Dictionary<string, ConversationReference>> _matchConversationPropertyAccessor)
            : base(nameof(MatchDialog))
    {
        // set some properties

        var waterfallSteps = new WaterfallStep[]
        {
                InitializeStateStepAsync,
                WaitForAnswer,
        };

        AddDialog(new WaterfallDialog(nameof(MatchDialog), waterfallSteps));
    }

    private async Task<DialogTurnResult> WaitForAnswer(WaterfallStepContext steps, CancellationToken cancellationToken)
    {
        var otherUser = await GetOtherUser(steps);
        var conversations = await GetMatchConversion(steps.Context);

        if (conversations.ContainsKey(otherUser.Identifier))
        {
            await steps.Context.Adapter.ContinueConversationAsync(AppId, conversations[otherUser.Identifier],
                   async (turnContext, token) =>
                   {
                       await turnContext.SendActivityAsync($"Found someone for you");

                   },
                   cancellationToken);
        }
    }

    private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        var state = await StateAccessor.GetAsync(stepContext.Context, () => null);
        var match = stepContext.Options as Match;

        if (state == null)
        {
            await StateAccessor.SetAsync(stepContext.Context, new MatchState() { Match = match });
        }
        else if (state.Match == null || match.Id != state.Match.Id)
        {
            state.Match = match;
        }

        return await stepContext.NextAsync();
    }

}

}

【问题讨论】:

  • 为了澄清,UserA 启动一个对话,并在此对话期间进入队列。当找到 UserB 并且可以与 UserA 配对时,会通知 UserA 找到 UserB。然后,您希望与 UserA 的对话继续。对吗?
  • 是的,正确。在我的情况下,用户 A 开始对话,系统以“我将你放入队列”作为响应,然后一段时间后 B 开始他的对话并询问他是否应该与 A 配对。用户 B 确认后,来自用户的对话A 应该继续。 -> 我用这个简短的描述更新了描述

标签: c# botframework


【解决方案1】:

有几种方法可以做到这一点,这实际上取决于您的代码。基本上,在您发送已找到用户 B 的主动消息的同一位置,您需要调用dc.ContinueDialogAsync()dc.RepromptDialogAsync(),如果适用。

话虽如此,我认为最好的选择是拆分您的 Dialog。一个对话框让用户 A 进入队列。一旦进入,他们就不再在对话中。一旦找到用户 B,它就会向用户 A 发送新的 Dialog。

我或多或少通过Sample 16. Proactive Messages 做到了这一点:

  1. 创建在找到用户 B 后调用的对话框
  2. CreateCallback()(此示例主动发送消息的位置)下,我将以下代码添加到该方法的末尾(出于某种原因,它不想格式化为代码):

await turnContext.SendActivityAsync($"Job {jobInfo.TimeStamp} 完成。");

var dc = await Dialogs.CreateContextAsync(turnContext);

等待 dc.BeginDialogAsync(nameof(MyDialog));

注意:为了测试,我在用户 A “运行”作业后为他们创建了一个对话框。对话框一直在那里,直到用户 B 完成工作。随即为用户 A 启动了一个新对话框。

对你来说,这可能看起来像:

//sample how I write something into the other conversation
var conversations = await GetMatchConversion(steps.Context);
if (conversations.ContainsKey(otherUser.Identifier))
{
    await steps.Context.Adapter.ContinueConversationAsync(AppId, conversations[otherUser.Identifier],
           async (turnContext, token) =>
           {
               // Send the user a proactive confirmation message.
               await turnContext.SendActivityAsync($"{currentUser.Display()} I found a matching user...");
               var dc = await Dialogs.CreateContextAsync(turnContext);
               await dc.BeginDialogAsync(nameof(UserFoundDialog));
           },
           cancellationToken);
}

【讨论】:

  • 我已经有两个单独的对话框,一个 QueueDialog 和一个 MatchDialog。我一直认为我需要 UserState 对象才能让该用户访问某些状态属性,但是由于您始终将 turnContext 作为参数,所以一切都应该没问题!我会试试看...谢谢你的时间
  • @mdrichardson 您从哪里获得主动回拨的对话?我需要在我的主动式机器人中实施此解决方案,请帮助。
猜你喜欢
  • 1970-01-01
  • 2022-11-14
  • 2018-02-26
  • 1970-01-01
  • 1970-01-01
  • 2017-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多