# SPDX-License-Identifier: MIT from __future__ import annotations import copy from typing import TYPE_CHECKING, ClassVar, Optional, Tuple from ..components import FileComponent, UnfurledMediaItem, handle_media_item_input from ..enums import ComponentType from ..utils import MISSING from .item import UIComponent if TYPE_CHECKING: from typing_extensions import Self from ..components import LocalMediaItemInput __all__ = ("File",) class File(UIComponent): """Represents a UI file component. .. versionadded:: 2.11 Parameters ---------- file: Union[:class:`str`, :class:`.UnfurledMediaItem`] The file to display. This **only** supports attachment references (i.e. using the ``attachment://`` syntax), not arbitrary URLs. spoiler: :class:`bool` Whether the file is marked as a spoiler. Defaults to ``False``. id: :class:`int` The numeric identifier for the component. Must be unique within the message. If set to ``0`` (the default) when sending a component, the API will assign sequential identifiers to the components in the message. """ __repr_attributes__: ClassVar[Tuple[str, ...]] = ( "file", "spoiler", ) # We have to set this to MISSING in order to overwrite the abstract property from UIComponent _underlying: FileComponent = MISSING def __init__( self, file: LocalMediaItemInput, *, spoiler: bool = False, id: int = 0, ) -> None: file_media = handle_media_item_input(file) if not file_media.url.startswith("attachment://"): raise ValueError( "File component only supports `attachment://` references, not external media URLs" ) self._underlying = FileComponent._raw_construct( type=ComponentType.file, id=id, file=file_media, spoiler=spoiler, name=None, size=None, ) @property def file(self) -> UnfurledMediaItem: """:class:`.UnfurledMediaItem`: The file to display.""" return self._underlying.file @file.setter def file(self, value: LocalMediaItemInput) -> None: file_media = handle_media_item_input(value) if not file_media.url.startswith("attachment://"): raise ValueError( "File component only supports `attachment://` references, not external media URLs" ) self._underlying.file = file_media @property def spoiler(self) -> bool: """:class:`bool`: Whether the file is marked as a spoiler.""" return self._underlying.spoiler @spoiler.setter def spoiler(self, value: bool) -> None: self._underlying.spoiler = value @property def name(self) -> Optional[str]: """Optional[:class:`str`]: The name of the file. This is available in objects from the API, and ignored when sending. """ return self._underlying.name @property def size(self) -> Optional[int]: """Optional[:class:`int`]: The size of the file. This is available in objects from the API, and ignored when sending. """ return self._underlying.size @classmethod def from_component(cls, file: FileComponent) -> Self: media = file.file if not media.url.startswith("attachment://") and file.name: # turn cdn url into `attachment://` url, retain other fields media = copy.copy(media) media.url = f"attachment://{file.name}" self = cls( file=media, spoiler=file.spoiler, id=file.id, ) # copy read-only fields self._underlying.name = file.name self._underlying.size = file.size return self