microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/setup-copilot-instructions

Branches

Tags

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

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore/AspNetCorePlugin.cs

251lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Text.Json;
5using System.Text.Json.Serialization;
6
7using Microsoft.AspNetCore.Builder;
8using Microsoft.AspNetCore.Http;
9using Microsoft.Teams.Api.Activities;
10using Microsoft.Teams.Api.Auth;
11using Microsoft.Teams.Api.Clients;
12using Microsoft.Teams.Apps;
13using Microsoft.Teams.Apps.Events;
14using Microsoft.Teams.Apps.Plugins;
15using Microsoft.Teams.Common.Http;
16using Microsoft.Teams.Common.Logging;
17
18using HttpRequest = Microsoft.AspNetCore.Http.HttpRequest;
19
20namespace Microsoft.Teams.Plugins.AspNetCore;
21
22[Plugin]
23public partial class AspNetCorePlugin : ISenderPlugin, IAspNetCorePlugin
24{
25 [Dependency]
26 public ILogger Logger { get; set; }
27
28 [Dependency("Token", optional: true)]
29 public IToken? Token { get; set; }
30
31 [Dependency]
32 public IHttpClient Client { get; set; }
33
34 public event EventFunction Events;
35
36 private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
37 {
38 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
39 };
40
41 public IApplicationBuilder Configure(IApplicationBuilder builder)
42 {
43 return builder;
44 }
45
46 public Task OnInit(App app, CancellationToken cancellationToken = default)
47 {
48 return Task.CompletedTask;
49 }
50
51 public Task OnStart(App app, CancellationToken cancellationToken = default)
52 {
53 Logger.Debug("OnStart");
54 return Task.CompletedTask;
55 }
56
57 public Task OnError(App app, IPlugin plugin, ErrorEvent @event, CancellationToken cancellationToken = default)
58 {
59 Logger.Debug("OnError");
60 return Task.CompletedTask;
61 }
62
63 public Task OnActivity(App app, ISenderPlugin sender, ActivityEvent @event, CancellationToken cancellationToken = default)
64 {
65 Logger.Debug("OnActivity");
66 return Task.CompletedTask;
67 }
68
69 public Task OnActivitySent(App app, ISenderPlugin sender, ActivitySentEvent @event, CancellationToken cancellationToken = default)
70 {
71 Logger.Debug("OnActivitySent");
72 return Task.CompletedTask;
73 }
74
75 public Task OnActivityResponse(App app, ISenderPlugin sender, ActivityResponseEvent @event, CancellationToken cancellationToken = default)
76 {
77 Logger.Debug("OnActivityResponse");
78 return Task.CompletedTask;
79 }
80
81 public Task<IActivity> Send(IActivity activity, Api.ConversationReference reference, CancellationToken cancellationToken = default)
82 {
83 return Send<IActivity>(activity, reference, isTargeted: false, cancellationToken);
84 }
85
86 public Task<IActivity> Send(IActivity activity, Api.ConversationReference reference, bool isTargeted, CancellationToken cancellationToken = default)
87 {
88 return Send<IActivity>(activity, reference, isTargeted, cancellationToken);
89 }
90
91 public Task<TActivity> Send<TActivity>(TActivity activity, Api.ConversationReference reference, CancellationToken cancellationToken = default) where TActivity : IActivity
92 {
93 return Send<TActivity>(activity, reference, isTargeted: false, cancellationToken);
94 }
95
96 public async Task<TActivity> Send<TActivity>(TActivity activity, Api.ConversationReference reference, bool isTargeted, CancellationToken cancellationToken = default) where TActivity : IActivity
97 {
98 var client = new ApiClient(reference.ServiceUrl, Client, cancellationToken);
99
100 activity.Conversation = reference.Conversation;
101 activity.From = reference.Bot;
102 activity.Recipient = reference.User;
103 activity.ChannelId = reference.ChannelId;
104
105 if (activity.Id is not null && !activity.IsStreaming)
106 {
107 await client
108 .Conversations
109 .Activities
110 .UpdateAsync(reference.Conversation.Id, activity.Id, activity, isTargeted);
111
112 return activity;
113 }
114
115 var res = await client
116 .Conversations
117 .Activities
118 .CreateAsync(reference.Conversation.Id, activity, isTargeted);
119
120 activity.Id = res?.Id;
121 return activity;
122 }
123
124 public IStreamer CreateStream(Api.ConversationReference reference, CancellationToken cancellationToken = default)
125 {
126 return new Stream()
127 {
128 Send = async activity =>
129 {
130 var res = await Send(activity, reference, false, cancellationToken);
131 return res;
132 }
133 };
134 }
135
136 public async Task<Response> Do(ActivityEvent @event, CancellationToken cancellationToken = default)
137 {
138 try
139 {
140 var @out = await Events(
141 this,
142 "activity",
143 @event,
144 cancellationToken
145 );
146
147 var res = (Response?)@out ?? throw new Exception("expected activity response");
148 Logger.Debug(res);
149 return res;
150 }
151 catch (Exception ex)
152 {
153 Logger.Error(ex);
154 await Events(
155 this,
156 "error",
157 new ErrorEvent() { Exception = ex },
158 cancellationToken
159 );
160
161 return new Response(System.Net.HttpStatusCode.InternalServerError, ex.ToString());
162 }
163 }
164
165 public async Task<IResult> Do(HttpContext httpContext, CancellationToken cancellationToken = default)
166 {
167 try
168 {
169 var request = httpContext.Request;
170 var token = ExtractToken(request);
171 var activity = await ParseActivity(request);
172
173 if (activity is null)
174 {
175 return Results.BadRequest("Missing activity");
176 }
177
178 var data = new Dictionary<string, object?>
179 {
180 ["Request.TraceId"] = httpContext.TraceIdentifier
181 };
182
183 foreach (var pair in httpContext.Items)
184 {
185 var key = pair.Key.ToString();
186
187 if (key is null) continue;
188
189 data[key] = pair.Value;
190 }
191
192 var res = await Do(new ActivityEvent()
193 {
194 Token = token,
195 Activity = activity,
196 Extra = data,
197 Services = httpContext.RequestServices
198 }, cancellationToken);
199
200 // convert response metadata to headers
201 foreach (var (key, value) in res.Meta)
202 {
203 var str = value?.ToString();
204 if (string.IsNullOrEmpty(str)) continue;
205 httpContext.Response.Headers.Append($"X-Teams-{char.ToUpper(key[0]) + key[1..]}", str);
206 }
207
208 return Results.Json(
209 res.Body,
210 _jsonSerializerOptions,
211 contentType: null,
212 statusCode: (int)res.Status
213 );
214 }
215 catch (Exception ex)
216 {
217 Logger.Error(ex);
218 await Events(
219 this,
220 "error",
221 new ErrorEvent() { Exception = ex },
222 cancellationToken
223 );
224
225 return Results.Problem(detail: ex.Message, statusCode: 500);
226 }
227 }
228
229 public JsonWebToken ExtractToken(HttpRequest httpRequest)
230 {
231 var authHeader = httpRequest.Headers.Authorization.FirstOrDefault() ?? throw new UnauthorizedAccessException();
232 return new JsonWebToken(authHeader.Replace("Bearer ", ""));
233 }
234
235 public async Task<Activity?> ParseActivity(HttpRequest httpRequest)
236 {
237 httpRequest.EnableBuffering();
238
239 if (httpRequest.Body.CanSeek)
240 {
241 // reset the stream position to the beginning in case it was read before
242 httpRequest.Body.Position = 0;
243 }
244
245 using StreamReader sr = new(httpRequest.Body);
246 var body = await sr.ReadToEndAsync();
247 Activity? activity = JsonSerializer.Deserialize<Activity>(body);
248
249 return activity;
250 }
251}