diff --git a/autoroom/autoroom.py b/autoroom/autoroom.py index e31ee8e..00705d2 100644 --- a/autoroom/autoroom.py +++ b/autoroom/autoroom.py @@ -16,6 +16,7 @@ from .c_autoroomset import AutoRoomSetCommands, channel_name_template from .pcx_lib import Perms, SettingDisplay from .pcx_template import Template from .waiting_room import WaitingRoom +from .control_panel import ControlPanel class CompositeMetaClass(type(commands.Cog), type(ABC)): @@ -117,6 +118,7 @@ class AutoRoom( self.config.register_channel(**self.default_channel_settings) self.template = Template() self.waiting_room = WaitingRoom(self) + self.control_panel = ControlPanel(self) self.bucket_autoroom_create = commands.CooldownMapping.from_cooldown( 2, 60, lambda member: member ) @@ -409,13 +411,13 @@ class AutoRoom( async def _process_autoroom_create( self, autoroom_source: discord.VoiceChannel, - autoroom_source_config: dict[str, Any], + asc: dict[str, Any], member: discord.Member, ) -> None: - """Create a voice channel for a member in an AutoRoom Source channel.""" + """Actually do channel creation for autoroom.""" # Check perms for guild, source, and dest guild = autoroom_source.guild - dest_category = guild.get_channel(autoroom_source_config["dest_category_id"]) + dest_category = guild.get_channel(asc["dest_category_id"]) if not isinstance(dest_category, discord.CategoryChannel): return required_check, optional_check, _ = self.check_perms_source_dest( @@ -452,7 +454,7 @@ class AutoRoom( voice_channel.name for voice_channel in dest_category.voice_channels ] new_channel_name = await self._generate_channel_name( - autoroom_source_config, member, taken_channel_names + asc, member, taken_channel_names ) # Generate overwrites @@ -478,20 +480,20 @@ class AutoRoom( perms.overwrite(target, permissions) if member_roles and target in member_roles: # If we have member roles and this target is one, apply AutoRoom type permissions - perms.update(target, autoroom_source_config["perms"]["access"]) + perms.update(target, asc["perms"]["access"]) # Update overwrites for default role to account for AutoRoom type if member_roles: - perms.update(guild.default_role, autoroom_source_config["perms"]["deny"]) + perms.update(guild.default_role, asc["perms"]["deny"]) else: - perms.update(guild.default_role, autoroom_source_config["perms"]["access"]) + perms.update(guild.default_role, asc["perms"]["access"]) # Bot overwrites perms.update(guild.me, self.perms_bot_dest) # AutoRoom Owner overwrites - if autoroom_source_config["room_type"] != "server": - perms.update(member, autoroom_source_config["perms"]["owner"]) + if asc["room_type"] != "server": + perms.update(member, asc["perms"]["owner"]) # Admin/moderator/bot overwrites # Add bot roles to be allowed @@ -504,83 +506,34 @@ class AutoRoom( additional_allowed_roles += await self.bot.get_admin_roles(guild) for role in additional_allowed_roles: # Add all the mod/admin roles, if required - perms.update(role, autoroom_source_config["perms"]["allow"]) + perms.update(role, asc["perms"]["allow"]) - # Create new AutoRoom + # Create the voice channel new_voice_channel = await guild.create_voice_channel( name=new_channel_name, category=dest_category, + bitrate=min( + int(guild.bitrate_limit), + int(autoroom_source.bitrate), + ), + user_limit=autoroom_source.user_limit, reason="AutoRoom: New AutoRoom needed.", overwrites=perms.overwrites if perms.overwrites else {}, - bitrate=min(autoroom_source.bitrate, int(guild.bitrate_limit)), - user_limit=autoroom_source.user_limit, ) - await self.config.channel(new_voice_channel).source_channel.set( - autoroom_source.id - ) - if autoroom_source_config["room_type"] != "server": - await self.config.channel(new_voice_channel).owner.set(member.id) - try: - await member.move_to( - new_voice_channel, reason="AutoRoom: Move user to new AutoRoom." - ) - except discord.HTTPException: - await self._process_autoroom_delete(new_voice_channel, member) - return - # Create optional legacy text channel - new_legacy_text_channel = None - if autoroom_source_config["legacy_text_channel"]: - # Sanity check on required permissions - for perm_name in self.perms_bot_dest_legacy_text: - if not getattr(dest_perms, perm_name): - return - # Generate overwrites - perms = Perms() - perms.update(guild.me, self.perms_bot_dest_legacy_text) - perms.update(guild.default_role, self.perms_legacy_text_deny) - if autoroom_source_config["room_type"] != "server": - perms.update(member, self.perms_autoroom_owner_legacy_text) - else: - perms.update(member, self.perms_legacy_text_allow) - # Admin/moderator overwrites - additional_allowed_roles_text = [] - if await self.config.guild(guild).mod_access(): - # Add mod roles to be allowed - additional_allowed_roles_text += await self.bot.get_mod_roles(guild) - if await self.config.guild(guild).admin_access(): - # Add admin roles to be allowed - additional_allowed_roles_text += await self.bot.get_admin_roles(guild) - for role in additional_allowed_roles_text: - # Add all the mod/admin roles, if required - perms.update(role, self.perms_legacy_text_allow) - # Create text channel - text_channel_topic = await self.template.render( - autoroom_source_config["text_channel_topic"], - self.get_template_data(member), - ) - new_legacy_text_channel = await guild.create_text_channel( - name=new_channel_name.replace("'s ", " "), - category=dest_category, - topic=text_channel_topic, - reason="AutoRoom: New legacy text channel needed.", - overwrites=perms.overwrites if perms.overwrites else {}, - ) - - await self.config.channel(new_voice_channel).associated_text_channel.set( - new_legacy_text_channel.id - ) + # Create control panel in the voice channel's text chat + await self.control_panel.create_control_panel(new_voice_channel) # Send text chat hint if enabled - if autoroom_source_config["text_channel_hint"]: + if asc["text_channel_hint"]: with suppress(Exception): hint = await self.template.render( - autoroom_source_config["text_channel_hint"], + asc["text_channel_hint"], self.get_template_data(member), ) if hint: - if new_legacy_text_channel: - await new_legacy_text_channel.send(hint) + if new_voice_channel: + await new_voice_channel.send(hint) else: await new_voice_channel.send(hint[:2000].strip()) @@ -1079,3 +1032,53 @@ class AutoRoom( # Update the bot role list to remove nonexistent roles await self.config.guild(guild).bot_access.set(bot_role_ids) return bot_roles + + async def _create_autoroom_legacy_text_channel( + self, + autoroom_source: discord.VoiceChannel, + autoroom_source_config: dict[str, Any], + member: discord.Member, + autoroom: discord.VoiceChannel, + ) -> discord.TextChannel | None: + """Create a legacy text channel for an AutoRoom.""" + # Sanity check on required permissions + for perm_name in self.perms_bot_dest_legacy_text: + if not getattr(autoroom_source.permissions_for(autoroom_source.guild.me), perm_name): + return None + + # Generate overwrites + perms = Perms() + perms.update(autoroom_source.guild.me, self.perms_bot_dest_legacy_text) + perms.update(autoroom_source.guild.default_role, self.perms_legacy_text_deny) + if autoroom_source_config["room_type"] != "server": + perms.update(member, self.perms_autoroom_owner_legacy_text) + else: + perms.update(member, self.perms_legacy_text_allow) + # Admin/moderator overwrites + additional_allowed_roles_text = [] + if await self.config.guild(autoroom_source.guild).mod_access(): + # Add mod roles to be allowed + additional_allowed_roles_text += await self.bot.get_mod_roles(autoroom_source.guild) + if await self.config.guild(autoroom_source.guild).admin_access(): + # Add admin roles to be allowed + additional_allowed_roles_text += await self.bot.get_admin_roles(autoroom_source.guild) + for role in additional_allowed_roles_text: + # Add all the mod/admin roles, if required + perms.update(role, self.perms_legacy_text_allow) + # Create text channel + text_channel_topic = await self.template.render( + autoroom_source_config["text_channel_topic"], + self.get_template_data(member), + ) + text_channel = await autoroom_source.guild.create_text_channel( + name=autoroom.name.replace("'s ", " "), + category=autoroom.category, + topic=text_channel_topic, + reason="AutoRoom: New legacy text channel needed.", + overwrites=perms.overwrites if perms.overwrites else {}, + ) + + await self.config.channel(autoroom).associated_text_channel.set( + text_channel.id + ) + return text_channel diff --git a/autoroom/control_panel.py b/autoroom/control_panel.py new file mode 100644 index 0000000..5c4adc6 --- /dev/null +++ b/autoroom/control_panel.py @@ -0,0 +1,199 @@ +"""Control panel functionality for AutoRoom cog.""" + +from typing import Any, Optional + +import discord +from discord import ui +from redbot.core import Config +from redbot.core.bot import Red + +class ControlPanelView(ui.View): + """View for AutoRoom control panel buttons.""" + + def __init__(self, cog: Any): + super().__init__(timeout=None) + self.cog = cog + + @discord.ui.button(label="🔓 Public", style=discord.ButtonStyle.green, custom_id="autoroom_public") + async def public_button(self, interaction: discord.Interaction, button: discord.ui.Button): + """Make the AutoRoom public.""" + if not interaction.message: + return + + # Get the AutoRoom info + autoroom_info = await self.cog.get_autoroom_info(interaction.channel) + if not autoroom_info: + await interaction.response.send_message("This is not an AutoRoom.", ephemeral=True) + return + + # Check if the interaction user is the owner + if interaction.user.id != autoroom_info["owner"]: + await interaction.response.send_message("Only the AutoRoom owner can use these buttons.", ephemeral=True) + return + + # Make the AutoRoom public + await self.cog._process_allow_deny(interaction, "allow") + await interaction.response.send_message("AutoRoom is now public.", ephemeral=True) + + @discord.ui.button(label="🔒 Locked", style=discord.ButtonStyle.grey, custom_id="autoroom_locked") + async def locked_button(self, interaction: discord.Interaction, button: discord.ui.Button): + """Make the AutoRoom locked.""" + if not interaction.message: + return + + # Get the AutoRoom info + autoroom_info = await self.cog.get_autoroom_info(interaction.channel) + if not autoroom_info: + await interaction.response.send_message("This is not an AutoRoom.", ephemeral=True) + return + + # Check if the interaction user is the owner + if interaction.user.id != autoroom_info["owner"]: + await interaction.response.send_message("Only the AutoRoom owner can use these buttons.", ephemeral=True) + return + + # Make the AutoRoom locked + await self.cog._process_allow_deny(interaction, "lock") + await interaction.response.send_message("AutoRoom is now locked.", ephemeral=True) + + @discord.ui.button(label="🔐 Private", style=discord.ButtonStyle.red, custom_id="autoroom_private") + async def private_button(self, interaction: discord.Interaction, button: discord.ui.Button): + """Make the AutoRoom private.""" + if not interaction.message: + return + + # Get the AutoRoom info + autoroom_info = await self.cog.get_autoroom_info(interaction.channel) + if not autoroom_info: + await interaction.response.send_message("This is not an AutoRoom.", ephemeral=True) + return + + # Check if the interaction user is the owner + if interaction.user.id != autoroom_info["owner"]: + await interaction.response.send_message("Only the AutoRoom owner can use these buttons.", ephemeral=True) + return + + # Make the AutoRoom private + await self.cog._process_allow_deny(interaction, "deny") + await interaction.response.send_message("AutoRoom is now private.", ephemeral=True) + + @discord.ui.button(label="👥 Add User", style=discord.ButtonStyle.blurple, custom_id="autoroom_add_user") + async def add_user_button(self, interaction: discord.Interaction, button: discord.ui.Button): + """Add a user to the AutoRoom.""" + if not interaction.message: + return + + # Get the AutoRoom info + autoroom_info = await self.cog.get_autoroom_info(interaction.channel) + if not autoroom_info: + await interaction.response.send_message("This is not an AutoRoom.", ephemeral=True) + return + + # Check if the interaction user is the owner + if interaction.user.id != autoroom_info["owner"]: + await interaction.response.send_message("Only the AutoRoom owner can use these buttons.", ephemeral=True) + return + + # Create a modal for user selection + modal = UserSelectModal(self.cog, "allow") + await interaction.response.send_modal(modal) + + @discord.ui.button(label="🚫 Remove User", style=discord.ButtonStyle.danger, custom_id="autoroom_remove_user") + async def remove_user_button(self, interaction: discord.Interaction, button: discord.ui.Button): + """Remove a user from the AutoRoom.""" + if not interaction.message: + return + + # Get the AutoRoom info + autoroom_info = await self.cog.get_autoroom_info(interaction.channel) + if not autoroom_info: + await interaction.response.send_message("This is not an AutoRoom.", ephemeral=True) + return + + # Check if the interaction user is the owner + if interaction.user.id != autoroom_info["owner"]: + await interaction.response.send_message("Only the AutoRoom owner can use these buttons.", ephemeral=True) + return + + # Create a modal for user selection + modal = UserSelectModal(self.cog, "deny") + await interaction.response.send_modal(modal) + +class UserSelectModal(ui.Modal, title="Select User"): + """Modal for selecting a user to add/remove.""" + + def __init__(self, cog: Any, action: str): + super().__init__() + self.cog = cog + self.action = action + self.user_id = ui.TextInput( + label="User ID or @mention", + placeholder="Enter user ID or @mention", + required=True + ) + self.add_item(self.user_id) + + async def on_submit(self, interaction: discord.Interaction): + """Handle the modal submission.""" + user_input = self.user_id.value.strip() + + # Try to get user from mention + if user_input.startswith("<@") and user_input.endswith(">"): + user_id = int(user_input[2:-1]) + else: + try: + user_id = int(user_input) + except ValueError: + await interaction.response.send_message("Invalid user ID or mention.", ephemeral=True) + return + + user = interaction.guild.get_member(user_id) + if not user: + await interaction.response.send_message("User not found in this server.", ephemeral=True) + return + + # Process the allow/deny action + await self.cog._process_allow_deny(interaction, self.action, member_or_role=user) + await interaction.response.send_message(f"User {user.mention} has been {'allowed' if self.action == 'allow' else 'denied'} access.", ephemeral=True) + +class ControlPanel: + """Handles control panel functionality.""" + + def __init__(self, cog: Any): + self.cog = cog + self.bot: Red = cog.bot + self.config: Config = cog.config + + async def create_control_panel(self, autoroom: discord.VoiceChannel) -> None: + """Create the control panel embed in the voice channel's text chat.""" + autoroom_info = await self.cog.get_autoroom_info(autoroom) + if not autoroom_info: + return + + owner = autoroom.guild.get_member(autoroom_info["owner"]) + if not owner: + return + + # Get the voice channel's text chat + text_channel = autoroom.guild.get_channel(autoroom.id) + if not text_channel: + return + + embed = discord.Embed( + title="AutoRoom Control Panel", + description=f"Control panel for {autoroom.mention}\nOwner: {owner.mention}", + color=discord.Color.blue() + ) + + # Add current status + status = "Public" if autoroom.permissions_for(autoroom.guild.default_role).connect else "Private" + embed.add_field(name="Status", value=status, inline=True) + + # Add member count + embed.add_field(name="Members", value=str(len(autoroom.members)), inline=True) + + # Create view with buttons + view = ControlPanelView(self.cog) + + # Send the embed + await text_channel.send(embed=embed, view=view) \ No newline at end of file diff --git a/autoroom/waiting_room.py b/autoroom/waiting_room.py index fbacf77..d3edb2e 100644 --- a/autoroom/waiting_room.py +++ b/autoroom/waiting_room.py @@ -141,8 +141,8 @@ class WaitingRoom: if not autoroom_info: return - # Get the text channel - text_channel = await self.cog.get_autoroom_legacy_text_channel(autoroom) + # Get the voice channel's text chat + text_channel = autoroom.guild.get_channel(autoroom.id) if not text_channel: return