microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
releases/core

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/ExtAIBot/ExtAIBotApp.cs

128lines · modecode

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