diff --git a/bot/cogs/__init__.py b/bot/cogs/__init__.py index e974cb5..997f0ee 100644 --- a/bot/cogs/__init__.py +++ b/bot/cogs/__init__.py @@ -161,6 +161,7 @@ from .moderation.warn import Warn from .moderation.role import Role from .moderation.message import Message from .moderation.moderation import Moderation +from .moderation.mod_hub import ModHub from .moderation.topcheck import TopCheck from .moderation.snipe import Snipe @@ -187,7 +188,7 @@ COG_CLASSES: tuple[type, ...] = ( AntiWebhookCreate, AntiWebhookDelete, AntiSpam, AntiCaps, AntiInvite, AntiLink, AntiMassMention, AntiEmojiSpam, Ban, Unban, Mute, Unmute, Lock, Unlock, Hide, Unhide, Kick, Warn, Role, - Message, Moderation, TopCheck, Snipe, + Message, Moderation, ModHub, TopCheck, Snipe, ) diff --git a/bot/cogs/commands/Games.py b/bot/cogs/commands/Games.py index b365a19..a60deb9 100644 --- a/bot/cogs/commands/Games.py +++ b/bot/cogs/commands/Games.py @@ -30,10 +30,25 @@ class Games(Cog): def __init__(self, client: Axiom): self.client = client + @commands.hybrid_group( + name="game", + description="Play mini-games with friends or the bot.", + fallback="help", + invoke_without_command=True, + ) + @blacklist_check() + @ignore_check() + @commands.cooldown(1, 3, commands.BucketType.user) + @commands.guild_only() + async def game(self, ctx: Context): + """Games root — use `/game ` or `>game `.""" + if ctx.subcommand_passed is None: + await ctx.send_help(ctx.command) + ctx.command.reset_cooldown(ctx) - @commands.hybrid_command(name="chess", + @game.command(name="chess", help="Play Chess with a user.", - usage="Chess ") + usage="game chess ") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -49,10 +64,10 @@ class Games(Cog): await game.start(ctx) - @commands.hybrid_command(name="rps", + @game.command(name="rps", help="Play Rock Paper Scissor with bot/user.", aliases=["rockpaperscissors"], - usage="Rockpaperscissors") + usage="game rps [user]") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -62,10 +77,10 @@ class Games(Cog): game = btn.BetaRockPaperScissors(player) await game.start(ctx, timeout=120) - @commands.hybrid_command(name="tic-tac-toe", + @game.command(name="tictactoe", help="play tic-tac-toe game with a user.", - aliases=["ttt", "tictactoe"], - usage="Ticktactoe ") + aliases=["ttt", "tic-tac-toe"], + usage="game tictactoe ") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -80,9 +95,9 @@ class Games(Cog): game = btn.BetaTictactoe(cross=ctx.author, circle=player) await game.start(ctx, timeout=30) - @commands.hybrid_command(name="wordle", + @game.command(name="wordle", help="Wordle Game | Play with bot.", - usage="Wordle") + usage="game wordle") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -92,10 +107,10 @@ class Games(Cog): game = games.Wordle() await game.start(ctx, timeout=120) - @commands.hybrid_command(name="2048", + @game.command(name="2048", help="Play 2048 game with bot.", aliases=["twenty48"], - usage="2048") + usage="game 2048") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -105,10 +120,10 @@ class Games(Cog): game = btn.BetaTwenty48() await game.start(ctx, win_at=2048) - @commands.hybrid_command(name="memory-game", + @game.command(name="memory", help="How strong is your memory?", - aliases=["memory"], - usage="memory-game") + aliases=["memory-game"], + usage="game memory") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -118,10 +133,10 @@ class Games(Cog): game = btn.MemoryGame() await game.start(ctx) - @commands.hybrid_command(name="number-slider", + @game.command(name="slider", help="slide numbers with bot", - aliases=["slider"], - usage="slider") + aliases=["number-slider"], + usage="game slider") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -131,10 +146,10 @@ class Games(Cog): game = btn.NumberSlider() await game.start(ctx) - @commands.hybrid_command(name="battleship", + @game.command(name="battleship", help="Play battleship game with your friend.", aliases=["battle-ship"], - usage="battleship ") + usage="game battleship ") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -164,10 +179,10 @@ class Games(Cog): async def _end_country_guesser(self, ctx: Context): await self.country_guesser_game.end_game_manually(ctx)""" - @commands.hybrid_command(name="connectfour", + @game.command(name="connectfour", help="Play Connect Four game with user.", aliases=["c4", "connect-four", "connect4"], - usage="connectfour ") + usage="game connectfour ") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -184,10 +199,10 @@ class Games(Cog): - @commands.hybrid_command(name="lights-out", + @game.command(name="lightsout", help="Play Lights Show game with bot.", - aliases=["lightsout"], - usage="Lights-out") + aliases=["lights-out"], + usage="game lightsout") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) diff --git a/bot/cogs/commands/Media.py b/bot/cogs/commands/Media.py index 9154b5d..197caa7 100644 --- a/bot/cogs/commands/Media.py +++ b/bot/cogs/commands/Media.py @@ -47,7 +47,13 @@ class Media(commands.Cog): async def on_ready(self): await self.set_db() - @commands.hybrid_group(name="media", help="Setup Media channel, Media channel will not allow users to send messages other than media files.", invoke_without_command=True) + @commands.hybrid_group( + name="media", + description="Setup Media channel; only media files are allowed.", + help="Setup Media channel, Media channel will not allow users to send messages other than media files.", + fallback="help", + invoke_without_command=True, + ) @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) diff --git a/bot/cogs/commands/anti_wl.py b/bot/cogs/commands/anti_wl.py index 88f221d..fc614b0 100644 --- a/bot/cogs/commands/anti_wl.py +++ b/bot/cogs/commands/anti_wl.py @@ -57,16 +57,34 @@ class Whitelist(commands.Cog): ''') await self.db.commit() - @commands.hybrid_command(name='whitelist', aliases=['wl'], help="Whitelists a user from antinuke for a specific action.") - + @commands.hybrid_group( + name="whitelist", + aliases=["wl"], + description="Manage antinuke whitelist.", + fallback="help", + invoke_without_command=True, + help="Whitelists a user from antinuke for a specific action.", + ) @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) @commands.max_concurrency(1, per=commands.BucketType.default, wait=False) @commands.guild_only() @commands.has_permissions(administrator=True) + async def whitelist(self, ctx): + """Whitelist root — use `/whitelist ` or `>whitelist `.""" + if ctx.subcommand_passed is None: + await ctx.send_help(ctx.command) + ctx.command.reset_cooldown(ctx) - async def whitelist(self, ctx, member: discord.Member = None): + @whitelist.command(name="add", help="Whitelists a user from antinuke for a specific action.") + @blacklist_check() + @ignore_check() + @commands.cooldown(1, 5, commands.BucketType.user) + @commands.max_concurrency(1, per=commands.BucketType.default, wait=False) + @commands.guild_only() + @commands.has_permissions(administrator=True) + async def whitelist_add(self, ctx, member: discord.Member = None): if ctx.guild.member_count < 2: view = CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria") return await ctx.send(view=view) @@ -101,7 +119,7 @@ class Whitelist(commands.Cog): view = CV2( "__Whitelist Commands__", "**Adding a user to the whitelist means that no actions will be taken against them if they trigger the Anti-Nuke Module.**", - f"**Usage**\n{ARROWRED} `{prefix}whitelist @user/id`\n{ARROWRED} `{prefix}wl @user`" + f"**Usage**\n{ARROWRED} `{prefix}whitelist add @user/id`\n{ARROWRED} `{prefix}wl add @user`" ) return await ctx.send(view=view) @@ -252,7 +270,7 @@ class Whitelist(commands.Cog): await msg.edit(view=None) - @commands.hybrid_command(name='whitelisted', aliases=['wlist'], help="Shows the list of whitelisted users.") + @whitelist.command(name="list", aliases=["whitelisted", "wlist"], help="Shows the list of whitelisted users.") @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) @@ -308,7 +326,7 @@ class Whitelist(commands.Cog): await ctx.send(view=view) - @commands.hybrid_command(name="whitelistreset", aliases=['wlreset'], help="Resets the whitelisted users.") + @whitelist.command(name="reset", aliases=["whitelistreset", "wlreset"], help="Resets the whitelisted users.") @blacklist_check() @ignore_check() @commands.cooldown(1, 10, commands.BucketType.user) diff --git a/bot/cogs/commands/automod.py b/bot/cogs/commands/automod.py index 59f6fa3..22983b1 100644 --- a/bot/cogs/commands/automod.py +++ b/bot/cogs/commands/automod.py @@ -183,11 +183,19 @@ class Automod(commands.Cog): """) await db.commit() - @commands.hybrid_group(invoke_without_command=True) + @commands.hybrid_group( + name="automod", + aliases=["am"], + invoke_without_command=True, + with_app_command=True, + description="Configure server Automod (enable, disable, punishments, ignores).", + fallback="help", + ) @blacklist_check() @ignore_check() @commands.cooldown(1, 4, commands.BucketType.user) async def automod(self, ctx): + """Automod root — use `/automod ` or `>automod `.""" if ctx.subcommand_passed is None: await ctx.send_help(ctx.command) ctx.command.reset_cooldown(ctx) diff --git a/bot/cogs/commands/customrole.py b/bot/cogs/commands/customrole.py index 2db74ac..9d30fb0 100644 --- a/bot/cogs/commands/customrole.py +++ b/bot/cogs/commands/customrole.py @@ -500,7 +500,23 @@ class Customrole(commands.Cog): - @commands.hybrid_command(name="staff", + @commands.hybrid_group( + name="customrole", + aliases=["cr"], + description="Assign or remove custom roles for members.", + fallback="help", + invoke_without_command=True, + ) + @blacklist_check() + @ignore_check() + @commands.cooldown(1, 3, commands.BucketType.user) + async def customrole(self, context: Context): + """Customrole root — use `/customrole ` or `>customrole `.""" + if context.subcommand_passed is None: + await context.send_help(context.command) + context.command.reset_cooldown(context) + + @customrole.command(name="staff", description="Gives the staff role to the user.", aliases=['official'], help="Gives the staff role to the user.") @@ -511,7 +527,7 @@ class Customrole(commands.Cog): async def _staff(self, context: Context, member: discord.Member) -> None: await self.handle_role_command(context, member, 'staff') - @commands.hybrid_command(name="girl", + @customrole.command(name="girl", description="Gives the girl role to the user.", aliases=['qt'], help="Gives the girl role to the user.") @@ -522,7 +538,7 @@ class Customrole(commands.Cog): async def _girl(self, context: Context, member: discord.Member) -> None: await self.handle_role_command(context, member, 'girl') - @commands.hybrid_command(name="vip", + @customrole.command(name="vip", description="Gives the VIP role to the user.", help="Gives the VIP role to the user.") @blacklist_check() @@ -532,7 +548,7 @@ class Customrole(commands.Cog): async def _vip(self, context: Context, member: discord.Member) -> None: await self.handle_role_command(context, member, 'vip') - @commands.hybrid_command(name="guest", + @customrole.command(name="guest", description="Gives the guest role to the user.", help="Gives the guest role to the user.") @blacklist_check() @@ -542,7 +558,7 @@ class Customrole(commands.Cog): async def _guest(self, context: Context, member: discord.Member) -> None: await self.handle_role_command(context, member, 'guest') - @commands.hybrid_command(name="friend", + @customrole.command(name="friend", description="Gives the friend role to the user.", aliases=['frnd'], help="Gives the friend role to the user.") diff --git a/bot/cogs/commands/extra.py b/bot/cogs/commands/extra.py index f2f904d..5157d85 100644 --- a/bot/cogs/commands/extra.py +++ b/bot/cogs/commands/extra.py @@ -125,7 +125,12 @@ class Extra(commands.Cog): self.color = 0xFF0000 self.start_time = datetime.datetime.now() - @commands.hybrid_group(name="banner") + @commands.hybrid_group( + name="banner", + description="Show user or server banners.", + fallback="help", + invoke_without_command=True, + ) @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) diff --git a/bot/cogs/commands/filters.py b/bot/cogs/commands/filters.py index 558c6c5..0c6d2a0 100644 --- a/bot/cogs/commands/filters.py +++ b/bot/cogs/commands/filters.py @@ -66,7 +66,12 @@ class FilterCog(commands.Cog): self.active_filters[ctx.guild.id] = filter_name await ctx.send(embed=discord.Embed(description=f"Filter set to **{filter_name}**.", color=discord.Color.green())) - @commands.hybrid_group(invoke_without_command=True) + @commands.hybrid_group( + name="filter", + description="Enable or disable audio filters for music.", + fallback="help", + invoke_without_command=True, + ) @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) diff --git a/bot/cogs/commands/giveaway.py b/bot/cogs/commands/giveaway.py index 8038565..194cd61 100644 --- a/bot/cogs/commands/giveaway.py +++ b/bot/cogs/commands/giveaway.py @@ -159,7 +159,24 @@ class Giveaway(commands.Cog): - @commands.hybrid_command(description="Starts a new giveaway.") + @commands.hybrid_group( + name="giveaway", + aliases=["g"], + description="Manage server giveaways.", + fallback="help", + invoke_without_command=True, + ) + @blacklist_check() + @ignore_check() + @commands.cooldown(1, 5, commands.BucketType.user) + @commands.has_guild_permissions(manage_guild=True) + async def giveaway(self, ctx): + """Giveaway root — use `/giveaway ` or `>giveaway `.""" + if ctx.subcommand_passed is None: + await ctx.send_help(ctx.command) + ctx.command.reset_cooldown(ctx) + + @giveaway.command(name="start", aliases=["gstart"], description="Starts a new giveaway.") @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) @@ -244,7 +261,7 @@ class Giveaway(commands.Cog): print(f"Giveaway message deleted in {message.guild.name} - {message.guild.id}") await self.connection.commit() - @commands.hybrid_command(name="gend", description="Ends a giveaway before its ending time.", help="Ends a giveaway before its ending time.") + @giveaway.command(name="end", aliases=["gend"], description="Ends a giveaway before its ending time.", help="Ends a giveaway before its ending time.") @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) @@ -329,7 +346,7 @@ class Giveaway(commands.Cog): await ctx.send("Please reply to the giveaway message or provide the giveaway ID.") await self.connection.commit() - @commands.hybrid_command(description="Rerolls a giveaway on replying the giveaway message.", help="Rerolls a giveaway on replying the giveaway message.") + @giveaway.command(name="reroll", aliases=["greroll"], description="Rerolls a giveaway on replying the giveaway message.", help="Rerolls a giveaway on replying the giveaway message.") @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) @@ -389,7 +406,7 @@ class Giveaway(commands.Cog): return val * time_dict[unit] - @commands.hybrid_command(name="glist", description="Lists all ongoing giveaways.") + @giveaway.command(name="list", aliases=["glist"], description="Lists all ongoing giveaways.") @blacklist_check() @ignore_check() @commands.cooldown(1, 5, commands.BucketType.user) diff --git a/bot/cogs/commands/logging.py b/bot/cogs/commands/logging.py index 94500f1..7c38b7d 100644 --- a/bot/cogs/commands/logging.py +++ b/bot/cogs/commands/logging.py @@ -1598,7 +1598,12 @@ class Logging(commands.Cog): embeds.append(config_embed) return embeds - @commands.hybrid_group(invoke_without_command=True, name="log") + @commands.hybrid_group( + invoke_without_command=True, + name="log", + description="Configure server logging channels and events.", + fallback="help", + ) async def log(self, ctx: commands.Context): """Main logging command group. Works with both slash (/) and prefix (!) commands.""" diff --git a/bot/cogs/commands/music.py b/bot/cogs/commands/music.py index d0d710e..2b0d835 100644 --- a/bot/cogs/commands/music.py +++ b/bot/cogs/commands/music.py @@ -585,7 +585,23 @@ class Music(commands.Cog): bar = '█' * filled_length + '░' * (length - filled_length) return bar - @commands.command(name="play", aliases=['p'], usage="play ", help="Plays a song or playlist.") + @commands.hybrid_group( + name="music", + aliases=["m"], + description="Music player commands.", + fallback="help", + invoke_without_command=True, + ) + @blacklist_check() + @ignore_check() + @commands.cooldown(1, 3, commands.BucketType.user) + async def music(self, ctx: commands.Context): + """Music root — use `/music ` or `>music `.""" + if ctx.subcommand_passed is None: + await ctx.send_help(ctx.command) + ctx.command.reset_cooldown(ctx) + + @music.command(name="play", aliases=['p'], usage="music play ", help="Plays a song or playlist.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -594,7 +610,7 @@ class Music(commands.Cog): await self.play_source(ctx, query) - @commands.command(name="search", usage="search ", help="Searches music from multiple platforms.") + @music.command(name="search", usage="music search ", help="Searches music from multiple platforms.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -606,7 +622,7 @@ class Music(commands.Cog): await ctx.send(view=PlatformSelectView(ctx, query)) - @commands.command(name="nowplaying", aliases=["nop"], usage="nowplaying", help="Shows the info about current playing song.") + @music.command(name="nowplaying", aliases=["nop"], usage="music nowplaying", help="Shows the info about current playing song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -659,7 +675,7 @@ class Music(commands.Cog): await ctx.send(view=view) - @commands.command(name="autoplay", usage="autoplay", help="Toggles autoplay mode.") + @music.command(name="autoplay", usage="music autoplay", help="Toggles autoplay mode.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -679,7 +695,7 @@ class Music(commands.Cog): ) await ctx.send(view=CV2(f"{TICK} Autoplay {'enabled' if vc.autoplay == wavelink.AutoPlayMode.enabled else 'disabled'} by {ctx.author.mention}.")) - @commands.command(name="loop", usage="loop", help="Toggles loop mode.") + @music.command(name="loop", usage="music loop", help="Toggles loop mode.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -700,7 +716,7 @@ class Music(commands.Cog): await ctx.send(view=CV2("I'm not connected to a voice channel.")) - @commands.command(name="pause", usage="pause", help="Pauses the current song.") + @music.command(name="pause", usage="music pause", help="Pauses the current song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -721,7 +737,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2(f"{WARNING} Nothing is playing or already paused.")) - @commands.command(name="resume", usage="resume", help="Resumes the paused song.") + @music.command(name="resume", usage="music resume", help="Resumes the paused song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -742,7 +758,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("Player is not paused.")) - @commands.command(name="skip", usage="skip", help="Skips the current song.") + @music.command(name="skip", usage="music skip", help="Skips the current song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -767,7 +783,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2(f"{WARNING} No song is playing or in the queue to skip.")) - @commands.command(name="shuffle", usage="shuffle", help="Shuffles the queue.") + @music.command(name="shuffle", usage="music shuffle", help="Shuffles the queue.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -787,7 +803,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("Queue is empty.")) - @commands.command(name="stop", usage="stop", help="Stops the current song and clears the queue.") + @music.command(name="stop", usage="music stop", help="Stops the current song and clears the queue.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -810,7 +826,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("Nothing is playing to stop.")) - @commands.command(name="volume", aliases=["vol"], usage="volume ", help="Sets the volume of the player.") + @music.command(name="volume", aliases=["vol"], usage="music volume ", help="Sets the volume of the player.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -834,7 +850,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("Bot is not connected to a voice channel.")) - @commands.command(name="queue", usage="queue", help="Shows the current queue.") + @music.command(name="queue", usage="music queue", help="Shows the current queue.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -860,7 +876,7 @@ class Music(commands.Cog): ctx=ctx) await paginator.paginate() - @commands.command(name="clearqueue", usage="clearqueue", help="Clears the queue.") + @music.command(name="clearqueue", usage="music clearqueue", help="Clears the queue.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -881,7 +897,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("No queue to clear.")) - @commands.command(name="replay", usage="replay", help="Replays the current song.") + @music.command(name="replay", usage="music replay", help="Replays the current song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -902,7 +918,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("No track is currently playing.")) - @commands.command(name="join", aliases=["connect"], usage="join", help="Joins the voice channel.") + @music.command(name="join", aliases=["connect"], usage="music join", help="Joins the voice channel.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -913,7 +929,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("You need to join a voice channel first.")) - @commands.hybrid_command(name="disconnect", aliases=["dc", "leave"], usage="disconnect", help="Disconnects the bot from the voice channel.") + @music.command(name="disconnect", aliases=["dc", "leave"], usage="music disconnect", help="Disconnects the bot from the voice channel.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) @@ -933,7 +949,7 @@ class Music(commands.Cog): else: await ctx.send(view=CV2("Bot is not connected to any voice channel.")) - @commands.command(name="seek", usage="seek ", help="Seeks to a specific percentage of the song.") + @music.command(name="seek", usage="music seek ", help="Seeks to a specific percentage of the song.") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) diff --git a/bot/cogs/commands/ticket.py b/bot/cogs/commands/ticket.py index dd19e3c..a84cd85 100644 --- a/bot/cogs/commands/ticket.py +++ b/bot/cogs/commands/ticket.py @@ -378,7 +378,12 @@ class TicketCog(commands.Cog, name="Ticket System"): await ch.send(content=" ".join(pings), embed=ticket_embed, view=TicketActionsView(self, ch.id, cat_id)) await inter.followup.send(f"Your ticket has been successfully created: {ch.mention}", ephemeral=True) - @commands.hybrid_group(name="ticket", description="Main command group for the ticket system.") + @commands.hybrid_group( + name="ticket", + description="Main command group for the ticket system.", + fallback="help", + invoke_without_command=True, + ) @commands.guild_only() async def ticket(self, ctx): if ctx.invoked_subcommand is None: await ctx.send_help(ctx.command) diff --git a/bot/cogs/commands/welcome.py b/bot/cogs/commands/welcome.py index e523e19..b7a1840 100644 --- a/bot/cogs/commands/welcome.py +++ b/bot/cogs/commands/welcome.py @@ -78,7 +78,13 @@ class Welcomer(commands.Cog): """) await db.commit() - @commands.hybrid_group(invoke_without_command=True, name="greet", help="Shows all the greet commands.") + @commands.hybrid_group( + invoke_without_command=True, + name="greet", + description="Configure welcome/greet messages for new members.", + help="Shows all the greet commands.", + fallback="help", + ) @blacklist_check() @ignore_check() async def greet(self, ctx: commands.Context): diff --git a/bot/cogs/moderation/ban.py b/bot/cogs/moderation/ban.py index e9b5d40..612204f 100644 --- a/bot/cogs/moderation/ban.py +++ b/bot/cogs/moderation/ban.py @@ -25,7 +25,7 @@ class Ban(commands.Cog): def get_user_avatar(self, user): return user.avatar.url if user.avatar else user.default_avatar.url - @commands.hybrid_command( + @commands.command( name="ban", help="Bans a user from the Server", usage="ban ", diff --git a/bot/cogs/moderation/hide.py b/bot/cogs/moderation/hide.py index d866f8d..8242c42 100644 --- a/bot/cogs/moderation/hide.py +++ b/bot/cogs/moderation/hide.py @@ -20,7 +20,7 @@ class Hide(commands.Cog): self.bot = bot self.color = discord.Color.from_rgb(255, 0, 0) # Red color for embeds - @commands.hybrid_command( + @commands.command( name="hide", help="Hides a channel from the default role (@everyone).", usage="hide [channel]", diff --git a/bot/cogs/moderation/kick.py b/bot/cogs/moderation/kick.py index 5c8c01d..ae57cd1 100644 --- a/bot/cogs/moderation/kick.py +++ b/bot/cogs/moderation/kick.py @@ -22,7 +22,7 @@ class Kick(commands.Cog): # Color is set to Red (255, 0, 0) as requested self.color = discord.Color.from_rgb(255, 0, 0) - @commands.hybrid_command( + @commands.command( name="kick", help="Kicks a member from the server.", usage="kick [reason]", diff --git a/bot/cogs/moderation/lock.py b/bot/cogs/moderation/lock.py index 71bc72d..a09303e 100644 --- a/bot/cogs/moderation/lock.py +++ b/bot/cogs/moderation/lock.py @@ -20,7 +20,7 @@ class Lock(commands.Cog): self.bot = bot self.color = discord.Color.red() - @commands.hybrid_command( + @commands.command( name="lock", description="Locks a channel to prevent sending messages.", aliases=["lockchannel"] diff --git a/bot/cogs/moderation/mod_hub.py b/bot/cogs/moderation/mod_hub.py new file mode 100644 index 0000000..3fa14ab --- /dev/null +++ b/bot/cogs/moderation/mod_hub.py @@ -0,0 +1,224 @@ +# ╔══════════════════════════════════════════════════════════════════╗ +# ║ ║ +# ║ +-+-+-+-+-+-+-+-+ ║ +# ║ |H|e|x|a|H|o|s|t| ║ +# ║ +-+-+-+-+-+-+-+-+ ║ +# ║ ║ +# ║ © 2026 HexaHost — All Rights Reserved ║ +# ║ ║ +# ║ discord ── https://discord.gg/hexahost ║ +# ║ github ── https://github.com/theoneandonlymace ║ +# ║ ║ +# ╚══════════════════════════════════════════════════════════════════╝ + +from __future__ import annotations + +import discord +from discord.ext import commands + + +class ModHub(commands.Cog): + """Slash/prefix hub that exposes moderation actions under `/mod `.""" + + def __init__(self, bot): + self.bot = bot + + async def _get_cog(self, ctx: commands.Context, name: str): + cog = self.bot.get_cog(name) + if not cog: + await ctx.send(f"{name} module unavailable.") + return None + return cog + + @commands.hybrid_group( + name="mod", + description="Moderation commands", + fallback="help", + invoke_without_command=True, + ) + @commands.guild_only() + async def mod(self, ctx: commands.Context): + """Moderation root — use `/mod ` or `>mod `.""" + if ctx.subcommand_passed is None: + await ctx.send_help(ctx.command) + + @mod.command(name="ban", description="Ban a user") + @commands.has_permissions(ban_members=True) + @commands.bot_has_permissions(ban_members=True) + @commands.guild_only() + async def mod_ban(self, ctx: commands.Context, user: discord.User, *, reason: str = None): + cog = await self._get_cog(ctx, "Ban") + if not cog: + return + await cog.ban(ctx, user, reason=reason) + + @mod.command(name="unban", description="Unban a user") + @commands.has_permissions(ban_members=True) + @commands.bot_has_permissions(ban_members=True) + @commands.guild_only() + async def mod_unban(self, ctx: commands.Context, user: discord.User, *, reason: str = None): + cog = await self._get_cog(ctx, "Unban") + if not cog: + return + await cog.unban(ctx, user, reason=reason) + + @mod.command(name="kick", description="Kick a member") + @commands.has_permissions(kick_members=True) + @commands.bot_has_permissions(kick_members=True) + @commands.guild_only() + async def mod_kick(self, ctx: commands.Context, member: discord.Member, *, reason: str = None): + cog = await self._get_cog(ctx, "Kick") + if not cog: + return + await cog.kick_command(ctx, member, reason=reason) + + @mod.command(name="mute", description="Mute / timeout a member") + @commands.has_permissions(moderate_members=True) + @commands.bot_has_permissions(moderate_members=True) + @commands.guild_only() + async def mod_mute( + self, + ctx: commands.Context, + user: discord.Member, + time: str = None, + *, + reason: str = None, + ): + cog = await self._get_cog(ctx, "Mute") + if not cog: + return + await cog.mute(ctx, user, time, reason=reason) + + @mod.command(name="unmute", description="Remove a timeout from a member") + @commands.has_permissions(moderate_members=True) + @commands.bot_has_permissions(moderate_members=True) + @commands.guild_only() + async def mod_unmute(self, ctx: commands.Context, user: discord.Member): + cog = await self._get_cog(ctx, "Unmute") + if not cog: + return + await cog.unmute(ctx, user) + + @mod.command(name="warn", description="Warn a member") + @commands.has_permissions(moderate_members=True) + @commands.guild_only() + async def mod_warn(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): + cog = await self._get_cog(ctx, "Warn") + if not cog: + return + await cog.warn(ctx, user, reason=reason) + + @mod.command(name="clearwarns", description="Clear warnings for a member") + @commands.has_permissions(moderate_members=True) + @commands.guild_only() + async def mod_clearwarns(self, ctx: commands.Context, user: discord.Member): + cog = await self._get_cog(ctx, "Warn") + if not cog: + return + await cog.clearwarns(ctx, user) + + @mod.command(name="lock", description="Lock a channel") + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.guild_only() + async def mod_lock(self, ctx: commands.Context, channel: discord.TextChannel = None): + cog = await self._get_cog(ctx, "Lock") + if not cog: + return + await cog.lock_command(ctx, channel) + + @mod.command(name="unlock", description="Unlock a channel") + @commands.has_permissions(manage_roles=True) + @commands.bot_has_permissions(manage_roles=True) + @commands.guild_only() + async def mod_unlock(self, ctx: commands.Context, channel: discord.TextChannel = None): + cog = await self._get_cog(ctx, "Unlock") + if not cog: + return + await cog.unlock_command(ctx, channel) + + @mod.command(name="hide", description="Hide a channel") + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.guild_only() + async def mod_hide(self, ctx: commands.Context, channel: discord.TextChannel = None): + cog = await self._get_cog(ctx, "Hide") + if not cog: + return + await cog.hide_command(ctx, channel) + + @mod.command(name="unhide", description="Unhide a channel") + @commands.has_permissions(manage_channels=True) + @commands.bot_has_permissions(manage_channels=True) + @commands.guild_only() + async def mod_unhide(self, ctx: commands.Context, channel: discord.TextChannel = None): + cog = await self._get_cog(ctx, "Unhide") + if not cog: + return + await cog.unhide_command(ctx, channel) + + @mod.command(name="lockall", description="Lock all channels") + @commands.has_permissions(administrator=True) + @commands.guild_only() + async def mod_lockall(self, ctx: commands.Context): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog.lockall(ctx) + + @mod.command(name="unlockall", description="Unlock all channels") + @commands.has_permissions(administrator=True) + @commands.guild_only() + async def mod_unlockall(self, ctx: commands.Context): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog.unlockall(ctx) + + @mod.command(name="nuke", description="Nuke the current channel") + @commands.has_permissions(manage_channels=True) + @commands.guild_only() + async def mod_nuke(self, ctx: commands.Context): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog._nuke(ctx) + + @mod.command(name="slowmode", description="Set channel slowmode") + @commands.has_permissions(manage_messages=True) + @commands.guild_only() + async def mod_slowmode(self, ctx: commands.Context, seconds: int = 0): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog._slowmode(ctx, seconds) + + @mod.command(name="nick", description="Change a member's nickname") + @commands.has_permissions(manage_nicknames=True) + @commands.bot_has_permissions(manage_nicknames=True) + @commands.guild_only() + async def mod_nick( + self, + ctx: commands.Context, + member: discord.Member, + *, + name: str = None, + ): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog.changenickname(ctx, member, name=name) + + @mod.command(name="audit", description="View recent audit log entries") + @commands.has_permissions(view_audit_log=True) + @commands.bot_has_permissions(view_audit_log=True) + @commands.guild_only() + async def mod_audit(self, ctx: commands.Context, limit: int): + cog = await self._get_cog(ctx, "Moderation") + if not cog: + return + await cog.auditlog(ctx, limit) + + +async def setup(bot): + await bot.add_cog(ModHub(bot)) diff --git a/bot/cogs/moderation/moderation.py b/bot/cogs/moderation/moderation.py index cad7914..a05082d 100644 --- a/bot/cogs/moderation/moderation.py +++ b/bot/cogs/moderation/moderation.py @@ -126,7 +126,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="unlockall", + @commands.command(name="unlockall", help="Unlocks all channels in the Guild.", usage="unlockall") @blacklist_check() @@ -211,7 +211,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="lockall", + @commands.command(name="lockall", help="locks all the channels in Guild.", usage="lockall") @blacklist_check() @@ -294,7 +294,7 @@ class Moderation(commands.Cog): await ctx.send(embed=denied, mention_author=False) - @commands.hybrid_command(name="give", + @commands.command(name="give", help="Gives the mentioned user a role.", usage="give ", aliases=["addrole"]) @@ -364,7 +364,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="hideall", help="Hides all the channels .", + @commands.command(name="hideall", help="Hides all the channels .", usage="hideall") @blacklist_check() @ignore_check() @@ -442,7 +442,7 @@ class Moderation(commands.Cog): icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url) await ctx.send(embed=denied, mention_author=False) - @commands.hybrid_command(name="unhideall", help="Unhides all the channels in the server.", + @commands.command(name="unhideall", help="Unhides all the channels in the server.", usage="unhideall") @blacklist_check() @ignore_check() @@ -522,7 +522,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command( + @commands.command( name="prefix", aliases=["setprefix", "prefixset"], help="Allows you to change the prefix of the bot for this server" @@ -560,7 +560,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="clone", help="Clones a channel.") + @commands.command(name="clone", help="Clones a channel.") @blacklist_check() @ignore_check() @commands.has_permissions(manage_channels=True) @@ -604,7 +604,7 @@ class Moderation(commands.Cog): await ctx.send(embed=error) - @commands.hybrid_command(name="nick", + @commands.command(name="nick", aliases=['setnick'], help="To change someone's nickname.", usage="nick [member]") @@ -680,7 +680,7 @@ class Moderation(commands.Cog): await ctx.send(embed=error) - @commands.hybrid_command(name="nuke", help="Nukes a channel", usage="nuke") + @commands.command(name="nuke", help="Nukes a channel", usage="nuke") @blacklist_check() @ignore_check() @top_check() @@ -744,7 +744,7 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="slowmode", + @commands.command(name="slowmode", help="Changes the slowmode", usage="slowmode [seconds]", aliases=["slow"]) @@ -774,7 +774,7 @@ class Moderation(commands.Cog): await ctx.send(embed=embed) - @commands.hybrid_command(name="unslowmode", + @commands.command(name="unslowmode", help="Disables slowmode", usage="unslowmode", aliases=["unslow"]) @@ -976,11 +976,10 @@ class Moderation(commands.Cog): - @commands.hybrid_command(name="unbanall", + @commands.command(name="unbanall", help="Unbans Everyone In The Guild!", aliases=['massunban'], - usage="Unbanall", - with_app_command=True) + usage="Unbanall") @blacklist_check() @ignore_check() @commands.cooldown(1, 30, commands.BucketType.user) @@ -1041,7 +1040,7 @@ class Moderation(commands.Cog): view.add_item(button1) await ctx.reply(embed=embed, view=view, mention_author=False) - @commands.hybrid_command(name="audit", + @commands.command(name="audit", help="See recents audit log action in the server .") @blacklist_check() @ignore_check() diff --git a/bot/cogs/moderation/timeout.py b/bot/cogs/moderation/timeout.py index 6ee0ec0..a4f7c01 100644 --- a/bot/cogs/moderation/timeout.py +++ b/bot/cogs/moderation/timeout.py @@ -132,7 +132,7 @@ class Mute(commands.Cog): return timedelta(days=time_value), f"{time_value} days" return None, None - @commands.hybrid_command( + @commands.command( name="mute", help="Mutes a user with optional time and reason", usage="mute [time] [reason]", diff --git a/bot/cogs/moderation/unban.py b/bot/cogs/moderation/unban.py index 5619b70..b2942db 100644 --- a/bot/cogs/moderation/unban.py +++ b/bot/cogs/moderation/unban.py @@ -132,7 +132,7 @@ class Unban(commands.Cog): def get_user_avatar(self, user): return user.avatar.url if user.avatar else user.default_avatar.url - @commands.hybrid_command( + @commands.command( name="unban", help="Unbans a user from the Server", usage="unban ", diff --git a/bot/cogs/moderation/unhide.py b/bot/cogs/moderation/unhide.py index 1fb2212..be82999 100644 --- a/bot/cogs/moderation/unhide.py +++ b/bot/cogs/moderation/unhide.py @@ -20,7 +20,7 @@ class Unhide(commands.Cog): self.bot = bot self.color = discord.Color.from_rgb(255,0, 0) # Green color for success - @commands.hybrid_command( + @commands.command( name="unhide", help="Unhides a channel for the default role (@everyone).", usage="unhide [channel]", diff --git a/bot/cogs/moderation/unlock.py b/bot/cogs/moderation/unlock.py index f57dfd7..ac460a7 100644 --- a/bot/cogs/moderation/unlock.py +++ b/bot/cogs/moderation/unlock.py @@ -69,7 +69,7 @@ class Unlock(commands.Cog): self.bot = bot self.color = discord.Color.from_rgb(255, 0, 0) - @commands.hybrid_command( + @commands.command( name="unlock", help="Unlocks a channel to allow sending messages.", usage="unlock ", diff --git a/bot/cogs/moderation/unmute.py b/bot/cogs/moderation/unmute.py index 84456ae..87ff019 100644 --- a/bot/cogs/moderation/unmute.py +++ b/bot/cogs/moderation/unmute.py @@ -140,7 +140,7 @@ class Unmute(commands.Cog): def get_user_avatar(self, user): return user.avatar.url if user.avatar else user.default_avatar.url - @commands.hybrid_command( + @commands.command( name="unmute", help="Unmutes a user from the Server", usage="unmute ", diff --git a/bot/cogs/moderation/warn.py b/bot/cogs/moderation/warn.py index 52addee..bf7ba42 100644 --- a/bot/cogs/moderation/warn.py +++ b/bot/cogs/moderation/warn.py @@ -94,7 +94,7 @@ class Warn(commands.Cog): except Exception as e: print(f"Error during database setup: {e}") - @commands.hybrid_command( + @commands.command( name="warn", help="Warn a user in the server", usage="warn [reason]", @@ -156,7 +156,7 @@ class Warn(commands.Cog): await ctx.send(f"An error occurred: {str(e)}") print(f"Error during warn command: {e}") - @commands.hybrid_command( + @commands.command( name="clearwarns", help="Clear all warnings for a user", aliases=["clearwarn" , "clearwarnings"],