microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
core/sso-in-channels

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/ExtAIBot/TeamsBotAppHandlers.cs

117lines · modecode

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