@@ -1408,7 +1408,7 @@ def _parse_level(name):
14081408
14091409 @permissions .command (name = "override" )
14101410 @checks .has_permissions (PermissionLevel .OWNER )
1411- async def permissions_override (self , ctx , command_name : str .lower , * , level_name : str ):
1411+ async def permissions_override (self , ctx , command_name : str .lower , * , level_name : str = None ):
14121412 """
14131413 Change a permission level for a specific command.
14141414
@@ -1422,8 +1422,16 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
14221422 - `{prefix}perms remove override reply`
14231423 - `{prefix}perms remove override plugin enabled`
14241424
1425+ You can also override multiple commands at once using:
1426+ - `{prefix}perms override bulk`
1427+
14251428 You can retrieve a single or all command level override(s), see`{prefix}help permissions get`.
14261429 """
1430+ if command_name == "bulk" :
1431+ return await self ._bulk_override_flow (ctx )
1432+
1433+ if level_name is None :
1434+ raise commands .MissingRequiredArgument (DummyParam ("level_name" ))
14271435
14281436 command = self .bot .get_command (command_name )
14291437 if command is None :
@@ -1458,6 +1466,126 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
14581466 )
14591467 return await ctx .send (embed = embed )
14601468
1469+ async def _bulk_override_flow (self , ctx ):
1470+ await ctx .send (
1471+ "Please list the commands you want to override. "
1472+ "You can list multiple commands separated by spaces or newlines.\n "
1473+ "Example: `ban, kick, mod`."
1474+ )
1475+
1476+ try :
1477+ msg = await self .bot .wait_for (
1478+ "message" ,
1479+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1480+ timeout = 120.0 ,
1481+ )
1482+ except asyncio .TimeoutError :
1483+ return await ctx .send ("Timed out." )
1484+
1485+ raw_commands = msg .content .replace ("," , " " ).replace ("\n " , " " ).split (" " )
1486+ # Filter empty strings from split
1487+ raw_commands = [c for c in raw_commands if c .strip ()]
1488+
1489+ found_commands = []
1490+ invalid_commands = []
1491+
1492+ for cmd_name in raw_commands :
1493+ cmd = self .bot .get_command (cmd_name )
1494+ if cmd :
1495+ found_commands .append (cmd )
1496+ else :
1497+ invalid_commands .append (cmd_name )
1498+
1499+ if invalid_commands :
1500+ embed = discord .Embed (
1501+ title = "Invalid Commands Found" ,
1502+ description = f"The following commands were not found:\n `{ ', ' .join (invalid_commands )} `\n \n "
1503+ "Do you want to continue with the valid commands? (y/n)" ,
1504+ color = self .bot .error_color ,
1505+ )
1506+ await ctx .send (embed = embed )
1507+ try :
1508+ msg = await self .bot .wait_for (
1509+ "message" ,
1510+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1511+ timeout = 60.0 ,
1512+ )
1513+ if msg .content .lower () not in ("y" , "yes" ):
1514+ return await ctx .send ("Aborted." )
1515+ except asyncio .TimeoutError :
1516+ return await ctx .send ("Timed out." )
1517+
1518+ if not found_commands :
1519+ return await ctx .send ("No valid commands provided. Aborting." )
1520+
1521+ # Expand subcommands
1522+ final_commands = set ()
1523+
1524+ def add_command_recursive (cmd ):
1525+ final_commands .add (cmd )
1526+ if hasattr (cmd , "commands" ):
1527+ for sub in cmd .commands :
1528+ add_command_recursive (sub )
1529+
1530+ for cmd in found_commands :
1531+ add_command_recursive (cmd )
1532+
1533+ await ctx .send (
1534+ f"Found { len (final_commands )} commands (including subcommands).\n "
1535+ "What permission level should these commands be set to? (e.g. `Owner`, `Admin`, `Moderator`, `Supporter`, `User`)"
1536+ )
1537+
1538+ try :
1539+ msg = await self .bot .wait_for (
1540+ "message" ,
1541+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel ,
1542+ timeout = 60.0 ,
1543+ )
1544+ except asyncio .TimeoutError :
1545+ return await ctx .send ("Timed out." )
1546+
1547+ level_name = msg .content
1548+ level = self ._parse_level (level_name )
1549+ if level == PermissionLevel .INVALID :
1550+ return await ctx .send (f"Invalid permission level: `{ level_name } `. Aborting." )
1551+
1552+ # Confirmation
1553+ command_list_str = ", " .join (f"`{ c .qualified_name } `" for c in sorted (final_commands , key = lambda x : x .qualified_name ))
1554+
1555+ # Truncate if too long for embed description
1556+ if len (command_list_str ) > 2000 :
1557+ command_list_str = command_list_str [:1997 ] + "..."
1558+
1559+ embed = discord .Embed (
1560+ title = "Confirm Bulk Override" ,
1561+ description = f"**Level:** { level .name } \n \n **Commands:**\n { command_list_str } \n \n "
1562+ "Type `confirm` to apply these changes or `cancel` to abort." ,
1563+ color = self .bot .main_color ,
1564+ )
1565+ await ctx .send (embed = embed )
1566+
1567+ try :
1568+ msg = await self .bot .wait_for (
1569+ "message" ,
1570+ check = lambda m : m .author == ctx .author and m .channel == ctx .channel and m .content .lower () in ("confirm" , "cancel" ),
1571+ timeout = 30.0 ,
1572+ )
1573+ except asyncio .TimeoutError :
1574+ return await ctx .send ("Timed out. No changes applied." )
1575+
1576+ if msg .content .lower () == "cancel" :
1577+ return await ctx .send ("Aborted." )
1578+
1579+ # Apply changes
1580+ count = 0
1581+ for cmd in final_commands :
1582+ self .bot .config ["override_command_level" ][cmd .qualified_name ] = level .name
1583+ count += 1
1584+
1585+ await self .bot .config .update ()
1586+
1587+ await ctx .send (f"Successfully updated permissions for { count } commands." )
1588+
14611589 @permissions .command (name = "add" , usage = "[command/level] [name] [user/role]" )
14621590 @checks .has_permissions (PermissionLevel .OWNER )
14631591 async def permissions_add (
0 commit comments