From 24bba312ec67794e52a077ef14484ffd12f98298 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 6 Nov 2023 21:21:17 +0100 Subject: [PATCH] it be cachin --- .../Converters/UserAvatarConverter.cs | 37 +++++-------------- Turbulence.Discord/Api.cs | 18 +++++---- Turbulence.Discord/Client.cs | 18 +++++---- Turbulence.Discord/IPlatformClient.cs | 2 +- Turbulence.Discord/Services/Cache.cs | 17 ++++++--- 5 files changed, 44 insertions(+), 48 deletions(-) diff --git a/Turbulence.Desktop/Converters/UserAvatarConverter.cs b/Turbulence.Desktop/Converters/UserAvatarConverter.cs index 215fd02..dcca9e0 100644 --- a/Turbulence.Desktop/Converters/UserAvatarConverter.cs +++ b/Turbulence.Desktop/Converters/UserAvatarConverter.cs @@ -3,6 +3,8 @@ using Avalonia.Data.Converters; using Avalonia.Platform; using System.Globalization; +using CommunityToolkit.Mvvm.DependencyInjection; +using Turbulence.Discord; using Turbulence.Discord.Models.DiscordUser; using Bitmap = Avalonia.Media.Imaging.Bitmap; @@ -10,6 +12,8 @@ namespace Turbulence.Desktop.Converters; public class UserAvatarConverter : IValueConverter { + private readonly IPlatformClient _client = Ioc.Default.GetService()!; + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is not User user) @@ -21,39 +25,18 @@ public class UserAvatarConverter : IValueConverter return new Bitmap(AssetLoader.Open(new Uri("resm:Avalonia.Skia.Assets.NoiseAsset_256X256_PNG.png?assembly=Avalonia.Skia"))); } - // TODO: use client avatar get - if (user.Avatar is { } avatar) - { - return Task.Run(async () => - await LoadFromWeb(new Uri($"https://cdn.discordapp.com/avatars/{user.Id}/{avatar}.png?size=80"))).Result; - } - else + var data = Task.Run(async () => await _client.GetAvatarAsync(user, 80)).Result; + var bmp = new Bitmap(new MemoryStream(data)); + if (bmp.PixelSize.Height > 80) { - return Task.Run(async () => - await LoadFromWeb(new Uri($"https://cdn.discordapp.com/embed/avatars/{(user.Id >> 22) % 6}.png"))).Result!.CreateScaledBitmap(new PixelSize(80, 80)); + bmp = bmp.CreateScaledBitmap(new PixelSize(80, 80)); } - // Image.Source = new Bitmap(new MemoryStream(await _client.GetAvatar(message.Author))); + + return bmp; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } - - private static async Task LoadFromWeb(Uri url) - { - using var httpClient = new HttpClient(); - try - { - var response = await httpClient.GetAsync(url); - response.EnsureSuccessStatusCode(); - var data = await response.Content.ReadAsByteArrayAsync(); - return new Bitmap(new MemoryStream(data)); - } - catch (HttpRequestException ex) - { - Console.WriteLine($"An error occurred while downloading image '{url}': {ex.Message}"); - return null; - } - } } diff --git a/Turbulence.Discord/Api.cs b/Turbulence.Discord/Api.cs index ab894ab..82dac05 100644 --- a/Turbulence.Discord/Api.cs +++ b/Turbulence.Discord/Api.cs @@ -91,7 +91,7 @@ private static async Task Delete(HttpClient client, string endpoint) private static async Task CdnGet(HttpClient client, string endpoint) { - var req = new HttpRequestMessage(HttpMethod.Get, $"{ApiRoot}{endpoint}"); + var req = new HttpRequestMessage(HttpMethod.Get, $"{CdnRoot}{endpoint}"); var response = await client.SendAsync(req); if (!response.IsSuccessStatusCode) { @@ -189,16 +189,20 @@ public static async Task DeleteMessage(HttpClient client, Message message) await Delete(client, $"/channels/{message.ChannelId}/messages/{message.Id}"); } - public static async Task GetAvatar(HttpClient client, Snowflake user, string avatar, int size = 32) + //https://discord.com/developers/docs/reference#image-formatting + public static Task GetAvatarAsync(HttpClient client, User user, int size = 32) { - var data = await CdnGet(client, $"avatars/{user}/{avatar}.png?size={size}"); - return new Image(data, size); // TODO: Size could easily be a lie, as the API will just send the largest available instead of given size + return CdnGet(client, $"avatars/{user.Id}/{user.Avatar}.png?size={size}"); + // TODO: Size could easily be a lie, as the API will just send the largest available instead of given size } - public static async Task GetDefaultAvatar(HttpClient client, int index) + //https://discord.com/developers/docs/reference#image-formatting + public static Task GetDefaultAvatarAsync(HttpClient client, User user) { - var data = await CdnGet(client, $"embed/avatars/{index}.png"); // TODO: Doesn't work - return new Image(data, 256); + // index depends on whether the user switched to new username system + // users with a username have a Discriminator of "0" + var index = user.Discriminator == "0" ? (int)(user.Id >> 22) % 6 : int.Parse(user.Discriminator) % 5; + return CdnGet(client, $"embed/avatars/{index}.png"); } // https://discord.com/developers/docs/resources/channel#get-pinned-messages diff --git a/Turbulence.Discord/Client.cs b/Turbulence.Discord/Client.cs index 99a273b..643d92e 100644 --- a/Turbulence.Discord/Client.cs +++ b/Turbulence.Discord/Client.cs @@ -423,20 +423,22 @@ public async Task GetGuild(Snowflake id) return Guilds.TryGetValue(id, out var ret) ? ret : await Api.GetGuild(HttpClient, id); } - public async Task GetAvatar(User user, int size = 128) + public async Task GetAvatarAsync(User user, int size = 128) { - if (_cache.GetAvatar(user.Id) is { } avatar) + if (_cache.GetAvatar(user.Id, size) is { } avatar) return avatar; - // https://discord.com/developers/docs/reference#image-formatting if (user.Avatar == null) { - // index depends on whether the user switched to new username system - // users with a username have a Discriminator of "0" - var index = user.Discriminator == "0" ? (int)(user.Id >> 22) % 6 : int.Parse(user.Discriminator) % 5; - return await Api.GetDefaultAvatar(CdnClient, index); + avatar = await Api.GetDefaultAvatarAsync(CdnClient, user); } - return await Api.GetAvatar(CdnClient, user.Id, user.Avatar!, size); + else + { + avatar = await Api.GetAvatarAsync(CdnClient, user, size); + } + + _cache.SetAvatar(user.Id, size, avatar); + return avatar; } public async Task GetChannel(Snowflake id) diff --git a/Turbulence.Discord/IPlatformClient.cs b/Turbulence.Discord/IPlatformClient.cs index b1944f3..595289b 100644 --- a/Turbulence.Discord/IPlatformClient.cs +++ b/Turbulence.Discord/IPlatformClient.cs @@ -22,7 +22,7 @@ public interface IPlatformClient public event EventHandler>? TypingStart; public User? CurrentUser { get; set; } - public Task GetAvatar(User user, int size = 128); + public Task GetAvatarAsync(User user, int size = 128); #region Discord specific shit that should not be here diff --git a/Turbulence.Discord/Services/Cache.cs b/Turbulence.Discord/Services/Cache.cs index 9023c3d..eb379c5 100644 --- a/Turbulence.Discord/Services/Cache.cs +++ b/Turbulence.Discord/Services/Cache.cs @@ -4,15 +4,22 @@ namespace Turbulence.Discord.Services; public interface ICache { - public Image? GetAvatar(Snowflake userId); + public byte[]? GetAvatar(Snowflake userId, int size); + + public void SetAvatar(Snowflake userId, int size, byte[] avatar); } public class Cache : ICache { - public Image? GetAvatar(Snowflake userId) + private readonly Dictionary<(Snowflake, int), byte[]> _avatars = new(); + + public byte[]? GetAvatar(Snowflake userId, int size) { - return null; + return _avatars.TryGetValue((userId, size), out var avatar) ? avatar : null; } -} -public record Image(byte[] Data, int Size); + public void SetAvatar(Snowflake userId, int size, byte[] avatar) + { + _avatars[(userId, size)] = avatar; + } +} \ No newline at end of file