Skip to content

Commit

Permalink
Merge pull request #70 from GravityWolfNotAmused/dev
Browse files Browse the repository at this point in the history
Latest <- Dev
  • Loading branch information
GravityWolfNotAmused committed Jul 22, 2024
2 parents b5bfe10 + 7873c22 commit 6f4bf72
Show file tree
Hide file tree
Showing 86 changed files with 954 additions and 283 deletions.
87 changes: 87 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,90 @@

# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = suggestion
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_indent_labels = one_less_than_current
csharp_space_around_binary_operators = before_and_after

[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = lf
insert_final_newline = false
31 changes: 23 additions & 8 deletions DiscordPlayerCountBot.Tests/DockerConfigurationTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using EnvironmentHelper = PlayerCountBot.Tests.Environment.EnvironmentHelper;
using Microsoft.Extensions.DependencyInjection;
using EnvironmentHelper = DiscordPlayerCountBot.Tests.Environment.EnvironmentHelper;

namespace PlayerCountBot.Tests;
namespace DiscordPlayerCountBot.Tests;

[Collection("Configuration Test Suite")]
public class DockerConfigurationTests
Expand All @@ -15,7 +15,10 @@ public async Task DockerConfigurationTestWithAllData()
var bots = new Dictionary<string, Bot>();
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;
Expand All @@ -35,7 +38,10 @@ public async Task DockerConfigurationWithDuplicateAddresses()
{
EnvironmentHelper.SetTestEnvironmentWithDuplicateAddresses();

var dockerConfiguration = new DockerConfiguration();
var services = new ServiceCollection()
.BuildServiceProvider();

var dockerConfiguration = new DockerConfiguration(services);
var configuration = await dockerConfiguration.Configure(false);

EnvironmentHelper.ClearTestEnvironmentVariables();
Expand All @@ -57,7 +63,10 @@ public async Task DockerConfigurationTestWithoutBattleMetrics()
var bots = new Dictionary<string, Bot>();
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;
Expand All @@ -80,7 +89,10 @@ public async Task DockerConfigurationTestWithoutApplicationVariables()
var bots = new Dictionary<string, Bot>();
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;
Expand All @@ -101,7 +113,10 @@ public async Task DockerConfigurationTestWithoutSteam()
var bots = new Dictionary<string, Bot>();
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;
Expand Down
4 changes: 2 additions & 2 deletions DiscordPlayerCountBot.Tests/Environment/EnivronmentHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace PlayerCountBot.Tests.Environment
namespace DiscordPlayerCountBot.Tests.Environment
{
public static class EnvironmentHelper
{
Expand Down Expand Up @@ -83,4 +83,4 @@ public static void ClearTestEnvironmentVariables()
System.Environment.SetEnvironmentVariable("BOT_APPLICATION_VARIABLES", null);
}
}
}
}
4 changes: 1 addition & 3 deletions DiscordPlayerCountBot.Tests/JsonTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using PlayerCountBot.Json;

namespace DiscordPlayerCountBot.Tests;
namespace PlayerCountBot.Tests;

[Collection("Json Serialization Test Suite")]
public class JsonTests
Expand Down
78 changes: 78 additions & 0 deletions DiscordPlayerCountBot.Tests/SunMoonTagTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
5 changes: 4 additions & 1 deletion DiscordPlayerCountBot.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion DiscordPlayerCountBot/Attributes/AttributeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ public static string GetNameFromAttribute(object obj)
return label;
}
}
}
}
3 changes: 1 addition & 2 deletions DiscordPlayerCountBot/Attributes/NameAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace PlayerCountBot.Attributes
{

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class NameAttribute : Attribute
{
Expand All @@ -11,4 +10,4 @@ public NameAttribute(string name)
Name = name;
}
}
}
}
41 changes: 22 additions & 19 deletions DiscordPlayerCountBot/Bot/Bot.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
namespace PlayerCountBot
using PlayerCountBot.Extensions;
using Microsoft.Extensions.DependencyInjection;

namespace PlayerCountBot
{

[Name("Bot")]
public class Bot : LoggableClass
{
public DiscordSocketClient DiscordClient { get; set; }
public Dictionary<int, IServerInformationProvider> DataProviders { get; set; } = new();
public Dictionary<string, string> ApplicationTokens { get; set; } = new();
public readonly DiscordSocketClient DiscordClient;
public readonly BotInformation Information;
public readonly Dictionary<DataProvider, IServerInformationProvider> DataProviders = new();
public readonly Dictionary<string, string> ApplicationTokens = new();

public Bot(BotInformation info, Dictionary<string, string> applicationTokens) : base(info)
public Bot(BotInformation info, Dictionary<string, string> 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<IServerInformationProvider>()
.ToDictionary(value => value.GetRequiredProviderType());
}

public async Task StartAsync(bool shouldStart)
Expand All @@ -38,7 +36,7 @@ public async Task StartAsync(bool shouldStart)
Information.Address = await AddressHelper.ResolveAddress(Information.Address);
}

Info($"Loaded {Information.Name} at address and port: {Information.Address}, {Information.ProviderType}");
Info($"Loaded {Information.Name} ({Information.Id}) at address and port: {Information.Address}, {(DataProvider)Information.ProviderType}");
await DiscordClient.LoginAndStartAsync(Information.Token, Information.Address, shouldStart);
}

Expand All @@ -49,20 +47,25 @@ public async Task StopAsync()

public async Task UpdateAsync()
{
var dataProviderType = EnumHelper.GetDataProvider(Information!.ProviderType);
var dataProviderInt = EnumHelper.GetDataProvider(Information!.ProviderType);

if (dataProviderType != Information.ProviderType)
if (dataProviderInt != Information.ProviderType)
{
Warn($"Config for bot at address: {Information.Address} has an invalid provider type: {Information.ProviderType}");
Warn($"Config for bot at address: {Information.Address} has an invalid provider type: {Information.ProviderType}", Information.Id.ToString());
}

var activityInteger = EnumHelper.GetActivityType(Information.Status);

if (Information.Status != activityInteger)
{
Warn($"Config for bot at address: {Information.Address} has an invalid activity type: {Information.Status}");
Warn($"Config for bot at address: {Information.Address} has an invalid activity type: {Information.Status}", Information.Id.ToString());
}

var dataProviderType = (DataProvider)dataProviderInt;

if (!DataProviders.ContainsKey(dataProviderType))
throw new Exception($"Missing Data Provider for Type: {dataProviderType}");

var dataProvider = DataProviders[dataProviderType];
var serverInformation = await dataProvider.GetServerInformation(Information, ApplicationTokens);

Expand Down
3 changes: 1 addition & 2 deletions DiscordPlayerCountBot/Bot/BotConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ public class BotConfig
public Dictionary<string, string> ApplicationTokens { get; set; } = new();
public void CreateDefaults()
{

ServerInformation.Add(new()
{
Name = "TestBot",
Expand All @@ -22,4 +21,4 @@ public void CreateDefaults()
ApplicationTokens.Add("BattleMetricsKey", "Here");
}
}
}
}
Loading

0 comments on commit 6f4bf72

Please sign in to comment.