Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Welcome User Bot #1314

Merged
merged 2 commits into from
Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples
{
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithErrorHandler(ICredentialProvider credentialProvider, ILogger<BotFrameworkHttpAdapter> logger, ConversationState conversationState = null)
: base(credentialProvider)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError($"Exception caught : {exception.Message}");

// Send a catch-all appology to the user.
await turnContext.SendActivityAsync("Sorry, it looks like something went wrong.");

if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.LogError($"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;

namespace Microsoft.BotBuilderSamples
{
public class ConfigurationCredentialProvider : SimpleCredentialProvider
{
public ConfigurationCredentialProvider(IConfiguration configuration)
: base(configuration["MicrosoftAppId"], configuration["MicrosoftAppPassword"])
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;

namespace Microsoft.BotBuilderSamples
{
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;

public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}

[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}
24 changes: 6 additions & 18 deletions samples/csharp_dotnetcore/03.welcome-user/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,16 @@ public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
CreateWebHostBuilder(args).Build().Run();
}

public static IWebHost BuildWebHost(string[] args) =>
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
.ConfigureLogging((logging) =>
{
// Add Azure Logging
logging.AddAzureWebAppDiagnostics();

// Logging Options.
// There are other logging options available:
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1
// logging.AddDebug();
// logging.AddConsole();
logging.AddDebug();
logging.AddConsole();
})

// Logging Options.
// Consider using Application Insights for your logging and metrics needs.
// https://azure.microsoft.com/en-us/services/application-insights/
// .UseApplicationInsights()
.UseStartup<Startup>()
.Build();
.UseStartup<Startup>();
}
}
156 changes: 30 additions & 126 deletions samples/csharp_dotnetcore/03.welcome-user/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,154 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Linq;
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.BotBuilderSamples
{
/// <summary>
/// The Startup class configures services and the app's request pipeline.
/// </summary>
public class Startup
{
private ILoggerFactory _loggerFactory;
private bool _isProduction = false;

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

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

Configuration = builder.Build();
}

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

/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services">Specifies the contract for a <see cref="IServiceCollection"/> of service descriptors.</param>
/// <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"/>
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddBot<WelcomeUserBot>(options =>
{
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
if (!File.Exists(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.
var botConfig = BotConfiguration.Load(botFilePath ?? @".\welcome-user.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));

// Retrieve current endpoint.
var environment = _isProduction ? "production" : "development";
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}'.");
}
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
// Create the credential provider to be used with the Bot Framework Adapter.
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

// Creates a logger for the application to use.
ILogger logger = _loggerFactory.CreateLogger<WelcomeUserBot>();
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

// 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.");
};
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();

// The Memory Storage used here is for local bot debugging only. When the bot
// is restarted, anything stored in memory will be gone.
IStorage dataStore = new MemoryStorage();
// Create the User state.
services.AddSingleton<UserState>();

// 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 = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>";
// var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId);
// if (!(blobConfig is BlobStorageService blobStorageConfig))
// {
// throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'.");
// }
// // Default container name.
// const string DefaultBotContainer = "<DEFAULT-CONTAINER>";
// var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container;
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer);

// Create Conversation State object.
// The Conversation State object is where we persist anything at the conversation-scope.
var userState = new UserState(dataStore);
options.State.Add(userState);
});

// Create and register state accessors.
// Accessors created here are passed into the IBot-derived class on every turn.
services.AddSingleton<WelcomeUserStateAccessors>(sp =>
{
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
if (options == null)
{
throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
}

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

// Create the custom state accessor.
// State accessors enable other components to read and write individual properties of state.
var accessors = new WelcomeUserStateAccessors(userState)
{
WelcomeUserState = userState.CreateProperty<WelcomeUserState>(WelcomeUserStateAccessors.WelcomeUserName),
};

return accessors;
});
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, WelcomeUserBot>();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
_loggerFactory = loggerFactory;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework();
//app.UseHttpsRedirection();
app.UseMvc();
}
}
}
24 changes: 5 additions & 19 deletions samples/csharp_dotnetcore/03.welcome-user/WelcomeUser.csproj
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<PropertyGroup>
<CodeAnalysisRuleSet></CodeAnalysisRuleSet>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9" />
<PackageReference Include="Microsoft.Bot.Builder" Version="4.2.2" />
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.2.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.2.2" />
<PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003" PrivateAssets="all" />
<PackageReference Include="Microsoft.Bot.Configuration" Version="4.2.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta008" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<None Update="*.bot">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.3.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" />
</ItemGroup>

</Project>
Loading