diff --git a/discord/asset.py b/discord/asset.py index 7b9b71133..e3422f311 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -246,6 +246,17 @@ class Asset(AssetMixin): animated=animated, ) + @classmethod + def _from_guild_banner(cls, state: _State, guild_id: int, member_id: int, banner: str) -> Self: + animated = banner.startswith('a_') + format = 'gif' if animated else 'png' + return cls( + state, + url=f"{cls.BASE}/guilds/{guild_id}/users/{member_id}/banners/{banner}.{format}?size=1024", + key=banner, + animated=animated, + ) + @classmethod def _from_avatar_decoration(cls, state: _State, avatar_decoration: str) -> Self: return cls( diff --git a/discord/member.py b/discord/member.py index 74ba86932..5b9b03f9e 100644 --- a/discord/member.py +++ b/discord/member.py @@ -322,6 +322,7 @@ class Member(discord.abc.Messageable, _UserTag): '_user', '_state', '_avatar', + '_banner', '_flags', '_avatar_decoration_data', ) @@ -358,6 +359,7 @@ class Member(discord.abc.Messageable, _UserTag): self.nick: Optional[str] = data.get('nick', None) self.pending: bool = data.get('pending', False) self._avatar: Optional[str] = data.get('avatar') + self._banner: Optional[str] = data.get('banner') self._permissions: Optional[int] self._flags: int = data['flags'] self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data') @@ -649,6 +651,28 @@ class Member(discord.abc.Messageable, _UserTag): return None return Asset._from_guild_avatar(self._state, self.guild.id, self.id, self._avatar) + @property + def display_banner(self) -> Optional[Asset]: + """Optional[:class:`Asset`]: Returns the member's displayed banner, if any. + + This is the member's guild banner if available, otherwise it's their + global banner. If the member has no banner set then ``None`` is returned. + + .. versionadded:: 2.5 + """ + return self.guild_banner or self._user.banner + + @property + def guild_banner(self) -> Optional[Asset]: + """Optional[:class:`Asset`]: Returns an :class:`Asset` for the guild banner + the member has. If unavailable, ``None`` is returned. + + .. versionadded:: 2.5 + """ + if self._banner is None: + return None + return Asset._from_guild_banner(self._state, self.guild.id, self.id, self._banner) + @property def activity(self) -> Optional[ActivityTypes]: """Optional[Union[:class:`BaseActivity`, :class:`Spotify`]]: Returns the primary diff --git a/discord/types/member.py b/discord/types/member.py index 6968edb6f..88fb619fd 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -48,6 +48,7 @@ class Member(PartialMember, total=False): pending: bool permissions: str communication_disabled_until: str + banner: NotRequired[Optional[str]] avatar_decoration_data: NotRequired[AvatarDecorationData]