Skip to content

Commit

Permalink
v4.0.0 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hawxy authored Jun 14, 2021
1 parent 2124404 commit 28b4eca
Show file tree
Hide file tree
Showing 37 changed files with 918 additions and 200 deletions.
6 changes: 6 additions & 0 deletions Discord.Addons.Hosting.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Serilog", "Samples\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Simple", "Samples\SampleBotSimple\Sample.Simple.csproj", "{E65C387F-9D99-4257-A458-1F6D5A4D80F7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.ShardedClient", "Samples\Sample.ShardedClient\Sample.ShardedClient.csproj", "{540A37B5-E304-49A4-B68E-08941C0D33F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{E65C387F-9D99-4257-A458-1F6D5A4D80F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E65C387F-9D99-4257-A458-1F6D5A4D80F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E65C387F-9D99-4257-A458-1F6D5A4D80F7}.Release|Any CPU.Build.0 = Release|Any CPU
{540A37B5-E304-49A4-B68E-08941C0D33F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{540A37B5-E304-49A4-B68E-08941C0D33F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{540A37B5-E304-49A4-B68E-08941C0D33F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{540A37B5-E304-49A4-B68E-08941C0D33F1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#region License
/*
Copyright 2020 Hawxy
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -19,28 +19,31 @@ limitations under the License.
using System.Threading.Tasks;
using Discord.Addons.Hosting.Util;
using Discord.Commands;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Discord.Addons.Hosting
{
internal class CommandServiceInitializerHost : InitializedService
internal class CommandServiceRegistrationHost : IHostedService
{
private readonly CommandService _commandService;
private readonly ILogger<CommandServiceInitializerHost> _logger;
private readonly ILogger<CommandServiceRegistrationHost> _logger;
private readonly LogAdapter<CommandService> _adapter;

public CommandServiceInitializerHost(CommandService commandService, ILogger<CommandServiceInitializerHost> logger, LogAdapter<CommandService> adapter)
public CommandServiceRegistrationHost(CommandService commandService, ILogger<CommandServiceRegistrationHost> logger, LogAdapter<CommandService> adapter)
{
_commandService = commandService;
_logger = logger;
_adapter = adapter;
}

public override Task InitializeAsync(CancellationToken cancellationToken)
public Task StartAsync(CancellationToken cancellationToken)
{
_commandService.Log += _adapter.Log;
_logger.LogDebug("Registered logger for CommandService");
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
12 changes: 6 additions & 6 deletions Discord.Addons.Hosting/Discord.Addons.Hosting.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;net5</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net5.0</TargetFrameworks>
<PackageId>Discord.Addons.Hosting</PackageId>
<Version>3.1.1</Version>
<Version>4.0.0</Version>
<Authors>Hawxy</Authors>
<Description>Simplifying Discord.Net hosting with .NET Generic Host (Microsoft.Extensions.Hosting)</Description>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
Expand All @@ -19,15 +19,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net.Commands" Version="2.2.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="2.2.0" />
<PackageReference Include="Discord.Net.Commands" Version="2.4.0" />
<PackageReference Include="Discord.Net.WebSocket" Version="2.4.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.10" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.16" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net5' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
</ItemGroup>

Expand Down
98 changes: 98 additions & 0 deletions Discord.Addons.Hosting/DiscordClientService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#region License

/*
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#endregion

using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
using Discord.WebSocket;
using Microsoft.Extensions.Logging;

namespace Discord.Addons.Hosting
{

/// <summary>
/// Base class for implementing an <see cref="DiscordClientService"/> with startup execution requirements. This class implements <see cref="BackgroundService"/>
/// </summary>
public abstract class DiscordServiceBase<T> : BackgroundService where T : BaseSocketClient
{
/// <summary>
/// The running Discord.NET Client
/// </summary>
protected T Client { get; }

/// <summary>
/// This service's logger
/// </summary>
protected ILogger Logger { get; }

/// <summary>
/// Creates a new <see cref="DiscordClientService" />
/// </summary>
/// <param name="logger">The logger for this service</param>
/// <param name="client">The discord client</param>
protected DiscordServiceBase(T client, ILogger logger)
{
Client = client;
Logger = logger;
}

/// <summary>
/// This method is called when the <see cref="IHostedService"/> starts. If the implementation is long-running, it should return a task that represents
/// the lifetime of the operation(s) being performed.
/// For more information, see <see href=" https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services#backgroundservice-base-class"/>
/// </summary>
/// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
/// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
protected abstract override Task ExecuteAsync(CancellationToken stoppingToken);
}

/// <summary>
/// Base class for implementing an <see cref="DiscordClientService"/> for a <see cref="DiscordShardedClient"/> with startup execution requirements.
/// This class implements <see cref="BackgroundService"/> and should be registered in your service collection with `AddHostedService`
/// </summary>
public abstract class DiscordClientService : DiscordServiceBase<DiscordSocketClient>
{
/// <summary>
/// Creates a new <see cref="DiscordClientService" />
/// </summary>
/// <param name="logger">The logger for this service</param>
/// <param name="client">The discord client</param>
protected DiscordClientService(DiscordSocketClient client, ILogger<DiscordClientService> logger) : base(client, logger)
{
}

}

/// <summary>
/// Base class for implementing an <see cref="DiscordShardedClientService"/> for a <see cref="DiscordShardedClient"/> with startup execution requirements.
/// This class implements <see cref="BackgroundService"/> and should be registered in your service collection with `AddHostedService`
/// </summary>
public abstract class DiscordShardedClientService : DiscordServiceBase<DiscordShardedClient>
{
/// <summary>
/// Creates a new <see cref="DiscordClientService" />
/// </summary>
/// <param name="logger">The logger for this service</param>
/// <param name="client">The discord client</param>
protected DiscordShardedClientService(DiscordShardedClient client, ILogger<DiscordShardedClientService> logger) : base(client, logger)
{
}
}
}
60 changes: 44 additions & 16 deletions Discord.Addons.Hosting/DiscordHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#region License
/*
Copyright 2020 Hawxy
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@ limitations under the License.
#endregion
using System;
using System.Linq;
using Discord.Addons.Hosting.Injectables;
using Discord.Addons.Hosting.Util;
using Discord.Commands;
using Discord.WebSocket;
Expand All @@ -32,38 +33,66 @@ namespace Discord.Addons.Hosting
public static class DiscordHostBuilderExtensions
{
/// <summary>
/// Adds and optionally configures a Discord.Net hosted instance of type <typeparamref name="T"/>
/// Adds and optionally configures a <see cref="DiscordShardedClient"/> along with the required services.
/// </summary>
/// <remarks>
/// A <see cref="HostBuilderContext"/> is supplied so that the configuration and service provider can be used.
/// </remarks>
/// <typeparam name="T">The type of Discord.Net client. Type must be or inherit from <see cref="DiscordSocketClient"/></typeparam>
/// <param name="builder">The host builder to configure.</param>
/// <param name="config">The delegate for the <see cref="DiscordHostConfiguration" /> that will be used to configure the host.</param>
/// <returns>The generic host builder.</returns>
/// <exception cref="InvalidOperationException">Thrown if client is already added to the service collection</exception>
public static IHostBuilder ConfigureDiscordHost<T>(this IHostBuilder builder, Action<HostBuilderContext, DiscordHostConfiguration>? config = null) where T : DiscordSocketClient
public static IHostBuilder ConfigureDiscordShardedHost(this IHostBuilder builder, Action<HostBuilderContext, DiscordHostConfiguration>? config = null)
{
builder.ConfigureServices((context, collection) =>
builder.ConfigureDiscordHostInternal(config);

return builder.ConfigureServices((_, collection) =>
{
if (collection.Any(x => x.ServiceType == typeof(BaseSocketClient)))
throw new InvalidOperationException("Cannot add more than one Discord Client to host");

collection.AddSingleton<DiscordShardedClient, InjectableDiscordShardedClient>();
collection.AddSingleton<BaseSocketClient>(x => x.GetRequiredService<DiscordShardedClient>());
});
}

/// <summary>
/// Adds and optionally configures a <see cref="DiscordSocketClient"/> along with the required services.
/// </summary>
/// <remarks>
/// A <see cref="HostBuilderContext"/> is supplied so that the configuration and service provider can be used.
/// </remarks>
/// <param name="builder">The host builder to configure.</param>
/// <param name="config">The delegate for the <see cref="DiscordHostConfiguration" /> that will be used to configure the host.</param>
/// <returns>The generic host builder.</returns>
/// <exception cref="InvalidOperationException">Thrown if client is already added to the service collection</exception>
public static IHostBuilder ConfigureDiscordHost(this IHostBuilder builder, Action<HostBuilderContext, DiscordHostConfiguration>? config = null)
{
builder.ConfigureDiscordHostInternal(config);

return builder.ConfigureServices((_, collection) =>
{
if (collection.Any(x => x.ServiceType == typeof(DiscordSocketClient)))
throw new InvalidOperationException($"Cannot add more than one Discord Client to host");
if (collection.Any(x => x.ServiceType == typeof(BaseSocketClient)))
throw new InvalidOperationException("Cannot add more than one Discord Client to host");

collection.AddSingleton<DiscordSocketClient, InjectableDiscordSocketClient>();
collection.AddSingleton<BaseSocketClient>(x => x.GetRequiredService<DiscordSocketClient>());
});
}

private static IHostBuilder ConfigureDiscordHostInternal(this IHostBuilder builder, Action<HostBuilderContext, DiscordHostConfiguration>? config = null)
{
return builder.ConfigureServices((context, collection) =>
{
collection.AddOptions<DiscordHostConfiguration>().Validate(x => ValidateToken(x.Token));

if (config != null)
collection.Configure<DiscordHostConfiguration>(x => config(context, x));

collection.AddSingleton(x=> (T)Activator.CreateInstance(typeof(T), x.GetRequiredService<IOptions<DiscordHostConfiguration>>().Value.SocketConfig)!);
if(typeof(T) != typeof(DiscordSocketClient))
collection.AddSingleton<DiscordSocketClient>(x => x.GetRequiredService<T>());


collection.AddSingleton(typeof(LogAdapter<>));
collection.AddHostedService<DiscordHostedService>();
});

return builder;

static bool ValidateToken(string token)
{
try
Expand All @@ -76,7 +105,6 @@ static bool ValidateToken(string token)
return false;
}
}

}

/// <summary>
Expand Down Expand Up @@ -111,7 +139,7 @@ public static IHostBuilder UseCommandService(this IHostBuilder builder, Action<H
collection.Configure<CommandServiceConfig>(x => config(context, x));

collection.AddSingleton(x=> new CommandService(x.GetRequiredService<IOptions<CommandServiceConfig>>().Value));
collection.AddHostedService<CommandServiceInitializerHost>();
collection.AddHostedService<CommandServiceRegistrationHost>();
});

return builder;
Expand Down
2 changes: 1 addition & 1 deletion Discord.Addons.Hosting/DiscordHostConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#region License
/*
Copyright 2020 Hawxy
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
6 changes: 3 additions & 3 deletions Discord.Addons.Hosting/DiscordHostedService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#region License
/*
Copyright 2020 Hawxy
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,10 +30,10 @@ namespace Discord.Addons.Hosting
internal class DiscordHostedService : IHostedService
{
private readonly ILogger<DiscordHostedService> _logger;
private readonly DiscordSocketClient _client;
private readonly BaseSocketClient _client;
private readonly DiscordHostConfiguration _config;

public DiscordHostedService(ILogger<DiscordHostedService> logger, IOptions<DiscordHostConfiguration> options, LogAdapter<DiscordSocketClient> adapter, DiscordSocketClient client)
public DiscordHostedService(ILogger<DiscordHostedService> logger, IOptions<DiscordHostConfiguration> options, LogAdapter<DiscordSocketClient> adapter, BaseSocketClient client)
{
_logger = logger;
_config = options.Value;
Expand Down
32 changes: 26 additions & 6 deletions Discord.Addons.Hosting/InitializedService.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
using Microsoft.Extensions.Hosting;
#region License
/*
Copyright 2021 Hawxy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

namespace Discord.Addons.Hosting
{
/// <summary>
/// Base class for implementing an <see cref="IHostedService"/> with one-time setup requirements.
/// </summary>
#if NET
[Obsolete("Replace with DiscordClientService. See the Discord.Addons.Hosting release notes for more information.", DiagnosticId
= "DAH001", UrlFormat = "https://github.com/Hawxy/Discord.Addons.Hosting/releases/")]
#else
[Obsolete("Replace with DiscordClientService. See the Discord.Addons.Hosting release notes for more information.")]
#endif
public abstract class InitializedService : IHostedService
{
private bool _initialized;

/// <summary>
/// This method is called when the <see cref="IHostedService"/> starts for the first time.
/// </summary>
Expand All @@ -20,12 +42,10 @@ public abstract class InitializedService : IHostedService
/// <inheritdoc cref="IHostedService.StartAsync"/>
public async Task StartAsync(CancellationToken cancellationToken)
{
if (_initialized) return;
await InitializeAsync(cancellationToken);
if (!cancellationToken.IsCancellationRequested) _initialized = true;
}

/// <inheritdoc cref="IHostedService.StopAsync"/>
public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
Loading

0 comments on commit 28b4eca

Please sign in to comment.