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 IDialog
s 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:
IDialog
s actuels, démarrez une nouvelle conversation .__ avec l'utilisateur (nouveau ConversationId
) 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.
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.
BotDataStore> BotData> DialogStack
Connaissant FAITS d'en haut, ma solution sera
// 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);
private static ILifetimeScope Container
{
get
{
var config = GlobalConfiguration.Configuration;
var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
return resolver.Container;
}
}
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.
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));
}
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);
}
}
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();
}
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