import json import logging from typing import Any, List, cast import discord from deepdiff.diff import DeepDiff from discord_slash import SlashCommand from discord.ext.commands import Bot logger = logging.getLogger(__name__) settings = ... class SpellBot(Bot): slash: SlashCommand def __init__(self): super().__init__( command_prefix="unused", self_bot=True, help_command=None, intents=discord.Intents().default(), ) async def on_ready(self): await self.sync_commands_if_needed() async def sync_commands_if_needed(self): def strip_keys(d: dict, *keys: str): for key in list(d.keys()): if key in keys: del d[key] continue val = d[key] if isinstance(val, dict): strip_keys(val, *keys) elif isinstance(val, list): for i in val: if isinstance(i, dict): strip_keys(i, *keys) def strip_empty_values(d: dict, *keys: str): for key in list(d.keys()): val = d[key] if key in keys: if not val: del d[key] continue if isinstance(val, dict): strip_empty_values(val, *keys) elif isinstance(val, list): for i in val: if isinstance(i, dict): strip_empty_values(i, *keys) remote_l: List[Any] = cast( List[Any], await self.slash.req.get_all_commands(settings.DEBUG_GUILD or None), ) remote_d = {"data": remote_l} strip_keys( remote_d, "application_id", "version", "default_permission", "guild_id", "id", ) strip_empty_values(remote_d, "description") remote_j = json.dumps(remote_d, sort_keys=True) remote_d = json.loads(remote_j) local_d = await self.slash.to_dict() local_d = {"data": local_d["global"]} strip_keys(local_d, "default_permission", "permissions") strip_empty_values(local_d, "options", "required") local_j = json.dumps(local_d, sort_keys=True) local_d = json.loads(local_j) diff = DeepDiff(remote_d, local_d, ignore_order=True) if diff: logger.info(f"change in commands detected: {diff=}") await self.slash.sync_all_commands() def build_bot() -> SpellBot: bot = SpellBot() bot.slash = SlashCommand(bot, debug_guild=settings.DEBUG_GUILD or None) # ...load cogs... return bot