microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
c92c1919ec3b5a0b25a067ad4ad46da598a2fdca

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Bot.Core/BotApplication.cs

147lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Diagnostics;
5using Microsoft.AspNetCore.Http;
6using Microsoft.AspNetCore.Mvc.Formatters;
7using Microsoft.Extensions.Configuration;
8using Microsoft.Extensions.Logging;
9using Microsoft.Teams.Bot.Core.Schema;
10
11namespace Microsoft.Teams.Bot.Core;
12
13/// <summary>
14/// Represents a bot application.
15/// </summary>
16[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates", Justification = "<Pending>")]
17public class BotApplication
18{
19 private readonly ILogger<BotApplication> _logger;
20 private readonly ConversationClient? _conversationClient;
21 private readonly UserTokenClient? _userTokenClient;
22 private readonly string _serviceKey;
23 internal TurnMiddleware MiddleWare { get; }
24
25 /// <summary>
26 /// Initializes a new instance of the BotApplication class with the specified conversation client, configuration,
27 /// logger, and optional service key.
28 /// </summary>
29 /// <remarks>This constructor sets up the bot application and starts the bot listener using the provided
30 /// configuration and service key. The service key is used to locate authentication credentials in the
31 /// configuration.</remarks>
32 /// <param name="conversationClient">The client used to manage and interact with conversations for the bot.</param>
33 /// <param name="userTokenClient">The client used to manage user tokens for authentication.</param>
34 /// <param name="config">The application configuration settings used to retrieve environment variables and service credentials.</param>
35 /// <param name="logger">The logger used to record operational and diagnostic information for the bot application.</param>
36 /// <param name="sectionName">The configuration key identifying the authentication service. Defaults to "AzureAd" if not specified.</param>
37 public BotApplication(ConversationClient conversationClient, UserTokenClient userTokenClient, IConfiguration config, ILogger<BotApplication> logger, string sectionName = "AzureAd")
38 {
39 ArgumentNullException.ThrowIfNull(config);
40 _logger = logger;
41 _serviceKey = sectionName;
42 MiddleWare = new TurnMiddleware();
43 _conversationClient = conversationClient;
44 _userTokenClient = userTokenClient;
45 string appId = config["MicrosoftAppId"] ?? config["CLIENT_ID"] ?? config[$"{sectionName}:ClientId"] ?? "Unknown AppID";
46 logger.LogInformation("Started bot listener \n on {Port} \n for AppID:{AppId} \n with SDK version {SdkVersion}", config?["ASPNETCORE_URLS"], appId, Version);
47
48 }
49
50
51 /// <summary>
52 /// Gets the client used to manage and interact with conversations.
53 /// </summary>
54 /// <remarks>Accessing this property before the client is initialized will result in an exception. Ensure
55 /// that the client is properly configured before use.</remarks>
56 public ConversationClient ConversationClient => _conversationClient ?? throw new InvalidOperationException("ConversationClient not initialized");
57
58 /// <summary>
59 /// Gets the client used to manage user tokens for authentication.
60 /// </summary>
61 /// <remarks>Accessing this property before the client is initialized will result in an exception. Ensure
62 /// that the client is properly configured before use.</remarks>
63 public UserTokenClient UserTokenClient => _userTokenClient ?? throw new InvalidOperationException("UserTokenClient not registered");
64
65 /// <summary>
66 /// Gets or sets the delegate that is invoked to handle an incoming activity asynchronously.
67 /// </summary>
68 /// <remarks>Assign a delegate to process activities as they are received. The delegate should accept an
69 /// <see cref="CoreActivity"/> and a <see cref="CancellationToken"/>, and return a <see cref="Task"/> representing the
70 /// asynchronous operation. If <see langword="null"/>, incoming activities will not be handled.</remarks>
71 public Func<CoreActivity, CancellationToken, Task>? OnActivity { get; set; }
72
73 /// <summary>
74 /// Processes an incoming HTTP request containing a bot activity.
75 /// </summary>
76 /// <param name="httpContext"></param>
77 /// <param name="cancellationToken"></param>
78 /// <returns></returns>
79 /// <exception cref="InvalidOperationException"></exception>
80 /// <exception cref="BotHandlerException"></exception>
81 public async Task ProcessAsync(HttpContext httpContext, CancellationToken cancellationToken = default)
82 {
83 ArgumentNullException.ThrowIfNull(httpContext);
84 ArgumentNullException.ThrowIfNull(_conversationClient);
85
86 _logger.LogDebug("Start processing HTTP request for activity");
87
88 CoreActivity activity = await CoreActivity.FromJsonStreamAsync(httpContext.Request.Body, cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException("Invalid Activity");
89
90 _logger.LogInformation("Processing activity {Type} {Id}", activity.Type, activity.Id);
91
92 if (_logger.IsEnabled(LogLevel.Trace))
93 {
94 _logger.LogTrace("Received activity: {Activity}", activity.ToJson());
95 }
96
97 using (_logger.BeginScope("Processing activity {Type} {Id}", activity.Type, activity.Id))
98 {
99 try
100 {
101 var token = Debugger.IsAttached ? CancellationToken.None : cancellationToken;
102 await MiddleWare.RunPipelineAsync(this, activity, this.OnActivity, 0, token).ConfigureAwait(false);
103
104 }
105 catch (Exception ex)
106 {
107 _logger.LogError(ex, "Error processing activity {Type} {Id}", activity.Type, activity.Id);
108 throw new BotHandlerException("Error processing activity", ex, activity);
109 }
110 finally
111 {
112 _logger.LogInformation("Finished processing activity {Type} {Id}", activity.Type, activity.Id);
113 }
114 }
115 }
116
117 /// <summary>
118 /// Adds the specified turn middleware to the middleware pipeline.
119 /// </summary>
120 /// <param name="middleware">The middleware component to add to the pipeline. Cannot be null.</param>
121 /// <returns>An ITurnMiddleWare instance representing the updated middleware pipeline.</returns>
122 public ITurnMiddleWare Use(ITurnMiddleWare middleware)
123 {
124 MiddleWare.Use(middleware);
125 return MiddleWare;
126 }
127
128 /// <summary>
129 /// Sends the specified activity to the conversation asynchronously.
130 /// </summary>
131 /// <param name="activity">The activity to send to the conversation. Cannot be null.</param>
132 /// <param name="cancellationToken">A cancellation token that can be used to cancel the send operation.</param>
133 /// <returns>A task that represents the asynchronous operation. The task result contains the identifier of the sent activity.</returns>
134 /// <exception cref="Exception">Thrown if the conversation client has not been initialized.</exception>
135 public async Task<SendActivityResponse?> SendActivityAsync(CoreActivity activity, CancellationToken cancellationToken = default)
136 {
137 ArgumentNullException.ThrowIfNull(activity);
138 ArgumentNullException.ThrowIfNull(_conversationClient, "ConversationClient not initialized");
139
140 return await _conversationClient.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false);
141 }
142
143 /// <summary>
144 /// Gets the version of the SDK.
145 /// </summary>
146 public static string Version => ThisAssembly.NuGetPackageVersion;
147}
148