Skip to content

Utils

These are a collection of utility methods that are used throughout the library. There are several more that are not included here, as they are reserved for internal use.

Info

All of these methods can be imported from the interactions.client.utils namespace

Formatting

These methods are to help you format strings for messages.

ansi_block(text)

Formats text for discord message as code block that allows for arbitrary coloring and formatting

Source code in interactions/client/utils/formatting.py
79
80
81
def ansi_block(text: str) -> str:
    """Formats text for discord message as code block that allows for arbitrary coloring and formatting"""
    return code_block(text, "ansi")

ansi_format(style=None, color=None, background=None)

Gives format prefix for ansi code block with selected styles

Source code in interactions/client/utils/formatting.py
118
119
120
121
122
123
124
125
def ansi_format(
    style: Optional[AnsiStyles] = None,
    color: Optional[AnsiColors] = None,
    background: Optional[AnsiBackgrounds] = None,
) -> str:
    """Gives format prefix for ansi code block with selected styles"""
    text_style = ";".join(str(_style.value) for _style in (style, color, background) if _style)
    return f"[{text_style}m"

ansi_styled(text, style=None, color=None, background=None)

Formats text for ansi code block with selected styles

Source code in interactions/client/utils/formatting.py
131
132
133
134
135
136
137
138
def ansi_styled(
    text: str,
    style: Optional[AnsiStyles] = None,
    color: Optional[AnsiColors] = None,
    background: Optional[AnsiBackgrounds] = None,
) -> str:
    """Formats text for ansi code block with selected styles"""
    return f"{ansi_format(style, color, background)}{text}{ansi_escape}"

bold(text)

Formats text for discord message as bold

Source code in interactions/client/utils/formatting.py
29
30
31
def bold(text: str) -> str:
    """Formats text for discord message as bold"""
    return f"**{text}**"

code_block(text, language)

Formats text for discord message as code block

Source code in interactions/client/utils/formatting.py
74
75
76
def code_block(text: str, language: Optional[str]) -> str:
    """Formats text for discord message as code block"""
    return f"```{language or ''}\n" f"{text}" f"```"

inline_code(text)

Formats text for discord message as inline code

Source code in interactions/client/utils/formatting.py
69
70
71
def inline_code(text: str) -> str:
    """Formats text for discord message as inline code"""
    return f"`{text}`"

italic(text)

Formats text for discord message as italic

Source code in interactions/client/utils/formatting.py
34
35
36
def italic(text: str) -> str:
    """Formats text for discord message as italic"""
    return f"*{text}*"

Makes a clickable link inside Embed object

Source code in interactions/client/utils/formatting.py
59
60
61
def link_in_embed(text: str, url: str) -> str:
    """Makes a clickable link inside Embed object"""
    return f"[{text}]({url})"

Makes link in discord message display without embedded website preview

Source code in interactions/client/utils/formatting.py
54
55
56
def no_embed_link(url: str) -> str:
    """Makes link in discord message display without embedded website preview"""
    return f"<{url}>"

quote_line(line)

Formats a text line for discord message as quote

Source code in interactions/client/utils/formatting.py
64
65
66
def quote_line(line: str) -> str:
    """Formats a text line for discord message as quote"""
    return f"> {line}"

spoiler(text)

Formats text for discord message as spoiler

Source code in interactions/client/utils/formatting.py
49
50
51
def spoiler(text: str) -> str:
    """Formats text for discord message as spoiler"""
    return f"||{text}||"

strikethrough(text)

Formats text for discord message as strikethrough

Source code in interactions/client/utils/formatting.py
44
45
46
def strikethrough(text: str) -> str:
    """Formats text for discord message as strikethrough"""
    return f"~~{text}~~"

underline(text)

Formats text for discord message as underlined

Source code in interactions/client/utils/formatting.py
39
40
41
def underline(text: str) -> str:
    """Formats text for discord message as underlined"""
    return f"__{text}__"

Attrs Utilities

These methods are intended to be used with attrs dataclasses

list_converter(converter)

Converts a list of values to a list of converted values

Source code in interactions/client/utils/attr_converters.py
38
39
40
41
42
43
44
45
46
47
48
def list_converter(converter) -> Callable[[list], list]:
    """Converts a list of values to a list of converted values"""

    def convert_action(value: Union[list, Any]) -> list:
        if not isinstance(value, list):
            """If only one single item was passed (without a list), then we only convert that one item instead of throwing an exception."""
            return [converter(value)]

        return [converter(element) for element in value]

    return convert_action

optional(converter)

A modified version of attrs optional converter that supports both None and MISSING

Type annotations will be inferred from the wrapped converter's, if it has any.

Parameters:

Name Type Description Default
converter typing.Callable

The convertor that is used for the non-None or MISSING

required
Source code in interactions/client/utils/attr_converters.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def optional(converter: typing.Callable) -> typing.Any:
    """
    A modified version of attrs optional converter that supports both `None` and `MISSING`

    Type annotations will be inferred from the wrapped converter's, if it
    has any.

    Args:
        converter: The convertor that is used for the non-None or MISSING

    """

    def optional_converter(val) -> typing.Any:
        return val if val is None or val is MISSING else converter(val)

    sig = None
    try:
        sig = inspect.signature(converter)
    except (ValueError, TypeError):  # inspect failed
        pass
    if sig:
        params = list(sig.parameters.values())
        if params and params[0].annotation is not inspect.Parameter.empty:
            optional_converter.__annotations__["val"] = typing.Optional[params[0].annotation]
        if sig.return_annotation is not inspect.Signature.empty:
            optional_converter.__annotations__["return"] = typing.Optional[sig.return_annotation]

    return optional_converter

timestamp_converter(value)

Converts a datetime, int, float, or str to a Timestamp object

Parameters:

Name Type Description Default
value Union[datetime, int, float, str]

The time value to convert

required

Returns:

Type Description
Timestamp

A Timestamp object

Source code in interactions/client/utils/attr_converters.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def timestamp_converter(value: Union[datetime, int, float, str]) -> Timestamp:
    """
    Converts a datetime, int, float, or str to a Timestamp object

    Args:
        value: The time value to convert

    Returns:
        A Timestamp object

    """
    try:
        if isinstance(value, str):
            return Timestamp.fromisoformat(value)
        if isinstance(value, (float, int)):
            return Timestamp.fromtimestamp(float(value))
        if isinstance(value, datetime):
            return Timestamp.fromdatetime(value)
        raise TypeError("Timestamp must be one of: datetime, int, float, ISO8601 str")
    except ValueError as e:
        interactions.const.get_logger().warning("Failed to convert timestamp", exc_info=e)
        # Should only happen if the timestamp is something stupid like 269533-01-01T00:00 - in which case we just return MISSING
        return MISSING

attrs_validator(validator, skip_fields=None)

Sets a validator to all fields of an attrs-dataclass.

Parameters:

Name Type Description Default
validator Callable

The validator to set

required
skip_fields list[str] | None

A list of fields to skip adding the validator to

None

Returns:

Type Description
Callable[[Any, list[Attribute]], list[Attribute]]

The new fields for the attrs class

Source code in interactions/client/utils/attr_utils.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def attrs_validator(
    validator: Callable, skip_fields: list[str] | None = None
) -> Callable[[Any, list[Attribute]], list[Attribute]]:
    """
    Sets a validator to all fields of an attrs-dataclass.

    Args:
        validator: The validator to set
        skip_fields: A list of fields to skip adding the validator to

    Returns:
        The new fields for the attrs class

    """

    def operation(_, attributes: list[Attribute]) -> list[Attribute]:
        new_attrs = []
        for attr in attributes:
            if skip_fields and attr.name in skip_fields:
                new_attrs.append(attr)
            else:
                new_attrs.append(attr.evolve(validator=validator))
        return new_attrs

    return operation

docs(doc_string)

Makes it easier to quickly type attr documentation.

Parameters:

Name Type Description Default
doc_string str

The documentation string.

required

Returns:

Type Description
dict[str, str]

The processed metadata dict

Source code in interactions/client/utils/attr_utils.py
26
27
28
29
30
31
32
33
34
35
36
37
def docs(doc_string: str) -> Dict[str, str]:
    """
    Makes it easier to quickly type attr documentation.

    Args:
        doc_string: The documentation string.

    Returns:
        The processed metadata dict

    """
    return {"docs": doc_string}

str_validator(cls, attribute, value)

Validates that the value is a string. Helps convert and ives a warning if it isn't.

Parameters:

Name Type Description Default
cls Any

The instance of the class.

required
attribute attrs.Attribute

The attr attribute being validated.

required
value Any

The value being validated.

required
Source code in interactions/client/utils/attr_utils.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def str_validator(cls: Any, attribute: attrs.Attribute, value: Any) -> None:
    """
    Validates that the value is a string. Helps convert and ives a warning if it isn't.

    Args:
        cls: The instance of the class.
        attribute: The attr attribute being validated.
        value: The value being validated.

    """
    if not isinstance(value, str):
        if value is MISSING:
            return
        setattr(cls, attribute.name, str(value))
        get_logger().warning(
            f"Value of {attribute.name} has been automatically converted to a string. Please use strings in future.\n"
            "Note: Discord will always return value as a string"
        )

Misc Utilities

Uncategorized utilities, might be useful, might not.

disable_components(*components)

Disables all components in a list of components.

Source code in interactions/client/utils/misc_utils.py
242
243
244
245
246
247
248
249
def disable_components(*components: "BaseComponent") -> list["BaseComponent"]:
    """Disables all components in a list of components."""
    for component in components:
        if component.type == ComponentType.ACTION_ROW:
            disable_components(*component.components)
        else:
            component.disabled = True
    return list(components)

escape_mentions(content)

Escape mentions that could ping someone in a string.

Note

This does not escape channel mentions as they do not ping anybody

Parameters:

Name Type Description Default
content str

The string to escape

required

Returns:

Type Description
str

Processed string

Source code in interactions/client/utils/misc_utils.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def escape_mentions(content: str) -> str:
    """
    Escape mentions that could ping someone in a string.

    !!! note
        This does not escape channel mentions as they do not ping anybody

    Args:
        content: The string to escape

    Returns:
        Processed string

    """
    return mention_reg.sub("@\u200b\\1", content)

find(predicate, sequence)

Find the first element in a sequence that matches the predicate.

Example Usage:
1
member = find(lambda m: m.name == "UserName", guild.members)

Parameters:

Name Type Description Default
predicate Callable[[T], bool]

A callable that returns a boolean value

required
sequence Iterable[T]

A sequence to be searched

required

Returns:

Type Description
Optional[T]

A match if found, otherwise None

Source code in interactions/client/utils/misc_utils.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def find(predicate: Callable[[T], bool], sequence: Iterable[T]) -> Optional[T]:
    """
    Find the first element in a sequence that matches the predicate.

    ??? Hint "Example Usage:"
        ```python
        member = find(lambda m: m.name == "UserName", guild.members)
        ```
    Args:
        predicate: A callable that returns a boolean value
        sequence: A sequence to be searched

    Returns:
        A match if found, otherwise None

    """
    return next((el for el in sequence if predicate(el)), None)

find_all(predicate, sequence)

Find all elements in a sequence that match the predicate.

Example Usage:
1
members = find_all(lambda m: m.name == "UserName", guild.members)

Parameters:

Name Type Description Default
predicate Callable[[T], bool]

A callable that returns a boolean value

required
sequence Iterable[T]

A sequence to be searched

required

Returns:

Type Description
List[T]

A list of matches

Source code in interactions/client/utils/misc_utils.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def find_all(predicate: Callable[[T], bool], sequence: Iterable[T]) -> List[T]:
    """
    Find all elements in a sequence that match the predicate.

    ??? Hint "Example Usage:"
        ```python
        members = find_all(lambda m: m.name == "UserName", guild.members)
        ```
    Args:
        predicate: A callable that returns a boolean value
        sequence: A sequence to be searched

    Returns:
        A list of matches

    """
    return [el for el in sequence if predicate(el)]

get(sequence, **kwargs)

Find the first element in a sequence that matches all attrs.

Example Usage:
1
channel = get(guild.channels, nsfw=False, category="General")

Parameters:

Name Type Description Default
sequence Iterable[T]

A sequence to be searched

required
**kwargs Any

Keyword arguments to search the sequence for

{}

Returns:

Type Description
Optional[T]

A match if found, otherwise None

Source code in interactions/client/utils/misc_utils.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def get(sequence: Iterable[T], **kwargs: Any) -> Optional[T]:
    """
    Find the first element in a sequence that matches all attrs.

    ??? Hint "Example Usage:"
        ```python
        channel = get(guild.channels, nsfw=False, category="General")
        ```

    Args:
        sequence: A sequence to be searched
        **kwargs: Keyword arguments to search the sequence for

    Returns:
        A match if found, otherwise None

    """
    if not kwargs:
        return sequence[0]

    for el in sequence:
        if any(not hasattr(el, attr) for attr in kwargs):
            continue
        if all(getattr(el, attr) == value for attr, value in kwargs.items()):
            return el
    return None

get_all(sequence, **kwargs)

Find all elements in a sequence that match all attrs.

Example Usage:
1
channels = get_all(guild.channels, nsfw=False, category="General")

Parameters:

Name Type Description Default
sequence Iterable[T]

A sequence to be searched

required
**kwargs Any

Keyword arguments to search the sequence for

{}

Returns:

Type Description
List[T]

A list of matches

Source code in interactions/client/utils/misc_utils.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def get_all(sequence: Iterable[T], **kwargs: Any) -> List[T]:
    """
    Find all elements in a sequence that match all attrs.

    ??? Hint "Example Usage:"
        ```python
        channels = get_all(guild.channels, nsfw=False, category="General")
        ```

    Args:
        sequence: A sequence to be searched
        **kwargs: Keyword arguments to search the sequence for

    Returns:
        A list of matches

    """
    if not kwargs:
        return sequence

    matches = []
    for el in sequence:
        if any(not hasattr(el, attr) for attr in kwargs):
            continue
        if all(getattr(el, attr) == value for attr, value in kwargs.items()):
            matches.append(el)
    return matches

get_event_name(event) cached

Get the event name smartly from an event class or string name.

Parameters:

Name Type Description Default
event Union[str, BaseEvent]

The event to parse the name of

required

Returns:

Type Description
str

The event name

Source code in interactions/client/utils/misc_utils.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
@functools.lru_cache(maxsize=50)
def get_event_name(event: Union[str, "events.BaseEvent"]) -> str:
    """
    Get the event name smartly from an event class or string name.

    Args:
        event: The event to parse the name of

    Returns:
        The event name

    """
    name = event

    if inspect.isclass(name) and issubclass(name, events.BaseEvent):
        name = name.__name__

    # convert CamelCase to snake_case
    name = camel_to_snake.sub(r"_\1", name).lower()
    # remove any leading underscores
    name = name.lstrip("_")
    # remove any `on_` prefixes
    name = name.removeprefix("on_")

    return name

get_object_name(x)

Gets the name of virtually any object.

Parameters:

Name Type Description Default
x Any

The object to get the name of.

required

Returns:

Name Type Description
str str

The name of the object.

Source code in interactions/client/utils/misc_utils.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
def get_object_name(x: Any) -> str:
    """
    Gets the name of virtually any object.

    Args:
        x (Any): The object to get the name of.

    Returns:
        str: The name of the object.

    """
    try:
        return x.__name__
    except AttributeError:
        return repr(x) if hasattr(x, "__origin__") else x.__class__.__name__

get_parameters(callback)

Gets all the parameters of a callback.

Parameters:

Name Type Description Default
callback Callable

The callback to get the parameters of

required

Returns:

Type Description
dict[str, inspect.Parameter]

A dictionary of parameters

Source code in interactions/client/utils/misc_utils.py
177
178
179
180
181
182
183
184
185
186
187
188
def get_parameters(callback: Callable) -> dict[str, inspect.Parameter]:
    """
    Gets all the parameters of a callback.

    Args:
        callback: The callback to get the parameters of

    Returns:
        A dictionary of parameters

    """
    return {p.name: p for p in inspect.signature(callback).parameters.values()}

maybe_coroutine(func, *args, **kwargs) async

Allows running either a coroutine or a function.

Source code in interactions/client/utils/misc_utils.py
235
236
237
238
239
async def maybe_coroutine(func: Callable, *args, **kwargs) -> Any:
    """Allows running either a coroutine or a function."""
    if inspect.iscoroutinefunction(func):
        return await func(*args, **kwargs)
    return func(*args, **kwargs)

nulled_boolean_get(data, key)

Gets a boolean value from a dictionary, but treats None as True.

Parameters:

Name Type Description Default
data dict[str, Any]

The dictionary to get the value from

required
key str

The key to get the value from

required

Returns:

Type Description
bool

The boolean value of the key

Source code in interactions/client/utils/misc_utils.py
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
def nulled_boolean_get(data: dict[str, Any], key: str) -> bool:
    """
    Gets a boolean value from a dictionary, but treats None as True.

    Args:
        data: The dictionary to get the value from
        key: The key to get the value from

    Returns:
        The boolean value of the key

    """
    # discord tags are weird, when they are None they are True, when they are True they are True and when they are False they are False
    if key in data:
        return True if data[key] is None else bool(data[key])
    return False

wrap_partial(obj, cls)

🎁 Wraps a commands callback objects into partials.

Note

This is used internally, you shouldn't need to use this function

Parameters:

Name Type Description Default
obj Any

The command object to process

required
cls Any

The class to use in partials

required

Returns:

Type Description
Callable

The original command object with its callback methods wrapped

Source code in interactions/client/utils/misc_utils.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def wrap_partial(obj: Any, cls: Any) -> Callable:
    """
    🎁 Wraps a commands callback objects into partials.

    !!! note
        This is used internally, you shouldn't need to use this function

    Args:
        obj: The command object to process
        cls: The class to use in partials

    Returns:
        The original command object with its callback methods wrapped

    """
    if obj.callback is None or isinstance(obj.callback, functools.partial):
        return obj
    if "_no_wrap" not in getattr(obj.callback, "__name__", ""):
        obj.callback = functools.partial(obj.callback, cls)

    if getattr(obj, "error_callback", None):
        obj.error_callback = functools.partial(obj.error_callback, cls)
    if getattr(obj, "pre_run_callback", None):
        obj.pre_run_callback = functools.partial(obj.pre_run_callback, cls)
    if getattr(obj, "post_run_callback", None):
        obj.post_run_callback = functools.partial(obj.post_run_callback, cls)
    if getattr(obj, "autocomplete_callbacks", None):
        obj.autocomplete_callbacks = {k: functools.partial(v, cls) for k, v in obj.autocomplete_callbacks.items()}
    if getattr(obj, "subcommands", None):
        obj.subcommands = {k: wrap_partial(v, cls) for k, v in obj.subcommands.items()}

    return obj