using System.Net; using System.Net.Http.Json; using System.Text.Json; using Foxcord.Models; using Foxcord.Rest.Rate; using Foxcord.Rest.Types; using Foxcord.Serialization; using Polly; using Serilog; namespace Foxcord.Rest; public class DiscordRestClient : BaseRestClient { public DiscordRestClient(ILogger logger, DiscordRestClientOptions options) : base(logger, new RestClientOptions { JsonSerializerOptions = JsonSerializerOptions, ApiBaseUrl = options.ApiBaseUrl ?? DefaultApiBaseUrl, UserAgent = options.UserAgent ?? DefaultUserAgent, TokenFactory = () => $"Bot {options.Token}", ErrorHandler = HandleError, }) { Pipeline = new ResiliencePipelineBuilder() .AddDiscordStrategy(new RateLimiter(logger.ForContext())) .Build(); } public const string DefaultUserAgent = "DiscordBot (https://code.vulpine.solutions/sam/Foxcord, v1)"; public const string DefaultApiBaseUrl = "https://discord.com/api/v10"; public async Task GatewayBotAsync(CancellationToken ct = default) => await RequestAsync(HttpMethod.Get, "/gateway/bot", ct); public async Task CreateMessageAsync(Snowflake channelId, CreateMessageParams message, CancellationToken ct = default) => await RequestAsync(HttpMethod.Post, $"/channels/{channelId}/messages", message, ct); #region BaseRestClient parameters private static readonly JsonSerializerOptions JsonSerializerOptions = JsonSerializerExtensions.CreateSerializer(); private static async Task HandleError(HttpResponseMessage resp, CancellationToken ct) { if (resp.IsSuccessStatusCode) return; if (resp.StatusCode == HttpStatusCode.TooManyRequests) { throw new RateLimitError(resp.Headers); } var error = await resp.Content.ReadFromJsonAsync(JsonSerializerOptions, ct); if (error == null) throw new ArgumentNullException(nameof(resp), "Response was deserialized as null"); throw error; } #endregion } public class DiscordRestClientOptions { public required string Token { get; init; } public string? UserAgent { get; init; } public string? ApiBaseUrl { get; init; } }