microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
aamirj/minimal

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Samples/Samples.MessageExtensions/Program.cs

834lines · modecode

1using System.Text.Json;
2
3using Microsoft.Teams.Api.Cards;
4using Microsoft.Teams.Apps;
5using Microsoft.Teams.Apps.Activities;
6using Microsoft.Teams.Apps.Activities.Invokes;
7using Microsoft.Teams.Apps.Annotations;
8using Microsoft.Teams.Apps.Extensions;
9using Microsoft.Teams.Cards;
10using Microsoft.Teams.Plugins.AspNetCore.DevTools.Extensions;
11using Microsoft.Teams.Plugins.AspNetCore.Extensions;
12
13namespace Samples.MessageExtensions;
14
15public static partial class Program
16{
17 public static void Main(string[] args)
18 {
19 var builder = WebApplication.CreateBuilder(args);
20
21 // Use environment variable to choose between Controller and Minimal API approaches
22 var useMinimalApi = builder.Configuration.GetValue<bool>("USE_MINIMAL_API", false);
23 if (useMinimalApi)
24 {
25 Console.WriteLine("Using Minimal API approach");
26 }
27 else
28 {
29 Console.WriteLine("Using Controller approach");
30 }
31
32 if (!useMinimalApi)
33 {
34 builder.Services.AddTransient<Controller>();
35 }
36
37 builder.AddTeams().AddTeamsDevTools();
38
39 var app = builder.Build();
40
41 app.UseHttpsRedirection();
42
43 // Log raw requests
44 app.Use(async (context, next) =>
45 {
46 if (context.Request.Method == "POST")
47 {
48 context.Request.EnableBuffering();
49 var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
50 context.Request.Body.Position = 0;
51
52 // var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
53 Console.WriteLine($"[RAW_REQUEST] {context.Request.Method} {context.Request.Path}: {body}");
54 }
55
56 await next();
57 });
58
59 var teams = app.UseTeams();
60
61 if (useMinimalApi)
62 {
63 RegisterMinimalApiHandlers(teams, app.Configuration);
64 }
65
66 // Serve settings page
67 app.MapGet("/settings", () => Results.Content(GetSettingsHtml(), "text/html"));
68
69 app.Run();
70 }
71
72 private static void RegisterMinimalApiHandlers(Microsoft.Teams.Apps.App teams, IConfiguration configuration)
73 {
74 // Handle messages using minimal API
75 teams.OnMessage(async context =>
76 {
77 var activity = context.Activity;
78
79 context.Log.Info($"[MESSAGE] Received: {SanitizeForLog(activity.Text)}");
80 context.Log.Info($"[MESSAGE] From: {SanitizeForLog(activity.From?.Name ?? "unknown")}");
81
82 await context.Send($"Echo: {activity.Text}\n\nThis is a message extension bot. Use the message extension commands in Teams to test functionality.");
83 });
84
85 // Handle message extension query using minimal API
86 teams.OnQuery(async context =>
87 {
88 var activity = context.Activity;
89
90 context.Log.Info("[MESSAGE_EXT_QUERY] Search query received");
91
92 var commandId = activity.Value?.CommandId;
93 var query = activity.Value?.Parameters?.FirstOrDefault(p => p.Name == "searchQuery")?.Value?.ToString() ?? "";
94
95 context.Log.Info($"[MESSAGE_EXT_QUERY] Command: {commandId}, Query: {query}");
96
97 if (commandId == "searchQuery")
98 {
99 return CreateSearchResults(query, context.Log);
100 }
101
102 return new Microsoft.Teams.Api.MessageExtensions.Response
103 {
104 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
105 {
106 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
107 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
108 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment>()
109 }
110 };
111 });
112
113 // Handle message extension submit action using minimal API
114 teams.OnSubmitAction(async context =>
115 {
116 var activity = context.Activity;
117
118 context.Log.Info("[MESSAGE_EXT_SUBMIT] Action submit received");
119
120 var commandId = activity.Value?.CommandId;
121 var data = activity.Value?.Data as JsonElement?;
122
123 context.Log.Info($"[MESSAGE_EXT_SUBMIT] Command: {commandId}");
124 context.Log.Info($"[MESSAGE_EXT_SUBMIT] Data: {JsonSerializer.Serialize(data)}");
125
126 switch (commandId)
127 {
128 case "createCard":
129 var cardResponse = HandleCreateCard(data, context.Log);
130 return new Microsoft.Teams.Api.MessageExtensions.ActionResponse
131 {
132 ComposeExtension = cardResponse.ComposeExtension
133 };
134
135 case "getMessageDetails":
136 // Convert to the correct activity type for HandleGetMessageDetails
137 var messageText = activity.Value?.MessagePayload?.Body?.Content ?? "No message content";
138 var messageId = activity.Value?.MessagePayload?.Id ?? "Unknown";
139 var detailsResponse = CreateMessageDetailsResponse(messageId, messageText, context.Log);
140 return new Microsoft.Teams.Api.MessageExtensions.ActionResponse
141 {
142 ComposeExtension = detailsResponse.ComposeExtension
143 };
144
145 default:
146 context.Log.Error($"[MESSAGE_EXT_SUBMIT] Unknown command: {commandId}");
147 return new Microsoft.Teams.Api.MessageExtensions.ActionResponse
148 {
149 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
150 {
151 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Message,
152 Text = "Unknown command"
153 }
154 };
155 }
156 });
157
158 // Handle message extension query link using minimal API
159 teams.OnQueryLink(async context =>
160 {
161 var activity = context.Activity;
162
163 context.Log.Info("[MESSAGE_EXT_QUERY_LINK] Link unfurling received");
164
165 var url = activity.Value?.Url;
166 context.Log.Info($"[MESSAGE_EXT_QUERY_LINK] URL: {url}");
167
168 if (string.IsNullOrEmpty(url))
169 {
170 return CreateErrorResponse("No URL provided");
171 }
172
173 return CreateLinkUnfurlResponse(url, context.Log);
174 });
175
176 // Handle message extension select item using minimal API
177 teams.OnSelectItem(async context =>
178 {
179 var activity = context.Activity;
180
181 context.Log.Info("[MESSAGE_EXT_SELECT_ITEM] Item selection received");
182
183 var selectedItem = activity.Value;
184 context.Log.Info($"[MESSAGE_EXT_SELECT_ITEM] Selected: {JsonSerializer.Serialize(selectedItem)}");
185
186 return CreateItemSelectionResponse(selectedItem, context.Log);
187 });
188
189 // Handle message extension query settings URL using minimal API
190 teams.OnQuerySettingsUrl(async context =>
191 {
192 context.Log.Info("[MESSAGE_EXT_QUERY_SETTINGS_URL] Settings URL requested");
193
194 return new Microsoft.Teams.Api.MessageExtensions.Response
195 {
196 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
197 {
198 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Config,
199 Text = "Settings configuration would be handled here"
200 }
201 };
202 });
203
204 // Handle message extension fetch task using minimal API
205 teams.OnFetchTask(async context =>
206 {
207 var activity = context.Activity;
208
209 context.Log.Info("[MESSAGE_EXT_FETCH_TASK] Fetch task received");
210
211 var commandId = activity.Value?.CommandId;
212 context.Log.Info($"[MESSAGE_EXT_FETCH_TASK] Command: {commandId}");
213
214 return CreateFetchTaskResponse(commandId, context.Log);
215 });
216
217 // Handle message extension setting using minimal API
218 teams.OnSetting(async context =>
219 {
220 var activity = context.Activity;
221
222 context.Log.Info("[MESSAGE_EXT_SETTING] Settings received");
223
224 var state = activity.Value?.State;
225 context.Log.Info($"[MESSAGE_EXT_SETTING] State: {state}");
226
227 if (state == "cancel")
228 {
229 context.Log.Info("[MESSAGE_EXT_SETTING] Settings cancelled by user");
230 // OnSetting doesn't return a response, just handles the setting
231 return;
232 }
233
234 // Process settings data
235 // Note: Settings property may not be available in current API
236 context.Log.Info("[MESSAGE_EXT_SETTING] Settings processing completed");
237 });
238 }
239
240 // Helper method to sanitize user input for logging
241 private static string SanitizeForLog(string? input)
242 {
243 if (input == null) return "";
244 return input.Replace("\r", "").Replace("\n", "");
245 }
246
247 // Helper methods for creating responses
248 private static Microsoft.Teams.Api.MessageExtensions.Response CreateSearchResults(string query, Microsoft.Teams.Common.Logging.ILogger log)
249 {
250 var attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment>();
251
252 // Create simple search results
253 for (int i = 1; i <= 5; i++)
254 {
255 var card = new Microsoft.Teams.Cards.AdaptiveCard
256 {
257 Body = new List<CardElement>
258 {
259 new TextBlock($"Search Result {i}")
260 {
261 Weight = TextWeight.Bolder,
262 Size = TextSize.Large
263 },
264 new TextBlock($"Query: '{query}' - Result description for item {i}")
265 {
266 Wrap = true,
267 IsSubtle = true
268 }
269 }
270 };
271
272 var previewCard = new ThumbnailCard()
273 {
274 Title = $"Result {i}",
275 Text = $"This is a preview of result {i} for query '{query}'."
276 };
277
278 var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment
279 {
280 ContentType = Microsoft.Teams.Api.ContentType.AdaptiveCard,
281 Content = card,
282 Preview = new Microsoft.Teams.Api.MessageExtensions.Attachment
283 {
284 ContentType = Microsoft.Teams.Api.ContentType.ThumbnailCard,
285 Content = previewCard
286 }
287 };
288
289 attachments.Add(attachment);
290 }
291
292 return new Microsoft.Teams.Api.MessageExtensions.Response
293 {
294 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
295 {
296 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
297 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
298 Attachments = attachments
299 }
300 };
301 }
302
303 private static Microsoft.Teams.Api.MessageExtensions.Response HandleCreateCard(JsonElement? data, Microsoft.Teams.Common.Logging.ILogger log)
304 {
305 var title = GetJsonValue(data, "title") ?? "Default Title";
306 var description = GetJsonValue(data, "description") ?? "Default Description";
307
308 log.Info($"[CREATE_CARD] Title: {title}, Description: {description}");
309
310 var card = new Microsoft.Teams.Cards.AdaptiveCard
311 {
312 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
313 Body = new List<CardElement>
314 {
315 new TextBlock("Custom Card Created")
316 {
317 Weight = TextWeight.Bolder,
318 Size = TextSize.Large,
319 Color = TextColor.Good
320 },
321 new TextBlock(title)
322 {
323 Weight = TextWeight.Bolder,
324 Size = TextSize.Medium
325 },
326 new TextBlock(description)
327 {
328 Wrap = true,
329 IsSubtle = true
330 }
331 }
332 };
333
334 var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment
335 {
336 ContentType = Microsoft.Teams.Api.ContentType.AdaptiveCard,
337 Content = card
338 };
339
340 return new Microsoft.Teams.Api.MessageExtensions.Response
341 {
342 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
343 {
344 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
345 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
346 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment> { attachment }
347 }
348 };
349 }
350
351 private static Microsoft.Teams.Api.MessageExtensions.Response HandleGetMessageDetails(Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.SubmitActionActivity activity, Microsoft.Teams.Common.Logging.ILogger log)
352 {
353 var messageText = activity.Value?.MessagePayload?.Body?.Content ?? "No message content";
354 var messageId = activity.Value?.MessagePayload?.Id ?? "Unknown";
355
356 log.Info($"[GET_MESSAGE_DETAILS] Message ID: {messageId}");
357
358 return CreateMessageDetailsResponse(messageId, messageText, log);
359 }
360
361 private static Microsoft.Teams.Api.MessageExtensions.Response CreateMessageDetailsResponse(string messageId, string messageText, Microsoft.Teams.Common.Logging.ILogger log)
362 {
363 log.Info($"[GET_MESSAGE_DETAILS] Message ID: {messageId}");
364
365 var card = new Microsoft.Teams.Cards.AdaptiveCard
366 {
367 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
368 Body = new List<CardElement>
369 {
370 new TextBlock("Message Details")
371 {
372 Weight = TextWeight.Bolder,
373 Size = TextSize.Large,
374 Color = TextColor.Accent
375 },
376 new TextBlock($"Message ID: {messageId}")
377 {
378 Wrap = true
379 },
380 new TextBlock($"Content: {messageText}")
381 {
382 Wrap = true
383 }
384 }
385 };
386
387 var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment
388 {
389 ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"),
390 Content = card
391 };
392
393 return new Microsoft.Teams.Api.MessageExtensions.Response
394 {
395 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
396 {
397 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
398 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
399 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment> { attachment }
400 }
401 };
402 }
403
404 private static Microsoft.Teams.Api.MessageExtensions.Response CreateLinkUnfurlResponse(string url, Microsoft.Teams.Common.Logging.ILogger log)
405 {
406 var card = new Microsoft.Teams.Cards.AdaptiveCard
407 {
408 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
409 Body = new List<CardElement>
410 {
411 new TextBlock("Link Preview")
412 {
413 Weight = TextWeight.Bolder,
414 Size = TextSize.Medium
415 },
416 new TextBlock($"URL: {url}")
417 {
418 IsSubtle = true,
419 Wrap = true
420 },
421 new TextBlock("This is a preview of the linked content generated by the message extension.")
422 {
423 Wrap = true,
424 Size = TextSize.Small
425 }
426 }
427 };
428
429 var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment
430 {
431 ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"),
432 Content = card,
433 Preview = new Microsoft.Teams.Api.MessageExtensions.Attachment
434 {
435 ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.thumbnail"),
436 Content = new ThumbnailCard
437 {
438 Title = "Link Preview",
439 Text = url
440 }
441 }
442 };
443
444 return new Microsoft.Teams.Api.MessageExtensions.Response
445 {
446 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
447 {
448 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
449 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
450 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment> { attachment }
451 }
452 };
453 }
454
455 private static Microsoft.Teams.Api.MessageExtensions.Response CreateItemSelectionResponse(object? selectedItem, Microsoft.Teams.Common.Logging.ILogger log)
456 {
457 var itemJson = JsonSerializer.Serialize(selectedItem);
458
459 var card = new Microsoft.Teams.Cards.AdaptiveCard
460 {
461 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
462 Body = new List<CardElement>
463 {
464 new TextBlock("Item Selected")
465 {
466 Weight = TextWeight.Bolder,
467 Size = TextSize.Large,
468 Color = TextColor.Good
469 },
470 new TextBlock("You selected the following item:")
471 {
472 Wrap = true
473 },
474 new TextBlock(itemJson)
475 {
476 Wrap = true,
477 FontType = FontType.Monospace,
478 Separator = true
479 }
480 }
481 };
482
483 var attachment = new Microsoft.Teams.Api.MessageExtensions.Attachment
484 {
485 ContentType = new Microsoft.Teams.Api.ContentType("application/vnd.microsoft.card.adaptive"),
486 Content = card
487 };
488
489 return new Microsoft.Teams.Api.MessageExtensions.Response
490 {
491 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
492 {
493 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
494 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
495 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment> { attachment }
496 }
497 };
498 }
499
500 private static Microsoft.Teams.Api.MessageExtensions.Response CreateErrorResponse(string message)
501 {
502 return new Microsoft.Teams.Api.MessageExtensions.Response
503 {
504 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
505 {
506 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Message,
507 Text = message
508 }
509 };
510 }
511
512 private static Microsoft.Teams.Api.MessageExtensions.Response CreateErrorActionResponse(string message)
513 {
514 return new Microsoft.Teams.Api.MessageExtensions.Response
515 {
516 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
517 {
518 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Message,
519 Text = message
520 }
521 };
522 }
523
524 private static string? GetJsonValue(JsonElement? data, string key)
525 {
526 if (data?.ValueKind == JsonValueKind.Object && data.Value.TryGetProperty(key, out var value))
527 {
528 return value.GetString();
529 }
530 return null;
531 }
532
533 private static Microsoft.Teams.Api.MessageExtensions.ActionResponse CreateFetchTaskResponse(string? commandId, Microsoft.Teams.Common.Logging.ILogger log)
534 {
535 log.Info($"[CREATE_FETCH_TASK] Creating task for command: {commandId}");
536 // Updated to use actual converation members
537
538 // Create an adaptive card for the task module
539 var card = new Microsoft.Teams.Cards.AdaptiveCard
540 {
541 Body = new List<CardElement>
542 {
543 new TextBlock("Conversation Members is not implemented in C# yet :(")
544 {
545 Weight = TextWeight.Bolder,
546 Color = TextColor.Accent
547 },
548 }
549 };
550
551 return new Microsoft.Teams.Api.MessageExtensions.ActionResponse
552 {
553 Task = new Microsoft.Teams.Api.TaskModules.ContinueTask(new Microsoft.Teams.Api.TaskModules.TaskInfo
554 {
555 Title = "Fetch Task Dialog",
556 Height = new Microsoft.Teams.Common.Union<int, Microsoft.Teams.Api.TaskModules.Size>(Microsoft.Teams.Api.TaskModules.Size.Small),
557 Width = new Microsoft.Teams.Common.Union<int, Microsoft.Teams.Api.TaskModules.Size>(Microsoft.Teams.Api.TaskModules.Size.Small),
558 Card = new Microsoft.Teams.Api.Attachment(card)
559 })
560 };
561 }
562
563 [TeamsController]
564 public class Controller
565 {
566 private readonly IConfiguration _configuration;
567
568 public Controller(IConfiguration configuration)
569 {
570 _configuration = configuration;
571 }
572
573 [Message]
574 public async System.Threading.Tasks.Task OnMessage([Context] Microsoft.Teams.Api.Activities.MessageActivity activity, [Context] IContext.Client client, [Context] Microsoft.Teams.Common.Logging.ILogger log)
575 {
576 log.Info($"[MESSAGE] Received: {SanitizeForLog(activity.Text)}");
577 log.Info($"[MESSAGE] From: {SanitizeForLog(activity.From?.Name ?? "unknown")}");
578
579 await client.Send($"Echo: {activity.Text}\n\nThis is a message extension bot. Use the message extension commands in Teams to test functionality.");
580 }
581
582 [MessageExtension.Query]
583 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionQuery(
584 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.QueryActivity activity,
585 [Context] IContext.Client client,
586 [Context] Microsoft.Teams.Common.Logging.ILogger log)
587 {
588 log.Info("[MESSAGE_EXT_QUERY] Search query received");
589
590 var commandId = activity.Value?.CommandId;
591 var query = activity.Value?.Parameters?.FirstOrDefault(p => p.Name == "searchQuery")?.Value?.ToString() ?? "";
592
593 log.Info($"[MESSAGE_EXT_QUERY] Command: {commandId}, Query: {query}");
594
595 if (commandId == "searchQuery")
596 {
597 return CreateSearchResults(query, log);
598 }
599
600 return new Microsoft.Teams.Api.MessageExtensions.Response
601 {
602 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
603 {
604 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Result,
605 AttachmentLayout = Microsoft.Teams.Api.Attachment.Layout.List,
606 Attachments = new List<Microsoft.Teams.Api.MessageExtensions.Attachment>()
607 }
608 };
609 }
610
611 [MessageExtension.SubmitAction]
612 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionSubmit(
613 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.SubmitActionActivity activity,
614 [Context] IContext.Client client,
615 [Context] Microsoft.Teams.Common.Logging.ILogger log)
616 {
617 log.Info("[MESSAGE_EXT_SUBMIT] Action submit received");
618
619 var commandId = activity.Value?.CommandId;
620 var data = activity.Value?.Data as JsonElement?;
621
622 log.Info($"[MESSAGE_EXT_SUBMIT] Command: {commandId}");
623 log.Info($"[MESSAGE_EXT_SUBMIT] Data: {JsonSerializer.Serialize(data)}");
624
625 switch (commandId)
626 {
627 case "createCard":
628 return HandleCreateCard(data, log);
629
630 case "getMessageDetails":
631 return HandleGetMessageDetails(activity, log);
632
633 default:
634 log.Error($"[MESSAGE_EXT_SUBMIT] Unknown command: {commandId}");
635 return CreateErrorActionResponse("Unknown command");
636 }
637 }
638
639 [MessageExtension.QueryLink]
640 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionQueryLink(
641 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.QueryLinkActivity activity,
642 [Context] IContext.Client client,
643 [Context] Microsoft.Teams.Common.Logging.ILogger log)
644 {
645 log.Info("[MESSAGE_EXT_QUERY_LINK] Link unfurling received");
646
647 var url = activity.Value?.Url;
648 log.Info($"[MESSAGE_EXT_QUERY_LINK] URL: {url}");
649
650 if (string.IsNullOrEmpty(url))
651 {
652 return CreateErrorResponse("No URL provided");
653 }
654
655 return CreateLinkUnfurlResponse(url, log);
656 }
657
658 [MessageExtension.SelectItem]
659 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionSelectItem(
660 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.SelectItemActivity activity,
661 [Context] IContext.Client client,
662 [Context] Microsoft.Teams.Common.Logging.ILogger log)
663 {
664 log.Info("[MESSAGE_EXT_SELECT_ITEM] Item selection received");
665
666 var selectedItem = activity.Value;
667 log.Info($"[MESSAGE_EXT_SELECT_ITEM] Selected: {JsonSerializer.Serialize(selectedItem)}");
668
669 return CreateItemSelectionResponse(selectedItem, log);
670 }
671
672 [MessageExtension.QuerySettingsUrl]
673 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionQuerySettingsUrl(
674 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.QuerySettingUrlActivity activity,
675 [Context] IContext.Client client,
676 [Context] Microsoft.Teams.Common.Logging.ILogger log)
677 {
678 log.Info("[MESSAGE_EXT_QUERY_SETTINGS_URL] Settings URL requested");
679
680 return new Microsoft.Teams.Api.MessageExtensions.Response
681 {
682 ComposeExtension = new Microsoft.Teams.Api.MessageExtensions.Result
683 {
684 Type = Microsoft.Teams.Api.MessageExtensions.ResultType.Config,
685 Text = "Settings configuration would be handled here"
686 }
687 };
688 }
689
690 [MessageExtension.FetchTask]
691 public async Task<Microsoft.Teams.Api.MessageExtensions.ActionResponse> OnMessageExtensionFetchTask(
692 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.FetchTaskActivity activity,
693 [Context] Microsoft.Teams.Common.Logging.ILogger log)
694 {
695 log.Info("[MESSAGE_EXT_FETCH_TASK] Fetch task received");
696
697 var commandId = activity.Value?.CommandId;
698 log.Info($"[MESSAGE_EXT_FETCH_TASK] Command: {commandId}");
699
700 return CreateFetchTaskResponse(commandId, log);
701 }
702
703 [MessageExtension.Setting]
704 public Microsoft.Teams.Api.MessageExtensions.Response OnMessageExtensionSetting(
705 [Context] Microsoft.Teams.Api.Activities.Invokes.MessageExtensions.SettingActivity activity,
706 [Context] IContext.Client client,
707 [Context] Microsoft.Teams.Common.Logging.ILogger log)
708 {
709 log.Info("[MESSAGE_EXT_SETTING] Settings received");
710
711 var state = activity.Value?.State;
712 log.Info($"[MESSAGE_EXT_SETTING] State: {state}");
713
714 if (state == "cancel")
715 {
716 log.Info("[MESSAGE_EXT_SETTING] Settings cancelled by user");
717 return new Microsoft.Teams.Api.MessageExtensions.Response();
718 }
719
720 // Process settings data
721 // Note: Settings property may not be available in current API
722 log.Info("[MESSAGE_EXT_SETTING] Settings processing completed");
723
724 return new Microsoft.Teams.Api.MessageExtensions.Response();
725 }
726 }
727
728 private static string GetSettingsHtml()
729 {
730 return """
731<!DOCTYPE html>
732<html>
733<head>
734 <title>Message Extension Settings</title>
735 <meta charset="utf-8">
736 <meta name="viewport" content="width=device-width, initial-scale=1.0">
737 <script src="https://statics.teams.cdn.office.net/sdk/v1.12.0/js/MicrosoftTeams.min.js"></script>
738 <style>
739 body {
740 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
741 margin: 20px;
742 background-color: #f5f5f5;
743 }
744 .container {
745 background-color: white;
746 padding: 20px;
747 border-radius: 8px;
748 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
749 max-width: 500px;
750 }
751 .form-group {
752 margin-bottom: 15px;
753 }
754 label {
755 display: block;
756 margin-bottom: 5px;
757 font-weight: 600;
758 }
759 select, input {
760 width: 100%;
761 padding: 8px;
762 border: 1px solid #ddd;
763 border-radius: 4px;
764 font-size: 14px;
765 }
766 .buttons {
767 margin-top: 20px;
768 text-align: right;
769 }
770 button {
771 padding: 8px 16px;
772 margin-left: 8px;
773 border: none;
774 border-radius: 4px;
775 cursor: pointer;
776 font-size: 14px;
777 }
778 .btn-primary {
779 background-color: #0078d4;
780 color: white;
781 }
782 .btn-secondary {
783 background-color: #6c757d;
784 color: white;
785 }
786 </style>
787</head>
788<body>
789 <div class="container">
790 <h2>Message Extension Settings</h2>
791 <form id="settingsForm">
792 <div class="form-group">
793 <label for="defaultAction">Default Action:</label>
794 <select id="defaultAction" name="defaultAction">
795 <option value="search">Search</option>
796 <option value="compose">Compose</option>
797 <option value="both">Both</option>
798 </select>
799 </div>
800
801 <div class="form-group">
802 <label for="maxResults">Max Search Results:</label>
803 <input type="number" id="maxResults" name="maxResults" value="10" min="1" max="50">
804 </div>
805
806 <div class="buttons">
807 <button type="button" class="btn-secondary" onclick="cancelSettings()">Cancel</button>
808 <button type="button" class="btn-primary" onclick="saveSettings()">Save</button>
809 </div>
810 </form>
811 </div>
812
813 <script>
814 microsoftTeams.initialize();
815
816 function saveSettings() {
817 const formData = new FormData(document.getElementById('settingsForm'));
818 const settings = {};
819 for (let [key, value] of formData.entries()) {
820 settings[key] = value;
821 }
822
823 microsoftTeams.tasks.submitTask(settings);
824 }
825
826 function cancelSettings() {
827 microsoftTeams.tasks.submitTask();
828 }
829 </script>
830</body>
831</html>
832""";
833 }
834}