using System.Text.Json; using Microsoft.Teams.Api.Cards; using Microsoft.Teams.Apps.Activities; using Microsoft.Teams.Apps.Activities.Invokes; using Microsoft.Teams.Apps.Extensions; using Microsoft.Teams.Cards; using Microsoft.Teams.Common; using Microsoft.Teams.Plugins.AspNetCore.Extensions; var builder = WebApplication.CreateBuilder(args); builder.AddTeams(); var app = builder.Build(); app.UseHttpsRedirection(); // Log raw requests app.Use(async (context, next) => { if (context.Request.Method == "POST") { context.Request.EnableBuffering(); var body = await new StreamReader(context.Request.Body).ReadToEndAsync(); context.Request.Body.Position = 0; Console.WriteLine($"[RAW_REQUEST] {context.Request.Method} {context.Request.Path}: {body}"); } await next(); }); var teams = app.UseTeams(); // Serve settings page app.MapGet("/settings", () => Results.Content(GetSettingsHtml(), "text/html")); teams.OnMessage(async (context, cancellationToken) => { var activity = context.Activity; context.Log.Info($"[MESSAGE] Received: {SanitizeForLog(activity.Text)}"); context.Log.Info($"[MESSAGE] From: {SanitizeForLog(activity.From?.Name ?? "unknown")}"); await context.Send($"Echo: {activity.Text}\n\nThis is a message extension bot. Use the message extension commands in Teams to test functionality.", cancellationToken); }); teams.OnQuery((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_QUERY] Search query received"); var activity = context.Activity; var commandId = activity.Value?.CommandId; var query = activity.Value?.Parameters?.FirstOrDefault(p => p.Name == "searchQuery")?.Value?.ToString() ?? ""; context.Log.Info($"[MESSAGE_EXT_QUERY] Command: {commandId}, Query: {query}"); if (commandId == "searchQuery") { return Task.FromResult(CreateSearchResults(query, context.Log)); } return Task.FromResult(new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = new List() } }); }); teams.OnSubmitAction((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_SUBMIT] Action submit received"); var activity = context.Activity; var commandId = activity.Value?.CommandId; var data = activity.Value?.Data as JsonElement?; context.Log.Info($"[MESSAGE_EXT_SUBMIT] Command: {commandId}"); context.Log.Info($"[MESSAGE_EXT_SUBMIT] Data: {JsonSerializer.Serialize(data)}"); var response = commandId switch { "createCard" => HandleCreateCard(data, context.Log), "getMessageDetails" => HandleGetMessageDetails(activity, context.Log), _ => CreateErrorActionResponse("Unknown command") }; return Task.FromResult(response); }); teams.OnQueryLink((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_QUERY_LINK] Link unfurling received"); var activity = context.Activity; var url = activity.Value?.Url; context.Log.Info($"[MESSAGE_EXT_QUERY_LINK] URL: {url}"); if (string.IsNullOrEmpty(url)) { return Task.FromResult(CreateErrorResponse("No URL provided")); } return Task.FromResult(CreateLinkUnfurlResponse(url, context.Log)); }); teams.OnSelectItem((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_SELECT_ITEM] Item selection received"); var activity = context.Activity; var selectedItem = activity.Value; context.Log.Info($"[MESSAGE_EXT_SELECT_ITEM] Selected: {JsonSerializer.Serialize(selectedItem)}"); return Task.FromResult(CreateItemSelectionResponse(selectedItem, context.Log)); }); teams.OnQuerySettingsUrl((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_QUERY_SETTINGS_URL] Settings URL requested"); return Task.FromResult(new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Config, Text = "Settings configuration would be handled here" } }); }); teams.OnFetchTask((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_FETCH_TASK] Fetch task received"); var activity = context.Activity; var commandId = activity.Value?.CommandId; context.Log.Info($"[MESSAGE_EXT_FETCH_TASK] Command: {commandId}"); return Task.FromResult(CreateFetchTaskResponse(commandId, context.Log)); }); teams.OnSetting((context, cancellationToken) => { context.Log.Info("[MESSAGE_EXT_SETTING] Settings received"); var activity = context.Activity; var state = activity.Value?.State; context.Log.Info($"[MESSAGE_EXT_SETTING] State: {state}"); if (state == "cancel") { context.Log.Info("[MESSAGE_EXT_SETTING] Settings cancelled by user"); } else { context.Log.Info("[MESSAGE_EXT_SETTING] Settings processing completed"); } return Task.CompletedTask; }); app.Run(); static string SanitizeForLog(string? input) { if (input == null) return ""; return input.Replace("\r", "").Replace("\n", ""); } static Microsoft.Teams.Api.MessageExtensions.Response CreateSearchResults(string query, Microsoft.Teams.Common.Logging.ILogger log) { var attachments = new List(); for (int i = 1; i <= 5; i++) { var card = new Microsoft.Teams.Cards.AdaptiveCard { Body = new List { new TextBlock($"Search Result {i}") { Weight = TextWeight.Bolder, Size = TextSize.Large }, new TextBlock($"Query: '{query}' - Result description for item {i}") { Wrap = true, IsSubtle = true } } }; var previewCard = new ThumbnailCard() { Title = $"Result {i}", Text = $"This is a preview of result {i} for query '{query}'." }; var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = Microsoft.Teams.Api.ContentType.AdaptiveCard, Content = card, Preview = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = Microsoft.Teams.Api.ContentType.ThumbnailCard, Content = previewCard } }; attachments.Add(attachment); } return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = attachments } }; } static Microsoft.Teams.Api.MessageExtensions.Response HandleCreateCard(JsonElement? data, Microsoft.Teams.Common.Logging.ILogger log) { var title = GetJsonValue(data, "title") ?? "Default Title"; var description = GetJsonValue(data, "description") ?? "Default Description"; log.Info($"[CREATE_CARD] Title: {title}, Description: {description}"); var card = new Microsoft.Teams.Cards.AdaptiveCard { Schema = "http://adaptivecards.io/schemas/adaptive-card.json", Body = new List { new TextBlock("Custom Card Created") { Weight = TextWeight.Bolder, Size = TextSize.Large, Color = TextColor.Good }, new TextBlock(title) { Weight = TextWeight.Bolder, Size = TextSize.Medium }, new TextBlock(description) { Wrap = true, IsSubtle = true } } }; var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = Microsoft.Teams.Api.ContentType.AdaptiveCard, Content = card }; return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = new List { attachment } } }; } static Microsoft.Teams.Api.MessageExtensions.Response HandleGetMessageDetails(Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.SubmitActionActivity activity, Microsoft.Teams.Common.Logging.ILogger log) { var messageText = activity.Value?.MessagePayload?.Body?.Content ?? "No message content"; var messageId = activity.Value?.MessagePayload?.Id ?? "Unknown"; log.Info($"[GET_MESSAGE_DETAILS] Message ID: {messageId}"); var card = new Microsoft.Teams.Cards.AdaptiveCard { Schema = "http://adaptivecards.io/schemas/adaptive-card.json", Body = new List { new TextBlock("Message Details") { Weight = TextWeight.Bolder, Size = TextSize.Large, Color = TextColor.Accent }, new TextBlock($"Message ID: {messageId}") { Wrap = true }, new TextBlock($"Content: {messageText}") { Wrap = true } } }; var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"), Content = card }; return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = new List { attachment } } }; } static Microsoft.Teams.Api.MessageExtensions.Response CreateLinkUnfurlResponse(string url, Microsoft.Teams.Common.Logging.ILogger log) { var card = new Microsoft.Teams.Cards.AdaptiveCard { Schema = "http://adaptivecards.io/schemas/adaptive-card.json", Body = new List { new TextBlock("Link Preview") { Weight = TextWeight.Bolder, Size = TextSize.Medium }, new TextBlock($"URL: {url}") { IsSubtle = true, Wrap = true }, new TextBlock("This is a preview of the linked content generated by the message extension.") { Wrap = true, Size = TextSize.Small } } }; var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"), Content = card, Preview = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = Microsoft.Teams.Api.ContentType.ThumbnailCard, Content = new ThumbnailCard { Title = "Link Preview", Text = url } } }; return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = new List { attachment } } }; } static Microsoft.Teams.Api.MessageExtensions.Response CreateItemSelectionResponse(object? selectedItem, Microsoft.Teams.Common.Logging.ILogger log) { var itemJson = JsonSerializer.Serialize(selectedItem); var card = new Microsoft.Teams.Cards.AdaptiveCard { Schema = "http://adaptivecards.io/schemas/adaptive-card.json", Body = new List { new TextBlock("Item Selected") { Weight = TextWeight.Bolder, Size = TextSize.Large, Color = TextColor.Good }, new TextBlock("You selected the following item:") { Wrap = true }, new TextBlock(itemJson) { Wrap = true, FontType = FontType.Monospace, Separator = true } } }; var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment { ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"), Content = card }; return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result, AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List, Attachments = new List { attachment } } }; } static Microsoft.Teams.Api.MessageExtensions.Response CreateErrorResponse(string message) { return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Message, Text = message } }; } static Microsoft.Teams.Api.MessageExtensions.Response CreateErrorActionResponse(string message) { return new Microsoft.Teams.Api.MessageExtensions.Response { ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result { Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Message, Text = message } }; } static string? GetJsonValue(JsonElement? data, string key) { if (data?.ValueKind == JsonValueKind.Object && data.Value.TryGetProperty(key, out var value)) { return value.GetString(); } return null; } static Microsoft.Teams.Api.MessageExtensions.ActionResponse CreateFetchTaskResponse(string? commandId, Microsoft.Teams.Common.Logging.ILogger log) { log.Info($"[CREATE_FETCH_TASK] Creating task for command: {commandId}"); var card = new Microsoft.Teams.Cards.AdaptiveCard { Body = new List { new TextBlock("Conversation Members is not implemented in C# yet :(") { Weight = TextWeight.Bolder, Color = TextColor.Accent }, } }; return new Microsoft.Teams.Api.MessageExtensions.ActionResponse { Task = new Microsoft.Teams.Api.TaskModules.ContinueTask(new Microsoft.Teams.Api.TaskModules.TaskInfo { Title = "Fetch Task Dialog", Height = new Union(Microsoft.Teams.Api.TaskModules.Size.Small), Width = new Union(Microsoft.Teams.Api.TaskModules.Size.Small), Card = new Microsoft.Teams.Api.Attachment(card) }) }; } static string GetSettingsHtml() { return """ Message Extension Settings

Message Extension Settings

"""; }