Created
March 3, 2026 06:35
-
-
Save Sighyu/939369b55c405db87135d46c1dcfb65d to your computer and use it in GitHub Desktop.
MakeItAQuoteAPI
This file contains hidden or 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
| [Command("quote")] | |
| [Description("Generate a Make it a Quote image for the supplied snowflake.")] | |
| [InteractionInstallType(DiscordApplicationIntegrationType.UserInstall, DiscordApplicationIntegrationType.GuildInstall)] | |
| [InteractionAllowedContexts(DiscordInteractionContextType.Guild, DiscordInteractionContextType.BotDM, DiscordInteractionContextType.PrivateChannel)] | |
| public static async ValueTask QuoteAsync( | |
| SlashCommandContext ctx, | |
| [Parameter("user_id"), Description("Target user snowflake (ID)")] string userId, | |
| [Parameter("text"), Description("Text for the quote")] string text, | |
| [Parameter("color"), Description("Use colored background")] bool? color = null, | |
| [Parameter("beta"), Description("Use the beta renderer")] bool? beta = null) | |
| { | |
| bool shouldEphemeral = ctx.Guild is not null; | |
| if (!CommandConfig.IsUserAllowed(ctx.User.Id)) | |
| { | |
| await ctx.RespondAsync("You are not authorized to use this command.\n https://gif.fxtwitter.com/tweet_video/G7FujbCbQAAsUIi.gif", shouldEphemeral); | |
| return; | |
| } | |
| if (!TryParseSnowflake(userId, out var snowflake)) | |
| { | |
| await ctx.RespondAsync("Invalid user supplied. Mention them or paste the numeric ID.", shouldEphemeral); | |
| return; | |
| } | |
| await ctx.DeferResponseAsync(); | |
| try | |
| { | |
| var user = await ctx.Client.GetUserAsync(snowflake); | |
| var avatar = user.GetAvatarUrl(MediaFormat.Png) ?? user.DefaultAvatarUrl; | |
| var username = user.Username; | |
| var displayName = user.GlobalName; | |
| var useColor = color ?? false; | |
| var useBeta = beta ?? false; | |
| var quote = new MiQ() | |
| .SetText(text) | |
| .SetAvatar(avatar) | |
| .SetUsername(username) | |
| .SetDisplayName(displayName) | |
| .SetColor(useColor) | |
| .SetWatermark(QuoteConstants.DefaultWatermark); | |
| var buffer = await quote.GenerateRawAsync(useBeta); | |
| await using var imageStream = new MemoryStream(buffer); | |
| var responseBuilder = new DiscordMessageBuilder() | |
| .WithContent($"Quote for <@{snowflake}> ({snowflake})") | |
| .AddFile("quote.png", imageStream); | |
| await ctx.EditResponseAsync(responseBuilder); | |
| } | |
| catch (Exception ex) | |
| { | |
| await ctx.EditResponseAsync(new DiscordMessageBuilder() | |
| .WithContent($"Failed to generate quote: {ex.Message}")); | |
| } | |
| } |
This file contains hidden or 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
| using System.Net.Http.Json; | |
| using System.Text.Json; | |
| namespace MakeItAQuoteBot; | |
| internal sealed class MiQ | |
| { | |
| private static readonly HttpClient Http = new() | |
| { | |
| Timeout = TimeSpan.FromSeconds(30) | |
| }; | |
| public QuoteFormat Format { get; } = new(); | |
| public MiQ SetText(string text) | |
| { | |
| if (string.IsNullOrWhiteSpace(text)) | |
| { | |
| throw new ArgumentException("Text is required.", nameof(text)); | |
| } | |
| Format.Text = text; | |
| return this; | |
| } | |
| public MiQ SetAvatar(string? avatar) | |
| { | |
| Format.Avatar = avatar; | |
| return this; | |
| } | |
| public MiQ SetUsername(string username) | |
| { | |
| Format.Username = username; | |
| return this; | |
| } | |
| public MiQ SetDisplayName(string displayName) | |
| { | |
| Format.DisplayName = displayName; | |
| return this; | |
| } | |
| public MiQ SetColor(bool color) | |
| { | |
| Format.Color = color; | |
| return this; | |
| } | |
| public MiQ SetWatermark(string watermark) | |
| { | |
| if (string.IsNullOrWhiteSpace(watermark)) | |
| { | |
| throw new ArgumentException("Watermark must be provided.", nameof(watermark)); | |
| } | |
| Format.Watermark = watermark; | |
| return this; | |
| } | |
| public async Task<string> GenerateUrlAsync(CancellationToken cancellationToken = default) | |
| { | |
| EnsureText(); | |
| try | |
| { | |
| using var response = await Http.PostAsJsonAsync(QuoteConstants.ApiUrl, Format, cancellationToken); | |
| await EnsureSuccess(response); | |
| var stream = await response.Content.ReadAsStreamAsync(cancellationToken); | |
| using var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken); | |
| if (!document.RootElement.TryGetProperty("url", out var urlElement)) | |
| { | |
| throw new InvalidOperationException("API response did not include a url property."); | |
| } | |
| var url = urlElement.GetString(); | |
| if (string.IsNullOrWhiteSpace(url)) | |
| { | |
| throw new InvalidOperationException("API response contained an empty url."); | |
| } | |
| return url; | |
| } | |
| catch (Exception ex) when (ex is not InvalidOperationException) | |
| { | |
| throw new InvalidOperationException($"Failed to generate quote URL: {ex.Message}", ex); | |
| } | |
| } | |
| public async Task<byte[]> GenerateRawAsync(bool useBeta = false, CancellationToken cancellationToken = default) | |
| { | |
| EnsureText(); | |
| try | |
| { | |
| if (useBeta) | |
| { | |
| using var betaResponse = await Http.PostAsJsonAsync(QuoteConstants.BetaApiUrl, Format, cancellationToken); | |
| await EnsureSuccess(betaResponse); | |
| return await betaResponse.Content.ReadAsByteArrayAsync(cancellationToken); | |
| } | |
| var url = await GenerateUrlAsync(cancellationToken); | |
| using var imageResponse = await Http.GetAsync(url, cancellationToken); | |
| await EnsureSuccess(imageResponse); | |
| return await imageResponse.Content.ReadAsByteArrayAsync(cancellationToken); | |
| } | |
| catch (Exception ex) | |
| { | |
| throw new InvalidOperationException($"Failed to generate quote image: {ex.Message}", ex); | |
| } | |
| } | |
| private static async Task EnsureSuccess(HttpResponseMessage response) | |
| { | |
| if (response.IsSuccessStatusCode) | |
| { | |
| return; | |
| } | |
| var body = await response.Content.ReadAsStringAsync(); | |
| throw new HttpRequestException($"Status {(int)response.StatusCode}: {body}"); | |
| } | |
| private void EnsureText() | |
| { | |
| if (string.IsNullOrWhiteSpace(Format.Text)) | |
| { | |
| throw new InvalidOperationException("Text is required before generating a quote."); | |
| } | |
| } | |
| } |
This file contains hidden or 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
| namespace MakeItAQuoteBot; | |
| internal static class QuoteConstants | |
| { | |
| public const string ApiUrl = "https://api.voids.top/fakequote"; | |
| public const string BetaApiUrl = "https://api.voids.top/fakequotebeta"; | |
| public const string DefaultWatermark = "Make it a Quote#6666"; | |
| } |
This file contains hidden or 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
| using System.Text.Json.Serialization; | |
| namespace MakeItAQuoteBot; | |
| internal sealed class QuoteFormat | |
| { | |
| [JsonPropertyName("text")] | |
| public string Text { get; set; } = string.Empty; | |
| [JsonPropertyName("avatar")] | |
| public string? Avatar { get; set; } | |
| [JsonPropertyName("username")] | |
| public string Username { get; set; } = string.Empty; | |
| [JsonPropertyName("display_name")] | |
| public string DisplayName { get; set; } = string.Empty; | |
| [JsonPropertyName("color")] | |
| public bool Color { get; set; } | |
| [JsonPropertyName("watermark")] | |
| public string Watermark { get; set; } = QuoteConstants.DefaultWatermark; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment