Skip to content

tux.utils.functions

Functions:

Name Description
truncate

Truncates a string to a specified length.

is_harmful

Check if a command is potentially harmful to the system.

strip_formatting

Strip formatting from a string.

parse_time_string

Convert a string representation of time into a datetime.timedelta object.

convert_to_seconds

Converts a formatted time string with the formats Mwdhms

seconds_to_human_readable

Converts a number of seconds into a human readable string

datetime_to_unix

This function accepts a datetime object or None, converts it into a Unix timestamp

datetime_to_elapsed_time

Takes a datetime and computes the elapsed time from then to now in the format: X years, Y months, Z days.

compare_changes

Compares the changes between two dictionaries and returns a list of strings representing the changes.

compare_guild_channel_changes

Compares the changes between two GuildChannel instances and returns a list of strings representing the changes.

compare_member_changes

Compares changes between two Member instances and returns a list of strings representing the changes.

extract_guild_attrs

Extracts relevant attributes from a discord.Guild and returns them as a dictionary.

extract_member_attrs

Extract relevant attributes from a member for comparison.

is_optional_param

Check if a parameter is optional.

get_matching_string

Matches the given argument to a specific string based on common usage.

generate_usage

Generate the usage string for a command.

Functions

truncate(text: str, length: int) -> str

Truncates a string to a specified length.

If the string is longer than the specified length, it will be truncated and an ellipsis will be appended. Otherwise, the original string is returned.

Parameters:

Name Type Description Default
text str

The string to truncate.

required
length int

The maximum length of the string.

required

Returns:

Type Description
str

The truncated string.

Source code in tux/utils/functions.py
Python
def truncate(text: str, length: int) -> str:
    """Truncates a string to a specified length.

    If the string is longer than the specified length, it will be truncated
    and an ellipsis will be appended. Otherwise, the original string is returned.

    Parameters
    ----------
    text : str
        The string to truncate.
    length : int
        The maximum length of the string.

    Returns
    -------
    str
        The truncated string.
    """
    return text if len(text) <= length else f"{text[: length - 3]}..."

is_harmful(command: str) -> str | None

Check if a command is potentially harmful to the system.

Parameters:

Name Type Description Default
command str

The command to check.

required

Returns:

Type Description
bool

True if the command is harmful, False otherwise.

Source code in tux/utils/functions.py
Python
def is_harmful(command: str) -> str | None:
    # sourcery skip: assign-if-exp, boolean-if-exp-identity, reintroduce-else
    """
    Check if a command is potentially harmful to the system.

    Parameters
    ----------
    command : str
        The command to check.

    Returns
    -------
    bool
        True if the command is harmful, False otherwise.
    """
    # Normalize command by removing all whitespace for fork bomb check
    normalized = "".join(command.strip().lower().split())
    if normalized in FORK_BOMB_PATTERNS:
        return "FORK_BOMB"

    # Check for dangerous rm commands
    if re.search(DANGEROUS_RM_COMMANDS, command, re.IGNORECASE):
        return "RM_COMMAND"

    # Check for dangerous dd commands
    if re.search(DANGEROUS_DD_COMMANDS, command, re.IGNORECASE):
        return "DD_COMMAND"

    # Check for format commands
    if bool(re.search(FORMAT_COMMANDS, command, re.IGNORECASE)):
        return "FORMAT_COMMAND"
    return None

strip_formatting(content: str) -> str

Strip formatting from a string.

Parameters:

Name Type Description Default
content str

The string to strip formatting from.

required

Returns:

Type Description
str

The string with formatting stripped.

Source code in tux/utils/functions.py
Python
def strip_formatting(content: str) -> str:
    """
    Strip formatting from a string.

    Parameters
    ----------
    content : str
        The string to strip formatting from.

    Returns
    -------
    str
        The string with formatting stripped.
    """
    # Remove triple backtick blocks
    content = re.sub(r"```(.*?)```", r"\1", content)
    # Remove single backtick code blocks
    content = re.sub(r"`([^`]*)`", r"\1", content)
    # Remove Markdown headers
    content = re.sub(r"^#+\s+", "", content, flags=re.MULTILINE)
    # Remove markdown formatting characters, but preserve |
    content = re.sub(r"[\*_~>]", "", content)
    # Remove extra whitespace
    content = re.sub(r"\s+", " ", content)

    return content.strip()

parse_time_string(time_str: str) -> timedelta

Convert a string representation of time into a datetime.timedelta object.

Parameters:

Name Type Description Default
time_str str

The string representation of time to convert. (e.g., '60s', '1m', '2h', '10d')

required

Returns:

Type Description
timedelta

The timedelta object representing the time string.

Source code in tux/utils/functions.py
Python
def parse_time_string(time_str: str) -> timedelta:
    """
    Convert a string representation of time into a datetime.timedelta object.

    Parameters
    ----------
    time_str : str
        The string representation of time to convert. (e.g., '60s', '1m', '2h', '10d')

    Returns
    -------
    timedelta
        The timedelta object representing the time string.
    """

    # Define regex pattern to parse time strings
    time_pattern = re.compile(r"^(?P<value>\d+)(?P<unit>[smhdw])$")

    # Match the input string with the pattern
    match = time_pattern.match(time_str)

    if not match:
        msg = f"Invalid time format: '{time_str}'"
        raise ValueError(msg)

    # Extract the value and unit from the pattern match
    value = int(match["value"])
    unit = match["unit"]

    # Define the mapping of units to keyword arguments for timedelta
    unit_map = {"s": "seconds", "m": "minutes", "h": "hours", "d": "days", "w": "weeks"}

    # Check if the unit is in the map
    if unit not in unit_map:
        msg = f"Unknown time unit: '{unit}'"
        raise ValueError(msg)

    # Create the timedelta with the appropriate keyword argument
    kwargs = {unit_map[unit]: value}

    return timedelta(**kwargs)

convert_to_seconds(time_str: str) -> int

Converts a formatted time string with the formats Mwdhms Any unexpected format leads to returning 0.

Parameters:

Name Type Description Default
time_str str

The formatted time string to convert to total seconds.

required

Returns:

Type Description
int

The total seconds from the formatted time string.

Source code in tux/utils/functions.py
Python
def convert_to_seconds(time_str: str) -> int:
    """
    Converts a formatted time string with the formats Mwdhms
    Any unexpected format leads to returning 0.

    Parameters
    ----------
    time_str : str
        The formatted time string to convert to total seconds.

    Returns
    -------
    int
        The total seconds from the formatted time string.
    """

    # Time conversion factors from units to seconds
    time_units = {
        "M": 2592000,  # Months to seconds
        "w": 604800,  # Weeks to seconds
        "d": 86400,  # Days to seconds
        "h": 3600,  # Hours to seconds
        "m": 60,  # Minutes to seconds
        "s": 1,  # Seconds to seconds
    }

    total_seconds = 0
    current_value = 0

    for char in time_str:
        if char.isdigit():
            # Build the current number
            current_value = current_value * 10 + int(char)
        elif char in time_units:
            # If the unit is known, update total_seconds
            if current_value == 0:
                return 0  # No number specified for the unit, thus treat as invalid input
            total_seconds += current_value * time_units[char]
            current_value = 0  # Reset for next number-unit pair
        else:
            # Unknown character indicates an invalid format
            return 0

    return 0 if current_value != 0 else total_seconds

seconds_to_human_readable(seconds: int) -> str

Converts a number of seconds into a human readable string

Parameters:

Name Type Description Default
seconds int

The number of seconds to convert

required

Returns:

Type Description
str

A string that breaks the time down by months, weeks, days, hours, minutes, and seconds.

Source code in tux/utils/functions.py
Python
def seconds_to_human_readable(seconds: int) -> str:
    """
    Converts a number of seconds into a human readable string

    Parameters
    ----------
    seconds : int
        The number of seconds to convert

    Returns
    -------
    str
        A string that breaks the time down by months, weeks, days, hours, minutes, and seconds.
    """
    units = (
        ("month", 2592000),
        ("week", 604800),
        ("day", 86400),
        ("hour", 3600),
        ("minute", 60),
        ("second", 1),
    )
    if seconds == 0:
        return "zero seconds"
    parts: list[str] = []
    for unit, div in units:
        amount, seconds = divmod(int(seconds), div)
        if amount > 0:
            parts.append(f"{amount} {unit}{'' if amount == 1 else 's'}")
    return ", ".join(parts)

datetime_to_unix(dt: datetime | None) -> str

This function accepts a datetime object or None, converts it into a Unix timestamp and returns it as a formatted Discord timestamp string or 'Never'

Parameters:

Name Type Description Default
dt datetime

The datetime object to convert to a Discord timestamp string.

required

Returns:

Type Description
str

The formatted Discord timestamp string or 'Never'

Source code in tux/utils/functions.py
Python
def datetime_to_unix(dt: datetime | None) -> str:
    """
    This function accepts a datetime object or None, converts it into a Unix timestamp
    and returns it as a formatted Discord timestamp string or 'Never'

    Parameters
    ----------
    dt : datetime
        The datetime object to convert to a Discord timestamp string.

    Returns
    -------
    str
        The formatted Discord timestamp string or 'Never'
    """

    if dt is None:
        return "Never"

    unix_timestamp = int(dt.timestamp())

    return f"<t:{unix_timestamp}>"

datetime_to_elapsed_time(dt: datetime | None) -> str

Takes a datetime and computes the elapsed time from then to now in the format: X years, Y months, Z days.

Parameters:

Name Type Description Default
dt datetime

The datetime object to compute the elapsed time from.

required

Returns:

Type Description
str

The elapsed time in the format: X years, Y months, Z days.

Source code in tux/utils/functions.py
Python
def datetime_to_elapsed_time(dt: datetime | None) -> str:
    """
    Takes a datetime and computes the elapsed time from then to now in the format: X years, Y months, Z days.

    Parameters
    ----------
    dt : datetime
        The datetime object to compute the elapsed time from.

    Returns
    -------
    str
        The elapsed time in the format: X years, Y months, Z days.
    """

    if dt is None:
        return "Never"

    elapsed_time = datetime.now(UTC) - dt
    elapsed_days = elapsed_time.days

    years, days_left = divmod(elapsed_days, 365)
    months, days_left = divmod(days_left, 30)

    return f"{years} years, {months} months, {days_left} days"

compare_changes(before: dict[str, Any], after: dict[str, Any]) -> list[str]

Compares the changes between two dictionaries and returns a list of strings representing the changes.

Parameters:

Name Type Description Default
before dict

The dictionary representing the state before the changes.

required
after dict

The dictionary representing the state after the changes.

required

Returns:

Type Description
list

A list of strings showing the changes made in the dictionaries.

Source code in tux/utils/functions.py
Python
def compare_changes(before: dict[str, Any], after: dict[str, Any]) -> list[str]:
    """
    Compares the changes between two dictionaries and returns a list of strings representing the changes.

    Parameters
    ----------
    before : dict
        The dictionary representing the state before the changes.
    after : dict
        The dictionary representing the state after the changes.

    Returns
    -------
    list
        A list of strings showing the changes made in the dictionaries.
    """

    return [f"{key}: {before[key]} -> {after[key]}" for key in before if key in after and before[key] != after[key]]

compare_guild_channel_changes(before: discord.abc.GuildChannel, after: discord.abc.GuildChannel) -> list[str]

Compares the changes between two GuildChannel instances and returns a list of strings representing the changes.

Parameters:

Name Type Description Default
before GuildChannel

The GuildChannel instance representing the state before the changes.

required
after GuildChannel

The GuildChannel instance representing the state after the changes.

required

Returns:

Type Description
list

A list of strings showing the changes made in the GuildChannel instances.

Source code in tux/utils/functions.py
Python
def compare_guild_channel_changes(
    before: discord.abc.GuildChannel,
    after: discord.abc.GuildChannel,
) -> list[str]:
    """
    Compares the changes between two GuildChannel instances and returns a list of strings representing the changes.

    Parameters
    ----------
    before : discord.abc.GuildChannel
        The GuildChannel instance representing the state before the changes.
    after : discord.abc.GuildChannel
        The GuildChannel instance representing the state after the changes.

    Returns
    -------
    list
        A list of strings showing the changes made in the GuildChannel instances.
    """

    keys = [
        "category",
        "changed_roles",
        "created_at",
        "guild",
        "name",
        "overwrites",
        "permissions_synced",
        "position",
    ]

    return [
        f"{key}: {getattr(before, key)} -> {getattr(after, key)}"
        for key in keys
        if getattr(before, key) != getattr(after, key)
    ]

compare_member_changes(before: discord.Member | discord.User, after: discord.Member | discord.User) -> list[str]

Compares changes between two Member instances and returns a list of strings representing the changes.

Parameters:

Name Type Description Default
before Member

The Member instance representing the state before the changes.

required
after Member

The Member instance representing the state after the changes.

required

Returns:

Type Description
list

A list of strings showing the changes made in the Member instances.

Source code in tux/utils/functions.py
Python
def compare_member_changes(
    before: discord.Member | discord.User,
    after: discord.Member | discord.User,
) -> list[str]:
    """
    Compares changes between two Member instances and returns a list of strings representing the changes.

    Parameters
    ----------
    before : discord.Member
        The Member instance representing the state before the changes.
    after : discord.Member
        The Member instance representing the state after the changes.

    Returns
    -------
    list
        A list of strings showing the changes made in the Member instances.
    """

    keys = ["name", "display_name", "global_name"]

    return [
        f"{key}: {getattr(before, key)} -> {getattr(after, key)}"
        for key in keys
        if getattr(before, key) != getattr(after, key)
    ]

extract_guild_attrs(guild: discord.Guild) -> dict[str, Any]

Extracts relevant attributes from a discord.Guild and returns them as a dictionary.

Parameters:

Name Type Description Default
guild Guild

The discord.Guild instance to extract attributes from.

required

Returns:

Type Description
dict

A dictionary containing the extracted attributes of the guild.

Source code in tux/utils/functions.py
Python
def extract_guild_attrs(guild: discord.Guild) -> dict[str, Any]:
    """
    Extracts relevant attributes from a discord.Guild and returns them as a dictionary.

    Parameters
    ----------
    guild : discord.Guild
        The discord.Guild instance to extract attributes from.

    Returns
    -------
    dict
        A dictionary containing the extracted attributes of the guild.
    """

    return {
        "name": guild.name,
        "description": guild.description,
        "member_count": guild.member_count,
        "verification_level": str(guild.verification_level),
        "system_channel": guild.system_channel,
    }

extract_member_attrs(member: discord.Member) -> dict[str, Any]

Extract relevant attributes from a member for comparison.

Parameters:

Name Type Description Default
member Member

The member to extract attributes from.

required

Returns:

Type Description
dict[str, Any]

The member's relevant attributes as a dictionary.

Source code in tux/utils/functions.py
Python
def extract_member_attrs(member: discord.Member) -> dict[str, Any]:
    """
    Extract relevant attributes from a member for comparison.

    Parameters
    ----------
    member : discord.Member
        The member to extract attributes from.

    Returns
    -------
    dict[str, Any]
        The member's relevant attributes as a dictionary.
    """
    return {
        "id": member.id,
        "display_name": member.display_name,
        "display_avatar": member.display_avatar.url,
        "nick": member.nick,
        "timed_out_until": member.timed_out_until,
        "guild_avatar": member.guild_avatar.url if member.guild_avatar else None,
        "pending": member.pending,
        "premium_since": member.premium_since,
        "flags": member.flags.value if member.flags else None,
        "guild_permissions": member.guild_permissions.value if member.guild_permissions else None,
        "roles": [role.name for role in member.roles],
    }

is_optional_param(param: commands.Parameter) -> bool

Check if a parameter is optional.

Parameters:

Name Type Description Default
param Parameter

The parameter to check.

required

Returns:

Type Description
bool

True if the parameter is optional, False otherwise.

Source code in tux/utils/functions.py
Python
def is_optional_param(param: commands.Parameter) -> bool:
    """
    Check if a parameter is optional.

    Parameters
    ----------
    param : commands.Parameter
        The parameter to check.

    Returns
    -------
    bool
        True if the parameter is optional, False otherwise.
    """

    if param.default is not inspect.Parameter.empty:
        return True

    param_type = param.annotation

    if get_origin(param_type) is Union:
        return type(None) in get_args(param_type)

    return False

get_matching_string(arg: str) -> str

Matches the given argument to a specific string based on common usage.

Parameters:

Name Type Description Default
arg str

The argument to match.

required

Returns:

Type Description
str

The matching string, or None if no match is found.

Source code in tux/utils/functions.py
Python
def get_matching_string(arg: str) -> str:
    """
    Matches the given argument to a specific string based on common usage.

    Parameters
    ----------
    arg : str
        The argument to match.

    Returns
    -------
    str
        The matching string, or None if no match is found.
    """
    match arg:
        case "user" | "target" | "member" | "username":
            return "@member"
        case "search_term":
            return "CIA"
        case "channel":
            return "#general"
        case "comic_id":
            return "1337"
        case _:
            return arg

generate_usage(command: commands.Command[Any, Any, Any], flag_converter: type[commands.FlagConverter] | None = None) -> str

Generate the usage string for a command.

Parameters:

Name Type Description Default
command Command[Any, Any, Any]

The command to generate the usage string for.

required
flag_converter type[FlagConverter] | None

The flag converter to use.

None

Returns:

Type Description
str

The usage string for the command.

Source code in tux/utils/functions.py
Python
def generate_usage(
    command: commands.Command[Any, Any, Any],
    flag_converter: type[commands.FlagConverter] | None = None,
) -> str:
    """
    Generate the usage string for a command.

    Parameters
    ----------
    command : commands.Command[Any, Any, Any]
        The command to generate the usage string for.
    flag_converter : type[commands.FlagConverter] | None
        The flag converter to use.

    Returns
    -------
    str
        The usage string for the command.
    """

    command_name = command.qualified_name
    usage = f"{command_name}"

    parameters: dict[str, commands.Parameter] = command.clean_params

    flag_prefix = getattr(flag_converter, "__commands_flag_prefix__", "-")
    flags: dict[str, commands.Flag] = flag_converter.get_flags() if flag_converter else {}

    # Handle regular parameters first
    for param_name, param in parameters.items():
        if param_name in {"ctx", "flags"}:
            continue

        is_required = not is_optional_param(param)
        matching_string = get_matching_string(param_name)

        if matching_string == param_name and is_required:
            matching_string = f"<{param_name}>"

        usage += f" {matching_string}" if is_required else f" [{matching_string}]"

    # Find positional flag if it exists
    positional_flag = None
    required_flags: list[str] = []
    optional_flags: list[str] = []

    for flag_name, flag_obj in flags.items():
        if getattr(flag_obj, "positional", False):
            positional_flag = flag_name
            continue

        flag = f"{flag_prefix}{flag_name}"

        if flag_obj.required:
            required_flags.append(flag)
        else:
            optional_flags.append(flag)

    # Add positional flag in its correct position
    if positional_flag:
        usage += f" [{positional_flag}]"

    # Add required flags
    for flag in required_flags:
        usage += f" {flag}"

    # Add optional flags
    if optional_flags:
        usage += f" [{' | '.join(optional_flags)}]"

    return usage