Skip to content

Commit a404504

Browse files
JonathanFingoldKaiqb
authored andcommitted
Create C# project for the handling interruptions topic. (#52)
1 parent 0a0fe72 commit a404504

16 files changed

Lines changed: 1453 additions & 3 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<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">
3+
<Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
4+
<Name Resource="MinimumRecommendedRules_Name" />
5+
<Description Resource="MinimumRecommendedRules_Description" />
6+
</Localization>
7+
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
8+
<Rule Id="SA1011" Action="None" />
9+
<Rule Id="SA1200" Action="None" />
10+
<Rule Id="SA1101" Action="None" />
11+
<Rule Id="SA1129" Action="None" />
12+
<Rule Id="SA1305" Action="Warning" />
13+
<Rule Id="SA1309" Action="None" />
14+
<Rule Id="SA1412" Action="Warning" />
15+
<Rule Id="SA1600" Action="None" />
16+
<Rule Id="SA1609" Action="Warning" />
17+
<Rule Id="SA1633" Action="None" />
18+
</Rules>
19+
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
20+
<Rule Id="AvoidAsyncVoid" Action="Warning" />
21+
</Rules>
22+
</RuleSet>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"version": "1.0",
3+
"resources": [
4+
{
5+
"type": "endpoint",
6+
"id": "24",
7+
"name": "Sample",
8+
"url": "http://localhost:3978/api/messages"
9+
},
10+
{
11+
"type": "endpoint",
12+
"id": "2",
13+
"name": "production",
14+
"url": "https://your-bot-url.azurewebsites.net/api/messages"
15+
},
16+
{
17+
"type": "abs",
18+
"id": "3",
19+
"name": "complex-dialog-Bot"
20+
},
21+
{
22+
"type": "appInsights",
23+
"id": "4",
24+
"name": "complex-dialog-Insights"
25+
},
26+
{
27+
"type": "blob",
28+
"id": "5",
29+
"name": "complex-dialog-Blob",
30+
"container": "botstatestore"
31+
}
32+
]
33+
}

SDKV4-Samples/dotnet_core/DialogInterruptionsBot/DialogInterruptionsBot.cs

Lines changed: 527 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.0</TargetFramework>
5+
<CodeAnalysisRuleSet>BotBuilder.ruleset</CodeAnalysisRuleSet>
6+
<RootNamespace>Microsoft.BotBuilderSamples</RootNamespace>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<None Update="*.bot">
11+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
12+
</None>
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003" PrivateAssets="all" />
17+
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
18+
<PackageReference Include="Microsoft.Bot.Builder" Version="4.1.5" />
19+
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.1.5" />
20+
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.1.5" />
21+
<PackageReference Include="Microsoft.Bot.Configuration" Version="4.1.5" />
22+
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta008" PrivateAssets="all" />
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.BotBuilderSamples
5+
{
6+
using System;
7+
using Microsoft.Bot.Builder;
8+
using Microsoft.Bot.Builder.Dialogs;
9+
10+
/// <summary>
11+
/// This class is created as a Singleton and passed into the IBot-derived constructor.
12+
/// - See <see cref="DialogInterruptionsBot"/> constructor for how that is injected.
13+
/// - See the Startup.cs file for more details on creating the Singleton that gets
14+
/// injected into the constructor.
15+
/// </summary>
16+
public class DialogInterruptionsBotAccessors
17+
{
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="DialogInterruptionsBotAccessors"/> class.
20+
/// Contains the <see cref="ConversationState"/> and associated <see cref="IStatePropertyAccessor{T}"/>.
21+
/// </summary>
22+
/// <param name="conversationState">The state object that stores the dialog state.</param>
23+
/// <param name="userState">The state object that stores the user state.</param>
24+
public DialogInterruptionsBotAccessors(ConversationState conversationState, UserState userState)
25+
{
26+
ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
27+
UserState = userState ?? throw new ArgumentNullException(nameof(userState));
28+
}
29+
30+
/// <summary>
31+
/// Gets or sets the <see cref="IStatePropertyAccessor{T}"/> for ConversationDialogState.
32+
/// </summary>
33+
/// <value>
34+
/// The accessor stores the dialog state for the conversation.
35+
/// </value>
36+
public IStatePropertyAccessor<DialogState> DialogStateAccessor { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets the <see cref="IStatePropertyAccessor{T}"/> for CounterState.
40+
/// </summary>
41+
/// <value>
42+
/// The accessor stores user data.
43+
/// </value>
44+
public IStatePropertyAccessor<UserProfile> UserProfileAccessor { get; set; }
45+
46+
/// <summary>
47+
/// Gets the <see cref="ConversationState"/> object for the conversation.
48+
/// </summary>
49+
/// <value>The <see cref="ConversationState"/> object.</value>
50+
public ConversationState ConversationState { get; }
51+
52+
/// <summary>
53+
/// Gets the <see cref="UserState"/> object for the conversation.
54+
/// </summary>
55+
/// <value>The <see cref="UserState"/> object.</value>
56+
public UserState UserState { get; }
57+
}
58+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.AspNetCore;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace Microsoft.BotBuilderSamples
9+
{
10+
public class Program
11+
{
12+
public static void Main(string[] args)
13+
{
14+
BuildWebHost(args).Run();
15+
}
16+
17+
public static IWebHost BuildWebHost(string[] args) =>
18+
WebHost.CreateDefaultBuilder(args)
19+
.ConfigureLogging((hostingContext, logging) =>
20+
{
21+
// Add Azure Logging
22+
logging.AddAzureWebAppDiagnostics();
23+
24+
// Logging Options.
25+
// There are other logging options available:
26+
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1
27+
// logging.AddDebug();
28+
// logging.AddConsole();
29+
})
30+
31+
// Logging Options.
32+
// Consider using Application Insights for your logging and metrics needs.
33+
// https://azure.microsoft.com/en-us/services/application-insights/
34+
// .UseApplicationInsights()
35+
.UseStartup<Startup>()
36+
.Build();
37+
}
38+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:3978/",
7+
"sslPort": 0
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"environmentVariables": {
15+
"ASPNETCORE_ENVIRONMENT": "Development"
16+
}
17+
},
18+
"DialogInterruptions_Sample": {
19+
"commandName": "Project",
20+
"launchBrowser": true,
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development"
23+
},
24+
"applicationUrl": "http://localhost:3978/"
25+
}
26+
}
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
This sample creates a dialog-based conversation that can be interrupted in various ways in ASP.Net Core 2.
2+
3+
# To try this sample
4+
- Clone the samples repository
5+
```bash
6+
git clone https://github.com/Microsoft/BotFramework-Samples.git
7+
```
8+
- [Optional] Update the `appsettings.json` file under `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` with your botFileSecret.
9+
For Azure Bot Service bots, you can find the botFileSecret under application settings.
10+
11+
# Running Locally
12+
## Visual Studio
13+
- Navigate to the samples folder (`BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/`) and open DialogInterruptionsBot.csproj in Visual Studio.
14+
- Run the project (press `F5` key).
15+
16+
## .NET Core CLI
17+
- Install the [.NET Core CLI tools](https://docs.microsoft.com/dotnet/core/tools/?tabs=netcore2x).
18+
- Using the command line, navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot/` folder.
19+
- Type `dotnet run`.
20+
21+
## Testing the bot using Bot Framework Emulator
22+
[Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot
23+
developers to test and debug their bots on localhost or running remotely through a tunnel.
24+
- Install the Bot Framework emulator from [here](https://aka.ms/botframeworkemulator).
25+
26+
## Connect to bot using Bot Framework Emulator **V4**
27+
- Launch the Bot Framework Emulator.
28+
- File -> Open bot and navigate to `BotFramework-Samples/SDKV4-Samples/dotnet_core/DialogInterruptionsBot` folder.
29+
- Select `complex-dialog.bot` file.
30+
31+
# Further reading
32+
- [Azure Bot Service](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
33+
- [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)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.BotBuilderSamples
5+
{
6+
using System;
7+
using System.Linq;
8+
using Microsoft.AspNetCore.Builder;
9+
using Microsoft.AspNetCore.Hosting;
10+
using Microsoft.Bot.Builder;
11+
using Microsoft.Bot.Builder.Dialogs;
12+
using Microsoft.Bot.Builder.Integration.AspNet.Core;
13+
using Microsoft.Bot.Configuration;
14+
using Microsoft.Bot.Connector.Authentication;
15+
using Microsoft.Extensions.Configuration;
16+
using Microsoft.Extensions.DependencyInjection;
17+
using Microsoft.Extensions.Logging;
18+
19+
/// <summary>
20+
/// The Startup class configures services and the app's request pipeline.
21+
/// </summary>
22+
public class Startup
23+
{
24+
private ILoggerFactory _loggerFactory;
25+
private bool _isProduction = false;
26+
27+
public Startup(IHostingEnvironment env)
28+
{
29+
_isProduction = env.IsProduction();
30+
31+
var builder = new ConfigurationBuilder()
32+
.SetBasePath(env.ContentRootPath)
33+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
34+
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
35+
.AddEnvironmentVariables();
36+
37+
Configuration = builder.Build();
38+
}
39+
40+
/// <summary>
41+
/// Gets the configuration that represents a set of key/value application configuration properties.
42+
/// </summary>
43+
/// <value>
44+
/// The <see cref="IConfiguration"/> that represents a set of key/value application configuration properties.
45+
/// </value>
46+
public IConfiguration Configuration { get; }
47+
48+
/// <summary>
49+
/// This method gets called by the runtime. Use this method to add services to the container.
50+
/// </summary>
51+
/// <param name="services">Specifies the contract for a <see cref="IServiceCollection"/> of service descriptors.</param>
52+
/// <seealso cref="IStatePropertyAccessor{T}"/>
53+
/// <seealso cref="https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/>
54+
/// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/>
55+
public void ConfigureServices(IServiceCollection services)
56+
{
57+
services.AddBot<DialogInterruptionsBot>(options =>
58+
{
59+
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
60+
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
61+
62+
// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
63+
var botConfig = BotConfiguration.Load(botFilePath ?? @".\complex-dialog.bot", secretKey);
64+
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. ({botConfig})"));
65+
66+
// Retrieve current endpoint.
67+
var environment = _isProduction ? "production" : "development";
68+
var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
69+
if (!(service is EndpointService endpointService))
70+
{
71+
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
72+
}
73+
74+
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
75+
76+
// Creates a logger for the application to use.
77+
ILogger logger = _loggerFactory.CreateLogger<DialogInterruptionsBot>();
78+
79+
// Catches any errors that occur during a conversation turn and logs them.
80+
options.OnTurnError = async (context, exception) =>
81+
{
82+
logger.LogError($"Exception caught : {exception}");
83+
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
84+
};
85+
});
86+
87+
// Create conversation and user state management objects, using memory storage.
88+
IStorage dataStore = new MemoryStorage();
89+
var conversationState = new ConversationState(dataStore);
90+
var userState = new UserState(dataStore);
91+
92+
// Create and register state accessors.
93+
// Accessors created here are passed into the IBot-derived class on every turn.
94+
services.AddSingleton<DialogInterruptionsBotAccessors>(sp =>
95+
{
96+
// Create the custom state accessor.
97+
// State accessors enable other components to read and write individual properties of state.
98+
var accessors = new DialogInterruptionsBotAccessors(conversationState, userState)
99+
{
100+
DialogStateAccessor = conversationState.CreateProperty<DialogState>("DialogState"),
101+
UserProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile"),
102+
};
103+
104+
return accessors;
105+
});
106+
}
107+
108+
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
109+
{
110+
_loggerFactory = loggerFactory;
111+
112+
app.UseDefaultFiles()
113+
.UseStaticFiles()
114+
.UseBotFramework();
115+
}
116+
}
117+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.BotBuilderSamples
5+
{
6+
using System.Collections.Generic;
7+
8+
/// <summary>Contains information about a user.</summary>
9+
public class UserProfile
10+
{
11+
/// <summary>Gets or sets the user's name.</summary>
12+
/// <value>The user's name.</value>
13+
public string Name { get; set; }
14+
15+
/// <summary>Gets or sets the user's age.</summary>
16+
/// <value>The user's age.</value>
17+
public int Age { get; set; }
18+
19+
/// <summary>Gets or sets the list of companies the user wants to review.</summary>
20+
/// <value>The list of companies the user wants to review.</value>
21+
public List<string> CompaniesToReview { get; set; } = new List<string>();
22+
}
23+
}

0 commit comments

Comments
 (0)