Skip to content

Commit

Permalink
Create C# project for the handling interruptions topic. (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanFingold authored and Kaiqb committed Feb 21, 2019
1 parent 0a0fe72 commit a404504
Show file tree
Hide file tree
Showing 16 changed files with 1,453 additions and 3 deletions.
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Microsoft Managed Recommended Rules" Description="These rules focus on the most critical problems in your code, including potential security holes, application crashes, and other important logic and design errors. It is recommended to include this rule set in any custom rule set you create for your projects." ToolsVersion="10.0">
<Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
<Name Resource="MinimumRecommendedRules_Name" />
<Description Resource="MinimumRecommendedRules_Description" />
</Localization>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1011" Action="None" />
<Rule Id="SA1200" Action="None" />
<Rule Id="SA1101" Action="None" />
<Rule Id="SA1129" Action="None" />
<Rule Id="SA1305" Action="Warning" />
<Rule Id="SA1309" Action="None" />
<Rule Id="SA1412" Action="Warning" />
<Rule Id="SA1600" Action="None" />
<Rule Id="SA1609" Action="Warning" />
<Rule Id="SA1633" Action="None" />
</Rules>
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
<Rule Id="AvoidAsyncVoid" Action="Warning" />
</Rules>
</RuleSet>
@@ -0,0 +1,33 @@
{
"version": "1.0",
"resources": [
{
"type": "endpoint",
"id": "24",
"name": "Sample",
"url": "http://localhost:3978/api/messages"
},
{
"type": "endpoint",
"id": "2",
"name": "production",
"url": "https://your-bot-url.azurewebsites.net/api/messages"
},
{
"type": "abs",
"id": "3",
"name": "complex-dialog-Bot"
},
{
"type": "appInsights",
"id": "4",
"name": "complex-dialog-Insights"
},
{
"type": "blob",
"id": "5",
"name": "complex-dialog-Blob",
"container": "botstatestore"
}
]
}

Large diffs are not rendered by default.

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<CodeAnalysisRuleSet>BotBuilder.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Microsoft.BotBuilderSamples</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Update="*.bot">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

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

</Project>
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.BotBuilderSamples
{
using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;

/// <summary>
/// This class is created as a Singleton and passed into the IBot-derived constructor.
/// - See <see cref="DialogInterruptionsBot"/> constructor for how that is injected.
/// - See the Startup.cs file for more details on creating the Singleton that gets
/// injected into the constructor.
/// </summary>
public class DialogInterruptionsBotAccessors
{
/// <summary>
/// Initializes a new instance of the <see cref="DialogInterruptionsBotAccessors"/> class.
/// Contains the <see cref="ConversationState"/> and associated <see cref="IStatePropertyAccessor{T}"/>.
/// </summary>
/// <param name="conversationState">The state object that stores the dialog state.</param>
/// <param name="userState">The state object that stores the user state.</param>
public DialogInterruptionsBotAccessors(ConversationState conversationState, UserState userState)
{
ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
UserState = userState ?? throw new ArgumentNullException(nameof(userState));
}

/// <summary>
/// Gets or sets the <see cref="IStatePropertyAccessor{T}"/> for ConversationDialogState.
/// </summary>
/// <value>
/// The accessor stores the dialog state for the conversation.
/// </value>
public IStatePropertyAccessor<DialogState> DialogStateAccessor { get; set; }

/// <summary>
/// Gets or sets the <see cref="IStatePropertyAccessor{T}"/> for CounterState.
/// </summary>
/// <value>
/// The accessor stores user data.
/// </value>
public IStatePropertyAccessor<UserProfile> UserProfileAccessor { get; set; }

/// <summary>
/// Gets the <see cref="ConversationState"/> object for the conversation.
/// </summary>
/// <value>The <see cref="ConversationState"/> object.</value>
public ConversationState ConversationState { get; }

/// <summary>
/// Gets the <see cref="UserState"/> object for the conversation.
/// </summary>
/// <value>The <see cref="UserState"/> object.</value>
public UserState UserState { get; }
}
}
38 changes: 38 additions & 0 deletions SDKV4-Samples/dotnet_core/DialogInterruptionsBot/Program.cs
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, 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 Options.
// Consider using Application Insights for your logging and metrics needs.
// https://azure.microsoft.com/en-us/services/application-insights/
// .UseApplicationInsights()
.UseStartup<Startup>()
.Build();
}
}
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3978/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"DialogInterruptions_Sample": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:3978/"
}
}
}
33 changes: 33 additions & 0 deletions SDKV4-Samples/dotnet_core/DialogInterruptionsBot/README.md
@@ -0,0 +1,33 @@
This sample creates a dialog-based conversation that can be interrupted in various ways in ASP.Net Core 2.

# To try this sample
- Clone the samples repository
```bash
git clone https://github.com/Microsoft/BotFramework-Samples.git
```
- [Optional] Update the `appsettings.json` file under `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` with your botFileSecret.
For Azure Bot Service bots, you can find the botFileSecret under application settings.

# Running Locally
## Visual Studio
- Navigate to the samples folder (`BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/`) and open DialogInterruptionsBot.csproj in Visual Studio.
- Run the project (press `F5` key).

## .NET Core CLI
- Install the [.NET Core CLI tools](https://docs.microsoft.com/dotnet/core/tools/?tabs=netcore2x).
- Using the command line, navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` folder.
- Type `dotnet run`.

## Testing the bot using Bot Framework Emulator
[Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot
developers to test and debug their bots on localhost or running remotely through a tunnel.
- Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator).

## Connect to bot using Bot Framework Emulator **V4**
- Launch the Bot Framework Emulator.
- File -> Open bot and navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot` folder.
- Select `complex-dialog.bot` file.

# Further reading
- [Azure Bot Service](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
- [Bot Storage](https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-state?view=azure-bot-service-3.0&viewFallbackFrom=azure-bot-service-4.0)
117 changes: 117 additions & 0 deletions SDKV4-Samples/dotnet_core/DialogInterruptionsBot/Startup.cs
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.BotBuilderSamples
{
using System;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

/// <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="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<DialogInterruptionsBot>(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 ?? @".\complex-dialog.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. ({botConfig})"));
// 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}'.");
}
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
// Creates a logger for the application to use.
ILogger logger = _loggerFactory.CreateLogger<DialogInterruptionsBot>();
// 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 conversation and user state management objects, using memory storage.
IStorage dataStore = new MemoryStorage();
var conversationState = new ConversationState(dataStore);
var userState = new UserState(dataStore);

// Create and register state accessors.
// Accessors created here are passed into the IBot-derived class on every turn.
services.AddSingleton<DialogInterruptionsBotAccessors>(sp =>
{
// Create the custom state accessor.
// State accessors enable other components to read and write individual properties of state.
var accessors = new DialogInterruptionsBotAccessors(conversationState, userState)
{
DialogStateAccessor = conversationState.CreateProperty<DialogState>("DialogState"),
UserProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile"),
};
return accessors;
});
}

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

app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework();
}
}
}
23 changes: 23 additions & 0 deletions SDKV4-Samples/dotnet_core/DialogInterruptionsBot/UserProfile.cs
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.BotBuilderSamples
{
using System.Collections.Generic;

/// <summary>Contains information about a user.</summary>
public class UserProfile
{
/// <summary>Gets or sets the user's name.</summary>
/// <value>The user's name.</value>
public string Name { get; set; }

/// <summary>Gets or sets the user's age.</summary>
/// <value>The user's age.</value>
public int Age { get; set; }

/// <summary>Gets or sets the list of companies the user wants to review.</summary>
/// <value>The list of companies the user wants to review.</value>
public List<string> CompaniesToReview { get; set; } = new List<string>();
}
}
@@ -0,0 +1,4 @@
{
"botFilePath": "dialog-interruptions.bot",
"botFileSecret": ""
}

0 comments on commit a404504

Please sign in to comment.