From 8d5f41c04de859bf8ac300586f0fa076a919db72 Mon Sep 17 00:00:00 2001
From: Harikrishnan Rajandiran
Date: Thu, 9 Jan 2025 14:54:23 +0530
Subject: [PATCH 01/10] Sample Code Optimization
---
.../AdapterWithErrorHandler.cs | 32 ++--
.../Bots/MessageExtension.cs | 102 +++++++----
.../Controllers/BotController.cs | 40 +++--
.../Controllers/HomeController.cs | 32 +++-
.../Program.cs | 33 ++--
.../Startup.cs | 60 +++++--
.../Views/Home/Configure.cshtml | 18 +-
.../Views/Home/First.cshtml | 4 +-
.../Views/Home/Index.cshtml | 4 +-
.../Views/Home/Second.cshtml | 4 +-
samples/app-hello-world/nodejs/build.js | 4 +-
samples/app-hello-world/nodejs/env/.env.local | 6 +-
samples/app-hello-world/nodejs/src/app.js | 43 ++---
samples/app-hello-world/nodejs/src/bot.js | 46 ++---
.../nodejs/src/message-extension.js | 52 ++++--
.../nodejs/src/static/scripts/teamsapp.js | 15 +-
samples/app-hello-world/nodejs/src/tabs.js | 38 ++--
.../nodejs/src/views/configure.html | 42 ++---
.../nodejs/src/views/first.html | 19 +-
.../nodejs/src/views/hello.html | 22 ++-
.../nodejs/src/views/second.html | 19 +-
samples/app-hello-world/python/app.py | 26 ++-
samples/app-hello-world/python/bots/bot.py | 168 +++++++++++-------
.../python/src/views/configure.html | 46 +++--
.../python/src/views/first.html | 25 ++-
.../python/src/views/hello.html | 22 ++-
.../python/src/views/second.html | 30 +++-
27 files changed, 582 insertions(+), 370 deletions(-)
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs
index 4a02d27f73..f0de09163e 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/AdapterWithErrorHandler.cs
@@ -1,30 +1,38 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-//
-// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.6.2
-using Microsoft.Bot.Builder.Integration.AspNet.Core;
+using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.Teams.Samples.HelloWorld.Web
{
+ ///
+ /// 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.
+ ///
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
+ ///
+ /// Initializes a new instance of the class.
+ /// Configures the error handling behavior for the adapter.
+ ///
+ /// The application's configuration.
+ /// The logger instance for logging errors.
public AdapterWithErrorHandler(IConfiguration configuration, ILogger logger)
: 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}");
-
- // 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");
+
+ // Optionally, you can uncomment the following line to send a message to the user when an error occurs.
+ // await turnContext.SendActivityAsync("Sorry, it looks like something went wrong.");
};
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs
index f1443a9cfa..7ac93b871c 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs
@@ -1,50 +1,67 @@
-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
{
+ ///
+ /// A class for handling Teams messaging extension requests.
+ ///
public class MessageExtension : TeamsActivityHandler
{
+ // Static Faker instance to generate fake data.
+ private static readonly Faker Faker = new Faker();
+
+ ///
+ /// Handles incoming message activity and replies with the same text.
+ ///
+ /// The context for the turn of conversation.
+ /// Token for canceling the operation.
+ /// Task representing the asynchronous operation.
protected override async Task OnMessageActivityAsync(ITurnContext 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);
}
+ ///
+ /// Handles incoming query activity for the messaging extension.
+ /// Responds to "getRandomText" query.
+ ///
+ /// The context for the turn of conversation.
+ /// The query from Teams.
+ /// Token for canceling the operation.
+ /// Task representing the asynchronous operation, containing the MessagingExtensionResponse.
protected override Task OnTeamsMessagingExtensionQueryAsync(ITurnContext 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
@@ -52,40 +69,65 @@ protected override Task OnTeamsMessagingExtensionQue
AttachmentLayout = "list",
Type = "result",
Attachments = attachments.ToList()
- },
+ }
};
+
return Task.FromResult(result);
+ }
+ ///
+ /// Generates a list of random attachments with fake data.
+ ///
+ /// The title for the card (if available).
+ /// List of generated messaging extension attachments.
+ private static IEnumerable GenerateRandomAttachments(string title)
+ {
+ return Enumerable.Range(0, 5)
+ .Select(_ => GetAttachment(title))
+ .ToArray();
}
+ ///
+ /// Creates a single random attachment with a title, text, and image.
+ ///
+ /// The title for the card.
+ /// A MessagingExtensionAttachment object.
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 { 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 { new CardImage($"http://lorempixel.com/640/480?rand={DateTime.Now.Ticks}") }
};
- return card
- .ToAttachment()
- .ToMessagingExtensionAttachment();
+ return card.ToAttachment().ToMessagingExtensionAttachment();
}
+
+ ///
+ /// Handles selection of a particular item from the messaging extension.
+ ///
+ /// The context for the turn of conversation.
+ /// The selected item details from the messaging extension.
+ /// Token for canceling the operation.
+ /// Task representing the asynchronous operation, containing the MessagingExtensionResponse.
protected override Task OnTeamsMessagingExtensionSelectItemAsync(ITurnContext 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 }
+ }
});
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/BotController.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/BotController.cs
index 0dd3b32f6f..54714d51c8 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/BotController.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/BotController.cs
@@ -1,36 +1,42 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-//
-// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
+using Microsoft.Bot.Builder;
+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.
+ ///
+ /// 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.
+ ///
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
- private readonly IBotFrameworkHttpAdapter Adapter;
- private readonly IBot Bot;
+ private readonly IBotFrameworkHttpAdapter _adapter;
+ private readonly IBot _bot;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Bot Framework HTTP adapter that processes incoming requests.
+ /// The bot implementation that processes activities.
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
- Adapter = adapter;
- Bot = bot;
+ _adapter = adapter;
+ _bot = bot;
}
+ ///
+ /// Handles incoming POST requests and delegates processing to the adapter.
+ ///
+ /// A Task representing 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);
+ // Delegate the processing of the incoming request to the adapter.
+ // The adapter will invoke the appropriate bot implementation.
+ await _adapter.ProcessAsync(Request, Response, _bot);
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/HomeController.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/HomeController.cs
index 294580eb0b..96d79a0c9b 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/HomeController.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Controllers/HomeController.cs
@@ -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
{
+ ///
+ /// 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.
+ ///
public class HomeController : Controller
{
+ ///
+ /// Displays the home page (Index view).
+ ///
+ /// Returns the Index view.
[Route("")]
public ActionResult Index()
{
return View();
}
+ ///
+ /// Redirects to the Index view but uses a different URL (Hello page).
+ ///
+ /// Returns the Index view as Hello.
[Route("hello")]
public ActionResult Hello()
{
return View("Index");
}
+ ///
+ /// Displays the 'First' page.
+ ///
+ /// Returns the First view.
[Route("first")]
public ActionResult First()
{
return View();
}
+ ///
+ /// Displays the 'Second' page.
+ ///
+ /// Returns the Second view.
[Route("second")]
public ActionResult Second()
{
return View();
}
+ ///
+ /// Displays the 'Configure' page.
+ ///
+ /// Returns the Configure view.
[Route("configure")]
public ActionResult Configure()
{
return View();
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Program.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Program.cs
index 06799403e0..dd56f2c142 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Program.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Program.cs
@@ -1,21 +1,32 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-//
-// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2
-using Microsoft.AspNetCore;
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
namespace Microsoft.Teams.Samples.HelloWorld.Web
{
+ /// The entry point for the application, responsible for configuring and starting the web host.
public class Program
{
+ ///
+ /// Main method to configure and start the web application.
+ ///
+ /// Command-line arguments passed to the application.
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();
+ ///
+ /// Configures the web host builder with the required settings.
+ ///
+ /// Command-line arguments passed to the application.
+ /// The configured IHostBuilder.
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ // Specify the Startup class to be used for configuration
+ webBuilder.UseStartup();
+ });
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Startup.cs b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Startup.cs
index 749bf291cf..1e3b3de954 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Startup.cs
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Startup.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2
+
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
@@ -12,56 +13,81 @@
namespace Microsoft.Teams.Samples.HelloWorld.Web
{
+ ///
+ /// Configures services and middleware for the application.
+ /// This class is used by the runtime to set up the application's request pipeline.
+ ///
public class Startup
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Configuration for setting up the application environment.
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
+ ///
+ /// Gets the configuration for the application.
+ ///
public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
+ ///
+ /// Configures services for the application.
+ ///
+ /// A collection of services to be added to the container.
public void ConfigureServices(IServiceCollection services)
{
- services.AddControllers();
- services.AddMvc();
+ // Add controllers and MVC services to the container
+ services.AddControllers(); // Adds controllers to the service collection
+ services.AddMvc(); // Adds MVC services for handling views and routes
- // Create the Bot Framework Adapter with error handling enabled.
+ // Register Bot Framework HTTP Adapter with custom error handler
services.AddSingleton();
- // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
+ // Register the bot (MessageExtension) as a transient service
services.AddTransient();
}
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ ///
+ /// Configures the HTTP request pipeline for the application.
+ ///
+ /// The application's builder for configuring the middleware pipeline.
+ /// The environment information for the application.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
+ // Enable developer exception page if the environment is development
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
- app.UseHsts();
+ // Enable HTTPS and HSTS for production environments
+ app.UseHsts(); // HTTP Strict Transport Security for enhanced security
+ app.UseHttpsRedirection(); // Redirect HTTP requests to HTTPS
}
- app.UseDefaultFiles();
- app.UseStaticFiles();
- app.UseWebSockets();
+ // Configure static files, default files, and WebSocket support
+ app.UseDefaultFiles(); // Serves default files (e.g., index.html)
+ app.UseStaticFiles(); // Serves static files (e.g., images, CSS, JS)
+ app.UseWebSockets(); // Enable WebSocket support
- // Runs matching. An endpoint is selected and set on the HttpContext if a match is found.
- app.UseRouting();
+ // Configure routing for the application
+ app.UseRouting(); // Enables routing capabilities
+ // Configure endpoint mapping
app.UseEndpoints(endpoints =>
{
- // Mapping of endpoints goes here:
+ // Map controller endpoints
endpoints.MapControllers();
+
+ // Map default route for controller actions
endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller=Home}/{action=Index}/{id?}");
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}"); // Default route for Home controller
});
- //app.UseHttpsRedirection();
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Configure.cshtml b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Configure.cshtml
index 4f53e66e30..9bd199d1c4 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Configure.cshtml
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Configure.cshtml
@@ -1,11 +1,9 @@
Configure your app here
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/First.cshtml b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/First.cshtml
index 7ac4de042b..ff559c6991 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/First.cshtml
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/First.cshtml
@@ -2,5 +2,5 @@
This is our first tab
- Welcome to Microsoft Teams Hello World sample app (C#).
-
+ Welcome to the Microsoft Teams Hello World sample app (C#).
+
\ No newline at end of file
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Index.cshtml b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Index.cshtml
index 82f96dddd1..edc266bfd9 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Index.cshtml
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Index.cshtml
@@ -2,5 +2,5 @@
Hello, World
- Welcome to Microsoft Teams Hello World sample app (C#).
-
+ Welcome to the Microsoft Teams Hello World sample app (C#).
+
\ No newline at end of file
diff --git a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Second.cshtml b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Second.cshtml
index 5787ad398b..cc57ac6bac 100644
--- a/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Second.cshtml
+++ b/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Views/Home/Second.cshtml
@@ -2,5 +2,5 @@
This is our second tab
- Welcome to Microsoft Teams Hello World sample app (C#).
-
+ Welcome to the Microsoft Teams Hello World sample app (C#).
+
\ No newline at end of file
diff --git a/samples/app-hello-world/nodejs/build.js b/samples/app-hello-world/nodejs/build.js
index 9038c9f3c1..c67a639e52 100644
--- a/samples/app-hello-world/nodejs/build.js
+++ b/samples/app-hello-world/nodejs/build.js
@@ -8,7 +8,7 @@ esbuild.build({
.then((r) => {
console.log(`Build succeeded.`);
})
- .catch((e) => {
- console.log("Error building:", e.message);
+ .catch((error) => {
+ console.error("Error building:", error.message);
process.exit(1);
});
\ No newline at end of file
diff --git a/samples/app-hello-world/nodejs/env/.env.local b/samples/app-hello-world/nodejs/env/.env.local
index c4bf86753f..074aa4ef73 100644
--- a/samples/app-hello-world/nodejs/env/.env.local
+++ b/samples/app-hello-world/nodejs/env/.env.local
@@ -14,4 +14,8 @@ AAD_APP_OAUTH_AUTHORITY_HOST=
TEAMS_APP_ID=
AAD_APP_ACCESS_AS_USER_PERMISSION_ID=
MICROSOFT_APP_TYPE=
-MICROSOFT_APP_TENANT_ID=
\ No newline at end of file
+MICROSOFT_APP_TENANT_ID=
+RESOURCE_SUFFIX=
+AZURE_SUBSCRIPTION_ID=
+AZURE_RESOURCE_GROUP_NAME=
+TEAMS_APP_TENANT_ID=
\ No newline at end of file
diff --git a/samples/app-hello-world/nodejs/src/app.js b/samples/app-hello-world/nodejs/src/app.js
index 5d43285988..954c5bb642 100644
--- a/samples/app-hello-world/nodejs/src/app.js
+++ b/samples/app-hello-world/nodejs/src/app.js
@@ -6,50 +6,45 @@ import restify from 'restify';
import { adapter, EchoBot } from './bot';
import tabs from './tabs';
import MessageExtension from './message-extension';
-
-// See https://aka.ms/bot-services to learn more about the different parts of a bot.
import { ActivityTypes } from 'botbuilder';
-// Read botFilePath and botFileSecret from .env file.
+// Read environment variables from .env file for bot credentials and settings.
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
-//Create HTTP server.
+// Create an HTTP server using Restify.
const server = restify.createServer({
formatters: {
- 'text/html': function (req, res, body) {
- return body;
- },
+ 'text/html': (req, res, body) => body, // Return body as-is for HTML responses.
},
});
+// Serve static files (e.g., for web pages or resources like images).
server.get(
'/*',
restify.plugins.serveStatic({
- directory: __dirname + '/static',
+ directory: path.join(__dirname, 'static'),
})
);
-server.listen(process.env.port || process.env.PORT || 3978, function () {
- console.log(`\n${server.name} listening to ${server.url}`);
+// Start the server on the configured port, falling back to 3978 if not set.
+server.listen(process.env.port || process.env.PORT || 3978, () => {
+ console.log(`${server.name} listening to ${server.url}`);
});
-// Adding tabs to our app. This will setup routes to various views
-tabs(server);
-
-// Adding a bot to our app
-const bot = new EchoBot();
-
-// Adding a messaging extension to our app
-const messageExtension = new MessageExtension();
+// Initialize tabs and message extension functionalities.
+tabs(server); // Setup routes for tab functionality in the bot.
+const bot = new EchoBot(); // Initialize the EchoBot to handle user interactions.
+const messageExtension = new MessageExtension(); // Initialize message extension for bot.
-// Listen for incoming requests.
server.post('/api/messages', (req, res, next) => {
+ // Process incoming activity and route to either bot or message extension based on activity type.
adapter.processActivity(req, res, async (context) => {
- if (context.activity.type === ActivityTypes.Invoke)
- await messageExtension.run(context);
- else
- await bot.run(context);
- return next();
+ if (context.activity.type === ActivityTypes.Invoke) {
+ await messageExtension.run(context); // Handle Invoke activities (e.g., message extensions).
+ } else {
+ await bot.run(context); // Handle other types of activities (e.g., user messages).
+ }
+ return next(); // Continue processing any other middleware.
});
});
diff --git a/samples/app-hello-world/nodejs/src/bot.js b/samples/app-hello-world/nodejs/src/bot.js
index 63b3016142..dd3124a864 100644
--- a/samples/app-hello-world/nodejs/src/bot.js
+++ b/samples/app-hello-world/nodejs/src/bot.js
@@ -10,47 +10,51 @@ import {
} from 'botbuilder';
import config from 'config';
-// Create adapter.
-// See https://aka.ms/about-bot-adapter to learn more about adapters.
+// Create adapter and configure it with app credentials from config.
+// See https://aka.ms/about-bot-adapter for more information.
export const adapter = new BotFrameworkAdapter({
appId: config.get('bot.appId'),
appPassword: config.get('bot.appPassword'),
});
+// Error handler to catch errors during bot interactions.
adapter.onTurnError = async (context, error) => {
- const errorMsg = error.message
- ? error.message
- : `Oops. Something went wrong!`;
- // This check writes out errors to console log .vs. app insights.
- // NOTE: In production environment, you should consider logging this to Azure
- // application insights.
- console.error(`\n [onTurnError] unhandled error: ${error}`);
-
- // Clear out state
+ const errorMsg = error.message || 'Oops. Something went wrong!';
+ // Log error (Consider logging to Azure Application Insights in production)
+ console.error(`[onTurnError] unhandled error: ${error}`);
+
+ // Clear any state that may have been corrupted during the error.
await conversationState.delete(context);
- // Send a message to the user
- await context.sendActivity(errorMsg);
- // Note: Since this Messaging Extension does not have the messageTeamMembers permission
- // in the manifest, the bot will not be allowed to message users.
+ // Send error message to user.
+ await context.sendActivity(errorMsg);
- // Uncomment below commented line for local debugging.
- // await context.sendActivity(`Sorry, it looks like something went wrong. Exception Caught: ${errorMsg}`);
+ // For local debugging, uncomment the line below to send detailed error message.
+ // await context.sendActivity(`Sorry, something went wrong. Exception: ${errorMsg}`);
};
-// Define state store for your bot.
+// Define in-memory storage for the bot's conversation state.
const memoryStorage = new MemoryStorage();
-// Create conversation state with in-memory storage provider.
+// Create a conversation state with the in-memory storage provider.
const conversationState = new ConversationState(memoryStorage);
+// EchoBot class that inherits from TeamsActivityHandler.
export class EchoBot extends TeamsActivityHandler {
constructor() {
super();
+
+ // Handles incoming messages and echoes them back.
this.onMessage(async (context, next) => {
+ // Remove recipient mention to avoid bot calling itself.
TurnContext.removeRecipientMention(context.activity);
- const text = context.activity.text.trim().toLocaleLowerCase();
- await context.sendActivity('You said ' + text);
+
+ // Process the incoming message, convert it to lowercase, and send a response.
+ const text = context.activity.text.trim().toLowerCase();
+ await context.sendActivity(`You said: ${text}`);
+
+ // Proceed to the next middleware, if any.
+ await next();
});
}
}
diff --git a/samples/app-hello-world/nodejs/src/message-extension.js b/samples/app-hello-world/nodejs/src/message-extension.js
index 0e3e04bdb3..af9628a28f 100644
--- a/samples/app-hello-world/nodejs/src/message-extension.js
+++ b/samples/app-hello-world/nodejs/src/message-extension.js
@@ -1,32 +1,39 @@
import { TeamsActivityHandler, CardFactory } from 'botbuilder';
import faker from 'faker';
-
-
export default class MessageExtension extends TeamsActivityHandler {
- async handleTeamsMessagingExtensionQuery(context, query) {
- // If the user supplied a title via the cardTitle parameter then use it or use a fake title
- let title = query.parameters && query.parameters[0].name === 'cardTitle'
- ? query.parameters[0].value
- : faker.lorem.sentence();
+ /**
+ * Handles messaging extension queries such as retrieving random text and images.
+ *
+ * @param {TurnContext} context - The context for the current request.
+ * @param {object} query - The query data from the messaging extension.
+ * @returns {object} A response containing the attachments (cards).
+ */
+ async handleTeamsMessagingExtensionQuery(context, query) {
+ // Default title from user input or fallback to a randomly generated title.
+ const title = query.parameters?.[0]?.name === 'cardTitle' ? query.parameters[0].value : faker.lorem.sentence();
- let randomImageUrl = "https://loremflickr.com/200/200"; // Faker's random images uses lorempixel.com, which has been down a lot
+ const randomImageUrl = "https://loremflickr.com/200/200"; // Using random image generator (fallback URL)
switch (query.commandId) {
case 'getRandomText':
- let attachments = [];
+ const attachments = [];
- // Generate 5 results to send with fake text and fake images
+ // Generate 5 results, each with a fake paragraph and image.
for (let i = 0; i < 5; i++) {
- let text = faker.lorem.paragraph();
- let images = [`${randomImageUrl}?random=${i}`]
- let thumbnailCard = CardFactory.thumbnailCard(title, text, images)
- let preview = CardFactory.thumbnailCard(title, text, images)
+ const text = faker.lorem.paragraph(); // Generate random text.
+ const images = [`${randomImageUrl}?random=${i}`]; // Ensure unique images per result.
+
+ // Create a thumbnail card with the generated content.
+ const thumbnailCard = CardFactory.thumbnailCard(title, text, images);
+ const preview = CardFactory.thumbnailCard(title, text, images);
+
+ // Set up tap action for the preview card.
preview.content.tap = { type: 'invoke', value: { title, text, images } };
- var attachment = { ...thumbnailCard, preview }
- attachments.push(attachment);
+ // Push the thumbnail card with preview to the attachments array.
+ attachments.push({ ...thumbnailCard, preview });
}
return {
@@ -38,13 +45,20 @@ export default class MessageExtension extends TeamsActivityHandler {
};
default:
- break;
+ // Return null if no matching commandId is found.
+ return null;
}
- return null;
}
+ /**
+ * Handles item selection in the messaging extension.
+ *
+ * @param {TurnContext} context - The context for the current request.
+ * @param {object} obj - The selected item from the messaging extension.
+ * @returns {object} A response containing the selected item (card).
+ */
async handleTeamsMessagingExtensionSelectItem(context, obj) {
- const { title, text, images } = obj
+ const { title, text, images } = obj;
return {
composeExtension: {
diff --git a/samples/app-hello-world/nodejs/src/static/scripts/teamsapp.js b/samples/app-hello-world/nodejs/src/static/scripts/teamsapp.js
index 9095b34899..0b9f8e1b5a 100644
--- a/samples/app-hello-world/nodejs/src/static/scripts/teamsapp.js
+++ b/samples/app-hello-world/nodejs/src/static/scripts/teamsapp.js
@@ -5,14 +5,14 @@
await microsoftTeams.app.initialize();
// Check the initial theme user chose and respect it
- microsoftTeams.getContext(function (context) {
- if (context && context.theme) {
+ microsoftTeams.getContext((context)=>{
+ if (context.theme) {
setTheme(context.theme);
}
});
// Handle theme changes
- microsoftTeams.registerOnThemeChangeHandler(function (theme) {
+ microsoftTeams.registerOnThemeChangeHandler((theme) => {
setTheme(theme);
});
@@ -23,9 +23,12 @@
contentUrl: createTabUrl(),
entityId: createTabUrl(),
});
- configPromise.
- then((result) => { saveEvent.notifySuccess() }).
- catch((error) => { saveEvent.notifyFailure("failure message") });
+ configPromise.then(() => {
+ saveEvent.notifySuccess();
+ }).
+ catch((error) => {
+ saveEvent.notifyFailure("failure message")
+ });
});
// Logic to let the user configure what they want to see in the tab being loaded
diff --git a/samples/app-hello-world/nodejs/src/tabs.js b/samples/app-hello-world/nodejs/src/tabs.js
index 1a942271c2..25b35bd08c 100644
--- a/samples/app-hello-world/nodejs/src/tabs.js
+++ b/samples/app-hello-world/nodejs/src/tabs.js
@@ -1,26 +1,28 @@
import send from 'send';
+/**
+ * Sets up routes for handling the tabs and their content in the application.
+ *
+ * @param {Object} server - The Restify server instance.
+ */
export default function tabs(server) {
- // Setup home page
- server.get('/', (req, res, next) => {
- send(req, 'src/views/hello.html').pipe(res);
- });
+ // Helper function to streamline serving static HTML files.
+ const serveHtml = (filePath, req, res, next) => {
+ send(req, filePath).pipe(res).on('error', next); // Handle errors in the send pipe.
+ };
- // Setup the static tab
- server.get('/hello', (req, res, next) => {
- send(req, 'src/views/hello.html').pipe(res);
- });
+ // Setup home page route.
+ server.get('/', (req, res, next) => serveHtml('src/views/hello.html', req, res, next));
- // Setup the configure tab, with first and second as content tabs
- server.get('/configure', (req, res, next) => {
- send(req, 'src/views/configure.html').pipe(res);
- });
+ // Setup the static tab route.
+ server.get('/hello', (req, res, next) => serveHtml('src/views/hello.html', req, res, next));
- server.get('/first', (req, res, next) => {
- send(req, 'src/views/first.html').pipe(res);
- });
+ // Setup the configure tab route.
+ server.get('/configure', (req, res, next) => serveHtml('src/views/configure.html', req, res, next));
- server.get('/second', (req, res, next) => {
- send(req, 'src/views/second.html').pipe(res);
- });
+ // Setup content tab with separate 'First' view.
+ server.get('/first', (req, res, next) => serveHtml('src/views/first.html', req, res, next));
+
+ // Setup content tab with separate 'second' view.
+ server.get('/second', (req, res, next) => serveHtml('src/views/second.html', req, res, next));
}
diff --git a/samples/app-hello-world/nodejs/src/views/configure.html b/samples/app-hello-world/nodejs/src/views/configure.html
index 32b877f0e7..1c00ecd5b2 100644
--- a/samples/app-hello-world/nodejs/src/views/configure.html
+++ b/samples/app-hello-world/nodejs/src/views/configure.html
@@ -2,37 +2,29 @@
-
- Microsoft Teams Hello World Sample App
-
+
+
+ Microsoft Teams Hello World Sample App
-
-
-
+
-