microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
733a37f2b7310e6fd954187427f2af0e713dc286

Branches

Tags

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

Clone

HTTPS

Download ZIP

Samples/Samples.Cards/Program.cs

356lines · modecode

1using System.Text.Json;
2
3using Microsoft.Teams.Api.AdaptiveCards;
4using Microsoft.Teams.Apps.Activities;
5using Microsoft.Teams.Apps.Activities.Invokes;
6using Microsoft.Teams.Apps.Extensions;
7using Microsoft.Teams.Cards;
8using Microsoft.Teams.Common;
9using Microsoft.Teams.Plugins.AspNetCore.Extensions;
10
11var builder = WebApplication.CreateBuilder(args);
12builder.WebHost.UseUrls("http://localhost:3978");
13builder.Services.AddOpenApi();
14builder.AddTeams();
15
16var app = builder.Build();
17
18if (app.Environment.IsDevelopment())
19{
20 app.MapOpenApi();
21}
22
23app.UseHttpsRedirection();
24var teams = app.UseTeams();
25
26teams.OnMessage(async (context, cancellationToken) =>
27{
28 var activity = context.Activity;
29 context.Log.Info($"[MESSAGE] Received: {SanitizeForLog(activity.Text)}");
30 context.Log.Info($"[MESSAGE] From: {SanitizeForLog(activity.From?.Name ?? "unknown")}");
31
32 var text = activity.Text?.ToLowerInvariant() ?? "";
33
34 if (text.Contains("card"))
35 {
36 context.Log.Info("[CARD] Basic card requested");
37 var card = CreateBasicAdaptiveCard();
38 await context.Send(card, cancellationToken);
39 }
40 else if (text.Contains("profile"))
41 {
42 context.Log.Info("[PROFILE] Profile card requested");
43 var card = CreateProfileCard();
44 await context.Send(card, cancellationToken);
45 }
46 else if (text.Contains("validation"))
47 {
48 context.Log.Info("[VALIDATION] Validation card requested");
49 var card = CreateProfileCardWithValidation();
50 await context.Send(card, cancellationToken);
51 }
52 else if (text.Contains("feedback"))
53 {
54 context.Log.Info("[FEEDBACK] Feedback card requested");
55 var card = CreateFeedbackCard();
56 await context.Send(card, cancellationToken);
57 }
58 else if (text.Contains("form"))
59 {
60 context.Log.Info("[FORM] Task form card requested");
61 var card = CreateTaskFormCard();
62 await context.Send(card, cancellationToken);
63 }
64 else if (text.Contains("json"))
65 {
66 context.Log.Info("[JSON] JSON deserialization card requested");
67 var card = CreateCardFromJson();
68 await context.Send(card, cancellationToken);
69 }
70 else if (text.Contains("reply"))
71 {
72 await context.Send("Hello! How can I assist you today?", cancellationToken);
73 }
74 else
75 {
76 await context.Typing(cancellationToken: cancellationToken);
77 await context.Send($"You said '{activity.Text}'. Try typing: card, profile, validation, feedback, form, json, or reply", cancellationToken);
78 }
79});
80
81teams.OnAdaptiveCardAction(async (context, cancellationToken) =>
82{
83 var activity = context.Activity;
84 context.Log.Info("[CARD_ACTION] Card action received");
85
86 var data = activity.Value?.Action?.Data;
87
88 context.Log.Info($"[CARD_ACTION] Raw data: {JsonSerializer.Serialize(data)}");
89
90 if (data == null)
91 {
92 context.Log.Error("[CARD_ACTION] No data in card action");
93 return new ActionResponse.Message("No data specified") { StatusCode = 400 };
94 }
95
96 string? action = data.TryGetValue("action", out var actionObj) ? actionObj?.ToString() : null;
97
98 if (string.IsNullOrEmpty(action))
99 {
100 context.Log.Error("[CARD_ACTION] No action specified in card data");
101 return new ActionResponse.Message("No action specified") { StatusCode = 400 };
102 }
103 context.Log.Info($"[CARD_ACTION] Processing action: {action}");
104
105 string? GetFormValue(string key)
106 {
107 if (data.TryGetValue(key, out var val))
108 {
109 if (val is JsonElement element)
110 return element.GetString();
111 return val?.ToString();
112 }
113 return null;
114 }
115
116 switch (action)
117 {
118 case "submit_basic":
119 var notifyValue = GetFormValue("notify") ?? "false";
120 await context.Send($"Basic card submitted! Notify setting: {notifyValue}", cancellationToken);
121 break;
122
123 case "submit_feedback":
124 var feedbackText = GetFormValue("feedback") ?? "No feedback provided";
125 await context.Send($"Feedback received: {feedbackText}", cancellationToken);
126 break;
127
128 case "create_task":
129 var title = GetFormValue("title") ?? "Untitled";
130 var priority = GetFormValue("priority") ?? "medium";
131 var dueDate = GetFormValue("due_date") ?? "No date";
132 await context.Send($"Task created!\nTitle: {title}\nPriority: {priority}\nDue: {dueDate}", cancellationToken);
133 break;
134
135 case "save_profile":
136 var name = GetFormValue("name") ?? "Unknown";
137 var email = GetFormValue("email") ?? "No email";
138 var subscribe = GetFormValue("subscribe") ?? "false";
139 var age = GetFormValue("age");
140 var location = GetFormValue("location") ?? "Not specified";
141
142 var response = $"Profile saved!\nName: {name}\nEmail: {email}\nSubscribed: {subscribe}";
143 if (!string.IsNullOrEmpty(age))
144 response += $"\nAge: {age}";
145 if (location != "Not specified")
146 response += $"\nLocation: {location}";
147
148 await context.Send(response, cancellationToken);
149 break;
150
151 case "test_json":
152 await context.Send("JSON deserialization test successful!", cancellationToken);
153 break;
154
155 default:
156 context.Log.Error($"[CARD_ACTION] Unknown action: {action}");
157 return new ActionResponse.Message("Unknown action") { StatusCode = 400 };
158 }
159
160 return new ActionResponse.Message("Action processed successfully") { StatusCode = 200 };
161});
162
163app.Run();
164
165static string SanitizeForLog(string? input)
166{
167 if (input == null) return "";
168 return input.Replace("\r", "").Replace("\n", "");
169}
170
171static Microsoft.Teams.Cards.AdaptiveCard CreateBasicAdaptiveCard()
172{
173 return new Microsoft.Teams.Cards.AdaptiveCard
174 {
175 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
176 Body = new List<CardElement>
177 {
178 new TextBlock("Hello world") { Wrap = true, Weight = TextWeight.Bolder },
179 new ToggleInput("Notify me") { Id = "notify" }
180 },
181 Actions = new List<Microsoft.Teams.Cards.Action>
182 {
183 new ExecuteAction
184 {
185 Title = "Submit",
186 Data = new Union<string, SubmitActionData>(new SubmitData("submit_basic")),
187 AssociatedInputs = AssociatedInputs.Auto
188 }
189 }
190 };
191}
192
193static Microsoft.Teams.Cards.AdaptiveCard CreateProfileCard()
194{
195 return new Microsoft.Teams.Cards.AdaptiveCard
196 {
197 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
198 Body = new List<CardElement>
199 {
200 new TextBlock("User Profile") { Weight = TextWeight.Bolder, Size = TextSize.Large },
201 new TextInput { Id = "name", Label = "Name", Value = "John Doe" },
202 new TextInput { Id = "email", Label = "Email", Value = "john@contoso.com" },
203 new ToggleInput("Subscribe to newsletter") { Id = "subscribe", Value = "false" }
204 },
205 Actions = new List<Microsoft.Teams.Cards.Action>
206 {
207 new ExecuteAction
208 {
209 Title = "Save",
210 Data = new Union<string, SubmitActionData>(new SubmitData("save_profile", new Dictionary<string, object?> { ["entity_id"] = "12345" })),
211 AssociatedInputs = AssociatedInputs.Auto
212 },
213 new OpenUrlAction("https://adaptivecards.microsoft.com") { Title = "Learn More" }
214 }
215 };
216}
217
218static Microsoft.Teams.Cards.AdaptiveCard CreateProfileCardWithValidation()
219{
220 return new Microsoft.Teams.Cards.AdaptiveCard
221 {
222 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
223 Body = new List<CardElement>
224 {
225 new TextBlock("Profile with Validation") { Weight = TextWeight.Bolder, Size = TextSize.Large },
226 new NumberInput { Id = "age", Label = "Age", IsRequired = true, Min = 0, Max = 120 },
227 new TextInput { Id = "name", Label = "Name", IsRequired = true, ErrorMessage = "Name is required" },
228 new TextInput { Id = "location", Label = "Location" }
229 },
230 Actions = new List<Microsoft.Teams.Cards.Action>
231 {
232 new ExecuteAction
233 {
234 Title = "Save",
235 Data = new Union<string, SubmitActionData>(new SubmitData("save_profile")),
236 AssociatedInputs = AssociatedInputs.Auto
237 }
238 }
239 };
240}
241
242static Microsoft.Teams.Cards.AdaptiveCard CreateFeedbackCard()
243{
244 return new Microsoft.Teams.Cards.AdaptiveCard
245 {
246 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
247 Body = new List<CardElement>
248 {
249 new TextBlock("Feedback Form") { Weight = TextWeight.Bolder, Size = TextSize.Large },
250 new TextInput { Id = "feedback", Label = "Your Feedback", Placeholder = "Please share your thoughts...", IsMultiline = true, IsRequired = true }
251 },
252 Actions = new List<Microsoft.Teams.Cards.Action>
253 {
254 new ExecuteAction
255 {
256 Title = "Submit Feedback",
257 Data = new Union<string, SubmitActionData>(new SubmitData("submit_feedback")),
258 AssociatedInputs = AssociatedInputs.Auto
259 }
260 }
261 };
262}
263
264static Microsoft.Teams.Cards.AdaptiveCard CreateTaskFormCard()
265{
266 return new Microsoft.Teams.Cards.AdaptiveCard
267 {
268 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
269 Body = new List<CardElement>
270 {
271 new TextBlock("Create New Task") { Weight = TextWeight.Bolder, Size = TextSize.Large },
272 new TextInput { Id = "title", Label = "Task Title", Placeholder = "Enter task title" },
273 new TextInput { Id = "description", Label = "Description", Placeholder = "Enter task details", IsMultiline = true },
274 new ChoiceSetInput
275 {
276 Id = "priority",
277 Label = "Priority",
278 Value = "medium",
279 Choices = new List<Choice>
280 {
281 new() { Title = "High", Value = "high" },
282 new() { Title = "Medium", Value = "medium" },
283 new() { Title = "Low", Value = "low" }
284 }
285 },
286 new DateInput { Id = "due_date", Label = "Due Date", Value = DateTime.Now.ToString("yyyy-MM-dd") }
287 },
288 Actions = new List<Microsoft.Teams.Cards.Action>
289 {
290 new ExecuteAction
291 {
292 Title = "Create Task",
293 Data = new Union<string, SubmitActionData>(new SubmitData("create_task")),
294 AssociatedInputs = AssociatedInputs.Auto,
295 Style = ActionStyle.Positive
296 }
297 }
298 };
299}
300
301static Microsoft.Teams.Cards.AdaptiveCard CreateCardFromJson()
302{
303 var cardJson = @"{
304 ""type"": ""AdaptiveCard"",
305 ""body"": [
306 {
307 ""type"": ""ColumnSet"",
308 ""columns"": [
309 {
310 ""type"": ""Column"",
311 ""verticalContentAlignment"": ""center"",
312 ""items"": [{ ""type"": ""Image"", ""style"": ""Person"", ""url"": ""https://aka.ms/AAp9xo4"", ""size"": ""Small"", ""altText"": ""Portrait of David Claux"" }],
313 ""width"": ""auto""
314 },
315 {
316 ""type"": ""Column"",
317 ""spacing"": ""medium"",
318 ""verticalContentAlignment"": ""center"",
319 ""items"": [{ ""type"": ""TextBlock"", ""weight"": ""Bolder"", ""text"": ""David Claux"", ""wrap"": true }],
320 ""width"": ""auto""
321 },
322 {
323 ""type"": ""Column"",
324 ""spacing"": ""medium"",
325 ""verticalContentAlignment"": ""center"",
326 ""items"": [{ ""type"": ""TextBlock"", ""text"": ""Principal Platform Architect at Microsoft"", ""isSubtle"": true, ""wrap"": true }],
327 ""width"": ""stretch""
328 }
329 ]
330 },
331 { ""type"": ""TextBlock"", ""text"": ""This card was created from JSON deserialization!"", ""wrap"": true, ""color"": ""good"", ""spacing"": ""medium"" }
332 ],
333 ""actions"": [{ ""type"": ""Action.Execute"", ""title"": ""Test JSON Action"", ""data"": { ""Value"": { ""action"": ""test_json"" } }, ""associatedInputs"": ""auto"" }],
334 ""version"": ""1.5"",
335 ""schema"": ""http://adaptivecards.io/schemas/adaptive-card.json""
336 }";
337
338 try
339 {
340 var card = JsonSerializer.Deserialize<Microsoft.Teams.Cards.AdaptiveCard>(cardJson);
341 return card ?? throw new InvalidOperationException("Failed to deserialize card");
342 }
343 catch (Exception ex)
344 {
345 Console.WriteLine($"Error deserializing card JSON: {ex.Message}");
346 return new Microsoft.Teams.Cards.AdaptiveCard
347 {
348 Schema = "http://adaptivecards.io/schemas/adaptive-card.json",
349 Body = new List<CardElement>
350 {
351 new TextBlock("JSON Deserialization Test") { Weight = TextWeight.Bolder, Size = TextSize.Large, Color = TextColor.Attention },
352 new TextBlock($"Deserialization failed: {ex.Message}") { Wrap = true, Color = TextColor.Attention }
353 }
354 };
355 }
356}