diff --git a/.editorconfig b/.editorconfig index e84e194..f6b6734 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,90 @@ # CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. dotnet_diagnostic.CS8618.severity = suggestion +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_indent_labels = one_less_than_current +csharp_space_around_binary_operators = before_and_after + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = lf +insert_final_newline = false diff --git a/.github/workflows/CreateReleaseApps.yml b/.github/workflows/CreateReleaseApps.yml index c6b2ec2..162568e 100644 --- a/.github/workflows/CreateReleaseApps.yml +++ b/.github/workflows/CreateReleaseApps.yml @@ -14,7 +14,7 @@ jobs: run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Get branch name id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v7.0.7 - name: Setup .NET uses: actions/setup-dotnet@v2 with: @@ -64,7 +64,7 @@ jobs: run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Get branch name id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v7.0.7 - name: Setup .NET uses: actions/setup-dotnet@v2 with: diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 12aa89c..811771a 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - name: Get branch name id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v7.0.7 - name: Login env: DOCKER_USER: ${{secrets.DOCKER_USER}} diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f02abae..721952d 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -15,7 +15,7 @@ jobs: run: echo "::set-output name=date::$(date +'%Y-%m-%d')" - name: Get branch name id: branch-name - uses: tj-actions/branch-names@v6 + uses: tj-actions/branch-names@v7.0.7 - name: Setup .NET uses: actions/setup-dotnet@v2 with: diff --git a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs index 1eb0484..5cb3d21 100644 --- a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs +++ b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.DependencyInjection; using EnvironmentHelper = DiscordPlayerCountBot.Tests.Environment.EnvironmentHelper; namespace DiscordPlayerCountBot.Tests; @@ -14,7 +15,10 @@ public async Task DockerConfigurationTestWithAllData() var bots = new Dictionary(); var time = -1; - var dockerConfiguration = new DockerConfiguration(); + var serviceProvider = new ServiceCollection() + .BuildServiceProvider(); + + var dockerConfiguration = new DockerConfiguration(serviceProvider); var configuration = await dockerConfiguration.Configure(false); bots = configuration.Item1; @@ -34,7 +38,10 @@ public async Task DockerConfigurationWithDuplicateAddresses() { EnvironmentHelper.SetTestEnvironmentWithDuplicateAddresses(); - var dockerConfiguration = new DockerConfiguration(); + var services = new ServiceCollection() + .BuildServiceProvider(); + + var dockerConfiguration = new DockerConfiguration(services); var configuration = await dockerConfiguration.Configure(false); EnvironmentHelper.ClearTestEnvironmentVariables(); @@ -56,7 +63,10 @@ public async Task DockerConfigurationTestWithoutBattleMetrics() var bots = new Dictionary(); var time = -1; - var dockerConfiguration = new DockerConfiguration(); + var services = new ServiceCollection() + .BuildServiceProvider(); + + var dockerConfiguration = new DockerConfiguration(services); var configuration = await dockerConfiguration.Configure(false); bots = configuration.Item1; @@ -79,7 +89,10 @@ public async Task DockerConfigurationTestWithoutApplicationVariables() var bots = new Dictionary(); var time = -1; - var dockerConfiguration = new DockerConfiguration(); + var services = new ServiceCollection() + .BuildServiceProvider(); + + var dockerConfiguration = new DockerConfiguration(services); var configuration = await dockerConfiguration.Configure(false); bots = configuration.Item1; @@ -100,7 +113,10 @@ public async Task DockerConfigurationTestWithoutSteam() var bots = new Dictionary(); var time = -1; - var dockerConfiguration = new DockerConfiguration(); + var services = new ServiceCollection() + .BuildServiceProvider(); + + var dockerConfiguration = new DockerConfiguration(services); var configuration = await dockerConfiguration.Configure(false); bots = configuration.Item1; diff --git a/DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs b/DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs index e754ccd..9a5cb1e 100644 --- a/DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs +++ b/DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs @@ -83,4 +83,4 @@ public static void ClearTestEnvironmentVariables() System.Environment.SetEnvironmentVariable("BOT_APPLICATION_VARIABLES", null); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot.Tests/JsonTests.cs b/DiscordPlayerCountBot.Tests/JsonTests.cs index 4b960a1..3197ad4 100644 --- a/DiscordPlayerCountBot.Tests/JsonTests.cs +++ b/DiscordPlayerCountBot.Tests/JsonTests.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Tests; +namespace PlayerCountBot.Tests; [Collection("Json Serialization Test Suite")] public class JsonTests diff --git a/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs b/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs index dad15c2..5af5eda 100644 --- a/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs +++ b/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Tests +namespace PlayerCountBot.Tests { [Collection("Sun & Moon Tag Test Suite")] public class SunMoonTagTests @@ -75,4 +75,4 @@ public void ShouldOutputCorrectValue(string time) Assert.Equal(sunMoonPhase, output); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot.Tests/Usings.cs b/DiscordPlayerCountBot.Tests/Usings.cs index faaf93f..c9c9fa5 100644 --- a/DiscordPlayerCountBot.Tests/Usings.cs +++ b/DiscordPlayerCountBot.Tests/Usings.cs @@ -2,5 +2,5 @@ global using PlayerCountBot; global using PlayerCountBot.Configuration; -global using DiscordPlayerCountBot.Extensions; +global using PlayerCountBot.Extensions; global using PlayerCountBot.Json; \ No newline at end of file diff --git a/DiscordPlayerCountBot/Attributes/AttributeHelper.cs b/DiscordPlayerCountBot/Attributes/AttributeHelper.cs index 8b64137..4f3b2d4 100644 --- a/DiscordPlayerCountBot/Attributes/AttributeHelper.cs +++ b/DiscordPlayerCountBot/Attributes/AttributeHelper.cs @@ -9,4 +9,4 @@ public static string GetNameFromAttribute(object obj) return label; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Attributes/NameAttribute.cs b/DiscordPlayerCountBot/Attributes/NameAttribute.cs index ff21f84..a1bf53a 100644 --- a/DiscordPlayerCountBot/Attributes/NameAttribute.cs +++ b/DiscordPlayerCountBot/Attributes/NameAttribute.cs @@ -1,6 +1,5 @@ namespace PlayerCountBot.Attributes { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] public class NameAttribute : Attribute { @@ -11,4 +10,4 @@ public NameAttribute(string name) Name = name; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Bot/Bot.cs b/DiscordPlayerCountBot/Bot/Bot.cs index 8a10036..145ed23 100644 --- a/DiscordPlayerCountBot/Bot/Bot.cs +++ b/DiscordPlayerCountBot/Bot/Bot.cs @@ -1,34 +1,32 @@ -namespace PlayerCountBot +using PlayerCountBot.Extensions; +using Microsoft.Extensions.DependencyInjection; + +namespace PlayerCountBot { [Name("Bot")] public class Bot : LoggableClass { - public DiscordSocketClient DiscordClient { get; set; } - public Dictionary DataProviders { get; set; } = new(); - public Dictionary ApplicationTokens { get; set; } = new(); + public readonly DiscordSocketClient DiscordClient; + public readonly BotInformation Information; + public readonly Dictionary DataProviders = new(); + public readonly Dictionary ApplicationTokens = new(); - public Bot(BotInformation info, Dictionary applicationTokens) : base(info) + public Bot(BotInformation info, Dictionary applicationTokens, IServiceProvider services) { if (info is null) throw new ArgumentNullException(nameof(info)); if (applicationTokens is null) throw new ArgumentException(nameof(applicationTokens)); ApplicationTokens = applicationTokens; + Information = info; DiscordClient = new DiscordSocketClient(new DiscordSocketConfig() { HandlerTimeout = null }); - InitDataProviders(); - } - - public void InitDataProviders() - { - DataProviders.Add((int)DataProvider.STEAM, new SteamProvider(Information!)); - DataProviders.Add((int)DataProvider.CFX, new CFXProvider(Information!)); - DataProviders.Add((int)DataProvider.MINECRAFT, new MinecraftProvider(Information!)); - DataProviders.Add((int)DataProvider.BATTLEMETRICS, new BattleMetricsProvider(Information!)); + DataProviders = services.GetServices() + .ToDictionary(value => value.GetRequiredProviderType()); } public async Task StartAsync(bool shouldStart) @@ -38,7 +36,7 @@ public async Task StartAsync(bool shouldStart) Information.Address = await AddressHelper.ResolveAddress(Information.Address); } - Info($"Loaded {Information.Name} at address and port: {Information.Address}, {Information.ProviderType}"); + Info($"Loaded {Information.Name} ({Information.Id}) at address and port: {Information.Address}, {(DataProvider)Information.ProviderType}"); await DiscordClient.LoginAndStartAsync(Information.Token, Information.Address, shouldStart); } @@ -49,20 +47,25 @@ public async Task StopAsync() public async Task UpdateAsync() { - var dataProviderType = EnumHelper.GetDataProvider(Information!.ProviderType); + var dataProviderInt = EnumHelper.GetDataProvider(Information!.ProviderType); - if (dataProviderType != Information.ProviderType) + if (dataProviderInt != Information.ProviderType) { - Warn($"Config for bot at address: {Information.Address} has an invalid provider type: {Information.ProviderType}"); + Warn($"Config for bot at address: {Information.Address} has an invalid provider type: {Information.ProviderType}", Information.Id.ToString()); } var activityInteger = EnumHelper.GetActivityType(Information.Status); if (Information.Status != activityInteger) { - Warn($"Config for bot at address: {Information.Address} has an invalid activity type: {Information.Status}"); + Warn($"Config for bot at address: {Information.Address} has an invalid activity type: {Information.Status}", Information.Id.ToString()); } + var dataProviderType = (DataProvider)dataProviderInt; + + if (!DataProviders.ContainsKey(dataProviderType)) + throw new Exception($"Missing Data Provider for Type: {dataProviderType}"); + var dataProvider = DataProviders[dataProviderType]; var serverInformation = await dataProvider.GetServerInformation(Information, ApplicationTokens); diff --git a/DiscordPlayerCountBot/Bot/BotConfig.cs b/DiscordPlayerCountBot/Bot/BotConfig.cs index 434c7d1..c690378 100644 --- a/DiscordPlayerCountBot/Bot/BotConfig.cs +++ b/DiscordPlayerCountBot/Bot/BotConfig.cs @@ -7,7 +7,6 @@ public class BotConfig public Dictionary ApplicationTokens { get; set; } = new(); public void CreateDefaults() { - ServerInformation.Add(new() { Name = "TestBot", @@ -22,4 +21,4 @@ public void CreateDefaults() ApplicationTokens.Add("BattleMetricsKey", "Here"); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Bot/BotInformation.cs b/DiscordPlayerCountBot/Bot/BotInformation.cs index 4b4b345..8a25514 100644 --- a/DiscordPlayerCountBot/Bot/BotInformation.cs +++ b/DiscordPlayerCountBot/Bot/BotInformation.cs @@ -14,6 +14,7 @@ public class BotInformation public string? StatusFormat { get; set; } public int? SunriseHour { get; set; } public int? SunsetHour { get; set; } + public string? RconServiceName { get; set; } public Tuple GetAddressAndPort() { @@ -32,4 +33,4 @@ public Tuple GetAddressAndPort() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs b/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs index 8077cf5..0f3ed50 100644 --- a/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs +++ b/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs @@ -2,6 +2,7 @@ { public interface IConfigurable { + public HostEnvironment GetRequiredEnvironment(); Task, int>> Configure(bool shouldStart = true); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs index 585350b..de0d592 100644 --- a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs @@ -4,7 +4,13 @@ [Name("Docker Configuration")] public class DockerConfiguration : LoggableClass, IConfigurable { - public DockerConfiguration() : base() { } + public IServiceProvider Services { get; set; } + + public DockerConfiguration(IServiceProvider services) + { + Services = services; + } + public async Task, int>> Configure(bool shouldStart = true) { var bots = new Dictionary(); @@ -75,7 +81,7 @@ public async Task, int>> Configure(bool shouldStar { if (e is FormatException || e is OverflowException) { - Error($"Could not parse Channel ID: {channelIDString}", e); + Error($"Could not parse Channel ID: {channelIDString}", null, e); } throw new Exception(e.Message, e); @@ -105,12 +111,17 @@ public async Task, int>> Configure(bool shouldStar ProviderType = EnumHelper.GetDataProvider(int.Parse(providerTypes?[i] ?? "0")) }; - var bot = new Bot(info, applicationTokens); + var bot = new Bot(info, applicationTokens, Services); await bot.StartAsync(shouldStart); bots.Add(bot.Information!.Id.ToString(), bot); } return new Tuple, int>(bots, int.Parse(Environment.GetEnvironmentVariable("BOT_UPDATE_TIME") ?? "30")); } + + public HostEnvironment GetRequiredEnvironment() + { + return HostEnvironment.DOCKER; + } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Configuration/EnvironmentHelper.cs b/DiscordPlayerCountBot/Configuration/EnvironmentHelper.cs index 9e1ad8b..8c4039c 100644 --- a/DiscordPlayerCountBot/Configuration/EnvironmentHelper.cs +++ b/DiscordPlayerCountBot/Configuration/EnvironmentHelper.cs @@ -23,4 +23,4 @@ public static Tuple> ValidateVariables() return new(hasAllRequiredVariables, listOfMissingVariableNames); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs index 4a2eca9..a34936e 100644 --- a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs @@ -3,7 +3,13 @@ [Name("Standard Configuration")] public class StandardConfiguration : LoggableClass, IConfigurable { - public StandardConfiguration() : base() { } + public IServiceProvider Services { get; set; } + + public StandardConfiguration(IServiceProvider services) + { + Services = services; + } + public async Task, int>> Configure(bool shouldStart = true) { var bots = new Dictionary(); @@ -11,25 +17,26 @@ public async Task, int>> Configure(bool shouldStar if (!File.Exists("./Config.json")) { - Warn("[Standard Configuration] - Creating new config file. Please configure the Config.json file, and restart the program."); + Warn("Creating new config file. Please configure the Config.json file, and restart the program."); config.CreateDefaults(); File.WriteAllText("./Config.json", JsonConvert.SerializeObject(config, Formatting.Indented)); Console.ReadLine(); + Environment.Exit(0); } if (File.Exists("./Config.json")) { - Info("[Standard Configuration] - Loading Config.json."); - string fileContents = await File.ReadAllTextAsync("./Config.json"); + Info("Loading Config.json."); + var fileContents = await File.ReadAllTextAsync("./Config.json"); config = JsonHelper.DeserializeObject(fileContents); - if (config == null) throw new ApplicationException("[Standard Configuration] - You have broken the syntax of your config file."); + if (config == null) throw new ApplicationException("You have broken the syntax of your config file."); - Debug($"[Standard Configuration] - Config.json loaded:\n{fileContents}"); + Debug($"Config.json loaded:\n{fileContents}"); foreach (var info in config.ServerInformation) { - var bot = new Bot(info, config.ApplicationTokens); + var bot = new Bot(info, config.ApplicationTokens, Services); await bot.StartAsync(shouldStart); bots.Add(bot.Information!.Id.ToString(), bot); } @@ -37,5 +44,10 @@ public async Task, int>> Configure(bool shouldStar return new Tuple, int>(bots, config.UpdateTime); } + + public HostEnvironment GetRequiredEnvironment() + { + return HostEnvironment.STANDARD; + } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/CFX/CFXPlayerInformation.cs b/DiscordPlayerCountBot/Data/CFX/CFXPlayerInformation.cs index f327168..3830682 100644 --- a/DiscordPlayerCountBot/Data/CFX/CFXPlayerInformation.cs +++ b/DiscordPlayerCountBot/Data/CFX/CFXPlayerInformation.cs @@ -17,4 +17,4 @@ public class CFXPlayerInformation [JsonProperty("ping")] public int Ping { get; set; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/CFX/CFXServer.cs b/DiscordPlayerCountBot/Data/CFX/CFXServer.cs index 20c10d7..6b83dbd 100644 --- a/DiscordPlayerCountBot/Data/CFX/CFXServer.cs +++ b/DiscordPlayerCountBot/Data/CFX/CFXServer.cs @@ -87,4 +87,4 @@ public class CFXVars } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftDebugInfo.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftDebugInfo.cs index d02e238..92d1121 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftDebugInfo.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftDebugInfo.cs @@ -34,4 +34,4 @@ public class MinecraftDebugInfo } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftErrorInfo.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftErrorInfo.cs index 6680f03..5e700fd 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftErrorInfo.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftErrorInfo.cs @@ -7,4 +7,4 @@ public class MinecraftErrorInfo } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftInfo.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftInfo.cs index 99a8783..62f9173 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftInfo.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftInfo.cs @@ -13,4 +13,4 @@ public class MinecraftInfo } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftMotd.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftMotd.cs index 420c90e..7530f29 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftMotd.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftMotd.cs @@ -13,4 +13,4 @@ public class MinecraftMotd } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftPlayerInfo.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftPlayerInfo.cs index 6ab79f2..9c0f494 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftPlayerInfo.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftPlayerInfo.cs @@ -10,4 +10,4 @@ public class MinecraftPlayerInfo } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Minecraft/MinecraftServer.cs b/DiscordPlayerCountBot/Data/Minecraft/MinecraftServer.cs index 9d6d2b0..f23adcc 100644 --- a/DiscordPlayerCountBot/Data/Minecraft/MinecraftServer.cs +++ b/DiscordPlayerCountBot/Data/Minecraft/MinecraftServer.cs @@ -50,4 +50,4 @@ public MinecraftViewModel GetViewModel() } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Scum/ScumProviderResponse.cs b/DiscordPlayerCountBot/Data/Scum/ScumProviderResponse.cs index 960961d..fecc646 100644 --- a/DiscordPlayerCountBot/Data/Scum/ScumProviderResponse.cs +++ b/DiscordPlayerCountBot/Data/Scum/ScumProviderResponse.cs @@ -12,4 +12,4 @@ public class ScumProviderResponse return Data.Where(data => data.QueryPort == port || data.Port == port).FirstOrDefault(); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Scum/ScumServerData.cs b/DiscordPlayerCountBot/Data/Scum/ScumServerData.cs index 7225493..4394954 100644 --- a/DiscordPlayerCountBot/Data/Scum/ScumServerData.cs +++ b/DiscordPlayerCountBot/Data/Scum/ScumServerData.cs @@ -14,4 +14,4 @@ public class ScumServerData public string? Time { get; set; } public bool Password { get; set; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Steam/SteamApiResponseData.cs b/DiscordPlayerCountBot/Data/Steam/SteamApiResponseData.cs index a5f7430..16e172c 100644 --- a/DiscordPlayerCountBot/Data/Steam/SteamApiResponseData.cs +++ b/DiscordPlayerCountBot/Data/Steam/SteamApiResponseData.cs @@ -42,4 +42,4 @@ public int GetQueueCount() return 0; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Steam/SteamServerListResponse.cs b/DiscordPlayerCountBot/Data/Steam/SteamServerListResponse.cs index b3aeee7..bb0eedb 100644 --- a/DiscordPlayerCountBot/Data/Steam/SteamServerListResponse.cs +++ b/DiscordPlayerCountBot/Data/Steam/SteamServerListResponse.cs @@ -14,4 +14,4 @@ public SteamServerListResponse() return response.GetAddressDataByPort(port); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Data/Steam/SteamServerListSubClass.cs b/DiscordPlayerCountBot/Data/Steam/SteamServerListSubClass.cs index a1be5fc..525ba63 100644 --- a/DiscordPlayerCountBot/Data/Steam/SteamServerListSubClass.cs +++ b/DiscordPlayerCountBot/Data/Steam/SteamServerListSubClass.cs @@ -22,4 +22,4 @@ public SteamServerListSubClass() return null; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj index 94d7fda..654375e 100644 --- a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj +++ b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj @@ -6,7 +6,6 @@ enable enable win-x64 - true @@ -14,15 +13,17 @@ + + + + + - - Always - \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enum/DataProvider.cs b/DiscordPlayerCountBot/Enums/DataProvider.cs similarity index 51% rename from DiscordPlayerCountBot/Enum/DataProvider.cs rename to DiscordPlayerCountBot/Enums/DataProvider.cs index 51fd4a9..2b82939 100644 --- a/DiscordPlayerCountBot/Enum/DataProvider.cs +++ b/DiscordPlayerCountBot/Enums/DataProvider.cs @@ -1,4 +1,4 @@ -namespace PlayerCountBot.Enum +namespace PlayerCountBot.Enums { public enum DataProvider { @@ -6,6 +6,8 @@ public enum DataProvider CFX, SCUM, MINECRAFT, - BATTLEMETRICS + BATTLEMETRICS, + RCONCLIENT, + STEAMQUERY } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enum/EnumHelper.cs b/DiscordPlayerCountBot/Enums/EnumHelper.cs similarity index 93% rename from DiscordPlayerCountBot/Enum/EnumHelper.cs rename to DiscordPlayerCountBot/Enums/EnumHelper.cs index fdc9946..fad23db 100644 --- a/DiscordPlayerCountBot/Enum/EnumHelper.cs +++ b/DiscordPlayerCountBot/Enums/EnumHelper.cs @@ -1,4 +1,4 @@ -namespace PlayerCountBot.Enum +namespace PlayerCountBot.Enums { public static class EnumHelper { @@ -15,4 +15,4 @@ public static int GetActivityType(int value) } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enum/HostEnviroment.cs b/DiscordPlayerCountBot/Enums/HostEnviroment.cs similarity index 69% rename from DiscordPlayerCountBot/Enum/HostEnviroment.cs rename to DiscordPlayerCountBot/Enums/HostEnviroment.cs index 1fa9e0f..9a983e4 100644 --- a/DiscordPlayerCountBot/Enum/HostEnviroment.cs +++ b/DiscordPlayerCountBot/Enums/HostEnviroment.cs @@ -1,8 +1,8 @@ -namespace PlayerCountBot.Enum +namespace PlayerCountBot.Enums { public enum HostEnvironment { STANDARD, DOCKER } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enums/RconServiceType.cs b/DiscordPlayerCountBot/Enums/RconServiceType.cs new file mode 100644 index 0000000..8b12c94 --- /dev/null +++ b/DiscordPlayerCountBot/Enums/RconServiceType.cs @@ -0,0 +1,9 @@ +namespace PlayerCountBot.Enums +{ + public enum RconServiceType + { + CSGO, + Minecraft, + Ark + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs new file mode 100644 index 0000000..3836164 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs @@ -0,0 +1,10 @@ +namespace PlayerCountBot.Exceptions +{ + internal class ConfigurationException : Exception + { + public ConfigurationException(string? message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/ParsingException.cs b/DiscordPlayerCountBot/Exceptions/ParsingException.cs new file mode 100644 index 0000000..2460d94 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/ParsingException.cs @@ -0,0 +1,10 @@ +namespace PlayerCountBot.Exceptions +{ + public class ParsingException : Exception + { + public ParsingException(string? message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs new file mode 100644 index 0000000..7af2704 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs @@ -0,0 +1,9 @@ +namespace PlayerCountBot.Exceptions +{ + internal class RconAuthenticationException : Exception + { + public RconAuthenticationException(string? message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/DiscordClient.cs b/DiscordPlayerCountBot/Extensions/DiscordClient.cs similarity index 97% rename from DiscordPlayerCountBot/DiscordClient.cs rename to DiscordPlayerCountBot/Extensions/DiscordClient.cs index a909922..501d471 100644 --- a/DiscordPlayerCountBot/DiscordClient.cs +++ b/DiscordPlayerCountBot/Extensions/DiscordClient.cs @@ -1,4 +1,4 @@ -namespace PlayerCountBot +namespace PlayerCountBot.Extensions { public static class DiscordClient { @@ -41,4 +41,4 @@ public static async Task SetChannelName(this IDiscordClient socket, ulong? chann } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Extensions/StringExtensions.cs b/DiscordPlayerCountBot/Extensions/StringExtensions.cs index fdd6370..cab05ce 100644 --- a/DiscordPlayerCountBot/Extensions/StringExtensions.cs +++ b/DiscordPlayerCountBot/Extensions/StringExtensions.cs @@ -1,11 +1,12 @@ -using Microsoft.VisualBasic; + +using Microsoft.VisualBasic; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace DiscordPlayerCountBot.Extensions +namespace PlayerCountBot.Extensions { public static class StringExtensions { @@ -31,4 +32,4 @@ public static bool TryGetSunMoonPhase(this string timeString, int? sunriseHour, return !string.IsNullOrEmpty(output); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/AddressHelper.cs b/DiscordPlayerCountBot/Http/AddressHelper.cs index f489726..7059cfe 100644 --- a/DiscordPlayerCountBot/Http/AddressHelper.cs +++ b/DiscordPlayerCountBot/Http/AddressHelper.cs @@ -22,4 +22,4 @@ public static async Task ResolveAddress(string address) return resolvedAddress + ":" + port; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/HttpExecuter.cs b/DiscordPlayerCountBot/Http/HttpExecuter.cs index 53305e2..3bb1db2 100644 --- a/DiscordPlayerCountBot/Http/HttpExecuter.cs +++ b/DiscordPlayerCountBot/Http/HttpExecuter.cs @@ -67,4 +67,4 @@ public void Dispose() return JsonHelper.DeserializeObject(jsonString); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/QueryParams/Base/BattleMetricsParameters.cs b/DiscordPlayerCountBot/Http/QueryParams/Base/BattleMetricsParameters.cs index 622455d..3abfe69 100644 --- a/DiscordPlayerCountBot/Http/QueryParams/Base/BattleMetricsParameters.cs +++ b/DiscordPlayerCountBot/Http/QueryParams/Base/BattleMetricsParameters.cs @@ -34,4 +34,4 @@ public override string CreateQueryParameterString() return filterString; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/QueryParams/Base/IQueryParameterBuilder.cs b/DiscordPlayerCountBot/Http/QueryParams/Base/IQueryParameterBuilder.cs index 441a36d..9a5737f 100644 --- a/DiscordPlayerCountBot/Http/QueryParams/Base/IQueryParameterBuilder.cs +++ b/DiscordPlayerCountBot/Http/QueryParams/Base/IQueryParameterBuilder.cs @@ -4,4 +4,4 @@ public interface IQueryParameterBuilder { public string CreateQueryParameterString(); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/QueryParams/Base/QueryParameterBuilder.cs b/DiscordPlayerCountBot/Http/QueryParams/Base/QueryParameterBuilder.cs index ea4f16f..80b280a 100644 --- a/DiscordPlayerCountBot/Http/QueryParams/Base/QueryParameterBuilder.cs +++ b/DiscordPlayerCountBot/Http/QueryParams/Base/QueryParameterBuilder.cs @@ -43,4 +43,4 @@ public virtual string CreateQueryParameterString() return queryString; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/QueryParams/ScumGetServerInformationQueryParams.cs b/DiscordPlayerCountBot/Http/QueryParams/ScumGetServerInformationQueryParams.cs index 09277d2..411799a 100644 --- a/DiscordPlayerCountBot/Http/QueryParams/ScumGetServerInformationQueryParams.cs +++ b/DiscordPlayerCountBot/Http/QueryParams/ScumGetServerInformationQueryParams.cs @@ -8,4 +8,4 @@ public class ScumGetServerInformationQueryParams : QueryParameterBuilder [Name("port")] public int? Port { get; set; } = null; } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Http/QueryParams/SteamGetServerListQueryParams.cs b/DiscordPlayerCountBot/Http/QueryParams/SteamGetServerListQueryParams.cs index 268dc0a..87973d4 100644 --- a/DiscordPlayerCountBot/Http/QueryParams/SteamGetServerListQueryParams.cs +++ b/DiscordPlayerCountBot/Http/QueryParams/SteamGetServerListQueryParams.cs @@ -8,4 +8,4 @@ public class SteamGetServerListQueryParams : QueryParameterBuilder [Name("filter")] public string Filter { get; set; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Json/JsonHelper.cs b/DiscordPlayerCountBot/Json/JsonHelper.cs index cfa160c..aa40e3b 100644 --- a/DiscordPlayerCountBot/Json/JsonHelper.cs +++ b/DiscordPlayerCountBot/Json/JsonHelper.cs @@ -12,4 +12,4 @@ public static class JsonHelper }); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Logging/LoggableClass.cs b/DiscordPlayerCountBot/Logging/LoggableClass.cs index 087a633..ae48aea 100644 --- a/DiscordPlayerCountBot/Logging/LoggableClass.cs +++ b/DiscordPlayerCountBot/Logging/LoggableClass.cs @@ -1,49 +1,41 @@ -namespace PlayerCountBot +using Serilog; + +namespace PlayerCountBot { public class LoggableClass { - protected readonly ILog Logger; - public readonly BotInformation? Information; - - public LoggableClass() + public void Info(string message, string? id = null) { - Logger = LogManager.GetLogger(GetType()); + Log.Information(BuildLogMessage(id, GetType().Name, message)); } - public LoggableClass(BotInformation information) : this() + public void Warn(string message, string? id = null) { - Information = information; + Log.Warning(BuildLogMessage(id, GetType().Name, message)); } - public void Info(string message) + public void Error(string message, string? id = null, Exception? exception = null) { - Logger.Info($"{GetLoggingPrefix()} {message}"); + Log.Error(BuildLogMessage(id, GetType().Name, message), exception); } - public void Warn(string message) + public void Debug(string message, string? id = null) { - Logger.Warn($"{GetLoggingPrefix()} {message}"); + Log.Debug(BuildLogMessage(id, GetType().Name, message)); } - public void Error(string message, Exception? exception = null) + public string BuildLogMessage(string? id, string label, string msg) { - Logger.Error($"{GetLoggingPrefix()} {message}", exception); - } + var message = ""; - public void Debug(string message) - { - Logger.Debug($"{GetLoggingPrefix()} {message}"); - } - public string GetLoggingPrefix() - { - var label = AttributeHelper.GetNameFromAttribute(this) ?? GetType().Name; + message += $"[{label}]"; - if (Information != null) + if (!string.IsNullOrEmpty(id)) { - return $"[{label}] - {Information.Id} -"; + message += $" - {id}"; } - return $"[{label}] -"; + return message + $" - {msg}"; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Program.cs b/DiscordPlayerCountBot/Program.cs index 92fa7e0..4efe036 100644 --- a/DiscordPlayerCountBot/Program.cs +++ b/DiscordPlayerCountBot/Program.cs @@ -2,7 +2,7 @@ global using PlayerCountBot.Attributes; global using PlayerCountBot.Configuration.Base; global using PlayerCountBot.Data; -global using PlayerCountBot.Enum; +global using PlayerCountBot.Enums; global using PlayerCountBot.Http; global using PlayerCountBot.Json; global using PlayerCountBot.Services; @@ -10,28 +10,51 @@ global using PlayerCountBot.Providers; global using PlayerCountBot.ViewModels; global using Newtonsoft.Json; -global using System; -global using System.Collections.Generic; -global using System.Linq; -global using System.Threading.Tasks; -global using System.IO; global using System.Text; global using Discord; global using Discord.WebSocket; -global using log4net; -using log4net.Config; -using System.Reflection; +using Serilog; +using Serilog.Sinks.SystemConsole.Themes; +using Microsoft.Extensions.DependencyInjection; +using PlayerCountBot.Configuration; +using PlayerCountBot.Services; +using PlayerCountBot.Services.Rcon.ServiceInformation; +using PlayerCountBot.Services.SteamQuery; -var repository = LogManager.GetRepository(Assembly.GetCallingAssembly()); -var fileInfo = new FileInfo(@"log4net.config"); -XmlConfigurator.Configure(repository, fileInfo); +Log.Logger = new LoggerConfiguration() + .WriteTo.Console(theme: AnsiConsoleTheme.Literate, outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}", restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug, applyThemeToRedirectedOutput: true) + .WriteTo.File("logs.txt", Serilog.Events.LogEventLevel.Warning) + .CreateLogger(); -var tempLogger = LogManager.GetLogger(typeof(Program)); -tempLogger.Info("[Application]:: Starting Controller."); +Log.Information("[Application] - Starting Player Count Discord Bot."); -var controller = new UpdateController(); -AppDomain.CurrentDomain.ProcessExit += new EventHandler(controller.OnProcessExit!); +var serviceCollection = new ServiceCollection() + .AddSingleton(); -controller.MainAsync().GetAwaiter().GetResult(); \ No newline at end of file +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); + +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); + +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); + +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); +serviceCollection.AddTransient(); + +var app = serviceCollection.BuildServiceProvider(); + +var controller = app.GetRequiredService(); +await controller.MainAsync(); \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs index 9888177..5fcd5a8 100644 --- a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs @@ -2,8 +2,10 @@ { public interface IServerInformationProvider { + bool WasLastExecutionAFailure { get; set; } Exception? LastException { get; set; } Task GetServerInformation(BotInformation information, Dictionary applicationVariables); + DataProvider GetRequiredProviderType(); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs index 145bc97..95362f7 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Net.Http; namespace PlayerCountBot.Providers.Base { @@ -8,23 +7,19 @@ public abstract class ServerInformationProvider : LoggableClass, IServerInformat public bool WasLastExecutionAFailure { get; set; } = false; public Exception? LastException { get; set; } - public ServerInformationProvider(BotInformation info) : base(info) - { - } - public abstract Task GetServerInformation(BotInformation information, Dictionary applicationVariables); + protected void HandleLastException(BotInformation information) { if (WasLastExecutionAFailure) { - Info($"Bot named: {information.Name} at address: {information.Address} successfully fetched data after failure."); LastException = null; WasLastExecutionAFailure = false; } } - protected void HandleException(Exception e) + protected void HandleException(Exception e, string? id = null) { if (e.Message == LastException?.Message) return; @@ -35,19 +30,19 @@ protected void HandleException(Exception e) if (e is TaskCanceledException canceledException) { - Error($"Update task was canceled likely because of system timeout."); + Error($"Update task was canceled likely because of system timeout.", id); return; } if (e is KeyNotFoundException keyNotFoundException) { - Error($"An application variable is missing from configuration."); + Error($"An application variable is missing from configuration.", id); return; } if (e is HttpRequestException requestException) { - Error($"The {Label} has failed to respond. {requestException.StatusCode}"); + Error($"The {Label} has failed to respond. {requestException.StatusCode}", id); return; } @@ -55,29 +50,31 @@ protected void HandleException(Exception e) { if (webException.Status == WebExceptionStatus.Timeout) { - Error($"Speaking with {Label} has timed out."); + Error($"Speaking with {Label} has timed out.", id); return; } else if (webException.Status == WebExceptionStatus.ConnectFailure) { - Error($"Could not connect to {Label}."); + Error($"Could not connect to {Label}.", id); return; } else { - Error($"There was an error speaking with your {Label} server.", e); + Error($"There was an error speaking with your {Label} server.", id, e); return; } } if (e is ApplicationException applicationException) { - Error($"{applicationException.Message}"); + Error($"{applicationException.Message}", id); return; } - Error($"There was an error speaking with {Label}.", e); + Error($"There was an error speaking with {Label}.", id, e); throw e; } + + public abstract DataProvider GetRequiredProviderType(); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs index 9a77261..1c232ce 100644 --- a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs +++ b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs @@ -1,22 +1,28 @@ -using DiscordPlayerCountBot.Extensions; +using PlayerCountBot.Extensions; namespace PlayerCountBot.Providers { [Name("BattleMetrics")] public class BattleMetricsProvider : ServerInformationProvider { - public BattleMetricsProvider(BotInformation info) : base(info) + private readonly BattleMetricsService Service; + + public BattleMetricsProvider(BattleMetricsService service) { + Service = service; } - public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + public override DataProvider GetRequiredProviderType() { - var service = new BattleMetricsService(); + return DataProvider.BATTLEMETRICS; + } + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { try { var addressAndPort = information.GetAddressAndPort(); - var server = await service.GetPlayerInformationAsync(addressAndPort.Item1, applicationVariables["BattleMetricsKey"]); + var server = await Service.GetPlayerInformationAsync(addressAndPort.Item1, applicationVariables["BattleMetricsKey"]); if (server == null) throw new ApplicationException("Server cannot be null. Is your server offline?"); @@ -34,9 +40,9 @@ public BattleMetricsProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/CFXProvider.cs b/DiscordPlayerCountBot/Providers/CFXProvider.cs index 5dd439f..7b5170e 100644 --- a/DiscordPlayerCountBot/Providers/CFXProvider.cs +++ b/DiscordPlayerCountBot/Providers/CFXProvider.cs @@ -3,18 +3,24 @@ [Name("CFX")] public class CFXProvider : ServerInformationProvider { - public CFXProvider(BotInformation info) : base(info) + public readonly CFXService Service; + + public CFXProvider(CFXService service) { + Service = service; } - public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + public override DataProvider GetRequiredProviderType() { - var service = new CFXService(); + return DataProvider.CFX; + } + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { try { - var playerInfo = await service.GetPlayerInformationAsync(information.Address); - var serverInfo = await service.GetServerInformationAsync(information.Address); + var playerInfo = await Service.GetPlayerInformationAsync(information.Address); + var serverInfo = await Service.GetServerInformationAsync(information.Address); var addressAndPort = information.GetAddressAndPort(); if (playerInfo == null) @@ -36,9 +42,9 @@ public CFXProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/MinecraftProvider.cs b/DiscordPlayerCountBot/Providers/MinecraftProvider.cs index a1f1752..973b882 100644 --- a/DiscordPlayerCountBot/Providers/MinecraftProvider.cs +++ b/DiscordPlayerCountBot/Providers/MinecraftProvider.cs @@ -3,8 +3,9 @@ [Name("Minecraft")] public class MinecraftProvider : ServerInformationProvider { - public MinecraftProvider(BotInformation info) : base(info) + public override DataProvider GetRequiredProviderType() { + return DataProvider.MINECRAFT; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) @@ -35,9 +36,9 @@ public MinecraftProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs new file mode 100644 index 0000000..c7e41ad --- /dev/null +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -0,0 +1,55 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Exceptions; +using PlayerCountBot.Services; + +namespace PlayerCountBot.Providers +{ + [Name("Rcon")] + public class RconProvider : ServerInformationProvider + { + private readonly RconService Service; + + public RconProvider(RconService service) + { + Service = service; + } + + public override DataProvider GetRequiredProviderType() + { + return DataProvider.RCONCLIENT; + } + + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { + var values = $"Valid Values: {string.Join(",", Enum.GetNames())}"; + + if (information.RconServiceName == null) + { + throw new ConfigurationException($"Bot: {information.Name} must have RconServiceName specified in it's config. {values}"); + } + + if (!Enum.TryParse(information.RconServiceName, true, out var serviceType)) + { + throw new ConfigurationException($"Bot: {information.Name} has an invalid RconServiceName specified in it's config. {values}"); + } + + try + { + var addressAndPort = information.GetAddressAndPort(); + var response = await Service.GetRconResponse(addressAndPort.Item1, addressAndPort.Item2, applicationVariables["RconPassword"], serviceType); + + if (response == null) + throw new ApplicationException($"Server Address: {information.Address} was not found in Steam's directory."); + + HandleLastException(information); + + return response; + } + catch (Exception e) + { + HandleException(e); + return null; + } + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/ScumProvider.cs b/DiscordPlayerCountBot/Providers/ScumProvider.cs index 5dc7b36..bb6c8b7 100644 --- a/DiscordPlayerCountBot/Providers/ScumProvider.cs +++ b/DiscordPlayerCountBot/Providers/ScumProvider.cs @@ -4,8 +4,9 @@ [Name("Scum")] public class ScumProvider : ServerInformationProvider { - public ScumProvider(BotInformation info) : base(info) + public override DataProvider GetRequiredProviderType() { + return DataProvider.SCUM; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) @@ -40,9 +41,9 @@ public ScumProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index 8a4ce94..a122c7e 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -1,22 +1,28 @@ -using DiscordPlayerCountBot.Extensions; +using PlayerCountBot.Extensions; namespace PlayerCountBot.Providers { [Name("Steam")] public class SteamProvider : ServerInformationProvider { - public SteamProvider(BotInformation info) : base(info) + private readonly SteamService Service; + + public SteamProvider(SteamService service) { + Service = service; } - public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + public override DataProvider GetRequiredProviderType() { - var service = new SteamService(); + return DataProvider.STEAM; + } + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { try { var addressAndPort = information.GetAddressAndPort(); - var response = await service.GetSteamApiResponse(addressAndPort.Item1, addressAndPort.Item2, applicationVariables["SteamAPIKey"]); + var response = await Service.GetSteamApiResponse(addressAndPort.Item1, addressAndPort.Item2, applicationVariables["SteamAPIKey"]); if (response == null) throw new ApplicationException($"Server Address: {information.Address} was not found in Steam's directory."); @@ -34,27 +40,30 @@ public SteamProvider(BotInformation info) : base(info) Map = response.map }; - var serverTime = model.Gametype.Split(",") - .Where(entry => entry.Contains(':') && entry.Length == 5) - .FirstOrDefault(); + if (!model.Gametype?.Contains(',') ?? false) + return model; + + var serverTime = model.Gametype?.Split(",") + .Where(entry => entry.Contains(':') && entry.Length == 5) + .FirstOrDefault(); if (!string.IsNullOrEmpty(serverTime)) { + model.Time = serverTime; + if (model.Time.TryGetSunMoonPhase(information.SunriseHour, information.SunsetHour, out var sunMoon)) { model.SunMoon = sunMoon; } - - model.Time = serverTime; } return model; } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs new file mode 100644 index 0000000..73a01d2 --- /dev/null +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -0,0 +1,43 @@ +using PlayerCountBot.Services.SteamQuery; + +namespace PlayerCountBot.Providers +{ + [Name("Steam Query")] + public class SteamQueryProvider : ServerInformationProvider + { + private readonly SteamQueryService Service; + + public SteamQueryProvider(SteamQueryService service) + { + Service = service; + } + + public override DataProvider GetRequiredProviderType() + { + return DataProvider.STEAMQUERY; + } + + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { + try + { + var addressAndPort = information.GetAddressAndPort(); + var response = await Service.GetQueryResponse(addressAndPort.Item1, addressAndPort.Item2); + + if (response == null) + { + throw new ApplicationException($" Failed to get a Server Information response from Steam Query."); + } + + HandleLastException(information); + + return response; + } + catch (Exception e) + { + HandleException(e); + return null; + } + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/BattleMetricsService.cs b/DiscordPlayerCountBot/Services/BattleMetricsService.cs index 0d54ee9..e046c59 100644 --- a/DiscordPlayerCountBot/Services/BattleMetricsService.cs +++ b/DiscordPlayerCountBot/Services/BattleMetricsService.cs @@ -10,4 +10,4 @@ public class BattleMetricsService : IBattleMetricsService return response?.data; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/CFXService.cs b/DiscordPlayerCountBot/Services/CFXService.cs index 6d00b04..11e44b4 100644 --- a/DiscordPlayerCountBot/Services/CFXService.cs +++ b/DiscordPlayerCountBot/Services/CFXService.cs @@ -14,4 +14,4 @@ public class CFXService : ICFXService return await httpClient.GET($"http://{address}/Info.json"); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/IBattleMetricsService.cs b/DiscordPlayerCountBot/Services/IBattleMetricsService.cs index 508b7ab..0d4c767 100644 --- a/DiscordPlayerCountBot/Services/IBattleMetricsService.cs +++ b/DiscordPlayerCountBot/Services/IBattleMetricsService.cs @@ -4,4 +4,4 @@ public interface IBattleMetricsService { public Task GetPlayerInformationAsync(string address, string token); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/ICFXService.cs b/DiscordPlayerCountBot/Services/ICFXService.cs index a9b71f1..ffc8dea 100644 --- a/DiscordPlayerCountBot/Services/ICFXService.cs +++ b/DiscordPlayerCountBot/Services/ICFXService.cs @@ -5,4 +5,4 @@ public interface ICFXService public Task GetServerInformationAsync(string address); public Task?> GetPlayerInformationAsync(string address); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/IMinecraftService.cs b/DiscordPlayerCountBot/Services/IMinecraftService.cs index fa35765..1f5c09f 100644 --- a/DiscordPlayerCountBot/Services/IMinecraftService.cs +++ b/DiscordPlayerCountBot/Services/IMinecraftService.cs @@ -4,4 +4,4 @@ public interface IMinecraftService { public Task GetMinecraftServerInformationAsync(string address, int port); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/IScumService.cs b/DiscordPlayerCountBot/Services/IScumService.cs index a907758..d731d7f 100644 --- a/DiscordPlayerCountBot/Services/IScumService.cs +++ b/DiscordPlayerCountBot/Services/IScumService.cs @@ -4,4 +4,4 @@ public interface IScumService { public Task GetPlayerInformationAsync(string address, int port); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/ISteamService.cs b/DiscordPlayerCountBot/Services/ISteamService.cs index d70ae25..aa03f0e 100644 --- a/DiscordPlayerCountBot/Services/ISteamService.cs +++ b/DiscordPlayerCountBot/Services/ISteamService.cs @@ -4,4 +4,4 @@ public interface ISteamService { public Task GetSteamApiResponse(string address, int port, string token); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/MinecraftService.cs b/DiscordPlayerCountBot/Services/MinecraftService.cs index 7f66d98..e22684a 100644 --- a/DiscordPlayerCountBot/Services/MinecraftService.cs +++ b/DiscordPlayerCountBot/Services/MinecraftService.cs @@ -8,4 +8,4 @@ public class MinecraftService : IMinecraftService return await httpClient.GET($"https://api.mcsrvstat.us/2/{address}:{port}"); } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/IRconService.cs b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs new file mode 100644 index 0000000..a39995f --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs @@ -0,0 +1,9 @@ +using PlayerCountBot.Enums; + +namespace PlayerCountBot.Services +{ + public interface IRconService + { + public Task GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType); + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs new file mode 100644 index 0000000..150461d --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs @@ -0,0 +1,29 @@ +using PlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace PlayerCountBot.Services.Praser +{ + public class ArkInformationParser : IRconInformationParser + { + public BaseViewModel Parse(string message) + { + var match = Regex.Match(message, @"There are (\d+)/(\d+) players online(?:.*\nQueue:\s+(\d+)\s+players waiting)?"); + + if (!match.Success) + { + throw new ParsingException("Could not find players, max players, or queue information from an ARK RCON Response"); + } + + var players = int.Parse(match.Groups[1].Value); + var maxPlayers = int.Parse(match.Groups[2].Value); + int queuedPlayers = match.Groups.Count < 3 || string.IsNullOrEmpty(match.Groups[3].Value) ? 0 : int.Parse(match.Groups[3].Value); + + return new BaseViewModel() + { + Players = players, + MaxPlayers = maxPlayers, + QueuedPlayers = queuedPlayers + }; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs new file mode 100644 index 0000000..5a2d868 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs @@ -0,0 +1,31 @@ +using PlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace PlayerCountBot.Services.Praser +{ + public class CSGOInformationParser : IRconInformationParser + { + public BaseViewModel Parse(string message) + { + var playersMatch = Regex.Match(message, @"players\s+:\s+(\d+)\s+humans"); + var maxPlayersMatch = Regex.Match(message, @"\((\d+)/\d+\s+max\)"); + var queuedPlayersMatch = Regex.Match(message, @"queue\s+:\s+(\d+)\s+players waiting"); + + if (!playersMatch.Success || !maxPlayersMatch.Success) + { + throw new ParsingException("Could not find players or max players from a CSGO Rcon Response"); + } + + var players = int.Parse(playersMatch.Groups[0].Value); + var maxPlayers = int.Parse(maxPlayersMatch.Groups[0].Value); + int queuedPlayers = !queuedPlayersMatch.Success ? 0 : int.Parse(queuedPlayersMatch.Groups[0].Value); + + return new BaseViewModel() + { + Players = players, + MaxPlayers = maxPlayers, + QueuedPlayers = queuedPlayers + }; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs new file mode 100644 index 0000000..6d4fb2d --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs @@ -0,0 +1,7 @@ +namespace PlayerCountBot.Services.Praser +{ + public interface IRconInformationParser + { + public BaseViewModel Parse(string message); + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs new file mode 100644 index 0000000..e112ce3 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs @@ -0,0 +1,30 @@ +using PlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace PlayerCountBot.Services.Praser +{ + public class MinecraftInformationParser : IRconInformationParser + { + public BaseViewModel Parse(string message) + { + var playerInfoMatch = Regex.Match(message, @"(\d+)\s+of a max of\s+(\d+)"); + var queuedPlayersMatch = Regex.Match(message, @"Queue:\s+(\d+)\s+players waiting"); + + if (!playerInfoMatch.Success || playerInfoMatch.Groups.Count < 3) + { + throw new ParsingException("Could not find players or max players from a Minecraft Rcon Response"); + } + + var players = int.Parse(playerInfoMatch.Groups[1].Value); + var maxPlayers = int.Parse(playerInfoMatch.Groups[2].Value); + int queuedPlayers = !queuedPlayersMatch.Success ? 0 : int.Parse(queuedPlayersMatch.Groups[1].Value); + + return new BaseViewModel() + { + Players = players, + MaxPlayers = maxPlayers, + QueuedPlayers = queuedPlayers + }; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs new file mode 100644 index 0000000..09ae98d --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -0,0 +1,46 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Exceptions; +using Microsoft.Extensions.DependencyInjection; +using RconSharp; + +namespace PlayerCountBot.Services +{ + public class RconService : IRconService + { + private readonly Dictionary PlayerCommands = new(); + + public RconService(IServiceProvider serviceProvider) + { + PlayerCommands = serviceProvider.GetServices() + .ToDictionary(value => value.GetServiceType()); + } + + public async Task GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType) + { + if (!PlayerCommands.TryGetValue(serviceType, out var serviceInformation)) + { + throw new ArgumentException("Unsupported Rcon Service Type.... How did you get here?"); + } + + var client = RconClient.Create(address, port); + + await client.ConnectAsync(); + + var authenticated = await client.AuthenticateAsync(authorizationToken); + + if (!authenticated) + { + throw new RconAuthenticationException($"There was a failure to authenticate with server: {address}:{port}.\nIs your password correct?\nIs this the correct server address?"); + } + + var command = serviceInformation.GetPlayerListCommand(); + var response = await client.ExecuteCommandAsync(command); + var viewModel = serviceInformation.GetParser().Parse(response); + + viewModel.Address = address; + viewModel.Port = port; + + return viewModel; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs new file mode 100644 index 0000000..79763fd --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs @@ -0,0 +1,12 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; + +namespace PlayerCountBot.Services +{ + public interface IRconServiceInformation + { + public abstract RconServiceType GetServiceType(); + public abstract string GetPlayerListCommand(); + public abstract IRconInformationParser GetParser(); + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs new file mode 100644 index 0000000..ebc3df0 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs @@ -0,0 +1,23 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; + +namespace PlayerCountBot.Services.Rcon.ServiceInformation +{ + public class ArkRconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new ArkInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.Ark; + } + + public string GetPlayerListCommand() + { + return "listplayers"; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs new file mode 100644 index 0000000..5d7ba19 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs @@ -0,0 +1,23 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; + +namespace PlayerCountBot.Services.Rcon.ServiceInformation +{ + public class CSGORconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new CSGOInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.CSGO; + } + + public string GetPlayerListCommand() + { + return "status"; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs new file mode 100644 index 0000000..722aed9 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs @@ -0,0 +1,23 @@ +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; + +namespace PlayerCountBot.Services.Rcon.ServiceInformation +{ + public class MinecraftRconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new MinecraftInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.Minecraft; + } + + public string GetPlayerListCommand() + { + return "list"; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/ScumService.cs b/DiscordPlayerCountBot/Services/ScumService.cs index 7aa9dde..3df83f2 100644 --- a/DiscordPlayerCountBot/Services/ScumService.cs +++ b/DiscordPlayerCountBot/Services/ScumService.cs @@ -14,4 +14,4 @@ public class ScumService : IScumService return response; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs new file mode 100644 index 0000000..7b9abd7 --- /dev/null +++ b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs @@ -0,0 +1,7 @@ +namespace PlayerCountBot.Services.SteamQuery +{ + internal interface ISteamQueryService + { + public Task GetQueryResponse(string address, int port); + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs new file mode 100644 index 0000000..add7916 --- /dev/null +++ b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs @@ -0,0 +1,29 @@ +using SteamQueryNet; + +namespace PlayerCountBot.Services.SteamQuery +{ + public class SteamQueryService : ISteamQueryService + { + public async Task GetQueryResponse(string address, int port) + { + var model = new BaseViewModel() + { + Address = address, + Port = port + }; + + var serverQuery = await new ServerQuery() + .Connect($"{address}:{port}") + .GetServerInfoAsync(); + + if (serverQuery == null) + throw new Exception("Failed to fetch server information from Steam Query"); + + model.MaxPlayers = serverQuery.MaxPlayers; + model.Players = serverQuery.Players; + model.QueuedPlayers = 0; + + return model; + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/SteamService.cs b/DiscordPlayerCountBot/Services/SteamService.cs index 4f50525..b2b57fa 100644 --- a/DiscordPlayerCountBot/Services/SteamService.cs +++ b/DiscordPlayerCountBot/Services/SteamService.cs @@ -21,4 +21,4 @@ public class SteamService : LoggableClass, ISteamService return data; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/UpdateController.cs b/DiscordPlayerCountBot/UpdateController.cs index 4c1e991..c1862f2 100644 --- a/DiscordPlayerCountBot/UpdateController.cs +++ b/DiscordPlayerCountBot/UpdateController.cs @@ -1,4 +1,4 @@ -using PlayerCountBot.Configuration; +using Microsoft.Extensions.DependencyInjection; using System.Net.WebSockets; using System.Security; using System.Timers; @@ -16,9 +16,12 @@ public class UpdateController : LoggableClass private int Time = 30; private Timer? Timer; - public UpdateController() : base() + public UpdateController(IServiceProvider services) { - InitHostingConfigurations(); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); + + HostingEnvironments = services.GetServices() + .ToDictionary(value => value.GetRequiredEnvironment()); try { @@ -29,24 +32,18 @@ public UpdateController() : base() { if (e is ArgumentException || e is FormatException) { - Error("Error while parsing ISDOCKER variable. Please check your docker file, and fix your changes.", e); + Error("Error while parsing ISDOCKER variable. Please check your docker file, and fix your changes.", null, e); } if (e is SecurityException) { - Error("A security error has happened when trying to fet ISDOCKER variable.", e); + Error("A security error has happened when trying to fet ISDOCKER variable.", null, e); } HostEnvType = HostEnvironment.STANDARD; } } - public void InitHostingConfigurations() - { - HostingEnvironments.Add(HostEnvironment.STANDARD, new StandardConfiguration()); - HostingEnvironments.Add(HostEnvironment.DOCKER, new DockerConfiguration()); - } - public async Task LoadConfig() { var configurationType = HostingEnvironments[HostEnvType]; @@ -54,6 +51,7 @@ public async Task LoadConfig() Bots = config.Item1; Time = config.Item2; + Info($"Created: {Bots.Count} bot(s) that update every {Time} seconds."); } @@ -61,43 +59,39 @@ public async Task UpdatePlayerCounts() { foreach (var bot in Bots.Values) { - await bot.UpdateAsync(); - } - } - - private async void OnTimerExecute(object? source, ElapsedEventArgs e) - { - try - { - await UpdatePlayerCounts(); - } - catch (Exception ex) - { - if (ex is OperationCanceledException canceledException) + try { - Warn($"Discord host connection was closed. Resetting connection."); - return; + await bot.UpdateAsync(); } - - if (ex is WebSocketException socketException) + catch (Exception ex) { - Warn($"Web socket was found to be in a invalid state."); - return; + if (ex is OperationCanceledException canceledException) + { + Warn($"Discord host connection was closed. Resetting connection.", bot.Information.Id.ToString()); + return; + } + + if (ex is WebSocketException socketException) + { + Warn($"Web socket was found to be in a invalid state.", bot.Information.Id.ToString()); + return; + } + + Error($"Please send crash log to https://discord.gg/FPXdPjcX27.", bot.Information.Id.ToString(), ex); } - - Error($"Please send crash log to https://discord.gg/FPXdPjcX27.", ex); } } + private async void OnTimerExecute(object? source, ElapsedEventArgs e) + { + await UpdatePlayerCounts(); + } + public async Task MainAsync() { await LoadConfig(); Start(); - - for (; ; ) - { - Thread.Sleep(100); - } + await Task.Delay(-1); } private void Start() @@ -113,11 +107,11 @@ private void Start() public async void OnProcessExit(object? sender, EventArgs e) { - foreach (KeyValuePair entry in Bots) + foreach (var entry in Bots) { Warn($"Stoping bot: {entry.Key}"); await entry.Value.StopAsync(); } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/ViewModels/BaseViewModel.cs b/DiscordPlayerCountBot/ViewModels/BaseViewModel.cs index ed79494..92a2d8a 100644 --- a/DiscordPlayerCountBot/ViewModels/BaseViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/BaseViewModel.cs @@ -48,4 +48,4 @@ public string ReplaceTagsWithValues(string? format, bool useNameAsLabel, string return status; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/ViewModels/CFX/CFXViewModel.cs b/DiscordPlayerCountBot/ViewModels/CFX/CFXViewModel.cs index 68f274e..47ba042 100644 --- a/DiscordPlayerCountBot/ViewModels/CFX/CFXViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/CFX/CFXViewModel.cs @@ -3,4 +3,4 @@ public class CFXViewModel : BaseViewModel { } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/ViewModels/Minecraft/MinecraftViewModel.cs b/DiscordPlayerCountBot/ViewModels/Minecraft/MinecraftViewModel.cs index 902534c..fea96cd 100644 --- a/DiscordPlayerCountBot/ViewModels/Minecraft/MinecraftViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/Minecraft/MinecraftViewModel.cs @@ -4,4 +4,4 @@ public class MinecraftViewModel : BaseViewModel { public string Version { get; set; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs b/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs index 4bd8ce2..be70811 100644 --- a/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs @@ -3,8 +3,8 @@ public class SteamViewModel : BaseViewModel { public string Map { get; set; } - public string Gametype { get; set; } + public string? Gametype { get; set; } public string Time { get; set; } public string SunMoon { get; set; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/log4net.config b/DiscordPlayerCountBot/log4net.config deleted file mode 100644 index e241699..0000000 --- a/DiscordPlayerCountBot/log4net.config +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file