web-dev-qa-db-fra.com

Terminez tous les dialogues et quittez la conversation dans MS Bot Framework lorsque l'utilisateur tape "exit", "quit", etc.

Je n'arrive pas à comprendre comment faire une chose très simple dans MS Bot Framework: permettre à l'utilisateur de rompre toute conversation, de quitter les dialogues actuels et de revenir au menu principal en tapant "quit", "exit" ou " recommencer". 

Voici comment ma conversation principale est organisée:

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

Je sais comment terminer un dialogue avec context.Done<DialogType>(this);, mais avec cette méthode, je n'ai pas accès à l'objet IDialogContext, je ne peux donc pas appeler .Done()

Existe-t-il un autre moyen de mettre fin à la pile de boîtes de dialogue entière lorsque l'utilisateur tape un certain message, mis à part l'ajout d'une vérification à chaque étape de toutes les boîtes de dialogue?

Prime publiée:

J'ai besoin d'un moyen de mettre fin à tous les IDialogs sans utiliser le piratage scandaleux que j'ai posté ici (qui supprime toutes les données utilisateur dont j'ai besoin, par exemple les paramètres utilisateur et les préférences). 

En gros, lorsque l'utilisateur tape "quit" ou "exit", je dois quitter le nom IDialog actuellement en cours et revenir à l'état neuf, comme si l'utilisateur venait d'entamer une conversation. 

Je dois pouvoir faire cela à partir de MessageController.cs, où je n'ai toujours pas accès à IDialogContext. La seule donnée utile que je semble avoir là-bas est l'objet Activity. Je serai heureux si quelqu'un indique d'autres moyens de le faire. 

Une autre façon de procéder consiste à rechercher un autre moyen de vérifier les mots clés "exit" et "quit" à un autre endroit du bot, plutôt que dans la méthode Post.

Mais cela ne devrait pas être une vérification effectuée à chaque étape du IDialog, car c'est trop de code et même pas toujours possible (lorsque j'utilise PromptDialog, je n'ai pas accès au texte saisi par l'utilisateur). 

Deux manières possibles que je n'ai pas explorées:  

  • Au lieu de mettre fin à tous les IDialogs actuels, démarrez une nouvelle conversation .__ avec l'utilisateur (nouveau ConversationId
  • Obtenez l'objet IDialogStack et utilisez-le pour gérer la pile de dialogue.

Les documents Microsoft ne disent rien sur cet objet, donc je ne sais pas comment l'obtenir. Je n'utilise pas l'objet Chain qui autorise .Switch() n'importe où dans le bot, mais si vous pensez qu'il peut être réécrit pour l'utiliser, il peut être l'un des moyens de résoudre ce problème également. Cependant, je n'ai pas trouvé comment brancher différents types de dialogues (FormFlow et le nom ordinaire IDialog), qui à leur tour appellent leurs propres dialogues enfants, etc.

24
K48

Problème résolu

D'après ce que j'ai compris de votre question, vous voulez obtenir réinitialiser la pile de dialogue sans détruire complètement l'état du bot.


FAITS (d'après ce que j'ai lu dans le dépôt github)

  1. Comment le cadre enregistre la pile de dialogue est comme suit:

BotDataStore> BotData> DialogStack

  1. BotFramework utilise AutoFac comme conteneur DI
  2. DialogModule est leur module Autofac pour les composants de dialogue.

COMMENT FAIRE

Connaissant FAITS d'en haut, ma solution sera

  1. Enregistrer les dépendances afin que nous puissions utiliser dans notre contrôleur:

// 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. Récupère le conteneur autofac (n'hésitez pas à insérer n'importe où dans votre code avec lequel vous êtes à l'aise)

private static ILifetimeScope Container
{
    get
    {
        var config = GlobalConfiguration.Configuration;
        var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
        return resolver.Container;
    }
}
  1. Chargez le BotData dans le portée _
  2. Charger le DialogStack
  3. Réinitialiser le DialogStack
  4. Repoussez le nouveau BotData en arrière sur 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));
}

J'espère que ça aide.


MISE À JOUR 1 (27/08/2016)

Grâce à @ejadib, Container est déjà exposé dans la classe de conversation.

_ {Nous pouvons supprimer l'étape 2 de la réponse ci-dessus, le code ressemblera à la fin} _

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));
}
24
Kien Chu

Voici un bidule horriblement moche qui fonctionne. En gros, toutes les données utilisateur (dont vous pourriez avoir réellement besoin) sont supprimées, ce qui entraîne le redémarrage de la conversation.

Si quelqu'un connaît un meilleur moyen, sans supprimer les données de l'utilisateur, merci de le partager. 

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

Je sais que c'est un peu vieux, mais j'ai eu le même problème et les solutions affichées ne sont plus les meilleures approches.

Je ne sais pas depuis quelle version est disponible, mais sous 3.8.1, vous pouvez enregistrer des services IScorable pouvant être déclenchés n’importe où dans le dialogue.

Il y a un exemple de code qui montre comment cela fonctionne et qui possède un gestionnaire de commandes global "cancel":

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

Une partie du code ressemblera à ceci:

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

Code supplémentaire qui a fonctionné pour quelqu'un d'autre:

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

Ce code est posté par l'utilisateur mmulhearn ici: https://github.com/Microsoft/BotBuilder/issues/101#issuecomment-316170517

0
K48