diff --git a/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs new file mode 100644 index 000000000..2b6030919 --- /dev/null +++ b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using DisCatSharp.Common.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace DisCatSharp.Configuration.Tests +{ + public class ConfigurationExtensionTests + { + private IConfiguration BasicDiscordConfiguration() => new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary() + { + {"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"} + }) + .Build(); + + private IConfiguration DiscordIntentsConfig() => new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + {"DisCatSharp:Discord:Intents", "GuildEmojisAndStickers,GuildMembers,GuildInvites,GuildMessageReactions"} + }) + .Build(); + + private IConfiguration DiscordHaphazardConfig() => new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "DisCatSharp:Discord:Intents", "GuildEmojisAndStickers,GuildMembers,Guilds" }, + { "DisCatSharp:Discord:MobileStatus", "true" }, + { "DisCatSharp:Discord:LargeThreshold", "1000" }, + { "DisCatSharp:Discord:HttpTimeout", "10:00:00" } + }) + .Build(); + + [Fact] + public void TestExtractDiscordConfig_Intents() + { + var source = this.DiscordIntentsConfig(); + + DiscordConfiguration 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 expectedIntents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers | + DiscordIntents.Guilds; + + Assert.Equal(expectedIntents, config.Intents); + Assert.True(config.MobileStatus); + Assert.Equal(1000, config.LargeThreshold); + + var expectedTimeout = TimeSpan.FromHours(10); + Assert.Equal(expectedTimeout, config.HttpTimeout); + } + + [Fact] + public void TestExtractDiscordConfig_Default() + { + var source = this.BasicDiscordConfiguration(); + DiscordConfiguration 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); + + TimeSpan timeout = TimeSpan.FromSeconds(20); + Assert.Equal(timeout, 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); + } + + + } +} + diff --git a/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj b/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj new file mode 100644 index 000000000..f32bc269a --- /dev/null +++ b/DisCatSharp.Configuration.Tests/DisCatSharp.Configuration.Tests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + enable + + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/DisCatSharp.Configuration/AssemblyProperties.cs b/DisCatSharp.Configuration/AssemblyProperties.cs new file mode 100644 index 000000000..596402186 --- /dev/null +++ b/DisCatSharp.Configuration/AssemblyProperties.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")] diff --git a/DisCatSharp.Configuration/ConfigurationExtensions.cs b/DisCatSharp.Configuration/ConfigurationExtensions.cs new file mode 100644 index 000000000..4932c7af2 --- /dev/null +++ b/DisCatSharp.Configuration/ConfigurationExtensions.cs @@ -0,0 +1,102 @@ +// 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.Reflection; +using DisCatSharp.Common.Configuration.Models; +using Microsoft.Extensions.Configuration; + +namespace DisCatSharp.Common.Configuration +{ + internal static class ConfigurationExtensions + { + /// + /// 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); + + /// + /// 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 + /// 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) + where TConfig : new() + { + var section = new ConfigSection(ref config, sectionName); + + // Default values should hopefully be provided from the constructor + TConfig configInstance = new(); + + PropertyInfo[] props = typeof(TConfig).GetProperties(); + + foreach (var prop in props) + // If found in the config -- user/dev wants to override default value + if (section.ContainsKey(prop.Name, out string path)) + { + // Must have a set method for this to work, otherwise continue on + if (prop.SetMethod == null) + continue; + + string entry = section.GetValue(path); + + try + { + object? value = null; + + // Primitive types are simple to convert + if (prop.PropertyType.IsPrimitive) + value = Convert.ChangeType(entry, prop.PropertyType); + else if (prop.PropertyType == typeof(string)) + value = entry; + 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(configInstance, value); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Unable to convert value of '{entry}' to type '{prop.PropertyType.Name}' for prop '{prop.Name}' in config '{typeof(TConfig).Name}'\n\t\t{ex.Message}"); + } + } + + return configInstance; + } + } +} diff --git a/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj new file mode 100644 index 000000000..df969b4df --- /dev/null +++ b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj @@ -0,0 +1,13 @@ + + + + net5.0 + enable + DisCatSharp.Common.Configuration + + + + + + + diff --git a/DisCatSharp.Configuration/Models/ConfigSection.cs b/DisCatSharp.Configuration/Models/ConfigSection.cs new file mode 100644 index 000000000..51b8ab0ed --- /dev/null +++ b/DisCatSharp.Configuration/Models/ConfigSection.cs @@ -0,0 +1,73 @@ +// 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.Common.Configuration.Models +{ + /// + /// Represents an object in + /// + internal readonly struct ConfigSection + { + /// + /// Key within which represents an object containing multiple values + /// + public string SectionName { get;} + + /// + /// Reference to used within application + /// + public IConfiguration Config { get; } + + public ConfigSection(ref IConfiguration config, string sectionName) + { + this.Config = config; + this.SectionName = sectionName; + } + + /// + /// Checks if key exists in + /// + /// Property / Key to search for in section + /// Config path to key + /// (Optional) Root key to use. Default is DisCatSharp because configs in library should be consolidated + /// True if key exists, otherwise false. Outputs path to config regardless + public bool ContainsKey(string name, out string path, string root = "DisCatSharp") + { + path = string.IsNullOrEmpty(root) + ? this.Config.ConfigPath(this.SectionName, name) + : this.Config.ConfigPath(root, this.SectionName, name); + + return !string.IsNullOrEmpty(this.Config[path]); + } + + /// + /// Attempts to get value associated to the config path. + /// + /// Config path to value + /// Value found at + public string GetValue(string path) + => this.Config[path]; + + } +} diff --git a/DisCatSharp.sln b/DisCatSharp.sln index 26d6c715d..37f92c734 100644 --- a/DisCatSharp.sln +++ b/DisCatSharp.sln @@ -1,111 +1,123 @@  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\dependabot.yml = .github\dependabot.yml DisCatSharp.targets = DisCatSharp.targets docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1 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\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}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}" +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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {430C28D8-5F85-4D6E-AA68-211549435245} = {4255B64D-92EC-46B3-BC3B-ED2C3A8073EE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7} EndGlobalSection EndGlobal diff --git a/DisCatSharp/DiscordConfiguration.cs b/DisCatSharp/DiscordConfiguration.cs index c8c8616a1..3b8c48c2a 100644 --- a/DisCatSharp/DiscordConfiguration.cs +++ b/DisCatSharp/DiscordConfiguration.cs @@ -1,242 +1,242 @@ // 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; using System.Net; using DisCatSharp.Net.Udp; using DisCatSharp.Net.WebSocket; using Microsoft.Extensions.Logging; namespace DisCatSharp { /// /// Represents configuration for and . /// public sealed class DiscordConfiguration { /// /// Sets the token used to identify the client. /// public string Token { internal get => this._token; set { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value), "Token cannot be null, empty, or all whitespace."); this._token = value.Trim(); } } private string _token = ""; /// /// Sets the type of the token used to identify the client. /// Defaults to . /// public TokenType TokenType { internal get; set; } = TokenType.Bot; /// /// Sets the minimum logging level for messages. /// Typically, the default value of is ok for most uses. /// public LogLevel MinimumLogLevel { internal get; set; } = LogLevel.Information; /// /// Sets whether to rely on Discord for NTP (Network Time Protocol) synchronization with the "X-Ratelimit-Reset-After" header. /// If the system clock is unsynced, setting this to true will ensure ratelimits are synced with Discord and reduce the risk of hitting one. /// This should only be set to false if the system clock is synced with NTP. /// Defaults to true. /// public bool UseRelativeRatelimit { internal get; set; } = true; /// /// Allows you to overwrite the time format used by the internal debug logger. /// Only applicable when is set left at default value. Defaults to ISO 8601-like format. /// public string LogTimestampFormat { internal get; set; } = "yyyy-MM-dd HH:mm:ss zzz"; /// /// Sets the member count threshold at which guilds are considered large. /// Defaults to 250. /// public int LargeThreshold { internal get; set; } = 250; /// /// Sets whether to automatically reconnect in case a connection is lost. /// Defaults to true. /// public bool AutoReconnect { internal get; set; } = true; /// /// Sets the ID of the shard to connect to. /// If not sharding, or sharding automatically, this value should be left with the default value of 0. /// public int ShardId { internal get; set; } = 0; /// /// Sets the total number of shards the bot is on. If not sharding, this value should be left with a default value of 1. /// If sharding automatically, this value will indicate how many shards to boot. If left default for automatic sharding, the client will determine the shard count automatically. /// public int ShardCount { internal get; set; } = 1; /// /// Sets the level of compression for WebSocket traffic. /// Disabling this option will increase the amount of traffic sent via WebSocket. Setting will enable compression for READY and GUILD_CREATE payloads. Setting will enable compression for the entire WebSocket stream, drastically reducing amount of traffic. /// Defaults to . /// public GatewayCompressionLevel GatewayCompressionLevel { internal get; set; } = GatewayCompressionLevel.Stream; /// /// Sets the size of the global message cache. /// Setting this to 0 will disable message caching entirely. Defaults to 1024. /// public int MessageCacheSize { internal get; set; } = 1024; /// /// Sets the proxy to use for HTTP and WebSocket connections to Discord. /// Defaults to null. /// public IWebProxy Proxy { internal get; set; } = null; /// /// Sets the timeout for HTTP requests. /// Set to to disable timeouts. - /// Defaults to 10 seconds. + /// Defaults to 20 seconds. /// - public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(100); + public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(20); /// /// Defines that the client should attempt to reconnect indefinitely. /// This is typically a very bad idea to set to true, as it will swallow all connection errors. /// Defaults to false. /// public bool ReconnectIndefinitely { internal get; set; } = false; /// /// Sets whether the client should attempt to cache members if exclusively using unprivileged intents. /// /// This will only take effect if there are no or /// intents specified. Otherwise, this will always be overwritten to true. /// /// Defaults to true. /// public bool AlwaysCacheMembers { internal get; set; } = true; /// /// Sets the gateway intents for this client. /// If set, the client will only receive events that they specify with intents. /// Defaults to . /// public DiscordIntents Intents { internal get; set; } = DiscordIntents.AllUnprivileged; /// /// Sets the factory method used to create instances of WebSocket clients. /// Use and equivalents on other implementations to switch out client implementations. /// Defaults to . /// public WebSocketClientFactoryDelegate WebSocketClientFactory { internal get => this._webSocketClientFactory; set { if (value == null) throw new InvalidOperationException("You need to supply a valid WebSocket client factory method."); this._webSocketClientFactory = value; } } private WebSocketClientFactoryDelegate _webSocketClientFactory = WebSocketClient.CreateNew; /// /// Sets the factory method used to create instances of UDP clients. /// Use and equivalents on other implementations to switch out client implementations. /// Defaults to . /// public UdpClientFactoryDelegate UdpClientFactory { internal get => this._udpClientFactory; set => this._udpClientFactory = value ?? throw new InvalidOperationException("You need to supply a valid UDP client factory method."); } private UdpClientFactoryDelegate _udpClientFactory = DCSUdpClient.CreateNew; /// /// Sets the logger implementation to use. /// To create your own logger, implement the instance. /// Defaults to built-in implementation. /// public ILoggerFactory LoggerFactory { internal get; set; } = null; /// /// Sets if the bot's status should show the mobile icon. /// Defaults to false. /// public bool MobileStatus { internal get; set; } = false; /// /// Use canary. /// Defaults to false. /// public bool UseCanary { internal get; set; } = false; /// /// Refresh full guild channel cache. /// Defaults to false. /// public bool AutoRefreshChannelCache { internal get; set; } = false; /// /// Creates a new configuration with default values. /// public DiscordConfiguration() { } /// /// Creates a clone of another discord configuration. /// /// Client configuration to clone. public DiscordConfiguration(DiscordConfiguration other) { this.Token = other.Token; this.TokenType = other.TokenType; this.MinimumLogLevel = other.MinimumLogLevel; this.UseRelativeRatelimit = other.UseRelativeRatelimit; this.LogTimestampFormat = other.LogTimestampFormat; this.LargeThreshold = other.LargeThreshold; this.AutoReconnect = other.AutoReconnect; this.ShardId = other.ShardId; this.ShardCount = other.ShardCount; this.GatewayCompressionLevel = other.GatewayCompressionLevel; this.MessageCacheSize = other.MessageCacheSize; this.WebSocketClientFactory = other.WebSocketClientFactory; this.UdpClientFactory = other.UdpClientFactory; this.Proxy = other.Proxy; this.HttpTimeout = other.HttpTimeout; this.ReconnectIndefinitely = other.ReconnectIndefinitely; this.Intents = other.Intents; this.LoggerFactory = other.LoggerFactory; this.MobileStatus = other.MobileStatus; this.UseCanary = other.UseCanary; this.AutoRefreshChannelCache = other.AutoRefreshChannelCache; } } } diff --git a/DisCatSharp/Properties/AssemblyProperties.cs b/DisCatSharp/Properties/AssemblyProperties.cs index 5bdc09503..d9af161e0 100644 --- a/DisCatSharp/Properties/AssemblyProperties.cs +++ b/DisCatSharp/Properties/AssemblyProperties.cs @@ -1,32 +1,34 @@ // 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.Common")] [assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")] [assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")] [assembly: InternalsVisibleTo("DisCatSharp.Interactivity")] [assembly: InternalsVisibleTo("DisCatSharp.Lavalink")] [assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")] [assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")] [assembly: InternalsVisibleTo("Nyaw")] // Ignore pls, DisCatSharp Dev Debug +[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")] +