Skip to content

feat: Support default_values for Select of type ComponentType.mentionable_select and ComponentType.channel_select #2793

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 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ These changes are available on the `master` branch, but have not yet been releas
([#2747](https://github.com/Pycord-Development/pycord/pull/2747))
- Added `discord.Interaction.created_at`.
([#2801](https://github.com/Pycord-Development/pycord/pull/2801))
- Added the ability to pass default values into `ui.Select` of type
`ComponentType.channel_select`, `ComponentType.user_select`,
`ComponentType.role_select` and `ComponentType.mentionable_select`

### Fixed

Expand Down
82 changes: 81 additions & 1 deletion discord/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,24 @@

from typing import TYPE_CHECKING, Any, ClassVar, TypeVar

from .abc import GuildChannel
from .emoji import AppEmoji, GuildEmoji
from .enums import ButtonStyle, ChannelType, ComponentType, InputTextStyle, try_enum
from .member import Member
from .partial_emoji import PartialEmoji, _EmojiTag
from .role import Role
from .threads import Thread
from .user import User
from .utils import MISSING, get_slots

if TYPE_CHECKING:
from .emoji import AppEmoji, GuildEmoji
from .types.components import ActionRow as ActionRowPayload
from .types.components import ButtonComponent as ButtonComponentPayload
from .types.components import Component as ComponentPayload
from .types.components import InputText as InputTextComponentPayload
from .types.components import SelectDefaultValue as SelectDefaultValuePayload
from .types.components import SelectMenu as SelectMenuPayload
from .types.components import SelectMenuDefaultValueType
from .types.components import SelectOption as SelectOptionPayload

__all__ = (
Expand All @@ -46,6 +53,7 @@
"Button",
"SelectMenu",
"SelectOption",
"SelectDefaultValue",
"InputText",
)

Expand Down Expand Up @@ -328,6 +336,9 @@ class SelectMenu(Component):
except for :attr:`ComponentType.channel_select`.
disabled: :class:`bool`
Whether the select is disabled or not.
default_values: List[:class:`SelectDefaultValue`]
A list of options that are selected by default for select menus of type user, role, channel, and mentionable.
Will be an empty list for the component type :attr:`ComponentType.string_select`.
"""

__slots__: tuple[str, ...] = (
Expand All @@ -338,6 +349,7 @@ class SelectMenu(Component):
"options",
"channel_types",
"disabled",
"default_values",
)

__repr_info__: ClassVar[tuple[str, ...]] = __slots__
Expand All @@ -355,6 +367,24 @@ def __init__(self, data: SelectMenuPayload):
self.channel_types: list[ChannelType] = [
try_enum(ChannelType, ct) for ct in data.get("channel_types", [])
]
_default_values = []
for d in data.get("default_values", []):
if isinstance(d, SelectDefaultValue):
_default_values.append(d)
elif isinstance(d, dict) and "id" in d and "type" in d:
_default_values.append(SelectDefaultValue(id=d["id"], type=d["type"]))
elif isinstance(d, (User, Member)):
_default_values.append(SelectDefaultValue(id=d.id, type="user"))
elif isinstance(d, Role):
_default_values.append(SelectDefaultValue(id=d.id, type="role"))
elif isinstance(d, (GuildChannel, Thread)):
_default_values.append(SelectDefaultValue(id=d.id, type="channel"))
else:
raise TypeError(
f"expected SelectDefaultValue, User, Member, Role, GuildChannel or Mentionable, not {d.__class__}"
)

self.default_values: list[SelectDefaultValue] = _default_values

def to_dict(self) -> SelectMenuPayload:
payload: SelectMenuPayload = {
Expand All @@ -371,6 +401,8 @@ def to_dict(self) -> SelectMenuPayload:
payload["channel_types"] = [ct.value for ct in self.channel_types]
if self.placeholder:
payload["placeholder"] = self.placeholder
if self.type is not ComponentType.string_select and self.default_values:
payload["default_values"] = [dv.to_dict() for dv in self.default_values]

return payload

Expand Down Expand Up @@ -494,6 +526,54 @@ def to_dict(self) -> SelectOptionPayload:
return payload


class SelectDefaultValue:
"""Represents a :class:`discord.SelectMenu` object's default option for user, role, channel, and mentionable select
menu types.

These can be created by users.

.. versionadded:: 2.7

Attributes
----------
id: :class:`int`
The snowflake ID of the default option.
type: :class:`SelectMenuDefaultValueType`
The type of the default value. This is not displayed to users.
"""

__slots__: tuple[str, ...] = (
"id",
"type",
)

def __init__(self, *, id: int, type: SelectMenuDefaultValueType) -> None:
self.id = id
self.type = type

def __repr__(self) -> str:
return "<SelectDefaultValue" f" id={self.id!r} type={self.type!r}>"

def __str__(self) -> str:
return f"{self.id} {self.type}"

@classmethod
def from_dict(cls, data: SelectDefaultValuePayload) -> SelectDefaultValue:

return cls(
id=data["id"],
type=data["type"],
)

def to_dict(self) -> SelectDefaultValuePayload:
payload: SelectDefaultValuePayload = {
"id": self.id,
"type": self.type.value,
}

return payload


def _component_factory(data: ComponentPayload) -> Component:
component_type = data["type"]
if component_type == 1:
Expand Down
12 changes: 12 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"InteractionContextType",
"PollLayoutType",
"MessageReferenceType",
"SelectMenuDefaultValueType",
)


Expand Down Expand Up @@ -1078,6 +1079,17 @@ class SubscriptionStatus(Enum):
inactive = 2


class SelectMenuDefaultValueType(Enum):
"""The type of a select menu's default value."""

Channel = "channel"
Role = "role"
User = "user"

def __str__(self):
return self.name


T = TypeVar("T")


Expand Down
7 changes: 7 additions & 0 deletions discord/types/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
ComponentType = Literal[1, 2, 3, 4]
ButtonStyle = Literal[1, 2, 3, 4, 5, 6]
InputTextStyle = Literal[1, 2]
SelectMenuDefaultValueType = Literal["channel", "role", "user"]


class ActionRow(TypedDict):
Expand Down Expand Up @@ -74,6 +75,11 @@ class SelectOption(TypedDict):
default: bool


class SelectDefaultValue(TypedDict):
id: Snowflake
type: SelectMenuDefaultValueType


class SelectMenu(TypedDict):
placeholder: NotRequired[str]
min_values: NotRequired[int]
Expand All @@ -83,6 +89,7 @@ class SelectMenu(TypedDict):
options: NotRequired[list[SelectOption]]
type: Literal[3, 5, 6, 7, 8]
custom_id: str
default_values: NotRequired[list[SelectDefaultValue]]


Component = Union[ActionRow, ButtonComponent, SelectMenu, InputText]
Loading