Skip to content

Commit

Permalink
Merge pull request #105 from Zechiax/v3base
Browse files Browse the repository at this point in the history
V3 Upgrade
  • Loading branch information
Zechiax authored Dec 24, 2023
2 parents d1b3842 + fd6ccbf commit 37003cb
Show file tree
Hide file tree
Showing 28 changed files with 1,005 additions and 438 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
53 changes: 53 additions & 0 deletions Asterion.Test/SplitExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Asterion.Extensions;

namespace Asterion.Test;

[TestFixture]
public class SplitExtensionTests
{
[Test]
public void TestSliceOnEmptyArray()
{
var array = Array.Empty<int>();

var result = array.Split(10).ToArray();

Assert.That(result, Is.Empty);
}

[Test]
public void TestSplitEvenArray()
{
int[] numbers = {1, 2, 3, 4, 5, 6};
var segments = numbers.Split(2).ToArray();

Assert.Multiple(() =>
{
Assert.That(segments, Has.Length.EqualTo(3));
Assert.That(segments[0], Has.Count.EqualTo(2));
Assert.That(segments[0].Array, Is.Not.Null);
Assert.That(segments[0].Array![segments[0].Offset], Is.EqualTo(1));
Assert.That(segments[0].Array![segments[0].Offset + 1], Is.EqualTo(2));
});
}

[Test]
public void TestSplitOddArray()
{
int[] numbers = {1, 2, 3, 4, 5, 6, 7};
var segments = numbers.Split(3).ToArray();

Assert.Multiple(() =>
{
Assert.That(segments, Has.Length.EqualTo(3));
Assert.That(segments[0], Has.Count.EqualTo(3));
Assert.That(segments[0].Array, Is.Not.Null);
Assert.That(segments[0].Array![segments[0].Offset], Is.EqualTo(1));
Assert.That(segments[0].Array![segments[0].Offset + 1], Is.EqualTo(2));
Assert.That(segments[0].Array![segments[0].Offset + 2], Is.EqualTo(3));

// Last segment should have 1 element
Assert.That(segments[2], Has.Count.EqualTo(1));
});
}
}
40 changes: 34 additions & 6 deletions Asterion/Asterion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Modrinth;
using Quartz;
using Serilog;
using RunMode = Discord.Commands.RunMode;

Expand All @@ -19,9 +20,12 @@ namespace Asterion;
public class Asterion
{
private readonly IConfiguration _config;
private int _shardId;

public Asterion()
public Asterion(int shardId)
{
_shardId = shardId;

_config = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("config.json", false, true)
Expand Down Expand Up @@ -78,12 +82,25 @@ public async Task MainAsync()
await client.LoginAsync(TokenType.Bot, _config.GetValue<string>("token"));
await client.StartAsync();

// We start the stats service after the client has been logged in
// so that we can get the correct guild count
services.GetRequiredService<IBotStatsService>().Initialize();

// We start the scheduler after the client has been logged in
// so that we can get the correct guild count
var scheduler = await services.GetRequiredService<ISchedulerFactory>().GetScheduler();
await scheduler.Start();

// Disconnect from Discord when pressing Ctrl+C
Console.CancelKeyPress += (_, args) =>
{
args.Cancel = true;

logger.LogInformation("{Key} pressed, exiting bot", args.SpecialKey);

logger.LogInformation("Stopping the scheduler");
scheduler.Shutdown(true).Wait();

logger.LogInformation("Logging out from Discord");
client.LogoutAsync().Wait();
logger.LogInformation("Stopping the client");
Expand All @@ -94,11 +111,7 @@ public async Task MainAsync()

args.Cancel = false;
};

// We start the stats service after the client has been logged in
// so that we can get the correct guild count
services.GetRequiredService<IBotStatsService>().Initialize();


await Task.Delay(Timeout.Infinite);
}

Expand Down Expand Up @@ -140,9 +153,24 @@ private ServiceProvider ConfigureServices()
.AddHttpClient()
.AddDbContext<DataContext>()
.AddSingleton<IBotStatsService, BotStatsService>()
.AddSingleton<ILocalizationService, LocalizationService>()
.AddMemoryCache()
.AddLogging(configure => configure.AddSerilog(dispose: true));

services.AddQuartz(q =>
{
q.UseInMemoryStore();
});
services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});

services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});

if (IsDebug())
services.Configure<LoggerFilterOptions>(options => options.MinLevel = LogLevel.Debug);
else
Expand Down
19 changes: 19 additions & 0 deletions Asterion/Asterion.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageReference Include="Discord.Net" Version="3.13.0" />
<PackageReference Include="Discord.Net.Core" Version="3.13.0" />
<PackageReference Include="Fergun.Interactive" Version="1.7.3" />
<PackageReference Include="Figgle" Version="0.5.1" />
<PackageReference Include="Html2Markdown" Version="6.2.0.3" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="LiveChartsCore.SkiaSharpView" Version="2.0.0-rc2" />
Expand All @@ -32,6 +33,9 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="Modrinth.Net" Version="3.4.0" />
<PackageReference Include="Quartz" Version="3.8.0" />
<PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.8.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
Expand All @@ -40,4 +44,19 @@
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Resources\Responses\responses.en-US.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>responses.en-US.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
<Compile Update="Resources\Responses\responses.en-US.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>responses.en-US.resx</DependentUpon>
</Compile>
</ItemGroup>

</Project>
58 changes: 58 additions & 0 deletions Asterion/Common/AsterionInteractionModuleBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Globalization;
using Asterion.EmbedBuilders;
using Asterion.Interfaces;
using Discord;
using Discord.Interactions;
using Color = Discord.Color;

namespace Asterion.Common;

public class AsterionInteractionModuleBase : InteractionModuleBase<SocketInteractionContext>
{
protected CultureInfo? CommandCultureInfo { get; set; }
protected ILocalizationService LocalizationService { get; }
public AsterionInteractionModuleBase(ILocalizationService localizationService)
{
LocalizationService = localizationService;
}

protected async Task FollowupWithSearchResultErrorAsync<T>(Services.Modrinth.SearchResult<T> status)
{
if (status.Success)
{
throw new ArgumentException("SearchResult was successful, but was expected to be an error");
}

string title = LocalizationService.Get("Modrinth_Search_Unsuccessful", CommandCultureInfo);

string? description;
switch (status.SearchStatus)
{
case Services.Modrinth.SearchStatus.ApiDown:
description = LocalizationService.Get("Error_ModrinthApiUnavailable", CommandCultureInfo);
title += ". " + LocalizationService.Get("Error_TryAgainLater", CommandCultureInfo);
break;
case Services.Modrinth.SearchStatus.NoResult:
description = LocalizationService.Get("Modrinth_Search_NoResult_WithQuery", CommandCultureInfo, new object[] {status.Query});
break;
case Services.Modrinth.SearchStatus.UnknownError:
description = LocalizationService.Get("Error_Unknown", CommandCultureInfo);
title += ". " + LocalizationService.Get("Error_TryAgainLater", CommandCultureInfo);
break;
default:
throw new ArgumentOutOfRangeException();
}

var embed = CommonEmbedBuilder.GetErrorEmbedBuilder(title, description).Build();

await FollowupAsync(embeds: new[] {embed});
}

public override void BeforeExecute(ICommandInfo cmd)
{
// We currently set US culture for all commands
CommandCultureInfo = CultureInfo.GetCultureInfo("en-US");

base.BeforeExecute(cmd);
}
}
48 changes: 48 additions & 0 deletions Asterion/EmbedBuilders/CommonEmbedBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Discord;
using Color = Discord.Color;

namespace Asterion.EmbedBuilders;

public static class CommonEmbedBuilder
{
private static Color _errorColor = Color.Red;
private static Color _warningColor = Color.Orange;
private static Color _infoColor = Color.Blue;
private static Color _successColor = Color.Green;

public static EmbedBuilder GetSuccessEmbedBuilder(string title, string description)
{
return new EmbedBuilder()
.WithTitle(title)
.WithDescription(description)
.WithColor(_successColor)
.WithCurrentTimestamp();
}

public static EmbedBuilder GetErrorEmbedBuilder(string title, string description)
{
return new EmbedBuilder()
.WithTitle(title)
.WithDescription(description)
.WithColor(_errorColor)
.WithCurrentTimestamp();
}

public static EmbedBuilder GetInfoEmbedBuilder(string title, string description)
{
return new EmbedBuilder()
.WithTitle(title)
.WithDescription(description)
.WithColor(_infoColor)
.WithCurrentTimestamp();
}

public static EmbedBuilder GetWarningEmbedBuilder(string title, string description)
{
return new EmbedBuilder()
.WithTitle(title)
.WithDescription(description)
.WithColor(_warningColor)
.WithCurrentTimestamp();
}
}
2 changes: 1 addition & 1 deletion Asterion/EmbedBuilders/ListEmbedBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static List<Embed> CreateListEmbed(IList<ModrinthEntry> entries)

var currentEntry = 0;

var numberOfEmbeds = entries.Count / 25 + 1;
var numberOfEmbeds = entries.Count / 25 + (entries.Count % 25 > 0 ? 1 : 0);

for (var i = 0; i < numberOfEmbeds; i++)
{
Expand Down
16 changes: 16 additions & 0 deletions Asterion/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Asterion.Extensions;

public static class ArrayExtensions
{
public static IEnumerable<ArraySegment<T>> Split<T>(this T[] array, int blockSize)
{
var offset = 0;
while (offset < array.Length)
{
var remaining = array.Length - offset;
var blockSizeToUse = Math.Min(remaining, blockSize);
yield return new ArraySegment<T>(array, offset, blockSizeToUse);
offset += blockSizeToUse;
}
}
}
13 changes: 13 additions & 0 deletions Asterion/Interfaces/ILocalizationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Globalization;
using Discord.Interactions;
using Microsoft.Extensions.Localization;

namespace Asterion.Interfaces;

public interface ILocalizationService
{
LocalizedString Get(string key);
LocalizedString Get(string key, CultureInfo? cultureInfo);
LocalizedString Get(string key, params object[] parameters);
LocalizedString Get(string key, CultureInfo? cultureInfo, params object[] parameters);
}
15 changes: 11 additions & 4 deletions Asterion/Modules/BotCommands.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
using Discord.Interactions;
using Asterion.Common;
using Asterion.Interfaces;
using Discord.Interactions;
using Discord.WebSocket;

namespace Asterion.Modules;

public class BotCommands : InteractionModuleBase<SocketInteractionContext>
public class BotCommands : AsterionInteractionModuleBase
{
#if DEBUG
[SlashCommand("ping", "Pings the bot", runMode: RunMode.Async)]
public async Task Ping()
{
await RespondAsync($"Pong :ping_pong: It took me {Context.Client.Latency}ms to respond to you",
ephemeral: true);
await RespondAsync($"Pong! Latency: {Context.Client.Latency}ms");
}
#endif
public BotCommands(ILocalizationService localizationService) : base(localizationService)
{
}
}
5 changes: 3 additions & 2 deletions Asterion/Modules/BotManagement.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using Asterion.AutocompleteHandlers;
using Asterion.Common;
using Asterion.Interfaces;
using Discord.Interactions;
using Microsoft.Extensions.DependencyInjection;

namespace Asterion.Modules;

[RequireOwner]
public class BotManagement : InteractionModuleBase<SocketInteractionContext>
public class BotManagement : AsterionInteractionModuleBase
{
private readonly IDataService _dataService;

public BotManagement(IServiceProvider serviceProvider)
public BotManagement(IServiceProvider serviceProvider, ILocalizationService localizationService) : base(localizationService)
{
_dataService = serviceProvider.GetRequiredService<IDataService>();
}
Expand Down
Loading

0 comments on commit 37003cb

Please sign in to comment.