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;