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

72 lines
No EOL
2.7 KiB
C#

using System.Net;
using System.Text.Json;
using System.Threading.RateLimiting;
using Humanizer;
using NodaTime;
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>();
private readonly ResiliencePipeline _pipeline = new ResiliencePipelineBuilder()
.AddRateLimiter(new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions()
{
Window = 1.Seconds(),
PermitLimit = 2,
QueueLimit = 64,
}))
.AddTimeout(20.Seconds())
.Build();
private async Task<T?> DoRequestAsync<T>(string path, bool allowNotFound = false,
CancellationToken ct = default) where T : class
{
var req = new HttpRequestMessage(HttpMethod.Get, $"{ApiBaseUrl}{path}");
req.Headers.Add("User-Agent", UserAgent);
_logger.Debug("Requesting {Path} from PluralKit API", path);
var resp = await _pipeline.ExecuteAsync(async ct2 => await _client.SendAsync(req, ct2), ct);
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)
{
_logger.Error("Received non-200 status code {StatusCode} from PluralKit API path {Path}", resp.StatusCode,
req);
throw new CataloggerError("Non-200 status code from PluralKit API");
}
return await resp.Content.ReadFromJsonAsync<T>(new JsonSerializerOptions
{ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }, ct) ??
throw new CataloggerError("JSON response from PluralKit API was null");
}
public async Task<PkMessage?> GetPluralKitMessageAsync(ulong id, CancellationToken ct = default) =>
await DoRequestAsync<PkMessage>($"/messages/{id}", allowNotFound: true, ct);
public async Task<PkSystem?> GetPluralKitSystemAsync(ulong id, CancellationToken ct = default) =>
await DoRequestAsync<PkSystem>($"/systems/{id}", allowNotFound: true, ct);
public record PkMessage(
ulong Id,
ulong Original,
ulong Sender,
ulong Channel,
ulong Guild,
PkSystem? System,
PkMember? Member);
public record PkSystem(string Id, Guid Uuid, string? Name, string? Tag, Instant? Created);
public record PkMember(string Id, Guid Uuid, string Name, string? DisplayName);
}