diff --git a/discord/member.py b/discord/member.py index a8c868d87..8b97e92d8 100644 --- a/discord/member.py +++ b/discord/member.py @@ -251,6 +251,11 @@ class Member(discord.abc.Messageable, _UserTag): premium_since: Optional[:class:`datetime.datetime`] An aware datetime object that specifies the date and time in UTC when the member used their "Nitro boost" on the guild, if available. This could be ``None``. + timed_out_until: Optional[:class:`datetime.datetime`] + An aware datetime object that specifies the date and time in UTC that the member's time out will expire. + This will be set to ``None`` if the user is not timed out. + + .. versionadded:: 2.0 """ __slots__ = ( @@ -261,6 +266,7 @@ class Member(discord.abc.Messageable, _UserTag): 'guild', 'pending', 'nick', + 'timed_out_until', '_client_status', '_user', '_state', @@ -296,6 +302,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.timed_out_until: Optional[datetime.datetime] = utils.parse_time(data.get('communication_disabled_until')) def __str__(self) -> str: return str(self._user) @@ -327,6 +334,7 @@ class Member(discord.abc.Messageable, _UserTag): self._roles = utils.SnowflakeList(map(int, data['roles'])) self.nick = data.get('nick', None) self.pending = data.get('pending', False) + self.timed_out_until = utils.parse_time(data.get('communication_disabled_until')) @classmethod def _try_upgrade(cls: Type[M], *, data: UserWithMemberPayload, guild: Guild, state: ConnectionState) -> Union[User, M]: @@ -351,6 +359,7 @@ class Member(discord.abc.Messageable, _UserTag): self.nick = member.nick self.pending = member.pending self.activities = member.activities + self.timed_out_until = member.timed_out_until self._state = member._state self._avatar = member._avatar @@ -377,6 +386,7 @@ class Member(discord.abc.Messageable, _UserTag): pass self.premium_since = utils.parse_time(data.get('premium_since')) + self.timed_out_until = utils.parse_time(data.get('communication_disabled_until')) self._roles = utils.SnowflakeList(map(int, data['roles'])) self._avatar = data.get('avatar') @@ -643,6 +653,7 @@ class Member(discord.abc.Messageable, _UserTag): suppress: bool = MISSING, roles: List[discord.abc.Snowflake] = MISSING, voice_channel: Optional[VocalGuildChannel] = MISSING, + timed_out_until: Optional[datetime.datetime] = MISSING, reason: Optional[str] = None, ) -> Optional[Member]: """|coro| @@ -651,19 +662,21 @@ class Member(discord.abc.Messageable, _UserTag): Depending on the parameter passed, this requires different permissions listed below: - +---------------+--------------------------------------+ - | Parameter | Permission | - +---------------+--------------------------------------+ - | nick | :attr:`Permissions.manage_nicknames` | - +---------------+--------------------------------------+ - | mute | :attr:`Permissions.mute_members` | - +---------------+--------------------------------------+ - | deafen | :attr:`Permissions.deafen_members` | - +---------------+--------------------------------------+ - | roles | :attr:`Permissions.manage_roles` | - +---------------+--------------------------------------+ - | voice_channel | :attr:`Permissions.move_members` | - +---------------+--------------------------------------+ + +-----------------+--------------------------------------+ + | Parameter | Permission | + +-----------------+--------------------------------------+ + | nick | :attr:`Permissions.manage_nicknames` | + +-----------------+--------------------------------------+ + | mute | :attr:`Permissions.mute_members` | + +-----------------+--------------------------------------+ + | deafen | :attr:`Permissions.deafen_members` | + +-----------------+--------------------------------------+ + | roles | :attr:`Permissions.manage_roles` | + +-----------------+--------------------------------------+ + | voice_channel | :attr:`Permissions.move_members` | + +-----------------+--------------------------------------+ + | timed_out_until | :attr:`Permissions.moderate_members` | + +-----------------+--------------------------------------+ All parameters are optional. @@ -691,6 +704,12 @@ class Member(discord.abc.Messageable, _UserTag): voice_channel: Optional[:class:`VoiceChannel`] The voice channel to move the member to. Pass ``None`` to kick them from voice. + timed_out_until: Optional[:class:`datetime.datetime`] + The date the member's timeout should expire, or ``None`` to remove the timeout. + This must be a timezone-aware datetime object. Consider using :func:`utils.utcnow`. + + .. versionadded:: 2.0 + reason: Optional[:class:`str`] The reason for editing this member. Shows up on the audit log. @@ -700,6 +719,8 @@ class Member(discord.abc.Messageable, _UserTag): You do not have the proper permissions to the action requested. HTTPException The operation failed. + TypeError + The datetime object passed to ``timed_out_until`` was not timezone-aware. Returns -------- @@ -746,6 +767,14 @@ class Member(discord.abc.Messageable, _UserTag): if roles is not MISSING: payload['roles'] = tuple(r.id for r in roles) + + if timed_out_until is not MISSING: + if timed_out_until is None: + payload['communication_disabled_until'] = None + else: + if timed_out_until.tzinfo is None: + raise TypeError('timed_out_until must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.') + payload['communication_disabled_until'] = timed_out_until.isoformat() if payload: data = await http.edit_member(guild_id, self.id, reason=reason, **payload) @@ -906,3 +935,17 @@ class Member(discord.abc.Messageable, _UserTag): The role or ``None`` if not found in the member's roles. """ return self.guild.get_role(role_id) if self._roles.has(role_id) else None + + def is_timed_out(self) -> bool: + """Returns whether this member is timed out. + + .. versionadded:: 2.0 + + Returns + -------- + :class:`bool` + ``True`` if the member is timed out. ``False`` otherwise. + """ + if self.timed_out_until is not None: + return utils.utcnow() < self.timed_out_until + return False diff --git a/discord/permissions.py b/discord/permissions.py index 9d40ca33f..c0584a1db 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -147,7 +147,7 @@ class Permissions(BaseFlags): """A factory method that creates a :class:`Permissions` with all permissions set to ``True``. """ - return cls(0b111111111111111111111111111111111111111) + return cls(0b11111111111111111111111111111111111111111) @classmethod def all_channel(cls: Type[P]) -> P: @@ -195,7 +195,7 @@ class Permissions(BaseFlags): .. versionadded:: 1.7 """ - return cls(0b00001100000000000000000000000111) + return cls(0b10000000000001100000000000000000000000111) @classmethod def text(cls: Type[P]) -> P: @@ -216,7 +216,7 @@ class Permissions(BaseFlags): def voice(cls: Type[P]) -> P: """A factory method that creates a :class:`Permissions` with all "Voice" permissions from the official Discord UI set to ``True``.""" - return cls(0b00000011111100000000001100000000) + return cls(0b1000000000000011111100000000001100000000) @classmethod def stage(cls: Type[P]) -> P: @@ -551,6 +551,22 @@ class Permissions(BaseFlags): """ return 1 << 38 + @flag_value + def start_embedded_activities(self) -> int: + """:class:`bool`: Returns ``True`` if a user can launch an embedded application in a Voice channel. + + .. versionadded:: 2.0 + """ + return 1 << 39 + + @flag_value + def moderate_members(self) -> int: + """:class:`bool`: Returns ``True`` if a user can time out other members. + + .. versionadded:: 2.0 + """ + return 1 << 40 + PO = TypeVar('PO', bound='PermissionOverwrite') def _augment_from_permissions(cls): diff --git a/discord/types/member.py b/discord/types/member.py index f1e81c005..c7bf5ac47 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -45,6 +45,7 @@ class Member(PartialMember, total=False): premium_since: str pending: bool permissions: str + communication_disabled_until: str class _OptionalMemberWithUser(PartialMember, total=False): @@ -53,6 +54,7 @@ class _OptionalMemberWithUser(PartialMember, total=False): premium_since: str pending: bool permissions: str + communication_disabled_until: str class MemberWithUser(_OptionalMemberWithUser):