CommandDialog.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
35 using System;
36 using System.Collections.Generic;
37 using System.IO;
38 using System.Runtime.Serialization;
39 using System.Security.Cryptography;
40 using System.Text;
41 using System.Text.RegularExpressions;
42 using System.Threading.Tasks;
43 
44 namespace Microsoft.Bot.Builder.Dialogs
45 {
46  #region Documentation
47  #endregion
49  [Serializable]
50  public class CommandDialog<T> : IDialog<T>
51  {
52  #region Documentation
53  #endregion
55  [Serializable]
56  public class Command
57  {
58  #region Documentation
59  #endregion
62  public string CommandId { set; get; }
63 
64  #region Documentation
65  #endregion
68  public Regex Expression { set; get; }
69 
70  #region Documentation
71  #endregion
74  public ResumeAfter<Connector.IMessageActivity> CommandHandler { set; get; }
75  }
76 
77  private Command defaultCommand;
78  private readonly List<Command> commands = new List<Command>();
79  private readonly Dictionary<string, Delegate> resultHandlers = new Dictionary<string, Delegate>();
80 
81  async Task IDialog<T>.StartAsync(IDialogContext context)
82  {
83  context.Wait(MessageReceived);
84  }
85 
86  public virtual async Task MessageReceived(IDialogContext context, IAwaitable<Connector.IMessageActivity> message)
87  {
88  var text = (await message).Text;
89  Command matched = null;
90  for (int idx = 0; idx < commands.Count; idx++)
91  {
92  var handler = commands[idx];
93  if (handler.Expression.Match(text).Success)
94  {
95  matched = handler;
96  break;
97  }
98  }
99 
100  if (matched == null && this.defaultCommand != null)
101  {
102  matched = this.defaultCommand;
103  }
104 
105  if (matched != null)
106  {
107  context.PrivateConversationData.SetValue("ActiveCommandId", matched.CommandId);
108  await matched.CommandHandler(context, message);
109  }
110  else
111  {
112  string error = $"CommandDialog doesn't have a registered command handler for this message: {text}";
113  throw new InvalidOperationException(error);
114  }
115  }
116 
117  #region Documentation
118  #endregion
125  public virtual async Task ResultHandler<U>(IDialogContext context, IAwaitable<U> result)
126  {
127  Delegate handler;
128  string commandId;
129  if (context.PrivateConversationData.TryGetValue("ActiveCommandId", out commandId) && resultHandlers.TryGetValue(commandId, out handler))
130  {
131  await ((ResumeAfter<U>)handler).Invoke(context, result);
132  context.Wait(MessageReceived);
133  }
134  else
135  {
136  string error = $"CommandDialog doesn't have a registered result handler for this type: {typeof(U)}";
137  throw new InvalidOperationException(error);
138  }
139  }
140 
141  #region Documentation
142  #endregion
149  public CommandDialog<T> On<U>(Regex expression, ResumeAfter<Connector.IMessageActivity> handler, ResumeAfter<U> resultHandler = null)
150  {
151  var command = new Command
152  {
153  CommandId = ComputeHash(expression.ToString()),
154  Expression = expression,
155  CommandHandler = handler,
156  };
157  commands.Add(command);
158  RegisterResultHandler(command, resultHandler);
159 
160  return this;
161  }
162 
163  #region Documentation
164  #endregion
170  public CommandDialog<T> OnDefault<U>(ResumeAfter<Connector.IMessageActivity> handler, ResumeAfter<U> resultHandler = null)
171  {
172  var command = new Command { CommandId = "defaultResultHandler", CommandHandler = handler };
173  this.defaultCommand = command;
174  RegisterResultHandler(command, resultHandler);
175 
176  return this;
177  }
178 
179  private void RegisterResultHandler<U>(Command command, ResumeAfter<U> resultHandler)
180  {
181  if (resultHandler != null)
182  {
183  resultHandlers.Add(command.CommandId, resultHandler);
184  }
185  }
186 
187  private string ComputeHash(string str)
188  {
189  var algorithm = SHA1.Create();
190  return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(str)));
191  }
192  }
193 }
Task StartAsync(IDialogContext context)
The start of the code that represents the conversational dialog.
The context for the execution of a dialog&#39;s conversational process.
Dialog that dispatches based on a regex matching input.
virtual async Task MessageReceived(IDialogContext context, IAwaitable< Connector.IMessageActivity > message)
Explicit interface to support the compiling of async/await.
Definition: Awaitable.cs:55
Namespace for internal machinery that is not useful for most developers and may change in the future...
A IDialog<TResult> is a suspendable conversational process that produces a result of type TResult ...
Definition: IDialog.cs:47
ResumeAfter< Connector.IMessageActivity > CommandHandler
Gets or sets the command handler.
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
string CommandId
Gets or sets the command ID used for persisting currently running command handler.
Root namespace for the Microsoft Bot Builder SDK.
IBotDataBag PrivateConversationData
Private bot data associated with a user in a conversation.
Definition: IBotData.cs:57