Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
c68ede8
introduce replace_item and some additional patches
NeloBlivion Dec 23, 2025
f757ac0
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 23, 2025
56b5b46
cls
NeloBlivion Dec 23, 2025
ec0ddfa
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 23, 2025
deeed74
rework underlying
NeloBlivion Dec 23, 2025
c085789
Merge branch 'master' into cv2_fixes
NeloBlivion Dec 23, 2025
130fab8
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 23, 2025
45da8fe
maybe fixed
NeloBlivion Dec 23, 2025
aadf0ed
,
NeloBlivion Dec 23, 2025
3289505
or
NeloBlivion Dec 23, 2025
d984274
spacing
NeloBlivion Dec 23, 2025
97db5d4
replace and remove on gallery
NeloBlivion Dec 23, 2025
3775262
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 23, 2025
a5d076c
index
NeloBlivion Dec 23, 2025
22aaeb1
select_type
NeloBlivion Dec 23, 2025
d06f364
row
NeloBlivion Dec 23, 2025
6906ae5
type
NeloBlivion Dec 23, 2025
d993efe
cl
NeloBlivion Dec 24, 2025
3fc8b2a
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 24, 2025
cb403d2
fix(actions): rework release workflow (#3034)
Lulalaby Dec 24, 2025
7d65cf4
Merge branch 'master' into cv2_fixes
Paillat-dev Dec 24, 2025
5bfee91
Merge branch 'master' into cv2_fixes
Lulalaby Dec 24, 2025
d16f857
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 24, 2025
8f048e1
Merge branch 'master' into cv2_fixes
Lulalaby Dec 24, 2025
1074d51
Merge branch 'master' into cv2_fixes
Lulalaby Dec 24, 2025
91390cb
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 24, 2025
2f48c7b
revert cl
NeloBlivion Dec 24, 2025
ea33a62
files
NeloBlivion Dec 24, 2025
2ba67c3
file again
NeloBlivion Dec 24, 2025
3c49f09
one more
NeloBlivion Dec 24, 2025
738b6ee
Merge branch 'master' into cv2_fixes
NeloBlivion Dec 24, 2025
1ae6835
cl
NeloBlivion Dec 24, 2025
eb9fa92
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 24, 2025
2488e63
buildout for new features & items aliases
NeloBlivion Dec 25, 2025
5ebdeaa
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 25, 2025
050f639
fix
NeloBlivion Dec 25, 2025
d831318
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 25, 2025
6eb6086
NeloBlivion Dec 25, 2025
6496c4c
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 25, 2025
de61d8e
fix modal typing
NeloBlivion Dec 29, 2025
718fe73
Merge branch 'master' into cv2_fixes
NeloBlivion Dec 29, 2025
bc785f4
Merge branch 'master' into cv2_fixes
NeloBlivion Dec 29, 2025
66a4db6
correct return types
NeloBlivion Dec 29, 2025
de42bb6
fix modal error docs
NeloBlivion Dec 29, 2025
c4000cb
remove incorrect release script
NeloBlivion Dec 29, 2025
2f3da73
Merge branch 'master' into cv2_fixes
NeloBlivion Jan 4, 2026
c7a3983
fix paginator
NeloBlivion Jan 4, 2026
5e02950
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 4, 2026
572fe5a
doc fix
NeloBlivion Jan 8, 2026
d9b9c1f
add convenience methods to DesignerView
NeloBlivion Jan 8, 2026
a054102
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 8, 2026
9adf32d
Merge branch 'master' into cv2_fixes
Dorukyum Jan 12, 2026
2728982
adjust underlying order
NeloBlivion Jan 17, 2026
534f743
fix fileupload
NeloBlivion Jan 17, 2026
7cf34bd
Merge branch 'master' into cv2_fixes
NeloBlivion Jan 17, 2026
9db2632
misc
NeloBlivion Jan 17, 2026
d6e287d
view.add_row
NeloBlivion Jan 17, 2026
eba1d9e
Merge branch 'master' into cv2_fixes
NeloBlivion Jan 19, 2026
4dccef7
Merge branch 'Pycord-Development:master' into cv2_fixes
NeloBlivion Jan 21, 2026
6e5507a
misc fixes
NeloBlivion Jan 23, 2026
01e95f8
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2026
0678073
fix
NeloBlivion Jan 29, 2026
2a4e4d5
adjust legacy item attributes
NeloBlivion Jan 30, 2026
71a0e81
Merge branch 'master' into cv2_fixes
NeloBlivion Jan 30, 2026
99ecd2c
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 30, 2026
23b7743
width docs adjustment
NeloBlivion Jan 30, 2026
fb2e439
Merge branch 'master' into cv2_fixes
NeloBlivion Feb 7, 2026
23be55b
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 7, 2026
7c69fb0
Merge branch 'master' into view_additions
NeloBlivion Feb 8, 2026
12caf98
Update CHANGELOG.md
NeloBlivion Feb 20, 2026
4b71a77
Merge branch 'master' into view_additions
NeloBlivion Feb 20, 2026
5d8609c
Merge branch 'master' into view_additions
NeloBlivion Feb 23, 2026
f12147d
Merge branch 'master' into view_additions
NeloBlivion Feb 26, 2026
db9a63c
Message.get_view
NeloBlivion Feb 26, 2026
66054a0
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2026
a985986
Merge branch 'master' into view_additions
Lulalaby Feb 28, 2026
2fb1ac5
Merge branch 'master' into view_additions
Paillat-dev Mar 2, 2026
d46f6e8
Merge branch 'master' into view_additions
NeloBlivion Mar 4, 2026
6d30016
adjust imports
NeloBlivion Mar 4, 2026
065e1a1
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 4, 2026
68e8252
sure whatever i hate this
NeloBlivion Mar 4, 2026
4c89957
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 4, 2026
c222ded
doc fixes
NeloBlivion Mar 13, 2026
774deea
Merge branch 'master' into view_additions
NeloBlivion Mar 16, 2026
8dfd1dc
types
NeloBlivion Mar 16, 2026
d44dbf7
remove silly check
NeloBlivion Mar 16, 2026
e7399fc
complete base add_item
NeloBlivion Mar 16, 2026
33c2b4a
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
d4f4da7
section, container, row, len
NeloBlivion Mar 16, 2026
2d43d5a
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
ea7aa0e
modal ext
NeloBlivion Mar 16, 2026
d414afc
-> Self
NeloBlivion Mar 16, 2026
1e14564
improve validation
NeloBlivion Mar 16, 2026
ee76172
fix item resolution
NeloBlivion Mar 16, 2026
411dabe
implement DesignerModal.from_dict
NeloBlivion Mar 16, 2026
836d3b0
]
NeloBlivion Mar 16, 2026
6c67038
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
6bf58d1
timeout
NeloBlivion Mar 16, 2026
0133164
.get
NeloBlivion Mar 16, 2026
6b23025
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
b83dc4c
get("id")
NeloBlivion Mar 16, 2026
37ff1d7
complete get_item
NeloBlivion Mar 16, 2026
88c9edd
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
83d621c
console itemgett
NeloBlivion Mar 16, 2026
3a29103
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
5ba58c3
final
NeloBlivion Mar 16, 2026
c2b0286
i cannot see
NeloBlivion Mar 16, 2026
f86e96e
none
NeloBlivion Mar 16, 2026
8c8346a
return
NeloBlivion Mar 16, 2026
c81235b
any
NeloBlivion Mar 16, 2026
ccbfa90
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2026
b9f0db7
update exceptions
NeloBlivion Mar 16, 2026
754894a
Merge branch 'master' into view_additions
NeloBlivion Mar 17, 2026
7a34cea
docs
NeloBlivion Mar 20, 2026
f95ded9
Merge branch 'master' into view_additions
NeloBlivion Mar 21, 2026
2ffe35f
unfurledmedia convenience
NeloBlivion Mar 21, 2026
094f6b0
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 21, 2026
377e536
resolved name
NeloBlivion Mar 21, 2026
98917bd
docs
NeloBlivion Mar 21, 2026
75f7a39
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 21, 2026
44e9611
state gate
NeloBlivion Mar 21, 2026
29573a6
default filename
NeloBlivion Mar 21, 2026
c16fd6f
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 21, 2026
2542ad5
remove_item
NeloBlivion Mar 21, 2026
5dd5ec3
avoid tragedy oh god
NeloBlivion Mar 21, 2026
abc3468
Merge branch 'master' into view_additions
NeloBlivion Mar 21, 2026
27a15b5
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 21, 2026
edda650
Merge branch 'master' into view_additions
NeloBlivion Mar 22, 2026
e3a0dc7
Merge branch 'master' into view_additions
NeloBlivion Mar 24, 2026
79252f4
Merge branch 'master' into view_additions
Lulalaby Mar 25, 2026
569941e
Merge branch 'master' into view_additions
NeloBlivion Mar 26, 2026
5d15471
Merge branch 'master' into view_additions
NeloBlivion Mar 27, 2026
d84a117
simplify param resolution
NeloBlivion Mar 27, 2026
521e60e
clarify len
NeloBlivion Mar 27, 2026
6c3f8df
Update discord/ui/modal.py
NeloBlivion Mar 27, 2026
cae8c7a
Update discord/ui/media_gallery.py
NeloBlivion Mar 27, 2026
11635d8
Update discord/types/components.py
NeloBlivion Mar 27, 2026
d36f6e2
extend cl
NeloBlivion Mar 27, 2026
aa3be7d
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 27, 2026
04bd81d
improve resolved_name
NeloBlivion Mar 27, 2026
c4a127b
improve exceptions
NeloBlivion Mar 27, 2026
ec5b645
AttributeError
NeloBlivion Mar 27, 2026
9a91538
fix view check
NeloBlivion Mar 27, 2026
cf57dc9
final mediagallery convenience
NeloBlivion Mar 27, 2026
21d6748
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 27, 2026
9974002
assets
NeloBlivion Mar 27, 2026
3b64337
,
NeloBlivion Mar 27, 2026
67773c3
meth
NeloBlivion Mar 31, 2026
c128d4e
Merge branch 'master' into view_additions
NeloBlivion Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ These changes are available on the `master` branch, but have not yet been releas

### Added

- Added `replace_item` to `DesignerView`, `Section`, `Container`, `ActionRow`, &
`MediaGallery` ([#3093](https://github.com/Pycord-Development/pycord/pull/3093))
- Added `index`, `before`, and `after` to `add_item` functions
([#3093](https://github.com/Pycord-Development/pycord/pull/3093))
- Added arbitrary kwarg support to `get_item` functions
([#3093](https://github.com/Pycord-Development/pycord/pull/3093))
- Added `Message.get_view`
([#3093](https://github.com/Pycord-Development/pycord/pull/3093))
- Added `from_dict`, `add_label`, & `add_text` to `DesignerModal`
([#3093](https://github.com/Pycord-Development/pycord/pull/3093))
- Added `read`, `save`, and `to_file` to `UnfurledMediaItem`
([#3093](https://github.com/Pycord-Development/pycord/pull/3093))

### Changed

### Fixed
Expand Down
7 changes: 7 additions & 0 deletions discord/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io
import os
from typing import TYPE_CHECKING, Any, Literal
from urllib.parse import urlparse

import yarl

Expand Down Expand Up @@ -122,6 +123,12 @@ async def save(
with open(fp, "wb") as f:
return f.write(data)

@property
def cdn_name(self) -> str | None:
"""Attempts to return the filename within this asset's URL, if present."""
parsed = urlparse(self.url)
return os.path.basename(parsed.path)


class Asset(AssetMixin):
"""Represents a CDN asset on Discord.
Expand Down
141 changes: 140 additions & 1 deletion discord/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@

from __future__ import annotations

import io
from os import PathLike, path
from typing import TYPE_CHECKING, Any, ClassVar, Iterator, TypeVar, overload
from urllib.parse import urlparse

from .asset import AssetMixin
from .colour import Colour
Expand All @@ -38,6 +41,7 @@
SeparatorSpacingSize,
try_enum,
)
from .file import File
from .flags import AttachmentFlags
from .partial_emoji import PartialEmoji, _EmojiTag
from .utils import MISSING, find, get_slots
Expand Down Expand Up @@ -956,6 +960,138 @@ def url(self, value: str) -> None:
value if value and value.startswith("attachment://") else None
)

@property
def resolved_name(self) -> str | None:
"""Attempts to return the filename within this media's URL, if present."""
if self.url.startswith("attachment://"):
return self.url.replace("attachment://", "")
else:
parsed = urlparse(self.url)
return path.basename(parsed.path)

async def save(
self,
fp: io.BufferedIOBase | PathLike,
*,
seek_begin: bool = True,
) -> int:
"""|coro|

Saves this media into a file-like object.

Parameters
----------
fp: Union[:class:`io.BufferedIOBase`, :class:`os.PathLike`]
The file-like object to save this media to or the filename
to use. If a filename is passed then a file is created with that
filename and used instead.
seek_begin: :class:`bool`
Whether to seek to the beginning of the file after saving is
successfully done.

Returns
-------
:class:`int`
The number of bytes written.

Raises
------
HTTPException
Saving the media failed.
NotFound
The media was deleted.
ValueError
You attempted to download from a local ``attachment://`` URL.
"""
data = await self.read()

if isinstance(fp, io.BufferedIOBase):
written = fp.write(data)
if seek_begin:
fp.seek(0)
return written
else:
with open(fp, "wb") as f:
return f.write(data)

async def read(self) -> bytes:
"""|coro|

Retrieves the content of this media as a :class:`bytes` object.

Returns
-------
:class:`bytes`
The contents of the media.

Raises
------
HTTPException
Downloading the media failed.
Forbidden
You do not have permissions to access this media.
NotFound
The media was deleted.
ValueError
You attempted to download from a local ``attachment://`` URL.
"""
if self.url.startswith("attachment://"):
raise ValueError("cannot download a local media URL.")
if not self._state:
raise RuntimeError("can only read when media is received from Discord.")
return await self._state.http.get_from_cdn(self.url)

async def to_file(
self,
filename: str | None = None,
*,
description: str | None = None,
spoiler: bool = False,
) -> File:
"""|coro|

Converts the media into a :class:`discord.File` suitable for sending via
:meth:`abc.Messageable.send`.

Parameters
----------
filename: :class:`str`
The name to initialize this file with. Defaults to :attr:`resolved_name` if available.
description: Optional[:class:`str`]
The description of this file.
spoiler: :class:`bool`
Whether the file is a spoiler.

Returns
-------
:class:`File`
The media as a file suitable for sending.

Raises
------
HTTPException
Downloading the media failed.
Forbidden
You do not have permissions to access this media
NotFound
The media was deleted.
ValueError
You attempted to download from a local ``attachment://`` URL.
"""
name = filename or self.resolved_name
if not name:
raise ValueError(
"no resolved_name available, please provide filename manually."
)

data = await self.read()
return File(
io.BytesIO(data),
filename=name,
spoiler=spoiler,
description=description,
)

@classmethod
def from_dict(cls, data: UnfurledMediaItemPayload, state=None) -> UnfurledMediaItem:
r = cls(data.get("url"))
Expand Down Expand Up @@ -1050,6 +1186,9 @@ def __init__(self, url, *, description=None, spoiler=False):
self.description: str | None = description
self.spoiler: bool = spoiler

def __repr__(self) -> str:
return f"<MediaGalleryItem url={self.url!r} spoiler={self.spoiler!r}>"

@property
def url(self) -> str:
return self.media.url
Expand Down Expand Up @@ -1347,7 +1486,7 @@ class Label(Component):

def __init__(self, data: LabelComponentPayload):
self.type: ComponentType = try_enum(ComponentType, data["type"])
self.id: int = data["id"]
self.id: int = data.get("id")
self.component: Component = _component_factory(data["component"])
self.label: str = data["label"]
self.description: str | None = data.get("description")
Expand Down
8 changes: 8 additions & 0 deletions discord/ext/pages/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import contextlib
from typing import List

from typing_extensions import Self

import discord
from discord.errors import DiscordException
from discord.ext.bridge import BridgeContext
Expand Down Expand Up @@ -916,6 +918,12 @@ def update_custom_view(self, custom_view: discord.ui.View):
for item in custom_view.children:
self.add_item(item)

def clear_items(self) -> Self:
# Necessary override due to behavior of Item.parent, see #3057
self.children.clear()
self._View__weights.clear()
return self

def get_page_group_content(self, page_group: PageGroup) -> list[Page]:
"""Returns a converted list of `Page` objects for the given page group based on the content of its pages."""
return [self.get_page_content(page) for page in page_group.pages]
Expand Down
31 changes: 30 additions & 1 deletion discord/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
from .types.snowflake import SnowflakeList
from .types.threads import ThreadArchiveDuration
from .types.user import User as UserPayload
from .ui.view import BaseView
from .ui.view import BaseView, DesignerView
from .user import User

MR = TypeVar("MR", bound="MessageReference")
Expand Down Expand Up @@ -2345,6 +2345,35 @@ def get_component(self, id: str | int) -> Component | None:
return component
return None

def get_view(
self, cls: BaseView | None = None, force: bool = False
) -> DesignerView | BaseView | None:
"""Retrieve this message's view from the ViewStore. If there is no stored view, a new view will be returned if :attr:`components` is not empty.

Parameters
----------
cls
The class that will be used to generate the new view.
By default, this is :class:`discord.ui.DesignerView`. Should a custom
class be provided, it must inherit from :class:`discord.ui.BaseView`
and properly implement ``from_message``.
force: :class:`bool`
When set to ``True``, this will ignore the ViewStore and forcibly create a new view.

Returns
-------
Optional[Union[:class:`discord.ui.DesignerView`, :class:`discord.ui.BaseView`]]
The view belonging to this message, if it exists or there are components available to create a new view.
"""
v = self._state.get_message_view(self.id) if not force else None
if not v and self.components:
if not cls:
from .ui.view import DesignerView

cls = DesignerView
v = cls.from_message(self)
return v


class PartialMessage(Hashable):
"""Represents a partial message to aid with working messages when only
Expand Down
3 changes: 3 additions & 0 deletions discord/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@ def store_view(self, view: BaseView, message_id: int | None = None) -> None:
def purge_message_view(self, message_id: int) -> None:
self._view_store.remove_message_view(message_id)

def get_message_view(self, message_id: int) -> BaseView | None:
return self._view_store.get_message_view(message_id)

def store_modal(self, modal: BaseModal, message_id: int) -> None:
self._modal_store.add_modal(modal, message_id)

Expand Down
12 changes: 12 additions & 0 deletions discord/types/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class BaseComponent(TypedDict):
id: NotRequired[int]


class Modal(TypedDict):
custom_id: str
title: str
components: list[AllowedModalComponents]


class ActionRow(BaseComponent):
type: Literal[1]
components: list[AllowedActionRowComponents]
Expand Down Expand Up @@ -254,3 +260,9 @@ class CheckboxComponent(BaseComponent):
CheckboxComponent,
CheckboxGroupComponent,
]

AllowedModalComponents = Union[
ActionRow,
LabelComponent,
TextDisplayComponent,
]
Loading
Loading