From 79b455ecf48bd74ca11ff9240024a5e921a041ed Mon Sep 17 00:00:00 2001 From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> Date: Sun, 13 Jul 2025 23:22:04 +0300 Subject: [PATCH] [Feature] User primary guild (#3156) --- src/Discord.Net.Core/CDN.cs | 9 ++++ src/Discord.Net.Core/Entities/Users/IUser.cs | 5 ++ .../Entities/Users/PrimaryGuild.cs | 46 +++++++++++++++++++ .../API/Common/PrimaryGuild.cs | 18 ++++++++ src/Discord.Net.Rest/API/Common/User.cs | 3 ++ .../Entities/Users/RestUser.cs | 14 ++++++ .../Entities/Users/SocketUser.cs | 29 ++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 src/Discord.Net.Core/Entities/Users/PrimaryGuild.cs create mode 100644 src/Discord.Net.Rest/API/Common/PrimaryGuild.cs diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 05bd257a..c9994048 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -285,5 +285,14 @@ namespace Discord /// public static string GetAvatarDecorationUrl(string avatarDecorationHash) => $"{DiscordConfig.CDNUrl}avatar-decoration-presets/{avatarDecorationHash}.png"; + + /// + /// Gets a guild tag badge url based off the hash. + /// + /// + /// A URL to the guild tag badge. + /// + public static string GetGuildTagBadgeUrl(ulong guildId, string badgeHash) + => $"{DiscordConfig.CDNUrl} guild-tag-badges/{guildId}/{badgeHash}.png"; } } diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs index 15e7f7aa..3ba4c4ff 100644 --- a/src/Discord.Net.Core/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -122,6 +122,11 @@ namespace Discord /// ulong? AvatarDecorationSkuId { get; } + /// + /// Gets the user's primary guild. if one is not set. + /// + PrimaryGuild? PrimaryGuild { get; } + /// /// Creates the direct message channel of this user. /// diff --git a/src/Discord.Net.Core/Entities/Users/PrimaryGuild.cs b/src/Discord.Net.Core/Entities/Users/PrimaryGuild.cs new file mode 100644 index 00000000..4cd02105 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/PrimaryGuild.cs @@ -0,0 +1,46 @@ +namespace Discord; + +/// +/// Represents a primary guild object. +/// +public readonly struct PrimaryGuild +{ + /// + /// Gets the id of the user's primary guild. + /// + public ulong? GuildId { get; } + + /// + /// Gets whether the user is displaying the primary guild's server tag. + /// + /// + /// This property will be if the system clears the identity, e.g. because the server no longer supports tags. + /// + public bool? IdentityEnabled { get; } + + /// + /// Gets the text of the user's server tag. + /// + public string Tag { get; } + + /// + /// Gets the hash of the guild tag badge. + /// + public string BadgeHash { get; } + + internal PrimaryGuild(ulong? guildId, bool? identityEnabled, string tag, string badgeHash) + { + GuildId = guildId; + IdentityEnabled = identityEnabled; + Tag = tag; + BadgeHash = badgeHash; + } + + /// + /// Gets the url for the tag badge. + /// + public string GetBadgeUrl() + => (GuildId is null || BadgeHash is null) + ? null + : CDN.GetGuildTagBadgeUrl(GuildId.Value, BadgeHash); +} diff --git a/src/Discord.Net.Rest/API/Common/PrimaryGuild.cs b/src/Discord.Net.Rest/API/Common/PrimaryGuild.cs new file mode 100644 index 00000000..9930e0f4 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/PrimaryGuild.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Discord.API; + +internal class PrimaryGuild +{ + [JsonProperty("identity_guild_id")] + public ulong? GuildId { get; set; } + + [JsonProperty("identity_enabled")] + public bool? IdentityEnabled { get; set; } + + [JsonProperty("tag")] + public string Tag { get; set; } + + [JsonProperty("badge")] + public string BadgeHash { get; set; } +} diff --git a/src/Discord.Net.Rest/API/Common/User.cs b/src/Discord.Net.Rest/API/Common/User.cs index bebb995c..b873a1b1 100644 --- a/src/Discord.Net.Rest/API/Common/User.cs +++ b/src/Discord.Net.Rest/API/Common/User.cs @@ -27,6 +27,9 @@ namespace Discord.API [JsonProperty("avatar_decoration_data")] public Optional AvatarDecoration { get; set; } + [JsonProperty("primary_guild")] + public Optional PrimaryGuild { get; set; } + //CurrentUser [JsonProperty("verified")] public Optional Verified { get; set; } diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 7887228f..896adf35 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -72,6 +72,8 @@ namespace Discord.Rest /// public ulong? AvatarDecorationSkuId { get; private set; } + /// + public PrimaryGuild? PrimaryGuild { get; private set; } internal RestUser(BaseDiscordClient discord, ulong id) : base(discord, id) @@ -126,6 +128,18 @@ namespace Discord.Rest AvatarDecorationHash = model.AvatarDecoration.Value?.Asset; AvatarDecorationSkuId = model.AvatarDecoration.Value?.SkuId; } + + if (model.PrimaryGuild.IsSpecified) + { + if (model.PrimaryGuild.Value is null) + PrimaryGuild = null; + else + PrimaryGuild = new( + model.PrimaryGuild.Value.GuildId, + model.PrimaryGuild.Value.IdentityEnabled, + model.PrimaryGuild.Value.Tag, + model.PrimaryGuild.Value.BadgeHash); + } } /// diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 5705f666..02cd8aa4 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -54,6 +54,9 @@ namespace Discord.WebSocket /// public ulong? AvatarDecorationSkuId { get; private set; } + /// + public PrimaryGuild? PrimaryGuild { get; private set; } + /// /// Gets mutual guilds shared with this user. /// @@ -113,6 +116,32 @@ namespace Discord.WebSocket hasChanges = true; } + if (model.PrimaryGuild.IsSpecified) + { + if (model.PrimaryGuild.Value is null) + { + PrimaryGuild = null; + if (PrimaryGuild is not null) + hasChanges = true; + } + else + { + if (PrimaryGuild?.GuildId != model.PrimaryGuild.Value.GuildId || + PrimaryGuild?.BadgeHash != model.PrimaryGuild.Value.BadgeHash || + PrimaryGuild?.Tag != model.PrimaryGuild.Value.Tag|| + PrimaryGuild?.IdentityEnabled != model.PrimaryGuild.Value.IdentityEnabled) + { + PrimaryGuild = new( + model.PrimaryGuild.Value.GuildId, + model.PrimaryGuild.Value.IdentityEnabled, + model.PrimaryGuild.Value.Tag, + model.PrimaryGuild.Value.BadgeHash); + + hasChanges = true; + } + } + } + return hasChanges; }