Skip to content
Merged
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 200 additions & 1 deletion cogs/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ def _parse_level(name):

@permissions.command(name="override")
@checks.has_permissions(PermissionLevel.OWNER)
async def permissions_override(self, ctx, command_name: str.lower, *, level_name: str):
async def permissions_override(self, ctx, command_name: str.lower, *, level_name: str = None):
"""
Change a permission level for a specific command.

Expand All @@ -1396,8 +1396,16 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
- `{prefix}perms remove override reply`
- `{prefix}perms remove override plugin enabled`

You can also override multiple commands at once using:
- `{prefix}perms override bulk`

You can retrieve a single or all command level override(s), see`{prefix}help permissions get`.
Comment thread
sebkuip marked this conversation as resolved.
"""
if command_name == "bulk":
Comment thread
sebkuip marked this conversation as resolved.
return await self._bulk_override_flow(ctx)

if level_name is None:
raise commands.MissingRequiredArgument(DummyParam("level_name"))

command = self.bot.get_command(command_name)
if command is None:
Expand Down Expand Up @@ -1432,6 +1440,197 @@ async def permissions_override(self, ctx, command_name: str.lower, *, level_name
)
return await ctx.send(embed=embed)

async def _bulk_override_flow(self, ctx):
message = None
embed = discord.Embed(
title="Bulk Override",
description=(
"Please list the commands you want to override. "
Comment thread
lorenzo132 marked this conversation as resolved.
"You can list multiple commands separated by spaces or newlines.\n"
Comment thread
sebkuip marked this conversation as resolved.
"Example: `reply, block, unblock`.\n"
),
color=self.bot.main_color,
)
await ctx.send(embed=embed)

try:
msg = await self.bot.wait_for(
"message",
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=120.0,
)

except asyncio.TimeoutError:
return await ctx.send(
embed=discord.Embed(title="Error", description="Timed out.", color=self.bot.error_color)
)

raw_commands = msg.content.replace(",", " ").replace("\n", " ").split(" ")
# Filter empty strings from split
raw_commands = [c for c in raw_commands if c.strip()]
Comment thread
sebkuip marked this conversation as resolved.

# Strip prefix from commands if present
prefixes = [self.bot.prefix, f"<@{self.bot.user.id}>", f"<@!{self.bot.user.id}>"]
if self.bot.prefix:
for i, cmd in enumerate(raw_commands):
for p in prefixes:
if cmd.startswith(p):
raw_commands[i] = cmd[len(p) :]
break
Comment thread
sebkuip marked this conversation as resolved.

# Filter empty strings again after stripping prefixes
raw_commands = [c for c in raw_commands if c.strip()]
Comment thread
sebkuip marked this conversation as resolved.

found_commands = []
invalid_commands = []

for cmd_name in raw_commands:
cmd = self.bot.get_command(cmd_name)
Comment thread
lorenzo132 marked this conversation as resolved.
if cmd:
Comment thread
lorenzo132 marked this conversation as resolved.
found_commands.append(cmd)
else:
invalid_commands.append(cmd_name)

Comment thread
lorenzo132 marked this conversation as resolved.
if invalid_commands:
description = f"The following commands were not found:\n`{', '.join(invalid_commands)}`\n\n"
if found_commands:
found_list = ", ".join(c.qualified_name for c in found_commands)
found_list = utils.return_or_truncate(found_list, 1000)
description += f"The following commands **were** found:\n`{found_list}`\n\n"

description += "Do you want to continue with the valid commands?"

embed = discord.Embed(
title="Invalid Commands Found",
description=description,
color=self.bot.error_color,
)
view = discord.ui.View()
Comment thread
sebkuip marked this conversation as resolved.
Comment thread
sebkuip marked this conversation as resolved.
view.add_item(utils.AcceptButton(custom_id="continue", emoji="✅"))
view.add_item(utils.DenyButton(custom_id="abort", emoji="❌"))

message = await ctx.send(embed=embed, view=view)
await view.wait()

if not view.value:
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated
return await message.edit(
embed=discord.Embed(
title="Operation Aborted",
description="No changes have been applied.",
color=self.bot.error_color,
),
view=None,
)

if not found_commands:
return await ctx.send(
embed=discord.Embed(
title="Error",
description="No valid commands provided. Aborting.",
color=self.bot.error_color,
)
)

# Expand subcommands
final_commands = set()

def add_command_recursive(cmd):
final_commands.add(cmd)
if hasattr(cmd, "commands"):
for sub in cmd.commands:
add_command_recursive(sub)

for cmd in found_commands:
add_command_recursive(cmd)

embed = discord.Embed(
title="Select Permission Level",
description=(
f"Found {len(final_commands)} commands (including subcommands).\n"
"What permission level should these commands be set to?"
),
color=self.bot.main_color,
)

class LevelSelect(discord.ui.Select):
def __init__(self):
options = [
discord.SelectOption(label="Owner", value="OWNER"),
discord.SelectOption(label="Administrator", value="ADMINISTRATOR"),
discord.SelectOption(label="Moderator", value="MODERATOR"),
discord.SelectOption(label="Supporter", value="SUPPORTER"),
discord.SelectOption(label="Regular", value="REGULAR"),
]
super().__init__(placeholder="Select permission level...", options=options)

async def callback(self, interaction: discord.Interaction):
self.view.value = self.values[0]
self.view.stop()
await interaction.response.defer()
Comment thread
sebkuip marked this conversation as resolved.

view = discord.ui.View()
Comment thread
sebkuip marked this conversation as resolved.
Comment thread
sebkuip marked this conversation as resolved.
view.add_item(LevelSelect())

if message:
await message.edit(embed=embed, view=view)
else:
message = await ctx.send(embed=embed, view=view)
await view.wait()

if view.value is None:
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated
return await message.edit(
embed=discord.Embed(title="Error", description="Timed out.", color=self.bot.error_color),
view=None,
)

level_name = view.value
level = self._parse_level(level_name)

# Confirmation
command_list_str = ", ".join(
f"`{c.qualified_name}`" for c in sorted(final_commands, key=lambda x: x.qualified_name)
)

command_list_str = utils.return_or_truncate(command_list_str, 2048)

embed = discord.Embed(
Comment thread
lorenzo132 marked this conversation as resolved.
title="Confirm Bulk Override",
description=f"**Level:** {level.name}\n\n**Commands:**\n{command_list_str}",
color=self.bot.main_color,
)

view = discord.ui.View()
Comment thread
sebkuip marked this conversation as resolved.
Comment thread
sebkuip marked this conversation as resolved.
view.add_item(utils.AcceptButton(custom_id="confirm", emoji="✅"))
view.add_item(utils.DenyButton(custom_id="cancel", emoji="❌"))

await message.edit(embed=embed, view=view)
await view.wait()

if not view.value:
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated
return await message.edit(
embed=discord.Embed(
title="Operation Aborted",
description="No changes have been applied.",
color=self.bot.error_color,
),
view=None,
)

# Apply changes
for cmd in final_commands:
self.bot.config["override_command_level"][cmd.qualified_name] = level.name

Comment thread
sebkuip marked this conversation as resolved.
await self.bot.config.update()
Comment thread
lorenzo132 marked this conversation as resolved.

await message.edit(
embed=discord.Embed(
title="Success",
description=f"Successfully updated permissions for {len(final_commands)} commands.",
color=self.bot.main_color,
),
view=None,
)

@permissions.command(name="add", usage="[command/level] [name] [user/role]")
@checks.has_permissions(PermissionLevel.OWNER)
async def permissions_add(
Expand Down
Loading