@@ -1382,7 +1382,7 @@ def _parse_level(name):
13821382
13831383 @permissions .command (name = "override" )
13841384 @checks .has_permissions (PermissionLevel .OWNER )
1385- async def permissions_override (self , ctx , command_name : str .lower , * , level_name : str ):
1385+ async def permissions_override (self , ctx , command_name : str .lower , * , level_name : str = None ):
13861386 """
13871387 Change a permission level for a specific command.
13881388
@@ -1396,8 +1396,16 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
13961396 - `{prefix}perms remove override reply`
13971397 - `{prefix}perms remove override plugin enabled`
13981398
1399+ You can also override multiple commands at once using:
1400+ - `{prefix}perms override bulk`
1401+
13991402 You can retrieve a single or all command level override(s), see`{prefix}help permissions get`.
14001403 """
1404+ if command_name == "bulk" :
1405+ return await self ._bulk_override_flow (ctx )
1406+
1407+ if level_name is None :
1408+ raise commands .MissingRequiredArgument (DummyParam ("level_name" ))
14011409
14021410 command = self .bot .get_command (command_name )
14031411 if command is None :
@@ -1432,6 +1440,126 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
14321440 )
14331441 return await ctx .send (embed = embed )
14341442
1443+ async def _bulk_override_flow (self , ctx ):
1444+ await ctx .send (
1445+ "Please list the commands you want to override. "
1446+ "You can list multiple commands separated by spaces or newlines.\n "
1447+ "Example: `ban, kick, mod`."
1448+ )
1449+
1450+ try :
1451+ msg = await self .bot .wait_for (
1452+ "message" ,
1453+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1454+ timeout = 120.0 ,
1455+ )
1456+ except asyncio .TimeoutError :
1457+ return await ctx .send ("Timed out." )
1458+
1459+ raw_commands = msg .content .replace ("," , " " ).replace ("\n " , " " ).split (" " )
1460+ # Filter empty strings from split
1461+ raw_commands = [c for c in raw_commands if c .strip ()]
1462+
1463+ found_commands = []
1464+ invalid_commands = []
1465+
1466+ for cmd_name in raw_commands :
1467+ cmd = self .bot .get_command (cmd_name )
1468+ if cmd :
1469+ found_commands .append (cmd )
1470+ else :
1471+ invalid_commands .append (cmd_name )
1472+
1473+ if invalid_commands :
1474+ embed = discord .Embed (
1475+ title = "Invalid Commands Found" ,
1476+ description = f"The following commands were not found:\n `{ ', ' .join (invalid_commands )} `\n \n "
1477+ "Do you want to continue with the valid commands? (y/n)" ,
1478+ color = self .bot .error_color ,
1479+ )
1480+ await ctx .send (embed = embed )
1481+ try :
1482+ msg = await self .bot .wait_for (
1483+ "message" ,
1484+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1485+ timeout = 60.0 ,
1486+ )
1487+ if msg .content .lower () not in ("y" , "yes" ):
1488+ return await ctx .send ("Aborted." )
1489+ except asyncio .TimeoutError :
1490+ return await ctx .send ("Timed out." )
1491+
1492+ if not found_commands :
1493+ return await ctx .send ("No valid commands provided. Aborting." )
1494+
1495+ # Expand subcommands
1496+ final_commands = set ()
1497+
1498+ def add_command_recursive (cmd ):
1499+ final_commands .add (cmd )
1500+ if hasattr (cmd , "commands" ):
1501+ for sub in cmd .commands :
1502+ add_command_recursive (sub )
1503+
1504+ for cmd in found_commands :
1505+ add_command_recursive (cmd )
1506+
1507+ await ctx .send (
1508+ f"Found { len (final_commands )} commands (including subcommands).\n "
1509+ "What permission level should these commands be set to? (e.g. `Owner`, `Admin`, `Moderator`, `Supporter`, `User`)"
1510+ )
1511+
1512+ try :
1513+ msg = await self .bot .wait_for (
1514+ "message" ,
1515+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1516+ timeout = 60.0 ,
1517+ )
1518+ except asyncio .TimeoutError :
1519+ return await ctx .send ("Timed out." )
1520+
1521+ level_name = msg .content
1522+ level = self ._parse_level (level_name )
1523+ if level == PermissionLevel .INVALID :
1524+ return await ctx .send (f"Invalid permission level: `{ level_name } `. Aborting." )
1525+
1526+ # Confirmation
1527+ command_list_str = ", " .join (f"`{ c .qualified_name } `" for c in sorted (final_commands , key = lambda x : x .qualified_name ))
1528+
1529+ # Truncate if too long for embed description
1530+ if len (command_list_str ) > 2000 :
1531+ command_list_str = command_list_str [:1997 ] + "..."
1532+
1533+ embed = discord .Embed (
1534+ title = "Confirm Bulk Override" ,
1535+ description = f"**Level:** { level .name } \n \n **Commands:**\n { command_list_str } \n \n "
1536+ "Type `confirm` to apply these changes or `cancel` to abort." ,
1537+ color = self .bot .main_color ,
1538+ )
1539+ await ctx .send (embed = embed )
1540+
1541+ try :
1542+ msg = await self .bot .wait_for (
1543+ "message" ,
1544+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel and m .content .lower () in ("confirm" , "cancel" ),
1545+ timeout = 30.0 ,
1546+ )
1547+ except asyncio .TimeoutError :
1548+ return await ctx .send ("Timed out. No changes applied." )
1549+
1550+ if msg .content .lower () == "cancel" :
1551+ return await ctx .send ("Aborted." )
1552+
1553+ # Apply changes
1554+ count = 0
1555+ for cmd in final_commands :
1556+ self .bot .config ["override_command_level" ][cmd .qualified_name ] = level .name
1557+ count += 1
1558+
1559+ await self .bot .config .update ()
1560+
1561+ await ctx .send (f"Successfully updated permissions for { count } commands." )
1562+
14351563 @permissions .command (name = "add" , usage = "[command/level] [name] [user/role]" )
14361564 @checks .has_permissions (PermissionLevel .OWNER )
14371565 async def permissions_add (
0 commit comments