Ruby-Cogs/autoroom/c_autoroomset.py
Valerie 4c69cf7ab9
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Add waiting room functionality to AutoRoom cog
This update introduces a waiting room system for AutoRoom sources, allowing users to be approved before entering the main AutoRoom. New commands for enabling and disabling waiting rooms have been added, along with corresponding settings in the configuration. The README has been updated to include instructions on using the waiting room feature.
2025-06-13 19:22:53 -04:00

1055 lines
42 KiB
Python

"""The autoroomset command."""
import asyncio
import io
from abc import ABC
from contextlib import suppress
import discord
from jinja2.exceptions import TemplateError
from redbot.core import checks, commands
from redbot.core.utils.chat_formatting import error, info, success, warning
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
from redbot.core.utils.predicates import MessagePredicate
from .abc import MixinMeta
from .pcx_lib import SettingDisplay
channel_name_template = {
"username": "{{username}}'s Room{% if dupenum > 1 %} ({{dupenum}}){% endif %}",
"game": "{{game}}{% if not game %}{{username}}'s Room{% endif %}{% if dupenum > 1 %} ({{dupenum}}){% endif %}",
}
MAX_MESSAGE_LENGTH = 2000
class AutoRoomSetCommands(MixinMeta, ABC):
"""The autoroomset command."""
@commands.group()
@commands.guild_only()
@checks.admin_or_permissions(manage_guild=True)
async def autoroomset(self, ctx: commands.Context) -> None:
"""Configure the AutoRoom cog.
For a quick rundown on how to get started with this cog,
check out [the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md)
"""
@autoroomset.command()
async def settings(self, ctx: commands.Context) -> None:
"""Display current settings."""
if not ctx.guild:
return
server_section = SettingDisplay("Server Settings")
server_section.add(
"Admin access all AutoRooms",
await self.config.guild(ctx.guild).admin_access(),
)
server_section.add(
"Moderator access all AutoRooms",
await self.config.guild(ctx.guild).mod_access(),
)
bot_roles = ", ".join(
[role.name for role in await self.get_bot_roles(ctx.guild)]
)
if bot_roles:
server_section.add("Bot roles allowed in all AutoRooms", bot_roles)
await ctx.send(server_section.display())
avcs = await self.get_all_autoroom_source_configs(ctx.guild)
for avc_id, avc_settings in avcs.items():
source_channel = ctx.guild.get_channel(avc_id)
if not isinstance(source_channel, discord.VoiceChannel):
continue
dest_category = ctx.guild.get_channel(avc_settings["dest_category_id"])
autoroom_section = SettingDisplay(f"AutoRoom - {source_channel.name}")
autoroom_section.add(
"Room type",
avc_settings["room_type"].capitalize(),
)
autoroom_section.add(
"Destination category",
f"#{dest_category.name}" if dest_category else "INVALID CATEGORY",
)
if avc_settings["legacy_text_channel"]:
autoroom_section.add(
"Legacy Text Channel",
"True",
)
if not avc_settings["perm_send_messages"]:
autoroom_section.add(
"Send Messages",
"False",
)
if not avc_settings["perm_owner_manage_channels"]:
autoroom_section.add(
"Owner Manage Channel",
"False",
)
member_roles = self.get_member_roles(source_channel)
if member_roles:
autoroom_section.add(
"Member Roles" if len(member_roles) > 1 else "Member Role",
", ".join(role.name for role in member_roles),
)
room_name_format = "Username"
if avc_settings["channel_name_type"] in channel_name_template:
room_name_format = avc_settings["channel_name_type"].capitalize()
elif (
avc_settings["channel_name_type"] == "custom"
and avc_settings["channel_name_format"]
):
room_name_format = f'Custom: "{avc_settings["channel_name_format"]}"'
autoroom_section.add("Room name format", room_name_format)
if avc_settings["text_channel_hint"]:
autoroom_section.add(
"Text Channel Hint",
avc_settings["text_channel_hint"],
)
if avc_settings["text_channel_topic"]:
autoroom_section.add(
"Text Channel Topic",
avc_settings["text_channel_topic"],
)
msg = autoroom_section.display()
if len(msg) < MAX_MESSAGE_LENGTH:
await ctx.send(msg)
else:
raw_msg = autoroom_section.raw()
msg_bytes = io.BytesIO(raw_msg.encode("utf-8"))
await ctx.send(
file=discord.File(msg_bytes, filename="autoroom_settings.txt")
)
message = ""
required_check, optional_check, _ = await self._check_all_perms(ctx.guild)
if not required_check:
message += "\n" + error(
"It looks like I am missing one or more required permissions. "
"Until I have them, the AutoRoom cog may not function properly "
"for all AutoRoom Sources. "
"Check `[p]autoroomset permissions` for more information."
)
elif not optional_check:
message += "\n" + warning(
"All AutoRooms will work correctly, as I have all of the required permissions. "
"However, it looks like I am missing one or more optional permissions "
"for one or more AutoRooms. "
"Check `[p]autoroomset permissions` for more information."
)
if message:
await ctx.send(message)
@autoroomset.command(aliases=["perms"])
async def permissions(self, ctx: commands.Context) -> None:
"""Check that the bot has all needed permissions."""
if not ctx.guild:
return
required_check, optional_check, details_list = await self._check_all_perms(
ctx.guild, detailed=True
)
if not details_list:
await ctx.send(
info(
"You don't have any AutoRoom Sources set up! "
"Set one up with `[p]autoroomset create` first, "
"then I can check what permissions I need for it."
)
)
return
if (
len(details_list) > 1
and not ctx.channel.permissions_for(ctx.guild.me).add_reactions
):
await ctx.send(
error(
"Since you have multiple AutoRoom Sources, "
'I need the "Add Reactions" permission to display permission information.'
)
)
return
if not required_check:
await ctx.send(
error(
"It looks like I am missing one or more required permissions. "
"Until I have them, the AutoRoom Source(s) in question will not function properly."
"\n\n"
"The easiest way of fixing this is just giving me these permissions as part of my server role, "
"otherwise you will need to give me these permissions on the AutoRoom Source and destination "
"category, as specified below."
)
)
elif not optional_check:
await ctx.send(
warning(
"It looks like I am missing one or more optional permissions. "
"All AutoRooms will work, however some extra features may not work. "
"\n\n"
"The easiest way of fixing this is just giving me these permissions as part of my server role, "
"otherwise you will need to give me these permissions on the destination category, "
"as specified below."
"\n\n"
"In the case of optional permissions, any permission on the AutoRoom Source will be copied to "
"the created AutoRoom, as if we were cloning the AutoRoom Source. In order for this to work, "
"I need each permission to be allowed in the destination category (or server). "
"If it isn't allowed, I will skip copying that permission over."
)
)
else:
await ctx.send(success("Everything looks good here!"))
if len(details_list) > 1:
if (
ctx.channel.permissions_for(ctx.guild.me).add_reactions
and ctx.channel.permissions_for(ctx.guild.me).read_message_history
):
await menu(ctx, details_list, DEFAULT_CONTROLS, timeout=60.0)
else:
for details in details_list:
await ctx.send(details)
else:
await ctx.send(details_list[0])
@autoroomset.group()
async def access(self, ctx: commands.Context) -> None:
"""Control access to all AutoRooms.
Roles that are considered "admin" or "moderator" are
set up with the commands `[p]set addadminrole`
and `[p]set addmodrole` (plus the remove commands too)
"""
@access.command(name="admin")
async def access_admin(self, ctx: commands.Context) -> None:
"""Allow Admins to join locked/private AutoRooms."""
if not ctx.guild:
return
admin_access = not await self.config.guild(ctx.guild).admin_access()
await self.config.guild(ctx.guild).admin_access.set(admin_access)
await ctx.send(
success(
f"Admins are {'now' if admin_access else 'no longer'} able to join (new) locked/private AutoRooms."
)
)
@access.command(name="mod")
async def access_mod(self, ctx: commands.Context) -> None:
"""Allow Moderators to join locked/private AutoRooms."""
if not ctx.guild:
return
mod_access = not await self.config.guild(ctx.guild).mod_access()
await self.config.guild(ctx.guild).mod_access.set(mod_access)
await ctx.send(
success(
f"Moderators are {'now' if mod_access else 'no longer'} able to join (new) locked/private AutoRooms."
)
)
@access.group(name="bot")
async def access_bot(self, ctx: commands.Context) -> None:
"""Automatically allow bots into AutoRooms.
The AutoRoom Owner is able to freely allow or deny these roles as they see fit.
"""
@access_bot.command(name="add")
async def access_bot_add(self, ctx: commands.Context, role: discord.Role) -> None:
"""Allow a bot role into every AutoRoom."""
if not ctx.guild:
return
bot_role_ids = await self.config.guild(ctx.guild).bot_access()
if role.id not in bot_role_ids:
bot_role_ids.append(role.id)
await self.config.guild(ctx.guild).bot_access.set(bot_role_ids)
role_list = "\n".join(
[role.name for role in await self.get_bot_roles(ctx.guild)]
)
await ctx.send(
success(
f"AutoRooms will now allow the following bot roles in by default:\n\n{role_list}"
)
)
@access_bot.command(name="remove", aliases=["delete", "del"])
async def access_bot_remove(
self, ctx: commands.Context, role: discord.Role
) -> None:
"""Disallow a bot role from joining every AutoRoom."""
if not ctx.guild:
return
bot_role_ids = await self.config.guild(ctx.guild).bot_access()
if role.id in bot_role_ids:
bot_role_ids.remove(role.id)
await self.config.guild(ctx.guild).bot_access.set(bot_role_ids)
if bot_role_ids:
role_list = "\n".join(
[role.name for role in await self.get_bot_roles(ctx.guild)]
)
await ctx.send(
success(
f"AutoRooms will now allow the following bot roles in by default:\n\n{role_list}"
)
)
else:
await ctx.send(
success("New AutoRooms will not allow any extra bot roles in.")
)
@autoroomset.command(aliases=["enable", "add"])
async def create(
self,
ctx: commands.Context,
source_voice_channel: discord.VoiceChannel,
dest_category: discord.CategoryChannel,
) -> None:
"""Create an AutoRoom Source.
Anyone joining an AutoRoom Source will automatically have a new
voice channel (AutoRoom) created in the destination category,
and then be moved into it.
"""
if not ctx.guild:
return
perms_required, perms_optional, details = self.check_perms_source_dest(
source_voice_channel, dest_category, detailed=True
)
if not perms_required or not perms_optional:
await ctx.send(
error(
"I am missing a permission that the AutoRoom cog requires me to have. "
"Check below for the permissions I require in both the AutoRoom Source "
"and the destination category. "
"Try creating the AutoRoom Source again once I have these permissions."
"\n"
f"{details}"
"\n"
"The easiest way of doing this is just giving me these permissions as part of my server role, "
"otherwise you will need to give me these permissions on the source channel and destination "
"category, as specified above."
)
)
return
new_source: dict[str, str | int] = {"dest_category_id": dest_category.id}
# Room type
options = ["public", "locked", "private", "server"]
pred = MessagePredicate.lower_contained_in(options, ctx)
await ctx.send(
"**Welcome to the setup wizard for creating an AutoRoom Source!**"
"\n"
f"Users joining the {source_voice_channel.mention} AutoRoom Source will have an AutoRoom "
f"created in the {dest_category.mention} category and be moved into it."
"\n\n"
"**AutoRoom Type**"
"\n"
"AutoRooms can be one of the following types when created:"
"\n"
"`public ` - Visible and joinable by other users. The AutoRoom Owner can kick/ban users out of them."
"\n"
"`locked ` - Visible, but not joinable by other users. The AutoRoom Owner must allow users into their room."
"\n"
"`private` - Not visible or joinable by other users. The AutoRoom Owner must allow users into their room."
"\n"
"`server ` - Same as a public AutoRoom, but with no AutoRoom Owner. "
"No modifications can be made to the generated AutoRoom."
"\n\n"
"What would you like these created AutoRooms to be by default? (`public`/`locked`/`private`/`server`)"
)
answer = None
with suppress(asyncio.TimeoutError):
await ctx.bot.wait_for("message", check=pred, timeout=60)
answer = pred.result
if answer is not None:
new_source["room_type"] = options[answer]
else:
await ctx.send("No valid answer was received, canceling setup process.")
return
# Check perms room type
perms_required, perms_optional, details = self.check_perms_source_dest(
source_voice_channel,
dest_category,
with_manage_roles_guild=new_source["room_type"] != "server",
detailed=True,
)
if not perms_required or not perms_optional:
await ctx.send(
error(
f"Since you want to have this AutoRoom Source create {new_source['room_type']} AutoRooms, "
"I will need a few extra permissions. "
"Try creating the AutoRoom Source again once I have these permissions."
"\n"
f"{details}"
)
)
return
# Channel name
options = ["username", "game"]
pred = MessagePredicate.lower_contained_in(options, ctx)
await ctx.send(
"**Channel Name**"
"\n"
"When an AutoRoom is created, a name will be generated for it. How would you like that name to be generated?"
"\n\n"
f'`username` - Shows up as "{ctx.author.display_name}\'s Room"\n'
"`game ` - AutoRoom Owner's playing game, otherwise `username`"
)
answer = None
with suppress(asyncio.TimeoutError):
await ctx.bot.wait_for("message", check=pred, timeout=60)
answer = pred.result
if answer is not None:
new_source["channel_name_type"] = options[answer]
else:
await ctx.send("No valid answer was received, canceling setup process.")
return
# Save new source
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(source_voice_channel.id)
).set(new_source)
await ctx.send(
success(
"Settings saved successfully!\n"
"Check out `[p]autoroomset modify` for even more AutoRoom Source settings, "
"or to make modifications to your above answers."
)
)
@autoroomset.command(aliases=["disable", "delete", "del"])
async def remove(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Remove an AutoRoom Source."""
if not ctx.guild:
return
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).clear()
await ctx.send(
success(
f"**{autoroom_source.mention}** is no longer an AutoRoom Source channel."
)
)
@autoroomset.group(aliases=["edit"])
async def modify(self, ctx: commands.Context) -> None:
"""Modify an existing AutoRoom Source."""
@modify.command(name="category")
async def modify_category(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
dest_category: discord.CategoryChannel,
) -> None:
"""Set the category that AutoRooms will be created in."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).dest_category_id.set(dest_category.id)
perms_required, perms_optional, details = self.check_perms_source_dest(
autoroom_source, dest_category, detailed=True
)
message = f"**{autoroom_source.mention}** will now create new AutoRooms in the **{dest_category.mention}** category."
if perms_required and perms_optional:
await ctx.send(success(message))
else:
await ctx.send(
warning(
f"{message}"
"\n"
"Do note, this new category does not have sufficient permissions for me to make AutoRooms. "
"Until you fix this, the AutoRoom Source will not work."
"\n"
f"{details}"
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.group(name="type")
async def modify_type(self, ctx: commands.Context) -> None:
"""Choose what type of AutoRoom is created."""
@modify_type.command(name="public")
async def modify_type_public(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Rooms will be open to all. AutoRoom Owner has control over room."""
await self._save_public_private(ctx, autoroom_source, "public")
@modify_type.command(name="locked")
async def modify_type_locked(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Rooms will be visible to all, but not joinable. AutoRoom Owner can allow users in."""
await self._save_public_private(ctx, autoroom_source, "locked")
@modify_type.command(name="private")
async def modify_type_private(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Rooms will be hidden. AutoRoom Owner can allow users in."""
await self._save_public_private(ctx, autoroom_source, "private")
@modify_type.command(name="server")
async def modify_type_server(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Rooms will be open to all, but the server owns the AutoRoom (so they can't be modified)."""
await self._save_public_private(ctx, autoroom_source, "server")
async def _save_public_private(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
room_type: str,
) -> None:
"""Save the public/private setting."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).room_type.set(room_type)
await ctx.send(
success(
f"**{autoroom_source.mention}** will now create `{room_type}` AutoRooms."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.group(name="name")
async def modify_name(self, ctx: commands.Context) -> None:
"""Set the default name format of an AutoRoom."""
@modify_name.command(name="username")
async def modify_name_username(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Default format: PhasecoreX's Room.
Custom format example:
`{{username}}'s Room{% if dupenum > 1 %} ({{dupenum}}){% endif %}`
""" # noqa: D401
await self._save_room_name(ctx, autoroom_source, "username")
@modify_name.command(name="game")
async def modify_name_game(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""The users current playing game, otherwise the username format.
Custom format example:
`{{game}}{% if not game %}{{username}}'s Room{% endif %}{% if dupenum > 1 %} ({{dupenum}}){% endif %}`
""" # noqa: D401
await self._save_room_name(ctx, autoroom_source, "game")
@modify_name.command(name="custom")
async def modify_name_custom(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
*,
template: str,
) -> None:
"""A custom channel name.
Use `{{ expressions }}` to print variables and `{% statements %}` to do basic evaluations on variables.
Variables supported:
- `username` - AutoRoom Owner's username
- `game ` - AutoRoom Owner's game
- `dupenum ` - An incrementing number that starts at 1, useful for un-duplicating channel names
Statements supported:
- `if/elif/else/endif`
- Example: `{% if dupenum > 1 %}DupeNum is {{dupenum}}, which is greater than 1{% endif %}`
- Another example: `{% if not game %}User isn't playing a game!{% endif %}`
It's kinda like Jinja2, but way simpler. Check out [the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md) for more info.
""" # noqa: D401
await self._save_room_name(ctx, autoroom_source, "custom", template)
async def _save_room_name(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
room_type: str,
template: str | None = None,
) -> None:
"""Save the room name type."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
data = self.get_template_data(ctx.author)
if template:
template = template.replace("\n", " ")
try:
# Validate template
await self.format_template_room_name(template, data)
except TemplateError as rte:
await ctx.send(
error(
"Hmm... that doesn't seem to be a valid template:"
"\n\n"
f"`{rte!s}`"
"\n\n"
"If you need some help, take a look at "
"[the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md)."
)
)
return
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).channel_name_format.set(template)
else:
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).channel_name_format.clear()
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).channel_name_type.set(room_type)
message = (
f"New AutoRooms created by **{autoroom_source.mention}** "
f"will use the **{room_type.capitalize()}** format"
)
if template:
message += f":\n`{template}`"
else:
# Load preset template for display purposes
template = channel_name_template[room_type]
message += "."
if "game" not in data:
data["game"] = "Example Game"
message += "\n\nExample room names:"
for room_num in range(1, 4):
message += f"\n{await self.format_template_room_name(template, data, room_num)}"
await ctx.send(success(message))
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.group(name="text")
async def modify_text(
self,
ctx: commands.Context,
) -> None:
"""Configure sending an introductory message to the AutoRoom text channel."""
@modify_text.command(name="set")
async def modify_text_set(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
*,
hint_text: str,
) -> None:
"""Send a message to the newly generated AutoRoom text channel.
This can have template variables and statements, which you can learn more
about by looking at `[p]autoroomset modify name custom`, or by looking at
[the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md).
The only additional variable that may be useful here is the `mention` variable,
which will insert the users mention (pinging them).
- Example:
`Hello {{mention}}! Welcome to your new AutoRoom!`
"""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
data = self.get_template_data(ctx.author)
try:
# Validate template
hint_text_formatted = await self.template.render(hint_text, data)
except TemplateError as rte:
await ctx.send(
error(
"Hmm... that doesn't seem to be a valid template:"
"\n\n"
f"`{rte!s}`"
"\n\n"
"If you need some help, take a look at "
"[the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md)."
)
)
return
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).text_channel_hint.set(hint_text)
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will have the following message sent in them:"
"\n\n"
f"{hint_text_formatted[:1900]}"
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify_text.command(name="disable")
async def modify_text_disable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Disable sending a message to the newly generated AutoRoom text channel."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).text_channel_hint.clear()
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will no longer have a message sent in them."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.group(name="specialperms")
async def special_perms(self, ctx: commands.Context) -> None:
"""Modify special AutoRoom permissions.
Remember, most permissions are automatically copied
from the AuthRoom Source over to the AutoRoom.
These are for configuring special cases.
"""
@special_perms.command(name="ownermodify")
async def owner_manage_channels(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Allow AutoRoom Owners to have the Manage Channels permission on their AutoRoom."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
new_config_value = not await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).perm_owner_manage_channels()
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).perm_owner_manage_channels.set(new_config_value)
await ctx.send(
success(
f"AutoRoom Owners are {'now' if new_config_value else 'no longer'} able to modify their AutoRoom with native Discord controls."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@special_perms.command(name="sendmessage")
async def public_send_messages(
self, ctx: commands.Context, autoroom_source: discord.VoiceChannel
) -> None:
"""Allow users to send messages in the AutoRoom built in text channel."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
new_config_value = not await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).perm_send_messages()
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).perm_send_messages.set(new_config_value)
await ctx.send(
success(
f"Users are {'now' if new_config_value else 'no longer'} able to send messages in the AutoRoom built in text channel."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.group(name="legacytextchannel")
async def modify_legacytext(
self,
ctx: commands.Context,
) -> None:
"""Manage if a legacy text channel should be created as well."""
@modify_legacytext.command(name="enable")
async def modify_legacytext_enable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Enable creating a legacy text channel with the AutoRoom."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).legacy_text_channel.set(value=True)
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will now get their own legacy text channel."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify_legacytext.command(name="disable")
async def modify_legacytext_disable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Disable creating a legacy text channel with the AutoRoom."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).legacy_text_channel.clear()
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will no longer get their own legacy text channel."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify_legacytext.group(name="topic")
async def modify_legacytext_topic(
self,
ctx: commands.Context,
) -> None:
"""Manage the legacy text channel topic."""
@modify_legacytext_topic.command(name="set")
async def modify_legacytext_topic_set(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
*,
topic_text: str,
) -> None:
"""Set the legacy text channel topic.
- Example:
`This channel is only visible to members of your voice channel, and admins of this server. It will be deleted when everyone leaves. `
"""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
data = self.get_template_data(ctx.author)
try:
# Validate template
topic_text_formatted = await self.template.render(topic_text, data)
except TemplateError as rte:
await ctx.send(
error(
"Hmm... that doesn't seem to be a valid template:"
"\n\n"
f"`{rte!s}`"
"\n\n"
"If you need some help, take a look at "
"[the readme](https://github.com/PhasecoreX/PCXCogs/tree/master/autoroom/README.md)."
)
)
return
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).text_channel_topic.set(topic_text)
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will have the following message topic set:"
"\n\n"
f"{topic_text_formatted}"
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify_legacytext_topic.command(name="disable")
async def modify_legacytext_topic_disable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Disable setting a legacy text channel topic."""
if not ctx.guild:
return
if await self.get_autoroom_source_config(autoroom_source):
await self.config.custom(
"AUTOROOM_SOURCE", str(ctx.guild.id), str(autoroom_source.id)
).text_channel_topic.clear()
await ctx.send(
success(
f"New AutoRooms created by **{autoroom_source.mention}** will no longer have a topic set."
)
)
else:
await ctx.send(
error(
f"**{autoroom_source.mention}** is not an AutoRoom Source channel."
)
)
@modify.command(
name="defaults", aliases=["bitrate", "memberrole", "other", "perms", "users"]
)
async def modify_defaults(self, ctx: commands.Context) -> None:
"""Learn how AutoRoom defaults are set."""
await ctx.send(
info(
"**Bitrate/User Limit**"
"\n"
"Default bitrate and user limit settings are copied from the AutoRoom Source to the resulting AutoRoom."
"\n\n"
"**Member Roles**"
"\n"
"Only members that can view and join an AutoRoom Source will be able to join its resulting AutoRooms. "
"If you would like to limit AutoRooms to only allow certain members, simply deny the everyone role "
"from viewing/connecting to the AutoRoom Source and allow your member roles to view/connect to it."
"\n\n"
"**Permissions**"
"\n"
"All permission overwrites (except for Manage Roles) will be copied from the AutoRoom Source "
"to the resulting AutoRoom. Every permission overwrite you want copied over, regardless if it is "
"allowed or denied, must be allowed for the bot. It can either be allowed for the bot in the "
"destination category or server-wide with the roles it has. `[p]autoroomset permissions` will "
"show what permissions will be copied over."
)
)
@modify.group(name="waitingroom")
async def modify_waitingroom(
self,
ctx: commands.Context,
) -> None:
"""Modify waiting room settings."""
pass
@modify_waitingroom.command(name="enable")
async def modify_waitingroom_enable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Enable waiting room for an AutoRoom source."""
if not await self.get_autoroom_source_config(autoroom_source):
await ctx.send("That is not an AutoRoom source.")
return
# Get the text channel
text_channel = await self.get_autoroom_legacy_text_channel(autoroom_source)
if not text_channel:
await ctx.send("This AutoRoom source needs a text channel enabled first.")
return
# Set up waiting room
success = await self.waiting_room.setup_waiting_room(autoroom_source, text_channel)
if success:
await ctx.send("Waiting room has been enabled for this AutoRoom source.")
else:
await ctx.send("Failed to set up waiting room. Please check my permissions.")
@modify_waitingroom.command(name="disable")
async def modify_waitingroom_disable(
self,
ctx: commands.Context,
autoroom_source: discord.VoiceChannel,
) -> None:
"""Disable waiting room for an AutoRoom source."""
if not await self.get_autoroom_source_config(autoroom_source):
await ctx.send("That is not an AutoRoom source.")
return
# Clean up waiting room
await self.waiting_room.cleanup_waiting_room(autoroom_source)
await ctx.send("Waiting room has been disabled for this AutoRoom source.")
async def _check_all_perms(
self, guild: discord.Guild, *, detailed: bool = False
) -> tuple[bool, bool, list[str]]:
"""Check all permissions for all AutoRooms in a guild."""
result_required = True
result_optional = True
result_list = []
avcs = await self.get_all_autoroom_source_configs(guild)
for avc_id, avc_settings in avcs.items():
autoroom_source = guild.get_channel(avc_id)
category_dest = guild.get_channel(avc_settings["dest_category_id"])
if isinstance(autoroom_source, discord.VoiceChannel) and isinstance(
category_dest, discord.CategoryChannel
):
(
required_check,
optional_check,
detail,
) = self.check_perms_source_dest(
autoroom_source,
category_dest,
with_manage_roles_guild=avc_settings["room_type"] != "server",
with_legacy_text_channel=avc_settings["legacy_text_channel"],
with_optional_clone_perms=True,
detailed=detailed,
)
result_required = result_required and required_check
result_optional = result_optional and optional_check
if detailed:
result_list.append(detail)
elif not result_required and not result_optional:
break
return result_required, result_optional, result_list