Source code for hikari.users

# -*- 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 Users on Discord."""

from __future__ import annotations

__all__: typing.Sequence[str] = ("PartialUser", "User", "OwnUser", "UserFlag", "PremiumType")

import abc
import typing

import attr

from hikari import snowflakes
from hikari import traits
from hikari import undefined
from hikari import urls
from hikari.internal import attr_extensions
from hikari.internal import enums
from hikari.internal import routes

if typing.TYPE_CHECKING:
    from hikari import channels
    from hikari import colors
    from hikari import embeds as embeds_
    from hikari import files
    from hikari import guilds
    from hikari import locales
    from hikari import messages
    from hikari.api import special_endpoints


@typing.final
[docs]class UserFlag(enums.Flag): """The known user flags that represent account badges."""
[docs] NONE = 0
"""None."""
[docs] DISCORD_EMPLOYEE = 1 << 0
"""Discord Employee."""
[docs] PARTNERED_SERVER_OWNER = 1 << 1
"""Owner of a partnered Discord server."""
[docs] HYPESQUAD_EVENTS = 1 << 2
"""HypeSquad Events."""
[docs] BUG_HUNTER_LEVEL_1 = 1 << 3
"""Bug Hunter Level 1."""
[docs] HYPESQUAD_BRAVERY = 1 << 6
"""House of Bravery."""
[docs] HYPESQUAD_BRILLIANCE = 1 << 7
"""House of Brilliance."""
[docs] HYPESQUAD_BALANCE = 1 << 8
"""House of Balance."""
[docs] EARLY_SUPPORTER = 1 << 9
"""Early Supporter."""
[docs] TEAM_USER = 1 << 10
"""Team user."""
[docs] BUG_HUNTER_LEVEL_2 = 1 << 14
"""Bug Hunter Level 2."""
[docs] VERIFIED_BOT = 1 << 16
"""Verified Bot."""
[docs] EARLY_VERIFIED_DEVELOPER = 1 << 17
"""Early verified Bot Developer. Only applies to users that verified their account before 20th August 2019. """
[docs] DISCORD_CERTIFIED_MODERATOR = 1 << 18
"""Discord Certified Moderator."""
[docs] BOT_HTTP_INTERACTIONS = 1 << 19
"""Bot uses only HTTP interactions and is shown in the active member list."""
[docs] ACTIVE_DEVELOPER = 1 << 22
"""User is an active bot developer."""
@typing.final
[docs]class PremiumType(int, enums.Enum): """The types of Nitro."""
[docs] NONE = 0
"""No premium."""
[docs] NITRO_CLASSIC = 1
"""Premium including basic perks like animated emojis and avatars."""
[docs] NITRO = 2
"""Premium including all perks (e.g. 2 server boosts)."""
[docs]class PartialUser(snowflakes.Unique, abc.ABC): """A partial interface for a user. Fields may or may not be present, and should be explicitly checked before using them to ensure they are not `hikari.undefined.UNDEFINED`. This is used for endpoints and events that only expose partial user information. For full user info, consider calling the `fetch_self` method to perform an API call. """ __slots__: typing.Sequence[str] = () @property @abc.abstractmethod
[docs] def app(self) -> traits.RESTAware: """Client application that models may use for procedures."""
@property @abc.abstractmethod
[docs] def avatar_hash(self) -> undefined.UndefinedNoneOr[str]: """Avatar hash for the user, if they have one, otherwise `None`."""
@property @abc.abstractmethod
[docs] def banner_hash(self) -> undefined.UndefinedNoneOr[str]: """Banner hash for the user, if they have one, otherwise `None`."""
@property @abc.abstractmethod
[docs] def accent_color(self) -> undefined.UndefinedNoneOr[colors.Color]: """Custom banner color for the user if set, else `None`. The official client will decide the default color if not set. """
@property
[docs] def accent_colour(self) -> undefined.UndefinedNoneOr[colors.Color]: """Alias for the `accent_color` field.""" return self.accent_color
@property @abc.abstractmethod
[docs] def discriminator(self) -> undefined.UndefinedOr[str]: """Discriminator for the user."""
@property @abc.abstractmethod
[docs] def username(self) -> undefined.UndefinedOr[str]: """Username for the user."""
@property @abc.abstractmethod
[docs] def is_bot(self) -> undefined.UndefinedOr[bool]: """Whether this user is a bot account."""
@property @abc.abstractmethod
[docs] def is_system(self) -> undefined.UndefinedOr[bool]: """Whether this user is a system account."""
@property @abc.abstractmethod
[docs] def flags(self) -> undefined.UndefinedOr[UserFlag]: """Flag bits that are set for the user."""
@property @abc.abstractmethod
[docs] def mention(self) -> str: """Return a raw mention string for the given user. Examples -------- .. code-block:: python >>> some_user.mention '<@123456789123456789>' """
[docs] async def fetch_dm_channel(self) -> channels.DMChannel: """Fetch the DM channel for this user. Returns ------- hikari.channels.DMChannel The requested channel. Raises ------ hikari.errors.UnauthorizedError If you are unauthorized to make the request (invalid/missing token). hikari.errors.NotFoundError If the user is not found. hikari.errors.RateLimitTooLongError Raised in the event that a rate limit occurs that is longer than `max_rate_limit` when making a request. hikari.errors.InternalServerError If an internal error occurs on Discord while handling the request. """ return await self.app.rest.create_dm_channel(self.id)
[docs] async def fetch_self(self) -> User: """Get this user's up-to-date object by performing an API call. Returns ------- hikari.users.User The requested user object. Raises ------ hikari.errors.NotFoundError If the user is not found. hikari.errors.RateLimitTooLongError Raised in the event that a rate limit occurs that is longer than `max_rate_limit` when making a request. hikari.errors.InternalServerError If an internal error occurs on Discord while handling the request. """ return await self.app.rest.fetch_user(user=self.id)
[docs] async def send( self, content: undefined.UndefinedOr[typing.Any] = undefined.UNDEFINED, *, attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED, attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED, component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED, components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED, embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED, embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED, tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED, reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages.PartialMessage]] = undefined.UNDEFINED, reply_must_exist: undefined.UndefinedOr[bool] = undefined.UNDEFINED, mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED, mentions_reply: undefined.UndefinedOr[bool] = undefined.UNDEFINED, user_mentions: undefined.UndefinedOr[ typing.Union[snowflakes.SnowflakeishSequence[PartialUser], bool] ] = undefined.UNDEFINED, role_mentions: undefined.UndefinedOr[ typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool] ] = undefined.UNDEFINED, flags: typing.Union[undefined.UndefinedType, int, messages.MessageFlag] = undefined.UNDEFINED, ) -> messages.Message: """Send a message to this user in DM's. Parameters ---------- content : hikari.undefined.UndefinedOr[typing.Any] If provided, the message contents. If `hikari.undefined.UNDEFINED`, then nothing will be sent in the content. Any other value here will be cast to a `str`. If this is a `hikari.embeds.Embed` and no `embed` nor `embeds` kwarg is provided, then this will instead update the embed. This allows for simpler syntax when sending an embed alone. Likewise, if this is a `hikari.files.Resource`, then the content is instead treated as an attachment if no `attachment` and no `attachments` kwargs are provided. Other Parameters ---------------- attachment : hikari.undefined.UndefinedOr[hikari.files.Resourceish] If provided, the message attachment. This can be a resource, or string of a path on your computer or a URL. Attachments can be passed as many different things, to aid in convenience. - If a `pathlib.PurePath` or `str` to a valid URL, the resource at the given URL will be streamed to Discord when sending the message. Subclasses of `hikari.files.WebResource` such as `hikari.files.URL`, `hikari.messages.Attachment`, `hikari.emojis.Emoji`, `EmbedResource`, etc will also be uploaded this way. This will use bit-inception, so only a small percentage of the resource will remain in memory at any one time, thus aiding in scalability. - If a `hikari.files.Bytes` is passed, or a `str` that contains a valid data URI is passed, then this is uploaded with a randomized file name if not provided. - If a `hikari.files.File`, `pathlib.PurePath` or `str` that is an absolute or relative path to a file on your file system is passed, then this resource is uploaded as an attachment using non-blocking code internally and streamed using bit-inception where possible. This depends on the type of `concurrent.futures.Executor` that is being used for the application (default is a thread pool which supports this behaviour). attachments : hikari.undefined.UndefinedOr[typing.Sequence[hikari.files.Resourceish]] If provided, the message attachments. These can be resources, or strings consisting of paths on your computer or URLs. component : hikari.undefined.UndefinedOr[hikari.api.special_endpoints.ComponentBuilder] If provided, builder object of the component to include in this message. components : hikari.undefined.UndefinedOr[typing.Sequence[hikari.api.special_endpoints.ComponentBuilder]] If provided, a sequence of the component builder objects to include in this message. embed : hikari.undefined.UndefinedOr[hikari.embeds.Embed] If provided, the message embed. embeds : hikari.undefined.UndefinedOr[typing.Sequence[hikari.embeds.Embed]] If provided, the message embeds. tts : hikari.undefined.UndefinedOr[bool] If provided, whether the message will be read out by a screen reader using Discord's TTS (text-to-speech) system. reply : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]] If provided, the message to reply to. reply_must_exist : hikari.undefined.UndefinedOr[bool] If provided, whether to error if the message being replied to does not exist instead of sending as a normal (non-reply) message. Defaults to `True`. This will not do anything if not being used with `reply`. mentions_everyone : hikari.undefined.UndefinedOr[bool] If provided, whether the message should parse @everyone/@here mentions. mentions_reply : hikari.undefined.UndefinedOr[bool] If provided, whether to mention the author of the message that is being replied to. This will not do anything if not being used with `reply`. user_mentions : hikari.undefined.UndefinedOr[typing.Union[hikari.snowflakes.SnowflakeishSequence[hikari.users.PartialUser], bool]] If provided, and `True`, all user mentions will be detected. If provided, and `False`, all user mentions will be ignored if appearing in the message body. Alternatively this may be a collection of `hikari.snowflakes.Snowflake`, or `hikari.users.PartialUser` derivatives to enforce mentioning specific users. role_mentions : hikari.undefined.UndefinedOr[typing.Union[hikari.snowflakes.SnowflakeishSequence[hikari.guilds.PartialRole], bool]] If provided, and `True`, all role mentions will be detected. If provided, and `False`, all role mentions will be ignored if appearing in the message body. Alternatively this may be a collection of `hikari.snowflakes.Snowflake`, or `hikari.guilds.PartialRole` derivatives to enforce mentioning specific roles. flags : hikari.undefined.UndefinedOr[hikari.messages.MessageFlag] If provided, optional flags to set on the message. If `hikari.undefined.UNDEFINED`, then nothing is changed. Note that some flags may not be able to be set. Currently the only flags that can be set are `NONE` and `SUPPRESS_EMBEDS`. Returns ------- hikari.messages.Message The created message. Raises ------ ValueError If more than 100 unique objects/entities are passed for `role_mentions` or `user_mentions`. TypeError If both `attachment` and `attachments` are specified. hikari.errors.BadRequestError This may be raised in several discrete situations, such as messages being empty with no attachments or embeds; messages with more than 2000 characters in them, embeds that exceed one of the many embed limits; too many attachments; attachments that are too large; invalid image URLs in embeds; `reply` not found or not in the same channel; too many components. hikari.errors.UnauthorizedError If you are unauthorized to make the request (invalid/missing token). hikari.errors.ForbiddenError If you are missing the `SEND_MESSAGES` in the channel or the person you are trying to message has the DM's disabled. hikari.errors.NotFoundError If the user is not found. hikari.errors.RateLimitTooLongError Raised in the event that a rate limit occurs that is longer than `max_rate_limit` when making a request. hikari.errors.InternalServerError If an internal error occurs on Discord while handling the request. """ # noqa: E501 - Line too long channel_id = None if isinstance(self.app, traits.CacheAware): channel_id = self.app.cache.get_dm_channel_id(self.id) if channel_id is None: channel_id = (await self.fetch_dm_channel()).id return await self.app.rest.create_message( channel=channel_id, content=content, attachment=attachment, attachments=attachments, component=component, components=components, embed=embed, embeds=embeds, tts=tts, reply=reply, reply_must_exist=reply_must_exist, mentions_everyone=mentions_everyone, user_mentions=user_mentions, role_mentions=role_mentions, mentions_reply=mentions_reply, flags=flags, )
[docs]class User(PartialUser, abc.ABC): """Interface for any user-like object. This does not include partial users, as they may not be fully formed. """ __slots__: typing.Sequence[str] = () @property @abc.abstractmethod
[docs] def app(self) -> traits.RESTAware: """Client application that models may use for procedures."""
@property @abc.abstractmethod
[docs] def accent_color(self) -> typing.Optional[colors.Color]: """The custom banner color for the user, if set else `None`. The official client will decide the default color if not set. """
@property
[docs] def accent_colour(self) -> typing.Optional[colors.Color]: """Alias for the `accent_color` field.""" return self.accent_color
@property @abc.abstractmethod
[docs] def avatar_hash(self) -> typing.Optional[str]: """Avatar hash for the user, if they have one, otherwise `None`."""
@property
[docs] def avatar_url(self) -> typing.Optional[files.URL]: """Avatar URL for the user, if they have one set. May be `None` if no custom avatar is set. In this case, you should use `default_avatar_url` instead. """ return self.make_avatar_url()
@property @abc.abstractmethod
[docs] def banner_hash(self) -> typing.Optional[str]: """Banner hash for the user, if they have one, otherwise `None`."""
@property
[docs] def banner_url(self) -> typing.Optional[files.URL]: """Banner URL for the user, if they have one set. May be `None` if no custom banner is set. """ return self.make_banner_url()
@property
[docs] def default_avatar_url(self) -> files.URL: """Default avatar URL for this user.""" return routes.CDN_DEFAULT_USER_AVATAR.compile_to_file( urls.CDN_URL, discriminator=int(self.discriminator) % 5, file_format="png", )
@property
[docs] def display_avatar_url(self) -> files.URL: """Display avatar URL for this user.""" return self.make_avatar_url() or self.default_avatar_url
@property @abc.abstractmethod
[docs] def discriminator(self) -> str: """Discriminator for the user."""
@property @abc.abstractmethod
[docs] def flags(self) -> UserFlag: """Flag bits that are set for the user."""
@property @abc.abstractmethod
[docs] def is_bot(self) -> bool: """Whether this user is a bot account."""
@property @abc.abstractmethod
[docs] def is_system(self) -> bool: """Whether this user is a system account."""
@property @abc.abstractmethod
[docs] def mention(self) -> str: """Return a raw mention string for the given user. Examples -------- .. code-block:: python >>> some_user.mention '<@123456789123456789>' """
@property @abc.abstractmethod
[docs] def username(self) -> str: """Username for the user."""
[docs] def make_avatar_url(self, *, ext: typing.Optional[str] = None, size: int = 4096) -> typing.Optional[files.URL]: """Generate the avatar URL for this user, if set. If no custom avatar is set, this returns `None`. You can then use the `default_avatar_url` attribute instead to fetch the displayed URL. Parameters ---------- ext : typing.Optional[str] The ext to use for this URL, defaults to `png` or `gif`. Supports `png`, `jpeg`, `jpg`, `webp` and `gif` (when animated). Will be ignored for default avatars which can only be `png`. If `None`, then the correct default extension is determined based on whether the icon is animated or not. size : int The size to set for the URL, defaults to `4096`. Can be any power of two between 16 and 4096. Will be ignored for default avatars. Returns ------- typing.Optional[hikari.files.URL] The URL to the avatar, or `None` if not present. Raises ------ ValueError If `size` is not a power of two or not between 16 and 4096. """ if self.avatar_hash is None: return None if ext is None: if self.avatar_hash.startswith("a_"): ext = "gif" else: ext = "png" return routes.CDN_USER_AVATAR.compile_to_file( urls.CDN_URL, user_id=self.id, hash=self.avatar_hash, size=size, file_format=ext, )
[docs] def make_banner_url(self, *, ext: typing.Optional[str] = None, size: int = 4096) -> typing.Optional[files.URL]: """Generate the banner URL for this user, if set. If no custom banner is set, this returns `None`. Parameters ---------- ext : typing.Optional[str] The ext to use for this URL, defaults to `png` or `gif`. Supports `png`, `jpeg`, `jpg`, `webp` and `gif` (when animated). If `None`, then the correct default extension is determined based on whether the banner is animated or not. size : int The size to set for the URL, defaults to `4096`. Can be any power of two between 16 and 4096. Returns ------- typing.Optional[hikari.files.URL] The URL to the banner, or `None` if not present. Raises ------ ValueError If `size` is not a power of two or not between 16 and 4096. """ if self.banner_hash is None: return None if ext is None: if self.banner_hash.startswith("a_"): ext = "gif" else: ext = "png" return routes.CDN_USER_BANNER.compile_to_file( urls.CDN_URL, user_id=self.id, hash=self.banner_hash, size=size, file_format=ext, )
@attr_extensions.with_copy @attr.define(hash=True, kw_only=True, weakref_slot=False) class PartialUserImpl(PartialUser): """Implementation for partial information about a user. This is pretty much the same as a normal user, but information may not be present, which will be denoted by `hikari.undefined.UNDEFINED`. """ id: snowflakes.Snowflake = attr.field(hash=True, repr=True) """The ID of this user.""" app: traits.RESTAware = attr.field( repr=False, eq=False, hash=False, metadata={attr_extensions.SKIP_DEEP_COPY: True} ) """Client application that models may use for procedures.""" discriminator: undefined.UndefinedOr[str] = attr.field(eq=False, hash=False, repr=True) """Four-digit discriminator for the user.""" username: undefined.UndefinedOr[str] = attr.field(eq=False, hash=False, repr=True) """Username of the user.""" avatar_hash: undefined.UndefinedNoneOr[str] = attr.field(eq=False, hash=False, repr=False) """Avatar hash of the user, if a custom avatar is set.""" banner_hash: undefined.UndefinedNoneOr[str] = attr.field(eq=False, hash=False, repr=False) """Banner hash of the user, if a custom banner is set.""" accent_color: undefined.UndefinedNoneOr[colors.Color] = attr.field(eq=False, hash=False, repr=False) """The custom banner color for the user, if set. The official client will decide the default color if not set. """ is_bot: undefined.UndefinedOr[bool] = attr.field(eq=False, hash=False, repr=True) """Whether this user is a bot account.""" is_system: undefined.UndefinedOr[bool] = attr.field(eq=False, hash=False, repr=True) """Whether this user is a system account.""" flags: undefined.UndefinedOr[UserFlag] = attr.field(eq=False, hash=False, repr=True) """Public flags for this user.""" @property def mention(self) -> str: """Return a raw mention string for the given user. Examples -------- .. code-block:: python >>> some_user.mention '<@123456789123456789>' """ return f"<@{self.id}>" def __str__(self) -> str: if self.username is undefined.UNDEFINED or self.discriminator is undefined.UNDEFINED: return f"Partial user ID {self.id}" return f"{self.username}#{self.discriminator}" @attr.define(hash=True, kw_only=True, weakref_slot=False) class UserImpl(PartialUserImpl, User): """Concrete implementation of user information.""" discriminator: str = attr.field(eq=False, hash=False, repr=True) """The user's discriminator.""" username: str = attr.field(eq=False, hash=False, repr=True) """The user's username.""" avatar_hash: typing.Optional[str] = attr.field(eq=False, hash=False, repr=False) """The user's avatar hash, if they have one, otherwise `None`.""" banner_hash: typing.Optional[str] = attr.field(eq=False, hash=False, repr=False) """Banner hash of the user, if they have one, otherwise `None`""" accent_color: typing.Optional[colors.Color] = attr.field(eq=False, hash=False, repr=False) """The custom banner color for the user, if set. The official client will decide the default color if not set. """ is_bot: bool = attr.field(eq=False, hash=False, repr=True) """`True` if this user is a bot account, `False` otherwise.""" is_system: bool = attr.field(eq=False, hash=False, repr=True) """`True` if this user is a system account, `False` otherwise.""" flags: UserFlag = attr.field(eq=False, hash=False, repr=True) """The public flags for this user.""" @attr.define(hash=True, kw_only=True, weakref_slot=False)
[docs]class OwnUser(UserImpl): """Represents a user with extended OAuth2 information."""
[docs] is_mfa_enabled: bool = attr.field(eq=False, hash=False, repr=False)
"""Whether the user's account has multi-factor authentication enabled."""
[docs] locale: typing.Optional[typing.Union[str, locales.Locale]] = attr.field(eq=False, hash=False, repr=False)
"""The user's set locale. This is not provided in the `READY` event. """
[docs] is_verified: typing.Optional[bool] = attr.field(eq=False, hash=False, repr=False)
"""Whether the email for this user's account has been verified. Will be `None` if retrieved through the OAuth2 flow without the `email` scope. """
[docs] email: typing.Optional[str] = attr.field(eq=False, hash=False, repr=False)
"""The user's set email. Will be `None` if retrieved through OAuth2 flow without the `email` scope. Will always be `None` for bot users. """
[docs] premium_type: typing.Union[PremiumType, int, None] = attr.field(eq=False, hash=False, repr=False)
"""The type of Nitro Subscription this user account had. This will always be `None` for bots. """
[docs] async def fetch_self(self) -> OwnUser: """Get this user's up-to-date object. Returns ------- hikari.users.OwnUser The requested user object. Raises ------ hikari.errors.UnauthorizedError If you are unauthorized to make the request (invalid/missing token). hikari.errors.RateLimitTooLongError Raised in the event that a rate limit occurs that is longer than `max_rate_limit` when making a request. hikari.errors.InternalServerError If an internal error occurs on Discord while handling the request. """ return await self.app.rest.fetch_my_user()
[docs] async def fetch_dm_channel(self) -> typing.NoReturn: raise TypeError("Unable to fetch your own DM channel")
[docs] async def send( self, content: undefined.UndefinedOr[typing.Any] = undefined.UNDEFINED, *, attachment: undefined.UndefinedOr[files.Resourceish] = undefined.UNDEFINED, attachments: undefined.UndefinedOr[typing.Sequence[files.Resourceish]] = undefined.UNDEFINED, component: undefined.UndefinedOr[special_endpoints.ComponentBuilder] = undefined.UNDEFINED, components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED, embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED, embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED, nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED, tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED, reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages.PartialMessage]] = undefined.UNDEFINED, reply_must_exist: undefined.UndefinedOr[bool] = undefined.UNDEFINED, mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED, mentions_reply: undefined.UndefinedOr[bool] = undefined.UNDEFINED, user_mentions: undefined.UndefinedOr[ typing.Union[snowflakes.SnowflakeishSequence[PartialUser], bool] ] = undefined.UNDEFINED, role_mentions: undefined.UndefinedOr[ typing.Union[snowflakes.SnowflakeishSequence[guilds.PartialRole], bool] ] = undefined.UNDEFINED, flags: typing.Union[undefined.UndefinedType, int, messages.MessageFlag] = undefined.UNDEFINED, ) -> typing.NoReturn: raise TypeError("Unable to send a DM to yourself")