microsoft framework emulator docs development bot c# bots botframework

c# - emulator - Termine todos los cuadros de diálogo y salga de la conversación en MS Bot Framework cuando el usuario escriba "salir", "salir", etc.



microsoft bot framework c# (4)

Desglose de problemas

Por lo que entiendo de tu pregunta, lo que quieres lograr es restablecer la pila de diálogos sin destruir completamente el estado del bot .

HECHOS (de lo que leí del repositorio github)

  1. La forma en que el framework guarda la pila de diálogos es la siguiente:

BotDataStore> BotData> DialogStack

  1. BotFramework está utilizando AutoFac como un contenedor DI
  2. DialogModule es su módulo Autofac para componentes de diálogo

CÓMO HACER

Conociendo HECHOS de arriba, mi solución será

  1. Registre las dependencias para que podamos utilizarlas en nuestro controlador:

// 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. Obtenga el contenedor Autofac (siéntase libre de poner en cualquier lugar de su código con el que se sienta cómodo)

private static ILifetimeScope Container { get { var config = GlobalConfiguration.Configuration; var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver; return resolver.Container; } }

  1. Cargue los BotData en el alcance
  2. Cargar el dialogstack
  3. Restablecer el DialogStack
  4. Empuje el nuevo BotData de nuevo a 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)); }

Espero eso ayude.

ACTUALIZACIÓN 1 (27/08/2016)

Gracias a @ejadib por señalar, Container ya está siendo expuesto en la clase de conversación.

Podemos eliminar el paso 2 en la respuesta anterior, al final, el código se verá como

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)); }

No puedo descubrir cómo hacer una cosa muy simple en MS Bot Framework: permitir que el usuario interrumpa cualquier conversación, deje los cuadros de diálogo actuales y regrese al menú principal escribiendo "salir", "salir" o " comenzar de nuevo".

Esta es la forma en que se configura mi conversación principal:

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); } }

Sé cómo terminar un diálogo con context.Done<DialogType>(this); , pero en este método, no tengo acceso al objeto IDialogContext, por lo que no puedo llamar a .Done() .

¿Hay alguna otra manera de terminar toda la pila de diálogos cuando el usuario escribe un mensaje determinado, aparte de agregar una comprobación de eso en cada paso de todos los diálogos?

Recompensa publicada:

Necesito una forma de terminar todos los IDialog s sin usar el truco indignante que he publicado aquí (lo que elimina todos los datos de usuario que necesito, por ejemplo, la configuración y las preferencias del usuario).

Básicamente, cuando el usuario escribe "salir" o "salir", necesito salir de cualquier IDialog esté actualmente en curso y volver al estado actual, como si el usuario hubiera iniciado una conversación.

Necesito poder hacer esto desde MessageController.cs, donde todavía no tengo acceso a IDialogContext . Los únicos datos útiles que parece tener allí es el objeto Activity . Seré feliz si alguien señala otras formas de hacerlo.

Otra forma de abordar esto es encontrar otra forma de verificar las palabras clave "salir" y "salir" en algún otro lugar del bot, en lugar de hacerlo en el método de publicación.

Pero no debería ser una comprobación que se realiza en cada paso del IDialog , porque es demasiado código y no siempre es posible (cuando PromptDialog , no tengo acceso al texto que el usuario escribió).

Dos formas posibles que no exploré:

  • En lugar de terminar todos los IDialog actuales, inicie una nueva conversación con el usuario (nuevo IDialog ConversationId )
  • Obtenga el objeto IDialogStack y haga algo con él para administrar la pila de diálogo.

Los documentos de Microsoft guardan silencio sobre este objeto, así que no tengo idea de cómo obtenerlo. No uso el objeto Chain que permite .Switch() en cualquier parte del bot, pero si crees que se puede reescribir para usarlo, puede ser una de las maneras de resolver esto también. Sin embargo, no he encontrado cómo hacer una ramificación entre varios tipos de diálogos ( FormFlow y el IDialog ordinario) que a su vez llaman a sus propios diálogos secundarios, etc.


Aquí hay un truco horriblemente feo que funciona. Básicamente, elimina todos los datos del usuario (que en realidad podría necesitar) y esto hace que la conversación se reinicie.

Si alguien conoce una forma mejor, sin eliminar los datos del usuario, por favor, comparta.

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); } }


Código adicional que funcionó para otra persona:

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); }

Este código lo publica el usuario mmulhearn aquí: https://github.com/Microsoft/BotBuilder/issues/101#issuecomment-316170517


Sé que esto es un poco viejo, pero tuve el mismo problema y las soluciones publicadas ya no son los mejores enfoques.

No estoy seguro desde qué versión está disponible, pero en 3.8.1 puede registrar servicios IScorable que pueden IScorable en cualquier parte del diálogo.

Hay un código de ejemplo que muestra cómo funciona, y tiene un controlador de comando global "cancelar":

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

Una parte del código se verá así:

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