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

Add a .NET sample with CLU #411

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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,56 @@
// 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.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples
{
public class AdapterWithErrorHandler : CloudAdapter
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger, ConversationState conversationState = default)
: base(auth, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
// NOTE: In production environment, you should consider logging this to
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
// to add telemetry capture to your bot.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);

errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);

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(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}

// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
}
14 changes: 14 additions & 0 deletions samples/csharp_dotnetcore/84.core-bot-clu/BookingDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.BotBuilderSamples
{
public class BookingDetails
{
public string Destination { get; set; }

public string Origin { get; set; }

public string TravelDate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Microsoft.BotBuilderSamples.Bots
{
public class DialogAndWelcomeBot<T> : DialogBot<T>
where T : Dialog
{
public DialogAndWelcomeBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
: base(conversationState, userState, dialog, logger)
{
}

protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in membersAdded)
{
// Greet anyone that was not the target (recipient) of this message.
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
if (member.Id != turnContext.Activity.Recipient.Id)
{
var welcomeCard = CreateAdaptiveCardAttachment();
var response = MessageFactory.Attachment(welcomeCard, ssml: "Welcome to Bot Framework!");
await turnContext.SendActivityAsync(response, cancellationToken);
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}
}
}

// Load attachment from embedded resource.
private Attachment CreateAdaptiveCardAttachment()
{
var cardResourcePath = "CoreBotCLU.Cards.welcomeCard.json";

using (var stream = GetType().Assembly.GetManifestResourceStream(cardResourcePath))
{
using (var reader = new StreamReader(stream))
{
var adaptiveCard = reader.ReadToEnd();
return new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCard),
};
}
}
}
}
}
51 changes: 51 additions & 0 deletions samples/csharp_dotnetcore/84.core-bot-clu/Bots/DialogBot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;

namespace Microsoft.BotBuilderSamples.Bots
{
// This IBot implementation can run any type of Dialog. The use of type parameterization is to allows multiple different bots
// to be run at different endpoints within the same project. This can be achieved by defining distinct Controller types
// each with dependency on distinct IBot types, this way ASP Dependency Injection can glue everything together without ambiguity.
// The ConversationState is used by the Dialog system. The UserState isn't, however, it might have been used in a Dialog implementation,
// and the requirement is that all BotState objects are saved at the end of a turn.
public class DialogBot<T> : ActivityHandler
where T : Dialog
{
protected readonly Dialog Dialog;
protected readonly BotState ConversationState;
protected readonly BotState UserState;
protected readonly ILogger Logger;

public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
{
ConversationState = conversationState;
UserState = userState;
Dialog = dialog;
Logger = logger;
}

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
await base.OnTurnAsync(turnContext, cancellationToken);

// Save any state changes that might have occurred during the turn.
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");

// Run the Dialog with the new message Activity.
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}
}
}
46 changes: 46 additions & 0 deletions samples/csharp_dotnetcore/84.core-bot-clu/Cards/welcomeCard.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Image",
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
"size": "stretch"
},
{
"type": "TextBlock",
"spacing": "medium",
"size": "default",
"weight": "bolder",
"text": "Welcome to Bot Framework!",
"wrap": true,
"maxLines": 0
},
{
"type": "TextBlock",
"size": "default",
"isSubtle": true,
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
"wrap": true,
"maxLines": 0
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Get an overview",
"url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0"
},
{
"type": "Action.OpenUrl",
"title": "Ask a question",
"url": "https://stackoverflow.com/questions/tagged/botframework"
},
{
"type": "Action.OpenUrl",
"title": "Learn how to deploy",
"url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
}
]
}
97 changes: 97 additions & 0 deletions samples/csharp_dotnetcore/84.core-bot-clu/Clu/CluApplication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace Microsoft.BotBuilderSamples.Clu
{
/// <summary>
/// Data describing a CLU application.
/// </summary>
public class CluApplication
{
/// <summary>
/// Initializes a new instance of the <see cref="CluApplication"/> class.
/// </summary>
/// <param name="projectName">CLU project name.</param>
/// <param name="deploymentName">CLU model deployment name.</param>
/// <param name="endpointKey">CLU subscription or endpoint key.</param>
/// <param name="endpoint">CLU endpoint to use like https://mytextanalyticsresource.cognitive.azure.com.</param>
public CluApplication(string projectName, string deploymentName, string endpointKey, string endpoint)
: this((projectName, deploymentName, endpointKey, endpoint))
{
}

private CluApplication(ValueTuple<string, string, string, string> props)
{
var (projectName, deploymentName, endpointKey, endpoint) = props;

if (string.IsNullOrWhiteSpace(projectName))
{
throw new ArgumentNullException("projectName value is Null or whitespace. Please use a valid projectName.");
}

if (string.IsNullOrWhiteSpace(deploymentName))
{
throw new ArgumentNullException("deploymentName value is Null or whitespace. Please use a valid deploymentName.");
}

if (string.IsNullOrWhiteSpace(endpointKey))
{
throw new ArgumentNullException("endpointKey value is Null or whitespace. Please use a valid endpointKey.");
}

if (string.IsNullOrWhiteSpace(endpoint))
{
throw new ArgumentNullException("Endpoint value is Null or whitespace. Please use a valid endpoint.");
}

if (!Guid.TryParse(endpointKey, out var _))
{
throw new ArgumentException($"\"{endpointKey}\" is not a valid CLU subscription key.");
}

if (!Uri.IsWellFormedUriString(endpoint, UriKind.Absolute))
{
throw new ArgumentException($"\"{endpoint}\" is not a valid CLU endpoint.");
}

ProjectName = projectName;
DeploymentName = deploymentName;
EndpointKey = endpointKey;
Endpoint = endpoint;
}

/// <summary>
/// Gets or sets the CLU project name.
/// </summary>
/// <value>
/// CLU project name.
/// </value>
public string ProjectName { get; set; }

/// <summary>
/// Gets or sets CLU model deployment name.
/// </summary>
/// <value>
/// CLU model deployment name.
/// </value>
public string DeploymentName { get; set; }

/// <summary>
/// Gets or sets CLU subscription or endpoint key.
/// </summary>
/// <value>
/// CLU subscription or endpoint key.
/// </value>
public string EndpointKey { get; set; }

/// <summary>
/// Gets or sets CLU endpoint like https://mytextanalyticsresource.cognitive.azure.com.
/// </summary>
/// <value>
/// CLU endpoint where application is hosted.
/// </value>
public string Endpoint { get; set; }
}
}
25 changes: 25 additions & 0 deletions samples/csharp_dotnetcore/84.core-bot-clu/Clu/CluEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Newtonsoft.Json;

namespace Microsoft.BotBuilderSamples.Clu
{
public class CluEntity
{
[JsonProperty("category")]
public string Category { get; set; }

[JsonProperty("text")]
public string Text { get; set; }

[JsonProperty("offset")]
public int Offset { get; set; }

[JsonProperty("length")]
public int Length { get; set; }

[JsonProperty("confidenceScore")]
public float ConfidenceScore { get; set; }
}
}
Loading