/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param> /// <seealso cref="IStatePropertyAccessor{T}"/> /// <seealso cref="https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/> /// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/> public void ConfigureServices(IServiceCollection services) { services.AddBot <SyntinelBot>(options => { var secretKey = Configuration.GetSection("BotFileSecret")?.Value; var botFilePath = Configuration.GetSection("BotFilePath")?.Value; // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. var botConfig = BotConfiguration.Load(botFilePath ?? @".\syntinel.bot", secretKey); services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})")); // Retrieve current endpoint. var environment = _isProduction ? "production" : "development"; // The default value for reference and nullable types is null. var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment); if (!(service is EndpointService endpointService)) { throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'."); } // Sets bot credentials options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword); // Catches any errors that occur during a conversation turn and logs them. options.OnTurnError = async(context, exception) => { _logger.LogError($"Exception caught : {exception}"); await context.SendActivityAsync("Sorry, it looks like something went wrong."); }; // The Memory Storage used here is for local bot debugging only. When the bot // is restarted, everything stored in memory will be gone. // IStorage dataStore = new MemoryStorage(); // For production bots use the Azure Blob or // Azure CosmosDB storage providers. For the Azure // based storage providers, add the Microsoft.Bot.Builder.Azure // Nuget package to your solution. That package is found at: // https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/ // Uncomment the following lines to use Azure Blob Storage // //Storage configuration name or ID from the .bot file. const string StorageConfigurationId = "SyntinelBot"; var cosmoConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId); if (!(cosmoConfig is CosmosDbService cosmoStorageConfig)) { throw new InvalidOperationException($"The .bot file does not contain a cosmo storage with name '{StorageConfigurationId}'."); } IStorage dataStore = new CosmosDbStorage(new CosmosDbStorageOptions { CollectionId = cosmoStorageConfig.Collection, CosmosDBEndpoint = new Uri(cosmoStorageConfig.Endpoint), DatabaseId = cosmoStorageConfig.Database, AuthKey = cosmoStorageConfig.Key, }); _botStore = dataStore; // Create Conversation State object. // The Conversation State object is where we persist anything at the conversation-scope. _conversationState = new ConversationState(dataStore); _userState = new UserState(dataStore); _serviceState = new ServiceState(dataStore); }); // https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-tutorial-persist-user-inputs?view=azure-bot-service-4.0&tabs=csharp // Create and register state accesssors. // Acessors created here are passed into the IBot-derived class on every turn. services.AddSingleton <BotAccessors>(sp => { if (_conversationState == null) { throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors."); } if (_userState == null) { throw new InvalidOperationException("UserState must be defined and added before adding user-scoped state accessors."); } if (_serviceState == null) { throw new InvalidOperationException("ServiceState must be defined and added before adding service-scoped state accessors."); } // Create the custom state accessor. // State accessors enable other components to read and write individual properties of state. var accessors = new BotAccessors(_conversationState, _userState, _serviceState) { UserDataAccessor = _userState.CreateProperty <UserData>(BotAccessors.UserDataName), JobDataAccessor = _serviceState.CreateProperty <Dictionary <string, Job> >(BotAccessors.JobDataName), NotificationDataAccessor = _serviceState.CreateProperty <Dictionary <string, Notification> >(BotAccessors.NotificationDataName), UserRegistryAccessor = _serviceState.CreateProperty <RegisteredUsers>(BotAccessors.UserRegistryName), ConversationDialogState = _conversationState.CreateProperty <DialogState>(BotAccessors.DialogStateName), }; return(accessors); }); }
/// <summary> /// Initializes a new instance of the <see cref="BotAccessors"/> class. /// </summary> public BotAccessors(ConversationState conversationState, UserState userState, ServiceState serviceState) { ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState)); UserState = userState ?? throw new ArgumentNullException(nameof(userState)); ServiceState = serviceState ?? throw new ArgumentNullException(nameof(serviceState)); }