diff --git a/discord/abc.py b/discord/abc.py index 111afd5c0..f4e2808a0 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -755,6 +755,20 @@ class GuildChannel: category = self.guild.get_channel(self.category_id) return bool(category and category.overwrites == self.overwrites) + def _apply_implicit_permissions(self, base: Permissions) -> None: + # if you can't send a message in a channel then you can't have certain + # permissions as well + if not base.send_messages: + base.send_tts_messages = False + base.mention_everyone = False + base.embed_links = False + base.attach_files = False + + # if you can't read a channel then you have no permissions there + if not base.read_messages: + denied = Permissions.all_channel() + base.value &= ~denied.value + def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: """Handles permission resolution for the :class:`~discord.Member` or :class:`~discord.Role`. @@ -765,6 +779,7 @@ class GuildChannel: - Guild roles - Channel overrides - Member overrides + - Implicit permissions - Member timeout If a :class:`~discord.Role` is passed, then it checks the permissions @@ -880,19 +895,6 @@ class GuildChannel: base.handle_overwrite(allow=overwrite.allow, deny=overwrite.deny) break - # if you can't send a message in a channel then you can't have certain - # permissions as well - if not base.send_messages: - base.send_tts_messages = False - base.mention_everyone = False - base.embed_links = False - base.attach_files = False - - # if you can't read a channel then you have no permissions there - if not base.read_messages: - denied = Permissions.all_channel() - base.value &= ~denied.value - if obj.is_timed_out(): # Timeout leads to every permission except VIEW_CHANNEL and READ_MESSAGE_HISTORY # being explicitly denied diff --git a/discord/channel.py b/discord/channel.py index 103065472..ef67da6f5 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -226,6 +226,7 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # text channels do not have voice related permissions denied = Permissions.voice() @@ -947,6 +948,7 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # voice channels cannot be edited by people who can't connect to them # It also implicitly denies all other voice perms @@ -2151,6 +2153,7 @@ class ForumChannel(discord.abc.GuildChannel, Hashable): @utils.copy_doc(discord.abc.GuildChannel.permissions_for) def permissions_for(self, obj: Union[Member, Role], /) -> Permissions: base = super().permissions_for(obj) + self._apply_implicit_permissions(base) # text channels do not have voice related permissions denied = Permissions.voice() diff --git a/discord/threads.py b/discord/threads.py index 17e419f82..a27974cde 100644 --- a/discord/threads.py +++ b/discord/threads.py @@ -31,10 +31,11 @@ import array import copy from .mixins import Hashable -from .abc import Messageable, _purge_helper +from .abc import Messageable, GuildChannel, _purge_helper from .enums import ChannelType, try_enum from .errors import ClientException, InvalidData from .flags import ChannelFlags +from .permissions import Permissions from .utils import MISSING, parse_time, snowflake_time, _get_as_snowflake, _unique __all__ = ( @@ -58,7 +59,6 @@ if TYPE_CHECKING: from .message import Message, PartialMessage from .abc import Snowflake, SnowflakeTime from .role import Role - from .permissions import Permissions from .state import ConnectionState @@ -411,10 +411,9 @@ class Thread(Messageable, Hashable): """Handles permission resolution for the :class:`~discord.Member` or :class:`~discord.Role`. - Since threads do not have their own permissions, they inherit them - from the parent channel. This is a convenience method for - calling :meth:`~discord.TextChannel.permissions_for` on the - parent channel. + Since threads do not have their own permissions, they mostly + inherit them from the parent channel with some implicit + permissions changed. Parameters ---------- @@ -437,7 +436,23 @@ class Thread(Messageable, Hashable): parent = self.parent if parent is None: raise ClientException('Parent channel not found') - return parent.permissions_for(obj) + + base = GuildChannel.permissions_for(parent, obj) + + # if you can't send a message in a channel then you can't have certain + # permissions as well + if not base.send_messages_in_threads: + base.send_tts_messages = False + base.mention_everyone = False + base.embed_links = False + base.attach_files = False + + # if you can't read a channel then you have no permissions there + if not base.read_messages: + denied = Permissions.all_channel() + base.value &= ~denied.value + + return base async def delete_messages(self, messages: Iterable[Snowflake], /, *, reason: Optional[str] = None) -> None: """|coro|