Catalogger.NET/Catalogger.Backend/Services/PluralkitApiService.cs

99 lines
3.2 KiB
C#
Raw Normal View History

2024-08-13 13:08:50 +02:00
using System.Net;
using System.Text.Json;
using System.Threading.RateLimiting;
using Humanizer;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
using NodaTime.Text;
2024-08-13 13:08:50 +02:00
using Polly;
namespace Catalogger.Backend.Services;
public class PluralkitApiService(ILogger logger)
{
private const string UserAgent = "Catalogger.NET (https://codeberg.org/starshine/catalogger)";
private const string ApiBaseUrl = "https://api.pluralkit.me/v2";
private readonly HttpClient _client = new();
private readonly ILogger _logger = logger.ForContext<PluralkitApiService>();
2024-10-12 23:28:15 +02:00
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
}.ConfigureForNodaTime(
new NodaJsonSettings
{
InstantConverter = new NodaPatternConverter<Instant>(InstantPattern.ExtendedIso),
}
);
2024-08-13 13:08:50 +02:00
private readonly ResiliencePipeline _pipeline = new ResiliencePipelineBuilder()
2024-10-09 17:35:11 +02:00
.AddRateLimiter(
new FixedWindowRateLimiter(
new FixedWindowRateLimiterOptions()
{
Window = 1.Seconds(),
PermitLimit = 2,
QueueLimit = 64,
}
)
)
2024-08-13 13:08:50 +02:00
.AddTimeout(20.Seconds())
.Build();
2024-10-09 17:35:11 +02:00
private async Task<T?> DoRequestAsync<T>(
string path,
bool allowNotFound = false,
CancellationToken ct = default
)
where T : class
2024-08-13 13:08:50 +02:00
{
var req = new HttpRequestMessage(HttpMethod.Get, $"{ApiBaseUrl}{path}");
req.Headers.Add("User-Agent", UserAgent);
_logger.Debug("Requesting {Path} from PluralKit API", path);
2024-08-20 21:03:03 +02:00
var resp = await _pipeline.ExecuteAsync(async ct2 => await _client.SendAsync(req, ct2), ct);
2024-08-13 13:08:50 +02:00
if (resp.StatusCode == HttpStatusCode.NotFound && allowNotFound)
{
_logger.Debug("PluralKit API path {Path} returned 404 but 404 response is valid", path);
return null;
}
if (!resp.IsSuccessStatusCode)
{
2024-10-09 17:35:11 +02:00
_logger.Error(
"Received non-200 status code {StatusCode} from PluralKit API path {Path}",
resp.StatusCode,
req
);
2024-08-13 13:08:50 +02:00
throw new CataloggerError("Non-200 status code from PluralKit API");
}
2024-10-12 23:28:15 +02:00
return await resp.Content.ReadFromJsonAsync<T>(_jsonOptions, ct)
2024-10-09 17:35:11 +02:00
?? throw new CataloggerError("JSON response from PluralKit API was null");
2024-08-13 13:08:50 +02:00
}
2024-10-09 17:35:11 +02:00
public async Task<PkMessage?> GetPluralKitMessageAsync(
ulong id,
CancellationToken ct = default
) => await DoRequestAsync<PkMessage>($"/messages/{id}", allowNotFound: true, ct);
2024-08-13 13:08:50 +02:00
2024-10-09 17:35:11 +02:00
public async Task<PkSystem?> GetPluralKitSystemAsync(
ulong id,
CancellationToken ct = default
) => await DoRequestAsync<PkSystem>($"/systems/{id}", allowNotFound: true, ct);
2024-08-13 13:08:50 +02:00
public record PkMessage(
ulong Id,
ulong Original,
ulong Sender,
ulong Channel,
ulong Guild,
PkSystem? System,
2024-10-09 17:35:11 +02:00
PkMember? Member
);
2024-08-13 13:08:50 +02:00
public record PkSystem(string Id, Guid Uuid, string? Name, string? Tag, Instant? Created);
public record PkMember(string Id, Guid Uuid, string Name, string? DisplayName);
2024-10-09 17:35:11 +02:00
}