Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Steam Query, and RCON Support #66

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions DiscordPlayerCountBot/Bot/Bot.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
namespace PlayerCountBot

Check failure on line 1 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L1

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
{

Check failure on line 2 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L2

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

[Name("Bot")]

Check failure on line 4 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L4

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
public class Bot : LoggableClass

Check failure on line 5 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L5

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
{

Check failure on line 6 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L6

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
public DiscordSocketClient DiscordClient { get; set; }

Check failure on line 7 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L7

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
public Dictionary<int, IServerInformationProvider> DataProviders { get; set; } = new();

Check failure on line 8 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L8

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
public Dictionary<string, string> ApplicationTokens { get; set; } = new();

Check failure on line 9 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L9

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

public Bot(BotInformation info, Dictionary<string, string> applicationTokens) : base(info)

Check failure on line 11 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L11

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
{

Check failure on line 12 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L12

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
if (info is null) throw new ArgumentNullException(nameof(info));

Check failure on line 13 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L13

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
if (applicationTokens is null) throw new ArgumentException(nameof(applicationTokens));

Check failure on line 14 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L14

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

ApplicationTokens = applicationTokens;

Check failure on line 16 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L16

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

DiscordClient = new DiscordSocketClient(new DiscordSocketConfig()

Check failure on line 18 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L18

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
{

Check failure on line 19 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L19

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
HandlerTimeout = null

Check failure on line 20 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L20

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
});

Check failure on line 21 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L21

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

InitDataProviders();

Check failure on line 23 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L23

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
}

Check failure on line 24 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L24

ENDOFLINE: Fix end of line marker. Replace 2 characters with '\r\n\r\n'.

public void InitDataProviders()

Check failure on line 26 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L26

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
{

Check failure on line 27 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L27

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
DataProviders.Add((int)DataProvider.STEAM, new SteamProvider(Information!));

Check failure on line 28 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L28

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
DataProviders.Add((int)DataProvider.CFX, new CFXProvider(Information!));

Check failure on line 29 in DiscordPlayerCountBot/Bot/Bot.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Bot/Bot.cs#L29

ENDOFLINE: Fix end of line marker. Replace 1 characters with '\r\n'.
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)
Expand Down
1 change: 1 addition & 0 deletions DiscordPlayerCountBot/Bot/BotInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, ushort> GetAddressAndPort()
{
Expand Down
3 changes: 2 additions & 1 deletion DiscordPlayerCountBot/DiscordPlayerCountBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SteamQueryNet" Version="1.0.6" />
<PackageReference Include="RconSharp" Version="2.0.1" />
<PackageReference Include="SteamServerQuery.NET" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<None Remove="README.MD" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
namespace PlayerCountBot.Enum
namespace PlayerCountBot.Enums
{
public enum DataProvider
{
STEAM,
CFX,
SCUM,
MINECRAFT,
BATTLEMETRICS
BATTLEMETRICS,
RCONClient,
SteamQuery
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace PlayerCountBot.Enum
namespace PlayerCountBot.Enums
{
public static class EnumHelper
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace PlayerCountBot.Enum
namespace PlayerCountBot.Enums
{
public enum HostEnvironment
{
Expand Down
9 changes: 9 additions & 0 deletions DiscordPlayerCountBot/Enums/RconServiceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DiscordPlayerCountBot.Enums
{
public enum RconServiceType
{
CSGO,
Minecraft,
Ark
}
}
10 changes: 10 additions & 0 deletions DiscordPlayerCountBot/Exceptions/ConfigurationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace DiscordPlayerCountBot.Exceptions
{
internal class ConfigurationException : Exception
{
public ConfigurationException(string? message) : base(message)
{

}
}
}
10 changes: 10 additions & 0 deletions DiscordPlayerCountBot/Exceptions/ParsingException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace DiscordPlayerCountBot.Exceptions
{
public class ParsingException : Exception
{
public ParsingException(string? message) : base(message)
{

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DiscordPlayerCountBot.Exceptions
{
internal class RconAuthenticationException : Exception
{
public RconAuthenticationException(string? message) : base(message)
{
}
}
}
2 changes: 1 addition & 1 deletion DiscordPlayerCountBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using SteamServerQuery;
using System.Net;
using System.Net.Http;

namespace PlayerCountBot.Providers.Base
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
49 changes: 49 additions & 0 deletions DiscordPlayerCountBot/Providers/RconProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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<BaseViewModel?> GetServerInformation(BotInformation information, Dictionary<string, string> applicationVariables)
{
var values = $"Valid Values: {string.Join(",", Enum.GetNames<RconServiceType>())}";

if (information.RconServiceName == null)
{
throw new ConfigurationException($"Bot: {information.Name} must have RconServiceName specified in it's config. {values}");
}

var service = new RconService();

Check failure on line 23 in DiscordPlayerCountBot/Providers/RconProvider.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Providers/RconProvider.cs#L23

WHITESPACE: Fix whitespace formatting. Replace 26 characters with '\r\n\r\n\s\s\s\s\s\s\s\s\s\s\s\s'.

if (!Enum.TryParse<RconServiceType>(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;
}
}
}
}
37 changes: 37 additions & 0 deletions DiscordPlayerCountBot/Providers/SteamQueryProvider.cs
Original file line number Diff line number Diff line change
@@ -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<BaseViewModel?> GetServerInformation(BotInformation information, Dictionary<string, string> 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);

Check failure on line 26 in DiscordPlayerCountBot/Providers/SteamQueryProvider.cs

View workflow job for this annotation

GitHub Actions / dotnet_format

DiscordPlayerCountBot/Providers/SteamQueryProvider.cs#L26

WHITESPACE: Fix whitespace formatting. Replace 34 characters with '\r\n\r\n\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s'.

return response;
}
catch (Exception e)
{
HandleException(e);
return null;
}
}
}
}
9 changes: 9 additions & 0 deletions DiscordPlayerCountBot/Services/Rcon/IRconService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using DiscordPlayerCountBot.Enums;

namespace DiscordPlayerCountBot.Services
{
public interface IRconService
{
public Task<BaseViewModel> GetRconResponse(string address, int port, string authorizationToken, RconServiceType serviceType);
}
}
29 changes: 29 additions & 0 deletions DiscordPlayerCountBot/Services/Rcon/Praser/ArkInformationParser.cs
Original file line number Diff line number Diff line change
@@ -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
};
}
}
}
Original file line number Diff line number Diff line change
@@ -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
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DiscordPlayerCountBot.Services.Praser
{
public interface IRconInformationParser
{
public BaseViewModel Parse(string message);
}
}
Loading
Loading