diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/AdapterWithErrorHandler.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/AdapterWithErrorHandler.cs index 876617a418..dec2c1ce26 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/AdapterWithErrorHandler.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/AdapterWithErrorHandler.cs @@ -12,23 +12,22 @@ namespace Microsoft.BotBuilderSamples { public class AdapterWithErrorHandler : CloudAdapter { - public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger logger, ConversationState conversationState = default) + // Constructor that initializes the bot framework authentication and logger. + public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger logger) : base(auth, logger) { + // Define the error handling behavior during the bot's turn. 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}"); + // Log the exception details for debugging and tracking errors. + logger.LogError(exception, $"[OnTurnError] unhandled error: {exception.Message}"); - // Uncomment below commented line for local debugging. + // For development purposes, uncomment to provide a custom error message to users locally. // 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 + // Send a trace activity to the Bot Framework Emulator for deeper debugging. await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); }; } } -} \ No newline at end of file +} diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/DialogBot.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/DialogBot.cs index 58dc3726a6..7b6c43527e 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/DialogBot.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/DialogBot.cs @@ -28,12 +28,13 @@ namespace Microsoft.BotBuilderSamples // and the requirement is that all BotState objects are saved at the end of a turn. public class DialogBot : TeamsActivityHandler where T : Dialog { - protected readonly BotState _conversationState; - protected readonly Dialog _dialog; - protected readonly ILogger _logger; - protected readonly BotState _userState; - protected string _connectionName { get; } + protected readonly BotState _conversationState; // Represents the conversation state + protected readonly Dialog _dialog; // The dialog logic to run + protected readonly ILogger _logger; // Logger for debugging and tracing + protected readonly BotState _userState; // Represents the user state + protected string _connectionName { get; } // Connection name for OAuth + // Constructor to initialize the bot with necessary dependencies public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger> logger, string connectionName) { _conversationState = conversationState; @@ -49,6 +50,7 @@ public DialogBot(ConversationState conversationState, UserState userState, T dia /// The context for the current turn. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A task that represents the work queued to execute. + // Get the sign-in link for OAuth private async Task GetSignInLinkAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var userTokenClient = turnContext.TurnState.Get(); @@ -62,13 +64,16 @@ private async Task GetSignInLinkAsync(ITurnContext turnContext, Cancella /// The context for the current turn. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A task that represents the work queued to execute. + // OnTurnAsync: Handles parallel saving of conversation and user state changes public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { await base.OnTurnAsync(turnContext, cancellationToken); - // Save any state changes that might have occurred during the turn. - await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken); - await _userState.SaveChangesAsync(turnContext, false, cancellationToken); + // Save any state changes in parallel to improve performance + await Task.WhenAll( + _conversationState.SaveChangesAsync(turnContext, false, cancellationToken), + _userState.SaveChangesAsync(turnContext, false, cancellationToken) + ); } /// @@ -77,22 +82,28 @@ private async Task GetSignInLinkAsync(ITurnContext turnContext, Cancella /// The context for the current turn. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A task that represents the work queued to execute. + // Simplified message activity handling to trigger appropriate adaptive card based on the message command protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var signInLink = await GetSignInLinkAsync(turnContext, cancellationToken).ConfigureAwait(false); - if (turnContext.Activity.Text.Contains("login")) + await HandleCommandAsync(turnContext.Activity.Text, turnContext, signInLink, cancellationToken); + } + + // Helper function to handle commands and send the appropriate adaptive card + private async Task HandleCommandAsync(string command, ITurnContext turnContext, string signInLink, CancellationToken cancellationToken) + { + var commandToFileMap = new Dictionary { - string[] path = { ".", "Resources", "options.json" }; - var member = await TeamsInfo.GetMemberAsync(turnContext, turnContext.Activity.From.Id, cancellationToken); - var initialAdaptiveCard = GetAdaptiveCardFromFileName(path, signInLink, turnContext.Activity.From.Name, member.Id); - await turnContext.SendActivityAsync(MessageFactory.Attachment(initialAdaptiveCard), cancellationToken); - } - else if (turnContext.Activity.Text.Contains("PerformSSO")) + { "login", "options.json" }, + { "PerformSSO", "AdaptiveCardWithSSOInRefresh.json" } + }; + + if (commandToFileMap.ContainsKey(command)) { - string[] path = { ".", "Resources", "AdaptiveCardWithSSOInRefresh.json" }; + string[] path = { ".", "Resources", commandToFileMap[command] }; var member = await TeamsInfo.GetMemberAsync(turnContext, turnContext.Activity.From.Id, cancellationToken); - var initialAdaptiveCard = GetAdaptiveCardFromFileName(path, signInLink, turnContext.Activity.From.Name, member.Id); - await turnContext.SendActivityAsync(MessageFactory.Attachment(initialAdaptiveCard), cancellationToken); + var adaptiveCard = GetAdaptiveCardFromFileName(path, signInLink, turnContext.Activity.From.Name, member.Id); + await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCard), cancellationToken); } else { @@ -106,6 +117,7 @@ protected override async Task OnMessageActivityAsync(ITurnContextThe context for the current turn. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A task that represents the work queued to execute. + // Override to handle invoke activities, such as OAuth and adaptive card actions protected override async Task OnInvokeActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { if (turnContext.Activity.Name == "adaptiveCard/action") @@ -118,40 +130,38 @@ protected override async Task OnInvokeActivityAsync(ITurnContext if (value["action"] == null) return null; - JObject actiondata = JsonConvert.DeserializeObject(value["action"].ToString()); + JObject actionData = JsonConvert.DeserializeObject(value["action"].ToString()); - if (actiondata["verb"] == null) + if (actionData["verb"] == null) return null; - - string verb = actiondata["verb"].ToString(); + + string verb = actionData["verb"].ToString(); JObject authentication = null; + string state = null; - // When adaptiveCard/action invoke activity from teams contains token in response to sso flow from earlier invoke. + // Check for authentication token or state if (value["authentication"] != null) { authentication = JsonConvert.DeserializeObject(value["authentication"].ToString()); } - // When adaptiveCard/action invoke activity from teams contains 6 digit state in response to nominal sign in flow from bot. - string state = null; if (value["state"] != null) { state = value["state"].ToString(); } - // authToken and state are absent, handle verb + // Token and state are absent, initiate SSO if (authentication == null && state == null) { - switch (verb) - { // when token is absent in the invoke. We can initiate SSO in response to the invoke - case "initiateSSO": - return await initiateSSOAsync(turnContext, cancellationToken); + if (verb == "initiateSSO") + { + return await InitiateSSOAsync(turnContext, cancellationToken); } } else { - return createAdaptiveCardInvokeResponseAsync(authentication, state); + return CreateAdaptiveCardInvokeResponseAsync(authentication, state); } } @@ -169,36 +179,26 @@ protected override async Task OnInvokeActivityAsync(ITurnContext /// Refresh type /// AdaptiveCardResponse.json /// A task that represents the work queued to execute. - private InvokeResponse createAdaptiveCardInvokeResponseAsync(JObject authentication, string state, bool isBasicRefresh = false, string fileName = "AdaptiveCardResponse.json") + private InvokeResponse CreateAdaptiveCardInvokeResponseAsync(JObject authentication, string state, bool isBasicRefresh = false, string fileName = "AdaptiveCardResponse.json") { - // Verify token is present or not. - - bool isTokenPresent = authentication != null ? true : false; - bool isStatePresent = state != null && state != "" ? true : false; - - string[] filepath = { ".", "Resources", fileName }; + string authResultData = (authentication != null) ? "SSO success" : (state != null && state != "") ? "OAuth success" : "SSO/OAuth failed"; - var adaptiveCardJson = File.ReadAllText(Path.Combine(filepath)); - AdaptiveCardTemplate template = new AdaptiveCardTemplate(adaptiveCardJson); - var authResultData = isTokenPresent ? "SSO success" : isStatePresent ? "OAuth success" : "SSO/OAuth failed"; - if (isBasicRefresh) { authResultData = "Refresh done"; } - - var payloadData = new - { - authResult = authResultData, - }; - var cardJsonstring = template.Expand(payloadData); + string[] filePath = { ".", "Resources", fileName }; + var adaptiveCardJson = File.ReadAllText(Path.Combine(filePath)); + AdaptiveCardTemplate template = new AdaptiveCardTemplate(adaptiveCardJson); + var payloadData = new { authResult = authResultData }; + var cardJsonString = template.Expand(payloadData); var adaptiveCardResponse = new AdaptiveCardInvokeResponse() { StatusCode = 200, Type = AdaptiveCard.ContentType, - Value = JsonConvert.DeserializeObject(cardJsonstring) + Value = JsonConvert.DeserializeObject(cardJsonString) }; return CreateInvokeResponse(adaptiveCardResponse); @@ -210,12 +210,12 @@ private InvokeResponse createAdaptiveCardInvokeResponseAsync(JObject authenticat /// The context for the current turn. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// A task that represents the work queued to execute. - private async Task initiateSSOAsync(ITurnContext turnContext, CancellationToken cancellationToken) + private async Task InitiateSSOAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var signInLink = await GetSignInLinkAsync(turnContext, cancellationToken).ConfigureAwait(false); var oAuthCard = new OAuthCard { - Text = "Signin Text", + Text = "Please sign in", ConnectionName = _connectionName, TokenExchangeResource = new TokenExchangeResource { @@ -227,7 +227,7 @@ private async Task initiateSSOAsync(ITurnContext initiateSSOAsync(ITurnContextcreatedBy /// createdById /// + // Method to retrieve adaptive card from a file and expand with dynamic data private Attachment GetAdaptiveCardFromFileName(string[] filepath, string signInLink, string name = null, string userMRI = null) { var adaptiveCardJson = File.ReadAllText(Path.Combine(filepath)); @@ -259,9 +260,9 @@ private Attachment GetAdaptiveCardFromFileName(string[] filepath, string signInL createdById = userMRI, createdBy = name }; - - var cardJsonstring = template.Expand(payloadData); - var card = JsonConvert.DeserializeObject(cardJsonstring); + + var cardJsonString = template.Expand(payloadData); + var card = JsonConvert.DeserializeObject(cardJsonString); var adaptiveCardAttachment = new Attachment() { ContentType = "application/vnd.microsoft.card.adaptive", diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/TeamsBot.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/TeamsBot.cs index 0d1b9fab0e..741c0eef48 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/TeamsBot.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Bots/TeamsBot.cs @@ -15,13 +15,19 @@ namespace Microsoft.BotBuilderSamples // This bot is derived (view DialogBot) from the TeamsActivityHandler class currently included as part of this sample. public class TeamsBot : DialogBot { + // Constructor to initialize the bot with necessary dependencies public TeamsBot(ConversationState conversationState, UserState userState, MainDialog dialog, ILogger> logger, IConfiguration configuration) : base(conversationState, userState, dialog, logger, configuration["ConnectionName"]) { + // Check if the ConnectionName exists in the configuration + if (string.IsNullOrEmpty(configuration["ConnectionName"])) + { + logger.LogError("ConnectionName is missing from configuration."); + } } /// - /// Override this in a derived class to provide logic for when members other than the bot join the conversation, such as your bot's welcome logic. + /// Override this in a derived class to provide logic for when members, except the bot, join the conversation, such as your bot's welcome logic. /// /// A list of all the members added to the conversation, as described by the conversation update activity. /// A strongly-typed context object for this turn. @@ -29,13 +35,16 @@ public TeamsBot(ConversationState conversationState, UserState userState, MainDi /// A task that represents the work queued to execute. protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) { - foreach (var member in turnContext.Activity.MembersAdded) + // Iterate over all members added to the conversation. + foreach (var member in membersAdded) { + // Ensure that the bot doesn't greet itself if (member.Id != turnContext.Activity.Recipient.Id) { - await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to Universal Adaptive Cards. Type 'login' to get sign in universal sso."), cancellationToken); + // Send a welcome message to new members. + await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to Universal Adaptive Cards. Type 'login' to sign in using Universal SSO."), cancellationToken); } } } } -} \ No newline at end of file +} diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Controllers/BotController.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Controllers/BotController.cs index fee03d9bf5..3ae911a6a6 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Controllers/BotController.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Controllers/BotController.cs @@ -2,7 +2,9 @@ // Copyright (c) Microsoft. All rights reserved. // +using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; @@ -17,20 +19,30 @@ namespace Microsoft.BotBuilderSamples public class BotController : ControllerBase { private readonly IBotFrameworkHttpAdapter _adapter; - private readonly IBot _bot; + private readonly IBot _botInstance; + // Constructor to inject the adapter and bot instance public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) { _adapter = adapter; - _bot = bot; + _botInstance = 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); + try + { + // Delegate HTTP request processing to the bot adapter. + await _adapter.ProcessAsync(Request, Response, _botInstance); + } + catch (Exception ex) + { + // Log exception and return 500 Internal Server Error + // You can replace this with proper logging mechanism. + Response.StatusCode = 500; + await Response.WriteAsync($"Error processing request: {ex.Message}"); + } } } } diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Dialogs/MainDialog.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Dialogs/MainDialog.cs index 97e8fd6db5..ffbb29f3ea 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Dialogs/MainDialog.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Dialogs/MainDialog.cs @@ -3,26 +3,22 @@ // using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Microsoft.BotBuilderSamples { + // MainDialog is a class that represents the main dialog of the bot. + // It inherits from ComponentDialog which allows adding and managing multiple dialogs. public class MainDialog : ComponentDialog { - protected readonly ILogger _logger; - + // Constructor that accepts a logger for logging purposes. public MainDialog(ILogger logger) - : base(nameof(MainDialog)) + : base(nameof(MainDialog)) // Pass the dialog's name to the base constructor { // The initial child Dialog to run. - InitialDialogId = nameof(WaterfallDialog); + InitialDialogId = nameof(WaterfallDialog); // Specifies that the first dialog to run is a WaterfallDialog. } } -} \ No newline at end of file +} diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Helper/SimpleGraphClient.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Helper/SimpleGraphClient.cs index fe4af4a112..e459be362e 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Helper/SimpleGraphClient.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Helper/SimpleGraphClient.cs @@ -14,11 +14,11 @@ namespace BotSsoAdaptivecard.Helper { // This class is a wrapper for the Microsoft Graph API // See: https://developer.microsoft.com/en-us/graph - public class SimpleGraphClient + public class GraphServiceClientHelper { private readonly string _token; - public SimpleGraphClient(string token) + public GraphServiceClientHelper(string token) { if (string.IsNullOrWhiteSpace(token)) { @@ -29,11 +29,11 @@ public SimpleGraphClient(string token) } /// - /// Sends an email on the users behalf using the Microsoft Graph API + /// Sends an email on the user's behalf using the Microsoft Graph API. /// - /// to address - /// mail subject - /// body content + /// Recipient email address. + /// Subject of the email. + /// Body content of the email. /// The task object representing the asynchronous operation. /// public async Task SendMailAsync(string toAddress, string subject, string content) @@ -54,7 +54,7 @@ public async Task SendMailAsync(string toAddress, string subject, string content } var graphClient = GetAuthenticatedClient(); - var recipients = new List + var recipients = new[] { new Recipient { @@ -77,51 +77,79 @@ public async Task SendMailAsync(string toAddress, string subject, string content ToRecipients = recipients, }; - // Send the message. - await graphClient.Me.SendMail(email, true).Request().PostAsync(); + try + { + // Send the message. + await graphClient.Me.SendMail(email, true).Request().PostAsync().ConfigureAwait(false); + } + catch (ServiceException ex) + { + // Handle exception + Console.WriteLine($"Error sending mail: {ex.Message}"); + } } - // Gets mail for the user using the Microsoft Graph API + /// + /// Gets the most recent mail for the user. + /// + /// A list of the 5 most recent messages. public async Task GetRecentMailAsync() { var graphClient = GetAuthenticatedClient(); - var messages = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync(); + var messages = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync().ConfigureAwait(false); return messages.Take(5).ToArray(); } - // Get information about the user. + /// + /// Gets information about the current authenticated user. + /// + /// User information. public async Task GetMeAsync() { var graphClient = GetAuthenticatedClient(); - var me = await graphClient.Me.Request().GetAsync(); + var me = await graphClient.Me.Request().GetAsync().ConfigureAwait(false); return me; } - // Gets the user's photo + /// + /// Gets the user's photo in base64 format. + /// + /// The base64-encoded photo string, or an empty string if no photo is available. public async Task GetPhotoAsync() { var graphClient = GetAuthenticatedClient(); - var photo = await graphClient.Me.Photo.Content.Request().GetAsync(); - if (photo != null) + try { - MemoryStream ms = new MemoryStream(); - photo.CopyTo(ms); - byte[] buffers = ms.ToArray(); - string imgDataURL = string.Format("data:image/png;base64,{0}", Convert.ToBase64String(buffers)); - return imgDataURL; + var photo = await graphClient.Me.Photo.Content.Request().GetAsync(); + if (photo != null) + { + MemoryStream ms = new MemoryStream(); + photo.CopyTo(ms); + byte[] buffers = ms.ToArray(); + string imgDataURL = string.Format("data:image/png;base64,{0}", Convert.ToBase64String(buffers)); + return imgDataURL; + } + else + { + return ""; // Return empty string if no photo is available. + } } - else + catch (ServiceException ex) { + Console.WriteLine($"Error fetching photo: {ex.Message}"); return ""; } } - // Get an Authenticated Microsoft Graph client using the token issued to the user. + /// + /// Returns an authenticated Microsoft Graph client using the token issued to the user. + /// + /// An authenticated instance of GraphServiceClient. private GraphServiceClient GetAuthenticatedClient() { var graphClient = new GraphServiceClient( new DelegateAuthenticationProvider( - requestMessage => + async requestMessage => { // Append the access token to the request. requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", _token); @@ -129,7 +157,7 @@ private GraphServiceClient GetAuthenticatedClient() // Get event times in the current time zone. requestMessage.Headers.Add("Prefer", "outlook.timezone=\"" + TimeZoneInfo.Local.Id + "\""); - return Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); })); return graphClient; diff --git a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Program.cs b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Program.cs index e925479f35..9d7858eeec 100644 --- a/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Program.cs +++ b/samples/bot-sso-adaptivecard/csharp/BotSsoAdaptivecard/Program.cs @@ -13,47 +13,62 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddHttpClient().AddControllers().AddNewtonsoftJson(); +// Add necessary services to the container. +ConfigureServices(builder.Services); -// Create the Bot Framework Adapter with error handling enabled. -builder.Services.AddSingleton(); +var app = builder.Build(); -// Create the Bot Framework Authentication to be used with the Bot Adapter. -builder.Services.AddSingleton(); +// Configure the HTTP request pipeline. +ConfigureApp(app); -// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.) -builder.Services.AddSingleton(); +app.Run(); -// Create the User state. (Used in this bot's Dialog implementation.) -builder.Services.AddSingleton(); +void ConfigureServices(IServiceCollection services) +{ + // Add HTTP client and JSON configuration. + services.AddHttpClient().AddControllers().AddNewtonsoftJson(); -// Create the Conversation state. (Used by the Dialog system itself.) -builder.Services.AddSingleton(); + // Register the Bot Framework Adapter with error handling. + services.AddSingleton(); -// The Dialog that will be run by the bot. -builder.Services.AddSingleton(); + // Register Bot Framework Authentication. + services.AddSingleton(); -// Create the bot as a transient. In this case the ASP Controller is expecting an IBot. -builder.Services.AddTransient(); + // Register state management services. Consider using Scoped for better isolation. + services.AddScoped(); // Consider replacing MemoryStorage with persistent storage for production. + services.AddScoped(); + services.AddScoped(); -var app = builder.Build(); + // Register the dialog to be used by the bot. + services.AddSingleton(); -// Configure the HTTP request pipeline.l -if (!app.Environment.IsDevelopment()) -{ - app.UseHsts(); + // Register the bot as a transient service. + services.AddTransient(); } -else + +void ConfigureApp(WebApplication app) { - app.UseDeveloperExceptionPage(); -} + // Environment-specific configurations. + if (!app.Environment.IsDevelopment()) + { + // Enabling HTTPS Strict Transport Security (HSTS) in production environments. + app.UseHsts(); + } + else + { + // Enabling developer exception page during development. + app.UseDeveloperExceptionPage(); + } -app.UseDefaultFiles() - .UseStaticFiles() - .UseRouting() - .UseAuthorization() - .UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); -app.Run(); \ No newline at end of file + // Serving static files and configuring routing. + app.UseDefaultFiles() + .UseStaticFiles() + .UseRouting() + .UseAuthorization(); + + // Configure endpoints to handle incoming requests. + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); // Mapping controller routes. + }); +}