From 4e1d3c907898ed7066f27270531c10eddcac1662 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:16:25 +0100 Subject: [PATCH 01/35] Added clan property to user object --- discord/user.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/discord/user.py b/discord/user.py index c5391372a..f12beb61e 100644 --- a/discord/user.py +++ b/discord/user.py @@ -32,6 +32,7 @@ from .colour import Colour from .enums import DefaultAvatar from .flags import PublicUserFlags from .utils import snowflake_time, _bytes_to_base64_data, MISSING, _get_as_snowflake +from .clan import Clan if TYPE_CHECKING: from typing_extensions import Self @@ -71,6 +72,7 @@ class BaseUser(_UserTag): '_public_flags', '_state', '_avatar_decoration_data', + '_clan' ) if TYPE_CHECKING: @@ -123,6 +125,7 @@ class BaseUser(_UserTag): self.bot = data.get('bot', False) self.system = data.get('system', False) self._avatar_decoration_data = data.get('avatar_decoration_data') + self._clan = data.get('clan') @classmethod def _copy(cls, user: Self) -> Self: @@ -139,6 +142,7 @@ class BaseUser(_UserTag): self._state = user._state self._public_flags = user._public_flags self._avatar_decoration_data = user._avatar_decoration_data + self._clan = user._clan return self @@ -304,6 +308,15 @@ class BaseUser(_UserTag): if self.global_name: return self.global_name return self.name + + @property + def clan(self) -> Optional[Clan]: + """:class:`Clan`: Returns the user's clan, if applicable. + + If the user has not set a clan, ``None`` is returned.""" + if self._clan: + return self._clan + return None def mentioned_in(self, message: Message) -> bool: """Checks if the user is mentioned in the specified message. From a9413a6d9c15d536d90bc48f0014f1cde447a4e9 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:19:05 +0100 Subject: [PATCH 02/35] Added base code for clan properties --- discord/clan.py | 74 +++++++++++++++++++++++++++++++++++++++++++ discord/types/clan.py | 31 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 discord/clan.py create mode 100644 discord/types/clan.py diff --git a/discord/clan.py b/discord/clan.py new file mode 100644 index 000000000..8eb489418 --- /dev/null +++ b/discord/clan.py @@ -0,0 +1,74 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from typing import TYPE_CHECKING + +from .asset import Asset +from .utils import snowflake_time + +if TYPE_CHECKING: + from datetime import datetime + + from .types.clan import Clan as ClanPayload + from .state import ConnectionState + + +class Clan: + __slots__ = ( + 'guild_id', + 'identity_enabled', + 'tag', + '_badge', + '_state' + ) + + if TYPE_CHECKING: + guild_id: int + identity_enabled: bool + tag: str + _badge: str + _state: ConnectionState + + def __init__(self, *, state: ConnectionState, data: ClanPayload) -> None: + self._state = state + self._update(data) + + def _update(self, data: ClanPayload): + self.guild_id = data["identity_guild_id"] + self.identity_enabled = data['identity_enabled'] + self.tag = data['tag'] + self._badge = data['badge'] + + @property + def badge(self) -> Asset: + """:class:`Asset`: Returns the clan's asset""" + return Asset._from_clan(self._state, self.guild_id, self._badge) + + @property + def created_at(self) -> datetime: + """:class:`datetime.datetime`: Returns the guilds's creation time in UTC. + + This is when the guild was created. + """ + return snowflake_time(self.guild_id) diff --git a/discord/types/clan.py b/discord/types/clan.py new file mode 100644 index 000000000..e21e30189 --- /dev/null +++ b/discord/types/clan.py @@ -0,0 +1,31 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from typing import TypedDict + +class Clan(TypedDict): + identity_guild_id: int + identity_enabled: bool + tag: str + badge: str \ No newline at end of file From b422aaf12b7feb7a4f8b331f0ec46e8a4413662d Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:19:29 +0100 Subject: [PATCH 03/35] Added getting clan tag asset --- discord/asset.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/discord/asset.py b/discord/asset.py index e3422f311..fbe2ae71f 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -346,6 +346,15 @@ class Asset(AssetMixin): animated=animated, ) + @classmethod + def _from_clan(cls, state: _State, guild_id: int, icon_hash: str) -> Self: + return cls( + state, + url=f'{cls.BASE}/clan-badges/{guild_id}/{icon_hash}.png?size=16', + key=icon_hash, + animated=False, + ) + def __str__(self) -> str: return self._url From 81e63bc90c598cff7e788283c41c7979738882f8 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:27:08 +0100 Subject: [PATCH 04/35] Fix type corrections --- discord/user.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/discord/user.py b/discord/user.py index f12beb61e..921bdcce6 100644 --- a/discord/user.py +++ b/discord/user.py @@ -43,6 +43,7 @@ if TYPE_CHECKING: from .guild import Guild from .message import Message from .state import ConnectionState + from .types.clan import Clan as ClanPayload from .types.channel import DMChannel as DMChannelPayload from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData @@ -88,6 +89,7 @@ class BaseUser(_UserTag): _accent_colour: Optional[int] _public_flags: int _avatar_decoration_data: Optional[AvatarDecorationData] + _clan: Optional[ClanPayload] def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None: self._state = state @@ -125,7 +127,7 @@ class BaseUser(_UserTag): self.bot = data.get('bot', False) self.system = data.get('system', False) self._avatar_decoration_data = data.get('avatar_decoration_data') - self._clan = data.get('clan') + self._clan = data.get('clan', None) @classmethod def _copy(cls, user: Self) -> Self: @@ -315,7 +317,7 @@ class BaseUser(_UserTag): If the user has not set a clan, ``None`` is returned.""" if self._clan: - return self._clan + return Clan(state=self._state, data=self._clan) return None def mentioned_in(self, message: Message) -> bool: From 644aba82af2c501f39a6d4df0a9b00707e4c041d Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:29:45 +0100 Subject: [PATCH 05/35] Comments --- discord/clan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/clan.py b/discord/clan.py index 8eb489418..fddf17f86 100644 --- a/discord/clan.py +++ b/discord/clan.py @@ -67,8 +67,8 @@ class Clan: @property def created_at(self) -> datetime: - """:class:`datetime.datetime`: Returns the guilds's creation time in UTC. + """:class:`datetime.datetime`: Returns the clan's guild creation time in UTC. - This is when the guild was created. + This is when the guild, of that clan tag, was created. """ return snowflake_time(self.guild_id) From e3e234cf7371891dc4856cfb53efae7fd442e22f Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 02:46:43 +0100 Subject: [PATCH 06/35] Fix circular import issues --- discord/clan.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/discord/clan.py b/discord/clan.py index fddf17f86..6d0c01f87 100644 --- a/discord/clan.py +++ b/discord/clan.py @@ -23,14 +23,13 @@ DEALINGS IN THE SOFTWARE. """ from typing import TYPE_CHECKING +from datetime import datetime from .asset import Asset from .utils import snowflake_time +from .types.clan import Clan as ClanPayload if TYPE_CHECKING: - from datetime import datetime - - from .types.clan import Clan as ClanPayload from .state import ConnectionState @@ -50,7 +49,7 @@ class Clan: _badge: str _state: ConnectionState - def __init__(self, *, state: ConnectionState, data: ClanPayload) -> None: + def __init__(self, *, state, data: ClanPayload) -> None: self._state = state self._update(data) @@ -72,3 +71,9 @@ class Clan: This is when the guild, of that clan tag, was created. """ return snowflake_time(self.guild_id) + + def __repr__(self) -> str: + return ( + f"" + ) From 106ad77cc2737dd15aa094c64076376e87c383a9 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 13:05:11 +0100 Subject: [PATCH 07/35] Rename all clan references to primary_guild --- discord/asset.py | 2 +- discord/{clan.py => primary_guild.py} | 16 +++++++-------- discord/types/{clan.py => primary_guild.py} | 2 +- discord/user.py | 22 ++++++++++----------- 4 files changed, 20 insertions(+), 22 deletions(-) rename discord/{clan.py => primary_guild.py} (82%) rename discord/types/{clan.py => primary_guild.py} (96%) diff --git a/discord/asset.py b/discord/asset.py index fbe2ae71f..8cdc00ed9 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -347,7 +347,7 @@ class Asset(AssetMixin): ) @classmethod - def _from_clan(cls, state: _State, guild_id: int, icon_hash: str) -> Self: + def _from_primary_guild(cls, state: _State, guild_id: int, icon_hash: str) -> Self: return cls( state, url=f'{cls.BASE}/clan-badges/{guild_id}/{icon_hash}.png?size=16', diff --git a/discord/clan.py b/discord/primary_guild.py similarity index 82% rename from discord/clan.py rename to discord/primary_guild.py index 6d0c01f87..6c87efb01 100644 --- a/discord/clan.py +++ b/discord/primary_guild.py @@ -27,13 +27,13 @@ from datetime import datetime from .asset import Asset from .utils import snowflake_time -from .types.clan import Clan as ClanPayload +from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload if TYPE_CHECKING: from .state import ConnectionState -class Clan: +class PrimaryGuild: __slots__ = ( 'guild_id', 'identity_enabled', @@ -49,11 +49,11 @@ class Clan: _badge: str _state: ConnectionState - def __init__(self, *, state, data: ClanPayload) -> None: + def __init__(self, *, state, data: PrimaryGuildPayload) -> None: self._state = state self._update(data) - def _update(self, data: ClanPayload): + def _update(self, data: PrimaryGuildPayload): self.guild_id = data["identity_guild_id"] self.identity_enabled = data['identity_enabled'] self.tag = data['tag'] @@ -61,14 +61,12 @@ class Clan: @property def badge(self) -> Asset: - """:class:`Asset`: Returns the clan's asset""" - return Asset._from_clan(self._state, self.guild_id, self._badge) + """:class:`Asset`: Returns the primary guild's asset""" + return Asset._from_primary_guild(self._state, self.guild_id, self._badge) @property def created_at(self) -> datetime: - """:class:`datetime.datetime`: Returns the clan's guild creation time in UTC. - - This is when the guild, of that clan tag, was created. + """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC. """ return snowflake_time(self.guild_id) diff --git a/discord/types/clan.py b/discord/types/primary_guild.py similarity index 96% rename from discord/types/clan.py rename to discord/types/primary_guild.py index e21e30189..b99f52641 100644 --- a/discord/types/clan.py +++ b/discord/types/primary_guild.py @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. from typing import TypedDict -class Clan(TypedDict): +class PrimaryGuild(TypedDict): identity_guild_id: int identity_enabled: bool tag: str diff --git a/discord/user.py b/discord/user.py index 921bdcce6..9dddefeac 100644 --- a/discord/user.py +++ b/discord/user.py @@ -32,7 +32,7 @@ from .colour import Colour from .enums import DefaultAvatar from .flags import PublicUserFlags from .utils import snowflake_time, _bytes_to_base64_data, MISSING, _get_as_snowflake -from .clan import Clan +from .primary_guild import PrimaryGuild if TYPE_CHECKING: from typing_extensions import Self @@ -43,7 +43,7 @@ if TYPE_CHECKING: from .guild import Guild from .message import Message from .state import ConnectionState - from .types.clan import Clan as ClanPayload + from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload from .types.channel import DMChannel as DMChannelPayload from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData @@ -73,7 +73,7 @@ class BaseUser(_UserTag): '_public_flags', '_state', '_avatar_decoration_data', - '_clan' + '_primary_guild' ) if TYPE_CHECKING: @@ -89,7 +89,7 @@ class BaseUser(_UserTag): _accent_colour: Optional[int] _public_flags: int _avatar_decoration_data: Optional[AvatarDecorationData] - _clan: Optional[ClanPayload] + _primary_guild: Optional[PrimaryGuildPayload] def __init__(self, *, state: ConnectionState, data: Union[UserPayload, PartialUserPayload]) -> None: self._state = state @@ -127,7 +127,7 @@ class BaseUser(_UserTag): self.bot = data.get('bot', False) self.system = data.get('system', False) self._avatar_decoration_data = data.get('avatar_decoration_data') - self._clan = data.get('clan', None) + self._primary_guild = data.get('primary_guild', None) @classmethod def _copy(cls, user: Self) -> Self: @@ -144,7 +144,7 @@ class BaseUser(_UserTag): self._state = user._state self._public_flags = user._public_flags self._avatar_decoration_data = user._avatar_decoration_data - self._clan = user._clan + self._primary_guild = user._primary_guild return self @@ -312,12 +312,12 @@ class BaseUser(_UserTag): return self.name @property - def clan(self) -> Optional[Clan]: - """:class:`Clan`: Returns the user's clan, if applicable. + def primary_guild(self) -> Optional[PrimaryGuild]: + """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable. - If the user has not set a clan, ``None`` is returned.""" - if self._clan: - return Clan(state=self._state, data=self._clan) + \nIf the user has not set a primary guild, ``None`` is returned.""" + if self._primary_guild: + return PrimaryGuild(state=self._state, data=self._primary_guild) return None def mentioned_in(self, message: Message) -> bool: From cb6d304608564ac440754e3e0fa7c12e9db204b2 Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Wed, 18 Jun 2025 13:34:25 +0100 Subject: [PATCH 08/35] Apply suggestions from code review Co-authored-by: DA344 <108473820+DA-344@users.noreply.github.com> --- discord/primary_guild.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 6c87efb01..3bb71a896 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -54,7 +54,7 @@ class PrimaryGuild: self._update(data) def _update(self, data: PrimaryGuildPayload): - self.guild_id = data["identity_guild_id"] + self.guild_id = data['identity_guild_id'] self.identity_enabled = data['identity_enabled'] self.tag = data['tag'] self._badge = data['badge'] @@ -66,12 +66,11 @@ class PrimaryGuild: @property def created_at(self) -> datetime: - """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC. - """ + """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" return snowflake_time(self.guild_id) def __repr__(self) -> str: return ( - f"" + f'' ) From 1aa48a17ec9ae9e1de4f1b4f7aec87e6526401b3 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 13:35:39 +0100 Subject: [PATCH 09/35] Fix import issues --- discord/primary_guild.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 3bb71a896..cd30f9575 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -22,15 +22,17 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from __future__ import annotations + from typing import TYPE_CHECKING from datetime import datetime from .asset import Asset from .utils import snowflake_time -from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload if TYPE_CHECKING: from .state import ConnectionState + from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload class PrimaryGuild: From ddf68cb69b36822c923ae4e3863ea61e645c55bf Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 13:43:25 +0100 Subject: [PATCH 10/35] Fix as identity_enabled is only field assured to be returned --- discord/primary_guild.py | 42 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index cd30f9575..9f7515f9d 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING from datetime import datetime from .asset import Asset @@ -37,17 +37,17 @@ if TYPE_CHECKING: class PrimaryGuild: __slots__ = ( - 'guild_id', + '_guild_id', 'identity_enabled', - 'tag', + '_tag', '_badge', '_state' ) if TYPE_CHECKING: - guild_id: int + _guild_id: int identity_enabled: bool - tag: str + _tag: str _badge: str _state: ConnectionState @@ -56,20 +56,38 @@ class PrimaryGuild: self._update(data) def _update(self, data: PrimaryGuildPayload): - self.guild_id = data['identity_guild_id'] + self._guild_id = data.get('identity_guild_id', None) self.identity_enabled = data['identity_enabled'] - self.tag = data['tag'] - self._badge = data['badge'] + self._tag = data.get('tag', None) + self._badge = data.get('badge') @property - def badge(self) -> Asset: + def guild_id(self) -> Optional[int]: + """:class:`int`: Returns the primary guild's id""" + if self._guild_id: + return self._guild_id + return None + + @property + def tag(self) -> Optional[str]: + """:class:`str`: Return's the primary guild's tag""" + if self._tag: + return self._tag + return None + + @property + def badge(self) -> Optional[Asset]: """:class:`Asset`: Returns the primary guild's asset""" - return Asset._from_primary_guild(self._state, self.guild_id, self._badge) + if self._badge and self.guild_id: + return Asset._from_primary_guild(self._state, self.guild_id, self._badge) + return None @property - def created_at(self) -> datetime: + def created_at(self) -> Optional[datetime]: """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" - return snowflake_time(self.guild_id) + if self.guild_id: + return snowflake_time(self.guild_id) + return None def __repr__(self) -> str: return ( From 6eb8e05e49359932bfa8bec5d106a8d56a3d5398 Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Wed, 18 Jun 2025 13:45:57 +0100 Subject: [PATCH 11/35] Apply change from code review Forgot to add this one Co-authored-by: DA344 <108473820+DA-344@users.noreply.github.com> --- discord/user.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/discord/user.py b/discord/user.py index 9dddefeac..688b41368 100644 --- a/discord/user.py +++ b/discord/user.py @@ -313,9 +313,7 @@ class BaseUser(_UserTag): @property def primary_guild(self) -> Optional[PrimaryGuild]: - """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable. - - \nIf the user has not set a primary guild, ``None`` is returned.""" + """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable.""" if self._primary_guild: return PrimaryGuild(state=self._state, data=self._primary_guild) return None From f6d93c4e1ce42e67e7b815575f9c52059578e376 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 13:12:00 +0000 Subject: [PATCH 12/35] Styling changes --- discord/primary_guild.py | 16 +++++----------- discord/types/primary_guild.py | 3 ++- discord/user.py | 4 ++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 9f7515f9d..6c5b637fe 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -36,13 +36,7 @@ if TYPE_CHECKING: class PrimaryGuild: - __slots__ = ( - '_guild_id', - 'identity_enabled', - '_tag', - '_badge', - '_state' - ) + __slots__ = ('_guild_id', 'identity_enabled', '_tag', '_badge', '_state') if TYPE_CHECKING: _guild_id: int @@ -50,7 +44,7 @@ class PrimaryGuild: _tag: str _badge: str _state: ConnectionState - + def __init__(self, *, state, data: PrimaryGuildPayload) -> None: self._state = state self._update(data) @@ -60,7 +54,7 @@ class PrimaryGuild: self.identity_enabled = data['identity_enabled'] self._tag = data.get('tag', None) self._badge = data.get('badge') - + @property def guild_id(self) -> Optional[int]: """:class:`int`: Returns the primary guild's id""" @@ -81,14 +75,14 @@ class PrimaryGuild: if self._badge and self.guild_id: return Asset._from_primary_guild(self._state, self.guild_id, self._badge) return None - + @property def created_at(self) -> Optional[datetime]: """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" if self.guild_id: return snowflake_time(self.guild_id) return None - + def __repr__(self) -> str: return ( f' Optional[PrimaryGuild]: """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable.""" From 2f4000d7b2baf37ed2077c1025da8a4313c4a29d Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 16:29:34 +0100 Subject: [PATCH 13/35] Add docs for PrimaryGuild --- discord/__init__.py | 1 + discord/primary_guild.py | 1 + docs/api.rst | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/discord/__init__.py b/discord/__init__.py index 48fe10925..3e6e1c0e6 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -73,6 +73,7 @@ from .poll import * from .soundboard import * from .subscription import * from .presences import * +from .primary_guild import * class VersionInfo(NamedTuple): diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 6c5b637fe..afc84e2d4 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -36,6 +36,7 @@ if TYPE_CHECKING: class PrimaryGuild: + r"""Represents the primary guild (formally known as a clan) of a :class:`User`""" __slots__ = ('_guild_id', 'identity_enabled', '_tag', '_badge', '_state') if TYPE_CHECKING: diff --git a/docs/api.rst b/docs/api.rst index e366f63bf..6d2f1fc7f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5453,6 +5453,14 @@ ClientStatus .. autoclass:: ClientStatus() :members: +PrimaryGuild +~~~~~~~~~~~~ + +.. attributetable:: PrimaryGuild + +.. autoclass:: PrimaryGuild() + :members: + Data Classes -------------- From db82935a9a768f88094b036be3ea6d8d07e1791b Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 17:40:07 +0100 Subject: [PATCH 14/35] tab complete was not working for members. Not too sure if this is the correct fix, but it works --- discord/member.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discord/member.py b/discord/member.py index 6af1571f4..0007d0922 100644 --- a/discord/member.py +++ b/discord/member.py @@ -74,6 +74,7 @@ if TYPE_CHECKING: GuildVoiceState as GuildVoiceStatePayload, VoiceState as VoiceStatePayload, ) + from .primary_guild import PrimaryGuild VocalGuildChannel = Union[VoiceChannel, StageChannel] @@ -308,6 +309,7 @@ class Member(discord.abc.Messageable, _UserTag): accent_colour: Optional[Colour] avatar_decoration: Optional[Asset] avatar_decoration_sku_id: Optional[int] + primary_guild: Optional[PrimaryGuild] def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: ConnectionState): self._state: ConnectionState = state From 9dc9c4072c65dc6075674e8a8d683d3455c42523 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 17:56:39 +0100 Subject: [PATCH 15/35] guild_id would sometimes return a string, convert to make sure it is always an int --- discord/primary_guild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index afc84e2d4..8e267dc8d 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -60,7 +60,7 @@ class PrimaryGuild: def guild_id(self) -> Optional[int]: """:class:`int`: Returns the primary guild's id""" if self._guild_id: - return self._guild_id + return int(self._guild_id) return None @property From 82b948316832c7a0242b42c4800ffa8d5231dc2e Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Wed, 18 Jun 2025 20:18:09 +0100 Subject: [PATCH 16/35] Apply suggestions from code review Co-authored-by: dolfies --- discord/asset.py | 2 +- discord/primary_guild.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/asset.py b/discord/asset.py index 8cdc00ed9..0753e61da 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -350,7 +350,7 @@ class Asset(AssetMixin): def _from_primary_guild(cls, state: _State, guild_id: int, icon_hash: str) -> Self: return cls( state, - url=f'{cls.BASE}/clan-badges/{guild_id}/{icon_hash}.png?size=16', + url=f'{cls.BASE}/clan-badges/{guild_id}/{icon_hash}.png?size=64', key=icon_hash, animated=False, ) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 8e267dc8d..b40f1929f 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -51,7 +51,7 @@ class PrimaryGuild: self._update(data) def _update(self, data: PrimaryGuildPayload): - self._guild_id = data.get('identity_guild_id', None) + self.id = _get_as_snowflake(data, 'identity_guild_id') self.identity_enabled = data['identity_enabled'] self._tag = data.get('tag', None) self._badge = data.get('badge') From d63672e0a14c195024d450aad5a556551cd1c06d Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 20:27:01 +0100 Subject: [PATCH 17/35] Change guild_id to id --- discord/primary_guild.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index b40f1929f..6ed789256 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -28,7 +28,7 @@ from typing import Optional, TYPE_CHECKING from datetime import datetime from .asset import Asset -from .utils import snowflake_time +from .utils import snowflake_time, _get_as_snowflake if TYPE_CHECKING: from .state import ConnectionState @@ -36,11 +36,11 @@ if TYPE_CHECKING: class PrimaryGuild: - r"""Represents the primary guild (formally known as a clan) of a :class:`User`""" - __slots__ = ('_guild_id', 'identity_enabled', '_tag', '_badge', '_state') + """Represents the primary guild (formally known as a clan) of a :class:`User`""" + __slots__ = ('_id', 'identity_enabled', '_tag', '_badge', '_state') if TYPE_CHECKING: - _guild_id: int + _id: Optional[int] identity_enabled: bool _tag: str _badge: str @@ -51,16 +51,16 @@ class PrimaryGuild: self._update(data) def _update(self, data: PrimaryGuildPayload): - self.id = _get_as_snowflake(data, 'identity_guild_id') + self._id = _get_as_snowflake(data, 'identity_guild_id') self.identity_enabled = data['identity_enabled'] self._tag = data.get('tag', None) self._badge = data.get('badge') @property - def guild_id(self) -> Optional[int]: + def id(self) -> Optional[int]: """:class:`int`: Returns the primary guild's id""" - if self._guild_id: - return int(self._guild_id) + if self._id: + return self._id return None @property @@ -73,19 +73,19 @@ class PrimaryGuild: @property def badge(self) -> Optional[Asset]: """:class:`Asset`: Returns the primary guild's asset""" - if self._badge and self.guild_id: - return Asset._from_primary_guild(self._state, self.guild_id, self._badge) + if self._badge and self._id: + return Asset._from_primary_guild(self._state, self._id, self._badge) return None @property def created_at(self) -> Optional[datetime]: """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" - if self.guild_id: - return snowflake_time(self.guild_id) + if self._id: + return snowflake_time(self._id) return None def __repr__(self) -> str: return ( - f'' ) From 3a8e44150f198a31917991dd12291918a675a3d8 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 20:31:52 +0100 Subject: [PATCH 18/35] Style fixes --- discord/primary_guild.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 6ed789256..89f769306 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -37,6 +37,7 @@ if TYPE_CHECKING: class PrimaryGuild: """Represents the primary guild (formally known as a clan) of a :class:`User`""" + __slots__ = ('_id', 'identity_enabled', '_tag', '_badge', '_state') if TYPE_CHECKING: @@ -85,7 +86,4 @@ class PrimaryGuild: return None def __repr__(self) -> str: - return ( - f'' - ) + return f'' From f718627cf331af3e48b050df568e83f0a1d4687b Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 20:46:35 +0100 Subject: [PATCH 19/35] Remove unnecessary returning of private attributes --- discord/primary_guild.py | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 89f769306..a27a614e0 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -38,12 +38,12 @@ if TYPE_CHECKING: class PrimaryGuild: """Represents the primary guild (formally known as a clan) of a :class:`User`""" - __slots__ = ('_id', 'identity_enabled', '_tag', '_badge', '_state') + __slots__ = ('id', 'identity_enabled', 'tag', '_badge', '_state') if TYPE_CHECKING: - _id: Optional[int] + id: Optional[int] identity_enabled: bool - _tag: str + tag: Optional[str] _badge: str _state: ConnectionState @@ -52,37 +52,23 @@ class PrimaryGuild: self._update(data) def _update(self, data: PrimaryGuildPayload): - self._id = _get_as_snowflake(data, 'identity_guild_id') + self.id = _get_as_snowflake(data, 'identity_guild_id') self.identity_enabled = data['identity_enabled'] - self._tag = data.get('tag', None) + self.tag = data.get('tag', None) self._badge = data.get('badge') - @property - def id(self) -> Optional[int]: - """:class:`int`: Returns the primary guild's id""" - if self._id: - return self._id - return None - - @property - def tag(self) -> Optional[str]: - """:class:`str`: Return's the primary guild's tag""" - if self._tag: - return self._tag - return None - @property def badge(self) -> Optional[Asset]: """:class:`Asset`: Returns the primary guild's asset""" - if self._badge and self._id: - return Asset._from_primary_guild(self._state, self._id, self._badge) + if self._badge and self.id: + return Asset._from_primary_guild(self._state, self.id, self._badge) return None @property def created_at(self) -> Optional[datetime]: """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" - if self._id: - return snowflake_time(self._id) + if self.id: + return snowflake_time(self.id) return None def __repr__(self) -> str: From 15cff6a3c34feb6525286891b30ccc6ea6045f53 Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:23:06 +0100 Subject: [PATCH 20/35] Apply suggestions from code review Co-authored-by: dolfies --- discord/primary_guild.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index a27a614e0..fd14381e2 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -36,7 +36,21 @@ if TYPE_CHECKING: class PrimaryGuild: - """Represents the primary guild (formally known as a clan) of a :class:`User`""" + """Represents the primary guild identity of a :class:`User` + + Attributes + ----------- + id: Optional[:class:`int`] + The ID of the user's primary guild, if any. + tag: Optional[:class:`str`] + The guild's tag. + identity_enabled: Optional[:class:`bool`] + Whether the user has their primary guild publicly displayed. If ``None``, the user has a public guild but has not reaffirmed the guild identity after a change + + .. note:: + + Users can have their primary guild publicly displayed while still having an :attr:`id` of ``None``. Be careful when checking this attribute! + """ __slots__ = ('id', 'identity_enabled', 'tag', '_badge', '_state') @@ -59,14 +73,14 @@ class PrimaryGuild: @property def badge(self) -> Optional[Asset]: - """:class:`Asset`: Returns the primary guild's asset""" + """Optional[:class:`Asset`]: Returns the primary guild's asset""" if self._badge and self.id: return Asset._from_primary_guild(self._state, self.id, self._badge) return None @property def created_at(self) -> Optional[datetime]: - """:class:`datetime.datetime`: Returns the primary guild's creation time in UTC.""" + """Optional[:class:`datetime.datetime`]: Returns the primary guild's creation time in UTC.""" if self.id: return snowflake_time(self.id) return None From 7802aaa9aa99e3a660bd90a77b5754b1f8ef36df Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:28:04 +0100 Subject: [PATCH 21/35] Apply suggestions from code review Co-authored-by: dolfies --- discord/types/primary_guild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/discord/types/primary_guild.py b/discord/types/primary_guild.py index 9362e103f..f63fe6f44 100644 --- a/discord/types/primary_guild.py +++ b/discord/types/primary_guild.py @@ -26,7 +26,7 @@ from typing import TypedDict class PrimaryGuild(TypedDict): - identity_guild_id: int - identity_enabled: bool - tag: str - badge: str + identity_guild_id: Optional[int] + identity_enabled: Optional[bool] + tag: Optional[str] + badge: Optional[str] From 4929a17d2b3f8b8c387c4bda13928a665dd811ad Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 21:29:09 +0100 Subject: [PATCH 22/35] Mark identity_enabled and _badge as optional --- discord/primary_guild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index fd14381e2..dec682639 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: class PrimaryGuild: """Represents the primary guild identity of a :class:`User` - + Attributes ----------- id: Optional[:class:`int`] @@ -46,7 +46,7 @@ class PrimaryGuild: The guild's tag. identity_enabled: Optional[:class:`bool`] Whether the user has their primary guild publicly displayed. If ``None``, the user has a public guild but has not reaffirmed the guild identity after a change - + .. note:: Users can have their primary guild publicly displayed while still having an :attr:`id` of ``None``. Be careful when checking this attribute! @@ -56,9 +56,9 @@ class PrimaryGuild: if TYPE_CHECKING: id: Optional[int] - identity_enabled: bool + identity_enabled: Optional[bool] tag: Optional[str] - _badge: str + _badge: Optional[str] _state: ConnectionState def __init__(self, *, state, data: PrimaryGuildPayload) -> None: From 96bebf4e34aa2183da7d2d9e7f6e8a819682a88b Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 21:31:55 +0100 Subject: [PATCH 23/35] Checks that identity_enabled is true when returning a PrimaryGuild --- discord/types/primary_guild.py | 2 +- discord/user.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/types/primary_guild.py b/discord/types/primary_guild.py index f63fe6f44..ad8a7c338 100644 --- a/discord/types/primary_guild.py +++ b/discord/types/primary_guild.py @@ -22,7 +22,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from typing import TypedDict +from typing import TypedDict, Optional class PrimaryGuild(TypedDict): diff --git a/discord/user.py b/discord/user.py index ce0481d8f..44f15dfcd 100644 --- a/discord/user.py +++ b/discord/user.py @@ -314,7 +314,7 @@ class BaseUser(_UserTag): @property def primary_guild(self) -> Optional[PrimaryGuild]: """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable.""" - if self._primary_guild: + if self._primary_guild and self._primary_guild.get("identity_enabled"): return PrimaryGuild(state=self._state, data=self._primary_guild) return None From 23e054f8926e90f7fa103c2ab2d6277acea014af Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 22:34:18 +0100 Subject: [PATCH 24/35] Add default state for a PrimaryGuild --- discord/primary_guild.py | 11 ++++++++++- discord/user.py | 8 ++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index dec682639..9ad67cc05 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -33,6 +33,7 @@ from .utils import snowflake_time, _get_as_snowflake if TYPE_CHECKING: from .state import ConnectionState from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload + from typing_extensions import Self class PrimaryGuild: @@ -43,7 +44,9 @@ class PrimaryGuild: id: Optional[:class:`int`] The ID of the user's primary guild, if any. tag: Optional[:class:`str`] - The guild's tag. + The primary guild's tag. + badge: Optional[:class:`Asset`] + The primary guild's asset identity_enabled: Optional[:class:`bool`] Whether the user has their primary guild publicly displayed. If ``None``, the user has a public guild but has not reaffirmed the guild identity after a change @@ -85,5 +88,11 @@ class PrimaryGuild: return snowflake_time(self.id) return None + @classmethod + def _default(cls, state: ConnectionState) -> Self: + """Creates a blank :class:`PrimaryGuild`""" + payload: PrimaryGuildPayload = {"identity_guild_id": None, "identity_enabled": False, "tag": None, "badge": None} + return cls(state=state, data=payload) + def __repr__(self) -> str: return f'' diff --git a/discord/user.py b/discord/user.py index 44f15dfcd..4bd2ac21d 100644 --- a/discord/user.py +++ b/discord/user.py @@ -312,11 +312,11 @@ class BaseUser(_UserTag): return self.name @property - def primary_guild(self) -> Optional[PrimaryGuild]: - """:class:`PrimaryGuild`: Returns the user's primary guild, if applicable.""" - if self._primary_guild and self._primary_guild.get("identity_enabled"): + def primary_guild(self) -> PrimaryGuild: + """:class:`PrimaryGuild`: Returns the user's primary guild.""" + if self._primary_guild: return PrimaryGuild(state=self._state, data=self._primary_guild) - return None + return PrimaryGuild._default(self._state) def mentioned_in(self, message: Message) -> bool: """Checks if the user is mentioned in the specified message. From d94276218c04f6b306ece4cfc1e9fbe412a4e068 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 22:38:25 +0100 Subject: [PATCH 25/35] Fix doc issue that last commit created --- discord/primary_guild.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 9ad67cc05..c97d3ccb2 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -45,8 +45,6 @@ class PrimaryGuild: The ID of the user's primary guild, if any. tag: Optional[:class:`str`] The primary guild's tag. - badge: Optional[:class:`Asset`] - The primary guild's asset identity_enabled: Optional[:class:`bool`] Whether the user has their primary guild publicly displayed. If ``None``, the user has a public guild but has not reaffirmed the guild identity after a change From e27c62d39db9908f2a198ed3e54d0e5dbcee0186 Mon Sep 17 00:00:00 2001 From: blord0 Date: Wed, 18 Jun 2025 23:53:04 +0100 Subject: [PATCH 26/35] Fix type for member modal --- discord/member.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/member.py b/discord/member.py index 0007d0922..8d8c93ea8 100644 --- a/discord/member.py +++ b/discord/member.py @@ -309,7 +309,7 @@ class Member(discord.abc.Messageable, _UserTag): accent_colour: Optional[Colour] avatar_decoration: Optional[Asset] avatar_decoration_sku_id: Optional[int] - primary_guild: Optional[PrimaryGuild] + primary_guild: PrimaryGuild def __init__(self, *, data: MemberWithUserPayload, guild: Guild, state: ConnectionState): self._state: ConnectionState = state From 279ae2e270eeb23d9a7fd2d95a1399c504e1d5b0 Mon Sep 17 00:00:00 2001 From: blord0 Date: Thu, 19 Jun 2025 12:54:29 +0100 Subject: [PATCH 27/35] Update Member._update_inner_user() to return primary_guild --- discord/member.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/discord/member.py b/discord/member.py index 8d8c93ea8..423ab539c 100644 --- a/discord/member.py +++ b/discord/member.py @@ -453,9 +453,11 @@ class Member(discord.abc.Messageable, _UserTag): u.global_name, u._public_flags, u._avatar_decoration_data['sku_id'] if u._avatar_decoration_data is not None else None, + u._primary_guild, ) decoration_payload = user.get('avatar_decoration_data') + primary_guild_payload = user.get('primary_guild', None) # These keys seem to always be available modified = ( user['username'], @@ -464,16 +466,26 @@ class Member(discord.abc.Messageable, _UserTag): user.get('global_name'), user.get('public_flags', 0), decoration_payload['sku_id'] if decoration_payload is not None else None, + primary_guild_payload, ) if original != modified: to_return = User._copy(self._user) - u.name, u.discriminator, u._avatar, u.global_name, u._public_flags, u._avatar_decoration_data = ( + ( + u.name, + u.discriminator, + u._avatar, + u.global_name, + u._public_flags, + u._avatar_decoration_data, + u._primary_guild, + ) = ( user['username'], user['discriminator'], user['avatar'], user.get('global_name'), user.get('public_flags', 0), decoration_payload, + primary_guild_payload, ) # Signal to dispatch on_user_update return to_return, u From 878930a551e16d5c7cbdfd4dfd871c5857ad3413 Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:28:30 +0100 Subject: [PATCH 28/35] Apply suggestions from code review Co-authored-by: DA344 <108473820+DA-344@users.noreply.github.com> Co-authored-by: dolfies --- discord/primary_guild.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index c97d3ccb2..4d92d7a2a 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -88,9 +88,8 @@ class PrimaryGuild: @classmethod def _default(cls, state: ConnectionState) -> Self: - """Creates a blank :class:`PrimaryGuild`""" payload: PrimaryGuildPayload = {"identity_guild_id": None, "identity_enabled": False, "tag": None, "badge": None} return cls(state=state, data=payload) def __repr__(self) -> str: - return f'' + return f'' From 5c696898d59ee2c7496cb957b09a5b766727e42c Mon Sep 17 00:00:00 2001 From: blord0 Date: Thu, 19 Jun 2025 22:32:10 +0100 Subject: [PATCH 29/35] Move `primary_guild`'s type file into `user`'s as it was too small --- discord/primary_guild.py | 2 +- discord/types/primary_guild.py | 32 -------------------------------- discord/types/user.py | 6 ++++++ discord/user.py | 3 +-- 4 files changed, 8 insertions(+), 35 deletions(-) delete mode 100644 discord/types/primary_guild.py diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 4d92d7a2a..1c986636e 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -32,7 +32,7 @@ from .utils import snowflake_time, _get_as_snowflake if TYPE_CHECKING: from .state import ConnectionState - from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload + from .types.user import PrimaryGuild as PrimaryGuildPayload from typing_extensions import Self diff --git a/discord/types/primary_guild.py b/discord/types/primary_guild.py deleted file mode 100644 index ad8a7c338..000000000 --- a/discord/types/primary_guild.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -The MIT License (MIT) - -Copyright (c) 2015-present Rapptz - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -""" - -from typing import TypedDict, Optional - - -class PrimaryGuild(TypedDict): - identity_guild_id: Optional[int] - identity_enabled: Optional[bool] - tag: Optional[str] - badge: Optional[str] diff --git a/discord/types/user.py b/discord/types/user.py index 1f027ce9d..b6bcc12a0 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -54,3 +54,9 @@ class User(PartialUser, total=False): flags: int premium_type: PremiumType public_flags: int + +class PrimaryGuild(TypedDict): + identity_guild_id: Optional[int] + identity_enabled: Optional[bool] + tag: Optional[str] + badge: Optional[str] \ No newline at end of file diff --git a/discord/user.py b/discord/user.py index 4bd2ac21d..e06db763f 100644 --- a/discord/user.py +++ b/discord/user.py @@ -43,9 +43,8 @@ if TYPE_CHECKING: from .guild import Guild from .message import Message from .state import ConnectionState - from .types.primary_guild import PrimaryGuild as PrimaryGuildPayload from .types.channel import DMChannel as DMChannelPayload - from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData + from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData, PrimaryGuild as PrimaryGuildPayload __all__ = ( From 4875a3dc412480153492e14a98bb49a93e08137c Mon Sep 17 00:00:00 2001 From: blord0 Date: Thu, 19 Jun 2025 22:33:17 +0100 Subject: [PATCH 30/35] Formatting --- discord/types/user.py | 3 ++- discord/user.py | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/discord/types/user.py b/discord/types/user.py index b6bcc12a0..b2b213ecf 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -55,8 +55,9 @@ class User(PartialUser, total=False): premium_type: PremiumType public_flags: int + class PrimaryGuild(TypedDict): identity_guild_id: Optional[int] identity_enabled: Optional[bool] tag: Optional[str] - badge: Optional[str] \ No newline at end of file + badge: Optional[str] diff --git a/discord/user.py b/discord/user.py index e06db763f..b3370eebb 100644 --- a/discord/user.py +++ b/discord/user.py @@ -44,7 +44,12 @@ if TYPE_CHECKING: from .message import Message from .state import ConnectionState from .types.channel import DMChannel as DMChannelPayload - from .types.user import PartialUser as PartialUserPayload, User as UserPayload, AvatarDecorationData, PrimaryGuild as PrimaryGuildPayload + from .types.user import ( + PartialUser as PartialUserPayload, + User as UserPayload, + AvatarDecorationData, + PrimaryGuild as PrimaryGuildPayload, + ) __all__ = ( From 3897ace250e76e16107c27e001db165ead40343d Mon Sep 17 00:00:00 2001 From: blord0 <68508813+blord0@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:13:16 +0100 Subject: [PATCH 31/35] Update discord/asset.py Co-authored-by: DA344 <108473820+DA-344@users.noreply.github.com> --- discord/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/asset.py b/discord/asset.py index 0753e61da..cbf7dd4b2 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -350,7 +350,7 @@ class Asset(AssetMixin): def _from_primary_guild(cls, state: _State, guild_id: int, icon_hash: str) -> Self: return cls( state, - url=f'{cls.BASE}/clan-badges/{guild_id}/{icon_hash}.png?size=64', + url=f'{cls.BASE}/guild-tag-badges/{guild_id}/{icon_hash}.png?size=64', key=icon_hash, animated=False, ) From fcda2e6548078a46ab31220ed0bd0f023f8210ec Mon Sep 17 00:00:00 2001 From: blord0 Date: Fri, 11 Jul 2025 11:49:40 +0100 Subject: [PATCH 32/35] Add recommended changes --- discord/primary_guild.py | 14 +++++--------- discord/user.py | 4 +++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index 1c986636e..b907db6d5 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -39,6 +39,8 @@ if TYPE_CHECKING: class PrimaryGuild: """Represents the primary guild identity of a :class:`User` + .. versionadded:: 2.6 + Attributes ----------- id: Optional[:class:`int`] @@ -48,19 +50,13 @@ class PrimaryGuild: identity_enabled: Optional[:class:`bool`] Whether the user has their primary guild publicly displayed. If ``None``, the user has a public guild but has not reaffirmed the guild identity after a change - .. note:: + .. warning:: Users can have their primary guild publicly displayed while still having an :attr:`id` of ``None``. Be careful when checking this attribute! """ __slots__ = ('id', 'identity_enabled', 'tag', '_badge', '_state') - if TYPE_CHECKING: - id: Optional[int] - identity_enabled: Optional[bool] - tag: Optional[str] - _badge: Optional[str] - _state: ConnectionState def __init__(self, *, state, data: PrimaryGuildPayload) -> None: self._state = state @@ -82,13 +78,13 @@ class PrimaryGuild: @property def created_at(self) -> Optional[datetime]: """Optional[:class:`datetime.datetime`]: Returns the primary guild's creation time in UTC.""" - if self.id: + if self.id is not None: return snowflake_time(self.id) return None @classmethod def _default(cls, state: ConnectionState) -> Self: - payload: PrimaryGuildPayload = {"identity_guild_id": None, "identity_enabled": False, "tag": None, "badge": None} + payload: PrimaryGuildPayload = {"identity_enabled": False} # type: ignore return cls(state=state, data=payload) def __repr__(self) -> str: diff --git a/discord/user.py b/discord/user.py index b3370eebb..fb00a2441 100644 --- a/discord/user.py +++ b/discord/user.py @@ -317,7 +317,9 @@ class BaseUser(_UserTag): @property def primary_guild(self) -> PrimaryGuild: - """:class:`PrimaryGuild`: Returns the user's primary guild.""" + """:class:`PrimaryGuild`: Returns the user's primary guild. + + .. versionadded:: 2.6""" if self._primary_guild: return PrimaryGuild(state=self._state, data=self._primary_guild) return PrimaryGuild._default(self._state) From 84dc82bfc8f6eb2548f118b449e15675ccd3b4ba Mon Sep 17 00:00:00 2001 From: blord0 Date: Fri, 11 Jul 2025 11:51:31 +0100 Subject: [PATCH 33/35] Forgot to run black --- discord/primary_guild.py | 3 +-- discord/user.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index b907db6d5..d759d75f9 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -57,7 +57,6 @@ class PrimaryGuild: __slots__ = ('id', 'identity_enabled', 'tag', '_badge', '_state') - def __init__(self, *, state, data: PrimaryGuildPayload) -> None: self._state = state self._update(data) @@ -84,7 +83,7 @@ class PrimaryGuild: @classmethod def _default(cls, state: ConnectionState) -> Self: - payload: PrimaryGuildPayload = {"identity_enabled": False} # type: ignore + payload: PrimaryGuildPayload = {"identity_enabled": False} # type: ignore return cls(state=state, data=payload) def __repr__(self) -> str: diff --git a/discord/user.py b/discord/user.py index fb00a2441..db7c2940c 100644 --- a/discord/user.py +++ b/discord/user.py @@ -318,7 +318,7 @@ class BaseUser(_UserTag): @property def primary_guild(self) -> PrimaryGuild: """:class:`PrimaryGuild`: Returns the user's primary guild. - + .. versionadded:: 2.6""" if self._primary_guild: return PrimaryGuild(state=self._state, data=self._primary_guild) From 551129bebffa622754d66542c685f7d731ae4cb7 Mon Sep 17 00:00:00 2001 From: blord0 Date: Sat, 12 Jul 2025 12:10:09 +0100 Subject: [PATCH 34/35] Reformat "is not None" checks to fit format --- discord/primary_guild.py | 2 +- discord/user.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index d759d75f9..dd1cfc975 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -70,7 +70,7 @@ class PrimaryGuild: @property def badge(self) -> Optional[Asset]: """Optional[:class:`Asset`]: Returns the primary guild's asset""" - if self._badge and self.id: + if self._badge is not None and self.id is not None: return Asset._from_primary_guild(self._state, self.id, self._badge) return None diff --git a/discord/user.py b/discord/user.py index db7c2940c..636c909f3 100644 --- a/discord/user.py +++ b/discord/user.py @@ -320,7 +320,7 @@ class BaseUser(_UserTag): """:class:`PrimaryGuild`: Returns the user's primary guild. .. versionadded:: 2.6""" - if self._primary_guild: + if self._primary_guild is not None: return PrimaryGuild(state=self._state, data=self._primary_guild) return PrimaryGuild._default(self._state) From 0ff7bb4bc9ef817d263a005aaea0c5c8a70f2429 Mon Sep 17 00:00:00 2001 From: blord0 Date: Sat, 12 Jul 2025 15:18:15 +0100 Subject: [PATCH 35/35] Give `state` the correct type hinting --- discord/primary_guild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/primary_guild.py b/discord/primary_guild.py index dd1cfc975..b65275a1f 100644 --- a/discord/primary_guild.py +++ b/discord/primary_guild.py @@ -57,7 +57,7 @@ class PrimaryGuild: __slots__ = ('id', 'identity_enabled', 'tag', '_badge', '_state') - def __init__(self, *, state, data: PrimaryGuildPayload) -> None: + def __init__(self, *, state: ConnectionState, data: PrimaryGuildPayload) -> None: self._state = state self._update(data)