mirror of
https://github.com/wlinator/luminara.git
synced 2024-10-02 20:43:12 +00:00
199 lines
6.1 KiB
Python
199 lines
6.1 KiB
Python
import inspect
|
|
import textwrap
|
|
from typing import Any
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
from pytimeparse import parse # type: ignore
|
|
|
|
from lib import exceptions
|
|
from lib.client import Luminara
|
|
from lib.const import CONST
|
|
from services.config_service import GuildConfig
|
|
|
|
|
|
def template(text: str, username: str, level: int | None = None) -> str:
|
|
"""
|
|
Replaces placeholders in the given text with actual values.
|
|
|
|
Args:
|
|
text (str): The template text containing placeholders.
|
|
username (str): The username to replace the "{user}" placeholder.
|
|
level (int | None, optional): The level to replace the "{level}" placeholder. Defaults to None.
|
|
|
|
Returns:
|
|
str: The formatted text with placeholders replaced by actual values.
|
|
"""
|
|
replacements: dict[str, str] = {
|
|
"{user}": username,
|
|
"{level}": str(level) if level else "",
|
|
}
|
|
|
|
for placeholder, value in replacements.items():
|
|
text = text.replace(placeholder, value)
|
|
|
|
return text
|
|
|
|
|
|
def shorten(text: str, width: int = 200) -> str:
|
|
"""
|
|
Shortens the input text to the specified width by adding a placeholder at the end if the text exceeds the width.
|
|
|
|
Args:
|
|
text (str): The text to be shortened.
|
|
width (int): The maximum width of the shortened text (default is 200).
|
|
|
|
Returns:
|
|
str: The shortened text.
|
|
|
|
Examples:
|
|
shortened_text = shorten("Lorem ipsum dolor sit amet", 10)
|
|
"""
|
|
return textwrap.shorten(text, width=width, placeholder="...")
|
|
|
|
|
|
def format_case_number(case_number: int) -> str:
|
|
"""
|
|
Formats a case number as a string with leading zeros if necessary.
|
|
|
|
Args:
|
|
case_number (int): The case number to format.
|
|
|
|
Returns:
|
|
str: The formatted case number as a string.
|
|
If the case number is less than 1000, it will be padded with leading zeros to three digits.
|
|
If the case number is 1000 or greater, it will be returned as a regular string.
|
|
|
|
Examples:
|
|
>>> format_case_number(1)
|
|
'001'
|
|
>>> format_case_number(42)
|
|
'042'
|
|
>>> format_case_number(999)
|
|
'999'
|
|
>>> format_case_number(1000)
|
|
'1000'
|
|
"""
|
|
return f"{case_number:03d}" if case_number < 1000 else str(case_number)
|
|
|
|
|
|
def get_prefix(ctx: commands.Context[Luminara]) -> str:
|
|
"""
|
|
Attempts to retrieve the prefix for the given guild context.
|
|
|
|
Args:
|
|
ctx (discord.ext.commands.Context): The context of the command invocation.
|
|
|
|
Returns:
|
|
str: The prefix for the guild. Defaults to "." if the guild or prefix is not found.
|
|
"""
|
|
try:
|
|
return GuildConfig.get_prefix(ctx.guild.id if ctx.guild else 0)
|
|
except (AttributeError, TypeError):
|
|
return "."
|
|
|
|
|
|
def get_invoked_name(ctx: commands.Context[Luminara]) -> str | None:
|
|
"""
|
|
Attempts to get the alias of the command used. If the user used a SlashCommand, return the command name.
|
|
|
|
Args:
|
|
ctx (discord.ext.commands.Context): The context of the command invocation.
|
|
|
|
Returns:
|
|
str: The alias or name of the invoked command.
|
|
"""
|
|
try:
|
|
return ctx.invoked_with
|
|
|
|
except (discord.app_commands.CommandInvokeError, AttributeError):
|
|
return ctx.command.name if ctx.command else None
|
|
|
|
|
|
def format_duration_to_seconds(duration: str) -> int:
|
|
"""
|
|
Converts a duration string to seconds. If the input is just an integer, it returns that integer as seconds.
|
|
"""
|
|
if duration.isdigit():
|
|
return int(duration)
|
|
|
|
try:
|
|
parsed_duration: int = parse(duration) # type: ignore
|
|
return max(0, parsed_duration)
|
|
|
|
except Exception as e:
|
|
raise exceptions.LumiException(CONST.STRINGS["error_invalid_duration"].format(duration)) from e
|
|
|
|
|
|
def format_seconds_to_duration_string(seconds: int) -> str:
|
|
"""
|
|
Formats a duration in seconds to a human-readable string.
|
|
Returns seconds if shorter than a minute.
|
|
"""
|
|
if seconds < 60:
|
|
return f"{seconds}s"
|
|
|
|
days = seconds // 86400
|
|
hours = (seconds % 86400) // 3600
|
|
minutes = (seconds % 3600) // 60
|
|
|
|
if days > 0:
|
|
return f"{days}d{hours}h" if hours > 0 else f"{days}d"
|
|
if hours > 0:
|
|
return f"{hours}h{minutes}m" if minutes > 0 else f"{hours}h"
|
|
|
|
return f"{minutes}m"
|
|
|
|
|
|
def generate_usage(
|
|
command: commands.Command[Any, Any, Any],
|
|
flag_converter: type[commands.FlagConverter] | None = None,
|
|
) -> str:
|
|
"""
|
|
Generate a usage string for a command with flags.
|
|
Credit to https://github.com/allthingslinux/tux (thanks kaizen ;p)
|
|
|
|
Parameters
|
|
----------
|
|
command : commands.Command
|
|
The command for which to generate the usage string.
|
|
flag_converter : type[commands.FlagConverter]
|
|
The flag converter class for the command.
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
The usage string for the command. Example: "ban [target] -[reason] -<silent>"
|
|
"""
|
|
|
|
# Get the name of the command
|
|
command_name = command.qualified_name
|
|
|
|
# Start the usage string with the command name
|
|
usage = f"{command_name}"
|
|
|
|
# Get the parameters of the command (excluding the `ctx` and `flags` parameters)
|
|
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 {}
|
|
|
|
# Add non-flag arguments to the usage string
|
|
for param_name, param in parameters.items():
|
|
# Ignore these parameters
|
|
if param_name in ["ctx", "flags"]:
|
|
continue
|
|
# Determine if the parameter is required
|
|
is_required = param.default == inspect.Parameter.empty
|
|
# Add the parameter to the usage string with required or optional wrapping
|
|
usage += f" <{param_name}>" if is_required else f" [{param_name}]"
|
|
|
|
# Add flag arguments to the usage string
|
|
for flag_name, flag_obj in flags.items():
|
|
# Determine if the flag is required or optional
|
|
if flag_obj.required:
|
|
usage += f" {flag_prefix}<{flag_name}>"
|
|
else:
|
|
usage += f" {flag_prefix}[{flag_name}]"
|
|
|
|
return usage
|