diff --git a/.github/workflows/build-complete-samples.yml b/.github/workflows/build-complete-samples.yml index 9e357bec34..f803d172bf 100644 --- a/.github/workflows/build-complete-samples.yml +++ b/.github/workflows/build-complete-samples.yml @@ -414,7 +414,7 @@ jobs: - project_path: 'samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdaptiveCardActions.csproj' name: 'bot-adaptive-card-actions' - version: '6.0.x' + version: '10.0.x' - project_path: 'samples/bot-formatting-cards/csharp/BotFormattingCards/BotFormattingCards.csproj' name: 'bot-formatting-cards' diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.sln b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.sln deleted file mode 100644 index b02105531a..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.sln +++ /dev/null @@ -1,37 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33205.214 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCardActions", "AdaptiveCardActions\AdaptiveCardActions.csproj", "{BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}" -EndProject -Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "M365Agent", "M365Agent\M365Agent.ttkproj", "{BD0063E4-061D-459C-A143-9805F2A64B99}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6794E91B-8779-453A-8802-7CD6A1B3A07A}" - ProjectSection(SolutionItems) = preProject - AdaptiveCardActions.slnLaunch.user = AdaptiveCardActions.slnLaunch.user - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Release|Any CPU.Build.0 = Release|Any CPU - {BD0063E4-061D-459C-A143-9805F2A64B99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD0063E4-061D-459C-A143-9805F2A64B99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD0063E4-061D-459C-A143-9805F2A64B99}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {BD0063E4-061D-459C-A143-9805F2A64B99}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD0063E4-061D-459C-A143-9805F2A64B99}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {8F7F570D-74C3-4CF3-AACD-361A3D3F8E37} - EndGlobalSection -EndGlobal diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnLaunch.user b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnLaunch.user index d4e29b3a8b..822a11cead 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnLaunch.user +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnLaunch.user @@ -1,31 +1,52 @@ [ { - "Name": "Microsoft Teams (browser)", + "Name": "Microsoft 365 Agents Playground (browser)", "Projects": [ { - "Path": "AdaptiveCardActions\\AdaptiveCardActions.csproj", - "Action": "Start", - "DebugTarget": "Start Project" + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft 365 Agents Playground (browser)" }, { - "Path": "M365Agent\\M365Agent.ttkproj", - "Action": "StartWithoutDebugging", - "DebugTarget": "Microsoft Teams (Browser)" + "Path": "AdaptiveCardActions\\AdaptiveCardActions.csproj", + "Name": "AdaptiveCardActions\\AdaptiveCardActions.csproj", + "Action": "Start", + "DebugTarget": "Microsoft 365 Agents Playground" } ] }, { - "Name": "Microsoft Teams (browser) (skip update app)", + "Name": "Microsoft Teams (browser)", "Projects": [ + { + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser)" + }, { "Path": "AdaptiveCardActions\\AdaptiveCardActions.csproj", + "Name": "AdaptiveCardActions\\AdaptiveCardActions.csproj", "Action": "Start", "DebugTarget": "Start Project" - }, + } + ] + }, + { + "Name": "Microsoft Teams (browser) (skip update app)", + "Projects": [ { - "Path": "M365Agent\\M365Agent.ttkproj", + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", "Action": "StartWithoutDebugging", "DebugTarget": "Microsoft Teams (browser) (skip update app)" + }, + { + "Path": "AdaptiveCardActions\\AdaptiveCardActions.csproj", + "Name": "AdaptiveCardActions\\AdaptiveCardActions.csproj", + "Action": "Start", + "DebugTarget": "Start Project" } ] } diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnx b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnx new file mode 100644 index 0000000000..bce8c6a16e --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions.slnx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/.gitignore b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/.gitignore index 7466c01f9c..c7b7dddfd8 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/.gitignore +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/.gitignore @@ -5,9 +5,11 @@ env/.env.*.user env/.env.local appsettings.Development.json .deployment +appsettings.Playground.json # User-specific files *.user +launchSettings.json # Build results [Dd]ebug/ @@ -22,4 +24,8 @@ bld/ [Ll]og/ # Notification local store -.notification.localstore.json \ No newline at end of file +.notification.localstore.json +.notification.playgroundstore.json + +# devTools +devTools/ \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdapterWithErrorHandler.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdapterWithErrorHandler.cs deleted file mode 100644 index 95bb27d622..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdapterWithErrorHandler.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Bot.Builder.TraceExtensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Microsoft.BotBuilderSamples -{ - public class AdapterWithErrorHandler : CloudAdapter - { - public AdapterWithErrorHandler(IConfiguration configuration, IHttpClientFactory httpClientFactory, ILogger logger, ConversationState conversationState = default) - : base(configuration, httpClientFactory, 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}"); - - // Uncomment below commented line for local debugging. - // await turnContext.SendActivityAsync($"Sorry, it looks like something went wrong. Exception Caught: {exception.Message}"); - - 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"); - }; - } - } -} diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdaptiveCardActions.csproj b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdaptiveCardActions.csproj index 1ad03a0ac0..2b2c429459 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdaptiveCardActions.csproj +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdaptiveCardActions.csproj @@ -1,25 +1,31 @@ - + - net6.0 - latest + net10.0 + enable + enable - - - - + + + + + + - - Always + + + PreserveNewest + None + + + + PreserveNewest + None - - - - - - + + \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Bots/AdaptiveCardActionsBot.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Bots/AdaptiveCardActionsBot.cs deleted file mode 100644 index 01469d4ae5..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Bots/AdaptiveCardActionsBot.cs +++ /dev/null @@ -1,159 +0,0 @@ -// 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.Schema; -using Newtonsoft.Json; -using AdaptiveCards.Templating; -using System; - -namespace Microsoft.BotBuilderSamples -{ - // This bot will respond to the user's input with suggested actions. - // Suggested actions enable your bot to present buttons that the user - // can tap to provide input. - public class AdaptiveCardActionsBot : ActivityHandler - { - private const string CommandString = "Please use one of these commands: **Card Actions** for Adaptive Card Actions, **Suggested Actions** for Bot Suggested Actions and **ToggleVisibility** for Action ToggleVisible Card"; - - /// - /// provide logic for when members other than the bot join the conversation - /// - protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) - { - // Send a welcome message to the user and tell them what actions they may perform to use this bot - var welcomeText = "Hello and Welcome!"; - - // Sends an activity to the sender of the incoming activity. - await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText), cancellationToken); - await turnContext.SendActivityAsync(MessageFactory.Text(CommandString), cancellationToken); - } - - /// - /// provide logic specific to Message activities, - /// - protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - if (turnContext.Activity.Text != null) - { - // Extract the text from the message activity the user sent. - var text = turnContext.Activity.Text.ToLowerInvariant(); - - if (text.Contains("card actions")) - { - await SendAdaptiveCardAsync(turnContext, cancellationToken, "AdaptiveCardActions.json"); - } - else if (text.Contains("suggested actions")) - { - // Respond to the user. - await turnContext.SendActivityAsync("Please Enter a color from the suggested action choices", cancellationToken: cancellationToken); - await SendAdaptiveCardAsync(turnContext, cancellationToken, "SuggestedActions.json"); - // Sends a suggested action card - await SendSuggestedActionsAsync(turnContext, cancellationToken); - } - else if (text.Contains("togglevisibility")) - { - await SendAdaptiveCardAsync(turnContext, cancellationToken, "ToggleVisibleCard.json"); - } - else if (text.Contains("red") || text.Contains("blue") || text.Contains("yellow")) - { - var responseText = ProcessInput(text); - await turnContext.SendActivityAsync(responseText, cancellationToken: cancellationToken); - await SendSuggestedActionsAsync(turnContext, cancellationToken); - } - else - { - await turnContext.SendActivityAsync(MessageFactory.Text(CommandString), cancellationToken); - } - } - await SendDataOnCardActionsAsync(turnContext, cancellationToken); - } - - /// - /// ProcessInput takes input string and returns message - /// - private static string ProcessInput(string text) - { - const string colorText = "is the best color, I agree."; - var colorResponses = new Dictionary - { - { "red", $"Red {colorText}" }, - { "yellow", $"Yellow {colorText}" }, - { "blue", $"Blue {colorText}" } - }; - - return colorResponses.TryGetValue(text, out var response) ? response : "Please select a color from the suggested action choices"; - } - - /// - /// Creates and sends an activity with suggested actions to the user. When the user - /// clicks one of the buttons the text value from the "CardAction" will be - /// displayed in the channel just as if the user entered the text. There are multiple - /// "ActionTypes" that may be used for different situations. - /// - private static async Task SendSuggestedActionsAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - var reply = MessageFactory.Text("What is your favorite color?"); - reply.SuggestedActions = new SuggestedActions - { - Actions = new List - { - new CardAction { Title = "Red", Type = ActionTypes.ImBack, Value = "Red" }, - new CardAction { Title = "Yellow", Type = ActionTypes.ImBack, Value = "Yellow" }, - new CardAction { Title = "Blue", Type = ActionTypes.ImBack, Value = "Blue" } - }, - To = new List { turnContext.Activity.From.Id } - }; - - await turnContext.SendActivityAsync(reply, cancellationToken); - } - - /// - /// sends the response on card action.submit - /// - private async Task SendDataOnCardActionsAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - if (turnContext.Activity.Value != null) - { - var reply = MessageFactory.Text($"Data Submitted: {turnContext.Activity.Value}"); - await turnContext.SendActivityAsync(reply, cancellationToken); - } - } - - /// - /// Sends an adaptive card to the user. - /// - private async Task SendAdaptiveCardAsync(ITurnContext turnContext, CancellationToken cancellationToken, string cardFileName) - { - string[] path = { ".", "Cards", cardFileName }; - var adaptiveCard = GetFirstOptionsAdaptiveCard(path, turnContext.Activity.From.Name); - await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCard), cancellationToken); - } - - /// - /// Get the initial card - /// - private Attachment GetFirstOptionsAdaptiveCard(string[] filepath, string name = null, string userMRI = null) - { - var adaptiveCardJson = File.ReadAllText(Path.Combine(filepath)); - var template = new AdaptiveCardTemplate(adaptiveCardJson); - var payloadData = new - { - createdById = userMRI, - createdBy = name - }; - - // "Expand" the template - this generates the final Adaptive Card payload - var cardJsonString = template.Expand(payloadData); - return new Attachment - { - ContentType = "application/vnd.microsoft.card.adaptive", - Content = JsonConvert.DeserializeObject(cardJsonString) - }; - } - } -} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/AdaptiveCardActions.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/AdaptiveCardActions.json deleted file mode 100644 index 284b6e8ffa..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/AdaptiveCardActions.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "TextBlock", - "text": "Adaptive Card Actions" - } - ], - "actions": [ - { - "type": "Action.OpenUrl", - "title": "Action Open URL", - "url": "https://adaptivecards.io" - }, - { - "type": "Action.ShowCard", - "title": "Action Submit", - "card": { - "type": "AdaptiveCard", - "version": "1.5", - "body": [ - { - "type": "Input.Text", - "id": "name", - "label": "Please enter your name:", - "isRequired": true, - "errorMessage": "Name is required" - } - ], - "actions": [ - { - "type": "Action.Submit", - "title": "Submit" - } - ] - } - }, - { - "type": "Action.ShowCard", - "title": "Action ShowCard", - "card": { - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "TextBlock", - "text": "This card's action will show another card" - } - ], - "actions": [ - { - "type": "Action.ShowCard", - "title": "Action.ShowCard", - "card": { - "type": "AdaptiveCard", - "body": [ - { - "type": "TextBlock", - "text": "Welcome To New Card" - } - ], - "actions": [ - { - "type": "Action.Submit", - "title": "Click Me", - "data": { - "value": "Button has Clicked" - } - } - ] - } - } - ] - } - } - ] -} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/SuggestedActions.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/SuggestedActions.json deleted file mode 100644 index 9cd5906877..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/SuggestedActions.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "TextBlock", - "text": "**Welcome to bot Suggested actions**" - }, - { - "type": "TextBlock", - "text": "please use below commands, to get response form the bot." - }, - { - "type": "TextBlock", - "text": "- Red \r- Blue \r - Yellow", - "wrap": true - } - ] -} diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/ToggleVisibleCard.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/ToggleVisibleCard.json deleted file mode 100644 index 73ac0b2fce..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Cards/ToggleVisibleCard.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "TextBlock", - "text": "**Action.ToggleVisibility example**: click the button to show or hide a welcome message" - }, - { - "type": "TextBlock", - "id": "helloWorld", - "isVisible": false, - "text": "**Hello World!**", - "size": "extraLarge" - } - ], - "actions": [ - { - "type": "Action.ToggleVisibility", - "title": "Click me!", - "targetElements": [ "helloWorld" ] - } - ] -} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Config.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Config.cs new file mode 100644 index 0000000000..d17b2075f5 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Config.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace AdaptiveCardActions +{ + public class ConfigOptions + { + public TeamsConfigOptions Teams { get; set; } + } + + public class TeamsConfigOptions + { + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public string TenantId { get; set; } + } +} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/BotController.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/BotController.cs deleted file mode 100644 index a54f5b3be2..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/BotController.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; - -namespace Microsoft.BotBuilderSamples -{ - /// - /// 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; - - /// - /// Initializes a new instance of the class. - /// - /// The bot framework HTTP adapter. - /// The bot instance. - public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } - - /// - /// Handles the HTTP POST request to process bot messages. - /// - /// A task that represents the asynchronous operation. - [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); - } - } -} diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/Controller.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/Controller.cs new file mode 100644 index 0000000000..ac7f01b7b8 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Controllers/Controller.cs @@ -0,0 +1,266 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Teams.Api.Activities; +using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Activities; +using Microsoft.Teams.Apps.Annotations; +using Microsoft.Teams.Cards; +using Microsoft.Teams.Api; +using Microsoft.Teams.Api.Cards; +using Microsoft.Teams.Common; + +namespace AdaptiveCardActions.Controllers +{ + [TeamsController] + public class Controller() + { + private const string CommandString = "Please use one of these commands: **Card Actions** for Adaptive Card Actions, **Suggested Actions** for Bot Suggested Actions and **ToggleVisibility** for Action ToggleVisible Card"; + + // Handles incoming message activities from users + [Message] + public async Task OnMessage([Context] MessageActivity activity, [Context] IContext.Client client, [Context] Microsoft.Teams.Common.Logging.ILogger log) + { + log.Info("Message received"); + + // Handle card action submissions (when activity.Value is not null) + if (activity.Value != null) + { + log.Info($"Data submitted: {activity.Value}"); + await client.Send($"Data Submitted: {activity.Value}"); + return; + } + + if (activity.Text != null) + { + var normalizedText = activity.Text.Trim().ToLowerInvariant(); + + if (normalizedText.Contains("card actions")) + { + await SendAdaptiveCardActionsAsync(client); + } + else if (normalizedText.Contains("suggested actions")) + { + // Respond to the user before sending suggested actions + await client.Send("Please Enter a color from the suggested action choices"); + await SendSuggestedActionsCardAsync(client); + // Send native Teams SDK suggested actions + await SendSuggestedActionsAsync(client, log); + } + else if (normalizedText.Contains("togglevisibility")) + { + await SendToggleVisibilityCardAsync(client); + } + else if (normalizedText.Contains("red") || normalizedText.Contains("blue") || normalizedText.Contains("yellow")) + { + var responseText = ProcessInput(normalizedText); + await client.Send(responseText); + // Send suggested actions again after color selection + await SendSuggestedActionsAsync(client, log); + } + else + { + await client.Send(CommandString); + } + } + } + + // Handles when new members are added to the conversation + [Microsoft.Teams.Apps.Activities.Conversation.MembersAdded] + public async Task OnMembersAdded(IContext context) + { + var welcomeText = "Hello and Welcome!"; + + foreach (var member in context.Activity.MembersAdded) + { + if (member.Id != context.Activity.Recipient?.Id) + { + await context.Send(welcomeText); + await context.Send(CommandString); + } + } + } + + // Processes user input and returns appropriate color response + private static string ProcessInput(string text) + { + const string colorText = "is the best color, I agree."; + var colorResponses = new Dictionary + { + { "red", $"Red {colorText}" }, + { "yellow", $"Yellow {colorText}" }, + { "blue", $"Blue {colorText}" } + }; + + return colorResponses.TryGetValue(text, out var response) ? response : "Please select a color from the suggested action choices"; + } + + // Sends the Adaptive Card Actions card using Microsoft.Teams.Cards API + private async Task SendAdaptiveCardActionsAsync(IContext.Client client) + { + // Build the innermost card for the nested ShowCard action + var nestedCard = new AdaptiveCard + { + Body = new List + { + new TextBlock("Welcome To New Card") + }, + Actions = new List + { + new SubmitAction + { + Title = "Click Me", + Data = new Union("{\"value\": \"Button has Clicked\"}") + } + } + }; + + // Build the middle card for the Action ShowCard + var showCard = new AdaptiveCard + { + Body = new List + { + new TextBlock("This card's action will show another card") + }, + Actions = new List + { + new ShowCardAction + { + Title = "Action.ShowCard", + Card = nestedCard + } + } + }; + + // Build the card for Action Submit with input field + var submitCard = new AdaptiveCard + { + Version = new Microsoft.Teams.Cards.Version("1.5"), + Body = new List + { + new TextInput + { + Id = "name", + Label = "Please enter your name:", + IsRequired = true, + ErrorMessage = "Name is required" + } + }, + Actions = new List + { + new SubmitAction { Title = "Submit" } + } + }; + + // Build the main card with all actions + var card = new AdaptiveCard + { + Body = new List + { + new TextBlock("Adaptive Card Actions") + }, + Actions = new List + { + new OpenUrlAction("https://adaptivecards.io") + { + Title = "Action Open URL" + }, + new ShowCardAction + { + Title = "Action Submit", + Card = submitCard + }, + new ShowCardAction + { + Title = "Action ShowCard", + Card = showCard + } + } + }; + + await client.Send(card); + } + + // Sends the Suggested Actions card using Microsoft.Teams.Cards API + private async Task SendSuggestedActionsCardAsync(IContext.Client client) + { + var card = new AdaptiveCard + { + Body = new List + { + new TextBlock("**Welcome to bot Suggested actions**"), + new TextBlock("please use below commands, to get response form the bot."), + new TextBlock("- Red \r- Blue \r - Yellow") + { + Wrap = true + } + } + }; + + await client.Send(card); + } + + // Sends the Toggle Visibility card using Microsoft.Teams.Cards API + private async Task SendToggleVisibilityCardAsync(IContext.Client client) + { + var card = new AdaptiveCard + { + Body = new List + { + new TextBlock("**Action.ToggleVisibility example**: click the button to show or hide a welcome message"), + new TextBlock("**Hello World!**") + { + Id = "helloWorld", + IsVisible = false, + Size = new TextSize("extraLarge") + } + }, + Actions = new List + { + new ToggleVisibilityAction + { + Title = "Click me!", + TargetElements = new Union, IList>((IList)new List { "helloWorld" }) + } + } + }; + + await client.Send(card); + } + + /// + /// Creates and sends a message with suggested actions to the user. + /// Uses native Teams SDK SuggestedActions with IMBack action type. + /// When the user clicks a button, the text value will be displayed in the channel as if the user typed it, + /// and will automatically trigger the OnMessage handler. + /// + private async Task SendSuggestedActionsAsync(IContext.Client client, Microsoft.Teams.Common.Logging.ILogger? log) + { + log?.Info("Sending suggested actions"); + + // Create MessageActivity with SuggestedActions using native Teams SDK + var message = new MessageActivity() + { + Text = "What is your favorite color?", + SuggestedActions = new Microsoft.Teams.Api.SuggestedActions() + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "Red", + Value = "Red" + }) + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "Yellow", + Value = "Yellow" + }) + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "Blue", + Value = "Blue" + }) + }; + + await client.Send(message); + } + } +} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Program.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Program.cs index e2aa261a84..29bfd594af 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Program.cs +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Program.cs @@ -1,44 +1,28 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using AdaptiveCardActions; +using AdaptiveCardActions.Controllers; +using Azure.Core; +using Azure.Identity; +using Microsoft.Teams.Api.Auth; +using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Extensions; +using Microsoft.Teams.Common.Http; +using Microsoft.Teams.Plugins.AspNetCore.Extensions; -namespace Microsoft.BotBuilderSamples -{ - /// - /// The main entry point for the application. - /// - public class Program - { - /// - /// The main method which is the entry point of the application. - /// - /// The command-line arguments. - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +// Create web application builder and load configuration +var builder = WebApplication.CreateBuilder(args); +var config = builder.Configuration.Get(); - /// - /// Creates and configures a host builder. - /// - /// The command-line arguments. - /// An initialized instance. - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - // Configure logging to include debug and console outputs - webBuilder.ConfigureLogging(logging => - { - logging.AddDebug(); - logging.AddConsole(); - }); +// Create Teams app builder +var appBuilder = App.Builder(); - // Specify the startup class to use - webBuilder.UseStartup(); - }); - } -} +// Register controller and configure Teams services +builder.Services.AddSingleton(); +builder.AddTeams(appBuilder); + +// Build and run the application +var app = builder.Build(); +app.UseTeams(); +app.Run(); \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.example.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.example.json new file mode 100644 index 0000000000..3bc05a1b19 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.example.json @@ -0,0 +1,26 @@ +{ + "profiles": { + // Debug project within Microsoft 365 Agents Playground + "Microsoft 365 Agents Playground": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:3978", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Playground", + "TEAMSFX_NOTIFICATION_STORE_FILENAME": ".notification.playgroundstore.json", + "UPDATE_TEAMS_APP": "false" + }, + "hotReloadProfile": "aspnetcore" + }, + // Debug project within Teams + "Start Project": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:3978", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "hotReloadProfile": "aspnetcore" + }, + } +} diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.json deleted file mode 100644 index d985e43c6f..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Properties/launchSettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "profiles": { - "Start Project": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7130;http://localhost:5130", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "hotReloadProfile": "aspnetcore" - } - } -} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Startup.cs b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Startup.cs deleted file mode 100644 index dc0d8008e5..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/Startup.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.BotBuilderSamples -{ - /// - /// Configures services and the app's request pipeline. - /// - public class Startup - { - /// - /// This method gets called by the runtime. Use this method to add services to the container. - /// - /// The collection of service descriptors. - public void ConfigureServices(IServiceCollection services) - { - // Add HTTP client and controllers with NewtonsoftJson support - services.AddHttpClient().AddControllers().AddNewtonsoftJson(); - - // Create the Bot Framework Adapter with error handling enabled. - services.AddSingleton(); - - // Create the bot as a transient. In this case, the ASP Controller is expecting an IBot. - services.AddTransient(); - } - - /// - /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - /// - /// The application builder. - /// The web hosting environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - // Use developer exception page in development environment - app.UseDeveloperExceptionPage(); - } - - // Configure the HTTP request pipeline - app.UseDefaultFiles() - .UseStaticFiles() - .UseRouting() - .UseAuthorization() - .UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Development.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Development.json index e203e9407e..8dadbe2c24 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Development.json +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Development.json @@ -1,9 +1,18 @@ { - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "", + "TenantId": "" + } +} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Playground.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Playground.json new file mode 100644 index 0000000000..5981a18df5 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.Playground.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "", + "TenantId": "" + } +} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.json b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.json index 92895b6d88..41145a092b 100644 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.json +++ b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/appsettings.json @@ -1,4 +1,17 @@ { - "MicrosoftAppId": "<>", - "MicrosoftAppPassword": "<>" -} + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "" + } +} \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/wwwroot/default.htm b/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/wwwroot/default.htm deleted file mode 100644 index 17d4d404c9..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/wwwroot/default.htm +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - - Adaptive Card Sample - - - - - -
-
-
-
Adaptive Card Actions Sample
-
-
-
-
-
Your bot is ready!
-
You can test your bot in the Bot Framework Emulator
- by connecting to http://localhost:3978/api/messages.
- -
Visit Azure - Bot Service to register your bot and add it to
- various channels. The bot's endpoint URL typically looks - like this:
-
https://your_bots_hostname/api/messages
-
-
-
-
- -
- - diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/.gitignore b/samples/bot-adaptive-card-actions/csharp/M365Agent/.gitignore new file mode 100644 index 0000000000..c5cae9258c --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/.gitignore @@ -0,0 +1,10 @@ +# TeamsFx files +build +appPackage/build +env/.env.*.user +env/.env.local +appsettings.Development.json +.deployment + +# User-specific files +*.user diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.atkproj b/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.atkproj new file mode 100644 index 0000000000..124eb75046 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.atkproj @@ -0,0 +1,9 @@ + + + + b069b3bd-f6bc-cc40-82ab-3fcc2ea50fdf + + + + + \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.ttkproj b/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.ttkproj deleted file mode 100644 index e3ffdc6c0c..0000000000 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/M365Agent.ttkproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - bd0063e4-061d-459c-a143-9805f2a64b99 - - - - - - - - - - \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/aad.manifest.json b/samples/bot-adaptive-card-actions/csharp/M365Agent/aad.manifest.json index 74b6608889..7bd8212f10 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/aad.manifest.json +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/aad.manifest.json @@ -1,6 +1,6 @@ { - "id": "${{AAD_APP_OBJECT_ID}}", - "appId": "${{AAD_APP_CLIENT_ID}}", + "id": "${{BOT_OBJECT_ID}}", + "appId": "${{BOT_ID}}", "displayName": "bot-adaptive-card-actions-aad", "signInAudience": "AzureADMyOrg", "api": { diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/color.png b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/color.png index b8cf81afbe..01aa37e347 100644 Binary files a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/color.png and b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/color.png differ diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/manifest.json b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/manifest.json index 073256fbbd..13e82a26bf 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/manifest.json +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/manifest.json @@ -1,4 +1,4 @@ -{ +{ "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.22/MicrosoftTeams.schema.json", "manifestVersion": "1.22", "version": "1.0.0", @@ -24,7 +24,7 @@ "accentColor": "#60A18E", "bots": [ { - "botId": "${{AAD_APP_CLIENT_ID}}", + "botId": "${{BOT_ID}}", "scopes": [ "personal", "copilot" @@ -40,7 +40,7 @@ "customEngineAgents": [ { "type": "bot", - "id": "${{AAD_APP_CLIENT_ID}}" + "id": "${{BOT_ID}}" } ] } diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/outline.png b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/outline.png index 2c3bf6fa65..f7a4c86447 100644 Binary files a/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/outline.png and b/samples/bot-adaptive-card-actions/csharp/M365Agent/appPackage/outline.png differ diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.dev b/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.dev new file mode 100644 index 0000000000..df4f9da508 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.dev @@ -0,0 +1,15 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev + +# Updating AZURE_SUBSCRIPTION_ID or AZURE_RESOURCE_GROUP_NAME after provision may also require an update to RESOURCE_SUFFIX, because some services require a globally unique name across subscriptions/resource groups. +AZURE_SUBSCRIPTION_ID= +AZURE_RESOURCE_GROUP_NAME= +RESOURCE_SUFFIX= + +# Generated during provision, you can also add your own variables. +BOT_ID= +TEAMS_APP_ID= +BOT_AZURE_APP_SERVICE_RESOURCE_ID= \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.local b/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.local index f6296d6224..86dfacc35a 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.local +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/env/.env.local @@ -7,19 +7,9 @@ APP_NAME_SUFFIX=local # Generated during provision, you can also add your own variables. BOT_ID= TEAMS_APP_ID= -RESOURCE_SUFFIX= -AAD_APP_CLIENT_ID= -AAD_APP_OBJECT_ID= -AAD_APP_TENANT_ID= -AAD_APP_OAUTH_AUTHORITY= -AAD_APP_OAUTH_AUTHORITY_HOST= TEAMS_APP_TENANT_ID= -MICROSOFT_APP_TYPE= -MICROSOFT_APP_TENANT_ID= +BOT_OBJECT_ID= TEAMSFX_M365_USER_NAME= BOT_ENDPOINT= -BOT_DOMAIN= - -AZURE_SUBSCRIPTION_ID= -AZURE_RESOURCE_GROUP_NAME= \ No newline at end of file +BOT_DOMAIN= \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.bicep b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.bicep index c3ce051b3d..658e412a21 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.bicep +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.bicep @@ -3,42 +3,84 @@ @description('Used to generate names for all resources in this file') param resourceBaseName string -@description('Required when create Azure Bot service') -param botAadAppClientId string - -param botAppDomain string +param webAppSKU string @maxLength(42) param botDisplayName string -param botServiceName string = resourceBaseName -param botServiceSku string = 'F0' -param microsoftAppType string -param microsoftAppTenantId string +param serverfarmsName string = resourceBaseName +param webAppName string = resourceBaseName +param identityName string = resourceBaseName +param location string = resourceGroup().location -// Register your web service as a bot with the Bot Framework -resource botService 'Microsoft.BotService/botServices@2021-03-01' = { - kind: 'azurebot' - location: 'global' - name: botServiceName - properties: { - displayName: botDisplayName - endpoint: 'https://${botAppDomain}/api/messages' - msaAppId: botAadAppClientId - msaAppType: microsoftAppType - msaAppTenantId: microsoftAppType == 'SingleTenant' ? microsoftAppTenantId : '' - } +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + location: location + name: identityName +} + +// Compute resources for your Web App +resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = { + kind: 'app' + location: location + name: serverfarmsName sku: { - name: botServiceSku + name: webAppSKU } } -// Connect the bot service to Microsoft Teams -resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { - parent: botService - location: 'global' - name: 'MsTeamsChannel' +// Web App that hosts your bot +resource webApp 'Microsoft.Web/sites@2021-02-01' = { + kind: 'app' + location: location + name: webAppName properties: { - channelName: 'MsTeamsChannel' + serverFarmId: serverfarm.id + httpsOnly: true + siteConfig: { + appSettings: [ + { + name: 'WEBSITE_RUN_FROM_PACKAGE' + value: '1' + } + { + name: 'Teams__ClientId' + value: identity.properties.clientId + } + { + name: 'Teams__TenantId' + value: identity.properties.tenantId + } + { + name: 'Teams__BotType' + value: 'UserAssignedMsi' + } + ] + ftpsState: 'FtpsOnly' + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${identity.id}': {} + } + } +} + +// Register your web service as a bot with the Bot Framework +module azureBotRegistration './botRegistration/azurebot.bicep' = { + name: 'Azure-Bot-registration' + params: { + resourceBaseName: resourceBaseName + identityClientId: identity.properties.clientId + identityResourceId: identity.id + identityTenantId: identity.properties.tenantId + botAppDomain: webApp.properties.defaultHostName + botDisplayName: botDisplayName } } + +// The output will be persisted in .env.{envName}. Visit https://aka.ms/teamsfx-actions/arm-deploy for more details. +output BOT_AZURE_APP_SERVICE_RESOURCE_ID string = webApp.id +output BOT_DOMAIN string = webApp.properties.defaultHostName +output BOT_ID string = identity.properties.clientId +output BOT_TENANT_ID string = identity.properties.tenantId diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.parameters.json b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.parameters.json index d8454ee752..aabb59b8af 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.parameters.json +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/azure.parameters.json @@ -1,24 +1,15 @@ { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "resourceBaseName": { - "value": "bot${{RESOURCE_SUFFIX}}" - }, - "botAadAppClientId": { - "value": "${{AAD_APP_CLIENT_ID}}" - }, - "botAppDomain": { - "value": "${{BOT_DOMAIN}}" - }, - "botDisplayName": { - "value": "TestBot" - }, - "microsoftAppType": { - "value": "${{MICROSOFT_APP_TYPE}}" - }, - "microsoftAppTenantId": { - "value": "${{MICROSOFT_APP_TENANT_ID}}" + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "bot${{RESOURCE_SUFFIX}}" + }, + "webAppSKU": { + "value": "B1" + }, + "botDisplayName": { + "value": "AdaptiveCardActions" + } } - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep new file mode 100644 index 0000000000..a5a27b8fe4 --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep @@ -0,0 +1,42 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@maxLength(42) +param botDisplayName string + +param botServiceName string = resourceBaseName +param botServiceSku string = 'F0' +param identityResourceId string +param identityClientId string +param identityTenantId string +param botAppDomain string + +// Register your web service as a bot with the Bot Framework +resource botService 'Microsoft.BotService/botServices@2021-03-01' = { + kind: 'azurebot' + location: 'global' + name: botServiceName + properties: { + displayName: botDisplayName + endpoint: 'https://${botAppDomain}/api/messages' + msaAppId: identityClientId + msaAppMSIResourceId: identityResourceId + msaAppTenantId:identityTenantId + msaAppType:'UserAssignedMSI' + } + sku: { + name: botServiceSku + } +} + +// Connect the bot service to Microsoft Teams +resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { + parent: botService + location: 'global' + name: 'MsTeamsChannel' + properties: { + channelName: 'MsTeamsChannel' + } +} diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/readme.md b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/readme.md new file mode 100644 index 0000000000..d5416243cd --- /dev/null +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/infra/botRegistration/readme.md @@ -0,0 +1 @@ +The `azurebot.bicep` module is provided to help you create Azure Bot service when you don't use Azure to host your app. If you use Azure as infrastrcture for your app, `azure.bicep` under infra folder already leverages this module to create Azure Bot service for you. You don't need to deploy `azurebot.bicep` again. \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/launchSettings.json b/samples/bot-adaptive-card-actions/csharp/M365Agent/launchSettings.json index d6491ef52c..2af8ce7a8a 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/launchSettings.json +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/launchSettings.json @@ -1,15 +1,25 @@ { - "profiles": { - // Debug project within Teams - "Microsoft Teams (browser)": { - "commandName": "Project", - "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" - }, - // Launch project within Teams without prepare app dependencies - "Microsoft Teams (browser) (skip update app)": { - "commandName": "Project", - "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, - "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" - } - } + "profiles": { + // Launch project within Microsoft 365 Agents Playground + "Microsoft 365 Agents Playground (browser)": { + "commandName": "Project", + "environmentVariables": { + "UPDATE_TEAMS_APP": "false", + "M365_AGENTS_PLAYGROUND_TARGET_SDK": "teams-ai-v2-dotnet" + }, + "launchTestTool": true, + "launchUrl": "http://localhost:56150", + }, + // Launch project within Teams + "Microsoft Teams (browser)": { + "commandName": "Project", + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}", + }, + // Launch project within Teams without prepare app dependencies + "Microsoft Teams (browser) (skip update app)": { + "commandName": "Project", + "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + } } \ No newline at end of file diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.local.yml b/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.local.yml index b2934e6f8c..e67997c8fe 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.local.yml +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.local.yml @@ -1,72 +1,66 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.8/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.8 - -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-adaptive-card-actions-csharp +version: v1.11 provision: - - uses: aadApp/create # Creates a new Azure Active Directory (AAD) app to authenticate users if the environment variable that stores clientId is empty - with: - name: bot-adaptive-card-actions-aad # Note: when you run aadApp/update, the AAD app name will be updated based on the definition in manifest. If you don't want to change the name, make sure the name in AAD manifest is the same with the name defined here. - generateClientSecret: true # If the value is false, the action will not generate client secret for you - signInAudience: "AzureADMultipleOrgs" # Multitenant - writeToEnvironmentFile: - # Write the information of created resources into environment file for the specified environment variable(s). - clientId: AAD_APP_CLIENT_ID - clientSecret: SECRET_AAD_APP_CLIENT_SECRET # Environment variable that starts with `SECRET_` will be stored to the .env.{envName}.user environment file - objectId: AAD_APP_OBJECT_ID - tenantId: AAD_APP_TENANT_ID - authority: AAD_APP_OAUTH_AUTHORITY - authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST - # Creates a Teams app - uses: teamsApp/create with: # Teams app name - name: bot-adaptive-card-actions${{APP_NAME_SUFFIX}} + name: AdaptiveCardActions${{APP_NAME_SUFFIX}} # Write the information of created resources into environment file for # the specified environment variable(s). writeToEnvironmentFile: teamsAppId: TEAMS_APP_ID - - uses: script + # Create or reuse an existing Microsoft Entra application for bot. + - uses: aadApp/create + with: + # The Microsoft Entra application's display name + name: AdaptiveCardActions${{APP_NAME_SUFFIX}} + generateClientSecret: true + generateServicePrincipal: true + signInAudience: AzureADMultipleOrgs + writeToEnvironmentFile: + # The Microsoft Entra application's client id created for bot. + clientId: BOT_ID + # The Microsoft Entra application's client secret created for bot. + clientSecret: SECRET_BOT_PASSWORD + # The Microsoft Entra application's object id created for bot. + objectId: BOT_OBJECT_ID + + # Update Microsoft Entra app with aad.manifest.json + - uses: aadApp/update with: - run: echo "::set-teamsfx-env MICROSOFT_APP_TYPE=SingleTenant"; echo - "::set-teamsfx-env MICROSOFT_APP_TENANT_ID=${{AAD_APP_TENANT_ID}}"; + manifestPath: ./aad.manifest.json + outputFilePath: ./build/aad.manifest.${{TEAMSFX_ENV}}.json # Generate runtime appsettings to JSON file - uses: file/createOrUpdateJsonFile with: - target: ../AdaptiveCardActions/appsettings.json + target: ../AdaptiveCardActions/appsettings.Development.json content: - MicrosoftAppId: ${{AAD_APP_CLIENT_ID}} - MicrosoftAppPassword: ${{SECRET_AAD_APP_CLIENT_SECRET}} - MicrosoftAppType: ${{MICROSOFT_APP_TYPE}} - MicrosoftAppTenantId: ${{MICROSOFT_APP_TENANT_ID}} - - - uses: arm/deploy # Deploy given ARM templates parallelly. - with: - subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} # The AZURE_SUBSCRIPTION_ID is a built-in environment variable. TeamsFx will ask you select one subscription if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select subscription if it's empty in this case. - resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} # The AZURE_RESOURCE_GROUP_NAME is a built-in environment variable. TeamsFx will ask you to select or create one resource group if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select or create resource grouop if it's empty in this case. - templates: - - path: ./infra/azure.bicep - parameters: ./infra/azure.parameters.json - deploymentName: Create-resources-for-bot - bicepCliVersion: v0.9.1 # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, will use bicep CLI in PATH if you remove this config. + Teams: + ClientId: ${{BOT_ID}} + ClientSecret: ${{SECRET_BOT_PASSWORD}} + TenantId: ${{TEAMS_APP_TENANT_ID}} - - uses: aadApp/update # Apply the AAD manifest to an existing AAD app. Will use the object id in manifest file to determine which AAD app to update. + # Create or update the bot registration on dev.botframework.com + - uses: botFramework/create with: - manifestPath: ./aad.manifest.json # Relative path to teamsfx folder. Environment variables in manifest will be replaced before apply to AAD app - outputFilePath: ./build/aad.manifest.${{TEAMSFX_ENV}}.json + botId: ${{BOT_ID}} + name: AdaptiveCardActions + messagingEndpoint: ${{BOT_ENDPOINT}}/api/messages + description: "" + channels: + - name: msteams # Validate using manifest schema - uses: teamsApp/validateManifest with: # Path to manifest template manifestPath: ./appPackage/manifest.json - # Build Teams app package with latest env value - uses: teamsApp/zipAppPackage with: diff --git a/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.yml b/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.yml index 93004d9346..6349e99e3d 100644 --- a/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.yml +++ b/samples/bot-adaptive-card-actions/csharp/M365Agent/m365agents.yml @@ -1,9 +1,88 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.8/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.9/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.8 +version: v1.9 -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-adaptive-card-actions-csharp +environmentFolderPath: ./env -environmentFolderPath: ./env \ No newline at end of file +# Triggered when 'teamsapp provision' is executed +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: AdaptiveCardActions${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + # AZURE_SUBSCRIPTION_ID is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select a subscription. + # Referencing other environment variables with empty values + # will skip the subscription selection prompt. + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} + # AZURE_RESOURCE_GROUP_NAME is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select or create one + # resource group. + # Referencing other environment variables with empty values + # will skip the resource group selection prompt. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} + templates: + - path: ./infra/azure.bicep # Relative path to this file + # Relative path to this yaml file. + # Placeholders will be replaced with corresponding environment + # variable before ARM deployment. + parameters: ./infra/azure.parameters.json + # Required when deploying ARM template + deploymentName: Create-resources-for-bot + # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, + # will use bicep CLI in PATH if you remove this config. + bicepCliVersion: v0.9.1 + + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + # Build Teams app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the Teams app manifest to an existing Teams app in + # Developer Portal. + # Will use the app id in manifest file to determine which Teams app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + +# Triggered when 'teamsapp deploy' is executed +deploy: + - uses: cli/runDotnetCommand + with: + args: publish --configuration Release --runtime win-x86 --self-contained + AdaptiveCardActions.csproj + workingDirectory: ../AdaptiveCardActions + # Deploy your application to Azure App Service using the zip deploy feature. + # For additional details, refer to https://aka.ms/zip-deploy-to-app-services. + - uses: azureAppService/zipDeploy + with: + # Deploy base folder + artifactFolder: bin/Release/net10.0/win-x86/publish + # The resource id of the cloud resource to be deployed to. + # This key will be generated by arm/deploy action automatically. + # You can replace it with your existing Azure Resource id + # or add it to your environment variable file. + resourceId: ${{BOT_AZURE_APP_SERVICE_RESOURCE_ID}} + workingDirectory: ../AdaptiveCardActions diff --git a/samples/bot-adaptive-card-actions/csharp/README.md b/samples/bot-adaptive-card-actions/csharp/README.md index 25a5598ded..199942b6e1 100644 --- a/samples/bot-adaptive-card-actions/csharp/README.md +++ b/samples/bot-adaptive-card-actions/csharp/README.md @@ -64,16 +64,18 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool > NOTE: When you create your app registration, you will create an App ID and App password - make sure you keep these for later. -2. Run ngrok - point to port 3978 +2. Setup dev tunnel - point to port 3978 + + Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: ```bash - ngrok http 3978 --host-header="localhost:3978" - ``` + devtunnel host -p 3978 --allow-anonymous + ``` - Alternatively, you can also use the `dev tunnels`. Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: + Alternatively, you can also use ngrok: ```bash - devtunnel host -p 3978 --allow-anonymous + ngrok http 3978 --host-header="localhost:3978" ``` 3. Register a new application in the [Microsoft Entra ID – App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) portal. @@ -83,13 +85,14 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool * Choose the **supported account types** (any account type will work) * Leave **Redirect URI** empty. * Choose **Register**. - B) On the overview page, copy and save the **Application (client) ID, Directory (tenant) ID**. You'll need those later when updating your Teams application manifest and in the appsettings.json. + B) On the overview page, copy and save the **Application (client) ID, Directory (tenant) ID**. You'll need those later when updating in the appsettings.Development.json. C) Navigate to **API Permissions**, and make sure to add the follow permissions: Select Add a permission * Select Add a permission * Select Microsoft Graph -\> Delegated permissions. * `User.Read` (enabled by default) * Click on Add permissions. Please make sure to grant the admin consent for the required permissions. + D) Under **Certificates & secrets**, create a new **Client secret** and save the value. You'll need this for the `ClientSecret` in appsettings.Development.json. 4. Setup For Code @@ -104,16 +107,19 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool - Launch Visual Studio - File -> Open Folder - Navigate to `samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions` folder - - Select `AdaptiveCardActions.sln` solution file - - - Modify the `/appsettings.json` and fill in the following details: - - `{{MicrosoftAppId}}` - Generated from Step 1 is the application app id - - `{{MicrosoftAppPassword}}` - Generated from Step 1, also referred to as Client secret + - Select `AdaptiveCardActions.slnx` solution file + + - Create your local `launchSettings.json` file in the `AdaptiveCardActions/Properties` folder. This file configures how Visual Studio launches and debugs your application, including environment variables and application URLs. Use `launchSettings.example.json` as a template to create your own configuration. + + - Modify the `/appsettings.Development.json` and fill in the following details: + - `ClientId` - Generated from Step 1 is the application app id + - `ClientSecret` - Generated from Step 1, also referred to as Client secret + - `TenantId` - Your Microsoft 365 tenant ID from Step 3 - Press `F5` to run the project 5. Setup Manifest for Teams - __*This step is specific to Teams.*__ - - **Edit** the `manifest.json` contained in the ./appPackage folder to replace your Microsoft App Id (that was created when you registered your app registration earlier) *everywhere* you see the place holder string `{{Microsoft-App-Id}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Edit** the `manifest.json` contained in the ./appPackage folder to replace your Microsoft App Id (that was created when you registered your app registration earlier) *everywhere* you see the place holder string `{{TEAMS_APP_ID}} and {{BOT_ID}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) - **Edit** the `manifest.json` for `validDomains` and replace `{{Domain-Name}}` with base Url of your domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your domain-name will be `1234.ngrok-free.app` and if you are using dev tunnels then your domain will be like: `12345.devtunnels.ms`. - **Zip** up the contents of the `appPackage` folder to create a `manifest.zip` (Make sure that zip file does not contains any subfolder otherwise you will get error while uploading your .zip package) @@ -123,8 +129,6 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool - Go to your project directory, the ./appPackage folder, select the zip folder, and choose Open. - Select Add in the pop-up dialog box. Your app is uploaded to Teams. -**Note**: If you are facing any issue in your app, please uncomment [this](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-adaptive-card-actions/csharp/AdaptiveCardActions/AdapterWithErrorHandler.cs#L28) line and put your debugger for local debug. - ## Running the sample diff --git a/samples/bot-adaptive-card-actions/csharp/assets/sample.json b/samples/bot-adaptive-card-actions/csharp/assets/sample.json index cdb3fa6354..0b2ee72946 100644 --- a/samples/bot-adaptive-card-actions/csharp/assets/sample.json +++ b/samples/bot-adaptive-card-actions/csharp/assets/sample.json @@ -9,7 +9,7 @@ "This sample demonstrates how to create and send Adaptive Cards with different action types using a Microsoft Teams bot. It includes features like submitting actions, showing cards, toggling visibility, and more." ], "creationDateTime": "2022-12-27", - "updateDateTime": "2024-10-10", + "updateDateTime": "2025-10-12", "products": [ "Teams" ],