Skip to content

Commit

Permalink
feat(Member): add flags (#918)
Browse files Browse the repository at this point in the history

Signed-off-by: Victor <67214928+Victorsitou@users.noreply.github.com>
Co-authored-by: shiftinv <8530778+shiftinv@users.noreply.github.com>
  • Loading branch information
Victorsitou and shiftinv committed Jan 26, 2023
1 parent 13550a1 commit af5a8dd
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog/918.feature.rst
@@ -0,0 +1 @@
Add :attr:`~Member.flags` property to :class:`Member`.
103 changes: 103 additions & 0 deletions disnake/flags.py
Expand Up @@ -39,6 +39,7 @@
"ApplicationFlags",
"ChannelFlags",
"AutoModKeywordPresets",
"MemberFlags",
)

BF = TypeVar("BF", bound="BaseFlags")
Expand Down Expand Up @@ -2134,3 +2135,105 @@ def slurs(self):
(contains insults or words that may be considered hate speech).
"""
return 1 << 3


class MemberFlags(BaseFlags):
"""Wraps up Discord Member flags.
.. container:: operations
.. describe:: x == y
Checks if two MemberFlags instances are equal.
.. describe:: x != y
Checks if two MemberFlags instances are not equal.
.. describe:: x <= y
Checks if an MemberFlags instance is a subset of another MemberFlags instance.
.. describe:: x >= y
Checks if an MemberFlags instance is a superset of another MemberFlags instance.
.. describe:: x < y
Checks if an MemberFlags instance is a strict subset of another MemberFlags instance.
.. describe:: x > y
Checks if an MemberFlags instance is a strict superset of another MemberFlags instance.
.. describe:: x | y, x |= y
Returns a new MemberFlags instance with all enabled flags from both x and y.
(Using ``|=`` will update in place).
.. describe:: x & y, x &= y
Returns a new MemberFlags instance with only flags enabled on both x and y.
(Using ``&=`` will update in place).
.. describe:: x ^ y, x ^= y
Returns a new MemberFlags instance with only flags enabled on one of x or y, but not both.
(Using ``^=`` will update in place).
.. describe:: ~x
Returns a new MemberFlags instance with all flags from x inverted.
.. describe:: hash(x)
Returns the flag's hash.
.. describe:: iter(x)
Returns an iterator of ``(name, value)`` pairs. This allows it
to be, for example, constructed as a dict or a list of pairs.
Note that aliases are not shown.
Additionally supported are a few operations on class attributes.
.. describe:: MemberFlags.y | MemberFlags.z, MemberFlags(y=True) | MemberFlags.z
Returns a MemberFlags instance with all provided flags enabled.
.. describe:: ~MemberFlags.y
Returns a MemberFlags instance with all flags except ``y`` inverted from their default value.
.. versionadded:: 2.8
Attributes
----------
value: :class:`int`
The raw value. You should query flags via the properties
rather than using this raw value.
"""

__slots__ = ()

if TYPE_CHECKING:

@_generated
def __init__(
self,
*,
bypasses_verification: bool = ...,
completed_onboarding: bool = ...,
did_rejoin: bool = ...,
started_onboarding: bool = ...,
) -> None:
...

@flag_value
def did_rejoin(self):
""":class:`bool`: Returns ``True`` if the member has left and rejoined the guild."""
return 1 << 0

@flag_value
def completed_onboarding(self):
""":class:`bool`: Returns ``True`` if the member has completed onboarding."""
return 1 << 1

@flag_value
def bypasses_verification(self):
""":class:`bool`: Returns ``True`` if the member is able to bypass guild verification requirements."""
return 1 << 2

@flag_value
def started_onboarding(self):
""":class:`bool`: Returns ``True`` if the member has started onboarding."""
return 1 << 3
41 changes: 41 additions & 0 deletions disnake/member.py
Expand Up @@ -28,6 +28,7 @@
from .asset import Asset
from .colour import Colour
from .enums import Status, try_enum
from .flags import MemberFlags
from .object import Object
from .permissions import Permissions
from .user import BaseUser, User, _UserTag
Expand Down Expand Up @@ -271,6 +272,7 @@ class Member(disnake.abc.Messageable, _UserTag):
"_state",
"_avatar",
"_communication_disabled_until",
"_flags",
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -336,6 +338,7 @@ def __init__(
self._avatar: Optional[str] = data.get("avatar")
timeout_datetime = utils.parse_time(data.get("communication_disabled_until"))
self._communication_disabled_until: Optional[datetime.datetime] = timeout_datetime
self._flags: int = data.get("flags", 0)

def __str__(self) -> str:
return str(self._user)
Expand Down Expand Up @@ -371,6 +374,7 @@ def _update_from_message(self, data: MemberPayload) -> None:
self._roles = utils.SnowflakeList(map(int, data["roles"]))
self.nick = data.get("nick", None)
self.pending = data.get("pending", False)
self._flags = data.get("flags", 0)

@classmethod
def _try_upgrade(
Expand Down Expand Up @@ -399,6 +403,7 @@ def _copy(cls, member: Member) -> Self:
self._state = member._state
self._avatar = member._avatar
self._communication_disabled_until = member.current_timeout
self._flags = member._flags

# Reference will not be copied unless necessary by PRESENCE_UPDATE
# See below
Expand Down Expand Up @@ -427,6 +432,7 @@ def _update(self, data: GuildMemberUpdateEvent) -> None:
self._avatar = data.get("avatar")
timeout_datetime = utils.parse_time(data.get("communication_disabled_until"))
self._communication_disabled_until = timeout_datetime
self._flags = data.get("flags", 0)

def _presence_update(
self, data: PresenceData, user: UserPayload
Expand Down Expand Up @@ -699,6 +705,14 @@ def current_timeout(self) -> Optional[datetime.datetime]:

return self._communication_disabled_until

@property
def flags(self) -> MemberFlags:
""":class:`MemberFlags`: Returns the member's flags.
.. versionadded:: 2.8
"""
return MemberFlags._from_value(self._flags)

@overload
async def ban(
self,
Expand Down Expand Up @@ -759,6 +773,8 @@ async def edit(
roles: Sequence[disnake.abc.Snowflake] = MISSING,
voice_channel: Optional[VocalGuildChannel] = MISSING,
timeout: Optional[Union[float, datetime.timedelta, datetime.datetime]] = MISSING,
flags: MemberFlags = MISSING,
bypasses_verification: bool = MISSING,
reason: Optional[str] = None,
) -> Optional[Member]:
"""|coro|
Expand All @@ -782,6 +798,10 @@ async def edit(
+------------------------------+-------------------------------------+
| timeout | :attr:`Permissions.moderate_members`|
+------------------------------+-------------------------------------+
| flags | :attr:`Permissions.moderate_members`|
+------------------------------+-------------------------------------+
| bypasses_verification | :attr:`Permissions.moderate_members`|
+------------------------------+-------------------------------------+
All parameters are optional.
Expand Down Expand Up @@ -816,6 +836,19 @@ async def edit(
.. versionadded:: 2.3
flags: :class:`MemberFlags`
The member's new flags. To know what flags are editable,
see :ddocs:`the documentation <resources/guild#guild-member-object-guild-member-flags>`.
If parameter ``bypasses_verification`` is provided, that will override the setting of :attr:`MemberFlags.bypasses_verification`.
.. versionadded:: 2.8
bypasses_verification: :class:`bool`
Whether the member bypasses guild verification requirements.
.. versionadded:: 2.8
reason: Optional[:class:`str`]
The reason for editing this member. Shows up on the audit log.
Expand Down Expand Up @@ -889,6 +922,14 @@ async def edit(
else:
payload["communication_disabled_until"] = None

if bypasses_verification is not MISSING:
# create base flags if flags are provided, otherwise use the internal flags.
flags = MemberFlags._from_value(self._flags if flags is MISSING else flags.value)
flags.bypasses_verification = bypasses_verification

if flags is not MISSING:
payload["flags"] = flags.value

if payload:
data = await http.edit_member(guild_id, self.id, reason=reason, **payload)
return Member(data=data, guild=self.guild, state=self._state)
Expand Down
3 changes: 2 additions & 1 deletion disnake/permissions.py
Expand Up @@ -916,7 +916,8 @@ def start_embedded_activities(self) -> int:

@flag_value
def moderate_members(self) -> int:
""":class:`bool`: Returns ``True`` if a user can perform limited moderation actions (timeout).
""":class:`bool`: Returns ``True`` if a user can perform limited moderation actions,
such as timeouts or editing members' flags.
.. versionadded:: 2.3
"""
Expand Down
1 change: 1 addition & 0 deletions disnake/types/gateway.py
Expand Up @@ -436,6 +436,7 @@ class GuildMemberUpdateEvent(TypedDict):
mute: NotRequired[bool]
pending: NotRequired[bool]
communication_disabled_until: NotRequired[Optional[str]]
flags: int


# https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update
Expand Down
1 change: 1 addition & 0 deletions disnake/types/member.py
Expand Up @@ -19,6 +19,7 @@ class BaseMember(TypedDict):
pending: NotRequired[bool]
permissions: NotRequired[str]
communication_disabled_until: NotRequired[Optional[str]]
flags: int


class Member(BaseMember, total=False):
Expand Down
8 changes: 8 additions & 0 deletions docs/api.rst
Expand Up @@ -6092,6 +6092,14 @@ PublicUserFlags
.. autoclass:: PublicUserFlags()
:members:

MemberFlags
~~~~~~~~~~~

.. attributetable:: MemberFlags

.. autoclass:: MemberFlags()
:members:

.. _discord_ui_kit:

Bot UI Kit
Expand Down
1 change: 1 addition & 0 deletions tests/interactions/test_base.py
Expand Up @@ -141,6 +141,7 @@ def test_init_member(self, state):
"joined_at": "2022-09-02T22:00:55.069000+00:00",
"deaf": False,
"mute": False,
"flags": 0,
}

user_payload: UserPayload = {
Expand Down

0 comments on commit af5a8dd

Please sign in to comment.