Skip to content

Commit dda763b

Browse files
authored
Adds BotBuilder plugin package (#19)
* bf compat package * cleanup * feedback * add to readme
1 parent 67faf6e commit dda763b

File tree

16 files changed

+356
-27
lines changed

16 files changed

+356
-27
lines changed

Libraries/Microsoft.Teams.Extensions/Microsoft.Teams.Extensions.Configuration/Microsoft.Teams.Apps.Extensions/TeamsSettings.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,23 @@ public class TeamsSettings
88
public string? ClientSecret { get; set; }
99
public string? TenantId { get; set; }
1010

11+
public bool Empty {
12+
get { return ClientId != "" && ClientSecret != ""; }
13+
}
14+
1115
public AppOptions Apply(AppOptions? options = null)
1216
{
1317
options ??= new AppOptions();
1418

15-
if (ClientId is not null && ClientSecret is not null)
19+
if (ClientId is not null && ClientSecret is not null && !Empty)
1620
{
1721
options.Credentials = new ClientCredentials(ClientId, ClientSecret, TenantId);
1822
}
1923

2024
return options;
2125
}
26+
27+
public bool IsNotEmpty() {
28+
return ClientId != "" && ClientSecret != "";
29+
}
2230
}

Libraries/Microsoft.Teams.Extensions/Microsoft.Teams.Extensions.Hosting/Microsoft.Teams.Apps.Extensions/HostApplicationBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static IHostApplicationBuilder AddTeamsCore(this IHostApplicationBuilder
2929
var loggingSettings = builder.Configuration.GetTeamsLogging();
3030

3131
// client credentials
32-
if (options.Credentials is null && settings.ClientId is not null && settings.ClientSecret is not null)
32+
if (options.Credentials is null && settings.ClientId is not null && settings.ClientSecret is not null && !settings.Empty)
3333
{
3434
options.Credentials = new ClientCredentials(
3535
settings.ClientId,
@@ -54,7 +54,7 @@ public static IHostApplicationBuilder AddTeamsCore(this IHostApplicationBuilder
5454
var loggingSettings = builder.Configuration.GetTeamsLogging();
5555

5656
// client credentials
57-
if (settings.ClientId is not null && settings.ClientSecret is not null)
57+
if (settings.ClientId is not null && settings.ClientSecret is not null && !settings.Empty)
5858
{
5959
appBuilder = appBuilder.AddCredentials(new ClientCredentials(
6060
settings.ClientId,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.Bot.Builder;
3+
using Microsoft.Bot.Builder.Integration.AspNet.Core;
4+
using Microsoft.Extensions.Hosting;
5+
using Microsoft.Teams.Api.Auth;
6+
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Teams.Apps.Extensions;
9+
using Microsoft.Teams.Api.Activities;
10+
using Microsoft.AspNetCore.Http;
11+
using System.Text.Json;
12+
13+
namespace Microsoft.Teams.Plugins.AspNetCore.BotBuilder
14+
{
15+
[ApiController]
16+
public class MessageController : ControllerBase
17+
{
18+
private readonly IBotFrameworkHttpAdapter _adapter;
19+
private readonly IBot _bot;
20+
21+
private readonly AspNetCorePlugin _plugin;
22+
23+
private readonly IHostApplicationLifetime _lifetime;
24+
25+
public MessageController(IBotFrameworkHttpAdapter adapter, IBot bot, AspNetCorePlugin plugin, IHostApplicationLifetime lifetime)
26+
{
27+
_plugin = plugin;
28+
_lifetime = lifetime;
29+
_adapter = adapter;
30+
_bot = bot;
31+
}
32+
33+
[HttpPost("/api/messages")]
34+
public async Task<IResult> PostAsync()
35+
{
36+
HttpContext.Request.EnableBuffering();
37+
var body = await new StreamReader(Request.Body).ReadToEndAsync();
38+
Activity? activity = JsonSerializer.Deserialize<Activity>(body);
39+
HttpContext.Request.Body.Position = 0;
40+
41+
if (activity == null) {
42+
return Results.BadRequest("Missing activity");
43+
}
44+
45+
// Delegate the processing of the HTTP POST to the adapter.
46+
// The adapter will invoke the bot.
47+
await _adapter.ProcessAsync(HttpContext.Request, HttpContext.Response, _bot);
48+
49+
if (Response.HasStarted) {
50+
return Results.Empty;
51+
}
52+
53+
// Fallback logic
54+
var authHeader = HttpContext.Request.Headers.Authorization.FirstOrDefault() ?? throw new UnauthorizedAccessException();
55+
var token = new JsonWebToken(authHeader.Replace("Bearer ", ""));
56+
var context = HttpContext.RequestServices.GetRequiredService<TeamsContext>();
57+
context.Token = token;
58+
var res = await _plugin.Do(token, activity, _lifetime.ApplicationStopping);
59+
return Results.Json(res.Body, statusCode: (int)res.Status);
60+
}
61+
}
62+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
2+
using Microsoft.Bot.Builder;
3+
using Microsoft.Bot.Builder.Integration.AspNet.Core;
4+
using Microsoft.Bot.Connector.Authentication;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Hosting;
7+
using Microsoft.Teams.Plugins.AspNetCore.Controllers;
8+
9+
namespace Microsoft.Teams.Plugins.AspNetCore.Extensions;
10+
11+
public static class HostApplicationBuilderExtensions
12+
{
13+
public static IHostApplicationBuilder AddBotBuilder(this IHostApplicationBuilder builder)
14+
{
15+
builder.Services.AddControllers().ConfigureApplicationPartManager((apm) => {
16+
apm.FeatureProviders.Add(new RemoveDefaultMessageController());
17+
apm.ApplicationParts.Add(new AssemblyPart(typeof(MessageController).Assembly));
18+
});
19+
return builder;
20+
}
21+
22+
public static IHostApplicationBuilder AddBotBuilder<TBot>(this IHostApplicationBuilder builder, BotFrameworkAuthentication authentication, IBotFrameworkHttpAdapter adapter) where TBot : class, IBot
23+
{
24+
builder.Services.AddSingleton(authentication);
25+
builder.Services.AddSingleton(adapter);
26+
builder.Services.AddTransient<IBot, TBot>();
27+
builder.Services.AddControllers().ConfigureApplicationPartManager((apm) => {
28+
apm.FeatureProviders.Add(new RemoveDefaultMessageController());
29+
apm.ApplicationParts.Add(new AssemblyPart(typeof(MessageController).Assembly));
30+
});
31+
return builder;
32+
}
33+
34+
public static IHostApplicationBuilder AddBotBuilder<TBot, TBotFrameworkHttpAdapter, TBotFrameworkAuthentication>(this IHostApplicationBuilder builder) where TBot : class, IBot where TBotFrameworkAuthentication : BotFrameworkAuthentication where TBotFrameworkHttpAdapter : class, IBotFrameworkHttpAdapter
35+
{
36+
builder.Services.AddSingleton<BotFrameworkAuthentication, TBotFrameworkAuthentication>();
37+
builder.Services.AddSingleton<IBotFrameworkHttpAdapter, TBotFrameworkHttpAdapter>();
38+
builder.Services.AddTransient<IBot, TBot>();
39+
builder.Services.AddControllers().ConfigureApplicationPartManager((apm) => {
40+
apm.FeatureProviders.Add(new RemoveDefaultMessageController());
41+
apm.ApplicationParts.Add(new AssemblyPart(typeof(MessageController).Assembly));
42+
});
43+
return builder;
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\..\Directory.Build.props" Condition="'$(Version)' == ''" />
4+
5+
<PropertyGroup>
6+
<PackageId>Microsoft.Teams.Plugins.AspNetCore.BotBuilder</PackageId>
7+
<PackageDescription>Teams AspNetCore BotBuilder Plugin</PackageDescription>
8+
<PackageProjectUrl>https://microsoft.github.io/teams-ai</PackageProjectUrl>
9+
<PackageTags>microsoft;teams;msteams;copilot;ai;plugins;aspnetcore;botbuilder</PackageTags>
10+
</PropertyGroup>
11+
12+
<PropertyGroup>
13+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
14+
<ImplicitUsings>enable</ImplicitUsings>
15+
<Nullable>enable</Nullable>
16+
<LangVersion>latest</LangVersion>
17+
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
18+
<NoWarn>CS8618,CS0067</NoWarn>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.23.0" />
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<ProjectReference Include="..\Microsoft.Teams.Plugins.AspNetCore\Microsoft.Teams.Plugins.AspNetCore.csproj" />
31+
32+
<ProjectReference Include="..\..\Microsoft.Teams.Api\Microsoft.Teams.Api.csproj" />
33+
<ProjectReference Include="..\..\Microsoft.Teams.Apps\Microsoft.Teams.Apps.csproj" />
34+
</ItemGroup>
35+
36+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Teams: AspNetCore BotBuilder Compatibility Plugin
2+
3+
<a href="https://microsoft.github.io/teams-ai" target="_blank">
4+
<img src="https://img.shields.io/badge/📖 Getting Started-blue?style=for-the-badge" />
5+
</a>
6+
7+
Use this plugin to integrate your existing BotBuilder activity handler and adapter with the Teams App.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.AspNetCore.Mvc.ApplicationParts;
2+
using Microsoft.AspNetCore.Mvc.Controllers;
3+
using System.Reflection;
4+
5+
public class RemoveDefaultMessageController : IApplicationFeatureProvider<ControllerFeature>
6+
{
7+
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
8+
{
9+
Type messageController = typeof(Microsoft.Teams.Plugins.AspNetCore.Controllers.MessageController);
10+
11+
List<TypeInfo> matches = feature.Controllers.Where(c => c.AsType() == messageController).ToList();
12+
foreach (TypeInfo match in matches) {
13+
feature.Controllers.Remove(match);
14+
}
15+
}
16+
}

Microsoft.Teams.sln

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.Tests"
5555
EndProject
5656
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.Testing", "Libraries\Microsoft.Teams.Apps.Testing\Microsoft.Teams.Apps.Testing.csproj", "{6542E318-AF5B-4D63-B5BA-242AA48085B9}"
5757
EndProject
58+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Plugins.AspNetCore.BotBuilder", "Libraries\Microsoft.Teams.Plugins\Microsoft.Teams.Plugins.AspNetCore.BotBuilder\Microsoft.Teams.Plugins.AspNetCore.BotBuilder.csproj", "{E7277625-CD7C-42C0-84C1-EE39D8CCF909}"
59+
EndProject
60+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.BotBuilder", "Samples\Samples.BotBuilder\Samples.BotBuilder.csproj", "{6A21694E-6F76-463E-AF50-C258A0C94BF4}"
61+
EndProject
5862
Global
5963
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6064
Debug|Any CPU = Debug|Any CPU
@@ -221,30 +225,6 @@ Global
221225
{15D7A915-DD11-40BF-811B-C97B2C963785}.Release|x64.Build.0 = Release|Any CPU
222226
{15D7A915-DD11-40BF-811B-C97B2C963785}.Release|x86.ActiveCfg = Release|Any CPU
223227
{15D7A915-DD11-40BF-811B-C97B2C963785}.Release|x86.Build.0 = Release|Any CPU
224-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
225-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
226-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|x64.ActiveCfg = Debug|Any CPU
227-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|x64.Build.0 = Debug|Any CPU
228-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|x86.ActiveCfg = Debug|Any CPU
229-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Debug|x86.Build.0 = Debug|Any CPU
230-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
231-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|Any CPU.Build.0 = Release|Any CPU
232-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|x64.ActiveCfg = Release|Any CPU
233-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|x64.Build.0 = Release|Any CPU
234-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|x86.ActiveCfg = Release|Any CPU
235-
{606E13CC-9F41-4739-8659-948D0D5A6DAC}.Release|x86.Build.0 = Release|Any CPU
236-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
237-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|Any CPU.Build.0 = Debug|Any CPU
238-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|x64.ActiveCfg = Debug|Any CPU
239-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|x64.Build.0 = Debug|Any CPU
240-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|x86.ActiveCfg = Debug|Any CPU
241-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Debug|x86.Build.0 = Debug|Any CPU
242-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|Any CPU.ActiveCfg = Release|Any CPU
243-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|Any CPU.Build.0 = Release|Any CPU
244-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|x64.ActiveCfg = Release|Any CPU
245-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|x64.Build.0 = Release|Any CPU
246-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|x86.ActiveCfg = Release|Any CPU
247-
{982F2C7D-2993-4C0E-8527-AF55CC90D47D}.Release|x86.Build.0 = Release|Any CPU
248228
{8E6AA215-43B1-4519-A554-A6642586E7A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
249229
{8E6AA215-43B1-4519-A554-A6642586E7A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
250230
{8E6AA215-43B1-4519-A554-A6642586E7A6}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -329,6 +309,30 @@ Global
329309
{6542E318-AF5B-4D63-B5BA-242AA48085B9}.Release|x64.Build.0 = Release|Any CPU
330310
{6542E318-AF5B-4D63-B5BA-242AA48085B9}.Release|x86.ActiveCfg = Release|Any CPU
331311
{6542E318-AF5B-4D63-B5BA-242AA48085B9}.Release|x86.Build.0 = Release|Any CPU
312+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
313+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|Any CPU.Build.0 = Debug|Any CPU
314+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|x64.ActiveCfg = Debug|Any CPU
315+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|x64.Build.0 = Debug|Any CPU
316+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|x86.ActiveCfg = Debug|Any CPU
317+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Debug|x86.Build.0 = Debug|Any CPU
318+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|Any CPU.ActiveCfg = Release|Any CPU
319+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|Any CPU.Build.0 = Release|Any CPU
320+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|x64.ActiveCfg = Release|Any CPU
321+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|x64.Build.0 = Release|Any CPU
322+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|x86.ActiveCfg = Release|Any CPU
323+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909}.Release|x86.Build.0 = Release|Any CPU
324+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
325+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
326+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|x64.ActiveCfg = Debug|Any CPU
327+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|x64.Build.0 = Debug|Any CPU
328+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|x86.ActiveCfg = Debug|Any CPU
329+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Debug|x86.Build.0 = Debug|Any CPU
330+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
331+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|Any CPU.Build.0 = Release|Any CPU
332+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|x64.ActiveCfg = Release|Any CPU
333+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|x64.Build.0 = Release|Any CPU
334+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|x86.ActiveCfg = Release|Any CPU
335+
{6A21694E-6F76-463E-AF50-C258A0C94BF4}.Release|x86.Build.0 = Release|Any CPU
332336
EndGlobalSection
333337
GlobalSection(SolutionProperties) = preSolution
334338
HideSolutionNode = FALSE
@@ -357,5 +361,7 @@ Global
357361
{E1AD5351-181A-4EEC-B1F6-BF1643E5A1FA} = {E2648928-28DE-6B24-129A-26A0E8B5B70E}
358362
{9251FA96-F118-4578-92A9-EBD667CE3978} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
359363
{6542E318-AF5B-4D63-B5BA-242AA48085B9} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE}
364+
{E7277625-CD7C-42C0-84C1-EE39D8CCF909} = {F2D7E877-38D6-8C91-7D90-99648DD298BA}
365+
{6A21694E-6F76-463E-AF50-C258A0C94BF4} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
360366
EndGlobalSection
361367
EndGlobal

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ $: dotnet test
5050
- [`Microsoft.Teams.Extensions.Logging`](./Libraries/Microsoft.Teams.Extensions/Microsoft.Teams.Extensions.Logging/README.md)
5151
- [`Microsoft.Teams.Plugins.AspNetCore`](./Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore/README.md)
5252
- [`Microsoft.Teams.Plugins.AspNetCore.DevTools`](./Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore.DevTools/README.md)
53+
- [`Microsoft.Teams.Plugins.AspNetCore.BotBuilder`](./Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore.BotBuilder/README.md)
5354

5455
## External Packages
5556

Samples/Samples.BotBuilder/Bot.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Microsoft.Bot.Builder;
2+
using Microsoft.Bot.Schema;
3+
4+
namespace Samples.BotBuilder
5+
{
6+
public class Bot : ActivityHandler
7+
{
8+
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
9+
{
10+
var replyText = $"hi from botbuilder...";
11+
await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
12+
}
13+
}
14+
}

0 commit comments

Comments
 (0)