DialogModule.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 
34 using System;
35 using System.IO;
36 using System.Resources;
37 using System.Text.RegularExpressions;
38 using System.Threading;
39 using Autofac;
47 using Microsoft.Bot.Connector;
48 
49 namespace Microsoft.Bot.Builder.Dialogs.Internals
50 {
54  public sealed class DialogModule : Module
55  {
56  public const string BlobKey = "DialogState";
57  public static readonly object LifetimeScopeTag = typeof(DialogModule);
58 
59  public static readonly object Key_DeleteProfile_Regex = new object();
60  public static readonly object Key_Dialog_Router = new object();
61 
62  public static ILifetimeScope BeginLifetimeScope(ILifetimeScope scope, IMessageActivity message)
63  {
64  var inner = scope.BeginLifetimeScope(LifetimeScopeTag);
65  inner.Resolve<IMessageActivity>(TypedParameter.From(message));
66  return inner;
67  }
68 
69  protected override void Load(ContainerBuilder builder)
70  {
71  base.Load(builder);
72 
73  builder.RegisterModule(new FiberModule<DialogTask>());
74 
75  // singleton components
76 
77  builder
78  .Register(c => new ResourceManager("Microsoft.Bot.Builder.Resource.Resources", typeof(Resource.Resources).Assembly))
79  .As<ResourceManager>()
80  .SingleInstance();
81 
82  // every lifetime scope is driven by a message
83 
84  builder
85  .Register((c, p) => p.TypedAs<IMessageActivity>())
86  .AsSelf()
87  .AsImplementedInterfaces()
88  .InstancePerMatchingLifetimeScope(LifetimeScopeTag);
89 
90  // make the address and cookie available for the lifetime scope
91 
92  builder
93  .Register(c => Address.FromActivity(c.Resolve<IActivity>()))
94  .AsImplementedInterfaces()
95  .InstancePerMatchingLifetimeScope(LifetimeScopeTag);
96 
97 #pragma warning disable CS0618
98  builder
99  .RegisterType<ResumptionCookie>()
100  .AsSelf()
101  .InstancePerMatchingLifetimeScope(LifetimeScopeTag);
102 #pragma warning restore CS0618
103 
104  builder
105  .Register(c => c.Resolve<IActivity>().ToConversationReference())
106  .AsSelf()
107  .InstancePerMatchingLifetimeScope(LifetimeScopeTag);
108 
109  // components not marked as [Serializable]
110  builder
111  .RegisterType<MicrosoftAppCredentials>()
112  .AsSelf()
113  .SingleInstance();
114 
115  builder
116  // not resolving IEqualityComparer<IAddress> from container because it's a very local policy
117  // and yet too broad of an interface. could explore using tags for registration overrides.
119  .As<IScope<IAddress>>()
120  .SingleInstance();
121 
122  builder
123  .Register(c => new ConnectorClientFactory(c.Resolve<IAddress>(), c.Resolve<MicrosoftAppCredentials>()))
125  .InstancePerLifetimeScope();
126 
127  builder
128  .Register(c => c.Resolve<IConnectorClientFactory>().MakeConnectorClient())
129  .As<IConnectorClient>()
130  .InstancePerLifetimeScope();
131 
132  builder
133  .Register(c => c.Resolve<IConnectorClientFactory>().MakeStateClient())
134  .As<IStateClient>()
135  .InstancePerLifetimeScope();
136 
137  builder
138  .Register(c => new DetectChannelCapability(c.Resolve<IAddress>()))
140  .InstancePerLifetimeScope();
141 
142  builder
143  .Register(c => c.Resolve<IDetectChannelCapability>().Detect())
144  .As<IChannelCapability>()
145  .InstancePerLifetimeScope();
146 
147  builder
148  .RegisterKeyedType<ConnectorStore, IBotDataStore<BotData>>()
149  .InstancePerLifetimeScope();
150 
151  builder
152  .RegisterKeyedType<InMemoryDataStore, IBotDataStore<BotData>>()
153  .SingleInstance();
154 
155  builder
156  .RegisterKeyedType<CachingBotDataStore, IBotDataStore<BotData>>()
157  .WithParameter((pi, c) => pi.ParameterType == typeof(CachingBotDataStoreConsistencyPolicy),
158  (pi, c) => CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)
159  .InstancePerLifetimeScope();
160 
161  builder
162  .RegisterAdapterChain<IBotDataStore<BotData>>
163  (
164  typeof(ConnectorStore),
165  typeof(CachingBotDataStore)
166  )
167  .InstancePerLifetimeScope();
168 
169  builder
170  .RegisterType<JObjectBotData>()
171  .AsSelf()
172  .InstancePerLifetimeScope();
173 
174  builder
175  .Register(c => new DialogTaskManagerBotDataLoader(c.Resolve<JObjectBotData>(),
176  c.Resolve<IDialogTaskManager>()))
177  .As<IBotData>()
178  .InstancePerLifetimeScope();
179 
180  builder
181  .Register((c, p) => new BotDataBagStream(p.TypedAs<IBotDataBag>(), p.TypedAs<string>()))
182  .As<Stream>()
183  .InstancePerDependency();
184 
185  builder
186  .Register(c => new DialogTaskManager(DialogModule.BlobKey,
187  c.Resolve<JObjectBotData>(),
188  c.Resolve<IStackStoreFactory<DialogTask>>(),
189  c.Resolve<Func<IDialogStack, CancellationToken, IDialogContext>>(),
190  c.Resolve<IEventProducer<IActivity>>()))
191  .AsSelf()
192  .As<IDialogTaskManager>()
193  .As<IDialogTasks>()
194  .InstancePerLifetimeScope();
195 
196  builder
197  .RegisterType<DialogSystem>()
198  .As<IDialogSystem>();
199 
200  builder
201  .RegisterType<DialogContext>()
202  .As<IDialogContext>()
203  .InstancePerDependency();
204 
205  builder
206  .Register(c =>
207  {
208  var cc = c.Resolve<IComponentContext>();
209 
210  Func<string, IBotDataBag, IStore<IFiberLoop<DialogTask>>> make = (taskId, botDataBag) =>
211  {
212  var stream = cc.Resolve<Stream>(TypedParameter.From(botDataBag), TypedParameter.From(taskId));
213  return cc.Resolve<IStore<IFiberLoop<DialogTask>>>(TypedParameter.From(stream));
214  };
215 
216  return make;
217  })
218  .As<Func<string, IBotDataBag, IStore<IFiberLoop<DialogTask>>>>()
219  .InstancePerDependency();
220 
221 
222  builder.Register(c => c.Resolve<IDialogTaskManager>().DialogTasks[0])
223  .As<IDialogStack>()
224  .As<IDialogTask>()
225  .InstancePerLifetimeScope();
226 
227  // Scorable implementing "/deleteprofile"
228  builder
229  .Register(c => new Regex("^(\\s)*/deleteprofile", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace))
230  .Keyed<Regex>(Key_DeleteProfile_Regex)
231  .SingleInstance();
232 
233  builder
234  .Register(c => new DeleteProfileScorable(c.Resolve<IDialogStack>(), c.Resolve<IBotData>(), c.Resolve<IBotToUser>(), c.ResolveKeyed<Regex>(Key_DeleteProfile_Regex)))
236  .InstancePerLifetimeScope();
237 
238  builder
239  .Register(c =>
240  {
241  var cc = c.Resolve<IComponentContext>();
242  Func<IActivity, IResolver> make = activity =>
243  {
244  var resolver = NoneResolver.Instance;
245  resolver = new EnumResolver(resolver);
246  resolver = new AutofacResolver(cc, resolver);
247  resolver = new ArrayResolver(resolver,
248  activity,
249  cc.Resolve<IBotToUser>(),
250  cc.Resolve<IBotData>(),
251  cc.Resolve<IDialogSystem>());
252  resolver = new ActivityResolver(resolver);
253  resolver = new EventActivityValueResolver(resolver);
254  resolver = new InvokeActivityValueResolver(resolver);
255  return resolver;
256  };
257  return make;
258  })
259  .AsSelf()
260  .InstancePerLifetimeScope();
261 
262  builder
263  .RegisterType<DialogRouter>()
264  .Keyed<IScorable<IActivity, double>>(Key_Dialog_Router)
265  .InstancePerLifetimeScope();
266 
267  builder
268  .RegisterType<EventQueue<IActivity>>()
269  .AsImplementedInterfaces()
270  .InstancePerLifetimeScope();
271 
272  builder
273  .RegisterType<ReactiveDialogTask>()
274  .AsSelf()
275  .InstancePerLifetimeScope();
276 
277  builder
278  .Register(c => new ScoringEventLoop<double>(c.Resolve<ReactiveDialogTask>(), c.Resolve<ReactiveDialogTask>(), c.Resolve<IEventConsumer<IActivity>>(), c.ResolveKeyed<IScorable<IActivity, double>>(Key_Dialog_Router)))
279  .As<IEventLoop>()
280  .InstancePerLifetimeScope();
281 
282  // register IDataBag that is used for to load/save ResumptionData
283  builder
284  .Register(c =>
285  {
286  var cc = c.Resolve<IComponentContext>();
287  Func<IBotDataBag> make = () =>
288  {
289  return cc.Resolve<IBotData>().PrivateConversationData;
290  };
291  return make;
292  })
293  .As<Func<IBotDataBag>>()
294  .InstancePerLifetimeScope();
295 
296  builder
297  .RegisterType<ResumptionContext>()
298  .AsSelf()
299  .InstancePerLifetimeScope();
300 
301  builder
302  .RegisterType<LocaleFinder>()
303  .AsSelf()
304  .AsImplementedInterfaces()
305  .InstancePerLifetimeScope();
306 
307  // IPostToBot services
308 
309  builder
310  .RegisterKeyedType<NullPostToBot, IPostToBot>()
311  .InstancePerLifetimeScope();
312 
313  builder
314  .RegisterKeyedType<EventLoopDialogTask, IPostToBot>()
315  .InstancePerLifetimeScope();
316 
317  builder
318  .RegisterKeyedType<PersistentDialogTask, IPostToBot>()
319  .InstancePerLifetimeScope();
320 
321  builder
322  .RegisterKeyedType<ExceptionTranslationDialogTask, IPostToBot>()
323  .InstancePerLifetimeScope();
324 
325  builder
326  .RegisterKeyedType<SerializeByConversation, IPostToBot>()
327  .InstancePerLifetimeScope();
328 
329  builder
330  .RegisterKeyedType<SetAmbientThreadCulture, IPostToBot>()
331  .InstancePerLifetimeScope();
332 
333  builder
334  .RegisterKeyedType<PostUnhandledExceptionToUser, IPostToBot>()
335  .InstancePerLifetimeScope();
336 
337  builder
338  .RegisterKeyedType<LogPostToBot, IPostToBot>()
339  .InstancePerLifetimeScope();
340 
341  builder
342  .RegisterAdapterChain<IPostToBot>
343  (
344  typeof(EventLoopDialogTask),
345  typeof(SetAmbientThreadCulture),
346  typeof(PersistentDialogTask),
348  typeof(SerializeByConversation),
350  typeof(LogPostToBot)
351  )
352  .InstancePerLifetimeScope();
353 
354  // other
355 
356  builder
357  .RegisterType<NullActivityLogger>()
358  .AsImplementedInterfaces()
359  .SingleInstance();
360 
361  builder
362  .RegisterType<KeyboardCardMapper>()
363  .AsImplementedInterfaces()
364  .SingleInstance();
365 
366  // IBotToUser services
367 
368  builder
369  .RegisterKeyedType<NullBotToUser, IBotToUser>()
370  .InstancePerLifetimeScope();
371 
372  builder
373  .RegisterKeyedType<AlwaysSendDirect_BotToUser, IBotToUser>()
374  .InstancePerLifetimeScope();
375 
376  builder
377  .RegisterKeyedType<MapToChannelData_BotToUser, IBotToUser>()
378  .InstancePerLifetimeScope();
379 
380  builder
381  .RegisterKeyedType<LogBotToUser, IBotToUser>()
382  .InstancePerLifetimeScope();
383 
384 #pragma warning disable CS1587
385 #pragma warning restore CS1587
390  builder
391  .RegisterAdapterChain<IBotToUser>
392  (
395  typeof(LogBotToUser)
396  )
397  .InstancePerLifetimeScope();
398  }
399  }
400 
401  public sealed class DialogModule_MakeRoot : Module
402  {
403  protected override void Load(ContainerBuilder builder)
404  {
405  base.Load(builder);
406 
407  builder.RegisterModule(new DialogModule());
408 
409  // TODO: let dialog resolve its dependencies from container
410  builder
411  .Register((c, p) => p.TypedAs<Func<IDialog<object>>>())
412  .AsSelf()
413  .InstancePerMatchingLifetimeScope(DialogModule.LifetimeScopeTag);
414  }
415 
416  public static void Register(ILifetimeScope scope, Func<IDialog<object>> MakeRoot)
417  {
418  // TODO: let dialog resolve its dependencies from container
419  scope.Resolve<Func<IDialog<object>>>(TypedParameter.From(MakeRoot));
420  }
421  }
422 }
This IPostToBot service converts any unhandled exceptions to a message sent to the user...
Definition: PostToBot.cs:129
Autofac module for Fiber components.
Definition: FiberModule.cs:52
Namespace for Microsoft Bot Builder SDK extensions to the Microsoft Bot Connector SDK...
Definition: KeyboardCard.cs:40
This dialog task loads the dialog stack from IBotData before handling the incoming activity and saves...
Definition: DialogTask.cs:443
Namespace for the Microsoft Bot Connector SDK.
Namespace for interfaces and classes for working with conversational history.
Autofac module for Dialog components.
Definition: DialogModule.cs:54
This IPostToBot service sets the ambient thread culture based on the IMessageActivity.Locale.
Definition: PostToBot.cs:79
This IPostToBot service serializes the execution of a particular conversation&#39;s code to avoid concurr...
Definition: PostToBot.cs:104
Caches data for BotDataBase<T> and wraps the data in BotData to be stored in CachingBotDataStore.inner
Allow the scoring of items, with external comparison of scores, and enable the winner to take some ac...
Definition: Scorable.cs:49
IStateClient MakeStateClient()
Make the IStateClient implementation.
Volitile in-memory implementation of IBotDataStore<BotData>
Definition: PostToBot.cs:61
Scorable for Dialog module routing.
Definition: DialogRouter.cs:48
Namespace for internal scorable implementation that is not useful for most developers and may change ...
CachingBotDataStoreConsistencyPolicy
The data consistency policy for CachingBotDataStore
Namespace for scorable interfaces, classes and compositions.
Namespace for internal machinery that is not useful for most developers and may change in the future...
Methods to send a message from the user to the bot.
Definition: PostToBot.cs:50
The key that minimally and completely identifies a bot&#39;s conversation with a user on a channel...
Definition: Address.cs:45
A IDialog<TResult> is a suspendable conversational process that produces a result of type TResult ...
Definition: IDialog.cs:47
The resumption context that is responsible for loading/persisting the ResumptionData.
This class is responsible for managing the set of dialog tasks.
This event loop dispatches incoming activities to a scorable action, and then if the scorable action ...
IReadOnlyList< IDialogTask > DialogTasks
The list of IDialogTask
A property bag of bot data.
Definition: IBotDataBag.cs:41
Compare two Address instances for equality, excluding the user information.
Definition: Address.cs:134
IChannelCapability Detect()
Detects channel capabilities.
IConnectorClient MakeConnectorClient()
Make the IConnectorClient implementation.
Shared properties for all activities
Definition: IActivity.cs:9
A reactive dialog task (in contrast to a proactive dialog task) is a dialog task that starts some roo...
Definition: DialogTask.cs:349
override void Load(ContainerBuilder builder)
Definition: DialogModule.cs:69
Activity logger that doesn&#39;t log.
A resolver to recover C# type information from Activity schema types.
static Address FromActivity(IActivity activity)
Definition: Address.cs:60
static ILifetimeScope BeginLifetimeScope(ILifetimeScope scope, IMessageActivity message)
Definition: DialogModule.cs:62
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Methods to send a message from the bot to the user.
Definition: BotToUser.cs:51
This dialog task translates from the more orthogonal (opaque) fiber exceptions to the more readable d...
Definition: DialogTask.cs:390
implementation of IBotDatStore which uses the State REST API on state.botframework.com to store data
The stack of dialogs in the conversational process.
Definition: IDialogTask.cs:46
The key that minimally and completely identifies a bot&#39;s conversation with a user on a channel...
Definition: Address.cs:58
The dialog system represents the top-level interface for the dialog tasks and their event loop...
Definition: DialogSystem.cs:50
The locale finder implementation based on ResumptionContext.
Definition: LocaleFinder.cs:28
static void Register(ILifetimeScope scope, Func< IDialog< object >> MakeRoot)
Provide an abstraction to serialize access to an item for a using-block scope of code.
Definition: Scope.cs:47
Root namespace for the Microsoft Bot Builder SDK.