【问题标题】:Terminate all dialogs and exit conversation in MS Bot Framework when the user types "exit", "quit" etc当用户键入“exit”、“quit”等时,终止 MS Bot Framework 中的所有对话框并退出对话
【发布时间】:2016-12-31 00:06:03
【问题描述】:

我不知道如何在 MS Bot Framework 中做一件非常简单的事情:允许用户中断任何对话,离开当前对话框并通过键入“quit”、“exit”返回主菜单”或“重新开始”。

这是我的主要对话的设置方式:

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        try
        {
            if (activity.Type == ActivityTypes.Message)
            {
                UserActivityLogger.LogUserBehaviour(activity);

                if (activity.Text.ToLower() == "start over")
                {
                    //Do something here, but I don't have the IDialogContext here!
                }
                BotUtils.SendTyping(activity); //send "typing" indicator upon each message received
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            else
            {
                HandleSystemMessage(activity);
            }
        }

我知道如何使用context.Done&lt;DialogType&gt;(this); 终止对话,但在此方法中,我无法访问 IDialogContext 对象,因此无法调用.Done()

除了在所有对话框的每个步骤中添加检查之外,还有其他方法可以在用户键入特定消息时终止整个对话框堆栈吗?

发布赏金:

我需要一种方法来终止所有 IDialogs,而无需使用我在此处发布的骇人听闻的 hack(这会删除我需要的所有用户数据,例如用户设置和首选项)。

基本上,当用户键入“退出”或“退出”时,我需要退出当前正在进行的 IDialog 并返回到新状态,就好像用户刚刚发起了对话一样。

我需要能够从MessageController.cs, 执行此操作,我仍然无法访问IDialogContext。我似乎拥有的唯一有用数据是Activity 对象。如果有人指出其他方法可以做到这一点,我会很高兴。

解决此问题的另一种方法是在机器人的其他位置而不是 Post 方法中找到其他方法来检查“exit”和“quit”关键字。

但这不应该是在IDialog 的每一步都进行的检查,因为那是太多的代码,而且并非总是可能的(当使用PromptDialog 时,我无法访问用户键入)。

我没有探索的两种可能的方式:

  • 不要终止所有当前的IDialogs,而是开始一个新的对话 与用户(新ConversationId
  • 获取IDialogStack 对象并使用它来管理对话框堆栈。

Microsoft 文档对此对象保持沉默,因此我不知道如何获取它。我不在机器人中的任何地方使用允许.Switch()Chain 对象,但如果你认为它可以被重写以使用它,它也可以是解决这个问题的方法之一。但是,我还没有找到如何在各种类型的对话框(FormFlow 和普通的IDialog)之间进行分支,这些对话框又调用自己的子对话框等。

【问题讨论】:

    标签: c# bots botframework


    【解决方案1】:

    这是一个非常丑陋的 hack,它有效。它基本上会删除所有用户数据(您可能实际需要),这会导致对话重新开始。

    如果有人知道更好的方法,不删除用户数据,请分享。

        public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
        {
            try
            {
                if (activity.Type == ActivityTypes.Message)
                {
                    //if the user types certain messages, quit all dialogs and start over
                    string msg = activity.Text.ToLower().Trim();
                    if (msg == "start over" || msg == "exit" || msg == "quit" || msg == "done" || msg =="start again" || msg == "restart" || msg == "leave" || msg == "reset")
                    {
                        //This is where the conversation gets reset!
                        activity.GetStateClient().BotState.DeleteStateForUser(activity.ChannelId, activity.From.Id);
                    }
    
                    //and even if we reset everything, show the welcome message again
                    BotUtils.SendTyping(activity); //send "typing" indicator upon each message received
                    await Conversation.SendAsync(activity, () => new RootDialog());
                }
                else
                {
                    HandleSystemMessage(activity);
                }
            }
    

    【讨论】:

      【解决方案2】:

      问题分解

      根据我对您问题的理解,您想要实现的是在不完全破坏机器人状态的情况下重置对话框堆栈


      事实(来自我从 github 存储库中读取的内容)

      1. 框架如何保存对话框栈如下:

      BotDataStore > BotData > DialogStack

      1. BotFramework 使用 AutoFac 作为 DI 容器
      2. DialogModule 是他们用于对话框组件的 Autofac 模块

      怎么做

      从上面了解 FACTS,我的解决方案将是

      1. 注册依赖项以便我们可以在控制器中使用:

      // in Global.asax.cs
      var builder = new ContainerBuilder();
      builder.RegisterModule(new DialogModule());
      builder.RegisterModule(new ReflectionSurrogateModule());
      builder.RegisterModule(new DialogModule_MakeRoot());
      
      var config = GlobalConfiguration.Configuration;
      builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
      builder.RegisterWebApiFilterProvider(config);
      var container = builder.Build();
      config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
      
      1. 获取 Autofac Container(随意在您的代码中任意放置您觉得合适的地方)

      private static ILifetimeScope Container
      {
          get
          {
              var config = GlobalConfiguration.Configuration;
              var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
              return resolver.Container;
          }
      }
      
      1. 作用域中加载BotData
      2. 加载 DialogStack
      3. 重置 DialogStack
      4. 将新的 BotData 推回 BotDataStore

      using (var scope = DialogModule.BeginLifetimeScope(Container, activity))
      {
          var botData = scope.Resolve<IBotData>();
          await botData.LoadAsync(default(CancellationToken));
          var stack = scope.Resolve<IDialogStack>();
          stack.Reset();
          await botData.FlushAsync(default(CancellationToken));
      }
      

      希望对你有帮助。


      更新 1(2016 年 8 月 27 日)

      感谢@ejadib 指出,Container 已经在对话类中公开了。

      我们可以去掉上面答案中的第 2 步,最后代码会是这样的

      using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
      {
          var botData = scope.Resolve<IBotData>();
          await botData.LoadAsync(default(CancellationToken));
          var stack = scope.Resolve<IDialogStack>();
          stack.Reset();
          await botData.FlushAsync(default(CancellationToken));
      }
      

      【讨论】:

      • 这里的小事:你不需要做所有的注册。容器已经在 Conversation 类中公开,因此您可以使用 => using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) { ... }
      • @ejadib:谢谢指出,我会更新答案
      【解决方案3】:

      我知道这有点老了,但我遇到了同样的问题,并且发布的解决方案不再是最好的方法。

      我不确定这是什么版本,但在 3.8.1 上你可以注册IScorable 服务,可以在对话框中的任何位置触发。

      有一个示例代码说明了它是如何工作的,它确实有一个“取消”全局命令处理程序:

      https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-GlobalMessageHandlers

      部分代码如下所示:

      protected override async Task PostAsync(IActivity item, string state, CancellationToken token)
      {
          this.task.Reset();
      }
      

      【讨论】:

      • 您的链接已损坏
      • 我有一段时间没有使用 Bot Framework,所以我不能说这在 2020 年仍然有意义。无论如何,你可以在 github.com/microsoft/BotBuilder-Samples/tree/…找到我在 2017 年提到的代码。 /跨度>
      【解决方案4】:

      对其他人有用的附加代码:

      private async Task _reset(Activity activity)
          {
              await activity.GetStateClient().BotState
                  .DeleteStateForUserWithHttpMessagesAsync(activity.ChannelId, activity.From.Id);
      
              var client = new ConnectorClient(new Uri(activity.ServiceUrl));
              var clearMsg = activity.CreateReply();
              clearMsg.Text = $"Reseting everything for conversation: {activity.Conversation.Id}";
              await client.Conversations.SendToConversationAsync(clearMsg);
          }
      

      此代码由用户 mmulhearn 发布在此处:https://github.com/Microsoft/BotBuilder/issues/101#issuecomment-316170517

      【讨论】:

        猜你喜欢
        • 2017-04-27
        • 1970-01-01
        • 2016-09-04
        • 1970-01-01
        • 2018-10-19
        • 2021-01-27
        • 2016-11-08
        • 1970-01-01
        • 2016-10-30
        相关资源
        最近更新 更多