2017-05-01 1 views
0

Ich habe hierarchische Menüstruktur, die ich durch den Bot und auf jeder Ebene präsentieren möchte, präsentieren 2 zusätzliche Optionen [Previous Menu] & [Exit]. [Previous Menu] löst die übergeordnete Ebene aus. Bei der Auswahl des Endknotens sind die letzten Ebenenoptionen erneut zu wiederholen.Microsoft BotFramework (Node SDK): Wie implementiert man ein rekursives hierarchisches Menü?

Also lassen Sie uns sagen, dass die Menüstruktur ist dies:

  • A
    • X
    • Y
      • P
      • Q
  • B
    • U
      • R
      • S
    • V

Was ich versuche, dies zu tun:

User : Menu 
Bot : [A] [B][Exit] 
User (selects): [A] 
Bot : [X] [Y] [Previous Menu] [Exit] 
User : [X] 
Bot : <Dialog for X> 
Bot : [X] [Y] [Previous Menu] [Exit] 
User : [Y] 
Bot : [P] [Q] [Previous Menu] [Exit] 
User : [Previous Menu] 
Bot : [X] [Y] [Previous Menu] [Exit] 
User : [Previous Menu] 
Bot : [A] [B][Exit] 
User [B] 
Bot : [U][V][Previous Menu] [Exit] 
User : [U] 
Bot : [R][S][Previous Menu] [Exit] 

... und so weiter.

Welches ist das bevorzugte Entwurfsmuster, um dieses Verhalten zu implementieren?

Ich habe eine Implementierung, aber das hängt davon ab, dass der Dialogstapel zu jedem Zeitpunkt nur einen Dialog im Stapel hat und ersetzt durch die Benutzerauswahl. Dies kann unkontrollierbar werden, wenn die Funktionen wachsen. Bitte schlagen Sie einen anständigen Weg vor, dies zuverlässig zu tun.

Dank

+0

Ich weiß nicht über Microsoft Bot-Framework, aber ich habe einen Bot für Telegramm geschrieben und für dieses Verhalten verlassen wir uns auf Zustandsautomaten. – Rachmaninoff

Antwort

3

vereinfachte ich eine frühere Implementierung von mir Ihnen ein Beispiel zu geben. Dies ist möglicherweise nicht der beste Weg, um es zu tun, aber es ist einfach und kann in vielerlei Hinsicht bearbeitet werden.

example image

Rootdialog.

Dies wird von der Steuerung aus gestartet. Durch Eingabe von "Menü" wird das Menü gestartet.

public class RootDialog : IDialog<object> 
{ 
    private MenuList _menu; 

    public async Task StartAsync(IDialogContext context) 
    { 
     var menuY = new MenuList("Y"); 
     menuY.AddItem(new MenuItem("P")); 
     menuY.AddItem(new MenuItem("Q")); 

     var menuA = new MenuList("A"); 
     menuA.AddItem(new MenuItem("X")); 
     menuA.AddItem(menuY); 

     var menuU = new MenuList("U"); 
     menuU.AddItem(new MenuItem("R")); 
     menuU.AddItem(new MenuItem("S")); 

     var menuB = new MenuList("B"); 
     menuB.AddItem(menuU); 
     menuB.AddItem(new MenuItem("V")); 

     _menu = new MenuList("Main"); 
     _menu.AddItem(menuA); 
     _menu.AddItem(menuB); 

     context.Wait(MessageReceived); 
    } 

    private async Task MessageReceived(IDialogContext context, IAwaitable<IMessageActivity> result) 
    { 
     var message = await result; 
     if (message.Text.ToLower() == "menu") 
     { 
      context.Call(new MenuDialog(_menu), ResumeAfterMenu); 
     } 
     else 
     { 
      await context.PostAsync("No result."); 
     } 
    } 

    private async Task ResumeAfterMenu(IDialogContext context, IAwaitable<IMenuItem> result) 
    { 
     var menuComponent = await result; 
     if (menuComponent == null) 
     { 
      await context.PostAsync("Menu exited."); 
      return; 
     } 
     if (menuComponent is MenuList) 
     { 
      context.Call(new MenuDialog((MenuList) menuComponent), ResumeAfterMenu); 
     } 
     else 
     { 
      var item = (MenuItem) menuComponent; 
      await item.Handle(context); 
      context.Wait(MessageReceived); 
     } 
    } 

MenuDialog

Diese Klasse behandelt die Menüoptionen.

[Serializable] 
public class MenuDialog : IDialog<IMenuItem> 
{ 
    private MenuList _menuList; 

    public MenuDialog(MenuList list) 
    { 
     this._menuList = list; 
    } 

    public async Task StartAsync(IDialogContext context) 
    { 
     await PromptUser(context); 
    } 

    private async Task PromptUser(IDialogContext context) 
    { 
     List<string> optionList = new List<string>(); 
     optionList.AddRange(_menuList.MenuItems.Select(m => m.name)); 
     optionList.Add("Back"); 
     optionList.Add("Exit"); 
     PromptOptions<string> options = new PromptOptions<string>("Choose an item:", 
      "That's not a valid option", options: optionList, attempts: 5); 
     PromptDialog.Choice(context, ReturnSelection, options); 
    } 

    protected virtual async Task ReturnSelection(IDialogContext context, IAwaitable<string> input) 
    { 
     string choice; 
     try 
     { 
      choice = await input; 
     } 
     catch (TooManyAttemptsException) 
     { 
      context.Done<IMenuItem>(null); 
      return; 
     } 

     if (choice == "Exit") 
     { 
      context.Done<IMenuItem>(null); 
     } 
     else if (choice == "Back") 
     { 
      context.Done<IMenuItem>(_menuList.parent); 
     } 
     else 
     { 
      context.Done<IMenuItem>(_menuList.MenuItems.Find(m => m.name == choice)); 
     } 
    } 

} 

Menü

Dies sind die Modelle, die ich verwendet mein Menü zu konstruieren.Die Handle-Methode hat zuerst einen Delegierten implementiert, aber ich habe das zur Vereinfachung weggelassen.

public enum MenuItemType 
{ 
    List, End 
} 

public interface IMenuItem 
{ 
    string name { get; } 
    MenuItemType Type { get; } 
    IMenuItem parent { get; set; } 
} 

[Serializable] 
public class MenuItem : IMenuItem 
{ 
    public string name { get; } 
    public MenuItemType Type => MenuItemType.End; 
    public IMenuItem parent { get; set; } 

    public MenuItem(string name) 
    { 
     this.name = name; 
    } 

    public async Task Handle(IDialogContext context) 
    { 
     await context.PostAsync("You activated item " + name); 
    } 
} 

[Serializable] 
public class MenuList : IMenuItem 
{ 
    public string name { get; } 
    public MenuItemType Type => MenuItemType.List; 
    public IMenuItem parent { get; set; } 

    public List<IMenuItem> MenuItems { get; } 

    public MenuList(string name) 
    { 
     this.name = name; 
     MenuItems = new List<IMenuItem>(); 
    } 

    public void AddItem(IMenuItem item) 
    { 
     item.parent = this; 
     MenuItems.Add(item); 
    } 
} 
+0

Danke für deine Antwort, es tut mir leid, ich habe nicht erwähnt, dass ich es für das Node SDK benötige, aber das wird Leuten helfen, die C# sicher benutzen. – moaglee

Verwandte Themen