microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/migration-bot

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Bot.Compat/CompatBotAdapter.cs

174lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Text.Json;
5using Microsoft.AspNetCore.Http;
6using Microsoft.Bot.Builder;
7using Microsoft.Bot.Schema;
8using Microsoft.Extensions.Logging;
9using Microsoft.Teams.Bot.Apps;
10using Microsoft.Teams.Bot.Core;
11using Microsoft.Teams.Bot.Core.Schema;
12using Newtonsoft.Json;
13
14
15namespace Microsoft.Teams.Bot.Compat;
16
17/// <summary>
18/// Provides a Bot Framework adapter that enables compatibility between the Bot Framework SDK and a custom bot
19/// application implementation.
20/// </summary>
21/// <remarks>Use this adapter to bridge Bot Framework turn contexts and activities with a custom bot application.
22/// This class is intended for scenarios where integration with non-standard bot runtimes or legacy systems is
23/// required.</remarks>
24/// <param name="botApplication">The Teams bot application instance.</param>
25/// <param name="httpContextAccessor">The HTTP context accessor.</param>
26/// <param name="logger">The logger instance.</param>
27public class CompatBotAdapter(
28 TeamsBotApplication botApplication,
29 IHttpContextAccessor? httpContextAccessor = null,
30 ILogger? logger = null) : BotAdapter
31{
32 private readonly JsonSerializerOptions _writeIndentedJsonOptions = new() { WriteIndented = true };
33 private readonly TeamsBotApplication botApplication = botApplication;
34 private readonly IHttpContextAccessor? httpContextAccessor = httpContextAccessor;
35 private readonly ILogger? logger = logger;
36
37 /// <summary>
38 /// Deletes an activity from the conversation.
39 /// </summary>
40 /// <param name="turnContext">The turn context containing the activity information. Cannot be null.</param>
41 /// <param name="reference">The conversation reference identifying the activity to delete.</param>
42 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
43 /// <returns>A task that represents the asynchronous delete operation.</returns>
44 public override async Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
45 {
46 ArgumentNullException.ThrowIfNull(turnContext);
47 ArgumentNullException.ThrowIfNull(reference);
48
49 // Extract values from conversation reference
50 string conversationId = reference.Conversation?.Id
51 ?? throw new ArgumentException("ConversationReference must contain a valid Conversation.Id", nameof(reference));
52
53 string activityId = reference.ActivityId
54 ?? throw new ArgumentException("ConversationReference must contain a valid ActivityId", nameof(reference));
55
56 string serviceUrlString = reference.ServiceUrl
57 ?? turnContext.Activity.ServiceUrl
58 ?? throw new ArgumentException("ServiceUrl must be provided", nameof(reference));
59
60 Uri serviceUrl = new(serviceUrlString);
61
62 // Extract agentic identity from turn context if available
63 AgenticIdentity? agenticIdentity = turnContext.Activity?.FromCompatActivity().From?.GetAgenticIdentity();
64
65 await botApplication.ConversationClient.DeleteActivityAsync(
66 conversationId,
67 activityId,
68 serviceUrl,
69 agenticIdentity,
70 customHeaders: null,
71 cancellationToken).ConfigureAwait(false);
72 }
73
74 /// <summary>
75 /// Sends a set of activities to the conversation.
76 /// </summary>
77 /// <param name="turnContext">The turn context for the conversation. Cannot be null.</param>
78 /// <param name="activities">An array of activities to send. Cannot be null.</param>
79 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
80 /// <returns>
81 /// A task that represents the asynchronous operation. The task result contains an array of <see cref="ResourceResponse"/>
82 /// objects with the IDs of the sent activities.
83 /// </returns>
84 public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
85 {
86 ArgumentNullException.ThrowIfNull(activities);
87 ArgumentNullException.ThrowIfNull(turnContext);
88
89 ResourceResponse[] responses = new Microsoft.Bot.Schema.ResourceResponse[activities.Length];
90
91 for (int i = 0; i < activities.Length; i++)
92 {
93 Activity activity = activities[i];
94
95 if (activity.Type == ActivityTypes.Trace)
96 {
97 return [new ResourceResponse() { Id = null }];
98 }
99
100 if (activity.Type == "invokeResponse")
101 {
102 WriteInvokeResponseToHttpResponse(activity.Value as InvokeResponse);
103 return [new ResourceResponse() { Id = null }];
104 }
105
106 CoreActivity coreActivity = activity.FromCompatActivity();
107
108 // Ensure ServiceUrl is set from turn context if not already present
109 if (coreActivity.ServiceUrl == null && !string.IsNullOrWhiteSpace(turnContext.Activity.ServiceUrl))
110 {
111 coreActivity.ServiceUrl = new Uri(turnContext.Activity.ServiceUrl);
112 }
113
114 SendActivityResponse? resp = await botApplication.SendActivityAsync(coreActivity, cancellationToken).ConfigureAwait(false);
115
116 logger?.LogInformation("Resp from SendActivitiesAsync: {RespId}", resp?.Id);
117
118 responses[i] = new Microsoft.Bot.Schema.ResourceResponse() { Id = resp?.Id };
119 }
120 return responses;
121 }
122
123 /// <summary>
124 /// Updates an existing activity in the conversation.
125 /// </summary>
126 /// <param name="turnContext">The turn context for the conversation.</param>
127 /// <param name="activity">The activity with updated content. Cannot be null.</param>
128 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
129 /// <returns>
130 /// A task that represents the asynchronous operation. The task result contains a <see cref="ResourceResponse"/>
131 /// with the ID of the updated activity.
132 /// </returns>
133 public override async Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
134 {
135 ArgumentNullException.ThrowIfNull(activity);
136 ArgumentNullException.ThrowIfNull(turnContext);
137
138 CoreActivity coreActivity = activity.FromCompatActivity();
139
140 // Ensure ServiceUrl is set from turn context if not already present
141 if (coreActivity.ServiceUrl == null && !string.IsNullOrWhiteSpace(turnContext.Activity.ServiceUrl))
142 {
143 coreActivity.ServiceUrl = new Uri(turnContext.Activity.ServiceUrl);
144 }
145
146 UpdateActivityResponse res = await botApplication.ConversationClient.UpdateActivityAsync(
147 activity.Conversation.Id,
148 activity.Id,
149 coreActivity,
150 cancellationToken: cancellationToken).ConfigureAwait(false);
151 return new ResourceResponse() { Id = res.Id };
152 }
153
154 private void WriteInvokeResponseToHttpResponse(InvokeResponse? invokeResponse)
155 {
156 ArgumentNullException.ThrowIfNull(invokeResponse);
157 HttpResponse? response = httpContextAccessor?.HttpContext?.Response;
158 if (response is not null && !response.HasStarted)
159 {
160 response.StatusCode = invokeResponse.Status;
161 using StreamWriter httpResponseStreamWriter = new(response.BodyWriter.AsStream());
162 using JsonTextWriter httpResponseJsonWriter = new(httpResponseStreamWriter);
163 logger?.LogTrace("Sending Invoke Response: \n {InvokeResponse} with status: {Status} \n", System.Text.Json.JsonSerializer.Serialize(invokeResponse.Body, _writeIndentedJsonOptions), invokeResponse.Status);
164 if (invokeResponse.Body is not null)
165 {
166 Microsoft.Bot.Builder.Integration.AspNet.Core.HttpHelper.BotMessageSerializer.Serialize(httpResponseJsonWriter, invokeResponse.Body);
167 }
168 }
169 else
170 {
171 logger?.LogWarning("HTTP response is null or has started. Cannot write invoke response. ResponseStarted: {ResponseStarted}", response?.HasStarted);
172 }
173 }
174}
175