From 87042d89ba67231ac0ea51c48f58aefe098e6017 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 20 Jul 2022 17:50:47 +0100 Subject: [PATCH] Add X10D.Hosting & X10D.DSharpPlus --- .github/workflows/nightly.yml | 2 + .github/workflows/prerelease.yml | 2 + .github/workflows/release.yml | 2 + CHANGELOG.md | 3 + X10D.DSharpPlus/X10D.DSharpPlus.csproj | 61 +++++ .../src/DiscordChannelExtensions.cs | 80 +++++++ .../src/DiscordClientExtensions.cs | 34 +++ .../src/DiscordEmbedBuilderExtensions.cs | 211 ++++++++++++++++++ X10D.DSharpPlus/src/DiscordGuildExtensions.cs | 63 ++++++ .../src/DiscordMemberExtensions.cs | 73 ++++++ .../src/DiscordMessageExtensions.cs | 89 ++++++++ X10D.DSharpPlus/src/DiscordUserExtensions.cs | 158 +++++++++++++ X10D.Hosting/X10D.Hosting.csproj | 61 +++++ .../ServiceCollectionExtensions.cs | 35 +++ X10D.sln | 12 + 15 files changed, 886 insertions(+) create mode 100644 X10D.DSharpPlus/X10D.DSharpPlus.csproj create mode 100644 X10D.DSharpPlus/src/DiscordChannelExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordClientExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordEmbedBuilderExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordGuildExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordMemberExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordMessageExtensions.cs create mode 100644 X10D.DSharpPlus/src/DiscordUserExtensions.cs create mode 100644 X10D.Hosting/X10D.Hosting.csproj create mode 100644 X10D.Hosting/src/DependencyInjection/ServiceCollectionExtensions.cs diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 87a6e4b..f661292 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,6 +32,8 @@ jobs: run: | mkdir build dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} + dotnet pack X10D.DSharpPlus -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} + dotnet pack X10D.Hosting -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='nightly' -p:BuildNumber=${{ github.run_number }} - name: Push NuGet Package to GitHub diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index c17716d..b257fbf 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -32,6 +32,8 @@ jobs: run: | mkdir build dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} + dotnet pack X10D.DSharpPlus -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} + dotnet pack X10D.Hosting -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} - name: Push NuGet Package to GitHub diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1de44e8..4de3372 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,8 @@ jobs: run: | mkdir build dotnet pack X10D -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build + dotnet pack X10D.DSharpPlus -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build + dotnet pack X10D.Hosting -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build dotnet pack X10D.Unity -p:SymbolPackageFormat=snupkg --include-symbols --include-source -o build - name: Push NuGet Package to GitHub diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e17676..cd5246e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 3.2.0 ### Added +- Added new library X10D.DSharpPlus +- Added new library X10D.Hosting + - X10D: Added `MathUtility.InverseLerp(float, float, float)` and `MathUtility.InverseLerp(double, double, double)` - X10D: Added `Circle`, `CircleF`, `Cuboid`, `Ellipse`, `EllipseF`, `Line3D`, `Line`, `LineF`, `Polygon`, `PolygonF`, `Polyhedron`, and `Sphere`, to complement System.Drawing structs such as `Point` and `Rectangle` - X10D: Added `Color.Deconstruct()` - with optional alpha parameter diff --git a/X10D.DSharpPlus/X10D.DSharpPlus.csproj b/X10D.DSharpPlus/X10D.DSharpPlus.csproj new file mode 100644 index 0000000..27715d7 --- /dev/null +++ b/X10D.DSharpPlus/X10D.DSharpPlus.csproj @@ -0,0 +1,61 @@ + + + + net6.0;netstandard2.1 + 10.0 + true + true + Oliver Booth + en + https://github.com/oliverbooth/X10D + git + Extension methods on crack. + LICENSE.md + icon.png + + dotnet extension-methods + true + 3.2.0 + enable + true + true + + + + $(VersionPrefix)-$(VersionSuffix) + $(VersionPrefix).0 + $(VersionPrefix).0 + + + + $(VersionPrefix)-$(VersionSuffix).$(BuildNumber) + $(VersionPrefix).$(BuildNumber) + $(VersionPrefix).$(BuildNumber) + + + + $(VersionPrefix) + $(VersionPrefix).0 + $(VersionPrefix).0 + + + + + + + + + True + + + + True + + + + True + + + + + diff --git a/X10D.DSharpPlus/src/DiscordChannelExtensions.cs b/X10D.DSharpPlus/src/DiscordChannelExtensions.cs new file mode 100644 index 0000000..48957c3 --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordChannelExtensions.cs @@ -0,0 +1,80 @@ +using DSharpPlus; +using DSharpPlus.Entities; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordChannelExtensions +{ + /// + /// Gets the category of this channel. + /// + /// The channel whose category to retrieve. + /// + /// The category of , or itself if it is already a category; + /// if this channel is not defined in a category. + /// + /// is . + public static DiscordChannel? GetCategory(this DiscordChannel channel) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(channel); +#else + if (channel is null) + { + throw new ArgumentNullException(nameof(channel)); + } +#endif + + while (true) + { + if (channel.IsCategory) + { + return channel; + } + + if (channel.Parent is not { } parent) + { + return null; + } + + channel = parent; + } + } + + /// + /// Normalizes a so that the internal client is assured to be a specified value. + /// + /// The to normalize. + /// The target client. + /// + /// A whose public values will match , but whose internal client + /// is . + /// + /// + /// is + /// -or- + /// is + /// + public static async Task NormalizeClientAsync(this DiscordChannel channel, DiscordClient client) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(channel); + ArgumentNullException.ThrowIfNull(client); +#else + if (channel is null) + { + throw new ArgumentNullException(nameof(channel)); + } + + if (client is null) + { + throw new ArgumentNullException(nameof(client)); + } +#endif + + return await client.GetChannelAsync(channel.Id); + } +} diff --git a/X10D.DSharpPlus/src/DiscordClientExtensions.cs b/X10D.DSharpPlus/src/DiscordClientExtensions.cs new file mode 100644 index 0000000..5835d93 --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordClientExtensions.cs @@ -0,0 +1,34 @@ +using DSharpPlus; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordClientExtensions +{ + /// + /// Instructs the client to automatically join all existing threads, and any newly-created threads. + /// + /// The whose events should be subscribed. + /// + /// to automatically rejoin a thread if this client was removed; otherwise, + /// . + /// + public static void AutoJoinThreads(this DiscordClient client, bool rejoinIfRemoved = true) + { + client.GuildAvailable += (_, args) => args.Guild.JoinAllThreadsAsync(); + client.ThreadCreated += (_, args) => args.Thread.JoinThreadAsync(); + + if (rejoinIfRemoved) + { + client.ThreadMembersUpdated += (_, args) => + { + if (args.RemovedMembers.Any(m => m.Id == client.CurrentUser.Id)) + return args.Thread.JoinThreadAsync(); + + return Task.CompletedTask; + }; + } + } +} diff --git a/X10D.DSharpPlus/src/DiscordEmbedBuilderExtensions.cs b/X10D.DSharpPlus/src/DiscordEmbedBuilderExtensions.cs new file mode 100644 index 0000000..fb21b85 --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordEmbedBuilderExtensions.cs @@ -0,0 +1,211 @@ +using DSharpPlus.Entities; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordEmbedBuilderExtensions +{ + /// + /// Conditionally adds a field to the embed. + /// + /// The to modify. + /// The condition whose value is used to determine whether the field will be added. + /// The name of the embed field. + /// The value of the embed field. + /// to display this field inline; otherwise, . + /// The type of . + /// The current instance of ; that is, . + /// is . + public static DiscordEmbedBuilder AddFieldIf( + this DiscordEmbedBuilder builder, + bool condition, + string name, + T? value, + bool inline = false) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(builder); +#else + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } +#endif + + if (condition) + { + builder.AddField(name, value?.ToString(), inline); + } + + return builder; + } + + /// + /// Conditionally adds a field to the embed. + /// + /// The to modify. + /// The predicate whose return value is used to determine whether the field will be added. + /// The name of the embed field. + /// The value of the embed field. + /// to display this field inline; otherwise, . + /// The type of . + /// The current instance of ; that is, . + /// + /// is . + /// -or- + /// is . + /// + public static DiscordEmbedBuilder AddFieldIf( + this DiscordEmbedBuilder builder, + Func predicate, + string name, + T? value, + bool inline = false) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(predicate); +#else + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (predicate is null) + { + throw new ArgumentNullException(nameof(predicate)); + } +#endif + + if (predicate.Invoke()) + { + builder.AddField(name, value?.ToString(), inline); + } + + return builder; + } + + /// + /// Conditionally adds a field to the embed. + /// + /// The to modify. + /// The predicate whose return value is used to determine whether the field will be added. + /// The name of the embed field. + /// The delegate whose return value will be used as the value of the embed field. + /// to display this field inline; otherwise, . + /// The return type of . + /// The current instance of ; that is, . + /// + /// is . + /// -or- + /// is . + /// -or- + /// is . + /// + public static DiscordEmbedBuilder AddFieldIf( + this DiscordEmbedBuilder builder, + Func predicate, + string name, + Func valueFactory, + bool inline = false) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(predicate); + ArgumentNullException.ThrowIfNull(valueFactory); +#else + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (predicate is null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + if (valueFactory is null) + { + throw new ArgumentNullException(nameof(valueFactory)); + } +#endif + + if (predicate.Invoke()) + { + builder.AddField(name, valueFactory.Invoke()?.ToString(), inline); + } + + return builder; + } + + /// + /// Conditionally adds a field to the embed. + /// + /// The to modify. + /// The condition whose value is used to determine whether the field will be added. + /// The name of the embed field. + /// The delegate whose return value will be used as the value of the embed field. + /// to display this field inline; otherwise, . + /// The return type of . + /// The current instance of ; that is, . + /// + /// is . + /// -or- + /// is . + /// + public static DiscordEmbedBuilder AddFieldIf( + this DiscordEmbedBuilder builder, + bool condition, + string name, + Func valueFactory, + bool inline = false) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(valueFactory); +#else + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + if (valueFactory is null) + { + throw new ArgumentNullException(nameof(valueFactory)); + } +#endif + + if (condition) + { + builder.AddField(name, valueFactory.Invoke()?.ToString(), inline); + } + + return builder; + } + + /// + /// Sets the embed's author. + /// + /// The embed builder to modify. + /// The author. + /// The current instance of . + public static DiscordEmbedBuilder WithAuthor(this DiscordEmbedBuilder builder, DiscordUser user) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(user); +#else + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + if (user is null) + { + throw new ArgumentNullException(nameof(user)); + } +#endif + + return builder.WithAuthor(user.Username, user.AvatarUrl); + } +} diff --git a/X10D.DSharpPlus/src/DiscordGuildExtensions.cs b/X10D.DSharpPlus/src/DiscordGuildExtensions.cs new file mode 100644 index 0000000..d5451f4 --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordGuildExtensions.cs @@ -0,0 +1,63 @@ +using DSharpPlus; +using DSharpPlus.Entities; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordGuildExtensions +{ + /// + /// Joins all active threads in the guild that this client has permission to view. + /// + /// The guild whose active threads to join. + /// is . + public static async Task JoinAllThreadsAsync(this DiscordGuild guild) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(guild); +#else + if (guild is null) + { + throw new ArgumentNullException(nameof(guild)); + } +#endif + + await Task.WhenAll(guild.Threads.Values.Select(t => t.JoinThreadAsync())); + } + + /// + /// Normalizes a so that the internal client is assured to be a specified value. + /// + /// The to normalize. + /// The target client. + /// + /// A whose public values will match , but whose internal client is + /// . + /// + /// + /// is + /// -or- + /// is + /// + public static async Task NormalizeClientAsync(this DiscordGuild guild, DiscordClient client) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(guild); + ArgumentNullException.ThrowIfNull(client); +#else + if (guild is null) + { + throw new ArgumentNullException(nameof(guild)); + } + + if (client is null) + { + throw new ArgumentNullException(nameof(client)); + } +#endif + + return await client.GetGuildAsync(guild.Id); + } +} diff --git a/X10D.DSharpPlus/src/DiscordMemberExtensions.cs b/X10D.DSharpPlus/src/DiscordMemberExtensions.cs new file mode 100644 index 0000000..aede000 --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordMemberExtensions.cs @@ -0,0 +1,73 @@ +using DSharpPlus; +using DSharpPlus.Entities; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordMemberExtensions +{ + /// + /// Returns a value indicating whether this member has the specified role. + /// + /// The member whose roles to search. + /// The role for which to check. + /// + /// if has the role; otherwise, . + /// + public static bool HasRole(this DiscordMember member, DiscordRole role) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(member); + ArgumentNullException.ThrowIfNull(role); +#else + if (member is null) + { + throw new ArgumentNullException(nameof(member)); + } + + if (role is null) + { + throw new ArgumentNullException(nameof(role)); + } +#endif + + return member.Roles.Contains(role); + } + + /// + /// Normalizes a so that the internal client is assured to be a specified value. + /// + /// The to normalize. + /// The target client. + /// + /// A whose public values will match , but whose internal client + /// is . + /// + /// + /// is + /// -or- + /// is + /// + public static async Task NormalizeClientAsync(this DiscordMember member, DiscordClient client) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(member); + ArgumentNullException.ThrowIfNull(client); +#else + if (member is null) + { + throw new ArgumentNullException(nameof(member)); + } + + if (client is null) + { + throw new ArgumentNullException(nameof(client)); + } +#endif + + DiscordGuild guild = await member.Guild.NormalizeClientAsync(client); + return await guild.GetMemberAsync(member.Id); + } +} diff --git a/X10D.DSharpPlus/src/DiscordMessageExtensions.cs b/X10D.DSharpPlus/src/DiscordMessageExtensions.cs new file mode 100644 index 0000000..e52b25f --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordMessageExtensions.cs @@ -0,0 +1,89 @@ +using DSharpPlus; +using DSharpPlus.Entities; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordMessageExtensions +{ + /// + /// Deletes this message after a specified delay. + /// + /// The message to delete. + /// The delay before deletion. + /// The reason for the deletion. + /// is . + public static async Task DeleteAfterAsync(this DiscordMessage message, TimeSpan delay, string? reason = null) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(message); +#else + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } +#endif + + await Task.Delay(delay); + await message.DeleteAsync(reason); + } + + /// + /// Deletes the message as created by this task after a specified delay. + /// + /// The task whose result should be deleted. + /// The delay before deletion. + /// The reason for the deletion. + /// is . + public static async Task DeleteAfterAsync(this Task task, TimeSpan delay, string? reason = null) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + throw new ArgumentNullException(nameof(task)); + } +#endif + + DiscordMessage message = await task; + await message.DeleteAfterAsync(delay, reason); + } + + /// + /// Normalizes a so that the internal client is assured to be a specified value. + /// + /// The to normalize. + /// The target client. + /// + /// A whose public values will match , but whose internal client + /// is . + /// + /// + /// is + /// -or- + /// is + /// + public static async Task NormalizeClientAsync(this DiscordMessage message, DiscordClient client) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(message); + ArgumentNullException.ThrowIfNull(client); +#else + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + + if (client is null) + { + throw new ArgumentNullException(nameof(client)); + } +#endif + + DiscordChannel channel = await message.Channel.NormalizeClientAsync(client); + return await channel.GetMessageAsync(message.Id); + } +} diff --git a/X10D.DSharpPlus/src/DiscordUserExtensions.cs b/X10D.DSharpPlus/src/DiscordUserExtensions.cs new file mode 100644 index 0000000..248a66c --- /dev/null +++ b/X10D.DSharpPlus/src/DiscordUserExtensions.cs @@ -0,0 +1,158 @@ +using DSharpPlus; +using DSharpPlus.Entities; +using DSharpPlus.Exceptions; + +namespace X10D.DSharpPlus; + +/// +/// Extension methods for . +/// +public static class DiscordUserExtensions +{ + /// + /// Returns the current as a member of the specified guild. + /// + /// The user to transform. + /// The guild whose member list to search. + /// + /// A whose is equal to , or + /// if this user is not in the specified . + /// + /// + /// is . + /// -or- + /// is . + /// + public static async Task GetAsMemberOfAsync(this DiscordUser user, DiscordGuild guild) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(user); + ArgumentNullException.ThrowIfNull(guild); +#else + if (user is null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (guild is null) + { + throw new ArgumentNullException(nameof(guild)); + } +#endif + + if (user is DiscordMember member && member.Guild == guild) + { + return member; + } + + if (guild.Members.TryGetValue(user.Id, out member!)) + { + return member; + } + + try + { + return await guild.GetMemberAsync(user.Id); + } + catch (NotFoundException) + { + return null; + } + } + + /// + /// Returns the user's username with the discriminator, in the format username#discriminator. + /// + /// The user whose username and discriminator to retrieve. + /// A string in the format username#discriminator + /// is . + public static string GetUsernameWithDiscriminator(this DiscordUser user) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(user); +#else + if (user is null) + { + throw new ArgumentNullException(nameof(user)); + } +#endif + + return $"{user.Username}#{user.Discriminator}"; + } + + /// + /// Returns a value indicating whether the current user is in the specified guild. + /// + /// The user to check. + /// The guild whose member list to search. + /// + /// if is a member of ; otherwise, + /// . + /// + public static async Task IsInGuildAsync(this DiscordUser user, DiscordGuild guild) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(user); + ArgumentNullException.ThrowIfNull(guild); +#else + if (user is null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (guild is null) + { + throw new ArgumentNullException(nameof(guild)); + } +#endif + + if (guild.Members.TryGetValue(user.Id, out _)) + { + return true; + } + + try + { + DiscordMember? member = await guild.GetMemberAsync(user.Id); + return member is not null; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// Normalizes a so that the internal client is assured to be a specified value. + /// + /// The to normalize. + /// The target client. + /// + /// A whose public values will match , but whose internal client is + /// . + /// + /// + /// is + /// -or- + /// is + /// + public static async Task NormalizeClientAsync(this DiscordUser user, DiscordClient client) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(user); + ArgumentNullException.ThrowIfNull(client); +#else + if (user is null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (client is null) + { + throw new ArgumentNullException(nameof(client)); + } +#endif + + return await client.GetGuildAsync(user.Id); + } +} diff --git a/X10D.Hosting/X10D.Hosting.csproj b/X10D.Hosting/X10D.Hosting.csproj new file mode 100644 index 0000000..1c487ee --- /dev/null +++ b/X10D.Hosting/X10D.Hosting.csproj @@ -0,0 +1,61 @@ + + + + net6.0;netstandard2.1 + 10.0 + true + true + Oliver Booth + en + https://github.com/oliverbooth/X10D + git + Extension methods on crack. + LICENSE.md + icon.png + + dotnet extension-methods + true + 3.2.0 + enable + true + true + + + + $(VersionPrefix)-$(VersionSuffix) + $(VersionPrefix).0 + $(VersionPrefix).0 + + + + $(VersionPrefix)-$(VersionSuffix).$(BuildNumber) + $(VersionPrefix).$(BuildNumber) + $(VersionPrefix).$(BuildNumber) + + + + $(VersionPrefix) + $(VersionPrefix).0 + $(VersionPrefix).0 + + + + + + + + + True + + + + True + + + + True + + + + + diff --git a/X10D.Hosting/src/DependencyInjection/ServiceCollectionExtensions.cs b/X10D.Hosting/src/DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..9a0bafe --- /dev/null +++ b/X10D.Hosting/src/DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace X10D.Hosting.DependencyInjection; + +/// +/// Dependency injection extensions for . +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds an registration for the given type, while simultaneously adding it as a singleton. + /// + /// The to add the service to. + /// The type of the service to add. + /// A reference to this instance after the operation has completed. + public static IServiceCollection AddHostedSingleton(this IServiceCollection services) + where TService : class, IHostedService + { + services.AddSingleton(); + return services.AddSingleton(provider => provider.GetRequiredService()); + } + + /// + /// Adds an registration for the given type, while simultaneously adding it as a singleton. + /// + /// The to add the service to. + /// The type of the service to register and the implementation to use. + /// A reference to this instance after the operation has completed. + public static IServiceCollection AddHostedSingleton(this IServiceCollection services, Type type) + { + services.AddSingleton(type); + return services.AddSingleton(provider => (IHostedService)provider.GetRequiredService(type)); + } +} diff --git a/X10D.sln b/X10D.sln index e6f0bcc..2c404b7 100644 --- a/X10D.sln +++ b/X10D.sln @@ -24,6 +24,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X10D.Unity", "X10D.Unity\X1 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X10D.SourceGenerator", "X10D.SourceGenerator\X10D.SourceGenerator.csproj", "{077A5D33-AD55-4C55-8A67-972CEBC32C7A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X10D.DSharpPlus", "X10D.DSharpPlus\X10D.DSharpPlus.csproj", "{675D3B25-7EA0-4FC3-B513-8DF27874F2CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X10D.Hosting", "X10D.Hosting\X10D.Hosting.csproj", "{B04AF429-30CF-4B69-81BA-38F560CA9126}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -50,6 +54,14 @@ Global {077A5D33-AD55-4C55-8A67-972CEBC32C7A}.Debug|Any CPU.Build.0 = Debug|Any CPU {077A5D33-AD55-4C55-8A67-972CEBC32C7A}.Release|Any CPU.ActiveCfg = Release|Any CPU {077A5D33-AD55-4C55-8A67-972CEBC32C7A}.Release|Any CPU.Build.0 = Release|Any CPU + {675D3B25-7EA0-4FC3-B513-8DF27874F2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {675D3B25-7EA0-4FC3-B513-8DF27874F2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {675D3B25-7EA0-4FC3-B513-8DF27874F2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {675D3B25-7EA0-4FC3-B513-8DF27874F2CF}.Release|Any CPU.Build.0 = Release|Any CPU + {B04AF429-30CF-4B69-81BA-38F560CA9126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B04AF429-30CF-4B69-81BA-38F560CA9126}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B04AF429-30CF-4B69-81BA-38F560CA9126}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B04AF429-30CF-4B69-81BA-38F560CA9126}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE