Untitled diff

Created Diff never expires
2 removals
157 lines
16 additions
171 lines
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Licensed under the MIT License.


using System;
using System;
using System.IO;
using System.IO;
using System.Linq;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options;


namespace Microsoft.BotBuilderSamples
namespace Microsoft.BotBuilderSamples
{
{
/// <summary>
/// <summary>
/// The Startup class configures services and the app's request pipeline.
/// The Startup class configures services and the app's request pipeline.
/// </summary>
/// </summary>
public class Startup
public class Startup
{
{
private const string CosmosServiceEndpoint = "https://localhost:8081";
private const string CosmosDBKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
private const string CosmosDBDatabaseName = "bot-cosmos-sql-db";
private const string CosmosDBCollectionName = "bot-storage";

private static readonly CosmosDbStorage _myStorage = new CosmosDbStorage(new CosmosDbStorageOptions
{
AuthKey = CosmosDBKey,
CollectionId = CosmosDBCollectionName,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDBDatabaseName,
});

private ILoggerFactory _loggerFactory;
private ILoggerFactory _loggerFactory;
private bool _isProduction = false;
private bool _isProduction = false;


public Startup(IHostingEnvironment env)
public Startup(IHostingEnvironment env)
{
{
_isProduction = env.IsProduction();
_isProduction = env.IsProduction();


var builder = new ConfigurationBuilder()
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
.AddEnvironmentVariables();


Configuration = builder.Build();
Configuration = builder.Build();
}
}


/// <summary>
/// <summary>
/// Gets the configuration that represents a set of key/value application configuration properties.
/// Gets the configuration that represents a set of key/value application configuration properties.
/// </summary>
/// </summary>
/// <value>
/// <value>
/// The <see cref="IConfiguration"/> that represents a set of key/value application configuration properties.
/// The <see cref="IConfiguration"/> that represents a set of key/value application configuration properties.
/// </value>
/// </value>
public IConfiguration Configuration { get; }
public IConfiguration Configuration { get; }


/// <summary>
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param>
/// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param>
/// <seealso cref="IStatePropertyAccessor{T}"/>
/// <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/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"/>
/// <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)
public void ConfigureServices(IServiceCollection services)
{
{
services.AddBot<SimplePromptBot>(options =>
services.AddBot<SimplePromptBot>(options =>
{
{
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
if (!File.Exists(botFilePath))
if (!File.Exists(botFilePath))
{
{
throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
}
}


// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
var botConfig = BotConfiguration.Load(botFilePath ?? @".\simple-prompt.bot", secretKey);
var botConfig = BotConfiguration.Load(botFilePath ?? @".\simple-prompt.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));


// Retrieve current endpoint.
// Retrieve current endpoint.
var environment = _isProduction ? "production" : "development";
var environment = _isProduction ? "production" : "development";
var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
if (!(service is EndpointService endpointService))
if (!(service is EndpointService endpointService))
{
{
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
}
}


options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);


// Creates a logger for the application to use.
// Creates a logger for the application to use.
ILogger logger = _loggerFactory.CreateLogger<SimplePromptBot>();
ILogger logger = _loggerFactory.CreateLogger<SimplePromptBot>();


// Catches any errors that occur during a conversation turn and logs them.
// Catches any errors that occur during a conversation turn and logs them.
options.OnTurnError = async (context, exception) =>
options.OnTurnError = async (context, exception) =>
{
{
logger.LogError($"Exception caught : {exception}");
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
};
};


// Memory Storage is for local bot debugging only. When the bot
// Memory Storage is for local bot debugging only. When the bot
// is restarted, everything stored in memory will be gone.
// is restarted, everything stored in memory will be gone.
IStorage dataStore = new MemoryStorage();
//IStorage dataStore = new MemoryStorage();


// For production bots use the Azure Blob or
// For production bots use the Azure Blob or
// Azure CosmosDB storage providers. For the Azure
// Azure CosmosDB storage providers. For the Azure
// based storage providers, add the Microsoft.Bot.Builder.Azure
// based storage providers, add the Microsoft.Bot.Builder.Azure
// Nuget package to your solution. That package is found at:
// Nuget package to your solution. That package is found at:
// https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/
// https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/
// Uncomment the following lines to use Azure Blob Storage
// Uncomment the following lines to use Azure Blob Storage
// //Storage configuration name or ID from the .bot file.
// //Storage configuration name or ID from the .bot file.
// const string StorageConfigurationId = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>";
// const string StorageConfigurationId = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>";
// var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId);
// var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId);
// if (!(blobConfig is BlobStorageService blobStorageConfig))
// if (!(blobConfig is BlobStorageService blobStorageConfig))
// {
// {
// throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'.");
// throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'.");
// }
// }
// // Default container name.
// // Default container name.
// const string DefaultBotContainer = "<DEFAULT-CONTAINER>";
// const string DefaultBotContainer = "<DEFAULT-CONTAINER>";
// var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container;
// var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container;
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer);
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer);


// Create Conversation State object.
// Create Conversation State object.
// The Conversation State object is where we persist anything at the conversation-scope.
// The Conversation State object is where we persist anything at the conversation-scope.
var conversationState = new ConversationState(dataStore);
var conversationState = new ConversationState(_myStorage);


options.State.Add(conversationState);
options.State.Add(conversationState);
});
});


services.AddSingleton(sp =>
services.AddSingleton(sp =>
{
{
// We need to grab the conversationState we added on the options in the previous step.
// We need to grab the conversationState we added on the options in the previous step.
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
if (options == null)
if (options == null)
{
{
throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
}
}


var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
if (conversationState == null)
if (conversationState == null)
{
{
throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors.");
throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors.");
}
}


// The dialogs will need a state store accessor. Creating it here once (on-demand) allows the dependency injection
// The dialogs will need a state store accessor. Creating it here once (on-demand) allows the dependency injection
// to hand it to our IBot class that is create per-request.
// to hand it to our IBot class that is create per-request.
var accessors = new SimplePromptBotAccessors(conversationState)
var accessors = new SimplePromptBotAccessors(conversationState)
{
{
ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState"),
ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState"),
};
};


return accessors;
return accessors;
});
});
}
}


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
{
_loggerFactory = loggerFactory;
_loggerFactory = loggerFactory;


app.UseDefaultFiles()
app.UseDefaultFiles()
.UseStaticFiles()
.UseStaticFiles()
.UseBotFramework();
.UseBotFramework();
}
}
}
}
}
}