Make users able to set a default name for their channels
This commit is contained in:
parent
6cdd236d48
commit
c54e7af00a
|
|
@ -2,19 +2,21 @@ import atexit
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union, Dict, List, Callable, Tuple
|
from typing import Union, Dict, List, Callable, Tuple, Any, Optional
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
from . import vNoneTov1_0
|
from . import vNoneTov1_0
|
||||||
|
from . import v1_0Tov1_1
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Functions used to convert between configuration versions, in a dictionary of: old_version -> converter
|
# Functions used to convert between configuration versions, in a dictionary of: old_version -> converter
|
||||||
# Each converter takes the old configuration and returns the converted configuration and the new version
|
# Each converter takes the old configuration and returns the converted configuration and the new version
|
||||||
config_version_converters: Dict[Union[None, str], Callable[[Dict], Tuple[Dict, str]]] = {
|
config_version_converters: Dict[Union[None, str], Callable[[Dict], Tuple[Dict, str]]] = {
|
||||||
None: vNoneTov1_0.convert
|
None: vNoneTov1_0.convert,
|
||||||
|
"1.0": v1_0Tov1_1.convert,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -22,7 +24,7 @@ class ChannelsConfigFile:
|
||||||
"""
|
"""
|
||||||
Wrapper for the channels configuration file, telling which channels to watch and which channels were created by the bot.
|
Wrapper for the channels configuration file, telling which channels to watch and which channels were created by the bot.
|
||||||
"""
|
"""
|
||||||
version = "1.0"
|
version = "1.1"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def empty_config() -> Dict:
|
def empty_config() -> Dict:
|
||||||
|
|
@ -121,6 +123,7 @@ class ChannelsConfigFile:
|
||||||
"_name": guild.name,
|
"_name": guild.name,
|
||||||
"watched_channels": [],
|
"watched_channels": [],
|
||||||
"created_channels": [],
|
"created_channels": [],
|
||||||
|
"user_config": {},
|
||||||
}
|
}
|
||||||
return self.config[str_guild_id]
|
return self.config[str_guild_id]
|
||||||
|
|
||||||
|
|
@ -202,3 +205,65 @@ class ChannelsConfigFile:
|
||||||
:return: Whether the given channel is one of the channels that have been created by the bot
|
:return: Whether the given channel is one of the channels that have been created by the bot
|
||||||
"""
|
"""
|
||||||
return channel.id in self._get_created_channels(guild)
|
return channel.id in self._get_created_channels(guild)
|
||||||
|
|
||||||
|
def _get_user_configs(self, guild: discord.Guild) -> Dict[str, Dict]:
|
||||||
|
"""
|
||||||
|
:return: The configuration for all users in a guild
|
||||||
|
"""
|
||||||
|
guild_configuration = self._get_guild_configuration(guild)
|
||||||
|
if "user_config" not in guild_configuration:
|
||||||
|
guild_configuration["user_config"] = {}
|
||||||
|
return guild_configuration["user_config"]
|
||||||
|
|
||||||
|
def _get_user_config(self, guild: discord.Guild, user: discord.Member) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
:return: The configuration for a user in a guild
|
||||||
|
"""
|
||||||
|
all_users_configs = self._get_user_configs(guild)
|
||||||
|
user_id_str = str(user.id)
|
||||||
|
if user_id_str not in all_users_configs:
|
||||||
|
all_users_configs[user_id_str] = {
|
||||||
|
"__username": f"{user.name}#{user.discriminator}",
|
||||||
|
"channel_name": None,
|
||||||
|
}
|
||||||
|
return all_users_configs[user_id_str]
|
||||||
|
|
||||||
|
def _clean_user_config_if_useless(self, guild: discord.Guild, user: discord.Member):
|
||||||
|
"""
|
||||||
|
If some of the information for a user in the user config of a guild is useless, remove it.
|
||||||
|
If the whole entry for a user in the user config of a guild is useless (i.e. it contains only its username), delete it.
|
||||||
|
"""
|
||||||
|
all_users_config = self._get_user_configs(guild)
|
||||||
|
user_id_str = str(user.id)
|
||||||
|
user_config = all_users_config[user_id_str] if user_id_str in all_users_config else {}
|
||||||
|
# If the default channel name for this user is set to None, it is the same as not being set
|
||||||
|
if "channel_name" in user_config and user_config["channel_name"] is None:
|
||||||
|
del user_config["channel_name"]
|
||||||
|
# If the only information remaining is the username (or nothing), delete the entry
|
||||||
|
if list(user_config.keys()) in ([], ["__username"]):
|
||||||
|
del all_users_config[user_id_str]
|
||||||
|
logger.debug(f"[{guild.name}({guild.id})] Deleted user {user.display_name}({user.id}) configuration because it was useless")
|
||||||
|
|
||||||
|
def set_user_channel_name(self, guild: discord.Guild, user: discord.Member, channel_name: str) -> None:
|
||||||
|
"""
|
||||||
|
Set the initial name for the channels created for a specific user
|
||||||
|
"""
|
||||||
|
user_config = self._get_user_config(guild, user)
|
||||||
|
user_config["channel_name"] = channel_name
|
||||||
|
|
||||||
|
def get_user_channel_name(self, guild: discord.Guild, user: discord.Member) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
:return: The initial name for the channels created for a specific user, if the user set one
|
||||||
|
"""
|
||||||
|
user_config = self._get_user_config(guild, user)
|
||||||
|
channel_name = user_config["channel_name"] if "channel_name" in user_config else None
|
||||||
|
self._clean_user_config_if_useless(guild, user)
|
||||||
|
return channel_name
|
||||||
|
|
||||||
|
def clear_user_channel_name(self, guild: discord.Guild, user: discord.Member):
|
||||||
|
"""
|
||||||
|
Remove the user preferences about the names for their channels
|
||||||
|
"""
|
||||||
|
user_config = self._get_user_config(guild, user)
|
||||||
|
user_config["channel_name"] = None
|
||||||
|
self._clean_user_config_if_useless(guild, user)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
Converter for converting ChannelsConfigFile from version 1.0 to version 1.1
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def convert(config: Dict) -> Tuple[Dict, str]:
|
||||||
|
assert config["__version__"] == "1.0"
|
||||||
|
|
||||||
|
for config_key, config_value in config.items():
|
||||||
|
# We skip the version information
|
||||||
|
if config_key == "__version__":
|
||||||
|
continue
|
||||||
|
# All the other entries are guild configuration
|
||||||
|
guild_config = config_value
|
||||||
|
if "user_config" not in guild_config:
|
||||||
|
guild_config["user_config"] = {}
|
||||||
|
|
||||||
|
config["__version__"] = "1.1"
|
||||||
|
return config, "1.1"
|
||||||
|
|
@ -3,6 +3,7 @@ import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import discord.utils
|
import discord.utils
|
||||||
|
|
@ -150,6 +151,44 @@ class VocalMaisBot(commands.Cog):
|
||||||
self.channels_config.clear_watched_channels(ctx.guild)
|
self.channels_config.clear_watched_channels(ctx.guild)
|
||||||
await ctx.send(":white_check_mark: I am no longer watching any channel")
|
await ctx.send(":white_check_mark: I am no longer watching any channel")
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
"set_name",
|
||||||
|
brief = "Set the default name for your voice channels",
|
||||||
|
help = "Set the default name for your voice channels. If you put {user} in the name, it will be replaced by your nickname",
|
||||||
|
usage = "<name>"
|
||||||
|
)
|
||||||
|
async def set_user_default_channel_name(self, ctx: commands.Context, *name_elements: str):
|
||||||
|
# Verify that the given name does not mention anyone by checking the message mentions
|
||||||
|
if len(set(ctx.message.mentions) - {self.bot.user}) > 0:
|
||||||
|
await ctx.send(":no_entry: User mentions are not allowed in channel names. Use {user} if you want to include your name.")
|
||||||
|
return
|
||||||
|
|
||||||
|
name = " ".join(name_elements)
|
||||||
|
if len(name) == 0:
|
||||||
|
await ctx.send(f":x: Missing channel name! {self.get_command_usage_message(ctx.command)}")
|
||||||
|
raise discord_utils.CheckFailDoNotNotify
|
||||||
|
|
||||||
|
self.channels_config.set_user_channel_name(ctx.guild, ctx.author, discord_utils.voice_channel_safe_name(name))
|
||||||
|
self.channels_config.save_to_file()
|
||||||
|
# We escape the markdown from the channel name when sending the message because for now discord does not support markdown in channel names,
|
||||||
|
# so we do not want the users to get their hopes up by seeing markdown working in the message
|
||||||
|
await ctx.send(f":white_check_mark: The default name for your channels has been set! If you create one now, it will appear as {discord_utils.voice_channel_safe_name(name, user_name_replacement = ctx.author.display_name, escape_markdown = True)}")
|
||||||
|
|
||||||
|
@commands.command("get_name", help = "Get the default name for your voice channels")
|
||||||
|
async def get_user_default_channel_name(self, ctx: commands.Context):
|
||||||
|
channel_name = self.channels_config.get_user_channel_name(ctx.guild, ctx.author)
|
||||||
|
if channel_name is None:
|
||||||
|
await ctx.send(":person_shrugging: You have not set a special name for your voice channels")
|
||||||
|
else:
|
||||||
|
channel_name = discord_utils.voice_channel_safe_name(channel_name, user_name_replacement = ctx.author.display_name, escape_markdown = True)
|
||||||
|
await ctx.send(f":memo: If you create a voice channel right now, it will be named like this: {channel_name}")
|
||||||
|
|
||||||
|
@commands.command("clear_name", help = "Do not use a special name for your voice channels")
|
||||||
|
async def clear_user_default_channel_name(self, ctx: commands.Context):
|
||||||
|
self.channels_config.clear_user_channel_name(ctx.guild, ctx.author)
|
||||||
|
self.channels_config.save_to_file()
|
||||||
|
await ctx.send(":white_check_mark: Your voice channels will use the default naming convention")
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
|
async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
|
||||||
if before.channel is not None: # They left a channel
|
if before.channel is not None: # They left a channel
|
||||||
|
|
@ -173,7 +212,10 @@ class VocalMaisBot(commands.Cog):
|
||||||
channel_permissions[user].manage_channels = True # We allow the user for which we created the channel to change the channel's name
|
channel_permissions[user].manage_channels = True # We allow the user for which we created the channel to change the channel's name
|
||||||
|
|
||||||
# Computing the channel name
|
# Computing the channel name
|
||||||
channel_name = f"{user.display_name}'s channel"
|
user_default_channel_name = self.channels_config.get_user_channel_name(channel.guild, user)
|
||||||
|
if user_default_channel_name is None:
|
||||||
|
user_default_channel_name = "{user}'s channel"
|
||||||
|
channel_name = discord_utils.voice_channel_safe_name(user_default_channel_name, user_name_replacement = user.display_name)
|
||||||
|
|
||||||
# Creating the channel and moving the user into it
|
# Creating the channel and moving the user into it
|
||||||
user_channel = await category.create_voice_channel(channel_name, overwrites = channel_permissions)
|
user_channel = await category.create_voice_channel(channel_name, overwrites = channel_permissions)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import discord
|
||||||
import discord.ext.commands as commands
|
import discord.ext.commands as commands
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,3 +8,25 @@ class CheckFailDoNotNotify(commands.CheckFailure):
|
||||||
For example it can be used if the check function already sent the user a customised error message.
|
For example it can be used if the check function already sent the user a customised error message.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def voice_channel_safe_name(template: str, max_chars: int = 100, escape_mentions: bool = True, escape_markdown: bool = False, user_name_replacement: str = None) -> str:
|
||||||
|
"""
|
||||||
|
Make a name that is suitable for naming voice channels from a template (e.g. found in the config file or given by a user)
|
||||||
|
:param template: The template used as basis for the name
|
||||||
|
:param max_chars: The name is ellipsed if it exceeds this number of characters. For a discord voice channel, it is currently 100 chars
|
||||||
|
:param escape_mentions: If true, escape role and user mentions
|
||||||
|
:param escape_markdown: If true, escape discord's markdown. For now, discord does not support markdown in channel names anyways
|
||||||
|
:param user_name_replacement: If not None, replace occurrences of '{user}' in the message with this value
|
||||||
|
"""
|
||||||
|
message = template
|
||||||
|
if user_name_replacement is not None:
|
||||||
|
message = message.replace("{user}", user_name_replacement)
|
||||||
|
if escape_mentions:
|
||||||
|
message = discord.utils.escape_mentions(message)
|
||||||
|
if escape_markdown:
|
||||||
|
message = discord.utils.escape_markdown(message)
|
||||||
|
if len(message) > max_chars:
|
||||||
|
message = message[:max_chars - 3] + "..."
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue