feat: Implement slash commands for role selection management and enhance user interaction
This commit is contained in:
parent
a3eacbcfe0
commit
f1364be0e5
@ -1,5 +1,6 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import app_commands # Added for slash commands
|
||||
from discord.ui import View, Select, select, Modal, TextInput
|
||||
import json
|
||||
import os
|
||||
@ -313,6 +314,9 @@ class RoleSelectorView(View):
|
||||
if error_messages: await interaction.followup.send("\n".join(error_messages), ephemeral=True)
|
||||
|
||||
class RoleSelectorCog(commands.Cog):
|
||||
roleselect_group = app_commands.Group(name="roleselect", description="Manage role selection categories and selectors.")
|
||||
rolepreset_group = app_commands.Group(name="rolepreset", description="Manage global role category presets.")
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.register_all_persistent_views())
|
||||
@ -356,40 +360,49 @@ class RoleSelectorCog(commands.Cog):
|
||||
return config
|
||||
return None
|
||||
|
||||
@commands.group(name="roleselect", invoke_without_command=True)
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect(self, ctx: commands.Context):
|
||||
await ctx.send_help(ctx.command)
|
||||
@roleselect_group.command(name="addcategory", description="Adds a new role category for selection.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(
|
||||
name="The name for the new category.",
|
||||
description="A description for this role category.",
|
||||
max_selectable="Maximum number of roles a user can select from this category (default: 1).",
|
||||
preset_id="Optional ID of a global preset to base this category on."
|
||||
)
|
||||
async def roleselect_addcategory(self, interaction: discord.Interaction, name: str, description: str, max_selectable: Optional[int] = 1, preset_id: Optional[str] = None):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
guild_id_str = str(interaction.guild_id)
|
||||
# Ensure max_selectable has a valid default if None is passed by Discord for optional int
|
||||
current_max_selectable = max_selectable if max_selectable is not None else 1
|
||||
|
||||
@roleselect.command(name="addcategory")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_addcategory(self, ctx: commands.Context, name: str, description: str, max_selectable: int = 1, preset_id: Optional[str] = None):
|
||||
guild_id_str = str(ctx.guild.id)
|
||||
if self._get_guild_category_config(ctx.guild.id, name) and not preset_id: # Allow adding preset even if name conflicts, preset name will be used
|
||||
await ctx.send(f"A custom role category named '{name}' already exists.")
|
||||
|
||||
if self._get_guild_category_config(interaction.guild_id, name) and not preset_id: # Allow adding preset even if name conflicts, preset name will be used
|
||||
await interaction.response.send_message(f"A custom role category named '{name}' already exists.", ephemeral=True)
|
||||
return
|
||||
|
||||
roles_to_add: List[GuildRole] = []
|
||||
is_preset_based = False
|
||||
final_name = name
|
||||
final_description = description
|
||||
final_max_selectable = max_selectable
|
||||
final_max_selectable = current_max_selectable
|
||||
|
||||
if preset_id:
|
||||
preset = db.get_role_category_preset(preset_id)
|
||||
if not preset:
|
||||
await ctx.send(f"Preset with ID '{preset_id}' not found.")
|
||||
await interaction.response.send_message(f"Preset with ID '{preset_id}' not found.", ephemeral=True)
|
||||
return
|
||||
|
||||
final_name = preset.name # Use preset's name
|
||||
if self._get_guild_category_config(ctx.guild.id, final_name): # Check if preset name already exists
|
||||
await ctx.send(f"A category based on preset '{final_name}' already exists.")
|
||||
if self._get_guild_category_config(interaction.guild_id, final_name): # Check if preset name already exists
|
||||
await interaction.response.send_message(f"A category based on preset '{final_name}' already exists.", ephemeral=True)
|
||||
return
|
||||
|
||||
for preset_role_option in preset.roles:
|
||||
role_in_guild = ctx.guild.get_role(int(preset_role_option.role_id))
|
||||
role_in_guild = interaction.guild.get_role(int(preset_role_option.role_id))
|
||||
if not role_in_guild:
|
||||
await ctx.send(f"Warning: Role '{preset_role_option.name}' (ID: {preset_role_option.role_id}) from preset not found in this server. Skipping.")
|
||||
# Using followup for potentially multiple messages
|
||||
await interaction.followup.send(f"Warning: Role '{preset_role_option.name}' (ID: {preset_role_option.role_id}) from preset not found in this server. Skipping.", ephemeral=True)
|
||||
continue
|
||||
roles_to_add.append(GuildRole(role_id=str(role_in_guild.id), name=role_in_guild.name, emoji=preset_role_option.emoji))
|
||||
|
||||
@ -411,98 +424,141 @@ class RoleSelectorCog(commands.Cog):
|
||||
if is_preset_based:
|
||||
msg += f" Based on preset '{preset_id}'."
|
||||
else:
|
||||
msg += f" Use `!roleselect addrole \"{final_name}\" <role> [emoji]` to add roles."
|
||||
msg += f" Then use `!roleselect post \"{final_name}\" #channel` to post."
|
||||
await ctx.send(msg)
|
||||
msg += f" Use `/roleselect addrole category_name_or_id:{final_name} role:<role> [emoji:<emoji>]` to add roles." # Updated help text
|
||||
msg += f" Then use `/roleselect post category_name_or_id:{final_name} channel:<#channel>` to post." # Updated help text
|
||||
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(msg, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(msg, ephemeral=True)
|
||||
|
||||
@roleselect.command(name="removecategory")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_removecategory(self, ctx: commands.Context, category_name_or_id: str):
|
||||
guild_id = ctx.guild.id
|
||||
@roleselect_group.command(name="removecategory", description="Removes a role category and its selector message.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(category_name_or_id="The name or ID of the category to remove.")
|
||||
async def roleselect_removecategory(self, interaction: discord.Interaction, category_name_or_id: str):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
|
||||
guild_id = interaction.guild_id
|
||||
config_to_remove = self._get_guild_category_config(guild_id, category_name_or_id)
|
||||
if not config_to_remove:
|
||||
await ctx.send(f"Role category '{category_name_or_id}' not found.")
|
||||
await interaction.response.send_message(f"Role category '{category_name_or_id}' not found.", ephemeral=True)
|
||||
return
|
||||
|
||||
deleted_message_feedback = ""
|
||||
if config_to_remove.message_id and config_to_remove.channel_id:
|
||||
try:
|
||||
channel = ctx.guild.get_channel(int(config_to_remove.channel_id))
|
||||
channel = interaction.guild.get_channel(int(config_to_remove.channel_id))
|
||||
if channel and isinstance(channel, discord.TextChannel):
|
||||
message = await channel.fetch_message(int(config_to_remove.message_id))
|
||||
await message.delete()
|
||||
await ctx.send(f"Deleted selector message for '{config_to_remove.name}'.")
|
||||
deleted_message_feedback = f" Deleted selector message for '{config_to_remove.name}'."
|
||||
except Exception as e:
|
||||
await ctx.send(f"Could not delete selector message: {e}")
|
||||
deleted_message_feedback = f" Could not delete selector message: {e}."
|
||||
|
||||
db.delete_guild_role_category_config(str(guild_id), config_to_remove.category_id)
|
||||
await ctx.send(f"Role category '{config_to_remove.name}' removed.")
|
||||
response_message = f"Role category '{config_to_remove.name}' removed.{deleted_message_feedback}"
|
||||
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(response_message, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(response_message, ephemeral=True)
|
||||
|
||||
@roleselect.command(name="listcategories")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_listcategories(self, ctx: commands.Context):
|
||||
guild_id_str = str(ctx.guild.id)
|
||||
@roleselect_group.command(name="listcategories", description="Lists all configured role selection categories.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
async def roleselect_listcategories(self, interaction: discord.Interaction):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
guild_id_str = str(interaction.guild_id)
|
||||
configs = db.get_guild_role_category_configs(guild_id_str)
|
||||
if not configs:
|
||||
await ctx.send("No role selection categories configured.")
|
||||
await interaction.response.send_message("No role selection categories configured.", ephemeral=True)
|
||||
return
|
||||
embed = discord.Embed(title="Configured Role Selection Categories", color=discord.Color.blue())
|
||||
for config in configs:
|
||||
roles_str = ", ".join([r.name for r in config.roles[:5]]) + ("..." if len(config.roles) > 5 else "")
|
||||
embed.add_field(name=f"{config.name} (ID: `{config.category_id}`)", value=f"Desc: {config.description}\nMax: {config.max_selectable}\nRoles: {roles_str or 'None'}\nPreset: {config.preset_id or 'No'}", inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
await interaction.response.send_message(embed=embed, ephemeral=False) # Make it visible
|
||||
|
||||
@roleselect.command(name="listpresets")
|
||||
@commands.is_owner() # Presets are global, so owner only
|
||||
async def roleselect_listpresets(self, ctx: commands.Context):
|
||||
@roleselect_group.command(name="listpresets", description="Lists all available global role category presets.")
|
||||
@app_commands.checks.is_owner() # Presets are global, so owner only
|
||||
async def roleselect_listpresets(self, interaction: discord.Interaction):
|
||||
presets = db.get_all_role_category_presets()
|
||||
if not presets:
|
||||
await ctx.send("No global presets available.")
|
||||
await interaction.response.send_message("No global presets available.", ephemeral=True)
|
||||
return
|
||||
embed = discord.Embed(title="Available Role Category Presets", color=discord.Color.green())
|
||||
for preset in sorted(presets, key=lambda p: p.display_order):
|
||||
roles_str = ", ".join([f"{r.name} ({r.role_id})" for r in preset.roles[:3]]) + ("..." if len(preset.roles) > 3 else "")
|
||||
embed.add_field(name=f"{preset.name} (ID: `{preset.id}`)", value=f"Desc: {preset.description}\nMax: {preset.max_selectable}\nRoles: {roles_str or 'None'}", inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
await interaction.response.send_message(embed=embed, ephemeral=False) # Make it visible
|
||||
|
||||
@roleselect.command(name="addrole")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_addrole(self, ctx: commands.Context, category_name_or_id: str, role: discord.Role, emoji: Optional[str] = None):
|
||||
guild_id = ctx.guild.id
|
||||
@roleselect_group.command(name="addrole", description="Adds a role to a specified category.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(
|
||||
category_name_or_id="The name or ID of the category to add the role to.",
|
||||
role="The role to add.",
|
||||
emoji="Optional emoji to display next to the role in the selector."
|
||||
)
|
||||
async def roleselect_addrole(self, interaction: discord.Interaction, category_name_or_id: str, role: discord.Role, emoji: Optional[str] = None):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
guild_id = interaction.guild_id
|
||||
config = self._get_guild_category_config(guild_id, category_name_or_id)
|
||||
if not config:
|
||||
await ctx.send(f"Category '{category_name_or_id}' not found.")
|
||||
await interaction.response.send_message(f"Category '{category_name_or_id}' not found.", ephemeral=True)
|
||||
return
|
||||
if config.is_preset:
|
||||
await ctx.send(f"Category '{config.name}' uses a preset. Roles are managed via the preset definition.")
|
||||
await interaction.response.send_message(f"Category '{config.name}' uses a preset. Roles are managed via the preset definition.", ephemeral=True)
|
||||
return
|
||||
if any(r.role_id == str(role.id) for r in config.roles):
|
||||
await ctx.send(f"Role '{role.name}' is already in '{config.name}'.")
|
||||
await interaction.response.send_message(f"Role '{role.name}' is already in '{config.name}'.", ephemeral=True)
|
||||
return
|
||||
config.roles.append(GuildRole(role_id=str(role.id), name=role.name, emoji=emoji))
|
||||
db.save_guild_role_category_config(config)
|
||||
await ctx.send(f"Role '{role.name}' added to '{config.name}'.")
|
||||
await interaction.response.send_message(f"Role '{role.name}' added to '{config.name}'.", ephemeral=True)
|
||||
|
||||
@roleselect.command(name="removerole")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_removerole(self, ctx: commands.Context, category_name_or_id: str, role: discord.Role):
|
||||
guild_id = ctx.guild.id
|
||||
@roleselect_group.command(name="removerole", description="Removes a role from a specified category.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(
|
||||
category_name_or_id="The name or ID of the category to remove the role from.",
|
||||
role="The role to remove."
|
||||
)
|
||||
async def roleselect_removerole(self, interaction: discord.Interaction, category_name_or_id: str, role: discord.Role):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
guild_id = interaction.guild_id
|
||||
config = self._get_guild_category_config(guild_id, category_name_or_id)
|
||||
if not config:
|
||||
await ctx.send(f"Category '{category_name_or_id}' not found.")
|
||||
await interaction.response.send_message(f"Category '{category_name_or_id}' not found.", ephemeral=True)
|
||||
return
|
||||
if config.is_preset:
|
||||
await ctx.send(f"Category '{config.name}' uses a preset. Roles are managed via the preset definition.")
|
||||
await interaction.response.send_message(f"Category '{config.name}' uses a preset. Roles are managed via the preset definition.", ephemeral=True)
|
||||
return
|
||||
initial_len = len(config.roles)
|
||||
config.roles = [r for r in config.roles if r.role_id != str(role.id)]
|
||||
if len(config.roles) < initial_len:
|
||||
db.save_guild_role_category_config(config)
|
||||
await ctx.send(f"Role '{role.name}' removed from '{config.name}'.")
|
||||
await interaction.response.send_message(f"Role '{role.name}' removed from '{config.name}'.", ephemeral=True)
|
||||
else:
|
||||
await ctx.send(f"Role '{role.name}' not found in '{config.name}'.")
|
||||
await interaction.response.send_message(f"Role '{role.name}' not found in '{config.name}'.", ephemeral=True)
|
||||
|
||||
@roleselect.command(name="setcolorui")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_setcolorui(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
|
||||
target_channel = channel or ctx.channel
|
||||
@roleselect_group.command(name="setcolorui", description="Posts the UI for users to set their custom role color.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(channel="The channel to post the custom color UI in (defaults to current channel).")
|
||||
async def roleselect_setcolorui(self, interaction: discord.Interaction, channel: Optional[discord.TextChannel] = None):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
target_channel = channel or interaction.channel
|
||||
if not isinstance(target_channel, discord.TextChannel): # Ensure it's a text channel
|
||||
await interaction.response.send_message("Invalid channel specified for custom color UI.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
title="🎨 Custom Role Color",
|
||||
description="Click the button below to set a custom color for your name in this server!",
|
||||
@ -511,144 +567,192 @@ class RoleSelectorCog(commands.Cog):
|
||||
view = CustomColorButtonView()
|
||||
try:
|
||||
await target_channel.send(embed=embed, view=view)
|
||||
if target_channel != ctx.channel:
|
||||
await ctx.send(f"Custom color button posted in {target_channel.mention}.", ephemeral=True)
|
||||
if target_channel != interaction.channel:
|
||||
await interaction.response.send_message(f"Custom color button posted in {target_channel.mention}.", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message("Custom color button posted.", ephemeral=True)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(f"I don't have permissions to send messages in {target_channel.mention}.", ephemeral=True)
|
||||
await interaction.response.send_message(f"I don't have permissions to send messages in {target_channel.mention}.", ephemeral=True)
|
||||
except Exception as e:
|
||||
await ctx.send(f"Error posting custom color button: {e}", ephemeral=True)
|
||||
await interaction.response.send_message(f"Error posting custom color button: {e}", ephemeral=True)
|
||||
|
||||
@roleselect.command(name="post")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def roleselect_post(self, ctx: commands.Context, category_name_or_id: str, channel: Optional[discord.TextChannel] = None):
|
||||
target_channel = channel or ctx.channel
|
||||
guild = ctx.guild
|
||||
@roleselect_group.command(name="post", description="Posts or updates the role selector message for a category.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(
|
||||
category_name_or_id="The name or ID of the category to post.",
|
||||
channel="The channel to post the selector in (defaults to current channel)."
|
||||
)
|
||||
async def roleselect_post(self, interaction: discord.Interaction, category_name_or_id: str, channel: Optional[discord.TextChannel] = None):
|
||||
if not interaction.guild:
|
||||
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
|
||||
return
|
||||
|
||||
target_channel = channel or interaction.channel
|
||||
if not isinstance(target_channel, discord.TextChannel): # Ensure it's a text channel
|
||||
await interaction.response.send_message("Invalid channel specified for posting.", ephemeral=True)
|
||||
return
|
||||
|
||||
guild = interaction.guild
|
||||
config = self._get_guild_category_config(guild.id, category_name_or_id)
|
||||
|
||||
if not config:
|
||||
await ctx.send(f"Category '{category_name_or_id}' not found.")
|
||||
await interaction.response.send_message(f"Category '{category_name_or_id}' not found.", ephemeral=True)
|
||||
return
|
||||
if not config.roles:
|
||||
await ctx.send(f"Category '{config.name}' has no roles. Add roles first.")
|
||||
await interaction.response.send_message(f"Category '{config.name}' has no roles. Add roles first using `/roleselect addrole`.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(title=f"✨ {config.name} Roles ✨", description=config.description, color=discord.Color.blue())
|
||||
view = RoleSelectorView(guild.id, config, self.bot)
|
||||
|
||||
# Defer the response as message fetching/editing can take time
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
if config.message_id and config.channel_id:
|
||||
try:
|
||||
original_channel = guild.get_channel(int(config.channel_id))
|
||||
if original_channel and isinstance(original_channel, discord.TextChannel):
|
||||
message_to_edit = await original_channel.fetch_message(int(config.message_id))
|
||||
await message_to_edit.edit(embed=embed, view=view)
|
||||
|
||||
if original_channel.id != target_channel.id: # Moved
|
||||
config.channel_id = str(target_channel.id)
|
||||
# Delete old message, post new one, then update config
|
||||
await message_to_edit.delete() # Delete the old message first
|
||||
new_msg = await target_channel.send(embed=embed, view=view)
|
||||
config.channel_id = str(target_channel.id)
|
||||
config.message_id = str(new_msg.id)
|
||||
await message_to_edit.delete()
|
||||
await ctx.send(f"Updated and moved selector for '{config.name}' to {target_channel.mention}.")
|
||||
else: # Just updated
|
||||
await ctx.send(f"Updated selector for '{config.name}' in {target_channel.mention}.")
|
||||
await interaction.followup.send(f"Updated and moved selector for '{config.name}' to {target_channel.mention}.", ephemeral=True)
|
||||
else: # Just updated in the same channel
|
||||
await interaction.followup.send(f"Updated selector for '{config.name}' in {target_channel.mention}.", ephemeral=True)
|
||||
db.save_guild_role_category_config(config)
|
||||
return
|
||||
except Exception as e: # NotFound, Forbidden, etc.
|
||||
await ctx.send(f"Couldn't update original message ({e}), posting new one.")
|
||||
config.message_id = None; config.channel_id = None # Clear old IDs
|
||||
await interaction.followup.send(f"Couldn't update original message ({e}), posting new one.", ephemeral=True)
|
||||
config.message_id = None
|
||||
config.channel_id = None # Clear old IDs as we are posting a new one
|
||||
try:
|
||||
msg = await target_channel.send(embed=embed, view=view)
|
||||
config.message_id = str(msg.id)
|
||||
config.channel_id = str(target_channel.id)
|
||||
db.save_guild_role_category_config(config)
|
||||
await ctx.send(f"Posted role selector for '{config.name}' in {target_channel.mention}.")
|
||||
await interaction.followup.send(f"Posted role selector for '{config.name}' in {target_channel.mention}.", ephemeral=True)
|
||||
except Exception as e:
|
||||
await ctx.send(f"Error posting role selector: {e}")
|
||||
await interaction.followup.send(f"Error posting role selector: {e}", ephemeral=True)
|
||||
|
||||
@commands.group(name="rolepreset", invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def rolepreset(self, ctx: commands.Context):
|
||||
await ctx.send_help(ctx.command)
|
||||
# Role Preset Commands (Owner Only)
|
||||
@rolepreset_group.command(name="add", description="Creates a new global role category preset.")
|
||||
@app_commands.checks.is_owner()
|
||||
@app_commands.describe(
|
||||
preset_id="A unique ID for this preset (e.g., 'color_roles', 'region_roles').",
|
||||
name="The display name for this preset.",
|
||||
description="A description for this preset.",
|
||||
max_selectable="Maximum roles a user can select from categories using this preset (default: 1).",
|
||||
display_order="Order in which this preset appears in lists (lower numbers first, default: 0)."
|
||||
)
|
||||
async def rolepreset_add(self, interaction: discord.Interaction, preset_id: str, name: str, description: str, max_selectable: Optional[int] = 1, display_order: Optional[int] = 0):
|
||||
current_max_selectable = max_selectable if max_selectable is not None else 1
|
||||
current_display_order = display_order if display_order is not None else 0
|
||||
|
||||
@rolepreset.command(name="add")
|
||||
@commands.is_owner()
|
||||
async def rolepreset_add(self, ctx: commands.Context, preset_id: str, name: str, description: str, max_selectable: int = 1, display_order: int = 0):
|
||||
if db.get_role_category_preset(preset_id):
|
||||
await ctx.send(f"Preset ID '{preset_id}' already exists.")
|
||||
await interaction.response.send_message(f"Preset ID '{preset_id}' already exists.", ephemeral=True)
|
||||
return
|
||||
new_preset = RoleCategoryPreset(id=preset_id, name=name, description=description, roles=[], max_selectable=max_selectable, display_order=display_order)
|
||||
new_preset = RoleCategoryPreset(id=preset_id, name=name, description=description, roles=[], max_selectable=current_max_selectable, display_order=current_display_order)
|
||||
db.save_role_category_preset(new_preset)
|
||||
await ctx.send(f"Preset '{name}' (ID: {preset_id}) created. Add roles with `!rolepreset addrole`.")
|
||||
await interaction.response.send_message(f"Preset '{name}' (ID: {preset_id}) created. Add roles with `/rolepreset addrole`.", ephemeral=True)
|
||||
|
||||
@rolepreset.command(name="remove")
|
||||
@commands.is_owner()
|
||||
async def rolepreset_remove(self, ctx: commands.Context, preset_id: str):
|
||||
@rolepreset_group.command(name="remove", description="Removes a global role category preset.")
|
||||
@app_commands.checks.is_owner()
|
||||
@app_commands.describe(preset_id="The ID of the preset to remove.")
|
||||
async def rolepreset_remove(self, interaction: discord.Interaction, preset_id: str):
|
||||
if not db.get_role_category_preset(preset_id):
|
||||
await ctx.send(f"Preset ID '{preset_id}' not found.")
|
||||
await interaction.response.send_message(f"Preset ID '{preset_id}' not found.", ephemeral=True)
|
||||
return
|
||||
db.delete_role_category_preset(preset_id)
|
||||
await ctx.send(f"Preset ID '{preset_id}' removed.")
|
||||
await interaction.response.send_message(f"Preset ID '{preset_id}' removed.", ephemeral=True)
|
||||
|
||||
@rolepreset.command(name="addrole")
|
||||
@commands.is_owner()
|
||||
async def rolepreset_addrole(self, ctx: commands.Context, preset_id: str, role_name_or_id: str, emoji: Optional[str] = None):
|
||||
@rolepreset_group.command(name="addrole", description="Adds a role (by ID or name) to a global preset.")
|
||||
@app_commands.checks.is_owner()
|
||||
@app_commands.describe(
|
||||
preset_id="The ID of the preset to add the role to.",
|
||||
role_name_or_id="The name or ID of the role to add. The first matching role found across all servers the bot is in will be used.",
|
||||
emoji="Optional emoji for the role in this preset."
|
||||
)
|
||||
async def rolepreset_addrole(self, interaction: discord.Interaction, preset_id: str, role_name_or_id: str, emoji: Optional[str] = None):
|
||||
preset = db.get_role_category_preset(preset_id)
|
||||
if not preset:
|
||||
await ctx.send(f"Preset ID '{preset_id}' not found.")
|
||||
await interaction.response.send_message(f"Preset ID '{preset_id}' not found.", ephemeral=True)
|
||||
return
|
||||
|
||||
target_role: Optional[discord.Role] = None
|
||||
role_display_name = role_name_or_id
|
||||
|
||||
# Attempt to find role by ID first, then by name across all guilds
|
||||
try:
|
||||
role_id_int = int(role_name_or_id)
|
||||
for guild in self.bot.guilds:
|
||||
if r := guild.get_role(role_id_int): target_role = r; role_display_name = r.name; break
|
||||
except ValueError:
|
||||
if r := guild.get_role(role_id_int):
|
||||
target_role = r
|
||||
role_display_name = r.name
|
||||
break
|
||||
except ValueError: # Not an ID, try by name
|
||||
for guild in self.bot.guilds:
|
||||
for r_obj in guild.roles:
|
||||
if r_obj.name.lower() == role_name_or_id.lower(): target_role = r_obj; role_display_name = r_obj.name; break
|
||||
if target_role: break
|
||||
if r_obj.name.lower() == role_name_or_id.lower():
|
||||
target_role = r_obj
|
||||
role_display_name = r_obj.name
|
||||
break
|
||||
if target_role:
|
||||
break
|
||||
|
||||
if not target_role:
|
||||
await ctx.send(f"Role '{role_name_or_id}' not found in any server.")
|
||||
await interaction.response.send_message(f"Role '{role_name_or_id}' not found in any server the bot is in.", ephemeral=True)
|
||||
return
|
||||
|
||||
if any(r.role_id == str(target_role.id) for r in preset.roles):
|
||||
await ctx.send(f"Role '{target_role.name}' already in preset '{preset.name}'.")
|
||||
await interaction.response.send_message(f"Role '{target_role.name}' (ID: {target_role.id}) is already in preset '{preset.name}'.", ephemeral=True)
|
||||
return
|
||||
|
||||
preset.roles.append(RoleOption(role_id=str(target_role.id), name=role_display_name, emoji=emoji))
|
||||
db.save_role_category_preset(preset)
|
||||
await ctx.send(f"Role '{role_display_name}' added to preset '{preset.name}'.")
|
||||
await interaction.response.send_message(f"Role '{role_display_name}' (ID: {target_role.id}) added to preset '{preset.name}'.", ephemeral=True)
|
||||
|
||||
@rolepreset.command(name="removerole")
|
||||
@commands.is_owner()
|
||||
async def rolepreset_removerole(self, ctx: commands.Context, preset_id: str, role_id_or_name: str):
|
||||
@rolepreset_group.command(name="removerole", description="Removes a role (by ID or name) from a global preset.")
|
||||
@app_commands.checks.is_owner()
|
||||
@app_commands.describe(
|
||||
preset_id="The ID of the preset to remove the role from.",
|
||||
role_id_or_name="The ID or name of the role to remove from the preset."
|
||||
)
|
||||
async def rolepreset_removerole(self, interaction: discord.Interaction, preset_id: str, role_id_or_name: str):
|
||||
preset = db.get_role_category_preset(preset_id)
|
||||
if not preset:
|
||||
await ctx.send(f"Preset ID '{preset_id}' not found.")
|
||||
await interaction.response.send_message(f"Preset ID '{preset_id}' not found.", ephemeral=True)
|
||||
return
|
||||
|
||||
initial_len = len(preset.roles)
|
||||
preset.roles = [r for r in preset.roles if not (r.role_id == role_id_or_name or r.name.lower() == role_id_or_name.lower())]
|
||||
# Try to match by ID first, then by name if ID doesn't match or isn't an int
|
||||
role_to_remove_id_str: Optional[str] = None
|
||||
try:
|
||||
role_id_int = int(role_id_or_name)
|
||||
role_to_remove_id_str = str(role_id_int)
|
||||
except ValueError:
|
||||
pass # role_id_or_name is not an integer, will try to match by name
|
||||
|
||||
if role_to_remove_id_str:
|
||||
preset.roles = [r for r in preset.roles if r.role_id != role_to_remove_id_str]
|
||||
else: # Match by name (case-insensitive)
|
||||
preset.roles = [r for r in preset.roles if r.name.lower() != role_id_or_name.lower()]
|
||||
|
||||
if len(preset.roles) < initial_len:
|
||||
db.save_role_category_preset(preset)
|
||||
await ctx.send(f"Role matching '{role_id_or_name}' removed from preset '{preset.name}'.")
|
||||
await interaction.response.send_message(f"Role matching '{role_id_or_name}' removed from preset '{preset.name}'.", ephemeral=True)
|
||||
else:
|
||||
await ctx.send(f"Role matching '{role_id_or_name}' not found in preset.")
|
||||
await interaction.response.send_message(f"Role matching '{role_id_or_name}' not found in preset '{preset.name}'.", ephemeral=True)
|
||||
|
||||
@commands.command(name="create_role_embeds", hidden=True)
|
||||
@commands.is_owner()
|
||||
async def create_role_embeds_old(self, ctx: commands.Context):
|
||||
await ctx.send("This command is deprecated. Use `!roleselect post <category_name> #channel` instead.")
|
||||
|
||||
@commands.command(name="update_role_selectors", hidden=True)
|
||||
@commands.is_owner()
|
||||
async def update_role_selectors_old(self, ctx: commands.Context):
|
||||
await ctx.send("This command is deprecated. Use `!roleselect post <category_name> #channel` to update an existing selector message.")
|
||||
|
||||
@commands.command(name="recreate_role_embeds", hidden=True)
|
||||
@commands.is_owner()
|
||||
async def recreate_role_embeds_old(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
|
||||
# This command's old logic is complex and relies on EXPECTED_ROLES and CATEGORY_DETAILS.
|
||||
# Re-implementing it fully with the new DB structure is out of scope for this refactor's immediate goals.
|
||||
# Admins should use `roleselect removecategory` and `roleselect post` for similar functionality.
|
||||
await ctx.send("This command is deprecated and its full functionality is not replicated. "
|
||||
"To recreate selectors, please use `!roleselect removecategory` to remove the old one "
|
||||
"(this will attempt to delete the message), then `!roleselect post` to create a new one.")
|
||||
# Deprecated commands are removed as they are not slash commands and functionality is covered
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(RoleSelectorCog(bot))
|
||||
print("RoleSelectorCog loaded. Persistent views will be registered once the bot is ready.")
|
||||
cog = RoleSelectorCog(bot)
|
||||
await bot.add_cog(cog)
|
||||
await bot.tree.sync() # Sync slash commands
|
||||
print("RoleSelectorCog loaded. Persistent views will be registered once the bot is ready. Slash commands synced.")
|
||||
|
Loading…
x
Reference in New Issue
Block a user