microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/update-sample-to-blazor

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/ExtAIBot/ExtAIBotApp.cs

129lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.AspNetCore.Http;
5using Microsoft.Teams.Apps;
6using Microsoft.Teams.Apps.Api.Clients;
7using Microsoft.Teams.Apps.Handlers;
8using Microsoft.Teams.Apps.Handlers.TaskModules;
9using Microsoft.Teams.Apps.Schema;
10using Microsoft.Teams.Apps.Schema.Entities;
11using Microsoft.Teams.Cards;
12
13namespace ExtAIBot;
14
15// Teams bot subclass: wires the four activity handlers (message, clarification card
16// submit, custom-feedback fetch/submit) in its constructor. Each handler funnels a
17// user-supplied string back through the Agent.
18internal class ExtAIBotApp : TeamsBotApplication
19{
20 private readonly Agent _agent;
21 private readonly ILogger<ExtAIBotApp> _logger;
22
23 public ExtAIBotApp(
24 Agent agent,
25 ApiClient api,
26 IHttpContextAccessor accessor,
27 ILogger<ExtAIBotApp> logger,
28 TeamsBotApplicationOptions? options = null)
29 : base(api, accessor, logger, options)
30 {
31 _agent = agent;
32 _logger = logger;
33
34 // Message handler.
35 this.OnMessage(async (context, cancellationToken) =>
36 {
37 string userText = context.Activity.TextWithoutMentions ?? "";
38 await RespondAsync(context, userText, cancellationToken);
39 });
40
41 // Clarification: adaptive card action.
42 // Triggered when the user submits the clarification card (Action.Execute, verb "clarification").
43 this.OnAdaptiveCardAction(async (context, cancellationToken) =>
44 {
45 if (context.Activity.Value?.Action?.Verb == "clarification")
46 {
47 string choice = context.Activity.Value.Action.Data?["clarificationChoice"]?.ToString() ?? "";
48 await RespondAsync(context, choice, cancellationToken);
49 }
50 return InvokeResponse.Ok();
51 });
52
53 // Feedback: message fetch task.
54 // Triggered when the user clicks thumbs up or thumbs down on a bot reply.
55 this.OnMessageFetchTask((context, cancellationToken) =>
56 {
57 string? reaction = context.Activity.Value?.Data?.ActionValue?.Reaction;
58
59 return Task.FromResult(TaskModuleResponse.CreateBuilder()
60 .WithType(TaskModuleResponseType.Continue)
61 .WithTitle("Feedback")
62 .WithHeight(TaskModuleSize.Small)
63 .WithWidth(TaskModuleSize.Small)
64 .WithCard(BuildFeedbackCard(reaction))
65 .Build());
66 });
67
68 // Feedback: message submit action.
69 this.OnMessageSubmitFeedback((context, cancellationToken) =>
70 {
71 MessageSubmitFeedbackValue? feedback = context.Activity.Value;
72 _logger.LogInformation("Feedback received — reaction: {Reaction}, feedback: {Feedback}",
73 feedback?.Reaction, feedback?.Feedback);
74 return Task.FromResult(InvokeResponse.Ok());
75 });
76 }
77
78 // Runs the agent and streams a response back. Shared between the incoming-message
79 // handler and the clarification-card submit handler — both flows ultimately just
80 // feed a user-supplied string into the agent.
81 private async Task RespondAsync<TActivity>(Context<TActivity> context, string userText, CancellationToken cancellationToken)
82 where TActivity : TeamsActivity
83 {
84 _ = context.Activity.Conversation?.Id
85 ?? throw new InvalidOperationException("Missing conversation ID.");
86
87 TeamsStreamingWriter writer = TeamsStreamingWriter.CreateFromContext(context);
88 RunResult result = await _agent.RunAsync(context.Activity.Conversation!.Id, userText, writer, cancellationToken);
89
90 IList<Entity> entities = result.Citations.BuildEntities(result.FullText);
91
92 MessageActivity final = new();
93
94 if (result.PendingCards.Count > 0)
95 {
96 // Card-only reply (e.g. clarification). No text and no feedback — the card IS the question.
97 final.Text = "";
98 final.AddAttachment([.. result.PendingCards.Select(c =>
99 TeamsAttachment.CreateBuilder().WithAdaptiveCard(c).Build())]);
100 }
101 else
102 {
103 final.AddFeedback(FeedbackType.Custom);
104 }
105
106 foreach (Entity entity in entities) final.AddEntity(entity);
107
108 if (result.FollowUpActions.Count > 0)
109 final.WithSuggestedActions(new SuggestedActions().AddActions([.. result.FollowUpActions]));
110
111 await writer.FinalizeResponseAsync(final, cancellationToken);
112 }
113
114 private static TeamsAttachment BuildFeedbackCard(string? reaction)
115 {
116 return TeamsAttachment.CreateBuilder()
117 .WithAdaptiveCard(new AdaptiveCard(
118 new TextBlock(reaction is null
119 ? "Tell us more about your experience:"
120 : $"You clicked {reaction}. Tell us more:")
121 .WithWrap(true),
122 new TextInput()
123 .WithId("feedbackText")
124 .WithPlaceholder("Enter your feedback here...")
125 .WithIsMultiline(true))
126 .WithActions(new SubmitAction().WithTitle("Submit")))
127 .Build();
128 }
129}
130