Skip to content

Commit

Permalink
Implemented two basic BOT Powered ACEs
Browse files Browse the repository at this point in the history
  • Loading branch information
PaoloPia committed Dec 14, 2023
1 parent 68952ae commit b90d344
Show file tree
Hide file tree
Showing 69 changed files with 2,642 additions and 83 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.18.1

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

namespace BotPowered_BasicAce_CollectFeedback
{
public class AdapterWithErrorHandler : CloudAdapter
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger)
: base(auth, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
await turnContext.SendActivityAsync("The bot encountered an error or bug.");
await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code.");
// 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");
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<UserSecretsId>262d8d01-f61b-48a2-9baa-31539b075b0a</UserSecretsId>
<RootNamespace>$(MSBuildProjectName.Replace(" ", "_").Replace("-", "_"))</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AdaptiveCards.Templating" Version="1.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.21.2" />
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33829.357
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotPowered-BasicAce-CollectFeedback", "BotPowered-BasicAce-CollectFeedback.csproj", "{C4EF0E19-232D-4F9B-ADB6-84B07D37C473}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C4EF0E19-232D-4F9B-ADB6-84B07D37C473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4EF0E19-232D-4F9B-ADB6-84B07D37C473}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4EF0E19-232D-4F9B-ADB6-84B07D37C473}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4EF0E19-232D-4F9B-ADB6-84B07D37C473}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {503AEAB0-93C3-462B-8F7E-7CF7F0EC9878}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.18.1

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

namespace BotPowered_BasicAce_CollectFeedback.Controllers
{
// 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]
[HttpGet]
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);
}
}
}
248 changes: 248 additions & 0 deletions samples/BotPowered-BasicAce-CollectFeedback/FeedbackBot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using AdaptiveCards;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.SharePoint;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.SharePoint;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.IO;
using AdaptiveCards.Templating;

namespace BotPowered_BasicAce_CollectFeedback
{
public class FeedbackBot : SharePointActivityHandler
{
private static string adaptiveCardExtensionId = Guid.NewGuid().ToString();
private IConfiguration configuration = null;
public readonly string baseUrl;

private static ConcurrentDictionary<string, CardViewResponse> cardViews = new ConcurrentDictionary<string, CardViewResponse>();

private static string CollectFeedbackCardView_ID = "GET_FEEDBACK_CARD_VIEW";
private static string OkFeedbackCardView_ID = "OK_FEEDBACK_CARD_VIEW";
private static string FeedbackQuickView_ID = "FEEDBACK_QUICK_VIEW";

public FeedbackBot(IConfiguration configuration)
: base()
{
this.configuration = configuration;
this.baseUrl = configuration["BaseUrl"];

// Add the CardViews
var aceData = new AceData()
{
Title = "Your voice matters!",
CardSize = AceData.AceCardSize.Large,
DataVersion = "1.0",
Id = adaptiveCardExtensionId
};

// Collect Feedback Card View (Input Text Card View)
CardViewResponse feedbackCardViewResponse = new CardViewResponse();
feedbackCardViewResponse.AceData = aceData;
feedbackCardViewResponse.CardViewParameters = CardViewParameters.TextInputCardViewParameters(
new CardBarComponent()
{
Id = "FeedbackCardView",
},
new CardTextComponent()
{
Text = "Please, provide feedback"
},
new CardTextInputComponent()
{
Id = "feedbackValue",
Placeholder = "Your feedback ..."
},
new List<CardButtonComponent>()
{
new CardButtonComponent()
{
Id = "SendFeedback",
Title = "Submit",
Action = new SubmitAction()
{
Parameters = new Dictionary<string, object>()
{
{"viewToNavigateTo", OkFeedbackCardView_ID},
{"dateTimeFeedback", DateTime.Now.ToString()}
}
}
}
},
new Microsoft.Bot.Schema.SharePoint.CardImage()
{
Image = $"{baseUrl}/Media/Collect-Feedback.png",
AltText = "Collect feedback picture"
});
feedbackCardViewResponse.ViewId = CollectFeedbackCardView_ID;

feedbackCardViewResponse.OnCardSelection = new QuickViewAction()
{
Parameters = new QuickViewActionParameters()
{
View = FeedbackQuickView_ID
}
};

cardViews.TryAdd(feedbackCardViewResponse.ViewId, feedbackCardViewResponse);

// OK Feedback Card View (Image Card View)
CardViewResponse okFeedbackCardViewResponse = new CardViewResponse();
okFeedbackCardViewResponse.AceData = aceData;
okFeedbackCardViewResponse.CardViewParameters = CardViewParameters.ImageCardViewParameters(
new CardBarComponent()
{
Id = "OkFeedbackCardView",
},
new CardTextComponent()
{
Text = "Here is your feedback '<feedback>' collected on '<dateTimeFeedback>'"
},
new List<BaseCardComponent>()
{
new CardButtonComponent()
{
Id = "OkButton",
Title = "Ok",
Action = new SubmitAction()
{
Parameters = new Dictionary<string, object>()
{
{"viewToNavigateTo", CollectFeedbackCardView_ID}
}
}
}
},
new Microsoft.Bot.Schema.SharePoint.CardImage()
{
Image = $"{baseUrl}/Media/Ok-Feedback.png",
AltText = "Feedback collected"
});
okFeedbackCardViewResponse.ViewId = OkFeedbackCardView_ID;

okFeedbackCardViewResponse.OnCardSelection = new QuickViewAction()
{
Parameters = new QuickViewActionParameters()
{
View = FeedbackQuickView_ID
}
};

cardViews.TryAdd(okFeedbackCardViewResponse.ViewId, okFeedbackCardViewResponse);
}

protected override Task<CardViewResponse> OnSharePointTaskGetCardViewAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
return Task.FromResult(cardViews[CollectFeedbackCardView_ID]);
}

protected override Task<QuickViewResponse> OnSharePointTaskGetQuickViewAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
// Add the Feedback QuickViews
QuickViewResponse feedbackQuickViewResponse = new QuickViewResponse();
feedbackQuickViewResponse.Title = "Your feedback";
feedbackQuickViewResponse.Template = new AdaptiveCard("1.5");

AdaptiveContainer container = new AdaptiveContainer();
container.Separator = true;
AdaptiveTextBlock titleText = new AdaptiveTextBlock();
titleText.Text = "Thanks for your feedback!";
titleText.Color = AdaptiveTextColor.Dark;
titleText.Weight = AdaptiveTextWeight.Bolder;
titleText.Size = AdaptiveTextSize.Large;
titleText.Wrap = true;
titleText.MaxLines = 1;
titleText.Spacing = AdaptiveSpacing.None;
container.Items.Add(titleText);

AdaptiveTextBlock dateTimeCollectedText = new AdaptiveTextBlock();
dateTimeCollectedText.Text = $"We truly appreciate your effort in providing valuable feedback to us. Thanks!";
dateTimeCollectedText.Color = AdaptiveTextColor.Dark;
dateTimeCollectedText.Size = AdaptiveTextSize.Medium;
dateTimeCollectedText.Wrap = true;
dateTimeCollectedText.MaxLines = 3;
dateTimeCollectedText.Spacing = AdaptiveSpacing.None;
container.Items.Add(dateTimeCollectedText);

feedbackQuickViewResponse.Template.Body.Add(container);

feedbackQuickViewResponse.ViewId = FeedbackQuickView_ID;

return Task.FromResult(feedbackQuickViewResponse);
}

protected override Task<GetPropertyPaneConfigurationResponse> OnSharePointTaskGetPropertyPaneConfigurationAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
return base.OnSharePointTaskGetPropertyPaneConfigurationAsync(turnContext, aceRequest, cancellationToken);
}

protected override Task<BaseHandleActionResponse> OnSharePointTaskSetPropertyPaneConfigurationAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
return base.OnSharePointTaskSetPropertyPaneConfigurationAsync(turnContext, aceRequest, cancellationToken);
}

protected override Task<BaseHandleActionResponse> OnSharePointTaskHandleActionAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
if (turnContext != null)
{
if (cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
}
Trace.Write("\n\n\nStarted to handle action.\n\n\n");
JObject actionParameters = (JObject)((JObject)turnContext.Activity.Value).Property("data").Value;

if (actionParameters["type"].ToString().Equals("Submit", StringComparison.InvariantCultureIgnoreCase) &&
actionParameters["id"].ToString().Equals("SendFeedback", StringComparison.InvariantCultureIgnoreCase))
{
CardViewHandleActionResponse response = new CardViewHandleActionResponse();

string viewToNavigateTo = actionParameters["data"]["viewToNavigateTo"].ToString();
var feedbackValue = actionParameters["data"]["feedbackValue"].ToString();
var dateTimeFeedback = DateTime.Parse(actionParameters["data"]["dateTimeFeedback"]?.ToString() ?? DateTime.MinValue.ToString());

var nextCard = cardViews[viewToNavigateTo];

// Configure title and description of task
var originalText = ((nextCard.CardViewParameters.Header.ToList())[0] as CardTextComponent).Text;
originalText = originalText.Replace("<feedback>", feedbackValue).Replace("<dateTimeFeedback>", dateTimeFeedback.ToString());
((nextCard.CardViewParameters.Header.ToList())[0] as CardTextComponent).Text = originalText;

// Set the response for the action
response.RenderArguments = cardViews[viewToNavigateTo];

Trace.Write("\n\n\nFinished handling action.\n\n\n");
return Task.FromResult<BaseHandleActionResponse>(response);
}
else if (actionParameters["type"].ToString().Equals("Submit", StringComparison.InvariantCultureIgnoreCase) &&
actionParameters["id"].ToString().Equals("OkButton", StringComparison.InvariantCultureIgnoreCase))
{
CardViewHandleActionResponse response = new CardViewHandleActionResponse();

string viewToNavigateTo = actionParameters["data"]["viewToNavigateTo"].ToString();

// Set the response for the action
response.RenderArguments = cardViews[viewToNavigateTo];

Trace.Write("\n\n\nFinished handling action.\n\n\n");
return Task.FromResult<BaseHandleActionResponse>(response);
}

Trace.Write("\n\n\nFinished handling action.\n\n\n");
return Task.FromResult<BaseHandleActionResponse>(new NoOpHandleActionResponse());
}
}
}
25 changes: 25 additions & 0 deletions samples/BotPowered-BasicAce-CollectFeedback/Program.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.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.18.1

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

namespace BotPowered_BasicAce_CollectFeedback
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Loading

0 comments on commit b90d344

Please sign in to comment.