Skip to content

Commit

Permalink
Merge pull request #123 from Galaxypedia-Wiki/generic-host
Browse files Browse the repository at this point in the history
  • Loading branch information
smallketchup82 authored Sep 5, 2024
2 parents 466b2f8 + 7f9050b commit 4849ed6
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .run/Build via docker for development.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build via docker for development" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<configuration default="false" name="Build via docker for development" type="docker-deploy" factoryName="dockerfile">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ketchupbot-updater" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace ketchupbot_framework.API;

public class MwClient
public class MediaWikiClient
{
protected static readonly HttpClient Client = new(new HttpClientHandler
{
Expand All @@ -26,7 +26,7 @@ public class MwClient
/// <param name="password">Password to use for logging in</param>
/// <param name="baseUrl"></param>
/// <exception cref="InvalidOperationException"></exception>
public MwClient(string? username = null, string? password = null, string baseUrl = "https://galaxypedia.org/api.php")
public MediaWikiClient(string? username = null, string? password = null, string baseUrl = "https://galaxypedia.org/api.php")
{
_baseUrl = baseUrl;

Expand Down Expand Up @@ -76,7 +76,7 @@ private async Task LogIn(string username, string password)
}

/// <summary>
/// Check whether the MwClient is currently logged in or not
/// Check whether the MediaWikiClient is currently logged in or not
/// </summary>
/// <returns></returns>
public async Task<bool> IsLoggedIn()
Expand Down
4 changes: 2 additions & 2 deletions ketchupbot-framework/ShipUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace ketchupbot_framework;
/// <summary>
/// Ship updater class to facilitate updating ship pages. You should pass this class to other classes via dependency injection.
/// </summary>
/// <param name="bot">The <see cref="MwClient"/> instance to use for interacting with the wiki</param>
/// <param name="bot">The <see cref="MediaWikiClient"/> instance to use for interacting with the wiki</param>
/// <param name="apiManager">The <see cref="ketchupbot_framework.API.ApiManager"/> instance to use for making API requests</param>
public partial class ShipUpdater(MwClient bot, ApiManager apiManager, bool dryRun = false)
public partial class ShipUpdater(MediaWikiClient bot, ApiManager apiManager, bool dryRun = false)
{
private static string GetShipName(string data) => GlobalConfiguration.ShipNameMap.GetValueOrDefault(data, data);

Expand Down
6 changes: 3 additions & 3 deletions ketchupbot-framework/TurretUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

namespace ketchupbot_framework;

public class TurretUpdater(MwClient mwClient, ApiManager apiManager)
public class TurretUpdater(MediaWikiClient mediaWikiClient, ApiManager apiManager)
{
public async Task UpdateTurrets(Dictionary<string, TurretData>? turretData = null)
{
turretData ??= await apiManager.GetTurretData();

if (turretData == null) throw new Exception("Failed to fetch turret data");

string turretPageWikitext = await mwClient.GetArticle("Turrets");
string turretPageWikitext = await mediaWikiClient.GetArticle("Turrets");

MatchCollection turretTables = WikiParser.ExtractTurretTables(turretPageWikitext);
string newTurretPageWikitext = turretPageWikitext;
Expand Down Expand Up @@ -53,7 +53,7 @@ public async Task UpdateTurrets(Dictionary<string, TurretData>? turretData = nul
if (newTurretPageWikitext == turretPageWikitext)
throw new Exception("Turrets page is up to date");

await mwClient.EditArticle("Turrets", newTurretPageWikitext, "Updating turrets");
await mediaWikiClient.EditArticle("Turrets", newTurretPageWikitext, "Updating turrets");
}

// TODO: Maybe in the future provide a way to update a single turret. This isn't really needed right now, so I'm not
Expand Down
4 changes: 2 additions & 2 deletions ketchupbot-updater-tests/ShipUpdater/UpdateAllShipsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ namespace ketchupbot_updater_tests.ShipUpdater;

public class UpdateAllShipsTests
{
// In order to begin writing tests for the UpdateAllShips method, I need to make MwClient available without being logged in. This should allow it to be Mock-able
// In order to begin writing tests for the UpdateAllShips method, I need to make MediaWikiClient available without being logged in. This should allow it to be Mock-able
/*
private static readonly Mock<MwClient> MockBot = new();
private static readonly Mock<MediaWikiClient> MockBot = new();
private static readonly Mock<ApiManager> MockApiManager = new();
private readonly ketchupbot_updater.ShipUpdater _shipUpdater = new(MockBot.Object, MockApiManager.Object);
Expand Down
102 changes: 72 additions & 30 deletions ketchupbot-updater/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using ketchupbot_framework.API;
using ketchupbot_updater.Jobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Quartz;
using Quartz.Impl;
using Serilog;
Expand All @@ -18,7 +20,7 @@ namespace ketchupbot_updater;
/// </summary>
public class Program
{
public static bool DryRun { get; private set; }
private static bool DryRun { get; set; }

private static async Task<int> Main(string[] args)
{
Expand Down Expand Up @@ -105,15 +107,17 @@ private static async Task<int> Main(string[] args)
Console.WriteLine(
$"\nketchupbot-updater | v{Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Development"} | {DateTime.Now}\n");

#region Configuration

IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(handler.ParseResult.GetValueForOption(secretsDirectoryOption) ?? AppContext.BaseDirectory)
.AddUserSecrets<Program>()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
HostApplicationBuilder applicationBuilder = Host.CreateApplicationBuilder(args);
applicationBuilder.Services.AddQuartz();
applicationBuilder.Services.AddQuartzHostedService();
applicationBuilder.Services.AddSerilog();
applicationBuilder.Configuration.AddUserSecrets<Program>()
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json",
true,
true);

IConfigurationRoot configuration = builder.Build();
#region Configuration

#if DEBUG
Log.Information("Running in development mode");
Expand All @@ -132,20 +136,57 @@ private static async Task<int> Main(string[] args)

#endregion

applicationBuilder.Services.AddSingleton<MediaWikiClient>(provider =>
{
provider.GetRequiredService<IConfiguration>();

return new MediaWikiClient(provider.GetRequiredService<IConfiguration>()["MWUSERNAME"] ??
throw new InvalidOperationException("MWUSERNAME not set"),
provider.GetRequiredService<IConfiguration>()["MWPASSWORD"] ??
throw new InvalidOperationException("MWPASSWORD not set"));
});

applicationBuilder.Services.AddSingleton<ApiManager>(provider =>
{
provider.GetRequiredService<IConfiguration>();
return new ApiManager(provider.GetRequiredService<IConfiguration>()["GIAPI_URL"] ??
throw new InvalidOperationException("GIAPI_URL not set"));
});

applicationBuilder.Services.AddSingleton<ShipUpdater>(provider => new ShipUpdater(
provider.GetRequiredService<MediaWikiClient>(),
provider.GetRequiredService<ApiManager>(),
DryRun));

bool turrets = handler.ParseResult.GetValueForOption(turretsOption);

if (turrets)
{
applicationBuilder.Services.AddSingleton<TurretUpdater>(provider => new TurretUpdater(
provider.GetRequiredService<MediaWikiClient>(),
provider.GetRequiredService<ApiManager>()));
}

IHost app = applicationBuilder.Build();

if (await app.Services.GetRequiredService<MediaWikiClient>().IsLoggedIn())
{
Log.Information("Logged in to MediaWiki");
}
else
{
Log.Error("Using MediaWiki anonymously. Editing will not be possible.");
}

#if !DEBUG
SentrySdk.Init(options =>
{
options.Dsn = configuration["SENTRY_DSN"];
options.Dsn = applicationBuilder.Configuration["SENTRY_DSN"];
options.AutoSessionTracking = true;
options.TracesSampleRate = 1.0;
options.ProfilesSampleRate = 1.0;
});
#endif
var mwClient = new MwClient(configuration["MWUSERNAME"] ?? throw new InvalidOperationException("MWUSERNAME not set"),
configuration["MWPASSWORD"] ?? throw new InvalidOperationException("MWPASSWORD not set"));
Log.Information("Logged into the Galaxypedia");
var apiManager = new ApiManager(configuration["GIAPI_URL"] ?? throw new InvalidOperationException("GIAPI_URL not set"));
var shipUpdater = new ShipUpdater(mwClient, apiManager, DryRun);

#region Scheduling Logic

Expand All @@ -170,8 +211,8 @@ private static async Task<int> Main(string[] args)
.WithIdentity("massUpdateJob", "group1")
.Build();

massUpdateJob.JobDataMap.Put("shipUpdater", shipUpdater);
massUpdateJob.JobDataMap.Put("healthChecksUrl", configuration["HEALTHCHECK_URL"]);
massUpdateJob.JobDataMap.Put("shipUpdater", app.Services.GetRequiredService<ShipUpdater>());
massUpdateJob.JobDataMap.Put("healthCheckUrl", applicationBuilder.Configuration["HEALTHCHECK_URL"]);

ITrigger massUpdateTrigger = TriggerBuilder.Create()
.WithIdentity("massUpdateTrigger", "group1")
Expand All @@ -181,7 +222,8 @@ private static async Task<int> Main(string[] args)
.Build();

await scheduler.ScheduleJob(massUpdateJob, massUpdateTrigger);
Console.WriteLine($"Scheduled ship mass update job for {massUpdateTrigger.GetNextFireTimeUtc()?.ToLocalTime()}");
Console.WriteLine(
$"Scheduled ship mass update job for {massUpdateTrigger.GetNextFireTimeUtc()?.ToLocalTime()}");
Console.WriteLine("Running a mass update job now...");
await scheduler.TriggerJob(new JobKey("massUpdateJob", "group1"));
}
Expand All @@ -192,7 +234,9 @@ private static async Task<int> Main(string[] args)
.WithIdentity("turretUpdateJob", "group1")
.Build();

turretUpdateJob.JobDataMap.Put("turretUpdater", new TurretUpdater(mwClient, apiManager));
turretUpdateJob.JobDataMap.Put("turretUpdater",
new TurretUpdater(app.Services.GetRequiredService<MediaWikiClient>(),
app.Services.GetRequiredService<ApiManager>()));

ITrigger turretUpdateTrigger = TriggerBuilder.Create()
.WithIdentity("turretUpdateTrigger", "group1")
Expand All @@ -205,37 +249,35 @@ private static async Task<int> Main(string[] args)
Console.WriteLine("Scheduled turret update job");
}

// Keep application running until it's manually stopped. The scheduler will never stop by itself.
await Task.Delay(-1);
await app.RunAsync();
}

#endregion

#region Ship Option Handler

string[]? shipsOptionValue = handler.ParseResult.GetValueForOption(shipsOption);
if (shipsOptionValue != null && shipsOptionValue.First() != "none" && shipScheduleOptionValue == null)
string[] shipsOptionValue = handler.ParseResult.GetValueForOption(shipsOption)!;
if (shipsOptionValue.First() != "none" && shipScheduleOptionValue == null)
{
if (shipsOptionValue.First() == "all")
await shipUpdater.UpdateAllShips();
{
await app.Services.GetRequiredService<ShipUpdater>().UpdateAllShips();
}
else
{
await shipUpdater.MassUpdateShips(shipsOptionValue.ToList());
await app.Services.GetRequiredService<ShipUpdater>().MassUpdateShips(shipsOptionValue.ToList());
}
}

#endregion

#region Turret Option Handler

bool turrets = handler.ParseResult.GetValueForOption(turretsOption);
if (turrets) await new TurretUpdater(mwClient, apiManager).UpdateTurrets();
if (turrets && turretScheduleOptionValue == null) await app.Services.GetRequiredService<TurretUpdater>().UpdateTurrets();

#endregion

await Log.CloseAndFlushAsync();
});

return await rootCommand.InvokeAsync(args);
}
}
}
5 changes: 4 additions & 1 deletion ketchupbot-updater/ketchupbot-updater.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
This project is a standalone application that builds upon ketchupbot-framework to provide an intuitive interface to ketchupbot-updater</Description>
<Copyright>https://creativecommons.org/licenses/by/4.0/</Copyright>
<PackageProjectUrl>https://github.com/smallketchup82/ketchupbot-updater</PackageProjectUrl>
<PackageLicenseUrl></PackageLicenseUrl>
<RepositoryUrl>https://github.com/smallketchup82/ketchupbot-updater</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<UserSecretsId>58dcf627-f360-418d-b50b-6e77cdf952b6</UserSecretsId>
Expand All @@ -28,10 +27,14 @@ This project is a standalone application that builds upon ketchupbot-framework t
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.13.0" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.13.0" />
<PackageReference Include="Quartz" Version="3.13.0" />
<PackageReference Include="Sentry" Version="4.10.2" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
Expand Down

0 comments on commit 4849ed6

Please sign in to comment.