-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
50 changed files
with
3,811 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
| ||
namespace InsightLogParser.Client; | ||
|
||
internal class Beeper | ||
{ | ||
private readonly Configuration _configuration; | ||
|
||
public static int MinFrequency => 37; | ||
public static int MaxFrequency => 32767; | ||
public static int MinDuration => 50; | ||
public static int MaxDuration => 1000; | ||
public static int MinBeeps => 1; | ||
public static int MaxBeeps => 10; | ||
public static int MinDelay => 50; | ||
public static int MaxDelay => 1000; | ||
|
||
public Beeper(Configuration configuration) | ||
{ | ||
_configuration = configuration; | ||
} | ||
|
||
public void BeepForNewParse() | ||
{ | ||
if (!_configuration.BeepOnNewParse) return; | ||
DoTheBeep(_configuration.NewParseBeepFrequency, _configuration.NewParseBeepDuration); | ||
} | ||
|
||
public void BeepForKnownParse() | ||
{ | ||
if (!_configuration.BeepOnKnownParse) return; | ||
DoTheBeep(_configuration.KnownParseBeepFrequency, _configuration.KnownParseBeepDuration); | ||
} | ||
|
||
public void BeepForOpeningSolvedPuzzle() | ||
{ | ||
if (!_configuration.BeepOnOpeningSolvedPuzzle) return; | ||
DoTheBeep(_configuration.OpenSolvedPuzzleBeepFrequency, _configuration.OpenSolvedPuzzleBeepDuration); | ||
} | ||
|
||
public async Task BeepForAttentionAsync() | ||
{ | ||
if (!_configuration.BeepForAttention) return; | ||
var count = _configuration.BeepForAttentionCount; | ||
if( count < MinBeeps) return; //No beeps | ||
if (count > MaxBeeps) return; //Too many beeps | ||
for (int i = 0; i < count; i++) | ||
{ | ||
DoTheBeep(_configuration.BeepForAttentionFrequency, _configuration.BeepForAttentionDuration); | ||
if (i + 1 < count) | ||
{ | ||
await Task.Delay(TimeSpan.FromMilliseconds(_configuration.BeepForAttentionInterval)).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); | ||
} | ||
} | ||
} | ||
|
||
private void DoTheBeep(int frequency, int duration) | ||
{ | ||
//Ignore invalid frequencies | ||
if (frequency < MinFrequency) return; | ||
if (frequency > MaxFrequency) return; | ||
Console.Beep(frequency, duration); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Http.Headers; | ||
using System.Net.Http.Json; | ||
using System.Text; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using InsightLogParser.Common.ApiModels; | ||
using InsightLogParser.Common.World; | ||
|
||
namespace InsightLogParser.Client.Cetus; | ||
|
||
internal class AuthResult | ||
{ | ||
[JsonPropertyName("accessToken")] | ||
public string? AccessToken { get; set; } | ||
|
||
[JsonPropertyName("expiresIn")] | ||
public int ExpiresIn { get; set; } | ||
} | ||
|
||
internal class CetusClient : ICetusClient | ||
{ | ||
private readonly MessageWriter _messageWriter; | ||
private readonly HttpClient _httpClient; | ||
private DateTimeOffset? _tokenValid; | ||
private string? _basicAuth; | ||
|
||
public bool IsDummy() => false; | ||
|
||
public CetusClient(string baseUri, MessageWriter messageWriter) | ||
{ | ||
_messageWriter = messageWriter; | ||
const string httpsPrefix = "https://"; | ||
|
||
if (!baseUri.StartsWith(httpsPrefix)) baseUri = $"{httpsPrefix}{baseUri}"; | ||
_httpClient = new HttpClient() | ||
{ | ||
BaseAddress = new Uri(baseUri) | ||
}; | ||
} | ||
|
||
public async Task<bool> RefreshAuthIfNeeded() | ||
{ | ||
if (_basicAuth == null || _tokenValid == null) return false; | ||
if (DateTimeOffset.UtcNow.AddMinutes(1) < _tokenValid.Value) return true; | ||
|
||
return await RefreshAuthAsync().ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
private async Task<bool> RefreshAuthAsync() | ||
{ | ||
_messageWriter.WriteDebug("CETUS: Refreshing Auth"); | ||
try | ||
{ | ||
var result = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "api/v1/auth/token") | ||
{ | ||
Headers = { Authorization = new AuthenticationHeaderValue("Basic", _basicAuth) } | ||
}).ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Auth returned {(int)result.StatusCode}-{result.StatusCode}"); | ||
if (!result.IsSuccessStatusCode) return false; | ||
if (result.StatusCode == HttpStatusCode.NoContent) return false; | ||
var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(await result.Content.ReadAsStreamAsync().ConfigureAwait(ConfigureAwaitOptions.None)).ConfigureAwait(false); | ||
if (authResult?.AccessToken == null) | ||
{ | ||
_messageWriter.WriteDebug("CETUS: Failed to parse token"); | ||
return false; | ||
} | ||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken); | ||
_tokenValid = DateTimeOffset.UtcNow.AddSeconds(authResult.ExpiresIn); | ||
} | ||
catch (Exception e) | ||
{ | ||
_messageWriter.WriteDebug($"CETUS: Exception: {e}"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
public async Task<bool> AuthenticateAsync(Guid playerId, string apiKey) | ||
{ | ||
_basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{playerId:N}:{apiKey}")); | ||
return await RefreshAuthAsync().ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
public void ClearAuth() | ||
{ | ||
_messageWriter.WriteDebug("CETUS: Auth was cleared"); | ||
_httpClient.DefaultRequestHeaders.Authorization = null; | ||
_tokenValid = null; | ||
_basicAuth = null; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_httpClient.Dispose(); | ||
} | ||
|
||
public async Task<SolvedResponse?> PostSolvedAsync(PlayerReport request) | ||
{ | ||
await RefreshAuthIfNeeded().ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug("CETUS: Posting solved"); | ||
return await MakePostAsync<SolvedResponse, PlayerReport>("api/v1/puzzle/solved", request).ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
public async Task<SeenResponse?> PostSeenAsync(PlayerReport request) | ||
{ | ||
await RefreshAuthIfNeeded().ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Posting seen"); | ||
return await MakePostAsync<SeenResponse, PlayerReport>("api/v1/puzzle/seen", request).ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
private async Task<TResponse?> MakePostAsync<TResponse, TRequest>(string requestUri, TRequest request, bool isRetry = false) | ||
{ | ||
try | ||
{ | ||
var result = await _httpClient.PostAsJsonAsync(requestUri, request) | ||
.ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Got a {(int)result.StatusCode}-{result.StatusCode}"); | ||
if (!result.IsSuccessStatusCode) | ||
{ | ||
if (!isRetry && result.StatusCode == HttpStatusCode.Unauthorized) | ||
{ | ||
_messageWriter.WriteDebug("CETUS: Retrying once with fresh auth"); | ||
var authResult = await RefreshAuthAsync().ConfigureAwait(ConfigureAwaitOptions.None); | ||
if (!authResult) return default; | ||
return await MakePostAsync<TResponse, TRequest>(requestUri, request, true).ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
return default; | ||
} | ||
return await result.Content.ReadFromJsonAsync<TResponse>().ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
catch (Exception e) | ||
{ | ||
_messageWriter.WriteDebug($"CETUS: Exception: {e}"); | ||
return default; | ||
} | ||
} | ||
|
||
|
||
public async Task<ZoneStatisticsResponse?> GetZoneStatisticsAsync(PuzzleZone zone) | ||
{ | ||
await RefreshAuthIfNeeded().ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Requesting statistics for {zone}"); | ||
return await MakeGetAsync<ZoneStatisticsResponse>($"api/v1.0/Puzzle/zone/{(int)zone}") | ||
.ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
public async Task<Sighting[]?> GetSightingsAsync(PuzzleZone zone, PuzzleType type) | ||
{ | ||
await RefreshAuthIfNeeded().ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Requesting sightings for {zone} {type}"); | ||
return await MakeGetAsync<Sighting[]>($"api/v1.0/Puzzle/sightings/{(int)zone}/{(int)type}") | ||
.ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
|
||
private async Task<T?> MakeGetAsync<T>(string requestUri, bool isRetry = false) | ||
{ | ||
try | ||
{ | ||
var result = await _httpClient.GetAsync(requestUri).ConfigureAwait(ConfigureAwaitOptions.None); | ||
_messageWriter.WriteDebug($"CETUS: Got a {(int)result.StatusCode}-{result.StatusCode}"); | ||
if (!result.IsSuccessStatusCode) | ||
{ | ||
if (!isRetry && result.StatusCode == HttpStatusCode.Unauthorized) | ||
{ | ||
_messageWriter.WriteDebug("CETUS: Retrying once with fresh auth"); | ||
var authResult = await RefreshAuthAsync().ConfigureAwait(ConfigureAwaitOptions.None); | ||
if (!authResult) return default; | ||
return await MakeGetAsync<T>(requestUri, true).ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
return default; | ||
} | ||
return await result.Content.ReadFromJsonAsync<T>().ConfigureAwait(ConfigureAwaitOptions.None); | ||
} | ||
catch (Exception e) | ||
{ | ||
_messageWriter.WriteDebug($"CETUS: Exception: {e}"); | ||
return default; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
| ||
using InsightLogParser.Common.ApiModels; | ||
using InsightLogParser.Common.World; | ||
|
||
namespace InsightLogParser.Client.Cetus | ||
{ | ||
internal class DummyCetusClient : ICetusClient | ||
{ | ||
public bool IsDummy() => true; | ||
public void ClearAuth() { } | ||
|
||
public Task<SolvedResponse?> PostSolvedAsync(PlayerReport request) | ||
{ | ||
return Task.FromResult<SolvedResponse?>(null); | ||
} | ||
|
||
public Task<SeenResponse?> PostSeenAsync(PlayerReport request) | ||
{ | ||
return Task.FromResult<SeenResponse?>(null); | ||
} | ||
|
||
public Task<bool> AuthenticateAsync(Guid playerId, string? configurationCetusApiKey) | ||
{ | ||
return Task.FromResult(true); | ||
} | ||
|
||
public Task<ZoneStatisticsResponse?> GetZoneStatisticsAsync(PuzzleZone zone) | ||
{ | ||
return Task.FromResult<ZoneStatisticsResponse?>(null); | ||
} | ||
|
||
public Task<Sighting[]?> GetSightingsAsync(PuzzleZone zone, PuzzleType type) | ||
{ | ||
return Task.FromResult<Sighting[]?>(null); | ||
} | ||
|
||
public void Dispose() { } //Noop | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using InsightLogParser.Common.ApiModels; | ||
using InsightLogParser.Common.World; | ||
|
||
namespace InsightLogParser.Client.Cetus; | ||
|
||
internal interface ICetusClient : IDisposable | ||
{ | ||
//Misc operations | ||
bool IsDummy(); | ||
void ClearAuth(); | ||
Task<bool> AuthenticateAsync(Guid playerId, string configurationCetusApiKey); | ||
|
||
//Get operations | ||
Task<ZoneStatisticsResponse?> GetZoneStatisticsAsync(PuzzleZone zone); | ||
Task<Sighting[]?> GetSightingsAsync(PuzzleZone zone, PuzzleType type); | ||
|
||
//Post operations | ||
Task<SeenResponse?> PostSeenAsync(PlayerReport request); | ||
Task<SolvedResponse?> PostSolvedAsync(PlayerReport request); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
| ||
namespace InsightLogParser.Client; | ||
|
||
public class Configuration | ||
{ | ||
public static readonly int CurrentConfigurationVersion = 1; | ||
|
||
public int ConfigVersion { get; set; } = CurrentConfigurationVersion; | ||
|
||
public string? ForceLogFolder { get; set; } | ||
public string? ForceGameRootFolder { get; set; } | ||
public string? ForcedParsedDatabasePath { get; set; } | ||
|
||
public string? CetusApiKey { get; set; } | ||
public string? CetusUri { get; set; } | ||
|
||
public bool ShowGameLogLines { get; set; } = false; | ||
public bool DebugMode { get; set; } = false; | ||
|
||
public bool BeepOnNewParse { get; set; } = true; | ||
public int NewParseBeepFrequency { get; set; } = 880; | ||
public int NewParseBeepDuration { get; set; } = 100; | ||
public bool BeepOnKnownParse { get; set; } = false; | ||
public int KnownParseBeepFrequency { get; set; } = 220; | ||
public int KnownParseBeepDuration { get; set; } = 200; | ||
public bool BeepOnOpeningSolvedPuzzle { get; set; } = true; | ||
public int OpenSolvedPuzzleBeepFrequency { get; set; } = 220; | ||
public int OpenSolvedPuzzleBeepDuration { get; set; } = 200; | ||
public bool BeepForAttention { get; set; } = true; | ||
public int BeepForAttentionFrequency { get; set; } = 220; | ||
public int BeepForAttentionDuration { get; set; } = 200; | ||
public int BeepForAttentionCount { get; set; } = 3; | ||
public int BeepForAttentionInterval { get; set; } = 200; | ||
|
||
} |
Oops, something went wrong.