Skip to content

feat: add Message.get_component and related functions #2849

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ These changes are available on the `master` branch, but have not yet been releas
([#2817](https://github.com/Pycord-Development/pycord/pull/2817))
- Added role gradients support with `Role.colours` and the `RoleColours` class.
([#2818](https://github.com/Pycord-Development/pycord/pull/2818))
- Added `get_component` to `Message`, `Section`, `Container` and `ActionRow`.
([#2849](https://github.com/Pycord-Development/pycord/pull/2849))

### Fixed

Expand Down
69 changes: 68 additions & 1 deletion discord/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
)
from .flags import AttachmentFlags
from .partial_emoji import PartialEmoji, _EmojiTag
from .utils import MISSING, get_slots
from .utils import MISSING, find, get_slots

if TYPE_CHECKING:
from .emoji import AppEmoji, GuildEmoji
Expand Down Expand Up @@ -187,6 +187,25 @@ def to_dict(self) -> ActionRowPayload:
def walk_components(self) -> Iterator[Component]:
yield from self.children

def get_component(self, id: str | int) -> Component | None:
"""Get a component from this action row. Roughly equivalent to `utils.get(row.children, ...)`.
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.

Parameters
----------
id: Union[:class:`str`, :class:`int`]
The custom_id or id of the component to get.

Returns
-------
Optional[:class:`Component`]
The component with the matching ``id`` or ``custom_id`` if it exists.
"""
if not id:
return None
attr = "id" if isinstance(id, int) else "custom_id"
return find(lambda i: getattr(i, attr, None) == id, self.children)

@classmethod
def with_components(cls, *components, id=None):
return cls._raw_construct(
Expand Down Expand Up @@ -620,6 +639,28 @@ def walk_components(self) -> Iterator[Component]:
yield from r + [self.accessory]
yield from r

def get_component(self, id: str | int) -> Component | None:
"""Get a component from this section. Roughly equivalent to `utils.get(section.walk_components(), ...)`.
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.

Parameters
----------
id: Union[:class:`str`, :class:`int`]
The custom_id or id of the component to get.

Returns
-------
Optional[:class:`Component`]
The component with the matching ``id`` or ``custom_id`` if it exists.
"""
if not id:
return None
attr = "id" if isinstance(id, int) else "custom_id"
if self.accessory and id == getattr(self.accessory, attr, None):
return self.accessory
component = find(lambda i: getattr(i, attr, None) == id, self.components)
return component


class TextDisplay(Component):
"""Represents a Text Display from Components V2.
Expand Down Expand Up @@ -1036,6 +1077,32 @@ def walk_components(self) -> Iterator[Component]:
else:
yield c

def get_component(self, id: str | int) -> Component | None:
"""Get a component from this container. Roughly equivalent to `utils.get(container.components, ...)`.
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.
This method will also search for nested components.

Parameters
----------
id: Union[:class:`str`, :class:`int`]
The custom_id or id of the component to get.

Returns
-------
Optional[:class:`Component`]
The component with the matching ``id`` or ``custom_id`` if it exists.
"""
if not id:
return None
attr = "id" if isinstance(id, int) else "custom_id"
component = find(lambda i: getattr(i, attr, None) == id, self.components)
if not component:
for i in self.components:
if hasattr(i, "get_component"):
if component := i.get_component(id):
return component
return component


COMPONENT_MAPPINGS = {
1: ActionRow,
Expand Down
28 changes: 27 additions & 1 deletion discord/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from .reaction import Reaction
from .sticker import StickerItem
from .threads import Thread
from .utils import MISSING, escape_mentions
from .utils import MISSING, escape_mentions, find

if TYPE_CHECKING:
from .abc import (
Expand Down Expand Up @@ -2211,6 +2211,32 @@ def to_message_reference_dict(

return data

def get_component(self, id: str | int) -> Component | None:
"""Gets a component from this message. Roughly equal to `utils.get(message.components, ...)`.
If an :class:`int` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.
This method will also search nested components.

Parameters
----------
id: Union[:class:`str`, :class:`int`]
The id or custom_id the item to get

Returns
-------
Optional[:class:`Component`]
The component with the matching ``custom_id`` or ``id`` if it exists.
"""
if not id:
return None
attr = "id" if isinstance(id, int) else "custom_id"
component = find(lambda i: getattr(i, attr, None) == id, self.components)
if not component:
for i in self.components:
if hasattr(i, "get_component"):
if component := i.get_component(id):
return component
return component


class PartialMessage(Hashable):
"""Represents a partial message to aid with working messages when only
Expand Down
2 changes: 1 addition & 1 deletion discord/ui/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def get_item(self, custom_id: str | int) -> Item[V] | None:

Parameters
----------
custom_id: :class:`str`
custom_id: Union[:class:`str`, :class:`int`]
The custom_id of the item to get

Returns
Expand Down
Loading