How to write a sync command? #8170
-
How are we supposed to write a sync command to sync application commands? First you have to clear the commands, then sync them. However after you clear commands, you can't restore the tree to then be able to actually sync your commands. bot: MyBot # subclass of discord.ext.commands.Bot,
# uses cogs to create a bunch of commands
# these cogs are then loaded in its setup_hook()
# settings.DEBUG_GUILD is `None` (global) or some int (a debug guild id)
guild_id: Optional[int] = settings.DEBUG_GUILD
if guild_id:
print("syncing commands to debug guild:", guild_id)
else:
print("syncing global commands")
guild = discord.Object(id=guild_id) if guild_id else None
# ok, let's first clear commands!
bot.tree.clear_commands(guild=guild)
await bot.tree.sync(guild=guild)
# cool, they've been cleared, now let's sync them!
# oh wait, we can't, they've been cleared... there's no way to restore bot.tree :(
await bot.tree.sync(guild=guild) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 6 replies
-
You DONT clear. Syncing is an upsert operation. It'll update discord's list of command to be synced with yours. Meaning it will delete, create and/or update commands. |
Beta Was this translation helpful? Give feedback.
-
Hopefully this is helpful to someone else, but this is basically what I did to solve this: import asyncio
import logging
from importlib import import_module
from inspect import isclass
from pathlib import Path
from pkgutil import iter_modules
from typing import TYPE_CHECKING
from discord.ext import commands
from discord.ext.commands import Bot
from mybot.cogs import FooCog
from mybot.cogs import BarCog
from mybot.cogs import BazCog
# ...
if TYPE_CHECKING:
from mybot.client import MyBot
logger = logging.getLogger(__name__)
__all__ = [
"FooCog",
"BarCog",
"BazCog",
#...
]
async def load_all_cogs(bot: Bot) -> Bot:
# iterate through the modules in the current package
package_dir = Path(__file__).resolve().parent
for info in iter_modules([str(package_dir)]):
# import the module and iterate through its attributes
module = import_module(f"{__name__}.{info.name}")
for attribute_name in dir(module): # pragma: no cover
attribute = getattr(module, attribute_name)
# Only load cogs in this module if they're exported
if (
isclass(attribute)
and issubclass(attribute, commands.Cog)
and attribute.__name__ in __all__
):
if module.__name__ in bot.extensions:
logger.info("reloading extension %s...", module.__name__)
await bot.reload_extension(module.__name__)
else:
logger.info("loading extension %s...", module.__name__)
await bot.load_extension(module.__name__)
break
return bot
async def load_extensions(bot: MyBot, do_sync: bool = False) -> None:
guild = bot.settings.GUILD_OBJECT
if do_sync:
if guild:
logger.info("syncing commands to debug guild: %s", guild.id)
else:
logger.info("syncing global commands")
logger.info("clearing commands...")
bot.tree.clear_commands(guild=guild)
await bot.tree.sync(guild=guild)
logger.info("waiting to avoid rate limit...")
await asyncio.sleep(1)
logger.info("loading cogs...")
await load_all_cogs(bot)
commands = [c.name for c in bot.tree.get_commands(guild=guild)]
logger.info("registered commands: %s", ", ".join(commands))
if do_sync:
logger.info("syncing commands...")
await bot.tree.sync(guild=guild)
# Then, inside MyBot#setup_hook:
await load_extensions(self)
# And inside the `!sync` command implementation in SyncCog:
await load_extensions(bot, do_sync=True) I was hoping there was an easier way of doing this tho! |
Beta Was this translation helpful? Give feedback.
Hopefully this is helpful to someone else, but this is basically what I did to solve this: