import discord from discord.ext import commands from cogs.cs import CSCog from cogs.insult import InsultsCog from cogs.roulette import RouletteCog from cogs.roles import RolesCog from setup.logger import LOGGER from setup.config import Config bot_intents = discord.Intents.default() bot_intents.message_content = True bot_intents.members = True bot_intents.presences = True bot_intents.guilds = True bot_intents.reactions = True bot_intents.voice_states = True bot = commands.Bot(command_prefix='$', intents=bot_intents) COG_REGISTRY = { 'cs': CSCog, 'insult': InsultsCog, 'roulette': RouletteCog, 'roles': RolesCog, } # Register a global admin slash command directly on the bot to avoid circular imports. from discord import option as discord_option @bot.slash_command(name="reload", description="Reload a feature (cog) by name, or 'all')") @discord_option(name="feature", description="Feature to reload (e.g. cs, insult, roles, roulette, all)", input_type=str, required=True) async def reload(ctx: discord.ApplicationContext, feature: str): # Only allow administrators if not ctx.author or not getattr(ctx.author, 'guild_permissions', None) or not ctx.author.guild_permissions.administrator: await ctx.respond("You must be a server administrator to use this command.", ephemeral=True) return await ctx.defer() reload_feature(feature) await ctx.followup.send(f"Reload requested for feature: {feature}", ephemeral=True) # Register a global admin slash command directly on the bot to avoid circular imports. from discord import option as discord_option @bot.slash_command(name="reload", description="Reload a feature (cog) by name, or 'all')") @discord_option(name="feature", description="Feature to reload (e.g. cs, insult, roles, roulette, all)", input_type=str, required=True) async def reload(ctx: discord.ApplicationContext, feature: str): # Only allow administrators if not ctx.author or not getattr(ctx.author, 'guild_permissions', None) or not ctx.author.guild_permissions.administrator: await ctx.respond("You must be a server administrator to use this command.", ephemeral=True) return await ctx.defer() reload_feature(feature) await ctx.followup.send(f"Reload requested for feature: {feature}", ephemeral=True) def _normalize_cog_names(cogs=None): if cogs is None: return list(COG_REGISTRY.keys()) if isinstance(cogs, str): cogs = [cogs] selected_cogs = [] for cog_name in cogs: name = str(cog_name).strip().lower() if not name: continue if name in {'all', '*'}: return list(COG_REGISTRY.keys()) if name in COG_REGISTRY: selected_cogs.append(name) else: LOGGER.warning("Unknown cog '%s'. Available cogs: %s", cog_name, ', '.join(COG_REGISTRY.keys())) return list(dict.fromkeys(selected_cogs)) def register_cogs(cogs=None): # Ensure guild configs are loaded before instantiating cogs try: Config.load_guilds_configs() except Exception: LOGGER.debug('Could not reload guild configs during register_cogs.') selected_cogs = _normalize_cog_names(cogs) existing_cog_names = set(COG_REGISTRY.values()) for existing_name in list(bot.cogs.keys()): if existing_name in {cog_class.__name__ for cog_class in existing_cog_names}: bot.remove_cog(existing_name) for cog_name in selected_cogs: cog_class = COG_REGISTRY[cog_name] bot.add_cog(cog_class(bot)) return selected_cogs def reload_feature(feature: str): normalized_feature = str(feature).strip().lower() LOGGER.debug(bot.cogs) if normalized_feature in {'all', '*'}: register_cogs() LOGGER.debug('All cogs reloaded!') return if normalized_feature not in COG_REGISTRY: LOGGER.warning("Unknown cog '%s'. Available cogs: %s", feature, ', '.join(COG_REGISTRY.keys())) return LOGGER.debug(f'Reloading {feature}...') # Refresh guild configs before reloading a single feature try: Config.load_guilds_configs() except Exception: LOGGER.debug('Could not reload guild configs during reload_feature.') target_cog_name = COG_REGISTRY[normalized_feature].__name__ for existing_name in list(bot.cogs.keys()): if existing_name == target_cog_name: bot.remove_cog(existing_name) break bot.add_cog(COG_REGISTRY[normalized_feature](bot)) LOGGER.debug(f'{feature} reloaded!')