Skip to content

Commit

Permalink
Merge pull request #1269 from OfficeDev/v-hrajandira/SuggestedPrompts
Browse files Browse the repository at this point in the history
New Csharp sample for Create a commands menu
  • Loading branch information
Harikrishnan-MSFT authored Oct 25, 2024
2 parents 0aea61f + 45ccb0c commit 3b332ba
Show file tree
Hide file tree
Showing 39 changed files with 1,505 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build-complete-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ jobs:
name: 'tab-nested-auth'
version: '6.0.x'

- project_path: 'samples/bot-commands-menu/csharp/CommandsMenu/CommandsMenu.csproj'
name: 'bot-commands-menu'
version: '6.0.x'

fail-fast: false
name: Build All "${{ matrix.name }}" csharp
steps:
Expand Down
36 changes: 36 additions & 0 deletions samples/bot-commands-menu/csharp/CommandsMenu.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandsMenu", "CommandsMenu\CommandsMenu.csproj", "{783D6E6F-7339-4D74-89D4-B240496CC7A3}"
EndProject
Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "TeamsApp", "TeamsApp\TeamsApp.ttkproj", "{C7FD0E24-2254-4184-BF12-6C75D2752F28}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7913B50-030E-47D9-B4F6-EA57E5B5CDF4}"
ProjectSection(SolutionItems) = preProject
CommandsMenu.slnLaunch.user = CommandsMenu.slnLaunch.user
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.Build.0 = Release|Any CPU
{C7FD0E24-2254-4184-BF12-6C75D2752F28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7FD0E24-2254-4184-BF12-6C75D2752F28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7FD0E24-2254-4184-BF12-6C75D2752F28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7FD0E24-2254-4184-BF12-6C75D2752F28}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C0264D40-8A26-4CAF-9DE9-0CFF497234D8}
EndGlobalSection
EndGlobal
17 changes: 17 additions & 0 deletions samples/bot-commands-menu/csharp/CommandsMenu.slnLaunch.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"Name": "Microsoft Teams (browser)",
"Projects": [
{
"Path": "CommandsMenu\\CommandsMenu.csproj",
"Action": "Start",
"DebugTarget": "Start Project"
},
{
"Path": "TeamsApp\\TeamsApp.ttkproj",
"Action": "StartWithoutDebugging",
"DebugTarget": "Microsoft Teams (Browser)"
}
]
}
]
25 changes: 25 additions & 0 deletions samples/bot-commands-menu/csharp/CommandsMenu/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# TeamsFx files
build
appPackage/build
env/.env.*.user
env/.env.local
appsettings.Development.json
.deployment

# User-specific files
*.user

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Notification local store
.notification.localstore.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.6.2

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

namespace Microsoft.Teams.Samples.HelloWorld.Web
{
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter

Check warning on line 13 in samples/bot-commands-menu/csharp/CommandsMenu/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "bot-commands-menu" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'

Check warning on line 13 in samples/bot-commands-menu/csharp/CommandsMenu/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "bot-commands-menu" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'
{
public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger)

Check warning on line 15 in samples/bot-commands-menu/csharp/CommandsMenu/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "bot-commands-menu" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'

Check warning on line 15 in samples/bot-commands-menu/csharp/CommandsMenu/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "bot-commands-menu" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'
: base(configuration, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Uncomment below commented line for local debugging.
// await turnContext.SendActivityAsync($"Sorry, it looks like something went wrong. Exception Caught: {exception.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");
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace CommandsMenu.Bots
{
public class CommandsMenuBot : TeamsActivityHandler
{
// Paths to the adaptive card JSON templates
private readonly string _flightsDetailsCardTemplate = Path.Combine(".", "Resources", "flightsDetails.json");
private readonly string _searchHotelsCardTemplate = Path.Combine(".", "Resources", "searchHotels.json");

/// <summary>
/// Handles incoming message activity.
/// </summary>
/// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Remove the bot's mention from the message text
turnContext.Activity.RemoveRecipientMention();

if (turnContext.Activity.Text != null)
{
// Normalize the message text
var text = turnContext.Activity.Text.Trim().ToLower();
// Check if the message contains specific keywords and respond accordingly
if (text.Contains("search flights"))
await SearchFlightsReaderCardAsync(turnContext, cancellationToken);
else if (text.Contains("search hotels"))
await SearchHotelsReaderCardAsync(turnContext, cancellationToken);
else if (text.Contains("help"))
await turnContext.SendActivityAsync(MessageFactory.Text("Displays this help message."));
else if (text.Contains("best time to fly"))
await turnContext.SendActivityAsync(MessageFactory.Text("Best time to fly to London for a 5 day trip is summer."));
}
else if (turnContext.Activity.Value != null)
{
// Respond with hotel search details if the activity has a value (e.g., from an adaptive card)
await turnContext.SendActivityAsync(MessageFactory.Text("Hotel search details are: " + turnContext.Activity.Value), cancellationToken);
}
}

/// <summary>
/// Sends an adaptive card for searching flights.
/// </summary>
/// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
private async Task SearchFlightsReaderCardAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Read the adaptive card JSON template for flight details
var cardJSON = File.ReadAllText(_flightsDetailsCardTemplate);
var adaptiveCardAttachment = new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(cardJSON),
};

// Send the adaptive card as an attachment
await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCardAttachment), cancellationToken);
}

/// <summary>
/// Sends an adaptive card for searching hotels.
/// </summary>
/// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
private async Task SearchHotelsReaderCardAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Read the adaptive card JSON template for hotel search
var cardJSON = File.ReadAllText(_searchHotelsCardTemplate);
var adaptiveCardAttachment = new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(cardJSON),
};

// Send the adaptive card as an attachment
await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCardAttachment), cancellationToken);
}
}

}
28 changes: 28 additions & 0 deletions samples/bot-commands-menu/csharp/CommandsMenu/CommandsMenu.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<Compile Remove="wwwroot\**" />
<Content Remove="wwwroot\**" />
<EmbeddedResource Remove="wwwroot\**" />
<None Remove="wwwroot\**" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.11" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.18.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.10" />
</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,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2

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

namespace CommandsMenu.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]
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc;

namespace Microsoft.Teams.Samples.HelloWorld.Web.Controllers
{
public class HomeController : Controller
{
[Route("")]
public ActionResult Index()
{
return View();
}
}
}
22 changes: 22 additions & 0 deletions samples/bot-commands-menu/csharp/CommandsMenu/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2

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

namespace Microsoft.Teams.Samples.HelloWorld.Web
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"Start Project": {
"commandName": "Project",
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:3978",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"hotReloadProfile": "aspnetcore"
}
}
}
Loading

0 comments on commit 3b332ba

Please sign in to comment.