diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index b170336dc..f46a0697f 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -47,6 +47,14 @@ namespace Discord return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}"; } + public static string GetGuildUserAvatarUrl(ulong userId, ulong guildId, string avatarId, ushort size, ImageFormat format) + { + if (avatarId == null) + return null; + string extension = FormatToExtension(format, avatarId); + return $"{DiscordConfig.CDNUrl}guilds/{guildId}/users/{userId}/avatars/{avatarId}.{extension}?size={size}"; + } + /// /// Returns a user banner URL. /// diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index 58a797c5f..947ff8521 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -25,6 +25,13 @@ namespace Discord /// string Nickname { get; } /// + /// Gets the guild specific avatar for this users. + /// + /// + /// The users guild avatar hash if they have one; otherwise . + /// + string GuildAvatarId { get; } + /// /// Gets the guild-level permissions for this user. /// /// @@ -96,6 +103,20 @@ namespace Discord /// ChannelPermissions GetPermissions(IGuildChannel channel); + /// + /// Gets the guild avatar URL for this user. + /// + /// + /// This property retrieves a URL for this guild user's guild specific avatar. In event that the user does not have a valid guild avatar + /// (i.e. their avatar identifier is not set), this method will return null. + /// + /// The format to return. + /// The size of the image to return in. This can be any power of two between 16 and 2048. + /// + /// + /// A string representing the user's avatar URL; null if the user does not have an avatar in place. + /// + string GetGuildAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128); /// /// Kicks this user from this guild. /// diff --git a/src/Discord.Net.Rest/API/Common/GuildMember.cs b/src/Discord.Net.Rest/API/Common/GuildMember.cs index 53f4908bf..9b888e86a 100644 --- a/src/Discord.Net.Rest/API/Common/GuildMember.cs +++ b/src/Discord.Net.Rest/API/Common/GuildMember.cs @@ -9,6 +9,8 @@ namespace Discord.API public User User { get; set; } [JsonProperty("nick")] public Optional Nick { get; set; } + [JsonProperty("avatar")] + public Optional Avatar { get; set; } [JsonProperty("roles")] public Optional Roles { get; set; } [JsonProperty("joined_at")] diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index 556e5e124..2e184d32e 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -21,6 +21,8 @@ namespace Discord.Rest /// public string Nickname { get; private set; } + /// + public string GuildAvatarId { get; private set; } internal IGuild Guild { get; private set; } /// public bool IsDeafened { get; private set; } @@ -80,6 +82,8 @@ namespace Discord.Rest _joinedAtTicks = model.JoinedAt.Value.UtcTicks; if (model.Nick.IsSpecified) Nickname = model.Nick.Value; + if (model.Avatar.IsSpecified) + GuildAvatarId = model.Avatar.Value; if (model.Deaf.IsSpecified) IsDeafened = model.Deaf.Value; if (model.Mute.IsSpecified) @@ -156,6 +160,9 @@ namespace Discord.Rest var guildPerms = GuildPermissions; return new ChannelPermissions(Permissions.ResolveChannel(Guild, this, channel, guildPerms.RawValue)); } + + public string GetGuildAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) + => CDN.GetGuildUserAvatarUrl(Id, GuildId, GuildAvatarId, size, format); #endregion #region IGuildUser diff --git a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs index 561cd92ee..2cd19da41 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestWebhookUser.cs @@ -54,6 +54,10 @@ namespace Discord.Rest /// string IGuildUser.Nickname => null; /// + string IGuildUser.GuildAvatarId => null; + /// + string IGuildUser.GetGuildAvatarUrl(ImageFormat format, ushort size) => null; + /// bool? IGuildUser.IsPending => null; /// int IGuildUser.Hierarchy => 0; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 8dd48891b..314471b17 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -30,7 +30,8 @@ namespace Discord.WebSocket public SocketGuild Guild { get; } /// public string Nickname { get; private set; } - + /// + public string GuildAvatarId { get; private set; } /// public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } } /// @@ -154,6 +155,8 @@ namespace Discord.WebSocket _joinedAtTicks = model.JoinedAt.Value.UtcTicks; if (model.Nick.IsSpecified) Nickname = model.Nick.Value; + if (model.Avatar.IsSpecified) + GuildAvatarId = model.Avatar.Value; if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); if (model.PremiumSince.IsSpecified) @@ -218,6 +221,8 @@ namespace Discord.WebSocket /// public ChannelPermissions GetPermissions(IGuildChannel channel) => new ChannelPermissions(Permissions.ResolveChannel(Guild, this, channel, GuildPermissions.RawValue)); + public string GetGuildAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) + => CDN.GetGuildUserAvatarUrl(Id, Guild.Id, GuildAvatarId, size, format); private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")}, Guild)"; internal new SocketGuildUser Clone() => MemberwiseClone() as SocketGuildUser; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs index c91921379..42fb807a1 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketThreadUser.cs @@ -52,6 +52,9 @@ namespace Discord.WebSocket get => GuildUser.AvatarId; internal set => GuildUser.AvatarId = value; } + /// + public string GuildAvatarId + => GuildUser.GuildAvatarId; /// public override string BannerId @@ -205,6 +208,8 @@ namespace Discord.WebSocket /// IReadOnlyCollection IGuildUser.RoleIds => GuildUser.Roles.Select(x => x.Id).ToImmutableArray(); + string IGuildUser.GetGuildAvatarUrl(ImageFormat format, ushort size) => GuildUser.GetGuildAvatarUrl(format, size); + internal override SocketGlobalUser GlobalUser => GuildUser.GlobalUser; internal override SocketPresence Presence { get => GuildUser.Presence; set => GuildUser.Presence = value; } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs index d68414ebb..7cc7d5a44 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs @@ -81,6 +81,10 @@ namespace Discord.WebSocket /// string IGuildUser.Nickname => null; /// + string IGuildUser.GuildAvatarId => null; + /// + string IGuildUser.GetGuildAvatarUrl(ImageFormat format, ushort size) => null; + /// DateTimeOffset? IGuildUser.PremiumSince => null; /// bool? IGuildUser.IsPending => null;