1008 lines
40 KiB
Python
1008 lines
40 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."
|
|
)
|
|
)
|
|
|
|
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
|