Syncing your command tree automatically causes extra unnecessary requests to be made, this is because you only need to sync when commands are updated. *see when you should sync for a more enunciated list on when to sync.
What syncing does is send your commands to discord for one guild or globally. If you haven't changed your command's descriptions, added/removed commands, changed names, parameters, etc. you shouldn't sync, since you'd only be updating discord with the commands they already have without doing any changes, which is pointless and a waste of an API request to a limit with an already tight rate limit. *see what syncing is for a more enunciated on what syncing is, and how to do so.
Where should I sync instead?
It's better to sync using a normal (message) command (or even an on_message
if you prefer Client
) You can even use just a simple eval to do so.
*for example: Prebuilt sync command
*But I don't have the new message content intent... What now?
Bots can still receive message content when the bot is mentioned in it, or in DMs! You could set the bot prefix to commands.when_mentioned
*Why do I get a Missing Access
error when trying to sync to a guild? -> See Missing Access when syncing
Sync when you...
- Added/removed a command
- Changed a command's...
- name (
name=
kwarg or function name) - description (
description=
kwarg or docstring)
- name (
- Added/removed an argument
- Changed an argument's...
- name (rename decorator)
- description (describe decorator)
- type (
arg: str
str is the type here)
- Added/modified permissions:
guild_only
decorator or kwargdefault_permissions
decorator or kwargnsfw
kwarg
- Converted the global/guild command to a guild/global command
Do not sync when you...
- Changed anything in the function's body (after the
async def ():
part) - Added or modified a library side check:
(@)app_commands.checks...
(@)commands...(.check)
@app_commands.checks.(dynamic_)cooldown(...)
This is the same for hybrid commands
App commands work differently from message commands, they're handled mostly on Discord's end. Discord just tells your bot when someone successfully triggers a command.
In order to do this, you need to register your commands on the command tree then tell discord they exist by syncing with tree.sync
.
Commands can be registered on the tree either as a global command or as a guild-specific command, and must be synced to the same scope they are associated with in the tree.
When you sync, you are telling Discord about the commands you currently have for a particular scope.
To sync global commands: await tree.sync()
To sync guild commands: await tree.sync(guild=guild)
Guilds must be either a Guild
object or a discord.Object
with the guild's id.
It may help to think of the tree as a dict like this.
{
None: [global, commands],
guild_one: [guild, one, commands],
guild_two: [guild, two, commands]
}
copy_global_to
will copy [global, commands]
and add them to the commands for the guild you pass.
This is only done locally, you must still sync.
All commands are global by default. There are a few ways to make them guild-specific:
@app_commands.guilds()
decorator on anapp_commands.Group
sublcass or@app_commands.command()
guild
/guilds
in@tree.command()
guild
/guilds
inbot.add_cog
, only if the cog is aGroupCog
guild
/guilds
intree.add_command
, not typically used
A common practice for syncing is to pick a specific guild for testing and run tree.copy_global_to(guild=guild)
then tree.sync(guild=guild)
.
When you're done testing, tree.clear_commands(guild=guild)
then tree.sync(guild=guild)
.
When you're ready to publish your commands, tree.sync()
.
The Prebuilt sync command makes this flow easy using !sync \*
, !sync ^
, and !sync
, respectively.
when you should sync a prebuilt sync command
When you declare an app command and register it in your CommandTree, you do so under a certain scope: global (by default) or one (or more) guilds. The CommandTree stores a mapping of scope
: list of commands
.
When you call CommandTree.sync, you sync one scope of the CommandTree, either the list of global commands (sync()
) or the list for one guild (sync(guild=discord.Object(...))
).
Calling tree.sync()
does not sync all of your commands globally, just the ones registered as global. Similarly, tree.sync(guild=...)
does not sync all of your commands to that guild, just the ones registered to that guild.
copy_global_to
will copy the global list of commands in your tree into the list of commands for the specified guild. This is not permanent (if you don't do it again when your bot restarts) and it does not impact the state of your commands on discord (you still have to sync).
@bot.command()
@commands.guild_only()
@commands.is_owner()
async def sync(
ctx: Context, guilds: Greedy[discord.Object], spec: Optional[Literal["~", "*", "^"]] = None) -> None:
if not guilds:
if spec == "~":
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "*":
ctx.bot.tree.copy_global_to(guild=ctx.guild)
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "^":
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.bot.tree.sync(guild=ctx.guild)
synced = []
else:
synced = await ctx.bot.tree.sync()
await ctx.send(
f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}"
)
return
ret = 0
for guild in guilds:
try:
await ctx.bot.tree.sync(guild=guild)
except discord.HTTPException:
pass
else:
ret += 1
await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")
Greedy
-> commands.Greedy
Context
-> commands.Context
(or your subclass)
Object
-> discord.Object
typing.Optional
and typing.Literal
Works like:
!sync
-> global sync
!sync ~
-> sync current guild
!sync *
-> copies all global app commands to current guild and syncs
!sync ^
-> clears all commands from the current guild target and syncs (removes guild commands)
!sync id_1 id_2
-> syncs guilds with id 1 and 2
Why do i get MISSING ACCESS when trying to sync my tree to a guild?
The bot does not have the applications.commands
scope.
All you have to do is go to https://discord.com/developers/applications/%3E and follow steps shown below:
https://media.discordapp.net/attachments/381965515721146390/957385902735364186/unknown.png?width=1440&height=515
You do not have to kick your bot from the guild, just re-invite it with the generated URL.
reasons on why auto syncing sucks - LeoCx1000#9999
when to sync - Soheab_#6240
what syncing is - sgtlaggy#5516
command tree explination - SolsticeShard#6320
prebuilt sync command - Umbra#0009
missing access when syncing - LeoCx1000#9999
All of these discord users are from the official discord.py server: https://discord.gg/dpy