diff --git a/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs index 92a9e064b..34a927ca2 100644 --- a/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs +++ b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs @@ -1,312 +1,312 @@ using System; using System.Collections.Generic; using System.Linq; using DisCatSharp.Configuration.Models; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Xunit; namespace DisCatSharp.Configuration.Tests { public class ConfigurationExtensionTests { #region Test Classes class SampleClass { public int Amount { get; set; } public string? Email { get; set; } } class ClassWithArray { public int[] Values { get; set; } = { 1, 2, 3, 4, 5 }; public string[] Strings { get; set; } = { "1", "2", "3", "4", "5" }; } class ClassWithEnumerable { public IEnumerable Values { get; set; } = new[] { 1, 2, 3, 4, 5 }; public IEnumerable Strings { get; set; } = new[] { "1", "2", "3", "4", "5" }; } class ClassWithList { public List Strings { get; set; } = new List { "1", "2", "3", "4", "5" }; public List Values { get; set; } = new List { 1, 2, 3, 4, 5 }; } class SampleClass2 { public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(7); public string Name { get; set; } = "Sample"; public string ConstructorValue { get; } public SampleClass2(string value) { this.ConstructorValue = value; } } #endregion private IConfiguration EnumerableTestConfiguration() => new ConfigurationBuilder() .AddJsonFile("enumerable-test.json") .Build(); private IConfiguration HasSectionWithSuffixConfiguration() => new ConfigurationBuilder() .AddJsonFile("section-with-suffix.json") .Build(); private IConfiguration HasSectionNoSuffixConfiguration() => new ConfigurationBuilder() .AddJsonFile("section-no-suffix.json") .Build(); private IConfiguration BasicDiscordConfiguration() => new ConfigurationBuilder() .AddJsonFile("default-discord.json") .Build(); private IConfiguration DiscordIntentsConfig() => new ConfigurationBuilder() .AddJsonFile("intents-discord.json") .Build(); private IConfiguration DiscordHaphazardConfig() => new ConfigurationBuilder() .AddJsonFile("haphazard-discord.json") .Build(); private IConfiguration SampleConfig() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "Sample:Amount", "200" }, { "Sample:Email", "test@gmail.com" } }) .Build(); private IConfiguration SampleClass2Configuration_Default() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { {"Random:Stuff", "Meow"}, {"SampleClass2:Name", "Purfection"} }) .Build(); private IConfiguration SampleClass2Configuration_Change() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "SampleClass:Timeout", "01:30:00" }, { "SampleClass:NotValid", "Something" } }) .Build(); private IConfiguration SampleClass2EnumerableTest() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "SampleClass:EnumerableTest", "[\"10\",\"20\",\"30\"]" } }) .Build(); private IConfiguration SampleClass2ArrayTest() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "SampleClass:ArrayTest", "[\"10\",\"20\",\"30\"]" } }) .Build(); private IConfiguration SampleClass2ListTest() => new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { { "SampleClass:ListTest", "[\"10\",\"20\",\"30\"]" } }) .Build(); [Fact] public void TestExtractDiscordConfig_Intents() { var source = this.DiscordIntentsConfig(); - DiscordConfiguration config = source.ExtractConfig("Discord"); + var config = source.ExtractConfig("Discord"); var expected = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers | DiscordIntents.GuildInvites | DiscordIntents.GuildMessageReactions; Assert.Equal(expected, config.Intents); } [Fact] public void TestExtractDiscordConfig_Haphzard() { var source = this.DiscordHaphazardConfig(); - DiscordConfiguration config = source.ExtractConfig("Discord"); + var config = source.ExtractConfig("Discord"); var expectedIntents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers | DiscordIntents.Guilds; Assert.Equal(expectedIntents, config.Intents); Assert.True(config.MobileStatus); Assert.Equal(1000, config.LargeThreshold); Assert.Equal(TimeSpan.FromHours(10), config.HttpTimeout); } [Fact] public void TestExtractDiscordConfig_Default() { var source = this.BasicDiscordConfiguration(); - DiscordConfiguration config = source.ExtractConfig("Discord"); + var config = source.ExtractConfig("Discord"); Assert.Equal("1234567890", config.Token); Assert.Equal(TokenType.Bot, config.TokenType); Assert.Equal(LogLevel.Information, config.MinimumLogLevel); Assert.True(config.UseRelativeRatelimit); Assert.Equal("yyyy-MM-dd HH:mm:ss zzz", config.LogTimestampFormat); Assert.Equal(250, config.LargeThreshold); Assert.True(config.AutoReconnect); Assert.Equal(123123, config.ShardId); Assert.Equal(GatewayCompressionLevel.Stream, config.GatewayCompressionLevel); Assert.Equal(1024, config.MessageCacheSize); Assert.Equal(TimeSpan.FromSeconds(20), config.HttpTimeout); Assert.False(config.ReconnectIndefinitely); Assert.True(config.AlwaysCacheMembers); Assert.Equal(DiscordIntents.AllUnprivileged, config.Intents); Assert.False(config.MobileStatus); Assert.False(config.UseCanary); Assert.False(config.AutoRefreshChannelCache); } [Fact] public void TestSection() { var source = this.SampleConfig(); - SampleClass config = source.ExtractConfig("Sample", null); + var config = source.ExtractConfig("Sample", null); Assert.Equal(200, config.Amount); Assert.Equal("test@gmail.com", config.Email); } [Fact] public void TestExtractConfig_V2_Default() { var source = this.SampleClass2Configuration_Default(); var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test"), null); Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout); Assert.Equal("Test", config.ConstructorValue); Assert.Equal("Sample", config.Name); } [Fact] public void TestExtractConfig_V2_Change() { var source = this.SampleClass2Configuration_Change(); var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test123"), null); var span = new TimeSpan(0, 1, 30, 0); Assert.Equal(span, config.Timeout); Assert.Equal("Test123", config.ConstructorValue); Assert.Equal("Sample", config.Name); } [Fact] public void TestExtractConfig_V3_Default() { var source = this.SampleClass2Configuration_Default(); var config = (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() => new SampleClass2("Meow")); Assert.Equal("Meow", config.ConstructorValue); Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout); Assert.Equal("Sample", config.Name); } [Fact] public void TestExtractConfig_V3_Change() { var source = this.SampleClass2Configuration_Change(); var config = (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() => new SampleClass2("Meow")); Assert.Equal("Meow", config.ConstructorValue); var span = new TimeSpan(0, 1, 30, 0); Assert.Equal(span, config.Timeout); Assert.Equal("Sample", config.Name); } [Fact] public void TestExtractConfig_Enumerable() { var source = this.EnumerableTestConfiguration(); var config = (ClassWithEnumerable)new ConfigSection(ref source, "ClassWithEnumerable", null).ExtractConfig(() => new ClassWithEnumerable()); Assert.NotNull(config.Values); Assert.Equal(3, config.Values.Count()); Assert.NotNull(config.Strings); Assert.Equal(3, config.Values.Count()); } [Fact] public void TestExtractConfig_Array() { var source = this.EnumerableTestConfiguration(); var config = (ClassWithArray)new ConfigSection(ref source, "ClassWithArray", null).ExtractConfig(() => new ClassWithArray()); Assert.NotNull(config.Values); Assert.Equal(3, config.Values.Length); Assert.NotNull(config.Strings); Assert.Equal(3, config.Values.Length); } [Fact] public void TestExtractConfig_List() { var source = this.EnumerableTestConfiguration(); var config = (ClassWithList)new ConfigSection(ref source, "ClassWithList", null).ExtractConfig(() => new ClassWithList()); Assert.NotNull(config.Values); Assert.Equal(3, config.Values.Count); Assert.NotNull(config.Strings); Assert.Equal(3, config.Values.Count); } [Fact] public void TestHasSectionWithSuffix() { var source = this.HasSectionWithSuffixConfiguration(); Assert.True(source.HasSection("DiscordConfiguration")); Assert.False(source.HasSection("Discord")); #pragma warning disable 8625 Assert.False(source.HasSection("DiscordConfiguration", null)); #pragma warning restore 8625 } [Fact] public void TestHasSectionNoSuffix() { var source = this.HasSectionNoSuffixConfiguration(); Assert.True(source.HasSection("Discord")); Assert.False(source.HasSection("DiscordConfiguration")); #pragma warning disable 8625 Assert.False(source.HasSection("Discord", null)); #pragma warning restore 8625 } } } diff --git a/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj b/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj index d09b883e9..4d9e32b25 100644 --- a/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj +++ b/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj @@ -1,51 +1,52 @@ net5.0 enable false + false runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all Always Always Always Always Always Always diff --git a/DisCatSharp.Configuration.Tests/GlobalSuppressions.cs b/DisCatSharp.Configuration.Tests/GlobalSuppressions.cs new file mode 100644 index 000000000..0929f5f15 --- /dev/null +++ b/DisCatSharp.Configuration.Tests/GlobalSuppressions.cs @@ -0,0 +1,67 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.BasicDiscordConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.DiscordHaphazardConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.DiscordIntentsConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.EnumerableTestConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.HasSectionNoSuffixConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.HasSectionWithSuffixConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ArrayTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ArrayTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2Configuration_Change~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2Configuration_Default~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2EnumerableTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2EnumerableTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ListTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ListTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.BasicDiscordConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.DiscordHaphazardConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.DiscordIntentsConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.EnumerableTestConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.HasSectionNoSuffixConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.HasSectionWithSuffixConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2.#ctor(System.String)")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ArrayTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2Configuration_Change~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2Configuration_Default~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2EnumerableTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2ListTest~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleConfig~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_Array")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_Enumerable")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_List")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_V2_Change")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_V2_Default")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_V3_Change")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractConfig_V3_Default")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractDiscordConfig_Default")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractDiscordConfig_Haphzard")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestExtractDiscordConfig_Intents")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestHasSectionNoSuffix")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestHasSectionWithSuffix")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.TestSection")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithArray.Strings")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithArray.Values")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithEnumerable.Strings")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithEnumerable.Values")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithList.Strings")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithList.Values")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass.Amount")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass.Email")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2.ConstructorValue")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2.Name")] +[assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2.Timeout")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithArray")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithEnumerable")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.ClassWithList")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Configuration.Tests.ConfigurationExtensionTests.SampleClass2")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "")] diff --git a/DisCatSharp.Configuration/ConfigurationExtensions.cs b/DisCatSharp.Configuration/ConfigurationExtensions.cs index 931e424e8..3c836ea2f 100644 --- a/DisCatSharp.Configuration/ConfigurationExtensions.cs +++ b/DisCatSharp.Configuration/ConfigurationExtensions.cs @@ -1,270 +1,279 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections; using System.Linq; -using System.Reflection; using DisCatSharp.Configuration.Models; using Microsoft.Extensions.Configuration; namespace DisCatSharp.Configuration { + /// + /// The configuration extensions. + /// internal static class ConfigurationExtensions { + /// + /// The factory error message. + /// private const string FactoryErrorMessage = "Require a function which provides a default entity to work with"; + /// + /// The default root lib. + /// public const string DefaultRootLib = "DisCatSharp"; + /// + /// The config suffix. + /// private const string ConfigSuffix = "Configuration"; /// /// Easily piece together paths that will work within /// /// (not used - only for adding context based functionality) /// The strings to piece together /// Strings joined together via ':' public static string ConfigPath(this IConfiguration config, params string[] values) => string.Join(":", values); /// /// Skims over the configuration section and only overrides values that are explicitly defined within the config /// /// Instance of config /// Section which contains values for private static void HydrateInstance(ref object config, ConfigSection section) { - PropertyInfo[] props = config.GetType().GetProperties(); + var props = config.GetType().GetProperties(); foreach (var prop in props) { // Must have a set method for this to work, otherwise continue on if (prop.SetMethod == null) continue; - string entry = section.GetValue(prop.Name); + var entry = section.GetValue(prop.Name); object? value = null; if (typeof(string) == prop.PropertyType) { // We do NOT want to override value if nothing was provided if(!string.IsNullOrEmpty(entry)) prop.SetValue(config, entry); continue; } // We need to address collections a bit differently // They can come in the form of "root:section:name" with a string representation OR // "root:section:name:0" <--- this is not detectable when checking the above path if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)) { - if (string.IsNullOrEmpty(section.GetValue(prop.Name))) - value = section.Config - .GetSection(section.GetPath(prop.Name)).Get(prop.PropertyType); - else - value = Newtonsoft.Json.JsonConvert.DeserializeObject(entry, prop.PropertyType); + value = string.IsNullOrEmpty(section.GetValue(prop.Name)) + ? section.Config + .GetSection(section.GetPath(prop.Name)).Get(prop.PropertyType) + : Newtonsoft.Json.JsonConvert.DeserializeObject(entry, prop.PropertyType); if (value == null) continue; prop.SetValue(config, value); } // From this point onward we require the 'entry' value to have something useful if (string.IsNullOrEmpty(entry)) continue; try { // Primitive types are simple to convert if (prop.PropertyType.IsPrimitive) value = Convert.ChangeType(entry, prop.PropertyType); else { // The following types require a different approach if (prop.PropertyType.IsEnum) value = Enum.Parse(prop.PropertyType, entry); else if (typeof(TimeSpan) == prop.PropertyType) value = TimeSpan.Parse(entry); else if (typeof(DateTime) == prop.PropertyType) value = DateTime.Parse(entry); else if (typeof(DateTimeOffset) == prop.PropertyType) value = DateTimeOffset.Parse(entry); } // Update value within our config instance prop.SetValue(config, value); } catch (Exception ex) { Console.Error.WriteLine( $"Unable to convert value of '{entry}' to type '{prop.PropertyType.Name}' for prop '{prop.Name}' in config '{config.GetType().Name}'\n\t\t{ex.Message}"); } } } /// /// Instantiate an entity using then walk through the specified /// and translate user-defined config values to the instantiated instance from /// /// Section containing values for targeted config /// Function which generates a default entity /// Hydrated instance of an entity which contains user-defined values (if any) /// When is null public static object ExtractConfig(this ConfigSection section, Func factory) { if (factory == null) throw new ArgumentNullException(nameof(factory),FactoryErrorMessage); // Create default instance - object config = factory(); + var config = factory(); HydrateInstance(ref config, section); return config; } /// /// Instantiate an entity using then walk through the specified /// in . Translate user-defined config values to the instantiated instance from /// /// Loaded App Configuration /// Name of section to load /// Function which creates a default entity to work with /// (Optional) Used when section is nested within another. Default value is /// Hydrated instance of an entity which contains user-defined values (if any) /// When is null public static object ExtractConfig(this IConfiguration config, string sectionName, Func factory, string? rootSectionName = DefaultRootLib) { if (factory == null) throw new ArgumentNullException(nameof(factory), FactoryErrorMessage); // create default instance - object instance = factory(); + var instance = factory(); HydrateInstance(ref instance, new ConfigSection(ref config, sectionName, rootSectionName)); return instance; } /// /// Instantiate a new instance of , then walk through the specified /// in . Translate user-defined config values to the instance. /// /// Loaded App Configuration /// Name of section to load /// (Optional) Used when section is nested with another. Default value is /// Type of instance that represents /// Hydrated instance of which contains the user-defined values (if any). public static TConfig ExtractConfig(this IConfiguration config, string sectionName, string? rootSectionName = DefaultRootLib) where TConfig : new() { // Default values should hopefully be provided from the constructor object configInstance = new TConfig(); HydrateInstance(ref configInstance, new ConfigSection(ref config, sectionName, rootSectionName)); return (TConfig) configInstance; } /// /// Determines if contains a particular section/object (not value) /// /// /// /// { /// "Discord": { // this is a section/object /// /// }, /// "Value": "something" // this is not a section/object /// } /// /// /// /// /// True if section exists, otherwise false public static bool HasSection(this IConfiguration config, params string[] values) { if (!values.Any()) return false; if (values.Length == 1) return config.GetChildren().Any(x => x.Key == values[0]); if (config.GetChildren().All(x => x.Key != values[0])) return false; - IConfigurationSection current = config.GetSection(values[0]); + var current = config.GetSection(values[0]); - for (int i = 1; i < values.Length - 1; i++) + for (var i = 1; i < values.Length - 1; i++) { if (current.GetChildren().All(x => x.Key != values[i])) return false; current = current.GetSection(values[i]); } return current.GetChildren().Any(x=>x.Key == values[^1]); } /// /// Instantiates an instance of , then consumes any custom /// configuration from user/developer from .
/// View remarks for more info ///
/// /// This is an example of how your JSON structure should look if you wish /// to override one or more of the default values from /// /// { /// "DisCatSharp": { /// "Discord": { } /// } /// } /// ///
/// Alternatively, you can use the type name itself /// /// { /// "DisCatSharp": { /// "DiscordConfiguration": { } /// } /// } /// ///
/// /// Instance of public static DiscordClient BuildClient(this IConfiguration config) { var section = config.HasSection(DefaultRootLib, "Discord") ? "Discord" : config.HasSection(DefaultRootLib, $"Discord{ConfigSuffix}") ? $"Discord:{ConfigSuffix}" : null; - if (string.IsNullOrEmpty(section)) - return new DiscordClient(new()); - - return new DiscordClient(config.ExtractConfig(section)); + return string.IsNullOrEmpty(section) + ? new DiscordClient(new()) + : new DiscordClient(config.ExtractConfig(section)); } } } diff --git a/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj index 8b14dac70..0f63f27b8 100644 --- a/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj +++ b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj @@ -1,42 +1,42 @@ - + net5.0 enable DisCatSharp.Configuration DisCatSharp.Configuration True DisCatSharp.Configuration Configuration for DisCatSharp. discord, discord-api, bots, discord-bots, net-sdk, dcs, discatsharp, csharp, dotnet, vb-net, fsharp LICENSE.md True diff --git a/DisCatSharp.Configuration/GlobalSuppressions.cs b/DisCatSharp.Configuration/GlobalSuppressions.cs new file mode 100644 index 000000000..3ee88f00e --- /dev/null +++ b/DisCatSharp.Configuration/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Configuration.ConfigurationExtensions.ConfigPath(Microsoft.Extensions.Configuration.IConfiguration,System.String[])~System.String")] diff --git a/DisCatSharp.Configuration/Models/ConfigSection.cs b/DisCatSharp.Configuration/Models/ConfigSection.cs index 162981435..34e4de608 100644 --- a/DisCatSharp.Configuration/Models/ConfigSection.cs +++ b/DisCatSharp.Configuration/Models/ConfigSection.cs @@ -1,87 +1,94 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using Microsoft.Extensions.Configuration; namespace DisCatSharp.Configuration.Models { /// /// Represents an object in /// internal readonly struct ConfigSection { /// /// Key within which represents an object containing multiple values /// public string SectionName { get;} /// /// Optional used to indicate this section is nested within another /// public string? Root { get; } /// /// Reference to used within application /// public IConfiguration Config { get; } + /// + /// Initializes a new instance of the class. + /// /// Reference to config /// Section of interest /// (Optional) Indicates is nested within this name. Default value is DisCatSharp public ConfigSection(ref IConfiguration config, string sectionName, string? rootName = "DisCatSharp") { this.Config = config; this.SectionName = sectionName; this.Root = rootName; } /// /// Checks if key exists in /// /// Property / Key to search for in section /// True if key exists, otherwise false. Outputs path to config regardless public bool ContainsKey(string name) { - string path = string.IsNullOrEmpty(this.Root) + var path = string.IsNullOrEmpty(this.Root) ? this.Config.ConfigPath(this.SectionName, name) : this.Config.ConfigPath(this.Root, this.SectionName, name); return !string.IsNullOrEmpty(this.Config[path]); } /// /// Attempts to get value associated to the config path.
Should be used in unison with ///
/// Config path to value /// Value found at public string GetValue(string propName) => this.Config[this.GetPath(propName)]; + /// + /// Gets the path. + /// + /// The value. + /// A string. public string GetPath(string value) { - if (string.IsNullOrEmpty(this.Root)) - return this.Config.ConfigPath(this.SectionName, value); - - return this.Config.ConfigPath(this.Root, this.SectionName, value); + return string.IsNullOrEmpty(this.Root) + ? this.Config.ConfigPath(this.SectionName, value) + : this.Config.ConfigPath(this.Root, this.SectionName, value); } } } diff --git a/DisCatSharp.Configuration/AssemblyProperties.cs b/DisCatSharp.Configuration/Properties/AssemblyProperties.cs similarity index 100% rename from DisCatSharp.Configuration/AssemblyProperties.cs rename to DisCatSharp.Configuration/Properties/AssemblyProperties.cs diff --git a/DisCatSharp.Docs/docfx.json b/DisCatSharp.Docs/docfx.json index 5503f8f0d..30a40d32c 100644 --- a/DisCatSharp.Docs/docfx.json +++ b/DisCatSharp.Docs/docfx.json @@ -1,82 +1,83 @@ { "metadata": [ { "src": [ { "src": "..", "files": [ "**.cs" ], "exclude": [ "**/obj/**", "**/bin/**", "_site/**" ] } ], "dest": "api", "filter": "filter_config.yml" } ], "build": { "content": [ { "files": [ "api/**.yml", "api/index.md" ] }, { "files": [ "articles/**.md", "articles/**/toc.yml", "natives/**.md", "faq/**.md", "toc.yml", "*.md" ], "exclude": [ "obj/**", "_site/**" ] } ], "resource": [ { "files": [ - "images/**" + "images/**", + "natives/**.zip" ], "exclude": [ "obj/**", "_site/**" ] } ], "overwrite": [ { "files": [ "apidoc/**.md" ], "exclude": [ "obj/**", "_site/**" ] } ], "dest": "_site", "globalMetadata": { "_appFooter": "© 2021 Aiko IT Systems", "_enableSearch": "true" }, "globalMetadataFiles": [], "fileMetadataFiles": [], "template": [ "dcs" ], "postProcessors": ["ExtractSearchIndex", "CustomMemberIndexer"], "noLangKeyword": false, "keepFileLink": false, "cleanupCacheHistory": false, "disableGitFeatures": false } } diff --git a/DisCatSharp.Docs/filter_config.yml b/DisCatSharp.Docs/filter_config.yml index 4cbe49777..0dee89b81 100644 --- a/DisCatSharp.Docs/filter_config.yml +++ b/DisCatSharp.Docs/filter_config.yml @@ -1,79 +1,83 @@ apiRules: - exclude: uidRegex: ^System\.Collections\.Immutable$ - exclude: uidRegex: ^System\.Runtime\.CompilerServices\.Unsafe$ - exclude: uidRegex: ^System\.Runtime\.CompilerServices$ - exclude: uidRegex: ^System\..*$ +- exclude: + uidRegex: ^DisCatSharp\.Hosting\.Tests$ +- exclude: + uidRegex: ^DisCatSharp\.Configuration\.Tests$ attributeRules: - exclude: uidRegex: ^System\.Collections\.Immutable$ type: Namespace - exclude: uidRegex: ^System\.Runtime\.CompilerServices\.Unsafe$ type: Namespace - exclude: uidRegex: ^System\.Runtime\.CompilerServices$ type: Namespace - exclude: uidRegex: ^System\.ComponentModel\.Design$ type: Namespace - exclude: uidRegex: ^System\.ComponentModel\.Design\.Serialization$ type: Namespace - exclude: uidRegex: ^System\.Xml\.Serialization$ type: Namespace - exclude: uidRegex: ^System\.Web\.Compilation$ type: Namespace - exclude: uidRegex: ^System\.Runtime\.Versioning$ type: Namespace - exclude: uidRegex: ^System\.Runtime\.ConstrainedExecution$ type: Namespace - exclude: uidRegex: ^System\.EnterpriseServices$ type: Namespace - exclude: uidRegex: ^System\.Diagnostics\.CodeAnalysis$ type: Namespace - include: uidRegex: ^System\.Diagnostics\.(ConditionalAttribute|EventLogPermissionAttribute|PerformanceCounterPermissionAttribute)$ type: Type - exclude: uidRegex: '^System\.Diagnostics\.[^.]+$' type: Type - include: uidRegex: ^System\.ComponentModel\.(BindableAttribute|BrowsableAttribute|ComplexBindingPropertiesAttribute|DataObjectAttribute|DefaultBindingPropertyAttribute|ListBindableAttribute|LookupBindingPropertiesAttribute|SettingsBindableAttribute|TypeConverterAttribute)$ type: Type - exclude: uidRegex: '^System\.ComponentModel\.[^.]+$' type: Type - exclude: uidRegex: ^System\.Reflection\.DefaultMemberAttribute$ type: Type - exclude: uidRegex: ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ type: Type - exclude: uidRegex: '^System\.Runtime\.CompilerServices\.[^.]+$' type: Type - exclude: uidRegex: '^System\.Runtime\.InteropServices\.[^.]+$' type: Type - include: uidRegex: ^System\.Security\.(SecurityCriticalAttribute|SecurityTreatAsSafeAttribute|AllowPartiallyTrustedCallersAttribute)$ type: Type - exclude: uidRegex: '^System\.Security\.[^.]+$' type: Type - exclude: uidRegex: '^System\.Web\.UI\.[^.]+$' type: Type - exclude: uidRegex: '^System\.Windows\.Markup\.[^.]+$' type: Type diff --git a/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj b/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj index 2627031e5..17f4eb101 100644 --- a/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj +++ b/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj @@ -1,20 +1,37 @@ + + net5.0 enable True + + DisCatSharp.Hosting.DependencyInjection + Dependency Injection for DisCatSharp.Hosting. + discord, discord-api, bots, discord-bots, net-sdk, dcs, discatsharp, csharp, dotnet, vb-net, fsharp + + LICENSE.md + + + + + True + + + + diff --git a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs index 092098806..b2e19d45f 100644 --- a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs +++ b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs @@ -1,25 +1,28 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; namespace DisCatSharp.Hosting.DependencyInjection { + /// + /// The service collection extensions. + /// public static class ServiceCollectionExtensions { /// /// Add as a background service /// /// /// is scoped to ServiceLifetime.Singleton.
/// Maps to Implementation of ///
/// /// /// public static IServiceCollection AddDiscordHostedService(this IServiceCollection services) where TService : class, IDiscordHostedService { services.AddSingleton(); services.AddHostedService(provider => provider.GetRequiredService()); return services; } } } diff --git a/DisCatSharp.Hosting.Tests/DisCatSharp.Hosting.Tests.csproj b/DisCatSharp.Hosting.Tests/DisCatSharp.Hosting.Tests.csproj index bd43e41d4..e844b7484 100644 --- a/DisCatSharp.Hosting.Tests/DisCatSharp.Hosting.Tests.csproj +++ b/DisCatSharp.Hosting.Tests/DisCatSharp.Hosting.Tests.csproj @@ -1,38 +1,39 @@ net5.0 enable false + false runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all Always diff --git a/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs new file mode 100644 index 000000000..b0f832543 --- /dev/null +++ b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs @@ -0,0 +1,20 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "")] +[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "")] +[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")] +[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")] +[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")] +[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")] +[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.DiscordHostedService},System.IServiceProvider)")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DiscordInteractivityAndLavaLinkConfiguration~Microsoft.Extensions.Configuration.IConfiguration")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create(System.Collections.Generic.Dictionary{System.String,System.String})~Microsoft.Extensions.Hosting.IHostBuilder")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")] diff --git a/DisCatSharp.Hosting.Tests/HostTests.cs b/DisCatSharp.Hosting.Tests/HostTests.cs index ec7e88fc8..df546cb3f 100644 --- a/DisCatSharp.Hosting.Tests/HostTests.cs +++ b/DisCatSharp.Hosting.Tests/HostTests.cs @@ -1,149 +1,143 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections.Generic; using DisCatSharp.Interactivity; using DisCatSharp.Lavalink; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Xunit; namespace DisCatSharp.Hosting.Tests { public class Bot : DiscordHostedService { public Bot(IConfiguration config, ILogger logger, IServiceProvider provider) : base(config, logger, provider) { } } public class HostTests { private Dictionary DefaultDiscord() => new() { { "DisCatSharp:Discord:Token", "1234567890" }, { "DisCatSharp:Discord:TokenType", "Bot" }, { "DisCatSharp:Discord:MinimumLogLevel", "Information" }, { "DisCatSharp:Discord:UseRelativeRateLimit", "true" }, { "DisCatSharp:Discord:LogTimestampFormat", "yyyy-MM-dd HH:mm:ss zzz" }, { "DisCatSharp:Discord:LargeThreshold", "250" }, { "DisCatSharp:Discord:AutoReconnect", "true" }, { "DisCatSharp:Discord:ShardId", "123123" }, { "DisCatSharp:Discord:GatewayCompressionLevel", "Stream" }, { "DisCatSharp:Discord:MessageCacheSize", "1024" }, { "DisCatSharp:Discord:HttpTimeout", "00:00:20" }, { "DisCatSharp:Discord:ReconnectIndefinitely", "false" }, { "DisCatSharp:Discord:AlwaysCacheMembers", "true" }, { "DisCatSharp:Discord:DiscordIntents", "AllUnprivileged" }, { "DisCatSharp:Discord:MobileStatus", "false" }, { "DisCatSharp:Discord:UseCanary", "false" }, { "DisCatSharp:Discord:AutoRefreshChannelCache", "false" }, { "DisCatSharp:Discord:Intents", "AllUnprivileged" } }; public Dictionary DiscordInteractivity() => new (this.DefaultDiscord()) { {"DisCatSharp:Using","[\"DisCatSharp.Interactivity\"]"}, }; public Dictionary DiscordInteractivityAndLavalink() => new (this.DefaultDiscord()) { {"DisCatSharp:Using","[\"DisCatSharp.Interactivity\", \"DisCatSharp.Lavalink\"]"}, }; IHostBuilder Create(Dictionary configValues) => Host.CreateDefaultBuilder() - .ConfigureServices(services => - { - services.AddSingleton(); - }) - .ConfigureHostConfiguration(builder => - { - builder.AddInMemoryCollection(configValues); - }); + .ConfigureServices(services => services.AddSingleton()) + .ConfigureHostConfiguration(builder => builder.AddInMemoryCollection(configValues)); [Fact] public void TestNoExtensions() { IHost? host = null; try { host = this.Create(this.DefaultDiscord()).Build(); var service = host.Services.GetRequiredService(); Assert.NotNull(service); Assert.NotNull(service.Client); } finally { host?.Dispose(); } } [Fact] public void TestInteractivityExtension() { IHost? host = null; try { host = this.Create(this.DiscordInteractivity()).Build(); var service = host.Services.GetRequiredService(); Assert.NotNull(service); Assert.NotNull(service.Client); Assert.NotNull(service.Client.GetExtension()); } finally { host?.Dispose(); } } [Fact] public void TestInteractivityLavalinkExtensions() { IHost? host = null; try { host = this.Create(this.DiscordInteractivityAndLavalink()).Build(); var service = host.Services.GetRequiredService(); Assert.NotNull(service); Assert.NotNull(service.Client); Assert.NotNull(service.Client.GetExtension()); Assert.NotNull(service.Client.GetExtension()); } finally { host?.Dispose(); } } } } diff --git a/DisCatSharp.Hosting/ConfigurationExtensions.cs b/DisCatSharp.Hosting/ConfigurationExtensions.cs index 947d7688c..09027e964 100644 --- a/DisCatSharp.Hosting/ConfigurationExtensions.cs +++ b/DisCatSharp.Hosting/ConfigurationExtensions.cs @@ -1,191 +1,201 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using DisCatSharp.Configuration; using DisCatSharp.Configuration.Models; using Microsoft.Extensions.Configuration; namespace DisCatSharp.Hosting { internal struct ExtensionConfigResult { + /// + /// Gets or sets the section. + /// public ConfigSection? Section { get; set; } + /// + /// Gets or sets the config type. + /// public Type ConfigType { get; set; } + /// + /// Gets or sets the implementation type. + /// public Type ImplementationType { get; set; } } + /// + /// The configuration extensions. + /// internal static class ConfigurationExtensions { /// /// Find assemblies that match the names provided via . /// /// /// In some cases the assembly the user is after could be used in the application but /// not appear within the .
/// The workaround for this is to check the assemblies in the , as well as referenced /// assemblies. If the targeted assembly is a reference, we need to load it into our workspace to get more info. ///
/// Names of assemblies to look for /// Assemblies which meet the given names. No duplicates public static Assembly[] FindAssemblies(string[]? names) { /* There is a possibility that an assembly can be referenced in multiple assemblies. To alleviate duplicates we need to shrink our queue as we find things */ List results = new(); if (names is null) return results.ToArray(); List queue = new(names); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (!queue.Any()) break; var loadedAssemblyName = assembly.GetName().Name; // Kinda need the name to do our thing if (loadedAssemblyName == null) continue; // Is this something we're looking for? if (queue.Contains(loadedAssemblyName)) { results.Add(assembly); // Shrink queue so we don't accidentally add the same assembly > 1 times queue.Remove(loadedAssemblyName); } // Time to check if one of the referenced assemblies is something we're looking for foreach(var referencedAssembly in assembly.GetReferencedAssemblies() .Where(x => x.Name != null && queue.Contains(x.Name))) try { // Must load the assembly into our workspace so we can do stuff with it later results.Add(Assembly.Load(referencedAssembly)); #pragma warning disable 8604 queue.Remove(referencedAssembly.Name); #pragma warning restore 8604 } catch (Exception ex) { Console.Error.WriteLine($"Unable to load referenced assembly: '{referencedAssembly.Name}' \n\t{ex.Message}"); } } return results.ToArray(); } /// /// Easily identify which configuration types have been added to the
/// This way we can dynamically load extensions without explicitly doing so ///
/// /// /// Dictionary where Key -> Name of implemented type
Value ->
public static Dictionary FindImplementedExtensions(this IConfiguration configuration, string rootName = Configuration.ConfigurationExtensions.DefaultRootLib) { if (string.IsNullOrEmpty(rootName)) throw new ArgumentNullException(nameof(rootName), "Root name must be provided"); Dictionary results = new(); string[]? assemblyNames; // Has the user defined a using section within the root name? if (!configuration.HasSection(rootName, "Using")) return results; /* There are 2 ways a user could list which assemblies are used "Using": "[\"Assembly.Name\"]" "Using": ["Assembly.Name"] JSON or as Text. */ - if (string.IsNullOrEmpty(configuration[configuration.ConfigPath(rootName, "Using")])) - assemblyNames = configuration.GetSection(configuration.ConfigPath(rootName, "Using")).Get(); - else - assemblyNames = - Newtonsoft.Json.JsonConvert.DeserializeObject( + assemblyNames = string.IsNullOrEmpty(configuration[configuration.ConfigPath(rootName, "Using")]) + ? configuration.GetSection(configuration.ConfigPath(rootName, "Using")).Get() + : Newtonsoft.Json.JsonConvert.DeserializeObject( configuration[configuration.ConfigPath(rootName, "Using")]); #pragma warning disable 8604 foreach (var assembly in FindAssemblies(assemblyNames.Select(x=> x.StartsWith(Constants.LibName) ? x : $"{Constants.LibName}.{x}").ToArray())) { ExtensionConfigResult result = new(); foreach (var type in assembly.ExportedTypes .Where(x => x.Name.EndsWith(Constants.ConfigSuffix) && !x.IsAbstract && !x.IsInterface)) { string sectionName = type.Name; string prefix = type.Name.Replace(Constants.ConfigSuffix, ""); result.ConfigType = type; // Does a section exist with the classname? (DiscordConfiguration - for instance) if(configuration.HasSection( rootName, sectionName)) result.Section = new ConfigSection(ref configuration, type.Name, rootName); // Does a section exist with the classname minus Configuration? (Discord - for Instance) else if (configuration.HasSection(rootName, prefix)) result.Section = new ConfigSection(ref configuration, prefix, rootName); // IF THE SECTION IS NOT PROVIDED --> WE WILL USE DEFAULT CONFIG IMPLEMENTATION /* Now we need to find the type which should consume our config In the event a user has some "fluff" between prefix and suffix we'll just check for beginning and ending values. Type should not be an interface or abstract, should also be assignable to BaseExtension */ var implementationType = assembly.ExportedTypes.FirstOrDefault(x => !x.IsAbstract && !x.IsInterface && x.Name.StartsWith(prefix) && x.Name.EndsWith(Constants.ExtensionSuffix) && x.IsAssignableTo(typeof(BaseExtension))); // If the implementation type was found we can add it to our result set if (implementationType != null) { result.ImplementationType = implementationType; results.Add(implementationType.Name, result); } } } #pragma warning restore 8604 return results; } } } diff --git a/DisCatSharp.Hosting/Constants.cs b/DisCatSharp.Hosting/Constants.cs index e7e9cbafc..04461964c 100644 --- a/DisCatSharp.Hosting/Constants.cs +++ b/DisCatSharp.Hosting/Constants.cs @@ -1,31 +1,43 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. namespace DisCatSharp.Hosting { + /// + /// The constants. + /// internal static class Constants { + /// + /// Gets the lib name. + /// public static string LibName => Configuration.ConfigurationExtensions.DefaultRootLib; + /// + /// Gets the config suffix. + /// public static string ConfigSuffix => "Configuration"; + /// + /// Gets the extension suffix. + /// public static string ExtensionSuffix => "Extension"; } } diff --git a/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj b/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj index 5851d19c8..fb56ac52c 100644 --- a/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj +++ b/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj @@ -1,22 +1,39 @@ + + net5.0 enable True + + DisCatSharp.Hosting + Hosting for DisCatSharp. + discord, discord-api, bots, discord-bots, net-sdk, dcs, discatsharp, csharp, dotnet, vb-net, fsharp + + LICENSE.md + + + + + True + + + + diff --git a/DisCatSharp.Hosting/DiscordHostedService.cs b/DisCatSharp.Hosting/DiscordHostedService.cs index a3f3ba128..71a4f7a8b 100644 --- a/DisCatSharp.Hosting/DiscordHostedService.cs +++ b/DisCatSharp.Hosting/DiscordHostedService.cs @@ -1,149 +1,157 @@ // This file is part of the DisCatSharp project, a fork of DSharpPlus. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using DisCatSharp.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace DisCatSharp.Hosting { /// /// Simple implementation for to work as a /// public abstract class DiscordHostedService : BackgroundService, IDiscordHostedService { /// public DiscordClient Client { get; private set; } private readonly ILogger _logger; #pragma warning disable 8618 + /// + /// Initializes a new instance of the class. + /// + /// The config. + /// The logger. + /// The provider. protected DiscordHostedService(IConfiguration config, ILogger logger, IServiceProvider provider) { this._logger = logger; this.Initialize(config, provider); } #pragma warning restore 8618 /// /// Automatically search for and configure /// /// /// private void Initialize(IConfiguration config, IServiceProvider provider) { var typeMap = config.FindImplementedExtensions(); this._logger.LogDebug($"Found the following config types: {string.Join("\n\t", typeMap.Keys)}"); this.Client = config.BuildClient(); foreach (var typePair in typeMap) try { /* If section is null --> utilize the default constructor This means the extension was explicitly added in the 'Using' array, but user did not wish to override any value(s) in the extension's config */ - object configInstance = typePair.Value.Section.HasValue + var configInstance = typePair.Value.Section.HasValue ? typePair.Value.Section.Value.ExtractConfig(() => ActivatorUtilities.CreateInstance(provider, typePair.Value.ConfigType)) : ActivatorUtilities.CreateInstance(provider, typePair.Value.ConfigType); /* Explanation for bindings Internal Constructors --> NonPublic Public Constructors --> Public Constructors --> Instance */ - BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; var ctors = typePair.Value.ImplementationType.GetConstructors(flags); - object? instance; + var instance = ctors.Any(x => x.GetParameters().Length == 1 && x.GetParameters().First().ParameterType == typePair.Value.ConfigType) + ? Activator.CreateInstance(typePair.Value.ImplementationType, flags, null, + new[] { configInstance }, null) + : Activator.CreateInstance(typePair.Value.ImplementationType, true); /* Certain extensions do not require a configuration argument Those who do -- pass config instance in, Those who don't -- simply instantiate ActivatorUtilities requires a public constructor, anything with internal breaks */ - if (ctors.Any(x => x.GetParameters().Length == 1 && x.GetParameters().First().ParameterType == typePair.Value.ConfigType)) - instance = Activator.CreateInstance(typePair.Value.ImplementationType, flags, null, - new[] { configInstance }, null); - else - instance = Activator.CreateInstance(typePair.Value.ImplementationType, true); if (instance == null) { this._logger.LogError($"Unable to instantiate '{typePair.Value.ImplementationType.Name}'"); continue; } // Add an easy reference to our extensions for later use this.Client.AddExtension((BaseExtension)instance); } catch (Exception ex) { this._logger.LogError($"Unable to register '{typePair.Value.ImplementationType.Name}': \n\t{ex.Message}"); } } + /// + /// Executes the bot. + /// + /// The stopping token. + /// A Task. protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (this.Client == null) throw new NullReferenceException("Discord Client cannot be null"); await this.PreConnect(); await this.Client.ConnectAsync(); await this.PostConnect(); // Wait indefinitely -- but use stopping token so we can properly cancel if needed await Task.Delay(-1, stoppingToken); } /// /// Runs just prior to the bot connecting /// protected virtual Task PreConnect() => Task.CompletedTask; /// /// Runs immediately after the bot connects /// protected virtual Task PostConnect() => Task.CompletedTask; } } diff --git a/DisCatSharp.Hosting/AssemblyProperties.cs b/DisCatSharp.Hosting/Properties/AssemblyProperties.cs similarity index 100% rename from DisCatSharp.Hosting/AssemblyProperties.cs rename to DisCatSharp.Hosting/Properties/AssemblyProperties.cs diff --git a/DisCatSharp.code-workspace b/DisCatSharp.code-workspace new file mode 100644 index 000000000..57097327f --- /dev/null +++ b/DisCatSharp.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} diff --git a/DisCatSharp.sln b/DisCatSharp.sln index 2a0af2afc..e65ed3ffe 100644 --- a/DisCatSharp.sln +++ b/DisCatSharp.sln @@ -1,145 +1,149 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29613.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp", "DisCatSharp\DisCatSharp.csproj", "{EB3D8310-DFAD-4295-97F9-82E253647583}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext", "DisCatSharp.VoiceNext\DisCatSharp.VoiceNext.csproj", "{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4255B64D-92EC-46B3-BC3B-ED2C3A8073EE}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore BUILDING.md = BUILDING.md CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md LICENSE.md = LICENSE.md README.md = README.md SECURITY.md = SECURITY.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{C8ED55FB-E028-468D-955F-1534C20274EF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Interactivity", "DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{DD32BEC3-0189-479F-86DC-CCF95E5634A9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F953F5D0-F0C9-41E6-ADBF-60A76D295899}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.config = .nuget\NuGet.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{84464D70-687B-40A8-836D-C4F737698969}" ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml + .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml .github\dependabot.yml = .github\dependabot.yml DisCatSharp.targets = DisCatSharp.targets docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1 + .github\workflows\docs.yml = .github\workflows\docs.yml + .github\workflows\dotnet.yml = .github\workflows\dotnet.yml NuGet.targets = NuGet.targets oneclick-rebuild.ps1 = oneclick-rebuild.ps1 Package.targets = Package.targets rebuild-all.ps1 = rebuild-all.ps1 rebuild-docs.ps1 = rebuild-docs.ps1 rebuild-lib.ps1 = rebuild-lib.ps1 Version.targets = Version.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{430C28D8-5F85-4D6E-AA68-211549435245}" ProjectSection(SolutionItems) = preProject .github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md + .github\CODEOWNERS = .github\CODEOWNERS .github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md .github\FUNDING.yml = .github\FUNDING.yml .github\pull_request_template.md = .github\pull_request_template.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext.Natives", "DisCatSharp.VoiceNext.Natives\DisCatSharp.VoiceNext.Natives.csproj", "{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Common", "DisCatSharp.Common\DisCatSharp.Common.csproj", "{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.ApplicationCommands", "DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B15E40E0-03FD-4852-B19B-2C50BCC67704}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.Build.0 = Release|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.Build.0 = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.Build.0 = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.Build.0 = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.Build.0 = Release|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.Build.0 = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.Build.0 = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.Build.0 = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.Build.0 = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.ActiveCfg = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.Build.0 = Release|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.Build.0 = Debug|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.ActiveCfg = Release|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.Build.0 = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.Build.0 = Release|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.Build.0 = Debug|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.ActiveCfg = Release|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.Build.0 = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {430C28D8-5F85-4D6E-AA68-211549435245} = {4255B64D-92EC-46B3-BC3B-ED2C3A8073EE} - {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94} = {B15E40E0-03FD-4852-B19B-2C50BCC67704} {E15E88B4-63AD-42DE-B685-D31697C62194} = {B15E40E0-03FD-4852-B19B-2C50BCC67704} + {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94} = {B15E40E0-03FD-4852-B19B-2C50BCC67704} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7} EndGlobalSection EndGlobal diff --git a/DisCatSharp.targets b/DisCatSharp.targets index a8786c7be..49fb0c0cb 100644 --- a/DisCatSharp.targets +++ b/DisCatSharp.targets @@ -1,11 +1,11 @@ - Lala Sabathil, Lunar, Auros, Geferon, J_M_Lutra, mortAlice, AITSYS contributors - Aiko IT Systems - False - https://github.com/Aiko-IT-Systems/DisCatSharp - https://github.com/Aiko-IT-Systems/DisCatSharp - Git - + Lala Sabathil, Lunar, Auros, Geferon, J_M_Lutra, Alice, AITSYS contributors + Aiko IT Systems + False + https://github.com/Aiko-IT-Systems/DisCatSharp + https://github.com/Aiko-IT-Systems/DisCatSharp + Git + diff --git a/DisCatSharp/Properties/AssemblyProperties.cs b/DisCatSharp/Properties/AssemblyProperties.cs index 4261a925d..7ff085f0e 100644 --- a/DisCatSharp/Properties/AssemblyProperties.cs +++ b/DisCatSharp/Properties/AssemblyProperties.cs @@ -1,38 +1,40 @@ // This file is part of the DisCatSharp project. // // Copyright (c) 2021 AITSYS // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")] [assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")] [assembly: InternalsVisibleTo("DisCatSharp.Common")] [assembly: InternalsVisibleTo("DisCatSharp.Configuration")] [assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")] [assembly: InternalsVisibleTo("DisCatSharp.Hosting")] -[assembly: InternalsVisibleTo("DisCatSharp.Test")] +[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")] +[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")] [assembly: InternalsVisibleTo("DisCatSharp.Interactivity")] [assembly: InternalsVisibleTo("DisCatSharp.Lavalink")] [assembly: InternalsVisibleTo("DisCatSharp.Phabricator")] [assembly: InternalsVisibleTo("DisCatSharp.Support")] +[assembly: InternalsVisibleTo("DisCatSharp.Test")] [assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")] [assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")] [assembly: InternalsVisibleTo("Nyaw")]