Back to top

hikari.emojis

Application and entities that are used to describe emojis on Discord.

View Source
# -*- coding: utf-8 -*-
# cython: language_level=3
# Copyright (c) 2020 Nekokatt
# Copyright (c) 2021-present davfsa
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Application and entities that are used to describe emojis on Discord."""

from __future__ import annotations

__all__: typing.Sequence[str] = ("Emoji", "UnicodeEmoji", "CustomEmoji", "KnownCustomEmoji")

import abc
import re
import typing

import attr

from hikari import files
from hikari import snowflakes
from hikari import urls
from hikari.internal import attr_extensions
from hikari.internal import routes

# import unicodedata


if typing.TYPE_CHECKING:
    from hikari import traits
    from hikari import users

_TWEMOJI_PNG_BASE_URL: typing.Final[str] = "https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/"
_CUSTOM_EMOJI_REGEX: typing.Final[typing.Pattern[str]] = re.compile(r"<(?P<flags>[^:]*):(?P<name>[^:]*):(?P<id>\d+)>")


class Emoji(files.WebResource, abc.ABC):
    """Base class for all emojis.

    Any emoji implementation supports being used as a
    `hikari.files.Resource` when uploading an attachment to the API.
    This is achieved in the same way as using a
    `hikari.files.WebResource` would achieve this.
    """

    __slots__: typing.Sequence[str] = ()

    @property
    @abc.abstractmethod
    def name(self) -> str:
        """Return the generic name/representation for this emoji."""

    @property
    @abc.abstractmethod
    def url(self) -> str:
        """URL of the emoji image to display in clients."""

    @property
    @abc.abstractmethod
    def url_name(self) -> str:
        """Name of the part of the emoji to use in requests."""

    @property
    @abc.abstractmethod
    def mention(self) -> str:
        """Mention string to use to mention the emoji with."""

    @classmethod
    def parse(cls, string: str, /) -> Emoji:
        """Parse a given string into an emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        Emoji
            The parsed emoji object. This will be a `CustomEmoji` if a custom
            emoji mention, or a `UnicodeEmoji` otherwise.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if string.startswith("<") and string.endswith(">"):
            return CustomEmoji.parse(string)
        return UnicodeEmoji.parse(string)


class UnicodeEmoji(str, Emoji):
    """Represents a unicode emoji.

    .. warning::
        A word of warning if you try to upload this emoji as a file attachment.

        While this emoji type can be used to upload the Twemoji representations
        of this emoji as a PNG, this is NOT foolproof. The mapping between
        Discord's implementation and official Twemoji bindings is very flaky.
        Responsible implementations relying on this behaviour will be
        implemented to expect this behaviour in the form of
        `hikari.errors.NotFoundError` exceptions being raised when a mismatch may
        occur. It is also likely that this will change in the future without
        notice, so you will likely be relying on flaky behaviour.

        If this is proven to be too unstable, this functionality will be
        removed in a future release after a deprecation period.
    """

    __slots__: typing.Sequence[str] = ()

    @property
    def name(self) -> str:
        """Return the code points which form the emoji."""
        return self

    @property
    @typing.final
    def url_name(self) -> str:
        return self

    @property
    def mention(self) -> str:
        return self

    @property
    @typing.final
    def codepoints(self) -> typing.Sequence[int]:
        """Integer codepoints that make up this emoji, as UTF-8."""
        return [ord(c) for c in self]

    @property
    def filename(self) -> str:
        """Filename to use if re-uploading this emoji's PNG."""
        codepoints = self.codepoints
        # It looks like the rule is to delete character #2 if the value
        # of this is 0xfe0f and the character is up to 4 characters long.
        # Other than that, the mapping is 1-to-1. There's a couple of outliers
        # to this 0xfe0f rule and they seem to follow a pattern where the
        # codepoint after 0xfe0f is 0x200D but this might be a coincidence and
        # if Discord start breaking this regularly I might need to ask for a
        # more permanent solution. This is provisionally provided. If we find it
        # breaks in other ways, I will just revoke this functionality in a
        # future update.
        if codepoints[1:2] == [0xFE0F] and len(codepoints) <= 4 and codepoints[2:3] != [0x200D]:
            codepoints = [codepoints[0], *codepoints[2:]]

        return "-".join(hex(c)[2:] for c in codepoints) + ".png"

    @property
    def url(self) -> str:
        """Get the URL of the PNG rendition of this emoji.

        This will use the official Twitter "twemoji" repository to fetch
        this information, as Discord only stores this in a hashed format
        that uses SVG files, which is not usually of any use.

        Since this uses "twemoji" directly, the emojis may not directly
        match what is on Discord if Discord have failed to keep their emoji
        packs up-to-date with this repository.

        Example
        -------
        ```py
        >>> emoji = hikari.UnicodeEmoji("\N{OK HAND SIGN}")
        >>> emoji.url
        'https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f44c.png'
        ```
        """
        return _TWEMOJI_PNG_BASE_URL + self.filename

    # @property
    # @typing.final
    # def unicode_names(self) -> typing.Sequence[str]:
    #     """Get the unicode name of the emoji as a sequence.
    #
    #     This returns the name of each codepoint. If only one codepoint exists,
    #     then this will only have one item in the resulting sequence.
    #     """
    #     return [unicodedata.name(c) for c in self]

    @property
    @typing.final
    def unicode_escape(self) -> str:
        """Get the unicode escape string for this emoji."""
        return bytes(self, "unicode_escape").decode("utf-8")

    @classmethod
    @typing.final
    def parse_codepoints(cls, codepoint: int, *codepoints: int) -> UnicodeEmoji:
        """Create a unicode emoji from one or more UTF-32 codepoints."""
        return cls("".join(map(chr, (codepoint, *codepoints))))

    @classmethod
    @typing.final
    def parse_unicode_escape(cls, escape: str) -> UnicodeEmoji:
        """Create a unicode emoji from a unicode escape string."""
        return cls(escape.encode("utf-8"), "unicode_escape")

    @classmethod
    @typing.final
    def parse(cls, string: str, /) -> UnicodeEmoji:
        """Parse a given string into a unicode emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        UnicodeEmoji
            The parsed UnicodeEmoji object.
        """
        # TODO: Re-add validity
        # Ensure validity.
        # for i, codepoint in enumerate(string, start=1):
        #     unicodedata.name(codepoint)

        return cls(string)


@attr_extensions.with_copy
@attr.define(hash=True, kw_only=True, weakref_slot=False)
class CustomEmoji(snowflakes.Unique, Emoji):
    """Represents a custom emoji.

    This is a custom emoji that is from a guild you might not be part of.

    All CustomEmoji objects and their derivatives act as valid
    `hikari.files.Resource` objects. This means you can use them as a
    file when sending a message.

        >>> emojis = await bot.rest.fetch_guild_emojis(12345)
        >>> picks = random.choices(emojis, 5)
        >>> await event.respond(files=picks)

    .. warning::
        Discord will not provide information on whether these emojis are
        animated or not when a reaction is removed and an event is fired. This
        is problematic if you need to try and determine the emoji that was
        removed. The side effect of this means that mentions for animated emojis
        will not be correct.

        This will not be changed as stated here:
        <https://github.com/discord/discord-api-docs/issues/1614#issuecomment-628548913>
    """

    id: snowflakes.Snowflake = attr.field(hash=True, repr=True)
    """The ID of this entity."""

    name: str = attr.field(eq=False, hash=False, repr=True)
    """The name of the emoji."""

    is_animated: bool = attr.field(eq=False, hash=False, repr=True)
    """Whether the emoji is animated."""

    def __str__(self) -> str:
        return self.mention

    @property
    def filename(self) -> str:
        return str(self.id) + (".gif" if self.is_animated else ".png")

    @property
    @typing.final
    def url_name(self) -> str:
        return f"{self.name}:{self.id}"

    @property
    @typing.final
    def mention(self) -> str:
        return f"<{'a' if self.is_animated else ''}:{self.url_name}>"

    @property
    @typing.final
    def url(self) -> str:
        ext = "gif" if self.is_animated else "png"

        return routes.CDN_CUSTOM_EMOJI.compile(urls.CDN_URL, emoji_id=self.id, file_format=ext)

    @classmethod
    def parse(cls, string: str, /) -> CustomEmoji:
        """Parse a given emoji mention string into a custom emoji object.

        Parameters
        ----------
        string : str
            The emoji mention to parse.

        Returns
        -------
        CustomEmoji
            The parsed emoji object.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if emoji_match := _CUSTOM_EMOJI_REGEX.match(string):
            return CustomEmoji(
                id=snowflakes.Snowflake(emoji_match.group("id")),
                name=emoji_match.group("name"),
                is_animated=emoji_match.group("flags").lower() == "a",
            )

        raise ValueError("Expected an emoji mention")


@attr.define(hash=True, kw_only=True, weakref_slot=False)
class KnownCustomEmoji(CustomEmoji):
    """Represents an emoji that is known from a guild the bot is in.

    This is a specialization of `CustomEmoji` that is from a guild that you
    _are_ part of. As a result, it contains a lot more information with it.
    """

    app: traits.RESTAware = attr.field(
        repr=False, eq=False, hash=False, metadata={attr_extensions.SKIP_DEEP_COPY: True}
    )
    """The client application that models may use for procedures."""

    guild_id: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=False)
    """The ID of the guild this emoji belongs to."""

    role_ids: typing.Sequence[snowflakes.Snowflake] = attr.field(eq=False, hash=False, repr=False)
    """The IDs of the roles that are whitelisted to use this emoji.

    If this is empty then any user can use this emoji regardless of their roles.
    """

    user: typing.Optional[users.User] = attr.field(eq=False, hash=False, repr=False)
    """The user that created the emoji.

    .. note::
        This will be `None` if you are missing the `MANAGE_EMOJIS_AND_STICKERS`
        permission in the server the emoji is from.
    """

    is_colons_required: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether this emoji must be wrapped in colons."""

    is_managed: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether the emoji is managed by an integration."""

    is_available: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether this emoji can currently be used.

    May be `False` due to a loss of Sever Boosts on the emoji's guild.
    """
#  
@attr_extensions.with_copy
@attr.define(hash=True, kw_only=True, weakref_slot=False)
class CustomEmoji(hikari.files.Resource[hikari.files.WebReader], abc.ABC):
View Source
@attr_extensions.with_copy
@attr.define(hash=True, kw_only=True, weakref_slot=False)
class CustomEmoji(snowflakes.Unique, Emoji):
    """Represents a custom emoji.

    This is a custom emoji that is from a guild you might not be part of.

    All CustomEmoji objects and their derivatives act as valid
    `hikari.files.Resource` objects. This means you can use them as a
    file when sending a message.

        >>> emojis = await bot.rest.fetch_guild_emojis(12345)
        >>> picks = random.choices(emojis, 5)
        >>> await event.respond(files=picks)

    .. warning::
        Discord will not provide information on whether these emojis are
        animated or not when a reaction is removed and an event is fired. This
        is problematic if you need to try and determine the emoji that was
        removed. The side effect of this means that mentions for animated emojis
        will not be correct.

        This will not be changed as stated here:
        <https://github.com/discord/discord-api-docs/issues/1614#issuecomment-628548913>
    """

    id: snowflakes.Snowflake = attr.field(hash=True, repr=True)
    """The ID of this entity."""

    name: str = attr.field(eq=False, hash=False, repr=True)
    """The name of the emoji."""

    is_animated: bool = attr.field(eq=False, hash=False, repr=True)
    """Whether the emoji is animated."""

    def __str__(self) -> str:
        return self.mention

    @property
    def filename(self) -> str:
        return str(self.id) + (".gif" if self.is_animated else ".png")

    @property
    @typing.final
    def url_name(self) -> str:
        return f"{self.name}:{self.id}"

    @property
    @typing.final
    def mention(self) -> str:
        return f"<{'a' if self.is_animated else ''}:{self.url_name}>"

    @property
    @typing.final
    def url(self) -> str:
        ext = "gif" if self.is_animated else "png"

        return routes.CDN_CUSTOM_EMOJI.compile(urls.CDN_URL, emoji_id=self.id, file_format=ext)

    @classmethod
    def parse(cls, string: str, /) -> CustomEmoji:
        """Parse a given emoji mention string into a custom emoji object.

        Parameters
        ----------
        string : str
            The emoji mention to parse.

        Returns
        -------
        CustomEmoji
            The parsed emoji object.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if emoji_match := _CUSTOM_EMOJI_REGEX.match(string):
            return CustomEmoji(
                id=snowflakes.Snowflake(emoji_match.group("id")),
                name=emoji_match.group("name"),
                is_animated=emoji_match.group("flags").lower() == "a",
            )

        raise ValueError("Expected an emoji mention")

Represents a custom emoji.

This is a custom emoji that is from a guild you might not be part of.

All CustomEmoji objects and their derivatives act as valid hikari.files.Resource objects. This means you can use them as a file when sending a message.

>>> emojis = await bot.rest.fetch_guild_emojis(12345)
>>> picks = random.choices(emojis, 5)
>>> await event.respond(files=picks)

Warning: Discord will not provide information on whether these emojis are animated or not when a reaction is removed and an event is fired. This is problematic if you need to try and determine the emoji that was removed. The side effect of this means that mentions for animated emojis will not be correct.

This will not be changed as stated here: https://github.com/discord/discord-api-docs/issues/1614#issuecomment-628548913

Variables and properties
#  created_at: datetime.datetime

When the object was created.

#  extension: Optional[str]

File extension, if there is one.

#  filename: str

Filename of the resource.

The ID of this entity.

#  is_animated: bool

Whether the emoji is animated.

#  mention: str

Mention string to use to mention the emoji with.

#  name: str

The name of the emoji.

#  url: str

URL of the emoji image to display in clients.

#  url_name: str

Name of the part of the emoji to use in requests.

Methods
#  def __init__(
   self,
   *,
   id: hikari.snowflakes.Snowflake,
   name: str,
   is_animated: bool
):
View Source
def __init__(self, *, id, name, is_animated):
    self.id = id
    self.name = name
    self.is_animated = is_animated

Method generated by attrs for class CustomEmoji.

#  
@classmethod
def parse(cls, string: str, /) -> hikari.emojis.CustomEmoji:
View Source
    @classmethod
    def parse(cls, string: str, /) -> CustomEmoji:
        """Parse a given emoji mention string into a custom emoji object.

        Parameters
        ----------
        string : str
            The emoji mention to parse.

        Returns
        -------
        CustomEmoji
            The parsed emoji object.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if emoji_match := _CUSTOM_EMOJI_REGEX.match(string):
            return CustomEmoji(
                id=snowflakes.Snowflake(emoji_match.group("id")),
                name=emoji_match.group("name"),
                is_animated=emoji_match.group("flags").lower() == "a",
            )

        raise ValueError("Expected an emoji mention")

Parse a given emoji mention string into a custom emoji object.

Parameters
  • string (str): The emoji mention to parse.
Returns
  • CustomEmoji: The parsed emoji object.
Raises
  • ValueError: If a mention is given that has an invalid format.
#  async def read(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None
) -> bytes:
View Source
    async def read(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
    ) -> bytes:
        """Read the entire resource at once into memory.

        ```py
        data = await resource.read(...)
        # ^-- This is a shortcut for the following --v
        async with resource.stream(...) as reader:
            data = await reader.read()
        ```

        .. warning::
            If you simply wish to re-upload this resource to Discord via
            any endpoint in Hikari, you should opt to just pass this
            resource object directly. This way, Hikari can perform byte
            inception, which significantly reduces the memory usage for
            your bot as it grows larger.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            The executor to run in for blocking operations.
            If `None`, then the default executor is used for the
            current event loop.

        Returns
        -------
        bytes
            The entire resource.
        """
        async with self.stream(executor=executor) as reader:
            return await reader.read()

Read the entire resource at once into memory.

data = await resource.read(...)
# ^-- This is a shortcut for the following --v
async with resource.stream(...) as reader:
    data = await reader.read()

Warning: If you simply wish to re-upload this resource to Discord via any endpoint in Hikari, you should opt to just pass this resource object directly. This way, Hikari can perform byte inception, which significantly reduces the memory usage for your bot as it grows larger.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): The executor to run in for blocking operations. If None, then the default executor is used for the current event loop.
Returns
  • bytes: The entire resource.
#  def stream(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None,
   head_only: bool = False
) -> hikari.files.AsyncReaderContextManager[hikari.files.WebReader]:
View Source
    def stream(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
        head_only: bool = False,
    ) -> AsyncReaderContextManager[WebReader]:
        """Start streaming the content into memory by downloading it.

        You can use this to fetch the entire resource, parts of the resource,
        or just to view any metadata that may be provided.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            Not used. Provided only to match the underlying interface.
        head_only : bool
            Defaults to `False`. If `True`, then the
            implementation may only retrieve HEAD information if supported.
            This currently only has any effect for web requests.

        Examples
        --------
        Downloading an entire resource at once into memory:
        ```py
        async with obj.stream() as stream:
            data = await stream.read()
        ```
        Checking the metadata:
        ```py
        async with obj.stream() as stream:
            mimetype = stream.mimetype

        if mimetype is None:
            ...
        elif mimetype not in whitelisted_mimetypes:
            ...
        else:
            ...
        ```
        Fetching the data-uri of a resource:
        ```py
        async with obj.stream() as stream:
            data_uri = await stream.data_uri()
        ```

        Returns
        -------
        AsyncReaderContextManager[WebReader]
            An async context manager that when entered, produces the
            data stream.

        Raises
        ------
        hikari.errors.BadRequestError
            If a 400 is returned.
        hikari.errors.UnauthorizedError
            If a 401 is returned.
        hikari.errors.ForbiddenError
            If a 403 is returned.
        hikari.errors.NotFoundError
            If a 404 is returned.
        hikari.errors.ClientHTTPResponseError
            If any other 4xx is returned.
        hikari.errors.InternalServerError
            If any other 5xx is returned.
        hikari.errors.HTTPResponseError
            If any other unexpected response code is returned.
        """
        return _WebReaderAsyncReaderContextManagerImpl(self, head_only)

Start streaming the content into memory by downloading it.

You can use this to fetch the entire resource, parts of the resource, or just to view any metadata that may be provided.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): Not used. Provided only to match the underlying interface.
  • head_only (bool): Defaults to False. If True, then the implementation may only retrieve HEAD information if supported. This currently only has any effect for web requests.
Examples

Downloading an entire resource at once into memory:

async with obj.stream() as stream:
    data = await stream.read()

Checking the metadata:

async with obj.stream() as stream:
    mimetype = stream.mimetype

if mimetype is None:
    ...
elif mimetype not in whitelisted_mimetypes:
    ...
else:
    ...

Fetching the data-uri of a resource:

async with obj.stream() as stream:
    data_uri = await stream.data_uri()
Returns
  • AsyncReaderContextManager[WebReader]: An async context manager that when entered, produces the data stream.
Raises
View Source
class Emoji(files.WebResource, abc.ABC):
    """Base class for all emojis.

    Any emoji implementation supports being used as a
    `hikari.files.Resource` when uploading an attachment to the API.
    This is achieved in the same way as using a
    `hikari.files.WebResource` would achieve this.
    """

    __slots__: typing.Sequence[str] = ()

    @property
    @abc.abstractmethod
    def name(self) -> str:
        """Return the generic name/representation for this emoji."""

    @property
    @abc.abstractmethod
    def url(self) -> str:
        """URL of the emoji image to display in clients."""

    @property
    @abc.abstractmethod
    def url_name(self) -> str:
        """Name of the part of the emoji to use in requests."""

    @property
    @abc.abstractmethod
    def mention(self) -> str:
        """Mention string to use to mention the emoji with."""

    @classmethod
    def parse(cls, string: str, /) -> Emoji:
        """Parse a given string into an emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        Emoji
            The parsed emoji object. This will be a `CustomEmoji` if a custom
            emoji mention, or a `UnicodeEmoji` otherwise.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if string.startswith("<") and string.endswith(">"):
            return CustomEmoji.parse(string)
        return UnicodeEmoji.parse(string)

Base class for all emojis.

Any emoji implementation supports being used as a hikari.files.Resource when uploading an attachment to the API. This is achieved in the same way as using a hikari.files.WebResource would achieve this.

Variables and properties
#  extension: Optional[str]

File extension, if there is one.

#  filename: str

Filename of the resource.

#  mention: str

Mention string to use to mention the emoji with.

#  name: str

Return the generic name/representation for this emoji.

#  url: str

URL of the emoji image to display in clients.

#  url_name: str

Name of the part of the emoji to use in requests.

Methods
#  
@classmethod
def parse(cls, string: str, /) -> hikari.emojis.Emoji:
View Source
    @classmethod
    def parse(cls, string: str, /) -> Emoji:
        """Parse a given string into an emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        Emoji
            The parsed emoji object. This will be a `CustomEmoji` if a custom
            emoji mention, or a `UnicodeEmoji` otherwise.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if string.startswith("<") and string.endswith(">"):
            return CustomEmoji.parse(string)
        return UnicodeEmoji.parse(string)

Parse a given string into an emoji object.

Parameters
  • string (str): The emoji object to parse.
Returns
Raises
  • ValueError: If a mention is given that has an invalid format.
#  async def read(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None
) -> bytes:
View Source
    async def read(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
    ) -> bytes:
        """Read the entire resource at once into memory.

        ```py
        data = await resource.read(...)
        # ^-- This is a shortcut for the following --v
        async with resource.stream(...) as reader:
            data = await reader.read()
        ```

        .. warning::
            If you simply wish to re-upload this resource to Discord via
            any endpoint in Hikari, you should opt to just pass this
            resource object directly. This way, Hikari can perform byte
            inception, which significantly reduces the memory usage for
            your bot as it grows larger.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            The executor to run in for blocking operations.
            If `None`, then the default executor is used for the
            current event loop.

        Returns
        -------
        bytes
            The entire resource.
        """
        async with self.stream(executor=executor) as reader:
            return await reader.read()

Read the entire resource at once into memory.

data = await resource.read(...)
# ^-- This is a shortcut for the following --v
async with resource.stream(...) as reader:
    data = await reader.read()

Warning: If you simply wish to re-upload this resource to Discord via any endpoint in Hikari, you should opt to just pass this resource object directly. This way, Hikari can perform byte inception, which significantly reduces the memory usage for your bot as it grows larger.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): The executor to run in for blocking operations. If None, then the default executor is used for the current event loop.
Returns
  • bytes: The entire resource.
#  def stream(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None,
   head_only: bool = False
) -> hikari.files.AsyncReaderContextManager[hikari.files.WebReader]:
View Source
    def stream(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
        head_only: bool = False,
    ) -> AsyncReaderContextManager[WebReader]:
        """Start streaming the content into memory by downloading it.

        You can use this to fetch the entire resource, parts of the resource,
        or just to view any metadata that may be provided.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            Not used. Provided only to match the underlying interface.
        head_only : bool
            Defaults to `False`. If `True`, then the
            implementation may only retrieve HEAD information if supported.
            This currently only has any effect for web requests.

        Examples
        --------
        Downloading an entire resource at once into memory:
        ```py
        async with obj.stream() as stream:
            data = await stream.read()
        ```
        Checking the metadata:
        ```py
        async with obj.stream() as stream:
            mimetype = stream.mimetype

        if mimetype is None:
            ...
        elif mimetype not in whitelisted_mimetypes:
            ...
        else:
            ...
        ```
        Fetching the data-uri of a resource:
        ```py
        async with obj.stream() as stream:
            data_uri = await stream.data_uri()
        ```

        Returns
        -------
        AsyncReaderContextManager[WebReader]
            An async context manager that when entered, produces the
            data stream.

        Raises
        ------
        hikari.errors.BadRequestError
            If a 400 is returned.
        hikari.errors.UnauthorizedError
            If a 401 is returned.
        hikari.errors.ForbiddenError
            If a 403 is returned.
        hikari.errors.NotFoundError
            If a 404 is returned.
        hikari.errors.ClientHTTPResponseError
            If any other 4xx is returned.
        hikari.errors.InternalServerError
            If any other 5xx is returned.
        hikari.errors.HTTPResponseError
            If any other unexpected response code is returned.
        """
        return _WebReaderAsyncReaderContextManagerImpl(self, head_only)

Start streaming the content into memory by downloading it.

You can use this to fetch the entire resource, parts of the resource, or just to view any metadata that may be provided.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): Not used. Provided only to match the underlying interface.
  • head_only (bool): Defaults to False. If True, then the implementation may only retrieve HEAD information if supported. This currently only has any effect for web requests.
Examples

Downloading an entire resource at once into memory:

async with obj.stream() as stream:
    data = await stream.read()

Checking the metadata:

async with obj.stream() as stream:
    mimetype = stream.mimetype

if mimetype is None:
    ...
elif mimetype not in whitelisted_mimetypes:
    ...
else:
    ...

Fetching the data-uri of a resource:

async with obj.stream() as stream:
    data_uri = await stream.data_uri()
Returns
  • AsyncReaderContextManager[WebReader]: An async context manager that when entered, produces the data stream.
Raises
#  
@attr.define(hash=True, kw_only=True, weakref_slot=False)
class KnownCustomEmoji(hikari.files.Resource[hikari.files.WebReader], abc.ABC):
View Source
@attr.define(hash=True, kw_only=True, weakref_slot=False)
class KnownCustomEmoji(CustomEmoji):
    """Represents an emoji that is known from a guild the bot is in.

    This is a specialization of `CustomEmoji` that is from a guild that you
    _are_ part of. As a result, it contains a lot more information with it.
    """

    app: traits.RESTAware = attr.field(
        repr=False, eq=False, hash=False, metadata={attr_extensions.SKIP_DEEP_COPY: True}
    )
    """The client application that models may use for procedures."""

    guild_id: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=False)
    """The ID of the guild this emoji belongs to."""

    role_ids: typing.Sequence[snowflakes.Snowflake] = attr.field(eq=False, hash=False, repr=False)
    """The IDs of the roles that are whitelisted to use this emoji.

    If this is empty then any user can use this emoji regardless of their roles.
    """

    user: typing.Optional[users.User] = attr.field(eq=False, hash=False, repr=False)
    """The user that created the emoji.

    .. note::
        This will be `None` if you are missing the `MANAGE_EMOJIS_AND_STICKERS`
        permission in the server the emoji is from.
    """

    is_colons_required: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether this emoji must be wrapped in colons."""

    is_managed: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether the emoji is managed by an integration."""

    is_available: bool = attr.field(eq=False, hash=False, repr=False)
    """Whether this emoji can currently be used.

    May be `False` due to a loss of Sever Boosts on the emoji's guild.
    """

Represents an emoji that is known from a guild the bot is in.

This is a specialization of CustomEmoji that is from a guild that you _are_ part of. As a result, it contains a lot more information with it.

Variables and properties

The client application that models may use for procedures.

#  created_at: datetime.datetime

When the object was created.

#  extension: Optional[str]

File extension, if there is one.

#  filename: str

Filename of the resource.

The ID of the guild this emoji belongs to.

The ID of this entity.

#  is_animated: bool

Whether the emoji is animated.

#  is_available: bool

Whether this emoji can currently be used.

May be False due to a loss of Sever Boosts on the emoji's guild.

#  is_colons_required: bool

Whether this emoji must be wrapped in colons.

#  is_managed: bool

Whether the emoji is managed by an integration.

#  mention: str

Mention string to use to mention the emoji with.

#  name: str

The name of the emoji.

The IDs of the roles that are whitelisted to use this emoji.

If this is empty then any user can use this emoji regardless of their roles.

#  url: str

URL of the emoji image to display in clients.

#  url_name: str

Name of the part of the emoji to use in requests.

#  user: Optional[hikari.users.User]

The user that created the emoji.

Note: This will be None if you are missing the MANAGE_EMOJIS_AND_STICKERS permission in the server the emoji is from.

Methods
#  def __init__(
   self,
   *,
   id: hikari.snowflakes.Snowflake,
   name: str,
   is_animated: bool,
   app: hikari.traits.RESTAware,
   guild_id: hikari.snowflakes.Snowflake,
   role_ids: Sequence[hikari.snowflakes.Snowflake],
   user: Optional[hikari.users.User],
   is_colons_required: bool,
   is_managed: bool,
   is_available: bool
):
View Source
def __init__(self, *, id, name, is_animated, app, guild_id, role_ids, user, is_colons_required, is_managed, is_available):
    self.id = id
    self.name = name
    self.is_animated = is_animated
    self.app = app
    self.guild_id = guild_id
    self.role_ids = role_ids
    self.user = user
    self.is_colons_required = is_colons_required
    self.is_managed = is_managed
    self.is_available = is_available

Method generated by attrs for class KnownCustomEmoji.

#  
@classmethod
def parse(cls, string: str, /) -> hikari.emojis.CustomEmoji:
View Source
    @classmethod
    def parse(cls, string: str, /) -> CustomEmoji:
        """Parse a given emoji mention string into a custom emoji object.

        Parameters
        ----------
        string : str
            The emoji mention to parse.

        Returns
        -------
        CustomEmoji
            The parsed emoji object.

        Raises
        ------
        ValueError
            If a mention is given that has an invalid format.
        """
        if emoji_match := _CUSTOM_EMOJI_REGEX.match(string):
            return CustomEmoji(
                id=snowflakes.Snowflake(emoji_match.group("id")),
                name=emoji_match.group("name"),
                is_animated=emoji_match.group("flags").lower() == "a",
            )

        raise ValueError("Expected an emoji mention")

Parse a given emoji mention string into a custom emoji object.

Parameters
  • string (str): The emoji mention to parse.
Returns
  • CustomEmoji: The parsed emoji object.
Raises
  • ValueError: If a mention is given that has an invalid format.
#  async def read(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None
) -> bytes:
View Source
    async def read(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
    ) -> bytes:
        """Read the entire resource at once into memory.

        ```py
        data = await resource.read(...)
        # ^-- This is a shortcut for the following --v
        async with resource.stream(...) as reader:
            data = await reader.read()
        ```

        .. warning::
            If you simply wish to re-upload this resource to Discord via
            any endpoint in Hikari, you should opt to just pass this
            resource object directly. This way, Hikari can perform byte
            inception, which significantly reduces the memory usage for
            your bot as it grows larger.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            The executor to run in for blocking operations.
            If `None`, then the default executor is used for the
            current event loop.

        Returns
        -------
        bytes
            The entire resource.
        """
        async with self.stream(executor=executor) as reader:
            return await reader.read()

Read the entire resource at once into memory.

data = await resource.read(...)
# ^-- This is a shortcut for the following --v
async with resource.stream(...) as reader:
    data = await reader.read()

Warning: If you simply wish to re-upload this resource to Discord via any endpoint in Hikari, you should opt to just pass this resource object directly. This way, Hikari can perform byte inception, which significantly reduces the memory usage for your bot as it grows larger.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): The executor to run in for blocking operations. If None, then the default executor is used for the current event loop.
Returns
  • bytes: The entire resource.
#  def stream(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None,
   head_only: bool = False
) -> hikari.files.AsyncReaderContextManager[hikari.files.WebReader]:
View Source
    def stream(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
        head_only: bool = False,
    ) -> AsyncReaderContextManager[WebReader]:
        """Start streaming the content into memory by downloading it.

        You can use this to fetch the entire resource, parts of the resource,
        or just to view any metadata that may be provided.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            Not used. Provided only to match the underlying interface.
        head_only : bool
            Defaults to `False`. If `True`, then the
            implementation may only retrieve HEAD information if supported.
            This currently only has any effect for web requests.

        Examples
        --------
        Downloading an entire resource at once into memory:
        ```py
        async with obj.stream() as stream:
            data = await stream.read()
        ```
        Checking the metadata:
        ```py
        async with obj.stream() as stream:
            mimetype = stream.mimetype

        if mimetype is None:
            ...
        elif mimetype not in whitelisted_mimetypes:
            ...
        else:
            ...
        ```
        Fetching the data-uri of a resource:
        ```py
        async with obj.stream() as stream:
            data_uri = await stream.data_uri()
        ```

        Returns
        -------
        AsyncReaderContextManager[WebReader]
            An async context manager that when entered, produces the
            data stream.

        Raises
        ------
        hikari.errors.BadRequestError
            If a 400 is returned.
        hikari.errors.UnauthorizedError
            If a 401 is returned.
        hikari.errors.ForbiddenError
            If a 403 is returned.
        hikari.errors.NotFoundError
            If a 404 is returned.
        hikari.errors.ClientHTTPResponseError
            If any other 4xx is returned.
        hikari.errors.InternalServerError
            If any other 5xx is returned.
        hikari.errors.HTTPResponseError
            If any other unexpected response code is returned.
        """
        return _WebReaderAsyncReaderContextManagerImpl(self, head_only)

Start streaming the content into memory by downloading it.

You can use this to fetch the entire resource, parts of the resource, or just to view any metadata that may be provided.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): Not used. Provided only to match the underlying interface.
  • head_only (bool): Defaults to False. If True, then the implementation may only retrieve HEAD information if supported. This currently only has any effect for web requests.
Examples

Downloading an entire resource at once into memory:

async with obj.stream() as stream:
    data = await stream.read()

Checking the metadata:

async with obj.stream() as stream:
    mimetype = stream.mimetype

if mimetype is None:
    ...
elif mimetype not in whitelisted_mimetypes:
    ...
else:
    ...

Fetching the data-uri of a resource:

async with obj.stream() as stream:
    data_uri = await stream.data_uri()
Returns
  • AsyncReaderContextManager[WebReader]: An async context manager that when entered, produces the data stream.
Raises
View Source
class UnicodeEmoji(str, Emoji):
    """Represents a unicode emoji.

    .. warning::
        A word of warning if you try to upload this emoji as a file attachment.

        While this emoji type can be used to upload the Twemoji representations
        of this emoji as a PNG, this is NOT foolproof. The mapping between
        Discord's implementation and official Twemoji bindings is very flaky.
        Responsible implementations relying on this behaviour will be
        implemented to expect this behaviour in the form of
        `hikari.errors.NotFoundError` exceptions being raised when a mismatch may
        occur. It is also likely that this will change in the future without
        notice, so you will likely be relying on flaky behaviour.

        If this is proven to be too unstable, this functionality will be
        removed in a future release after a deprecation period.
    """

    __slots__: typing.Sequence[str] = ()

    @property
    def name(self) -> str:
        """Return the code points which form the emoji."""
        return self

    @property
    @typing.final
    def url_name(self) -> str:
        return self

    @property
    def mention(self) -> str:
        return self

    @property
    @typing.final
    def codepoints(self) -> typing.Sequence[int]:
        """Integer codepoints that make up this emoji, as UTF-8."""
        return [ord(c) for c in self]

    @property
    def filename(self) -> str:
        """Filename to use if re-uploading this emoji's PNG."""
        codepoints = self.codepoints
        # It looks like the rule is to delete character #2 if the value
        # of this is 0xfe0f and the character is up to 4 characters long.
        # Other than that, the mapping is 1-to-1. There's a couple of outliers
        # to this 0xfe0f rule and they seem to follow a pattern where the
        # codepoint after 0xfe0f is 0x200D but this might be a coincidence and
        # if Discord start breaking this regularly I might need to ask for a
        # more permanent solution. This is provisionally provided. If we find it
        # breaks in other ways, I will just revoke this functionality in a
        # future update.
        if codepoints[1:2] == [0xFE0F] and len(codepoints) <= 4 and codepoints[2:3] != [0x200D]:
            codepoints = [codepoints[0], *codepoints[2:]]

        return "-".join(hex(c)[2:] for c in codepoints) + ".png"

    @property
    def url(self) -> str:
        """Get the URL of the PNG rendition of this emoji.

        This will use the official Twitter "twemoji" repository to fetch
        this information, as Discord only stores this in a hashed format
        that uses SVG files, which is not usually of any use.

        Since this uses "twemoji" directly, the emojis may not directly
        match what is on Discord if Discord have failed to keep their emoji
        packs up-to-date with this repository.

        Example
        -------
        ```py
        >>> emoji = hikari.UnicodeEmoji("\N{OK HAND SIGN}")
        >>> emoji.url
        'https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f44c.png'
        ```
        """
        return _TWEMOJI_PNG_BASE_URL + self.filename

    # @property
    # @typing.final
    # def unicode_names(self) -> typing.Sequence[str]:
    #     """Get the unicode name of the emoji as a sequence.
    #
    #     This returns the name of each codepoint. If only one codepoint exists,
    #     then this will only have one item in the resulting sequence.
    #     """
    #     return [unicodedata.name(c) for c in self]

    @property
    @typing.final
    def unicode_escape(self) -> str:
        """Get the unicode escape string for this emoji."""
        return bytes(self, "unicode_escape").decode("utf-8")

    @classmethod
    @typing.final
    def parse_codepoints(cls, codepoint: int, *codepoints: int) -> UnicodeEmoji:
        """Create a unicode emoji from one or more UTF-32 codepoints."""
        return cls("".join(map(chr, (codepoint, *codepoints))))

    @classmethod
    @typing.final
    def parse_unicode_escape(cls, escape: str) -> UnicodeEmoji:
        """Create a unicode emoji from a unicode escape string."""
        return cls(escape.encode("utf-8"), "unicode_escape")

    @classmethod
    @typing.final
    def parse(cls, string: str, /) -> UnicodeEmoji:
        """Parse a given string into a unicode emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        UnicodeEmoji
            The parsed UnicodeEmoji object.
        """
        # TODO: Re-add validity
        # Ensure validity.
        # for i, codepoint in enumerate(string, start=1):
        #     unicodedata.name(codepoint)

        return cls(string)

Represents a unicode emoji.

Warning: A word of warning if you try to upload this emoji as a file attachment.

While this emoji type can be used to upload the Twemoji representations of this emoji as a PNG, this is NOT foolproof. The mapping between Discord's implementation and official Twemoji bindings is very flaky. Responsible implementations relying on this behaviour will be implemented to expect this behaviour in the form of hikari.errors.NotFoundError exceptions being raised when a mismatch may occur. It is also likely that this will change in the future without notice, so you will likely be relying on flaky behaviour.

If this is proven to be too unstable, this functionality will be removed in a future release after a deprecation period.

Variables and properties
#  codepoints: Sequence[int]

Integer codepoints that make up this emoji, as UTF-8.

#  extension: Optional[str]

File extension, if there is one.

#  filename: str

Filename to use if re-uploading this emoji's PNG.

#  mention: str

Mention string to use to mention the emoji with.

#  name: str

Return the code points which form the emoji.

#  unicode_escape: str

Get the unicode escape string for this emoji.

#  url: str

Get the URL of the PNG rendition of this emoji.

This will use the official Twitter "twemoji" repository to fetch this information, as Discord only stores this in a hashed format that uses SVG files, which is not usually of any use.

Since this uses "twemoji" directly, the emojis may not directly match what is on Discord if Discord have failed to keep their emoji packs up-to-date with this repository.

Example
>>> emoji = hikari.UnicodeEmoji("👌")
>>> emoji.url
'https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f44c.png'
#  url_name: str

Name of the part of the emoji to use in requests.

Methods
#  def __init__():
#  def capitalize(self, /):

Return a capitalized version of the string.

More specifically, make the first character have upper case and the rest lower case.

#  def casefold(self, /):

Return a version of the string suitable for caseless comparisons.

#  def center(self, width, fillchar=' ', /):

Return a centered string of length width.

Padding is done using the specified fill character (default is a space).

#  def count(unknown):

S.count(sub[, start[, end]]) -> int

Return the number of non-overlapping occurrences of substring sub in string S[start:end]. Optional arguments start and end are interpreted as in slice notation.

#  def encode(self, /, encoding='utf-8', errors='strict'):

Encode the string using the codec registered for encoding.

encoding The encoding in which to encode the string. errors The error handling scheme to use for encoding errors. The default is 'strict' meaning that encoding errors raise a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and 'xmlcharrefreplace' as well as any other name registered with codecs.register_error that can handle UnicodeEncodeErrors.

#  def endswith(unknown):

S.endswith(suffix[, start[, end]]) -> bool

Return True if S ends with the specified suffix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. suffix can also be a tuple of strings to try.

#  def expandtabs(self, /, tabsize=8):

Return a copy where all tab characters are expanded using spaces.

If tabsize is not given, a tab size of 8 characters is assumed.

#  def find(unknown):

S.find(sub[, start[, end]]) -> int

Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

#  def format(unknown):

S.format(args, *kwargs) -> str

Return a formatted version of S, using substitutions from args and kwargs. The substitutions are identified by braces ('{' and '}').

#  def format_map(unknown):

S.format_map(mapping) -> str

Return a formatted version of S, using substitutions from mapping. The substitutions are identified by braces ('{' and '}').

#  def index(unknown):

S.index(sub[, start[, end]]) -> int

Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Raises ValueError when the substring is not found.

#  def isalnum(self, /):

Return True if the string is an alpha-numeric string, False otherwise.

A string is alpha-numeric if all characters in the string are alpha-numeric and there is at least one character in the string.

#  def isalpha(self, /):

Return True if the string is an alphabetic string, False otherwise.

A string is alphabetic if all characters in the string are alphabetic and there is at least one character in the string.

#  def isascii(self, /):

Return True if all characters in the string are ASCII, False otherwise.

ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too.

#  def isdecimal(self, /):

Return True if the string is a decimal string, False otherwise.

A string is a decimal string if all characters in the string are decimal and there is at least one character in the string.

#  def isdigit(self, /):

Return True if the string is a digit string, False otherwise.

A string is a digit string if all characters in the string are digits and there is at least one character in the string.

#  def isidentifier(self, /):

Return True if the string is a valid Python identifier, False otherwise.

Call keyword.iskeyword(s) to test whether string s is a reserved identifier, such as "def" or "class".

#  def islower(self, /):

Return True if the string is a lowercase string, False otherwise.

A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string.

#  def isnumeric(self, /):

Return True if the string is a numeric string, False otherwise.

A string is numeric if all characters in the string are numeric and there is at least one character in the string.

#  def isprintable(self, /):

Return True if the string is printable, False otherwise.

A string is printable if all of its characters are considered printable in repr() or if it is empty.

#  def isspace(self, /):

Return True if the string is a whitespace string, False otherwise.

A string is whitespace if all characters in the string are whitespace and there is at least one character in the string.

#  def istitle(self, /):

Return True if the string is a title-cased string, False otherwise.

In a title-cased string, upper- and title-case characters may only follow uncased characters and lowercase characters only cased ones.

#  def isupper(self, /):

Return True if the string is an uppercase string, False otherwise.

A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string.

#  def join(self, iterable, /):

Concatenate any number of strings.

The string whose method is called is inserted in between each given string. The result is returned as a new string.

Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'

#  def ljust(self, width, fillchar=' ', /):

Return a left-justified string of length width.

Padding is done using the specified fill character (default is a space).

#  def lower(self, /):

Return a copy of the string converted to lowercase.

#  def lstrip(self, chars=None, /):

Return a copy of the string with leading whitespace removed.

If chars is given and not None, remove characters in chars instead.

#  def maketrans(unknown):

Return a translation table usable for str.translate().

If there is only one argument, it must be a dictionary mapping Unicode ordinals (integers) or characters to Unicode ordinals, strings or None. Character keys will be then converted to ordinals. If there are two arguments, they must be strings of equal length, and in the resulting dictionary, each character in x will be mapped to the character at the same position in y. If there is a third argument, it must be a string, whose characters will be mapped to None in the result.

#  
@classmethod
@typing.final
def parse(cls, string: str, /) -> hikari.emojis.UnicodeEmoji:
View Source
    @classmethod
    @typing.final
    def parse(cls, string: str, /) -> UnicodeEmoji:
        """Parse a given string into a unicode emoji object.

        Parameters
        ----------
        string : str
            The emoji object to parse.

        Returns
        -------
        UnicodeEmoji
            The parsed UnicodeEmoji object.
        """
        # TODO: Re-add validity
        # Ensure validity.
        # for i, codepoint in enumerate(string, start=1):
        #     unicodedata.name(codepoint)

        return cls(string)

Parse a given string into a unicode emoji object.

Parameters
  • string (str): The emoji object to parse.
Returns
  • UnicodeEmoji: The parsed UnicodeEmoji object.
#  
@classmethod
@typing.final
def parse_codepoints(cls, codepoint: int, *codepoints: int) -> hikari.emojis.UnicodeEmoji:
View Source
    @classmethod
    @typing.final
    def parse_codepoints(cls, codepoint: int, *codepoints: int) -> UnicodeEmoji:
        """Create a unicode emoji from one or more UTF-32 codepoints."""
        return cls("".join(map(chr, (codepoint, *codepoints))))

Create a unicode emoji from one or more UTF-32 codepoints.

#  
@classmethod
@typing.final
def parse_unicode_escape(cls, escape: str) -> hikari.emojis.UnicodeEmoji:
View Source
    @classmethod
    @typing.final
    def parse_unicode_escape(cls, escape: str) -> UnicodeEmoji:
        """Create a unicode emoji from a unicode escape string."""
        return cls(escape.encode("utf-8"), "unicode_escape")

Create a unicode emoji from a unicode escape string.

#  def partition(self, sep, /):

Partition the string into three parts using the given separator.

This will search for the separator in the string. If the separator is found, returns a 3-tuple containing the part before the separator, the separator itself, and the part after it.

If the separator is not found, returns a 3-tuple containing the original string and two empty strings.

#  async def read(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None
) -> bytes:
View Source
    async def read(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
    ) -> bytes:
        """Read the entire resource at once into memory.

        ```py
        data = await resource.read(...)
        # ^-- This is a shortcut for the following --v
        async with resource.stream(...) as reader:
            data = await reader.read()
        ```

        .. warning::
            If you simply wish to re-upload this resource to Discord via
            any endpoint in Hikari, you should opt to just pass this
            resource object directly. This way, Hikari can perform byte
            inception, which significantly reduces the memory usage for
            your bot as it grows larger.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            The executor to run in for blocking operations.
            If `None`, then the default executor is used for the
            current event loop.

        Returns
        -------
        bytes
            The entire resource.
        """
        async with self.stream(executor=executor) as reader:
            return await reader.read()

Read the entire resource at once into memory.

data = await resource.read(...)
# ^-- This is a shortcut for the following --v
async with resource.stream(...) as reader:
    data = await reader.read()

Warning: If you simply wish to re-upload this resource to Discord via any endpoint in Hikari, you should opt to just pass this resource object directly. This way, Hikari can perform byte inception, which significantly reduces the memory usage for your bot as it grows larger.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): The executor to run in for blocking operations. If None, then the default executor is used for the current event loop.
Returns
  • bytes: The entire resource.
#  def removeprefix(self, prefix, /):

Return a str with the given prefix string removed if present.

If the string starts with the prefix string, return string[len(prefix):]. Otherwise, return a copy of the original string.

#  def removesuffix(self, suffix, /):

Return a str with the given suffix string removed if present.

If the string ends with the suffix string and that suffix is not empty, return string[:-len(suffix)]. Otherwise, return a copy of the original string.

#  def replace(self, old, new, count=-1, /):

Return a copy with all occurrences of substring old replaced by new.

count Maximum number of occurrences to replace. -1 (the default value) means replace all occurrences.

If the optional argument count is given, only the first count occurrences are replaced.

#  def rfind(unknown):

S.rfind(sub[, start[, end]]) -> int

Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

#  def rindex(unknown):

S.rindex(sub[, start[, end]]) -> int

Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Raises ValueError when the substring is not found.

#  def rjust(self, width, fillchar=' ', /):

Return a right-justified string of length width.

Padding is done using the specified fill character (default is a space).

#  def rpartition(self, sep, /):

Partition the string into three parts using the given separator.

This will search for the separator in the string, starting at the end. If the separator is found, returns a 3-tuple containing the part before the separator, the separator itself, and the part after it.

If the separator is not found, returns a 3-tuple containing two empty strings and the original string.

#  def rsplit(self, /, sep=None, maxsplit=-1):

Return a list of the words in the string, using sep as the delimiter string.

sep The delimiter according which to split the string. None (the default value) means split according to any whitespace, and discard empty strings from the result. maxsplit Maximum number of splits to do. -1 (the default value) means no limit.

Splits are done starting at the end of the string and working to the front.

#  def rstrip(self, chars=None, /):

Return a copy of the string with trailing whitespace removed.

If chars is given and not None, remove characters in chars instead.

#  def split(self, /, sep=None, maxsplit=-1):

Return a list of the words in the string, using sep as the delimiter string.

sep The delimiter according which to split the string. None (the default value) means split according to any whitespace, and discard empty strings from the result. maxsplit Maximum number of splits to do. -1 (the default value) means no limit.

#  def splitlines(self, /, keepends=False):

Return a list of the lines in the string, breaking at line boundaries.

Line breaks are not included in the resulting list unless keepends is given and true.

#  def startswith(unknown):

S.startswith(prefix[, start[, end]]) -> bool

Return True if S starts with the specified prefix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. prefix can also be a tuple of strings to try.

#  def stream(
   self,
   *,
   executor: Optional[concurrent.futures._base.Executor] = None,
   head_only: bool = False
) -> hikari.files.AsyncReaderContextManager[hikari.files.WebReader]:
View Source
    def stream(
        self,
        *,
        executor: typing.Optional[concurrent.futures.Executor] = None,
        head_only: bool = False,
    ) -> AsyncReaderContextManager[WebReader]:
        """Start streaming the content into memory by downloading it.

        You can use this to fetch the entire resource, parts of the resource,
        or just to view any metadata that may be provided.

        Parameters
        ----------
        executor : typing.Optional[concurrent.futures.Executor]
            Not used. Provided only to match the underlying interface.
        head_only : bool
            Defaults to `False`. If `True`, then the
            implementation may only retrieve HEAD information if supported.
            This currently only has any effect for web requests.

        Examples
        --------
        Downloading an entire resource at once into memory:
        ```py
        async with obj.stream() as stream:
            data = await stream.read()
        ```
        Checking the metadata:
        ```py
        async with obj.stream() as stream:
            mimetype = stream.mimetype

        if mimetype is None:
            ...
        elif mimetype not in whitelisted_mimetypes:
            ...
        else:
            ...
        ```
        Fetching the data-uri of a resource:
        ```py
        async with obj.stream() as stream:
            data_uri = await stream.data_uri()
        ```

        Returns
        -------
        AsyncReaderContextManager[WebReader]
            An async context manager that when entered, produces the
            data stream.

        Raises
        ------
        hikari.errors.BadRequestError
            If a 400 is returned.
        hikari.errors.UnauthorizedError
            If a 401 is returned.
        hikari.errors.ForbiddenError
            If a 403 is returned.
        hikari.errors.NotFoundError
            If a 404 is returned.
        hikari.errors.ClientHTTPResponseError
            If any other 4xx is returned.
        hikari.errors.InternalServerError
            If any other 5xx is returned.
        hikari.errors.HTTPResponseError
            If any other unexpected response code is returned.
        """
        return _WebReaderAsyncReaderContextManagerImpl(self, head_only)

Start streaming the content into memory by downloading it.

You can use this to fetch the entire resource, parts of the resource, or just to view any metadata that may be provided.

Parameters
  • executor (typing.Optional[concurrent.futures.Executor]): Not used. Provided only to match the underlying interface.
  • head_only (bool): Defaults to False. If True, then the implementation may only retrieve HEAD information if supported. This currently only has any effect for web requests.
Examples

Downloading an entire resource at once into memory:

async with obj.stream() as stream:
    data = await stream.read()

Checking the metadata:

async with obj.stream() as stream:
    mimetype = stream.mimetype

if mimetype is None:
    ...
elif mimetype not in whitelisted_mimetypes:
    ...
else:
    ...

Fetching the data-uri of a resource:

async with obj.stream() as stream:
    data_uri = await stream.data_uri()
Returns
  • AsyncReaderContextManager[WebReader]: An async context manager that when entered, produces the data stream.
Raises
#  def strip(self, chars=None, /):

Return a copy of the string with leading and trailing whitespace removed.

If chars is given and not None, remove characters in chars instead.

#  def swapcase(self, /):

Convert uppercase characters to lowercase and lowercase characters to uppercase.

#  def title(self, /):

Return a version of the string where each word is titlecased.

More specifically, words start with uppercased characters and all remaining cased characters have lower case.

#  def translate(self, table, /):

Replace each character in the string using the given translation table.

table Translation table, which must be a mapping of Unicode ordinals to Unicode ordinals, strings, or None.

The table must implement lookup/indexing via __getitem__, for instance a dictionary or list. If this operation raises LookupError, the character is left untouched. Characters mapped to None are deleted.

#  def upper(self, /):

Return a copy of the string converted to uppercase.

#  def zfill(self, width, /):

Pad a numeric string with zeros on the left, to fill a field of the given width.

The string is never truncated.