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;
}