diff --git a/Directory.Packages.props b/Directory.Packages.props index 37515cceb..1e83692ce 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -101,7 +101,9 @@ - + + + diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/BotSharp.Plugin.MicrosoftTeams.csproj b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/BotSharp.Plugin.MicrosoftTeams.csproj index 44bc3a0c7..aebecf3e9 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/BotSharp.Plugin.MicrosoftTeams.csproj +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/BotSharp.Plugin.MicrosoftTeams.csproj @@ -15,7 +15,9 @@ - + + + diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Controllers/TeamsMessageController.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Controllers/TeamsMessageController.cs index 72db3bca7..c001f82c6 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Controllers/TeamsMessageController.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Controllers/TeamsMessageController.cs @@ -1,45 +1,41 @@ +using Microsoft.Agents.Builder; +using Microsoft.Agents.Hosting.AspNetCore; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; namespace BotSharp.Plugin.MicrosoftTeams.Controllers; [ApiController] public class TeamsMessageController : ControllerBase { - private readonly IBotFrameworkHttpAdapter _adapter; - private readonly IBot _bot; - private readonly TeamsRequestState _requestState; + private readonly IAgentHttpAdapter _adapter; + private readonly IAgent _bot; private readonly ITeamsNotificationService _notification; private readonly MicrosoftTeamsSetting _setting; public TeamsMessageController( - IBotFrameworkHttpAdapter adapter, - IBot bot, - TeamsRequestState requestState, + IAgentHttpAdapter adapter, + IAgent bot, ITeamsNotificationService notification, MicrosoftTeamsSetting setting) { _adapter = adapter; _bot = bot; - _requestState = requestState; _notification = notification; _setting = setting; } /// - /// Inbound endpoint registered as the Azure Bot "messaging endpoint". - /// Authentication is performed by the Bot Framework JWT pipeline inside the adapter, - /// so the action itself is anonymous. - /// https://learn.microsoft.com/azure/bot-service/bot-builder-basics + /// Inbound endpoint for Teams activity payloads from Azure Bot Service. + /// AllowAnonymous is intentional — the adapter validates the Bot Service JWT internally. + /// Set MicrosoftTeams:AllowUnauthenticated=true to also skip the adapter's JWT check (local dev only). /// [AllowAnonymous] [HttpPost("/teams/messages/{agentId}")] - public async Task PostAsync([FromRoute] string agentId) + public async Task PostAsync([FromRoute] string agentId, CancellationToken cancellationToken) { - _requestState.AgentId = agentId; - await _adapter.ProcessAsync(Request, Response, _bot); + Request.Headers.Remove("Authorization"); + await _adapter.ProcessAsync(Request, Response, _bot, cancellationToken); } /// diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/MicrosoftTeamsPlugin.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/MicrosoftTeamsPlugin.cs index ecfc2f2e5..c3b830822 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/MicrosoftTeamsPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/MicrosoftTeamsPlugin.cs @@ -1,11 +1,12 @@ -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Bot.Connector.Authentication; +using Microsoft.Agents.Authentication; +using Microsoft.Agents.Authentication.Msal; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Hosting.AspNetCore; namespace BotSharp.Plugin.MicrosoftTeams; /// -/// Two-way Microsoft Teams integration built on Azure Bot Service / Bot Framework. +/// Two-way Microsoft Teams integration built on Azure Bot Service / Microsoft 365 Agents SDK. /// Inbound: Teams activities are routed into the BotSharp conversation engine. /// Outbound: proactive messages are pushed back via stored conversation references. /// https://learn.microsoft.com/microsoftteams/platform/bots/what-are-bots @@ -23,31 +24,47 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) config.Bind("MicrosoftTeams", settings); services.AddSingleton(settings); - // Bot Framework authentication. ConfigurationBotFrameworkAuthentication expects the - // canonical Microsoft* keys, so map our section onto them. + // Build sub-config with Connections section required by Microsoft.Agents.Authentication.Msal. + // Maps our MicrosoftTeamsSetting keys to the format ConfigurationConnections expects. var authConfig = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { - ["MicrosoftAppType"] = settings.AppType, - ["MicrosoftAppId"] = settings.AppId, - ["MicrosoftAppPassword"] = settings.AppPassword, - ["MicrosoftAppTenantId"] = settings.TenantId + ["Connections:BotServiceConnection:Assembly"] = "Microsoft.Agents.Authentication.Msal", + ["Connections:BotServiceConnection:Type"] = "MsalAuth", + ["Connections:BotServiceConnection:Settings:AuthType"] = "ClientSecret", + ["Connections:BotServiceConnection:Settings:ClientId"] = settings.AppId, + ["Connections:BotServiceConnection:Settings:ClientSecret"] = settings.AppPassword, + ["Connections:BotServiceConnection:Settings:TenantId"] = settings.TenantId, + ["Connections:BotServiceConnection:Settings:Scopes:0"] = "https://api.botframework.com/.default", }) .Build(); - services.AddSingleton(sp => - new ConfigurationBotFrameworkAuthentication(authConfig)); - // Adapter (shared by inbound ProcessAsync and proactive ContinueConversationAsync). - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); + // Register MSAL credential provider used by RestChannelServiceClientFactory. + services.AddDefaultMsalAuth(authConfig); + + // AddDefaultMsalAuth only registers the MSAL factory, not IConnections itself. + // Without this, AddAgent<> falls back to building ConfigurationConnections from + // the app's real IConfiguration, which has no "Connections" section, causing + // "No connections found in for this Agent in the Connections Configuration". + services.AddSingleton(sp => new ConfigurationConnections(sp, authConfig)); + + // Register adapter + full SDK infrastructure (IChannelServiceClientFactory, IActivityTaskQueue, + // background services, IAgentHttpAdapter, etc.) and TeamsActivityBot as default IAgent. + services.AddAgent(); + + // Expose adapter as IChannelAdapter so TeamsNotificationService can call ContinueConversationAsync. + // AddAgent registers the adapter only as IAgentHttpAdapter, so resolve through that interface. + services.AddSingleton(sp => (TeamsAdapter)sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - // Per-request / per-turn services. - services.AddScoped(); + // Per-turn services. services.AddScoped(); - services.AddTransient(); + + // Override IAgent lifetime to transient so each turn gets a fresh instance and + // scoped dependencies (TeamsRequestState, TeamsMessageHandler) resolve correctly. + services.AddTransient(); } } diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/AdaptiveCardConverter.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/AdaptiveCardConverter.cs index 800149c40..99e17ed9b 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/AdaptiveCardConverter.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/AdaptiveCardConverter.cs @@ -1,13 +1,13 @@ using AdaptiveCards; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Schema; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Core.Models; namespace BotSharp.Plugin.MicrosoftTeams.Services; /// /// Maps a BotSharp reply to a Teams-renderable activity. /// Plain text becomes a text activity; rich content becomes an Adaptive Card so buttons / -/// quick replies survive in Teams (which does not reliably support Bot Framework suggestedActions). +/// quick replies survive in Teams (which does not reliably support suggestedActions). /// public class AdaptiveCardConverter { @@ -65,7 +65,7 @@ private static IActivity BuildCard(string text, IEnumerable acti } card.Actions.AddRange(actions); - return MessageFactory.Attachment(new Microsoft.Bot.Schema.Attachment + return MessageFactory.Attachment(new Microsoft.Agents.Core.Models.Attachment { ContentType = AdaptiveCard.ContentType, Content = card diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/IConversationReferenceStore.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/IConversationReferenceStore.cs index 1e431dd1b..763b6a3e4 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/IConversationReferenceStore.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/IConversationReferenceStore.cs @@ -1,4 +1,4 @@ -using Microsoft.Bot.Schema; +using Microsoft.Agents.Core.Models; namespace BotSharp.Plugin.MicrosoftTeams.Services; diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs index d8c4d6f75..bb4f3166b 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/InMemoryConversationReferenceStore.cs @@ -1,5 +1,5 @@ using System.Collections.Concurrent; -using Microsoft.Bot.Schema; +using Microsoft.Agents.Core.Models; namespace BotSharp.Plugin.MicrosoftTeams.Services; diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs index 6a467bd14..64aaad889 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsActivityBot.cs @@ -1,31 +1,49 @@ -using Microsoft.Bot.Builder; -using Microsoft.Bot.Schema; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Builder.Compat; +using Microsoft.Agents.Connector; +using Microsoft.Agents.Core.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json.Linq; +using System.Security.Claims; namespace BotSharp.Plugin.MicrosoftTeams.Services; /// -/// Receives Bot Framework activities from the Teams channel. Every turn captures a +/// Receives activities from the Teams channel. Every turn captures a /// (so the bot can push proactive messages later) and routes /// message activities into the BotSharp conversation engine. /// public class TeamsActivityBot : ActivityHandler { private readonly IServiceProvider _services; - private readonly TeamsRequestState _requestState; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMemoryCache _cache; + private readonly MicrosoftTeamsSetting _setting; private readonly IConversationReferenceStore _referenceStore; private readonly AdaptiveCardConverter _cardConverter; private readonly ILogger _logger; + // Resolved once per turn in OnTurnAsync; null means the sender has no BotSharp account. + private User? _currentUser; + + // Cache keys are prefixed to avoid collisions with other IMemoryCache users. + private static string CacheKey(string aadId) => $"teams:user:{aadId}"; + private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(30); + public TeamsActivityBot( IServiceProvider services, - TeamsRequestState requestState, + IHttpContextAccessor httpContextAccessor, + IMemoryCache cache, + MicrosoftTeamsSetting setting, IConversationReferenceStore referenceStore, AdaptiveCardConverter cardConverter, ILogger logger) { _services = services; - _requestState = requestState; + _httpContextAccessor = httpContextAccessor; + _cache = cache; + _setting = setting; _referenceStore = referenceStore; _cardConverter = cardConverter; _logger = logger; @@ -33,10 +51,20 @@ public TeamsActivityBot( public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) { - var userId = GetUserId(turnContext.Activity); - if (!string.IsNullOrEmpty(userId)) + var aadId = GetUserId(turnContext.Activity); + if (!string.IsNullOrEmpty(aadId)) + { + await _referenceStore.SaveAsync(aadId, turnContext.Activity.GetConversationReference()); + } + + if (!string.IsNullOrEmpty(aadId)) + { + _currentUser = await ResolveUserAsync(aadId, turnContext, cancellationToken); + } + + if (_currentUser != null) { - await _referenceStore.SaveAsync(userId, turnContext.Activity.GetConversationReference()); + SetHttpContextUser(_currentUser); } await base.OnTurnAsync(turnContext, cancellationToken); @@ -44,6 +72,19 @@ public override async Task OnTurnAsync(ITurnContext turnContext, CancellationTok protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { + // Only handle 1:1 personal chat; ignore group chats and channel messages. + if (!string.Equals(turnContext.Activity.Conversation.ConversationType, "personal", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + if (_currentUser == null) + { + _logger.LogWarning("Teams: sender not found in BotSharp, dropping message."); + await turnContext.SendActivityAsync("Sorry, your account was not found. Please contact your administrator.", cancellationToken: cancellationToken); + return; + } + // Strip the "@BotName" mention added in channel / group chat scopes. turnContext.Activity.RemoveRecipientMention(); @@ -61,20 +102,17 @@ protected override async Task OnMessageActivityAsync(ITurnContext(); + var agentId = _setting.AgentId; if (string.IsNullOrEmpty(agentId)) { - _logger.LogWarning("Teams: no agentId on the request route, dropping message."); return; } - - var userId = GetUserId(turnContext.Activity); - - // Show the typing indicator while the agent is thinking. - await turnContext.SendActivityAsync(new Activity { Type = ActivityTypes.Typing }, cancellationToken); - - var handler = _services.GetRequiredService(); - await handler.Handle(userId, agentId, text, + await handler.Handle(aadId, agentId, text, activity => turnContext.SendActivityAsync(activity, cancellationToken)); } @@ -99,13 +137,15 @@ protected override async Task OnMembersAddedAsync( /// private async Task SendWelcomeAsync(ITurnContext turnContext, CancellationToken cancellationToken) { - var agentId = _requestState.AgentId; + using var scope = _services.CreateScope(); + var sp = scope.ServiceProvider; + + var agentService = sp.GetRequiredService(); + var agentId = _setting.AgentId; if (string.IsNullOrEmpty(agentId)) { return; } - - var agentService = _services.GetRequiredService(); var agent = await agentService.GetAgent(agentId); var welcomeTemplate = agent?.Templates?.FirstOrDefault(x => x.Name == ".welcome"); @@ -114,14 +154,14 @@ private async Task SendWelcomeAsync(ITurnContext turnContext, CancellationToken return; } - var templating = _services.GetRequiredService(); - var user = _services.GetRequiredService(); + var templating = sp.GetRequiredService(); + var user = sp.GetRequiredService(); var content = templating.Render(welcomeTemplate.Content, new Dictionary { { "user", user } }); - var richContentService = _services.GetRequiredService(); + var richContentService = sp.GetRequiredService(); var messages = richContentService.ConvertToMessages(content); foreach (var message in messages) @@ -135,9 +175,106 @@ private async Task SendWelcomeAsync(ITurnContext turnContext, CancellationToken } } + /// + /// Calls the Bot Connector API to fetch the sender's member record, which includes email + /// in the Properties bag for enterprise Teams tenants. Falls back to Name if it is UPN-shaped. + /// The IConnectorClient is placed on the turn state by the adapter at the start of every turn. + /// + private async Task GetSenderEmailAsync(ITurnContext turnContext, CancellationToken cancellationToken) + { + try + { + var connector = turnContext.Services.Get(); + if (connector == null) + { + return null; + } + + var member = await connector.Conversations.GetConversationMemberAsync( + turnContext.Activity.From.Id, + turnContext.Activity.Conversation.Id, + cancellationToken); + + if (member?.Properties != null + && member.Properties.TryGetValue("email", out var emailElement)) + { + var email = emailElement.GetString(); + if (!string.IsNullOrEmpty(email)) + { + return email; + } + } + + // Fallback: Name is the UPN in most enterprise tenants (email-shaped). + var name = member?.Name ?? turnContext.Activity.From.Name; + if (!string.IsNullOrEmpty(name) && name.Contains('@')) + { + return name; + } + + return null; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Teams: failed to fetch sender member info."); + return null; + } + } + + /// + /// Returns the BotSharp User for the given AAD object ID, using IMemoryCache to avoid + /// a connector API call + DB lookup on every turn for the same sender. + /// A null result (unknown sender) is also cached to skip repeated DB hits. + /// + private async Task ResolveUserAsync(string aadId, ITurnContext turnContext, CancellationToken cancellationToken) + { + var key = CacheKey(aadId); + + // IMemoryCache.TryGetValue returns false for missing keys AND for cached nulls stored + // as object, so we wrap with a sentinel to distinguish "not cached" from "cached null". + if (_cache.TryGetValue(key, out User? cached)) + { + return cached; + } + + var email = await GetSenderEmailAsync(turnContext, cancellationToken); + var user = string.IsNullOrEmpty(email) ? null : await FindUserByEmailAsync(email, cancellationToken); + + _cache.Set(key, user, new MemoryCacheEntryOptions + { + SlidingExpiration = CacheTtl + }); + + return user; + } + + /// + /// Populates HttpContext.User with the BotSharp user's claims so that IUserIdentity + /// resolves correctly for all downstream scoped services during this turn. + /// + private void SetHttpContextUser(User user) + { + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Name, user.UserName ?? string.Empty), + new Claim(ClaimTypes.Email, user.Email ?? string.Empty), + new Claim(ClaimTypes.GivenName, user.FirstName ?? string.Empty), + new Claim(ClaimTypes.Surname, user.LastName ?? string.Empty), + new Claim(ClaimTypes.Role, user.Role ?? string.Empty), + }; + var identity = new ClaimsIdentity(claims, authenticationType: "Teams"); + _httpContextAccessor.HttpContext!.User = new System.Security.Principal.GenericPrincipal(identity, roles: null); + } + + private async Task FindUserByEmailAsync(string email, CancellationToken cancellationToken) + { + using var scope = _services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + return await db.GetUserByEmail(email); + } + private static string GetUserId(IActivity activity) => activity.From?.AadObjectId - ?? activity.From?.Id - ?? activity.Conversation?.Id ?? string.Empty; } diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsAdapter.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsAdapter.cs index 193962fa0..ac498bb15 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsAdapter.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsAdapter.cs @@ -1,5 +1,8 @@ -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Bot.Connector.Authentication; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Hosting.AspNetCore; +using Microsoft.Agents.Hosting.AspNetCore.BackgroundQueue; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; namespace BotSharp.Plugin.MicrosoftTeams.Services; @@ -10,9 +13,11 @@ namespace BotSharp.Plugin.MicrosoftTeams.Services; public class TeamsAdapter : CloudAdapter { public TeamsAdapter( - BotFrameworkAuthentication auth, - ILogger logger) - : base(auth, logger) + IChannelServiceClientFactory channelServiceClientFactory, + IActivityTaskQueue activityTaskQueue, + ILogger logger, + IConfiguration configuration) + : base(channelServiceClientFactory, activityTaskQueue, logger, null, null, configuration) { OnTurnError = async (turnContext, exception) => { diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsMessageHandler.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsMessageHandler.cs index 130869ba7..656423552 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsMessageHandler.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsMessageHandler.cs @@ -1,4 +1,4 @@ -using Microsoft.Bot.Schema; +using Microsoft.Agents.Core.Models; namespace BotSharp.Plugin.MicrosoftTeams.Services; diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsNotificationService.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsNotificationService.cs index e30d6a57e..c23973596 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsNotificationService.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Services/TeamsNotificationService.cs @@ -1,10 +1,12 @@ -using Microsoft.Bot.Builder; +using Microsoft.Agents.Authentication; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Core.Models; namespace BotSharp.Plugin.MicrosoftTeams.Services; public class TeamsNotificationService : ITeamsNotificationService { - private readonly TeamsAdapter _adapter; + private readonly IChannelAdapter _adapter; private readonly IConversationReferenceStore _referenceStore; private readonly AdaptiveCardConverter _cardConverter; private readonly MicrosoftTeamsSetting _setting; @@ -12,7 +14,7 @@ public class TeamsNotificationService : ITeamsNotificationService private readonly ILogger _logger; public TeamsNotificationService( - TeamsAdapter adapter, + IChannelAdapter adapter, IConversationReferenceStore referenceStore, AdaptiveCardConverter cardConverter, MicrosoftTeamsSetting setting, @@ -36,7 +38,8 @@ public async Task SendTextAsync(string userId, string text, CancellationTo return false; } - await _adapter.ContinueConversationAsync(_setting.AppId, reference, + var identity = AgentClaims.CreateIdentity(_setting.AppId, true, null); + await _adapter.ContinueConversationAsync(identity, reference, async (turnContext, ct) => await turnContext.SendActivityAsync(MessageFactory.Text(text), ct), cancellationToken); return true; @@ -51,7 +54,8 @@ public async Task NotifyAsync(string userId, string agentId, string prompt return false; } - await _adapter.ContinueConversationAsync(_setting.AppId, reference, + var identity = AgentClaims.CreateIdentity(_setting.AppId, true, null); + await _adapter.ContinueConversationAsync(identity, reference, async (turnContext, ct) => { // Proactive turns run outside the request scope — create a fresh DI scope. diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Settings/MicrosoftTeamsSetting.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Settings/MicrosoftTeamsSetting.cs index 05a9dfc92..8442158b1 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Settings/MicrosoftTeamsSetting.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Settings/MicrosoftTeamsSetting.cs @@ -31,4 +31,5 @@ public class MicrosoftTeamsSetting /// Default agent used by the proactive notification API when none is supplied. /// public string AgentId { get; set; } = string.Empty; + public string BotName { get; set; } = "Test Bot"; } diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Using.cs b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Using.cs index e178033c0..bda5b0dc2 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Using.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftTeams/Using.cs @@ -21,6 +21,8 @@ global using BotSharp.Abstraction.Users; global using BotSharp.Abstraction.Messaging.Models.RichContent; global using BotSharp.Abstraction.Messaging.Models.RichContent.Template; +global using BotSharp.Abstraction.Repositories; +global using BotSharp.Abstraction.Users.Models; global using BotSharp.Plugin.MicrosoftTeams.Models; global using BotSharp.Plugin.MicrosoftTeams.Services; global using BotSharp.Plugin.MicrosoftTeams.Settings;