Skip to content

discord_lfg.input_config

Process configs for the bot.

logger = logging.getLogger(__name__) module-attribute #

CommandArgument(name, python_type, required, description, autocomplete_options, autocomplete_channel_numbers=False, display_name='') dataclass #

For storage of parameters and creation of discord.app_command.Command arguments.

as_parameter property #

Gets the definition of an argument for a function.

More specifically, this uses the inspect.Parameter functionality to programmatically define what the argument should look like.

e.g. if you had a function defined like this: def func(name_arg: str, detail_info: dict): the two arguments would look like this:

name_arg = inspect.Parameter(
    name="name_arg",
    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
    annotation=str,
    default=inspect.Parameter.empty
)
detail_info = inspect.Parameter(
    name="detail_info",
    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
    annotation=dict,
    default=inspect.Parameter.empty
)
and you could then create a function by defining a function and then overriding the signature of the function using func.__signature__ and inspect.Signature.

autocomplete_channel_numbers = False class-attribute instance-attribute #

autocomplete_options instance-attribute #

description instance-attribute #

display_name = '' class-attribute instance-attribute #

displayed_name property #

Gets the name that is displayed to discord users.

name instance-attribute #

python_type instance-attribute #

required instance-attribute #

discord_autocomplete(command) #

Applies an autocompleter for a discord command that has had this parameter added.

Note that just because an argument has an autocomplete set, discord does not enforce the autocomplete values. See _autocomplete_validator.

Source code in src\discord_lfg\input_config.py
def discord_autocomplete(self, command: discord.app_commands.Command):
    """Applies an autocompleter for a discord command that has had this parameter added.

    Note that just because an argument has an autocomplete set, discord does not enforce the
    autocomplete values. See `_autocomplete_validator`.
    """
    if self.autocomplete_channel_numbers:
        autocomplete_choice_from_channel_numbers(command, self.name)
    elif self.autocomplete_options is not None:
        autocomplete_choice_from_list(self.autocomplete_options, command, self.name)

discord_description(command) #

Applies a description for a discord command that has had this parameter added.

Source code in src\discord_lfg\input_config.py
def discord_description(self, command: discord.app_commands.Command):
    """Applies a description for a discord command that has had this parameter added."""
    command._params[self.name].description = self.description

discord_rename(command) #

Renames how discord displays the name of this command.

This will make it so that although Python thinks the argument is named one thing, the name displayed to the user when they use the slash command is something different. This makes it possible to have a generalised function, but make it so that discord displays a customised name to the user.

Source code in src\discord_lfg\input_config.py
def discord_rename(self, command: discord.app_commands.Command):
    """Renames how discord displays the name of this command.

    This will make it so that although Python thinks the argument is named one thing,
    the name displayed to the user when they use the slash command is something different.
    This makes it possible to have a generalised function, but make it so that
    discord displays a customised name to the user.
    """
    if self.display_name != "":
        command._params[self.name]._rename = self.display_name

validate() #

Validates that the argument elements are acceptable.

Source code in src\discord_lfg\input_config.py
def validate(self):
    """Validates that the argument elements are acceptable."""
    errors = []
    if self.display_name == "":
        errors.append("    Command arguments must have a name.")
    if self.description == "":
        errors.append("    Command arguments must have a description.")
    if self.autocomplete_channel_numbers and self.autocomplete_options is not None:
        errors.append(
            f"    {self.name}: If you define `options_from_channel_numbers` "
            f"you cannot provide an options list."
        )
    return errors

CommandConfig(args, roles, name, description, debug, guild_name, timeout_length, editable_length, kick_reasons, channel_whitelist, channel_role_mentions, guild_roles) dataclass #

Configuration for individual command.

args instance-attribute #

channel_role_mentions instance-attribute #

channel_whitelist instance-attribute #

debug instance-attribute #

description instance-attribute #

editable_length instance-attribute #

guild_name instance-attribute #

guild_roles instance-attribute #

kick_reasons instance-attribute #

name instance-attribute #

roles instance-attribute #

timeout_length instance-attribute #

validate() #

Validates the config inputs.

Source code in src\discord_lfg\input_config.py
def validate(self):
    """Validates the config inputs."""
    errors = []
    if self.name == "":
        errors.append("Commands must have a name.")
    if self.description == "":
        errors.append("Commands must have a description.")
    return errors

ConfigValueError(messages) #

Bases: Exception

Autocompletion error message handler.

Initialisation.

Source code in src\discord_lfg\input_config.py
def __init__(self, messages):
    """Initialisation."""
    self.messages = messages

messages = messages instance-attribute #

LFGConfig(debug, guild_id_int, guild_id_discord, guild_name, moderator_role_name, log_folder, stats_folder, all_roles, commands) dataclass #

Configuration for LFG bot.

all_roles instance-attribute #

commands instance-attribute #

debug instance-attribute #

guild_id_discord instance-attribute #

guild_id_int instance-attribute #

guild_name instance-attribute #

log_folder instance-attribute #

moderator_role_name instance-attribute #

stats_folder instance-attribute #

validate() #

Validates the config inputs.

Source code in src\discord_lfg\input_config.py
def validate(self):
    """Validates the config inputs."""
    errors = []
    if not isinstance(self.debug, bool):
        errors.append("Debug must be `true` or `false`")
    if self.guild_id_int <= 0:
        errors.append("Guild ID must not be 0 or negative")
    if self.log_folder is not None and not self.log_folder.exists():
        errors.append(f"Log folder given does not exist, please create it: {self.log_folder}")
    if self.stats_folder is not None and not self.stats_folder.exists():
        errors.append(
            f"Data folder given does not exist, please create it: {self.stats_folder}"
        )
    errors += self._validate_roles()
    for command_path in self.commands:
        if not command_path.exists():
            errors.append(f"Command path given does not exist: {command_path}")
    return errors

command_argument_from_config(argument_definition, arg_name) #

Builds a command argument based on information given in a toml config.

Source code in src\discord_lfg\input_config.py
def command_argument_from_config(argument_definition: dict, arg_name: str):
    """Builds a command argument based on information given in a toml config."""
    type_lookups = {
        "str": str,
        "int": int,
        "float": float,
        "discord.member": discord.Member,
        "": None,
    }

    required_elements = ["display_name", "python_type", "description"]
    errors = []
    for element in required_elements:
        if argument_definition.get(element) is None:
            errors.append(f"    {arg_name} is missing {element} from argument definition")
    display_name = argument_definition.get("display_name", "")
    python_type = type_lookups[argument_definition.get("python_type", "").lower()]
    required_default = arg_name == "activity"
    required = argument_definition.get("required", required_default)
    description = argument_definition.get("description", "")
    autocomplete_options = argument_definition.get("options")
    autocomplete_channel_numbers = argument_definition.get("options_from_channel_numbers", False)
    command_argument = CommandArgument(
        name=arg_name,
        python_type=python_type,
        required=required,
        description=description,
        autocomplete_options=autocomplete_options,
        autocomplete_channel_numbers=autocomplete_channel_numbers,
        display_name=display_name,
    )
    errors += command_argument.validate()
    if len(errors) > 0:
        raise ConfigValueError(errors)

    return command_argument

parse_inputs() #

Parse the inputs to the bot.py script.

Source code in src\discord_lfg\input_config.py
def parse_inputs() -> tuple[str, LFGConfig, list[CommandConfig]]:
    """Parse the inputs to the bot.py script."""
    token, config_data = _argparser()
    config, commands = _parse_config(config_data)
    return token, config, commands

setup_logging(log_folder, debug=False) #

Setup logger.

Source code in src\discord_lfg\input_config.py
def setup_logging(log_folder: Path | None, debug: bool = False) -> logging.Logger:
    """Setup logger."""
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    if logger.handlers:
        return logger

    formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
    )

    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    if log_folder is not None and log_folder.exists():
        dt = datetime.now(timezone.utc).strftime("%Y-%m-%d_%H-%M-%S")
        log_file_path = log_folder / f"{dt}_discord-lfg.log"

        file_handler = logging.FileHandler(log_file_path, encoding="utf-8")
        file_handler.setLevel(logging.DEBUG if debug else logging.INFO)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

    logger.propagate = False
    return logger