Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incorrect activities and permissions on Interaction and Option objects #1365

Merged
merged 11 commits into from
Jun 10, 2022
23 changes: 12 additions & 11 deletions discord/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ class _TextChannel(discord.abc.GuildChannel, Hashable):
def __init__(self, *, state: ConnectionState, guild: Guild, data: Union[TextChannelPayload, ForumChannelPayload]):
self._state: ConnectionState = state
self.id: int = int(data["id"])
self._type: int = data["type"]
self._update(guild, data)

@property
Expand All @@ -195,16 +194,18 @@ def _update(self, guild: Guild, data: Union[TextChannelPayload, ForumChannelPayl
self.guild: Guild = guild
self.name: str = data["name"]
self.category_id: Optional[int] = utils._get_as_snowflake(data, "parent_id")
self.topic: Optional[str] = data.get("topic")
self.position: int = data.get("position")
self.nsfw: bool = data.get("nsfw", False)
# Does this need coercion into `int`? No idea yet.
self.slowmode_delay: int = data.get("rate_limit_per_user", 0)
self.default_auto_archive_duration: ThreadArchiveDuration = data.get("default_auto_archive_duration", 1440)
self._type: int = data.get("type", self._type)
self.last_message_id: Optional[int] = utils._get_as_snowflake(data, "last_message_id")
self.flags: ChannelFlags = ChannelFlags._from_value(data.get("flags", 0))
self._fill_overwrites(data)
self._type: int = data["type"]

if not data.get("_invoke_flag"):
self.topic: Optional[str] = data.get("topic")
self.position: int = data.get("position")
self.nsfw: bool = data.get("nsfw", False)
# Does this need coercion into `int`? No idea yet.
self.slowmode_delay: int = data.get("rate_limit_per_user", 0)
self.default_auto_archive_duration: ThreadArchiveDuration = data.get("default_auto_archive_duration", 1440)
self.last_message_id: Optional[int] = utils._get_as_snowflake(data, "last_message_id")
self.flags: ChannelFlags = ChannelFlags._from_value(data.get("flags", 0))
self._fill_overwrites(data)

@property
def type(self) -> ChannelType:
Expand Down
38 changes: 24 additions & 14 deletions discord/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ async def _invoke(self, ctx: ApplicationContext) -> None:
if (_user_data := resolved.get("users", {}).get(arg)) is not None:
# We resolved the user from the user id
_data["user"] = _user_data
arg = Member(state=ctx.interaction._state, data=_data, guild=ctx.guild)
cache_flag = ctx.interaction._state.member_cache_flags.interaction
arg = ctx.guild._get_and_update_member(_data, int(arg), cache_flag)
elif op.input_type is SlashCommandOptionType.mentionable:
if (_data := resolved.get("users", {}).get(arg)) is not None:
arg = User(state=ctx.interaction._state, data=_data)
Expand All @@ -811,19 +812,28 @@ async def _invoke(self, ctx: ApplicationContext) -> None:
else:
arg = Object(id=int(arg))
elif (_data := resolved.get(f"{op.input_type.name}s", {}).get(arg)) is not None:
obj_type = None
kw = {}
if op.input_type is SlashCommandOptionType.user:
obj_type = User
elif op.input_type is SlashCommandOptionType.role:
obj_type = Role
kw["guild"] = ctx.guild
elif op.input_type is SlashCommandOptionType.channel:
obj_type = _guild_channel_factory(_data["type"])[0]
kw["guild"] = ctx.guild
elif op.input_type is SlashCommandOptionType.attachment:
obj_type = Attachment
arg = obj_type(state=ctx.interaction._state, data=_data, **kw)
if op.input_type is SlashCommandOptionType.channel and int(arg) in ctx.guild._channels:
arg = ctx.guild.get_channel(int(arg))
baronkobama marked this conversation as resolved.
Show resolved Hide resolved
_data["_invoke_flag"] = True
arg._update(ctx.guild, _data)
else:
obj_type = None
kw = {}
if op.input_type is SlashCommandOptionType.user:
obj_type = User
elif op.input_type is SlashCommandOptionType.role:
obj_type = Role
kw["guild"] = ctx.guild
elif op.input_type is SlashCommandOptionType.channel:
# NOTE:
# This is a fallback in case the channel is not found in the guild's channels.
# If this fallback occurs, at the very minimum, permissions will be incorrect
# due to a lack of permission_overwrite data.
obj_type = _guild_channel_factory(_data["type"])[0]
kw["guild"] = ctx.guild
elif op.input_type is SlashCommandOptionType.attachment:
obj_type = Attachment
arg = obj_type(state=ctx.interaction._state, data=_data, **kw)
else:
# We couldn't resolve the object, so we just return an empty object
arg = Object(id=int(arg))
Expand Down
10 changes: 10 additions & 0 deletions discord/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,15 @@ def joined(self):
"""
return 2

@flag_value
def interaction(self):
""":class:`bool`: Whether to cache members obtained through interactions.

This includes members received through
:class:`discord.Interaction` and :class:`discord.Option`.
"""
return 4

@classmethod
def from_intents(cls: Type[MemberCacheFlags], intents: Intents) -> MemberCacheFlags:
"""A factory method that creates a :class:`MemberCacheFlags` based on
Expand All @@ -1083,6 +1092,7 @@ def from_intents(cls: Type[MemberCacheFlags], intents: Intents) -> MemberCacheFl
"""

self = cls.none()
self.interaction = True
if intents.members:
self.joined = True
if intents.voice_states:
Expand Down
17 changes: 17 additions & 0 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
from .types.guild import Ban as BanPayload
from .types.guild import Guild as GuildPayload
from .types.guild import GuildFeature, MFALevel
from .types.member import Member as MemberPayload
from .types.threads import Thread as ThreadPayload
from .types.voice import GuildVoiceState
from .voice_client import VoiceProtocol
Expand Down Expand Up @@ -353,6 +354,22 @@ def _voice_state_for(self, user_id: int, /) -> Optional[VoiceState]:
def _add_member(self, member: Member, /) -> None:
self._members[member.id] = member

def _get_and_update_member(self, payload: MemberPayload, user_id: int, cache_flag: bool, /) -> Member:
# we always get the member, and we only update if the cache_flag (this cache flag should
# always be MemberCacheFlag.interaction or MemberCacheFlag.option) is set to True
if user_id in self._members:
member = self.get_member(user_id)
member._update(payload) if cache_flag else None
else:
# NOTE:
# This is a fallback in case the member is not found in the guild's members.
# If this fallback occurs, multiple aspects of the Member
# class will be incorrect such as status and activities.
member = Member(guild=self, state=self._state, data=payload) # type: ignore
if cache_flag:
self._members[user_id] = member
return member

def _store_thread(self, payload: ThreadPayload, /) -> Thread:
thread = Thread(guild=self, state=self._state, data=payload)
self._threads[thread.id] = thread
Expand Down
5 changes: 3 additions & 2 deletions discord/interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ def _from_data(self, data: InteractionPayload):
except KeyError:
pass
else:
self.user = Member(state=self._state, guild=guild, data=member) # type: ignore
cache_flag = self._state.member_cache_flags.interaction
self.user = guild._get_and_update_member(member, int(member["user"]["id"]), cache_flag)
self._permissions = int(member.get("permissions", 0))
else:
try:
Expand Down Expand Up @@ -623,7 +624,7 @@ async def send_message(
before deleting the message we just sent.
file: :class:`File`
The file to upload.
files: :class:`List[File]`
files: List[:class:`File`]
A list of files to upload. Must be a maximum of 10.

Raises
Expand Down