From 5d6bf8298a616f2884eafc5ba0ec8ef2d106f692 Mon Sep 17 00:00:00 2001 From: Dante Mendoza Leyva Date: Thu, 4 Nov 2021 16:37:31 -0600 Subject: [PATCH 1/4] Fix for discord.User parsing --- discord/commands/commands.py | 39 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/discord/commands/commands.py b/discord/commands/commands.py index 5b084a26d7..3094604700 100644 --- a/discord/commands/commands.py +++ b/discord/commands/commands.py @@ -60,7 +60,7 @@ "MessageCommand", ) -if TYPE_CHECKING: +if TYPE_CHECKING: from ..interactions import Interaction def wrap_callback(coro): @@ -98,7 +98,7 @@ class _BaseCommand: class ApplicationCommand(_BaseCommand): cog = None - + def __repr__(self): return f"" @@ -142,8 +142,8 @@ async def can_run(self, ctx: ApplicationContext) -> bool: # since we have no checks, then we just return True. return True - return await async_all(predicate(ctx) for predicate in predicates) # type: ignore - + return await async_all(predicate(ctx) for predicate in predicates) # type: ignore + async def dispatch_error(self, ctx: ApplicationContext, error: Exception) -> None: ctx.command_failed = True cog = self.cog @@ -345,7 +345,7 @@ class SlashCommand(ApplicationCommand): .. note:: - If this is not empty then default_permissions will be set to False. + If this is not empty then default_permissions will be set to False. cog: Optional[:class:`Cog`] The cog that this command belongs to. ``None`` if there isn't one. @@ -504,8 +504,11 @@ async def _invoke(self, ctx: ApplicationContext) -> None: <= op.input_type.value <= SlashCommandOptionType.role.value ): - name = "member" if op.input_type.name == "user" else op.input_type.name - arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg)) + if ctx.guild is None and op.input_type.name == "user": + arg = User(state=ctx.interaction._state, data=int(arg)) + else: + name = "member" if op.input_type.name == "user" else op.input_type.name + arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg)) elif op.input_type == SlashCommandOptionType.mentionable: arg_id = int(arg) @@ -521,7 +524,7 @@ async def _invoke(self, ctx: ApplicationContext) -> None: for o in self.options: if o._parameter_name not in kwargs: kwargs[o._parameter_name] = o.default - + if self.cog is not None: await self.callback(self.cog, ctx, **kwargs) else: @@ -529,12 +532,12 @@ async def _invoke(self, ctx: ApplicationContext) -> None: async def invoke_autocomplete_callback(self, interaction: Interaction): values = { i.name: i.default for i in self.options } - + for op in interaction.data.get("options", []): if op.get("focused", False): option = find(lambda o: o.name == op["name"], self.options) values.update({ - i["name"]:i["value"] + i["name"]:i["value"] for i in interaction.data["options"] }) ctx = AutocompleteContext(interaction, command=self, focused=option, value=op.get("value"), options=values) @@ -631,7 +634,7 @@ def __init__( self.min_value: minmax_typehint = kwargs.pop("min_value", None) self.max_value: minmax_typehint = kwargs.pop("max_value", None) - + if not (isinstance(self.min_value, minmax_types) or self.min_value is None): raise TypeError(f"Expected {minmax_typehint} for min_value, got \"{type(self.min_value).__name__}\"") if not (isinstance(self.max_value, minmax_types) or self.min_value is None): @@ -639,7 +642,7 @@ def __init__( self.autocomplete = kwargs.pop("autocomplete", None) if ( - self.autocomplete and + self.autocomplete and not asyncio.iscoroutinefunction(self.autocomplete) ): raise TypeError("Autocomplete callback must be a coroutine.") @@ -848,7 +851,7 @@ def __init__(self, func: Callable, *args, **kwargs) -> None: self.checks = checks self._before_invoke = None self._after_invoke = None - + self.validate_parameters() # Context Menu commands don't have permissions @@ -890,7 +893,7 @@ def validate_parameters(self): ) except StopIteration: pass - + def qualified_name(self): return self.name @@ -929,12 +932,12 @@ async def _invoke(self, ctx: ApplicationContext) -> None: guild=ctx.interaction._state._get_guild(ctx.interaction.guild_id), state=ctx.interaction._state, ) - + if self.cog is not None: await self.callback(self.cog, ctx, target) else: await self.callback(ctx, target) - + def copy(self): """Creates a copy of this command. @@ -995,12 +998,12 @@ async def _invoke(self, ctx: ApplicationContext): channel = ctx.interaction._state.add_dm_channel(data) target = Message(state=ctx.interaction._state, channel=channel, data=message) - + if self.cog is not None: await self.callback(self.cog, ctx, target) else: await self.callback(ctx, target) - + def copy(self): """Creates a copy of this command. From c7aeb26a06edbb0999c70d41790a8008154f0798 Mon Sep 17 00:00:00 2001 From: Dante Mendoza Leyva Date: Thu, 4 Nov 2021 16:48:32 -0600 Subject: [PATCH 2/4] Fix for the fix --- discord/commands/commands.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/commands/commands.py b/discord/commands/commands.py index 3094604700..cce0f00b47 100644 --- a/discord/commands/commands.py +++ b/discord/commands/commands.py @@ -505,7 +505,9 @@ async def _invoke(self, ctx: ApplicationContext) -> None: <= SlashCommandOptionType.role.value ): if ctx.guild is None and op.input_type.name == "user": - arg = User(state=ctx.interaction._state, data=int(arg)) + _data = ctx.interaction.data["resolved"]["users"][arg] + _data["id"] = int(arg) + arg = User(state=ctx.interaction._state, data=_data) else: name = "member" if op.input_type.name == "user" else op.input_type.name arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg)) From 100126f32cc1fe8c946309d5a644d8893b919964 Mon Sep 17 00:00:00 2001 From: Dante Mendoza Leyva Date: Fri, 5 Nov 2021 16:03:01 -0600 Subject: [PATCH 3/4] Sync autocomplete is allowed now --- discord/commands/commands.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/discord/commands/commands.py b/discord/commands/commands.py index fe6d2638b9..489a3bb038 100644 --- a/discord/commands/commands.py +++ b/discord/commands/commands.py @@ -647,12 +647,7 @@ def __init__( raise TypeError(f"Expected {minmax_typehint} for max_value, got \"{type(self.max_value).__name__}\"") self.autocomplete = kwargs.pop("autocomplete", None) - if ( - self.autocomplete and - not asyncio.iscoroutinefunction(self.autocomplete) - ): - raise TypeError("Autocomplete callback must be a coroutine.") - + def to_dict(self) -> Dict: as_dict = { "name": self.name, From 04e2bc81799d3bfa95f54af86d7ffdf8aee6919c Mon Sep 17 00:00:00 2001 From: Dante Mendoza Leyva Date: Sat, 20 Nov 2021 10:17:50 -0600 Subject: [PATCH 4/4] Update commands.py --- discord/commands/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/commands/commands.py b/discord/commands/commands.py index e09e2561ec..e3f42c4dd7 100644 --- a/discord/commands/commands.py +++ b/discord/commands/commands.py @@ -535,12 +535,12 @@ async def _invoke(self, ctx: ApplicationContext) -> None: async def invoke_autocomplete_callback(self, ctx: AutocompleteContext): values = { i.name: i.default for i in self.options } - for op in interaction.data.get("options", []): + for op in ctx.interaction.data.get("options", []): if op.get("focused", False): option = find(lambda o: o.name == op["name"], self.options) values.update({ i["name"]:i["value"] - for i in interaction.data["options"] + for i in ctx.interaction.data["options"] }) ctx.command = self ctx.focused = option