Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample Code Optimization - Ashlesha #1525

Merged
merged 12 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-complete-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ jobs:
name: 'bot-conversation'
version: '6.0.x'

- project_path: 'samples/bot-file-upload/csharp/TeamsFileUpload.csproj'
- project_path: 'samples/bot-file-upload/csharp/TeamsFileUpload/TeamsFileUpload.csproj'
name: 'bot-file-upload'
version: '6.0.x'

- project_path: 'samples/bot-initiate-thread-in-channel/csharp/TeamsStartNewThreadInTeam.csproj'
- project_path: 'samples/bot-initiate-thread-in-channel/csharp/TeamsStartNewThreadInTeam/TeamsStartNewThreadInTeam.csproj'
name: 'bot-initiate-thread-in-channel'
version: '6.0.x'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,35 @@

namespace Microsoft.Teams.Samples.HelloWorld.Web
{
/// <summary>
/// Custom adapter that extends the BotFrameworkHttpAdapter with error handling capabilities.
/// This class overrides the OnTurnError property to log errors, trace the error for debugging,
/// and optionally notify the user.
/// </summary>
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter

Check warning on line 17 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 17 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 17 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 17 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'
{
/// <summary>
/// Initializes a new instance of the <see cref="AdapterWithErrorHandler"/> class.
/// Configures the error handling behavior for the adapter.
/// </summary>
/// <param name="configuration">The application's configuration.</param>
/// <param name="logger">The logger instance for logging errors.</param>
public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger)

Check warning on line 25 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 25 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 25 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

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

Check warning on line 25 in samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs

View workflow job for this annotation

GitHub Actions / Build All "app-hello-world" csharp

'BotFrameworkHttpAdapter' is obsolete: 'Use `CloudAdapter` instead.'
: base(configuration, logger)
{
// Configuring the OnTurnError handler to capture unhandled errors.
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Log the error using the logger with a detailed message.
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}");
Pawank-MSFT marked this conversation as resolved.
Show resolved Hide resolved

// Send a trace activity, which will be displayed in the Bot Framework Emulator
// Trace the error in the Bot Framework Emulator to aid debugging.
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");

};
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,91 +1,133 @@
using System.Threading;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json.Linq;
using System.Linq;
using System;
using System.Collections.Generic;
using Bogus;

namespace Microsoft.Teams.Samples.HelloWorld.Web
{
/// <summary>
/// A class for handling Teams messaging extension requests.
/// </summary>
public class MessageExtension : TeamsActivityHandler
{
// Static Faker instance to generate fake data.
private static readonly Faker Faker = new Faker();

/// <summary>
/// Handles incoming message activity and replies with the same text.
/// </summary>
/// <param name="turnContext">The context for the turn of conversation.</param>
/// <param name="cancellationToken">Token for canceling the operation.</param>
/// <returns>Task representing the asynchronous operation.</returns>
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Clean the activity text by removing the recipient mention.
turnContext.Activity.RemoveRecipientMention();

// Process the text to make it lowercase and remove leading/trailing spaces.
var text = turnContext.Activity.Text.Trim().ToLower();

// Send back the same text as a reply.
var replyText = $"You said: {text}";
await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
}

/// <summary>
/// Handles incoming query activity for the messaging extension.
/// Responds to "getRandomText" query.
/// </summary>
/// <param name="turnContext">The context for the turn of conversation.</param>
/// <param name="query">The query from Teams.</param>
/// <param name="cancellationToken">Token for canceling the operation.</param>
/// <returns>Task representing the asynchronous operation, containing the MessagingExtensionResponse.</returns>
protected override Task<MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
{
var title = "";
var titleParam = query.Parameters?.FirstOrDefault(p => p.Name == "cardTitle");
if (titleParam != null)
{
title = titleParam.Value.ToString();
}

// Ensure that only "getRandomText" command is processed
if (query == null || query.CommandId != "getRandomText")
{
// We only process the 'getRandomText' queries with this message extension
throw new NotImplementedException($"Invalid CommandId: {query.CommandId}");
}

var attachments = new MessagingExtensionAttachment[5];
// Retrieve cardTitle parameter if present, otherwise use an empty string.
var title = query.Parameters?.FirstOrDefault(p => p.Name == "cardTitle")?.Value.ToString() ?? string.Empty;

for (int i = 0; i < 5; i++)
{
attachments[i] = GetAttachment(title);
}
// Generate 5 attachments with random text and images.
var attachments = GenerateRandomAttachments(title);

// Construct the response with the generated attachments.
var result = new MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
AttachmentLayout = "list",
Type = "result",
Attachments = attachments.ToList()
},
}
};

return Task.FromResult(result);
}

/// <summary>
/// Generates a list of random attachments with fake data.
/// </summary>
/// <param name="title">The title for the card (if available).</param>
/// <returns>List of generated messaging extension attachments.</returns>
private static IEnumerable<MessagingExtensionAttachment> GenerateRandomAttachments(string title)
{
return Enumerable.Range(0, 5)
.Select(_ => GetAttachment(title))
.ToArray();
}

/// <summary>
/// Creates a single random attachment with a title, text, and image.
/// </summary>
/// <param name="title">The title for the card.</param>
/// <returns>A MessagingExtensionAttachment object.</returns>
private static MessagingExtensionAttachment GetAttachment(string title)
{
// Create a ThumbnailCard with either the provided title or a random one.
var card = new ThumbnailCard
{
Title = !string.IsNullOrWhiteSpace(title) ? title : new Faker().Lorem.Sentence(),
Text = new Faker().Lorem.Paragraph(),
Images = new List<CardImage> { new CardImage("http://lorempixel.com/640/480?rand=" + DateTime.Now.Ticks.ToString()) }
Title = string.IsNullOrWhiteSpace(title) ? Faker.Lorem.Sentence() : title,
Text = Faker.Lorem.Paragraph(),
Images = new List<CardImage> { new CardImage($"http://lorempixel.com/640/480?rand={DateTime.Now.Ticks}") }
};

return card
.ToAttachment()
.ToMessagingExtensionAttachment();
return card.ToAttachment().ToMessagingExtensionAttachment();
}

/// <summary>
/// Handles selection of a particular item from the messaging extension.
/// </summary>
/// <param name="turnContext">The context for the turn of conversation.</param>
/// <param name="query">The selected item details from the messaging extension.</param>
/// <param name="cancellationToken">Token for canceling the operation.</param>
/// <returns>Task representing the asynchronous operation, containing the MessagingExtensionResponse.</returns>
protected override Task<MessagingExtensionResponse> OnTeamsMessagingExtensionSelectItemAsync(ITurnContext<IInvokeActivity> turnContext, JObject query, CancellationToken cancellationToken)
{
// Create a default attachment as a placeholder.
var attachment = new ThumbnailCard()
.ToAttachment()
.ToMessagingExtensionAttachment();

return Task.FromResult(new MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
AttachmentLayout = "list",
Type = "result",
Attachments = new MessagingExtensionAttachment[]{
new ThumbnailCard()
.ToAttachment()
.ToMessagingExtensionAttachment()
}
},
Attachments = new[] { attachment }
}
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,45 @@
// Licensed under the MIT License.
//
Pawank-MSFT marked this conversation as resolved.
Show resolved Hide resolved
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using System.Threading.Tasks;

namespace Microsoft.Teams.Samples.HelloWorld.Web.Controllers
{
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
/// <summary>
/// This controller handles incoming requests and delegates them to the Bot Framework adapter.
/// Dependency Injection is used to inject the Bot Framework HTTP adapter and the bot implementation.
/// </summary>
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter Adapter;
private readonly IBot Bot;
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;

/// <summary>
/// Initializes a new instance of the <see cref="BotController"/> class.
/// </summary>
/// <param name="adapter">The Bot Framework HTTP adapter that processes incoming requests.</param>
/// <param name="bot">The bot implementation that processes activities.</param>
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
Adapter = adapter;
Bot = bot;
_adapter = adapter;
_bot = bot;
}

/// <summary>
/// Handles incoming POST requests and delegates processing to the adapter.
/// </summary>
/// <returns>A Task representing the asynchronous operation.</returns>
[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);
// Delegate the processing of the incoming request to the adapter.
// The adapter will invoke the appropriate bot implementation.
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;

namespace Microsoft.Teams.Samples.HelloWorld.Web.Controllers
{
/// <summary>
/// HomeController is responsible for handling routes related to the main pages of the application.
/// This includes rendering the main index page, hello page, and other specific views.
/// </summary>
public class HomeController : Controller
{
/// <summary>
/// Displays the home page (Index view).
/// </summary>
/// <returns>Returns the Index view.</returns>
[Route("")]
public ActionResult Index()
{
return View();
}

/// <summary>
/// Redirects to the Index view but uses a different URL (Hello page).
/// </summary>
/// <returns>Returns the Index view as Hello.</returns>
[Route("hello")]
public ActionResult Hello()
{
return View("Index");
}

/// <summary>
/// Displays the 'First' page.
/// </summary>
/// <returns>Returns the First view.</returns>
[Route("first")]
public ActionResult First()
{
return View();
}

/// <summary>
/// Displays the 'Second' page.
/// </summary>
/// <returns>Returns the Second view.</returns>
[Route("second")]
public ActionResult Second()
{
return View();
}

/// <summary>
/// Displays the 'Configure' page.
/// </summary>
/// <returns>Returns the Configure view.</returns>
[Route("configure")]
public ActionResult Configure()
{
return View();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,35 @@
// Licensed under the MIT License.
//
Pawank-MSFT marked this conversation as resolved.
Show resolved Hide resolved
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2
using Microsoft.AspNetCore;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;

namespace Microsoft.Teams.Samples.HelloWorld.Web
{
/// The entry point for the application, responsible for configuring and starting the web host.
public class Program
{
/// <summary>
/// Main method to configure and start the web application.
/// </summary>
/// <param name="args">Command-line arguments passed to the application.</param>
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
// Build and run the web host
CreateHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
/// <summary>
/// Configures the web host builder with the required settings.
/// </summary>
/// <param name="args">Command-line arguments passed to the application.</param>
/// <returns>The configured IHostBuilder.</returns>
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// Specify the Startup class to be used for configuration
webBuilder.UseStartup<Startup>();
});
}
}
}
Loading
Loading