From 863fb5d78717a66dc1e97bcf0ef4011e7eb9e4ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:31:53 +0000 Subject: [PATCH 01/17] Bump tj-actions/branch-names from 6 to 7.0.7 in /.github/workflows Bumps [tj-actions/branch-names](https://github.com/tj-actions/branch-names) from 6 to 7.0.7. - [Release notes](https://github.com/tj-actions/branch-names/releases) - [Changelog](https://github.com/tj-actions/branch-names/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/branch-names/compare/v6...v7.0.7) --- updated-dependencies: - dependency-name: tj-actions/branch-names dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/CreateReleaseApps.yml | 4 ++-- .github/workflows/docker-image.yml | 2 +- .github/workflows/dotnet.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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: From 588bfc9e1f6fda8dd3f4a83b12e757738799ebda Mon Sep 17 00:00:00 2001 From: Larry Date: Sat, 13 Jan 2024 16:21:30 -0700 Subject: [PATCH 02/17] Init RCON --- .editorconfig | 86 +++++++++++++++++++ DiscordPlayerCountBot/Bot/BotInformation.cs | 1 + .../DiscordPlayerCountBot.csproj | 1 + .../{Enum => Enums}/DataProvider.cs | 5 +- .../{Enum => Enums}/EnumHelper.cs | 2 +- .../{Enum => Enums}/HostEnviroment.cs | 2 +- .../Enums/RconServiceType.cs | 9 ++ .../Exceptions/ConfigurationException.cs | 10 +++ .../Exceptions/ParsingException.cs | 10 +++ .../SteamQueryAuthenticationException.cs | 9 ++ DiscordPlayerCountBot/Program.cs | 2 +- .../Providers/RconProvider.cs | 50 +++++++++++ .../Services/Rcon/IRconService.cs | 9 ++ .../Rcon/Praser/ArkInformationParser.cs | 29 +++++++ .../Rcon/Praser/CSGOInformationParser.cs | 31 +++++++ .../Rcon/Praser/IRconInformationParser.cs | 7 ++ .../Rcon/Praser/MinecraftInformationParser.cs | 30 +++++++ .../Services/Rcon/RconService.cs | 45 ++++++++++ .../Services/Rcon/RconServiceInformation.cs | 16 ++++ 19 files changed, 349 insertions(+), 5 deletions(-) rename DiscordPlayerCountBot/{Enum => Enums}/DataProvider.cs (58%) rename DiscordPlayerCountBot/{Enum => Enums}/EnumHelper.cs (94%) rename DiscordPlayerCountBot/{Enum => Enums}/HostEnviroment.cs (70%) create mode 100644 DiscordPlayerCountBot/Enums/RconServiceType.cs create mode 100644 DiscordPlayerCountBot/Exceptions/ConfigurationException.cs create mode 100644 DiscordPlayerCountBot/Exceptions/ParsingException.cs create mode 100644 DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs create mode 100644 DiscordPlayerCountBot/Providers/RconProvider.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/IRconService.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/RconService.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs diff --git a/.editorconfig b/.editorconfig index e84e194..2db502b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,89 @@ # 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 = crlf diff --git a/DiscordPlayerCountBot/Bot/BotInformation.cs b/DiscordPlayerCountBot/Bot/BotInformation.cs index 4b4b345..66e55c0 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() { diff --git a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj index 94d7fda..6a60158 100644 --- a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj +++ b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj @@ -16,6 +16,7 @@ + diff --git a/DiscordPlayerCountBot/Enum/DataProvider.cs b/DiscordPlayerCountBot/Enums/DataProvider.cs similarity index 58% rename from DiscordPlayerCountBot/Enum/DataProvider.cs rename to DiscordPlayerCountBot/Enums/DataProvider.cs index 51fd4a9..321e5eb 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,7 @@ public enum DataProvider CFX, SCUM, MINECRAFT, - BATTLEMETRICS + BATTLEMETRICS, + RCONClient } } diff --git a/DiscordPlayerCountBot/Enum/EnumHelper.cs b/DiscordPlayerCountBot/Enums/EnumHelper.cs similarity index 94% rename from DiscordPlayerCountBot/Enum/EnumHelper.cs rename to DiscordPlayerCountBot/Enums/EnumHelper.cs index fdc9946..bc7eca2 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 { diff --git a/DiscordPlayerCountBot/Enum/HostEnviroment.cs b/DiscordPlayerCountBot/Enums/HostEnviroment.cs similarity index 70% rename from DiscordPlayerCountBot/Enum/HostEnviroment.cs rename to DiscordPlayerCountBot/Enums/HostEnviroment.cs index 1fa9e0f..f1bdfde 100644 --- a/DiscordPlayerCountBot/Enum/HostEnviroment.cs +++ b/DiscordPlayerCountBot/Enums/HostEnviroment.cs @@ -1,4 +1,4 @@ -namespace PlayerCountBot.Enum +namespace PlayerCountBot.Enums { public enum HostEnvironment { diff --git a/DiscordPlayerCountBot/Enums/RconServiceType.cs b/DiscordPlayerCountBot/Enums/RconServiceType.cs new file mode 100644 index 0000000..e04becd --- /dev/null +++ b/DiscordPlayerCountBot/Enums/RconServiceType.cs @@ -0,0 +1,9 @@ +namespace DiscordPlayerCountBot.Enums +{ + public enum RconServiceType + { + CSGO, + Minecraft, + Ark + } +} diff --git a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs new file mode 100644 index 0000000..d4ffa67 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs @@ -0,0 +1,10 @@ +namespace DiscordPlayerCountBot.Exceptions +{ + internal class ConfigurationException : Exception + { + public ConfigurationException(string? message) : base(message) + { + + } + } +} diff --git a/DiscordPlayerCountBot/Exceptions/ParsingException.cs b/DiscordPlayerCountBot/Exceptions/ParsingException.cs new file mode 100644 index 0000000..dcc7c60 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/ParsingException.cs @@ -0,0 +1,10 @@ +namespace DiscordPlayerCountBot.Exceptions +{ + public class ParsingException : Exception + { + public ParsingException(string? message) : base(message) + { + + } + } +} diff --git a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs new file mode 100644 index 0000000..a5309d8 --- /dev/null +++ b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs @@ -0,0 +1,9 @@ +namespace DiscordPlayerCountBot.Exceptions +{ + internal class RconAuthenticationException : Exception + { + public RconAuthenticationException(string? message) : base(message) + { + } + } +} diff --git a/DiscordPlayerCountBot/Program.cs b/DiscordPlayerCountBot/Program.cs index 92fa7e0..d25e85e 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; diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs new file mode 100644 index 0000000..595718e --- /dev/null +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -0,0 +1,50 @@ +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Exceptions; +using DiscordPlayerCountBot.Services; + +namespace PlayerCountBot.Providers +{ + [Name("Rcon")] + public class RconProvider : ServerInformationProvider + { + public RconProvider(BotInformation info) : base(info) + { + } + + 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}"); + } + + var service = new RconService(); + + 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; + } + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/IRconService.cs b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs new file mode 100644 index 0000000..47ce035 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs @@ -0,0 +1,9 @@ +using DiscordPlayerCountBot.Enums; + +namespace DiscordPlayerCountBot.Services +{ + public interface IRconService + { + public Task GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType); + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs new file mode 100644 index 0000000..528c264 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs @@ -0,0 +1,29 @@ +using DiscordPlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace DiscordPlayerCountBot.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 + }; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs new file mode 100644 index 0000000..21b44e5 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs @@ -0,0 +1,31 @@ +using DiscordPlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace DiscordPlayerCountBot.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 + }; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs new file mode 100644 index 0000000..abd74d9 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs @@ -0,0 +1,7 @@ +namespace DiscordPlayerCountBot.Services.Praser +{ + public interface IRconInformationParser + { + public BaseViewModel Parse(string message); + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs new file mode 100644 index 0000000..1e77d24 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs @@ -0,0 +1,30 @@ +using DiscordPlayerCountBot.Exceptions; +using System.Text.RegularExpressions; + +namespace DiscordPlayerCountBot.Services.Praser +{ + public class MinecraftInformationParser : IRconInformationParser + { + public BaseViewModel Parse(string message) + { + var playerInfoMatch = Regex.Match(message, @"There are (\d+)/(\d+) players online"); + var queuedPlayersMatch = Regex.Match(message, @"Queue:\s+(\d+)\s+players waiting"); + + if (!playerInfoMatch.Success || playerInfoMatch.Groups.Count != 2) + { + throw new ParsingException("Could not find players or max players from a CSGO Rcon Response"); + } + + var players = int.Parse(playerInfoMatch.Groups[0].Value); + var maxPlayers = int.Parse(playerInfoMatch.Groups[1].Value); + int queuedPlayers = !queuedPlayersMatch.Success ? 0 : int.Parse(queuedPlayersMatch.Groups[0].Value); + + return new BaseViewModel() + { + Players = players, + MaxPlayers = maxPlayers, + QueuedPlayers = queuedPlayers + }; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs new file mode 100644 index 0000000..556662b --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -0,0 +1,45 @@ +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Exceptions; +using DiscordPlayerCountBot.Services.Praser; +using RconSharp; + +namespace DiscordPlayerCountBot.Services +{ + public class RconService : IRconService + { + private Dictionary PlayerCommands = new() + { + {RconServiceType.CSGO, new RconServiceInformation("status", new CSGOInformationParser())}, + {RconServiceType.Minecraft, new RconServiceInformation("list", new MinecraftInformationParser()) }, + {RconServiceType.Ark, new RconServiceInformation("listplayers", new ArkInformationParser())} + }; + + 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.PlayersCommand; + var response = await client.ExecuteCommandAsync(command); + var viewModel = serviceInformation.Parser.Parse(response); + + viewModel.Address = address; + viewModel.Port = port; + + return viewModel; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs new file mode 100644 index 0000000..c06e30c --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs @@ -0,0 +1,16 @@ +using DiscordPlayerCountBot.Services.Praser; + +namespace DiscordPlayerCountBot.Services +{ + public class RconServiceInformation + { + public string PlayersCommand { get; private set; } + public IRconInformationParser Parser { get; private set; } + + public RconServiceInformation(string playersCommand, IRconInformationParser parser) + { + PlayersCommand = playersCommand; + Parser = parser; + } + } +} From b42f2b7c2b9f8ee9dd565213e561b059ea214162 Mon Sep 17 00:00:00 2001 From: Larry Date: Sat, 13 Jan 2024 18:43:45 -0700 Subject: [PATCH 03/17] Add Steam Query, and Rcon as data providers --- DiscordPlayerCountBot/Enums/DataProvider.cs | 3 +- .../Base/ServerInformationProvider.cs | 10 ++++- .../Providers/RconProvider.cs | 1 - .../Providers/SteamQueryProvider.cs | 37 +++++++++++++++++++ .../Services/Rcon/RconService.cs | 2 +- .../Services/SteamQuery/ISteamQueryService.cs | 9 +++++ .../Services/SteamQuery/SteamQueryService.cs | 29 +++++++++++++++ 7 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 DiscordPlayerCountBot/Providers/SteamQueryProvider.cs create mode 100644 DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs create mode 100644 DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs diff --git a/DiscordPlayerCountBot/Enums/DataProvider.cs b/DiscordPlayerCountBot/Enums/DataProvider.cs index 321e5eb..66f7585 100644 --- a/DiscordPlayerCountBot/Enums/DataProvider.cs +++ b/DiscordPlayerCountBot/Enums/DataProvider.cs @@ -7,6 +7,7 @@ public enum DataProvider SCUM, MINECRAFT, BATTLEMETRICS, - RCONClient + RCONClient, + SteamQuery } } diff --git a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs index 145bc97..1317f2c 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -1,4 +1,5 @@ -using System.Net; +using SteamServerQuery; +using System.Net; using System.Net.Http; namespace PlayerCountBot.Providers.Base @@ -17,7 +18,6 @@ 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; @@ -76,6 +76,12 @@ protected void HandleException(Exception e) return; } + if(e is SteamException steamException) + { + Error($"There was an issue speaking with Steam Query Server.", e); + return; + } + Error($"There was an error speaking with {Label}.", e); throw e; } diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index 595718e..fa7eec5 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -1,5 +1,4 @@ using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Enums; using DiscordPlayerCountBot.Exceptions; using DiscordPlayerCountBot.Services; diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs new file mode 100644 index 0000000..8a02379 --- /dev/null +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -0,0 +1,37 @@ +using DiscordPlayerCountBot.Services.SteamQuery; + +namespace PlayerCountBot.Providers +{ + [Name("Steam Query")] + public class SteamQueryProvider : ServerInformationProvider + { + public SteamQueryProvider(BotInformation info) : base(info) + { + } + + public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + { + var service = new SteamQueryService(); + + 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; + } + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs index 556662b..b92f001 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -7,7 +7,7 @@ namespace DiscordPlayerCountBot.Services { public class RconService : IRconService { - private Dictionary PlayerCommands = new() + private readonly Dictionary PlayerCommands = new() { {RconServiceType.CSGO, new RconServiceInformation("status", new CSGOInformationParser())}, {RconServiceType.Minecraft, new RconServiceInformation("list", new MinecraftInformationParser()) }, diff --git a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs new file mode 100644 index 0000000..5eb2f46 --- /dev/null +++ b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs @@ -0,0 +1,9 @@ +using DiscordPlayerCountBot.Enums; + +namespace DiscordPlayerCountBot.Services.SteamQuery +{ + internal interface ISteamQueryService + { + public Task GetQueryResponse(string address, int port); + } +} diff --git a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs new file mode 100644 index 0000000..884f1a4 --- /dev/null +++ b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs @@ -0,0 +1,29 @@ +using SteamServerQuery; + +namespace DiscordPlayerCountBot.Services.SteamQuery +{ + public class SteamQueryService : ISteamQueryService + { + public async Task GetQueryResponse(string address, int port, string authorizationToken) + { + var model = new BaseViewModel() + { + Address = address, + Port = port + }; + + try + { + var serverInformation = await SteamServer.QueryServerAsync(address, port = 0); + model.MaxPlayers = serverInformation.MaxPlayers; + model.Players = serverInformation.Players; + model.QueuedPlayers = 0; + }catch + { + throw; + } + + return model; + } + } +} From 9dd4ea8aef3f4016620af878597406b49be06dab Mon Sep 17 00:00:00 2001 From: Larry Date: Sat, 13 Jan 2024 18:53:36 -0700 Subject: [PATCH 04/17] Linting V1 --- .../Providers/Base/ServerInformationProvider.cs | 2 +- DiscordPlayerCountBot/Providers/RconProvider.cs | 4 ++-- .../Services/SteamQuery/SteamQueryService.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs index 1317f2c..77e67dc 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -76,7 +76,7 @@ protected void HandleException(Exception e) return; } - if(e is SteamException steamException) + if (e is SteamException steamException) { Error($"There was an issue speaking with Steam Query Server.", e); return; diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index fa7eec5..d540515 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -22,7 +22,7 @@ public RconProvider(BotInformation info) : base(info) var service = new RconService(); - if(!Enum.TryParse(information.RconServiceName, true, out var serviceType)) + 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}"); } @@ -32,7 +32,7 @@ public RconProvider(BotInformation info) : base(info) var addressAndPort = information.GetAddressAndPort(); var response = await service.GetRconResponse(addressAndPort.Item1, addressAndPort.Item2, applicationVariables["RconPassword"], serviceType); - if(response == null) + if (response == null) throw new ApplicationException($"Server Address: {information.Address} was not found in Steam's directory."); HandleLastException(information); diff --git a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs index 884f1a4..abf3df0 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs @@ -4,7 +4,7 @@ namespace DiscordPlayerCountBot.Services.SteamQuery { public class SteamQueryService : ISteamQueryService { - public async Task GetQueryResponse(string address, int port, string authorizationToken) + public async Task GetQueryResponse(string address, int port) { var model = new BaseViewModel() { @@ -18,7 +18,7 @@ public async Task GetQueryResponse(string address, int port, stri model.MaxPlayers = serverInformation.MaxPlayers; model.Players = serverInformation.Players; model.QueuedPlayers = 0; - }catch + } catch { throw; } From 88e224462998ddb7d570e10ea61871d427836db5 Mon Sep 17 00:00:00 2001 From: Larry Date: Sat, 13 Jan 2024 18:55:58 -0700 Subject: [PATCH 05/17] Linting V2 --- DiscordPlayerCountBot/DiscordPlayerCountBot.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj index 6a60158..71a54fe 100644 --- a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj +++ b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj @@ -17,7 +17,7 @@ - + From 7979550374773d4c9e347afe785300ff1e9e03d2 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 12:21:26 -0700 Subject: [PATCH 06/17] Split up Service Information for later updates, first test with Minecraft was successful --- DiscordPlayerCountBot/Bot/Bot.cs | 2 ++ .../Rcon/Praser/MinecraftInformationParser.cs | 12 +++++----- .../Services/Rcon/RconService.cs | 14 +++++------ .../Services/Rcon/RconServiceInformation.cs | 16 +++++-------- .../ArkRconServiceInformation.cs | 23 +++++++++++++++++++ .../CSGORconServiceInformation.cs | 23 +++++++++++++++++++ .../MinecraftRconServiceInformation.cs | 23 +++++++++++++++++++ 7 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs create mode 100644 DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs diff --git a/DiscordPlayerCountBot/Bot/Bot.cs b/DiscordPlayerCountBot/Bot/Bot.cs index 8a10036..89e488c 100644 --- a/DiscordPlayerCountBot/Bot/Bot.cs +++ b/DiscordPlayerCountBot/Bot/Bot.cs @@ -29,6 +29,8 @@ public void InitDataProviders() 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.Add((int)DataProvider.RCONClient, new RconProvider(Information!)); + DataProviders.Add((int)DataProvider.SteamQuery, new SteamQueryProvider(Information!)); } public async Task StartAsync(bool shouldStart) diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs index 1e77d24..2411fd1 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs @@ -7,17 +7,17 @@ public class MinecraftInformationParser : IRconInformationParser { public BaseViewModel Parse(string message) { - var playerInfoMatch = Regex.Match(message, @"There are (\d+)/(\d+) players online"); + 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 != 2) + if (!playerInfoMatch.Success || playerInfoMatch.Groups.Count < 3) { - throw new ParsingException("Could not find players or max players from a CSGO Rcon Response"); + throw new ParsingException("Could not find players or max players from a Minecraft Rcon Response"); } - var players = int.Parse(playerInfoMatch.Groups[0].Value); - var maxPlayers = int.Parse(playerInfoMatch.Groups[1].Value); - int queuedPlayers = !queuedPlayersMatch.Success ? 0 : int.Parse(queuedPlayersMatch.Groups[0].Value); + 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() { diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs index b92f001..474025e 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -1,17 +1,17 @@ using DiscordPlayerCountBot.Enums; using DiscordPlayerCountBot.Exceptions; -using DiscordPlayerCountBot.Services.Praser; +using DiscordPlayerCountBot.Services.Rcon.ServiceInformation; using RconSharp; namespace DiscordPlayerCountBot.Services { public class RconService : IRconService { - private readonly Dictionary PlayerCommands = new() + private readonly Dictionary PlayerCommands = new() { - {RconServiceType.CSGO, new RconServiceInformation("status", new CSGOInformationParser())}, - {RconServiceType.Minecraft, new RconServiceInformation("list", new MinecraftInformationParser()) }, - {RconServiceType.Ark, new RconServiceInformation("listplayers", new ArkInformationParser())} + {RconServiceType.CSGO, new CSGORconServiceInformation()}, + {RconServiceType.Minecraft, new MinecraftRconServiceInformation()}, + {RconServiceType.Ark, new ArkRconServiceInformation()} }; public async Task GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType) @@ -32,9 +32,9 @@ public async Task GetRconResponse(string address, int port, strin 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.PlayersCommand; + var command = serviceInformation.GetPlayerListCommand(); var response = await client.ExecuteCommandAsync(command); - var viewModel = serviceInformation.Parser.Parse(response); + var viewModel = serviceInformation.GetParser().Parse(response); viewModel.Address = address; viewModel.Port = port; diff --git a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs index c06e30c..40f8184 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs @@ -1,16 +1,12 @@ -using DiscordPlayerCountBot.Services.Praser; +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Services.Praser; namespace DiscordPlayerCountBot.Services { - public class RconServiceInformation + public interface IRconServiceInformation { - public string PlayersCommand { get; private set; } - public IRconInformationParser Parser { get; private set; } - - public RconServiceInformation(string playersCommand, IRconInformationParser parser) - { - PlayersCommand = playersCommand; - Parser = parser; - } + public abstract RconServiceType GetServiceType(); + public abstract string GetPlayerListCommand(); + public abstract IRconInformationParser GetParser(); } } diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs new file mode 100644 index 0000000..0fd54c9 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs @@ -0,0 +1,23 @@ +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Services.Praser; + +namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +{ + public class ArkRconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new ArkInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.Ark; + } + + public string GetPlayerListCommand() + { + return "listplayers"; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs new file mode 100644 index 0000000..1dc2ad3 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs @@ -0,0 +1,23 @@ +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Services.Praser; + +namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +{ + public class CSGORconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new CSGOInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.CSGO; + } + + public string GetPlayerListCommand() + { + return "status"; + } + } +} diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs new file mode 100644 index 0000000..e99a2e9 --- /dev/null +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs @@ -0,0 +1,23 @@ +using DiscordPlayerCountBot.Enums; +using DiscordPlayerCountBot.Services.Praser; + +namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +{ + public class MinecraftRconServiceInformation : IRconServiceInformation + { + public IRconInformationParser GetParser() + { + return new MinecraftInformationParser(); + } + + public RconServiceType GetServiceType() + { + return RconServiceType.Minecraft; + } + + public string GetPlayerListCommand() + { + return "list"; + } + } +} From 5276baccdaaae5add5ed776499656aeeecf721d4 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 13:50:45 -0700 Subject: [PATCH 07/17] Change to Dependency Injection and Serilog. --- .../DockerConfigurationTests.cs | 26 ++++++++--- DiscordPlayerCountBot/Bot/Bot.cs | 40 +++++++++-------- DiscordPlayerCountBot/Bot/BotConfig.cs | 1 - .../Configuration/Base/IConfigurable.cs | 1 + .../Configuration/DockerConfiguration.cs | 17 +++++-- .../Configuration/StandardConfiguration.cs | 26 ++++++++--- .../DiscordPlayerCountBot.csproj | 7 +-- .../Logging/LoggableClass.cs | 42 +++++++---------- DiscordPlayerCountBot/Program.cs | 40 ++++++++++------- .../Base/IServerInformationProvider.cs | 2 + .../Base/ServerInformationProvider.cs | 26 +++++------ .../Providers/BattleMetricsProvider.cs | 7 +-- .../Providers/CFXProvider.cs | 5 ++- .../Providers/MinecraftProvider.cs | 5 ++- .../Providers/ScumProvider.cs | 5 ++- .../Providers/SteamProvider.cs | 5 ++- DiscordPlayerCountBot/UpdateController.cs | 30 +++++-------- DiscordPlayerCountBot/log4net.config | 45 ------------------- 18 files changed, 163 insertions(+), 167 deletions(-) delete mode 100644 DiscordPlayerCountBot/log4net.config diff --git a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs index 6d5b68f..04c5fd1 100644 --- a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs +++ b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic; using EnvironmentHelper = PlayerCountBot.Tests.Environment.EnvironmentHelper; @@ -15,7 +16,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; @@ -35,7 +39,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(); @@ -57,7 +64,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; @@ -80,7 +90,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; @@ -101,7 +114,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/Bot/Bot.cs b/DiscordPlayerCountBot/Bot/Bot.cs index 8a10036..7b3ed75 100644 --- a/DiscordPlayerCountBot/Bot/Bot.cs +++ b/DiscordPlayerCountBot/Bot/Bot.cs @@ -1,34 +1,31 @@ -namespace PlayerCountBot +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 +35,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} at address and port: {Information.Address}, {(DataProvider)Information.ProviderType}"); await DiscordClient.LoginAndStartAsync(Information.Token, Information.Address, shouldStart); } @@ -49,20 +46,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..e61eafa 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", diff --git a/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs b/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs index 8077cf5..ae099cf 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); } } diff --git a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs index 585350b..c6a2a76 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; + } } } diff --git a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs index 4a2eca9..aea6054 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}"); + Info($"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; + } } } diff --git a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj index bb34664..06ff908 100644 --- a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj +++ b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj @@ -13,15 +13,16 @@ + + + + - - Always - \ No newline at end of file diff --git a/DiscordPlayerCountBot/Logging/LoggableClass.cs b/DiscordPlayerCountBot/Logging/LoggableClass.cs index 087a633..b173936 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}"; } } } diff --git a/DiscordPlayerCountBot/Program.cs b/DiscordPlayerCountBot/Program.cs index 92fa7e0..2b310d1 100644 --- a/DiscordPlayerCountBot/Program.cs +++ b/DiscordPlayerCountBot/Program.cs @@ -10,28 +10,36 @@ 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; -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(); + + +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..954c9b9 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(); } } diff --git a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs index 145bc97..fa75cfe 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -8,23 +8,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 +31,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 +51,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(); } } diff --git a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs index 13f361a..406313a 100644 --- a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs +++ b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs @@ -3,8 +3,9 @@ [Name("BattleMetrics")] public class BattleMetricsProvider : ServerInformationProvider { - public BattleMetricsProvider(BotInformation info) : base(info) + public override DataProvider GetRequiredProviderType() { + return DataProvider.BATTLEMETRICS; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) @@ -25,9 +26,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..8995080 100644 --- a/DiscordPlayerCountBot/Providers/CFXProvider.cs +++ b/DiscordPlayerCountBot/Providers/CFXProvider.cs @@ -3,8 +3,9 @@ [Name("CFX")] public class CFXProvider : ServerInformationProvider { - public CFXProvider(BotInformation info) : base(info) + public override DataProvider GetRequiredProviderType() { + return DataProvider.CFX; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) @@ -36,7 +37,7 @@ public CFXProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } diff --git a/DiscordPlayerCountBot/Providers/MinecraftProvider.cs b/DiscordPlayerCountBot/Providers/MinecraftProvider.cs index a1f1752..da5fe4e 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,7 +36,7 @@ public MinecraftProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } diff --git a/DiscordPlayerCountBot/Providers/ScumProvider.cs b/DiscordPlayerCountBot/Providers/ScumProvider.cs index 5dc7b36..a523e9a 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,7 +41,7 @@ public ScumProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index 69f2f3b..96c941d 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -3,8 +3,9 @@ [Name("Steam")] public class SteamProvider : ServerInformationProvider { - public SteamProvider(BotInformation info) : base(info) + public override DataProvider GetRequiredProviderType() { + return DataProvider.STEAM; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) @@ -34,7 +35,7 @@ public SteamProvider(BotInformation info) : base(info) } catch (Exception e) { - HandleException(e); + HandleException(e, information.Id.ToString()); return null; } } diff --git a/DiscordPlayerCountBot/UpdateController.cs b/DiscordPlayerCountBot/UpdateController.cs index 4c1e991..4236f72 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."); } @@ -85,7 +83,7 @@ private async void OnTimerExecute(object? source, ElapsedEventArgs e) return; } - Error($"Please send crash log to https://discord.gg/FPXdPjcX27.", ex); + Error($"Please send crash log to https://discord.gg/FPXdPjcX27.", null, ex); } } @@ -93,11 +91,7 @@ public async Task MainAsync() { await LoadConfig(); Start(); - - for (; ; ) - { - Thread.Sleep(100); - } + await Task.Delay(-1); } private void Start() @@ -113,7 +107,7 @@ 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(); 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 From 712adf9aceb01c58544468f8afa44aaedab59568 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 14:45:44 -0700 Subject: [PATCH 08/17] Merge RCON/Query Update with SunMoon --- .../DockerConfigurationTests.cs | 1 - DiscordPlayerCountBot.Tests/JsonTests.cs | 4 +- DiscordPlayerCountBot.Tests/Usings.cs | 5 ++- DiscordPlayerCountBot/Bot/Bot.cs | 5 ++- .../Configuration/StandardConfiguration.cs | 2 +- DiscordPlayerCountBot/DiscordClient.cs | 44 ------------------- .../DiscordPlayerCountBot.csproj | 7 +-- .../Enums/RconServiceType.cs | 2 +- .../Exceptions/ConfigurationException.cs | 2 +- .../Exceptions/ParsingException.cs | 2 +- .../SteamQueryAuthenticationException.cs | 2 +- DiscordPlayerCountBot/Program.cs | 15 +++++++ .../Base/ServerInformationProvider.cs | 12 +---- .../Providers/BattleMetricsProvider.cs | 23 +++++----- .../Providers/CFXProvider.cs | 13 ++++-- .../Providers/RconProvider.cs | 22 ++++++---- .../Providers/SteamProvider.cs | 31 +++++++------ .../Providers/SteamQueryProvider.cs | 16 ++++--- .../Services/Rcon/IRconService.cs | 4 +- .../Rcon/Praser/ArkInformationParser.cs | 4 +- .../Rcon/Praser/CSGOInformationParser.cs | 4 +- .../Rcon/Praser/IRconInformationParser.cs | 2 +- .../Rcon/Praser/MinecraftInformationParser.cs | 4 +- .../Services/Rcon/RconService.cs | 19 ++++---- .../Services/Rcon/RconServiceInformation.cs | 6 +-- .../ArkRconServiceInformation.cs | 6 +-- .../CSGORconServiceInformation.cs | 6 +-- .../MinecraftRconServiceInformation.cs | 6 +-- .../Services/SteamQuery/ISteamQueryService.cs | 4 +- .../Services/SteamQuery/SteamQueryService.cs | 24 +++++----- DiscordPlayerCountBot/UpdateController.cs | 42 +++++++++--------- 31 files changed, 161 insertions(+), 178 deletions(-) delete mode 100644 DiscordPlayerCountBot/DiscordClient.cs diff --git a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs index 04c5fd1..dac5bc8 100644 --- a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs +++ b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using System.Collections.Generic; using EnvironmentHelper = PlayerCountBot.Tests.Environment.EnvironmentHelper; namespace PlayerCountBot.Tests; diff --git a/DiscordPlayerCountBot.Tests/JsonTests.cs b/DiscordPlayerCountBot.Tests/JsonTests.cs index 05c3d88..3197ad4 100644 --- a/DiscordPlayerCountBot.Tests/JsonTests.cs +++ b/DiscordPlayerCountBot.Tests/JsonTests.cs @@ -1,6 +1,4 @@ -using PlayerCountBot.Json; - -namespace DiscordPlayerCountBot.Tests; +namespace PlayerCountBot.Tests; [Collection("Json Serialization Test Suite")] public class JsonTests diff --git a/DiscordPlayerCountBot.Tests/Usings.cs b/DiscordPlayerCountBot.Tests/Usings.cs index eaab6e2..c9c9fa5 100644 --- a/DiscordPlayerCountBot.Tests/Usings.cs +++ b/DiscordPlayerCountBot.Tests/Usings.cs @@ -1,3 +1,6 @@ global using Xunit; + +global using PlayerCountBot; global using PlayerCountBot.Configuration; -global using PlayerCountBot.Tests.Environment; +global using PlayerCountBot.Extensions; +global using PlayerCountBot.Json; \ No newline at end of file diff --git a/DiscordPlayerCountBot/Bot/Bot.cs b/DiscordPlayerCountBot/Bot/Bot.cs index 7b3ed75..145ed23 100644 --- a/DiscordPlayerCountBot/Bot/Bot.cs +++ b/DiscordPlayerCountBot/Bot/Bot.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using PlayerCountBot.Extensions; +using Microsoft.Extensions.DependencyInjection; namespace PlayerCountBot { @@ -35,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}, {(DataProvider)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); } diff --git a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs index aea6054..f186260 100644 --- a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs @@ -32,7 +32,7 @@ public async Task, int>> Configure(bool shouldStar if (config == null) throw new ApplicationException("You have broken the syntax of your config file."); - Info($"Config.json loaded:\n{fileContents}"); + Debug($"Config.json loaded:\n{fileContents}"); foreach (var info in config.ServerInformation) { diff --git a/DiscordPlayerCountBot/DiscordClient.cs b/DiscordPlayerCountBot/DiscordClient.cs deleted file mode 100644 index a909922..0000000 --- a/DiscordPlayerCountBot/DiscordClient.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace PlayerCountBot -{ - public static class DiscordClient - { - public static async Task LoginAndStartAsync(this DiscordSocketClient client, string token, string address, bool shouldStart = true) - { - if (shouldStart) - { - await client.LoginAsync(TokenType.Bot, token); - await client.SetGameAsync($"Starting: {address}"); - await client.StartAsync(); - } - } - - public static async Task SetChannelName(this IDiscordClient socket, ulong? channelId, string gameStatus) - { - if (channelId == null) return; - - IGuildChannel channel = (IGuildChannel)await socket.GetChannelAsync(channelId.Value); - - if (channel is null) - { - throw new ArgumentException($"[Bot] - Invalid Channel Id: {channelId}, Channel was not found."); - } - - /* - * Keep in mind there is a massive rate limit on this call that is specific to discord, and not Discord.Net - * 2x per 10 minutes - * https://discord.com/developers/docs/topics/rate-limits - * https://www.reddit.com/r/Discord_Bots/comments/qzrl5h/channel_name_edit_rate_limit/ - */ - - if (channel != null) - { - if (channel is ITextChannel && channel is not IVoiceChannel) - { - gameStatus = gameStatus.Replace('/', '-').Replace(' ', '-').Replace(':', '-'); - } - - await channel.ModifyAsync(prop => prop.Name = gameStatus); - } - } - } -} diff --git a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj index 5c723cb..654375e 100644 --- a/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj +++ b/DiscordPlayerCountBot/DiscordPlayerCountBot.csproj @@ -6,7 +6,6 @@ enable enable win-x64 - true @@ -17,15 +16,11 @@ -<<<<<<< HEAD + -======= - - ->>>>>>> rcon diff --git a/DiscordPlayerCountBot/Enums/RconServiceType.cs b/DiscordPlayerCountBot/Enums/RconServiceType.cs index e04becd..9ca97a3 100644 --- a/DiscordPlayerCountBot/Enums/RconServiceType.cs +++ b/DiscordPlayerCountBot/Enums/RconServiceType.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Enums +namespace PlayerCountBot.Enums { public enum RconServiceType { diff --git a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs index d4ffa67..b475db9 100644 --- a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs +++ b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Exceptions +namespace PlayerCountBot.Exceptions { internal class ConfigurationException : Exception { diff --git a/DiscordPlayerCountBot/Exceptions/ParsingException.cs b/DiscordPlayerCountBot/Exceptions/ParsingException.cs index dcc7c60..a9ed82b 100644 --- a/DiscordPlayerCountBot/Exceptions/ParsingException.cs +++ b/DiscordPlayerCountBot/Exceptions/ParsingException.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Exceptions +namespace PlayerCountBot.Exceptions { public class ParsingException : Exception { diff --git a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs index a5309d8..e7b0286 100644 --- a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs +++ b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Exceptions +namespace PlayerCountBot.Exceptions { internal class RconAuthenticationException : Exception { diff --git a/DiscordPlayerCountBot/Program.cs b/DiscordPlayerCountBot/Program.cs index 450cc57..4efe036 100644 --- a/DiscordPlayerCountBot/Program.cs +++ b/DiscordPlayerCountBot/Program.cs @@ -19,6 +19,9 @@ using Serilog.Sinks.SystemConsole.Themes; using Microsoft.Extensions.DependencyInjection; using PlayerCountBot.Configuration; +using PlayerCountBot.Services; +using PlayerCountBot.Services.Rcon.ServiceInformation; +using PlayerCountBot.Services.SteamQuery; 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) @@ -33,11 +36,23 @@ 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(); diff --git a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs index fc6d980..41d5c7b 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -1,6 +1,4 @@ -using SteamServerQuery; -using System.Net; -using System.Net.Http; +using System.Net; namespace PlayerCountBot.Providers.Base { @@ -73,13 +71,7 @@ protected void HandleException(Exception e, string? id = null) return; } - if (e is SteamException steamException) - { - Error($"There was an issue speaking with Steam Query Server.", e); - return; - } - - Error($"There was an error speaking with {Label}.", e); + Error($"There was an error speaking with {Label}.", id, e); throw e; } diff --git a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs index 85c5b6c..1c232ce 100644 --- a/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs +++ b/DiscordPlayerCountBot/Providers/BattleMetricsProvider.cs @@ -1,8 +1,17 @@ -namespace PlayerCountBot.Providers +using PlayerCountBot.Extensions; + +namespace PlayerCountBot.Providers { [Name("BattleMetrics")] public class BattleMetricsProvider : ServerInformationProvider { + private readonly BattleMetricsService Service; + + public BattleMetricsProvider(BattleMetricsService service) + { + Service = service; + } + public override DataProvider GetRequiredProviderType() { return DataProvider.BATTLEMETRICS; @@ -10,12 +19,10 @@ public override DataProvider GetRequiredProviderType() public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) { - var service = new BattleMetricsService(); - 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?"); @@ -24,13 +31,9 @@ public override DataProvider GetRequiredProviderType() var model = server.GetViewModel(); - if (!string.IsNullOrEmpty(model.Time) && TimeOnly.TryParse(model.Time, out var time)) + if (model.Time.TryGetSunMoonPhase(information.SunriseHour, information.SunsetHour, out var sunMoon)) { - if (information.SunriseHour.HasValue && information.SunsetHour.HasValue) - model.SunMoon = time.Hour > information.SunriseHour && time.Hour < information.SunsetHour ? "☀️" : "🌙"; - - if (!information.SunriseHour.HasValue || !information.SunsetHour.HasValue) - model.SunMoon = time.Hour > 6 && time.Hour < 20 ? "☀️" : "🌙"; + model.SunMoon = sunMoon; } return model; diff --git a/DiscordPlayerCountBot/Providers/CFXProvider.cs b/DiscordPlayerCountBot/Providers/CFXProvider.cs index 8995080..f3d9a39 100644 --- a/DiscordPlayerCountBot/Providers/CFXProvider.cs +++ b/DiscordPlayerCountBot/Providers/CFXProvider.cs @@ -3,6 +3,13 @@ [Name("CFX")] public class CFXProvider : ServerInformationProvider { + public readonly CFXService Service; + + public CFXProvider(CFXService service) + { + Service = service; + } + public override DataProvider GetRequiredProviderType() { return DataProvider.CFX; @@ -10,12 +17,10 @@ public override DataProvider GetRequiredProviderType() public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) { - var service = new CFXService(); - 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) diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index d540515..69a7160 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -1,14 +1,22 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Exceptions; -using DiscordPlayerCountBot.Services; +using PlayerCountBot.Enums; +using PlayerCountBot.Exceptions; +using PlayerCountBot.Services; namespace PlayerCountBot.Providers { [Name("Rcon")] public class RconProvider : ServerInformationProvider { - public RconProvider(BotInformation info) : base(info) + 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) @@ -19,9 +27,7 @@ public RconProvider(BotInformation info) : base(info) { throw new ConfigurationException($"Bot: {information.Name} must have RconServiceName specified in it's config. {values}"); } - - var service = new RconService(); - + 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}"); @@ -30,7 +36,7 @@ public RconProvider(BotInformation info) : base(info) try { var addressAndPort = information.GetAddressAndPort(); - var response = await service.GetRconResponse(addressAndPort.Item1, addressAndPort.Item2, applicationVariables["RconPassword"], serviceType); + 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."); diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index c6f34a5..ce36b8c 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -1,8 +1,17 @@ -namespace PlayerCountBot.Providers +using PlayerCountBot.Extensions; + +namespace PlayerCountBot.Providers { [Name("Steam")] public class SteamProvider : ServerInformationProvider { + private readonly SteamService Service; + + public SteamProvider(SteamService service) + { + Service = service; + } + public override DataProvider GetRequiredProviderType() { return DataProvider.STEAM; @@ -10,12 +19,10 @@ public override DataProvider GetRequiredProviderType() public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) { - var service = new SteamService(); - 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,20 +41,16 @@ public override DataProvider GetRequiredProviderType() }; var serverTime = model.Gametype.Split(",") - .Where(entry => entry.Contains(':') && entry.Length == 5) - .FirstOrDefault(); + .Where(entry => entry.Contains(':') && entry.Length == 5) + .FirstOrDefault(); if (!string.IsNullOrEmpty(serverTime)) { - if (TimeOnly.TryParse(serverTime, out var time)) - { - if (information.SunriseHour.HasValue && information.SunsetHour.HasValue) - model.SunMoon = time.Hour > information.SunriseHour && time.Hour < information.SunsetHour ? "☀️" : "🌙"; + model.Time = serverTime; - if (!information.SunriseHour.HasValue || !information.SunsetHour.HasValue) - model.SunMoon = time.Hour > 6 && time.Hour < 20 ? "☀️" : "🌙"; - - model.Time = serverTime; + if (model.Time.TryGetSunMoonPhase(information.SunriseHour, information.SunsetHour, out var sunMoon)) + { + model.SunMoon = sunMoon; } } diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs index 8a02379..4d6d07c 100644 --- a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -1,22 +1,28 @@ -using DiscordPlayerCountBot.Services.SteamQuery; +using PlayerCountBot.Services.SteamQuery; namespace PlayerCountBot.Providers { [Name("Steam Query")] public class SteamQueryProvider : ServerInformationProvider { - public SteamQueryProvider(BotInformation info) : base(info) + private readonly SteamQueryService Service; + + public SteamQueryProvider(SteamQueryService service) { + Service = service; } - public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) + public override DataProvider GetRequiredProviderType() { - var service = new SteamQueryService(); + 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); + var response = await Service.GetQueryResponse(addressAndPort.Item1, addressAndPort.Item2); if (response == null) { diff --git a/DiscordPlayerCountBot/Services/Rcon/IRconService.cs b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs index 47ce035..68a8fa9 100644 --- a/DiscordPlayerCountBot/Services/Rcon/IRconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs @@ -1,6 +1,6 @@ -using DiscordPlayerCountBot.Enums; +using PlayerCountBot.Enums; -namespace DiscordPlayerCountBot.Services +namespace PlayerCountBot.Services { public interface IRconService { diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs index 528c264..73e6f0f 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Exceptions; +using PlayerCountBot.Exceptions; using System.Text.RegularExpressions; -namespace DiscordPlayerCountBot.Services.Praser +namespace PlayerCountBot.Services.Praser { public class ArkInformationParser : IRconInformationParser { diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs index 21b44e5..10ab660 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Exceptions; +using PlayerCountBot.Exceptions; using System.Text.RegularExpressions; -namespace DiscordPlayerCountBot.Services.Praser +namespace PlayerCountBot.Services.Praser { public class CSGOInformationParser : IRconInformationParser { diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs index abd74d9..95dd2a2 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs @@ -1,4 +1,4 @@ -namespace DiscordPlayerCountBot.Services.Praser +namespace PlayerCountBot.Services.Praser { public interface IRconInformationParser { diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs index 2411fd1..1d833cd 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Exceptions; +using PlayerCountBot.Exceptions; using System.Text.RegularExpressions; -namespace DiscordPlayerCountBot.Services.Praser +namespace PlayerCountBot.Services.Praser { public class MinecraftInformationParser : IRconInformationParser { diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs index 474025e..db281d6 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -1,18 +1,19 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Exceptions; -using DiscordPlayerCountBot.Services.Rcon.ServiceInformation; +using PlayerCountBot.Enums; +using PlayerCountBot.Exceptions; +using Microsoft.Extensions.DependencyInjection; using RconSharp; -namespace DiscordPlayerCountBot.Services +namespace PlayerCountBot.Services { public class RconService : IRconService { - private readonly Dictionary PlayerCommands = new() + private readonly Dictionary PlayerCommands = new(); + + public RconService(IServiceProvider serviceProvider) { - {RconServiceType.CSGO, new CSGORconServiceInformation()}, - {RconServiceType.Minecraft, new MinecraftRconServiceInformation()}, - {RconServiceType.Ark, new ArkRconServiceInformation()} - }; + PlayerCommands = serviceProvider.GetServices() + .ToDictionary(value => value.GetServiceType()); + } public async Task GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType) { diff --git a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs index 40f8184..82f26d3 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Services.Praser; +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; -namespace DiscordPlayerCountBot.Services +namespace PlayerCountBot.Services { public interface IRconServiceInformation { diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs index 0fd54c9..b4ce406 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Services.Praser; +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; -namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +namespace PlayerCountBot.Services.Rcon.ServiceInformation { public class ArkRconServiceInformation : IRconServiceInformation { diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs index 1dc2ad3..99f3ee1 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Services.Praser; +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; -namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +namespace PlayerCountBot.Services.Rcon.ServiceInformation { public class CSGORconServiceInformation : IRconServiceInformation { diff --git a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs index e99a2e9..9270f6c 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs @@ -1,7 +1,7 @@ -using DiscordPlayerCountBot.Enums; -using DiscordPlayerCountBot.Services.Praser; +using PlayerCountBot.Enums; +using PlayerCountBot.Services.Praser; -namespace DiscordPlayerCountBot.Services.Rcon.ServiceInformation +namespace PlayerCountBot.Services.Rcon.ServiceInformation { public class MinecraftRconServiceInformation : IRconServiceInformation { diff --git a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs index 5eb2f46..e78cff4 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs @@ -1,6 +1,6 @@ -using DiscordPlayerCountBot.Enums; +using PlayerCountBot.Enums; -namespace DiscordPlayerCountBot.Services.SteamQuery +namespace PlayerCountBot.Services.SteamQuery { internal interface ISteamQueryService { diff --git a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs index abf3df0..71f6bdb 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs @@ -1,6 +1,6 @@ -using SteamServerQuery; +using SteamQueryNet; -namespace DiscordPlayerCountBot.Services.SteamQuery +namespace PlayerCountBot.Services.SteamQuery { public class SteamQueryService : ISteamQueryService { @@ -12,16 +12,16 @@ public async Task GetQueryResponse(string address, int port) Port = port }; - try - { - var serverInformation = await SteamServer.QueryServerAsync(address, port = 0); - model.MaxPlayers = serverInformation.MaxPlayers; - model.Players = serverInformation.Players; - model.QueuedPlayers = 0; - } catch - { - throw; - } + 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; } diff --git a/DiscordPlayerCountBot/UpdateController.cs b/DiscordPlayerCountBot/UpdateController.cs index 4236f72..0196eb4 100644 --- a/DiscordPlayerCountBot/UpdateController.cs +++ b/DiscordPlayerCountBot/UpdateController.cs @@ -59,34 +59,34 @@ 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.", null, ex); } } + private async void OnTimerExecute(object? source, ElapsedEventArgs e) + { + await UpdatePlayerCounts(); + } + public async Task MainAsync() { await LoadConfig(); From 648f62ec024ac79c17467ccd88d393d20df06dd0 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 14:47:06 -0700 Subject: [PATCH 09/17] Add extensions --- .../SunMoonTagTests.cs | 78 +++++++++++++++++++ .../Extensions/DiscordClient.cs | 44 +++++++++++ .../Extensions/StringExtensions.cs | 27 +++++++ 3 files changed, 149 insertions(+) create mode 100644 DiscordPlayerCountBot.Tests/SunMoonTagTests.cs create mode 100644 DiscordPlayerCountBot/Extensions/DiscordClient.cs create mode 100644 DiscordPlayerCountBot/Extensions/StringExtensions.cs diff --git a/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs b/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs new file mode 100644 index 0000000..5af5eda --- /dev/null +++ b/DiscordPlayerCountBot.Tests/SunMoonTagTests.cs @@ -0,0 +1,78 @@ +namespace PlayerCountBot.Tests +{ + [Collection("Sun & Moon Tag Test Suite")] + public class SunMoonTagTests + { + [Theory(DisplayName = "Test If Should Be Moon", Timeout = 30)] + [InlineData("05:30")] + [InlineData("21:30")] + [InlineData("23:30")] + public void ShouldBeMoonPhase(string time) + { + var success = time.TryGetSunMoonPhase(null, null, out var output); + + Assert.True(success, "SunMoon Phase failed to parse"); + Assert.True(!string.IsNullOrEmpty(output)); + Assert.Equal("🌙", output); + } + + [Theory(DisplayName = "Test If Should Be Sun", Timeout = 30)] + [InlineData("06:04")] + [InlineData("15:42")] + [InlineData("19:06")] + public void ShouldBeSunPhase(string time) + { + var success = time.TryGetSunMoonPhase(null, null, out var output); + + Assert.True(success, "SunMoon Phase failed to parse"); + Assert.True(!string.IsNullOrEmpty(output)); + Assert.Equal("☀️", output); + } + + [Theory(DisplayName = "Test Invalid Values", Timeout = 30)] + [InlineData("abc")] + [InlineData("")] + [InlineData("not a time value")] + [InlineData("24:30")] + public void ShouldNotParse(string time) + { + var success = time.TryGetSunMoonPhase(null, null, out var output); + + Assert.False(success, "SunMoon Phase successfully parsed when it should have failed"); + Assert.True(string.IsNullOrEmpty(output), "SunMoon output is not null or empty."); + } + + [Theory(DisplayName = "Test custom sunrise and sunset", Timeout = 30)] + [InlineData("05:30")] + [InlineData("21:30")] + [InlineData("23:30")] + [InlineData("06:04")] + [InlineData("15:42")] + [InlineData("19:06")] + public void ShouldOutputCorrectValue(string time) + { + var information = new BotInformation() + { + SunriseHour = 1, + SunsetHour = 21 + }; + + var success = time.TryGetSunMoonPhase(information.SunriseHour, information.SunsetHour, out var output); + var manualProcessingSuccess = TimeOnly.TryParse(time, out var timeOutput); + + Assert.True(success, "SunMoon Phase parsing failed"); + Assert.True(manualProcessingSuccess, "Manual Parsing SunMoon Phase failed"); + + var isBetween = timeOutput.Hour >= information.SunriseHour && timeOutput.Hour < information.SunsetHour; + var sunMoonPhase = isBetween ? "☀️" : "🌙"; + + if (isBetween) + Assert.True(sunMoonPhase.Equals("☀️"), "Time was between sunrise and sunset, but was not a Sun."); + + if (!isBetween) + Assert.True(sunMoonPhase.Equals("🌙"), "Time not was between sunrise and sunset, but was not a Moon."); + + Assert.Equal(sunMoonPhase, output); + } + } +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Extensions/DiscordClient.cs b/DiscordPlayerCountBot/Extensions/DiscordClient.cs new file mode 100644 index 0000000..5493dd2 --- /dev/null +++ b/DiscordPlayerCountBot/Extensions/DiscordClient.cs @@ -0,0 +1,44 @@ +namespace PlayerCountBot.Extensions +{ + public static class DiscordClient + { + public static async Task LoginAndStartAsync(this DiscordSocketClient client, string token, string address, bool shouldStart = true) + { + if (shouldStart) + { + await client.LoginAsync(TokenType.Bot, token); + await client.SetGameAsync($"Starting: {address}"); + await client.StartAsync(); + } + } + + public static async Task SetChannelName(this IDiscordClient socket, ulong? channelId, string gameStatus) + { + if (channelId == null) return; + + IGuildChannel channel = (IGuildChannel)await socket.GetChannelAsync(channelId.Value); + + if (channel is null) + { + throw new ArgumentException($"[Bot] - Invalid Channel Id: {channelId}, Channel was not found."); + } + + /* + * Keep in mind there is a massive rate limit on this call that is specific to discord, and not Discord.Net + * 2x per 10 minutes + * https://discord.com/developers/docs/topics/rate-limits + * https://www.reddit.com/r/Discord_Bots/comments/qzrl5h/channel_name_edit_rate_limit/ + */ + + if (channel != null) + { + if (channel is ITextChannel && channel is not IVoiceChannel) + { + gameStatus = gameStatus.Replace('/', '-').Replace(' ', '-').Replace(':', '-'); + } + + await channel.ModifyAsync(prop => prop.Name = gameStatus); + } + } + } +} diff --git a/DiscordPlayerCountBot/Extensions/StringExtensions.cs b/DiscordPlayerCountBot/Extensions/StringExtensions.cs new file mode 100644 index 0000000..02dd458 --- /dev/null +++ b/DiscordPlayerCountBot/Extensions/StringExtensions.cs @@ -0,0 +1,27 @@ +namespace PlayerCountBot.Extensions +{ + public static class StringExtensions + { + public static bool TryGetSunMoonPhase(this string timeString, int? sunriseHour, int? sunsetHour, out string output) + { + if (string.IsNullOrWhiteSpace(timeString)) + { + output = string.Empty; + return false; + } + + if (!TimeOnly.TryParse(timeString, out var time)) + { + output = string.Empty; + return false; + } + + var sunrise = sunriseHour ?? 6; + var sunset = sunsetHour ?? 20; + var hour = time.Hour; + + output = hour >= sunrise && hour < sunset ? "☀️" : "🌙"; + return !string.IsNullOrEmpty(output); + } + } +} \ No newline at end of file From 0305f8c3ffd3631040ce96bbe1138a7b758c4a27 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 14:54:59 -0700 Subject: [PATCH 10/17] Clean up and small fixes. --- DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs | 2 +- DiscordPlayerCountBot/Enums/DataProvider.cs | 4 ++-- DiscordPlayerCountBot/Providers/RconProvider.cs | 2 +- DiscordPlayerCountBot/Providers/SteamQueryProvider.cs | 2 +- .../Services/SteamQuery/ISteamQueryService.cs | 4 +--- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs index b4f6e8b..5cb3d21 100644 --- a/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs +++ b/DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using EnvironmentHelper = PlayerCountBot.Tests.Environment.EnvironmentHelper; +using EnvironmentHelper = DiscordPlayerCountBot.Tests.Environment.EnvironmentHelper; namespace DiscordPlayerCountBot.Tests; diff --git a/DiscordPlayerCountBot/Enums/DataProvider.cs b/DiscordPlayerCountBot/Enums/DataProvider.cs index 66f7585..8c7644f 100644 --- a/DiscordPlayerCountBot/Enums/DataProvider.cs +++ b/DiscordPlayerCountBot/Enums/DataProvider.cs @@ -7,7 +7,7 @@ public enum DataProvider SCUM, MINECRAFT, BATTLEMETRICS, - RCONClient, - SteamQuery + RCONCLIENT, + STEAMQUERY } } diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index 69a7160..178a14c 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -16,7 +16,7 @@ public RconProvider(RconService service) public override DataProvider GetRequiredProviderType() { - return DataProvider.RCONClient; + return DataProvider.RCONCLIENT; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs index 4d6d07c..19254e9 100644 --- a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -14,7 +14,7 @@ public SteamQueryProvider(SteamQueryService service) public override DataProvider GetRequiredProviderType() { - return DataProvider.SteamQuery; + return DataProvider.STEAMQUERY; } public async override Task GetServerInformation(BotInformation information, Dictionary applicationVariables) diff --git a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs index e78cff4..618cfc9 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs @@ -1,6 +1,4 @@ -using PlayerCountBot.Enums; - -namespace PlayerCountBot.Services.SteamQuery +namespace PlayerCountBot.Services.SteamQuery { internal interface ISteamQueryService { From f7efcf2894840db33878c57d052c6635ca8d7b07 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 14:57:55 -0700 Subject: [PATCH 11/17] Linting --- DiscordPlayerCountBot/Configuration/DockerConfiguration.cs | 2 +- DiscordPlayerCountBot/Extensions/StringExtensions.cs | 4 ++-- DiscordPlayerCountBot/Logging/LoggableClass.cs | 2 +- .../Providers/Base/IServerInformationProvider.cs | 2 +- DiscordPlayerCountBot/Providers/RconProvider.cs | 4 ++-- DiscordPlayerCountBot/Providers/SteamProvider.cs | 2 +- DiscordPlayerCountBot/Providers/SteamQueryProvider.cs | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs index c6a2a76..923ee32 100644 --- a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs @@ -6,7 +6,7 @@ public class DockerConfiguration : LoggableClass, IConfigurable { public IServiceProvider Services { get; set; } - public DockerConfiguration(IServiceProvider services) + public DockerConfiguration(IServiceProvider services) { Services = services; } diff --git a/DiscordPlayerCountBot/Extensions/StringExtensions.cs b/DiscordPlayerCountBot/Extensions/StringExtensions.cs index 52ebbf1..22e47f4 100644 --- a/DiscordPlayerCountBot/Extensions/StringExtensions.cs +++ b/DiscordPlayerCountBot/Extensions/StringExtensions.cs @@ -1,12 +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 PlayerCountBot.Extensions +namespace PlayerCountBot.Extensions { public static class StringExtensions { diff --git a/DiscordPlayerCountBot/Logging/LoggableClass.cs b/DiscordPlayerCountBot/Logging/LoggableClass.cs index b173936..9d74133 100644 --- a/DiscordPlayerCountBot/Logging/LoggableClass.cs +++ b/DiscordPlayerCountBot/Logging/LoggableClass.cs @@ -14,7 +14,7 @@ public void Warn(string message, string? id = null) Log.Warning(BuildLogMessage(id, GetType().Name, message)); } - public void Error(string message, string? id = null, Exception ? exception = null) + public void Error(string message, string? id = null, Exception? exception = null) { Log.Error(BuildLogMessage(id, GetType().Name, message), exception); } diff --git a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs index 954c9b9..d728094 100644 --- a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs @@ -2,7 +2,7 @@ { public interface IServerInformationProvider { - + bool WasLastExecutionAFailure { get; set; } Exception? LastException { get; set; } Task GetServerInformation(BotInformation information, Dictionary applicationVariables); diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index 178a14c..eae8924 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -9,7 +9,7 @@ public class RconProvider : ServerInformationProvider { private readonly RconService Service; - public RconProvider(RconService service) + public RconProvider(RconService service) { Service = service; } @@ -27,7 +27,7 @@ public override DataProvider GetRequiredProviderType() { 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}"); diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index 83709db..ce36b8c 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -47,7 +47,7 @@ public override DataProvider GetRequiredProviderType() if (!string.IsNullOrEmpty(serverTime)) { model.Time = serverTime; - + if (model.Time.TryGetSunMoonPhase(information.SunriseHour, information.SunsetHour, out var sunMoon)) { model.SunMoon = sunMoon; diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs index 19254e9..c923b67 100644 --- a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -30,7 +30,7 @@ public override DataProvider GetRequiredProviderType() } HandleLastException(information); - + return response; } catch (Exception e) From 638d381eb1ad063b0c2120cec46bd1fee04476be Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 15:02:58 -0700 Subject: [PATCH 12/17] lint? --- DiscordPlayerCountBot/Attributes/NameAttribute.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DiscordPlayerCountBot/Attributes/NameAttribute.cs b/DiscordPlayerCountBot/Attributes/NameAttribute.cs index ff21f84..cca5322 100644 --- a/DiscordPlayerCountBot/Attributes/NameAttribute.cs +++ b/DiscordPlayerCountBot/Attributes/NameAttribute.cs @@ -1,4 +1,5 @@ -namespace PlayerCountBot.Attributes + +namespace PlayerCountBot.Attributes { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] From a4f6a3f3a02d2922dc20934a39956497915b563e Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 15:03:11 -0700 Subject: [PATCH 13/17] Fix --- DiscordPlayerCountBot/Attributes/NameAttribute.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/DiscordPlayerCountBot/Attributes/NameAttribute.cs b/DiscordPlayerCountBot/Attributes/NameAttribute.cs index cca5322..fdb1373 100644 --- a/DiscordPlayerCountBot/Attributes/NameAttribute.cs +++ b/DiscordPlayerCountBot/Attributes/NameAttribute.cs @@ -1,5 +1,4 @@ - -namespace PlayerCountBot.Attributes +namespace PlayerCountBot.Attributes { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] @@ -12,4 +11,4 @@ public NameAttribute(string name) Name = name; } } -} +} \ No newline at end of file From d441cd38e4356b3af1c2c361910efe3b1e85f592 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 15:05:56 -0700 Subject: [PATCH 14/17] working.. --- DiscordPlayerCountBot/Attributes/NameAttribute.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/DiscordPlayerCountBot/Attributes/NameAttribute.cs b/DiscordPlayerCountBot/Attributes/NameAttribute.cs index fdb1373..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 { From fe67c53b4dc42b47d4e4027ba7929de24caa257f Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 15:09:27 -0700 Subject: [PATCH 15/17] remove extra lines --- .editorconfig | 1 + DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs | 2 +- DiscordPlayerCountBot/Attributes/AttributeHelper.cs | 2 +- DiscordPlayerCountBot/Bot/BotConfig.cs | 2 +- DiscordPlayerCountBot/Bot/BotInformation.cs | 2 +- DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs | 2 +- DiscordPlayerCountBot/Configuration/DockerConfiguration.cs | 2 +- DiscordPlayerCountBot/Configuration/EnvironmentHelper.cs | 2 +- DiscordPlayerCountBot/Configuration/StandardConfiguration.cs | 2 +- DiscordPlayerCountBot/Data/CFX/CFXPlayerInformation.cs | 2 +- DiscordPlayerCountBot/Data/CFX/CFXServer.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftDebugInfo.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftErrorInfo.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftInfo.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftMotd.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftPlayerInfo.cs | 2 +- DiscordPlayerCountBot/Data/Minecraft/MinecraftServer.cs | 2 +- DiscordPlayerCountBot/Data/Scum/ScumProviderResponse.cs | 2 +- DiscordPlayerCountBot/Data/Scum/ScumServerData.cs | 2 +- DiscordPlayerCountBot/Data/Steam/SteamApiResponseData.cs | 2 +- DiscordPlayerCountBot/Data/Steam/SteamServerListResponse.cs | 2 +- DiscordPlayerCountBot/Data/Steam/SteamServerListSubClass.cs | 2 +- DiscordPlayerCountBot/Enums/DataProvider.cs | 2 +- DiscordPlayerCountBot/Enums/EnumHelper.cs | 2 +- DiscordPlayerCountBot/Enums/HostEnviroment.cs | 2 +- DiscordPlayerCountBot/Enums/RconServiceType.cs | 2 +- DiscordPlayerCountBot/Exceptions/ConfigurationException.cs | 2 +- DiscordPlayerCountBot/Exceptions/ParsingException.cs | 2 +- .../Exceptions/SteamQueryAuthenticationException.cs | 2 +- DiscordPlayerCountBot/Extensions/DiscordClient.cs | 2 +- DiscordPlayerCountBot/Extensions/StringExtensions.cs | 2 +- DiscordPlayerCountBot/Http/AddressHelper.cs | 2 +- DiscordPlayerCountBot/Http/HttpExecuter.cs | 2 +- .../Http/QueryParams/Base/BattleMetricsParameters.cs | 2 +- .../Http/QueryParams/Base/IQueryParameterBuilder.cs | 2 +- .../Http/QueryParams/Base/QueryParameterBuilder.cs | 2 +- .../Http/QueryParams/ScumGetServerInformationQueryParams.cs | 2 +- .../Http/QueryParams/SteamGetServerListQueryParams.cs | 2 +- DiscordPlayerCountBot/Json/JsonHelper.cs | 2 +- DiscordPlayerCountBot/Logging/LoggableClass.cs | 2 +- .../Providers/Base/IServerInformationProvider.cs | 2 +- .../Providers/Base/ServerInformationProvider.cs | 2 +- DiscordPlayerCountBot/Providers/CFXProvider.cs | 2 +- DiscordPlayerCountBot/Providers/MinecraftProvider.cs | 2 +- DiscordPlayerCountBot/Providers/RconProvider.cs | 2 +- DiscordPlayerCountBot/Providers/ScumProvider.cs | 2 +- DiscordPlayerCountBot/Providers/SteamProvider.cs | 2 +- DiscordPlayerCountBot/Providers/SteamQueryProvider.cs | 2 +- DiscordPlayerCountBot/Services/BattleMetricsService.cs | 2 +- DiscordPlayerCountBot/Services/CFXService.cs | 2 +- DiscordPlayerCountBot/Services/IBattleMetricsService.cs | 2 +- DiscordPlayerCountBot/Services/ICFXService.cs | 2 +- DiscordPlayerCountBot/Services/IMinecraftService.cs | 2 +- DiscordPlayerCountBot/Services/IScumService.cs | 2 +- DiscordPlayerCountBot/Services/ISteamService.cs | 2 +- DiscordPlayerCountBot/Services/MinecraftService.cs | 2 +- DiscordPlayerCountBot/Services/Rcon/IRconService.cs | 2 +- .../Services/Rcon/Praser/ArkInformationParser.cs | 2 +- .../Services/Rcon/Praser/CSGOInformationParser.cs | 2 +- .../Services/Rcon/Praser/IRconInformationParser.cs | 2 +- .../Services/Rcon/Praser/MinecraftInformationParser.cs | 2 +- DiscordPlayerCountBot/Services/Rcon/RconService.cs | 2 +- DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs | 2 +- .../Rcon/ServiceInformation/ArkRconServiceInformation.cs | 2 +- .../Rcon/ServiceInformation/CSGORconServiceInformation.cs | 2 +- .../Rcon/ServiceInformation/MinecraftRconServiceInformation.cs | 2 +- DiscordPlayerCountBot/Services/ScumService.cs | 2 +- DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs | 2 +- DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs | 2 +- DiscordPlayerCountBot/Services/SteamService.cs | 2 +- DiscordPlayerCountBot/UpdateController.cs | 2 +- DiscordPlayerCountBot/ViewModels/BaseViewModel.cs | 2 +- DiscordPlayerCountBot/ViewModels/CFX/CFXViewModel.cs | 2 +- .../ViewModels/Minecraft/MinecraftViewModel.cs | 2 +- DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs | 2 +- 75 files changed, 75 insertions(+), 74 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2db502b..386d37a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -88,3 +88,4 @@ dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 indent_size = 4 end_of_line = crlf +insert_final_newline = false 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/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/Bot/BotConfig.cs b/DiscordPlayerCountBot/Bot/BotConfig.cs index e61eafa..c690378 100644 --- a/DiscordPlayerCountBot/Bot/BotConfig.cs +++ b/DiscordPlayerCountBot/Bot/BotConfig.cs @@ -21,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 66e55c0..8a25514 100644 --- a/DiscordPlayerCountBot/Bot/BotInformation.cs +++ b/DiscordPlayerCountBot/Bot/BotInformation.cs @@ -33,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 ae099cf..0f3ed50 100644 --- a/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs +++ b/DiscordPlayerCountBot/Configuration/Base/IConfigurable.cs @@ -5,4 +5,4 @@ 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 923ee32..de0d592 100644 --- a/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/DockerConfiguration.cs @@ -124,4 +124,4 @@ 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 f186260..a34936e 100644 --- a/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs +++ b/DiscordPlayerCountBot/Configuration/StandardConfiguration.cs @@ -50,4 +50,4 @@ 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/Enums/DataProvider.cs b/DiscordPlayerCountBot/Enums/DataProvider.cs index 8c7644f..2b82939 100644 --- a/DiscordPlayerCountBot/Enums/DataProvider.cs +++ b/DiscordPlayerCountBot/Enums/DataProvider.cs @@ -10,4 +10,4 @@ public enum DataProvider RCONCLIENT, STEAMQUERY } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enums/EnumHelper.cs b/DiscordPlayerCountBot/Enums/EnumHelper.cs index bc7eca2..fad23db 100644 --- a/DiscordPlayerCountBot/Enums/EnumHelper.cs +++ b/DiscordPlayerCountBot/Enums/EnumHelper.cs @@ -15,4 +15,4 @@ public static int GetActivityType(int value) } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enums/HostEnviroment.cs b/DiscordPlayerCountBot/Enums/HostEnviroment.cs index f1bdfde..9a983e4 100644 --- a/DiscordPlayerCountBot/Enums/HostEnviroment.cs +++ b/DiscordPlayerCountBot/Enums/HostEnviroment.cs @@ -5,4 +5,4 @@ public enum HostEnvironment STANDARD, DOCKER } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Enums/RconServiceType.cs b/DiscordPlayerCountBot/Enums/RconServiceType.cs index 9ca97a3..8b12c94 100644 --- a/DiscordPlayerCountBot/Enums/RconServiceType.cs +++ b/DiscordPlayerCountBot/Enums/RconServiceType.cs @@ -6,4 +6,4 @@ public enum RconServiceType Minecraft, Ark } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs index b475db9..3836164 100644 --- a/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs +++ b/DiscordPlayerCountBot/Exceptions/ConfigurationException.cs @@ -7,4 +7,4 @@ public ConfigurationException(string? message) : base(message) } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/ParsingException.cs b/DiscordPlayerCountBot/Exceptions/ParsingException.cs index a9ed82b..2460d94 100644 --- a/DiscordPlayerCountBot/Exceptions/ParsingException.cs +++ b/DiscordPlayerCountBot/Exceptions/ParsingException.cs @@ -7,4 +7,4 @@ public ParsingException(string? message) : base(message) } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs index e7b0286..7af2704 100644 --- a/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs +++ b/DiscordPlayerCountBot/Exceptions/SteamQueryAuthenticationException.cs @@ -6,4 +6,4 @@ public RconAuthenticationException(string? message) : base(message) { } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Extensions/DiscordClient.cs b/DiscordPlayerCountBot/Extensions/DiscordClient.cs index 5493dd2..501d471 100644 --- a/DiscordPlayerCountBot/Extensions/DiscordClient.cs +++ b/DiscordPlayerCountBot/Extensions/DiscordClient.cs @@ -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 22e47f4..cab05ce 100644 --- a/DiscordPlayerCountBot/Extensions/StringExtensions.cs +++ b/DiscordPlayerCountBot/Extensions/StringExtensions.cs @@ -32,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 9d74133..ae48aea 100644 --- a/DiscordPlayerCountBot/Logging/LoggableClass.cs +++ b/DiscordPlayerCountBot/Logging/LoggableClass.cs @@ -38,4 +38,4 @@ public string BuildLogMessage(string? id, string label, string msg) return message + $" - {msg}"; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs index d728094..5fcd5a8 100644 --- a/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/IServerInformationProvider.cs @@ -8,4 +8,4 @@ public interface IServerInformationProvider 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 41d5c7b..95362f7 100644 --- a/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs +++ b/DiscordPlayerCountBot/Providers/Base/ServerInformationProvider.cs @@ -77,4 +77,4 @@ protected void HandleException(Exception e, string? id = null) public abstract DataProvider GetRequiredProviderType(); } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/CFXProvider.cs b/DiscordPlayerCountBot/Providers/CFXProvider.cs index f3d9a39..7b5170e 100644 --- a/DiscordPlayerCountBot/Providers/CFXProvider.cs +++ b/DiscordPlayerCountBot/Providers/CFXProvider.cs @@ -47,4 +47,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/MinecraftProvider.cs b/DiscordPlayerCountBot/Providers/MinecraftProvider.cs index da5fe4e..973b882 100644 --- a/DiscordPlayerCountBot/Providers/MinecraftProvider.cs +++ b/DiscordPlayerCountBot/Providers/MinecraftProvider.cs @@ -41,4 +41,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/RconProvider.cs b/DiscordPlayerCountBot/Providers/RconProvider.cs index eae8924..c7e41ad 100644 --- a/DiscordPlayerCountBot/Providers/RconProvider.cs +++ b/DiscordPlayerCountBot/Providers/RconProvider.cs @@ -52,4 +52,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/ScumProvider.cs b/DiscordPlayerCountBot/Providers/ScumProvider.cs index a523e9a..bb6c8b7 100644 --- a/DiscordPlayerCountBot/Providers/ScumProvider.cs +++ b/DiscordPlayerCountBot/Providers/ScumProvider.cs @@ -46,4 +46,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index ce36b8c..bf450fa 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -63,4 +63,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs index c923b67..73a01d2 100644 --- a/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamQueryProvider.cs @@ -40,4 +40,4 @@ public override DataProvider GetRequiredProviderType() } } } -} +} \ 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 index 68a8fa9..a39995f 100644 --- a/DiscordPlayerCountBot/Services/Rcon/IRconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/IRconService.cs @@ -6,4 +6,4 @@ 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 index 73e6f0f..150461d 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs @@ -26,4 +26,4 @@ public BaseViewModel Parse(string message) }; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs index 10ab660..5a2d868 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/CSGOInformationParser.cs @@ -28,4 +28,4 @@ public BaseViewModel Parse(string message) }; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs index 95dd2a2..6d4fb2d 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/IRconInformationParser.cs @@ -4,4 +4,4 @@ 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 index 1d833cd..e112ce3 100644 --- a/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs +++ b/DiscordPlayerCountBot/Services/Rcon/Praser/MinecraftInformationParser.cs @@ -27,4 +27,4 @@ public BaseViewModel Parse(string message) }; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/RconService.cs b/DiscordPlayerCountBot/Services/Rcon/RconService.cs index db281d6..09ae98d 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconService.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconService.cs @@ -43,4 +43,4 @@ public async Task GetRconResponse(string address, int port, strin return viewModel; } } -} +} \ No newline at end of file diff --git a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs index 82f26d3..79763fd 100644 --- a/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/RconServiceInformation.cs @@ -9,4 +9,4 @@ public interface IRconServiceInformation 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 index b4ce406..ebc3df0 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/ArkRconServiceInformation.cs @@ -20,4 +20,4 @@ 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 index 99f3ee1..5d7ba19 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/CSGORconServiceInformation.cs @@ -20,4 +20,4 @@ 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 index 9270f6c..722aed9 100644 --- a/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs +++ b/DiscordPlayerCountBot/Services/Rcon/ServiceInformation/MinecraftRconServiceInformation.cs @@ -20,4 +20,4 @@ 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 index 618cfc9..7b9abd7 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/ISteamQueryService.cs @@ -4,4 +4,4 @@ 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 index 71f6bdb..add7916 100644 --- a/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs +++ b/DiscordPlayerCountBot/Services/SteamQuery/SteamQueryService.cs @@ -26,4 +26,4 @@ public async Task GetQueryResponse(string address, int port) 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 0196eb4..c1862f2 100644 --- a/DiscordPlayerCountBot/UpdateController.cs +++ b/DiscordPlayerCountBot/UpdateController.cs @@ -114,4 +114,4 @@ public async void OnProcessExit(object? sender, EventArgs e) } } } -} +} \ 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..f411f04 100644 --- a/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs @@ -7,4 +7,4 @@ public class SteamViewModel : BaseViewModel public string Time { get; set; } public string SunMoon { get; set; } } -} +} \ No newline at end of file From e5a3f678de2f470bcf3fe9cf9dd03c5a0dd1e5ed Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 15 Jan 2024 15:11:45 -0700 Subject: [PATCH 16/17] Change linting --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 386d37a..f6b6734 100644 --- a/.editorconfig +++ b/.editorconfig @@ -87,5 +87,5 @@ 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 = crlf +end_of_line = lf insert_final_newline = false From 257e5401ae9096b340aac8db3645ea4a26cb92a6 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 22 Jul 2024 16:07:58 -0600 Subject: [PATCH 17/17] Fix issue where null gametype from steam caused error --- DiscordPlayerCountBot/Providers/SteamProvider.cs | 5 ++++- DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DiscordPlayerCountBot/Providers/SteamProvider.cs b/DiscordPlayerCountBot/Providers/SteamProvider.cs index bf450fa..a122c7e 100644 --- a/DiscordPlayerCountBot/Providers/SteamProvider.cs +++ b/DiscordPlayerCountBot/Providers/SteamProvider.cs @@ -40,7 +40,10 @@ public override DataProvider GetRequiredProviderType() Map = response.map }; - var serverTime = model.Gametype.Split(",") + if (!model.Gametype?.Contains(',') ?? false) + return model; + + var serverTime = model.Gametype?.Split(",") .Where(entry => entry.Contains(':') && entry.Length == 5) .FirstOrDefault(); diff --git a/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs b/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs index f411f04..be70811 100644 --- a/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs +++ b/DiscordPlayerCountBot/ViewModels/Steam/SteamViewModel.cs @@ -3,7 +3,7 @@ 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; } }