microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
next/core

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Bot.Apps/TeamsBotApplication.cs

131lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.AspNetCore.Http;
5using Microsoft.Extensions.Logging;
6using Microsoft.Teams.Bot.Apps.Api.Clients;
7using Microsoft.Teams.Bot.Apps.Auth;
8using Microsoft.Teams.Bot.Apps.Handlers;
9using Microsoft.Teams.Bot.Apps.Routing;
10using Microsoft.Teams.Bot.Apps.Schema;
11using Microsoft.Teams.Bot.Core;
12using Microsoft.Teams.Bot.Core.Hosting;
13
14namespace Microsoft.Teams.Bot.Apps;
15
16/// <summary>
17/// Teams specific Bot Application
18/// </summary>
19public class TeamsBotApplication : BotApplication
20{
21 private readonly Api.Clients.ApiClient _teamsApiClient;
22
23 /// <summary>
24 /// Gets the router for dispatching Teams activities to registered routes.
25 /// </summary>
26 internal Router Router { get; }
27
28 /// <summary>
29 /// Gets the registry of OAuthFlow instances. Set by AddOAuthFlow.
30 /// </summary>
31 internal OAuthFlowRegistry? OAuthRegistry { get; set; }
32
33 /// <summary>
34 /// Gets a registered <see cref="OAuthFlow"/> by connection name.
35 /// Use this to attach callbacks (<see cref="OAuthFlow.OnSignInComplete"/>, <see cref="OAuthFlow.OnSignInFailure"/>)
36 /// to flows that were configured via <see cref="TeamsBotApplicationOptions.AddOAuthFlow"/>.
37 /// </summary>
38 /// <param name="connectionName">The OAuth connection name.</param>
39 /// <returns>The <see cref="OAuthFlow"/> instance.</returns>
40 /// <exception cref="InvalidOperationException">No flow is registered for the given connection name.</exception>
41 public OAuthFlow GetOAuthFlow(string connectionName)
42 {
43 ArgumentException.ThrowIfNullOrWhiteSpace(connectionName);
44
45 OAuthFlow? flow = OAuthRegistry?.Resolve(connectionName);
46 if (flow is null)
47 {
48 IEnumerable<string> registered = OAuthRegistry?.GetRegisteredConnectionNames() ?? [];
49 throw new InvalidOperationException(
50 $"No OAuthFlow registered for connection '{connectionName}'. " +
51 $"Registered connections: [{string.Join(", ", registered)}].");
52 }
53
54 return flow;
55 }
56
57 /// <summary>
58 /// Gets the client used to interact with the Teams API service.
59 /// </summary>
60 public ApiClient TeamsApiClient => _teamsApiClient;
61 /// <summary>
62 /// Gets the hierarchical API facade for Teams operations.
63 /// </summary>
64 /// <remarks>
65 /// This property provides a structured API for accessing Teams operations through a hierarchy:
66 /// <list type="bullet">
67 /// <item><c>Api.Conversations.Activities</c> - Activity operations (send, update, delete)</item>
68 /// <item><c>Api.Conversations.Members</c> - Member operations (get, delete)</item>
69 /// <item><c>Api.Users.Token</c> - User token operations (OAuth SSO, sign-in resources)</item>
70 /// <item><c>Api.Teams</c> - Team operations (get details, channels)</item>
71 /// <item><c>Api.Meetings</c> - Meeting operations (get info, participant, notifications)</item>
72 /// <item><c>Api.Batch</c> - Batch messaging operations</item>
73 /// </list>
74 /// </remarks>
75 public ApiClient Api { get; }
76
77 /// <param name="conversationClient"></param>
78 /// <param name="userTokenClient"></param>
79 /// <param name="teamsApiClient"></param>
80 /// <param name="httpContextAccessor"></param>
81 /// <param name="logger"></param>
82 /// <param name="options">Options containing the application (client) ID, used for logging and diagnostics. Defaults to an empty instance if not provided.</param>
83 /// <param name="teamsOptions">Teams-specific options including OAuth flow configuration. Defaults to an empty instance if not provided.</param>
84 public TeamsBotApplication(
85 ConversationClient conversationClient,
86 UserTokenClient userTokenClient,
87 ApiClient teamsApiClient,
88 IHttpContextAccessor httpContextAccessor,
89 ILogger<TeamsBotApplication> logger,
90 BotApplicationOptions? options = null,
91 TeamsBotApplicationOptions? teamsOptions = null)
92 : base(conversationClient, userTokenClient, logger, options)
93 {
94 _teamsApiClient = teamsApiClient;
95 Api = teamsApiClient;
96 Router = new Router(logger);
97
98 // Auto-register OAuth flows from DI options
99 if (teamsOptions is not null)
100 {
101 foreach (var descriptor in teamsOptions.OAuthFlows)
102 {
103 this.AddOAuthFlow(descriptor.Options);
104 }
105 }
106 OnActivity = async (activity, cancellationToken) =>
107 {
108 logger.LogDebug("OnActivity invoked for activity: Id={Id}", activity.Id);
109 TeamsActivity teamsActivity = TeamsActivity.FromActivity(activity);
110 Context<TeamsActivity> defaultContext = new(this, teamsActivity);
111
112 if (teamsActivity.Type != TeamsActivityType.Invoke)
113 {
114 await Router.DispatchAsync(defaultContext, cancellationToken).ConfigureAwait(false);
115 }
116 else // invokes
117 {
118 InvokeResponse invokeResponse = await Router.DispatchWithReturnAsync(defaultContext, cancellationToken).ConfigureAwait(false);
119 HttpContext? httpContext = httpContextAccessor.HttpContext;
120 if (httpContext is not null && invokeResponse is not null)
121 {
122 httpContext.Response.StatusCode = invokeResponse.Status;
123 logger.LogDebug("Sending invoke response with status {Status}", invokeResponse.Status);
124 logger.LogTrace("Sending invoke response with status {Status} and Body {Body}", invokeResponse.Status, invokeResponse.Body);
125 if (invokeResponse.Body is not null)
126 await httpContext.Response.WriteAsJsonAsync(invokeResponse.Body, cancellationToken).ConfigureAwait(false);
127 }
128 }
129 };
130 }
131}
132