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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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