diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index bc8d96f86..1dbc9a781 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -12,26 +12,45 @@ namespace Discord /// /// The application identifier. /// The icon identifier. + /// The format to return. Mustn't be a gif. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the application's icon. /// - public static string GetApplicationIconUrl(ulong appId, string iconId) - => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; + public static string GetApplicationIconUrl(ulong appId, string iconId, ImageFormat format, ushort size) + { + if (string.IsNullOrWhiteSpace(iconId)) + return null; + if (format == ImageFormat.Gif) + throw new ArgumentException("Requested image format mustn't be a gif."); + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); + + string extension = FormatToExtension(format, iconId); + return $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.{extension}?size={size}"; + } /// /// Returns a user avatar URL. /// /// The user snowflake identifier. /// The avatar identifier. - /// The size of the image to return in. This can be any power of two between 16 and 2048. /// The format to return. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the user's avatar in the specified size. /// - public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size, ImageFormat format) + public static string GetUserAvatarUrl(ulong userId, string avatarId, ImageFormat format, ushort size) { - if (avatarId == null) + if (string.IsNullOrWhiteSpace(avatarId)) return null; + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); + string extension = FormatToExtension(format, avatarId); return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}"; } @@ -51,8 +70,8 @@ namespace Discord /// /// The guild snowflake identifier. /// The icon identifier. + /// The format to return. Mustn't be a gif. /// The size of the image to return in. This can be any power of two between 16 and 2048. - /// The format to return. /// /// A URL pointing to the guild's icon in the specified size. /// @@ -62,6 +81,10 @@ namespace Discord return null; if (format == ImageFormat.Gif) throw new ArgumentException("Requested image format mustn't be a gif."); + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); string extension = FormatToExtension(format, iconId); return $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.{extension}?size={size}"; @@ -71,44 +94,63 @@ namespace Discord /// /// The guild snowflake identifier. /// The splash icon identifier. + /// The format to return. Mustn't be a gif. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the guild's icon. /// - public static string GetGuildSplashUrl(ulong guildId, string splashId) - => splashId != null ? $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null; - /// - /// Returns a channel icon URL. - /// - /// The channel snowflake identifier. - /// The icon identifier. - /// - /// A URL pointing to the channel's icon. - /// - public static string GetChannelIconUrl(ulong channelId, string iconId) - => iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null; + public static string GetGuildSplashUrl(ulong guildId, string splashId, ImageFormat format, ushort size) + { + if (string.IsNullOrWhiteSpace(splashId)) + return null; + if (format == ImageFormat.Gif) + throw new ArgumentException("Requested image format mustn't be a gif."); + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); + + string extension = FormatToExtension(format, splashId); + return $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.{ extension}?size={size}"; + } /// /// Returns an emoji URL. /// /// The emoji snowflake identifier. /// Whether this emoji is animated. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the custom emote. /// - public static string GetEmojiUrl(ulong emojiId, bool animated) - => $"{DiscordConfig.CDNUrl}emojis/{emojiId}.{(animated ? "gif" : "png")}"; + public static string GetEmojiUrl(ulong emojiId, bool animated, ushort size) + { + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); + + return $"{DiscordConfig.CDNUrl}emojis/{emojiId}.{(animated ? "gif" : "png")}?size={size}"; + } /// /// Returns a Rich Presence asset URL. /// /// The application identifier. /// The asset identifier. - /// The size of the image to return in. This can be any power of two between 16 and 2048. /// The format to return. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the asset image in the specified size. /// - public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format) + public static string GetRichAssetUrl(ulong appId, string assetId, ImageFormat format, ushort size) { + if (string.IsNullOrWhiteSpace(assetId)) + return null; + if (!(size >= 16 && size <= 2048)) + throw new ArgumentOutOfRangeException("Size must be a power of two in a range between 16 and 2048."); + if ((size & (size - 1)) != 0) + throw new ArgumentException("Size must be a power of two."); + string extension = FormatToExtension(format, ""); return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}"; } diff --git a/src/Discord.Net.Core/Entities/Activities/GameAsset.cs b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs index 7217bded3..ccd0fa15b 100644 --- a/src/Discord.Net.Core/Entities/Activities/GameAsset.cs +++ b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs @@ -33,6 +33,6 @@ namespace Discord /// A string pointing to the image URL of the asset; null when the application ID does not exist. /// public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) - => ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null; + => ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, format, size) : null; } } diff --git a/src/Discord.Net.Core/Entities/Emotes/Emote.cs b/src/Discord.Net.Core/Entities/Emotes/Emote.cs index bebd9fa4f..172e88794 100644 --- a/src/Discord.Net.Core/Entities/Emotes/Emote.cs +++ b/src/Discord.Net.Core/Entities/Emotes/Emote.cs @@ -26,10 +26,11 @@ namespace Discord /// /// Gets the image URL of this emote. /// + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A string that points to the URL of this emote. /// - public string Url => CDN.GetEmojiUrl(Id, Animated); + public string GetUrl(ushort size = 128) => CDN.GetEmojiUrl(Id, Animated, size); internal Emote(ulong id, string name, bool animated) { diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 826b4ff2f..627195083 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -69,6 +69,8 @@ namespace Discord /// /// Gets the URL of this guild's icon. /// + /// The format to return. Mustn't be a gif. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the guild's icon; null if none is set. /// @@ -83,10 +85,12 @@ namespace Discord /// /// Gets the URL of this guild's splash image. /// + /// The format to return. Mustn't be a gif. + /// The size of the image to return in. This can be any power of two between 16 and 2048. /// /// A URL pointing to the guild's splash image; null if none is set. /// - string SplashUrl { get; } + string GetSplashUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128); /// /// Determines if this guild is currently connected and ready to be used. /// diff --git a/src/Discord.Net.Core/Entities/IApplication.cs b/src/Discord.Net.Core/Entities/IApplication.cs index 78a87dc19..08ae75250 100644 --- a/src/Discord.Net.Core/Entities/IApplication.cs +++ b/src/Discord.Net.Core/Entities/IApplication.cs @@ -21,7 +21,9 @@ namespace Discord /// /// Gets the icon URL of the application. /// - string IconUrl { get; } + /// The format to return. Mustn't be a gif. + /// The size of the image to return in. This can be any power of two between 16 and 2048. + string GetIconUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128); /// /// Gets the partial user object containing info on the owner of the application. diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 6552dc768..6eb731275 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -62,7 +62,8 @@ namespace Discord.Rest public string GetIconUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) => CDN.GetGuildIconUrl(Id, IconId, format, size); /// - public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); + public string GetSplashUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) + => CDN.GetGuildSplashUrl(Id, SplashId, format, size); /// /// Gets the built-in role containing all users in this guild. diff --git a/src/Discord.Net.Rest/Entities/RestApplication.cs b/src/Discord.Net.Rest/Entities/RestApplication.cs index d033978d0..d9ab68388 100644 --- a/src/Discord.Net.Rest/Entities/RestApplication.cs +++ b/src/Discord.Net.Rest/Entities/RestApplication.cs @@ -28,7 +28,8 @@ namespace Discord.Rest /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); /// - public string IconUrl => CDN.GetApplicationIconUrl(Id, _iconId); + public string GetIconUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) + => CDN.GetApplicationIconUrl(Id, _iconId, format, size); internal RestApplication(BaseDiscordClient discord, ulong id) : base(discord, id) diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 6af5b5c95..d938385d2 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -80,7 +80,7 @@ namespace Discord.Rest /// public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) - => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); + => CDN.GetUserAvatarUrl(Id, AvatarId, format, size); /// public string GetDefaultAvatarUrl() diff --git a/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs b/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs index 1fdc95a63..3d9ecdfd9 100644 --- a/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs +++ b/src/Discord.Net.Rest/Entities/Webhooks/RestWebhook.cs @@ -75,7 +75,7 @@ namespace Discord.Rest /// public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) - => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); + => CDN.GetUserAvatarUrl(Id, AvatarId, format, size); public async Task ModifyAsync(Action func, RequestOptions options = null) { diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 2a210ecd3..522aaa431 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -100,7 +100,8 @@ namespace Discord.WebSocket public string GetIconUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) => CDN.GetGuildIconUrl(Id, IconId, format, size); /// - public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); + public string GetSplashUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) => + CDN.GetGuildSplashUrl(Id, SplashId, format, size); /// Indicates whether the client has all the members downloaded to the local guild cache. public bool HasAllMembers => MemberCount == DownloadedMemberCount;// _downloaderPromise.Task.IsCompleted; /// Indicates whether the guild cache is synced to this guild. diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 4832e7311..6e1f1ef67 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -84,7 +84,7 @@ namespace Discord.WebSocket /// public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) - => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); + => CDN.GetUserAvatarUrl(Id, AvatarId, format, size); /// public string GetDefaultAvatarUrl() diff --git a/src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs b/src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs index 60cb89ee2..309c5f62e 100644 --- a/src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs +++ b/src/Discord.Net.Webhook/Entities/Webhooks/RestInternalWebhook.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.Webhook; @@ -45,7 +45,7 @@ namespace Discord.Webhook } public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) - => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); + => CDN.GetUserAvatarUrl(Id, AvatarId, format, size); public async Task ModifyAsync(Action func, RequestOptions options = null) { diff --git a/test/Discord.Net.Tests/Tests.Emotes.cs b/test/Discord.Net.Tests/Tests.Emotes.cs index eeadbddf8..26f040306 100644 --- a/test/Discord.Net.Tests/Tests.Emotes.cs +++ b/test/Discord.Net.Tests/Tests.Emotes.cs @@ -14,7 +14,7 @@ namespace Discord Assert.Equal(394207658351263745UL, emote.Id); Assert.False(emote.Animated); Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt); - Assert.EndsWith("png", emote.Url); + Assert.EndsWith("png", emote.GetUrl()); } [Fact] public void Test_Invalid_Emote_Parse() @@ -32,7 +32,7 @@ namespace Discord Assert.Equal(394207658351263745UL, emote.Id); Assert.True(emote.Animated); Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt); - Assert.EndsWith("gif", emote.Url); + Assert.EndsWith("gif", emote.GetUrl()); } [Fact] public void Test_Invalid_Amimated_Emote_Parse()