Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/886.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Emit a :class:`SyncWarning` instead of sending an ephemeral interaction response when an unknown guild application command is invoked.
53 changes: 20 additions & 33 deletions disnake/ext/commands/interaction_bot_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1470,47 +1470,34 @@ async def process_application_commands(
interaction: :class:`disnake.ApplicationCommandInteraction`
The interaction to process commands for.
"""
if not isinstance(self, disnake.Client):
msg = "This method is only usable in disnake.Client subclasses"
raise NotImplementedError(msg)

# This usually comes from the blind spots of the sync algorithm.
# Since not all guild commands are cached, it is possible to experience such issues.
# In this case, the blind spot is the interaction guild, let's fix it:
# Since not all guild commands are cached, it is possible to experience such issues
# when a guild *used to* have synced app commands, but they have been removed from the
# bot/cache, in which case the commands remain registered with the API.
if (
# if we're not currently syncing,
not self._sync_queued.locked()
# and we're instructed to sync guild commands
# and we're instructed to sync guild commands,
and self._command_sync_flags.sync_guild_commands
# and the current command was registered to a guild
# and we're in a guild and the current command was registered to a guild,
and interaction.guild_id
and interaction.data.get("guild_id")
# and we don't know the command
and not self.get_guild_command(interaction.guild_id, interaction.data.id) # pyright: ignore[reportAttributeAccessIssue]
and not self.get_guild_command(interaction.guild_id, interaction.data.id)
):
# don't do anything if we aren't allowed to disable them
if self._command_sync_flags.allow_command_deletion:
try:
await self.bulk_overwrite_guild_commands(interaction.guild_id, []) # pyright: ignore[reportAttributeAccessIssue]
except disnake.HTTPException:
# for some reason we were unable to sync the command
# either malformed API request, or some other error
# in theory this will never error: if a command exists the bot has authorisation
# in practice this is not the case, the API could change valid requests at any time
message = "This command could not be processed. Additionally, an error occurred when trying to sync commands."
else:
message = "This command has just been synced."
else:
# this block is responsible for responding to guild commands that we don't delete
# this could be changed to not respond but that behavior is undecided
message = "This command could not be processed."
try:
# This part is in a separate try-except because we still should respond to the interaction
message += (
" More information about this: "
"https://docs.disnake.dev/page/ext/commands/additional_info.html#unknown-commands."
)
await interaction.response.send_message(
message,
ephemeral=True,
)
except (disnake.HTTPException, disnake.InteractionTimedOut):
pass
warnings.warn(
f"This command is not present in the bot's command cache for guild ID {interaction.guild_id}."
" This can usually happen after removing the guild from the `guild_ids` of a command,"
" or from the `test_guilds` of the bot."
" To resolve this, consider clearing the guild command using"
f" `await bot.bulk_overwrite_guild_commands({interaction.guild_id}, [])`.",
SyncWarning,
stacklevel=2,
)
return

command_type = interaction.data.type
Expand Down
25 changes: 12 additions & 13 deletions docs/ext/commands/additional_info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,28 @@ App command sync

If you're using :ref:`disnake_ext_commands` for application commands (slash commands, context menus) you should
understand how your commands show up in Discord. By default, the library registers / updates all commands automatically.
Based on the application commands defined in your code the library automatically determines
which commands should be registered, edited or deleted, but there're some edge cases you should keep in mind.
Based on the application commands defined in your code, the library automatically determines
which commands should be registered, edited or deleted, but there are some edge cases you should keep in mind.

Unknown Commands
+++++++++++++++++
++++++++++++++++

Unlike global commands, per-guild application commands are synced in a lazy fashion. This is due to Discord ratelimits,
as checking all guilds for application commands is infeasible past two or three guilds.
This can lead to situations where a command no longer exists in the code but still exists in a server.
as checking all guilds for application commands is infeasible beyond only a handful of guilds.
This can lead to situations where a command no longer exists in the code but still exists in the Discord API.

To rectify this, just run the command. It will automatically be deleted.
When an interaction for a previously removed guild command is received, a :class:`SyncWarning` will be emitted.
To rectify this, consider using :meth:`await bot.bulk_overwrite_guild_commands(\<guild_id\>, []) <ext.commands.Bot.bulk_overwrite_guild_commands>`.

.. _changing-test-guilds:

This will also occur when IDs are removed from the ``test_guilds`` kwarg of :class:`Bot <ext.commands.Bot>` (or a similar class) or from the ``guild_ids`` kwarg of
:func:`slash_command <ext.commands.slash_command>`, :func:`user_command <ext.commands.user_command>`, or :func:`message_command <ext.commands.message_command>`.
This will also occur when IDs are removed from the ``test_guilds`` kwarg of :class:`~ext.commands.Bot` or from the ``guild_ids`` kwarg of
:func:`~ext.commands.slash_command`, :func:`~ext.commands.user_command`, or :func:`~ext.commands.message_command`.

Command Sync with Multiple Clusters
++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++

If your bot requires shard distribution across several clusters, you should disable command sync on all clusters except one.
This will prevent conflicts and race conditions. Discord API doesn't provide users with events related to application command updates,
so it's impossible to keep the cache of multiple machines synced. Having only 1 cluster with ``sync_commands`` set to ``True`` is enough
This will prevent conflicts and race conditions. The Discord API doesn't provide users with events related to application command updates,
so it's impossible to keep the cache of multiple machines synced. Having only 1 cluster with :attr:`CommandSyncFlags.sync_commands <ext.commands.CommandSyncFlags.sync_commands>` set to ``True`` is enough
because global registration of application commands doesn't depend on sharding.

.. _why_params_and_injections_return_any:
Expand Down
Loading