From 3a3f42d755cc308e80a10761ea8eb9cfbfd7156a Mon Sep 17 00:00:00 2001 From: Una Kearney Date: Fri, 5 Dec 2025 18:10:27 -0500 Subject: [PATCH] Add support for a time placeholder in URLs --- PluralKit.Bot/Utils/AvatarUtils.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/PluralKit.Bot/Utils/AvatarUtils.cs b/PluralKit.Bot/Utils/AvatarUtils.cs index 65a208ed..c56f22f0 100644 --- a/PluralKit.Bot/Utils/AvatarUtils.cs +++ b/PluralKit.Bot/Utils/AvatarUtils.cs @@ -14,6 +14,10 @@ public static class AvatarUtils private static readonly string DiscordMediaUrlReplacement = "https://media.discordapp.net/attachments/$1/$2/$3.$4?width=256&height=256"; + + // Rewrite time "cachebuster" parameters for randomly generated/chosen avatars with custom URLs. + // Number match uses `[1-9][0-9]{0,6}` rather than `[0-9]+` to avoid needing to deal with special cases for zero and limit to reasonable numbers. + private static readonly Regex TimePlaceholder = new(@"\{time(?:/(?[1-9][0-9]{0,6}))?(?:%(?[1-9][0-9]{0,6}))?\}", RegexOptions.IgnoreCase); public static string? TryRewriteCdnUrl(string? url) { @@ -25,8 +29,24 @@ public static class AvatarUtils if (match.Groups["query"].Success) newUrl += "&" + match.Groups["query"].Value; + newUrl = TimePlaceholder.Replace(newUrl, ProcessTimePlaceholder); + return newUrl; } + + private static string? ProcessTimePlaceholder(Match m) { + // Minutes are the maximum accuracy to avoid too much cache thrashing + // AND with the maximum positive value so it's always positive (as if this code will exist long enough for the 64-bit signed unix time to go negative...) + var time = (DateTimeOffset.UtcNow.ToUnixTimeSeconds()/60)&Int64.MaxValue; + + if (m.Groups["divisor"].Success) + time /= Int32.Parse(m.Groups["divisor"].Value); // as above - guaranteed to not throw and be > 0 + + if (m.Groups["modulus"].Success) + time %= Int32.Parse(m.Groups["modulus"].Value); + + return time.ToString(); + } public static bool IsDiscordCdnUrl(string? url) => url != null && DiscordCdnUrl.Match(url).Success; } \ No newline at end of file