diff --git a/changes/1804.feature.md b/changes/1804.feature.md new file mode 100644 index 0000000000..3a7f9b5390 --- /dev/null +++ b/changes/1804.feature.md @@ -0,0 +1 @@ +Add `default_values` to autopopulating select menus diff --git a/changes/1804.removal.md b/changes/1804.removal.md new file mode 100644 index 0000000000..c2fc746732 --- /dev/null +++ b/changes/1804.removal.md @@ -0,0 +1 @@ +Deprecate `hikari.api.MessageActionRowBuilder.add_select_menu()` diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py index a33f446266..0919ad620e 100644 --- a/hikari/api/special_endpoints.py +++ b/hikari/api/special_endpoints.py @@ -46,6 +46,11 @@ "InteractionModalBuilder", "MessageActionRowBuilder", "ModalActionRowBuilder", + "SelectDefaultBuilder", + "AutoPopulatedSelectMenuBuilder", + "UserSelectMenuBuilder", + "RoleSelectMenuBuilder", + "MentionableSelectMenuBuilder", ) import abc @@ -1586,6 +1591,62 @@ def build(self) -> typing.MutableMapping[str, typing.Any]: """ +class SelectDefaultBuilder(abc.ABC, typing.Generic[components_.DefaultT]): + """Represents a default value for an auto-populated select.""" + + __slots__: typing.Sequence[str] = () + + @property + @abc.abstractmethod + def id(self) -> snowflakes.Snowflake: + """The Snowflake ID of the default.""" + + @property + @abc.abstractmethod + def type(self) -> components_.DefaultT: + """The type of the default.""" + + @abc.abstractmethod + def set_id(self, id_: snowflakes.Snowflakeish, /) -> Self: + """Set the ID of the default. + + Parameters + ---------- + id_ : hikari.snowflakes.Snowflakeish + The ID to set. + + Returns + ------- + SelectDefaultBuilder + The builder object to enable chained calls. + """ + + @abc.abstractmethod + def set_type(self, type_: components_.DefaultT, /) -> Self: + """Set the type of the default. + + Parameters + ---------- + type_ : hikari.interactions.commands.SelectDefaultType + The type to set. + + Returns + ------- + SelectDefaultBuilder + The builder object to enable chained calls. + """ + + @abc.abstractmethod + def build(self) -> typing.MutableMapping[str, typing.Any]: + """Build a JSON object from this builder. + + Returns + ------- + typing.MutableMapping[str, typing.Any] + The built json object representation of this builder. + """ + + class SelectMenuBuilder(ComponentBuilder, abc.ABC): """Builder class for a select menu.""" @@ -1763,7 +1824,37 @@ def add_option( """ -class ChannelSelectMenuBuilder(SelectMenuBuilder, abc.ABC): +class AutoPopulatedSelectMenuBuilder(SelectMenuBuilder, abc.ABC, typing.Generic[components_.DefaultT]): + """Builder class for an auto-populated select menu.""" + + __slots__: typing.Sequence[str] = () + + @property + @abc.abstractmethod + def default_values(self) -> undefined.UndefinedOr[typing.Sequence[SelectDefaultBuilder[components_.DefaultT]]]: + """Sequence of the default values set for this select menu.""" + + @abc.abstractmethod + def add_default_value(self, id: snowflakes.Snowflakeish, *, type: components_.DefaultT) -> Self: + """Add a default value to this menu. + + Parameters + ---------- + value : _OptionT + The ID of the option to add as a default value. + type : components_.DefaultT + The type of default value to add. + + Returns + ------- + AutoPopulatedSelectMenuBuilder + The select menu builder to enable call chaining. + """ + + +class ChannelSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[components_.SelectDefaultType.CHANNEL]], abc.ABC +): """Builder class for a channel select menu.""" __slots__: typing.Sequence[str] = () @@ -1789,6 +1880,35 @@ def set_channel_types(self, value: typing.Sequence[channels.ChannelType], /) -> """ +class RoleSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[components_.SelectDefaultType.ROLE]], abc.ABC +): + """Builder class for a role select menu.""" + + __slots__: typing.Sequence[str] = () + + +class UserSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[components_.SelectDefaultType.USER]], abc.ABC +): + """Builder class for a user select menu.""" + + __slots__: typing.Sequence[str] = () + + +class MentionableSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[ + typing.Union[ + typing.Literal[components_.SelectDefaultType.ROLE], typing.Literal[components_.SelectDefaultType.USER] + ] + ], + abc.ABC, +): + """Builder class for a mentionable select menu.""" + + __slots__: typing.Sequence[str] = () + + class TextInputBuilder(ComponentBuilder, abc.ABC): """Builder class for text inputs components.""" @@ -2068,6 +2188,7 @@ def add_link_button( The action row builder to enable chained calls. """ + # TODO: Remove in 2.0.0.dev126 @abc.abstractmethod def add_select_menu( self, @@ -2086,6 +2207,10 @@ def add_select_menu( `MessageActionRowBuilder.add_channel_menu` and `MessageActionRowBuilder.add_text_menu`. + .. deprecated:: 2.0.0.dev123 + Use the `add_*_menu()` method specific to the given select type instead. + This method will be removed in 2.0.0.dev126. + Parameters ---------- type_ : typing.Union[hikari.components.ComponentType, int] @@ -2113,6 +2238,131 @@ def add_select_menu( If an invalid select menu type is passed. """ + @abc.abstractmethod + def add_role_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[SelectDefaultBuilder[typing.Literal[components_.SelectDefaultType.ROLE]]] + ] = undefined.UNDEFINED, + ) -> Self: + """Add a role select menu component to this action row builder. + + Parameters + ---------- + custom_id : str + A developer-defined custom identifier used to identify which menu + triggered component interactions. + placeholder : hikari.undefined.UndefinedOr[str] + Placeholder text to show when no entries have been selected. + min_values : int + The minimum amount of entries which need to be selected. + max_values : int + The maximum amount of entries which can be selected. + is_disabled : bool + Whether this select menu should be marked as disabled. + default_values : + hikari.undefined.UndefinedOr[hikari.api.SelectDefaultBuilder[typing.Literal[hikari.components.SelectDefaultType.ROLE]]] + The default values for this menu. + + Returns + ------- + Self + The action row builder to enable chained calls. + """ + + @abc.abstractmethod + def add_user_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[SelectDefaultBuilder[typing.Literal[components_.SelectDefaultType.USER]]] + ] = undefined.UNDEFINED, + ) -> Self: + """Add a user select menu component to this action row builder. + + Parameters + ---------- + custom_id : str + A developer-defined custom identifier used to identify which menu + triggered component interactions. + placeholder : hikari.undefined.UndefinedOr[str] + Placeholder text to show when no entries have been selected. + min_values : int + The minimum amount of entries which need to be selected. + max_values : int + The maximum amount of entries which can be selected. + is_disabled : bool + Whether this select menu should be marked as disabled. + default_values : + hikari.undefined.UndefinedOr[hikari.api.SelectDefaultBuilder[typing.Literal[hikari.components.SelectDefaultType.USER]]] + The default values for this menu. + + Returns + ------- + Self + The action row builder to enable chained calls. + """ + + @abc.abstractmethod + def add_mentionable_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[ + SelectDefaultBuilder[ + typing.Literal[components_.SelectDefaultType.USER, components_.SelectDefaultType.ROLE], + ] + ] + ] = undefined.UNDEFINED, + ) -> Self: + """Add a mentionable select menu component to this action row builder. + + Parameters + ---------- + custom_id : str + A developer-defined custom identifier used to identify which menu + triggered component interactions. + placeholder : hikari.undefined.UndefinedOr[str] + Placeholder text to show when no entries have been selected. + min_values : int + The minimum amount of entries which need to be selected. + max_values : int + The maximum amount of entries which can be selected. + is_disabled : bool + Whether this select menu should be marked as disabled. + default_values : + hikari.undefined.UndefinedOr[ + hikari.api.SelectDefaultBuilder[ + typing.Literal[hikari.components.SelectDefaultType.USER, hikari.components.SelectDefaultType.ROLE] + ] + ] + The default values for this menu. + + Returns + ------- + Self + The action row builder to enable chained calls. + """ + @abc.abstractmethod def add_channel_menu( self, @@ -2124,6 +2374,9 @@ def add_channel_menu( min_values: int = 0, max_values: int = 1, is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[SelectDefaultBuilder[typing.Literal[components_.SelectDefaultType.CHANNEL],]] + ] = undefined.UNDEFINED, ) -> Self: """Add a channel select menu component to this action row builder. @@ -2145,6 +2398,9 @@ def add_channel_menu( The maximum amount of entries which can be selected. is_disabled : bool Whether this select menu should be marked as disabled. + default_values : + hikari.undefined.UndefinedOr[hikari.api.SelectDefaultBuilder[typing.Literal[hikari.components.SelectDefaultType.CHANNEL]]] + The default values for this menu. Returns ------- diff --git a/hikari/components.py b/hikari/components.py index 288cde751b..68e846869b 100644 --- a/hikari/components.py +++ b/hikari/components.py @@ -31,8 +31,14 @@ "ButtonStyle", "ButtonComponent", "SelectMenuOption", + "SelectDefaultValue", + "SelectDefaultType", "SelectMenuComponent", "TextSelectMenuComponent", + "AutoPopulatedSelectMenuComponent", + "MentionableSelectMenuComponent", + "UserSelectMenuComponent", + "RoleSelectMenuComponent", "ChannelSelectMenuComponent", "TextInputStyle", "TextInputComponent", @@ -50,6 +56,7 @@ from hikari import channels from hikari import emojis +from hikari import snowflakes from hikari.internal import enums @@ -128,6 +135,30 @@ class ComponentType(int, enums.Enum): """ +@typing.final +class SelectDefaultType(str, enums.Enum): + """The type of a select menu default value.""" + + ROLE = "role" + """The default value is a role.""" + + USER = "user" + """The default value is a user.""" + + CHANNEL = "channel" + """The default value is a channel.""" + + +DefaultT = typing.TypeVar( + "DefaultT", + bound=typing.Union[ + typing.Literal[SelectDefaultType.ROLE], + typing.Literal[SelectDefaultType.USER], + typing.Literal[SelectDefaultType.CHANNEL], + ], +) + + @typing.final class ButtonStyle(int, enums.Enum): """Enum of the available button styles. @@ -258,6 +289,17 @@ class SelectMenuOption: """Whether this option will be selected by default.""" +@attrs.define(hash=True, kw_only=True, weakref_slot=False) +class SelectDefaultValue(typing.Generic[DefaultT]): + """A default value for an auto-populated select menu component.""" + + id: snowflakes.Snowflakeish = attrs.field(hash=True) + """The ID of the option to set as the default.""" + + type: DefaultT = attrs.field(hash=True) + """The type of the default value.""" + + @attrs.define(hash=True, kw_only=True, weakref_slot=False) class SelectMenuComponent(PartialComponent): """Represents a select menu component.""" @@ -286,6 +328,17 @@ class SelectMenuComponent(PartialComponent): """Whether the select menu is disabled.""" +@attrs.define(hash=True, kw_only=True, weakref_slot=False) +class AutoPopulatedSelectMenuComponent(SelectMenuComponent, typing.Generic[DefaultT]): + """Represents a select menu component where the options are populated by Discord.""" + + default_values: typing.Sequence[SelectDefaultValue[DefaultT]] = attrs.field(eq=False) + """The default values for this menu. + + The number of values must be in the range of `min_values` to `max_values`. + """ + + @attrs.define(kw_only=True, weakref_slot=False) class TextSelectMenuComponent(SelectMenuComponent): """Represents a text select menu component.""" @@ -295,7 +348,24 @@ class TextSelectMenuComponent(SelectMenuComponent): @attrs.define(kw_only=True, weakref_slot=False) -class ChannelSelectMenuComponent(SelectMenuComponent): +class UserSelectMenuComponent(AutoPopulatedSelectMenuComponent[typing.Literal[SelectDefaultType.USER]]): + """Represents a user select menu component.""" + + +@attrs.define(kw_only=True, weakref_slot=False) +class RoleSelectMenuComponent(AutoPopulatedSelectMenuComponent[typing.Literal[SelectDefaultType.ROLE]]): + """Represents a role select menu component.""" + + +@attrs.define(kw_only=True, weakref_slot=False) +class MentionableSelectMenuComponent( + AutoPopulatedSelectMenuComponent[typing.Literal[SelectDefaultType.ROLE, SelectDefaultType.USER]] +): + """Represents a mentionable select menu component.""" + + +@attrs.define(kw_only=True, weakref_slot=False) +class ChannelSelectMenuComponent(AutoPopulatedSelectMenuComponent[typing.Literal[SelectDefaultType.CHANNEL]]): """Represents a channel select menu component.""" channel_types: typing.Sequence[typing.Union[int, channels.ChannelType]] = attrs.field(eq=False) diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index a5f6cebf7f..b7e53e0031 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -509,9 +509,9 @@ def __init__(self, app: traits.RESTAware) -> None: ] = { component_models.ComponentType.BUTTON: self._deserialize_button, component_models.ComponentType.TEXT_SELECT_MENU: self._deserialize_text_select_menu, - component_models.ComponentType.USER_SELECT_MENU: self._deserialize_select_menu, - component_models.ComponentType.ROLE_SELECT_MENU: self._deserialize_select_menu, - component_models.ComponentType.MENTIONABLE_SELECT_MENU: self._deserialize_select_menu, + component_models.ComponentType.USER_SELECT_MENU: self._deserialize_user_select_menu, + component_models.ComponentType.ROLE_SELECT_MENU: self._deserialize_role_select_menu, + component_models.ComponentType.MENTIONABLE_SELECT_MENU: self._deserialize_mentionable_select_menu, component_models.ComponentType.CHANNEL_SELECT_MENU: self._deserialize_channel_select_menu, } self._modal_component_type_mapping: typing.Dict[ @@ -2871,14 +2871,81 @@ def _deserialize_button(self, payload: data_binding.JSONObject) -> component_mod is_disabled=payload.get("disabled", False), ) - def _deserialize_select_menu(self, payload: data_binding.JSONObject) -> component_models.SelectMenuComponent: - return component_models.SelectMenuComponent( + def _deserialize_select_default_value( + self, payload: data_binding.JSONObject + ) -> component_models.SelectDefaultValue[typing.Any]: + return component_models.SelectDefaultValue( + id=snowflakes.Snowflake(payload["id"]), type=component_models.ComponentType(payload["type"]) + ) + + def _deserialize_user_select_menu( + self, payload: data_binding.JSONObject + ) -> component_models.UserSelectMenuComponent: + return component_models.UserSelectMenuComponent( type=component_models.ComponentType(payload["type"]), custom_id=payload["custom_id"], - placeholder=payload.get("placeholder"), + placeholder=payload.get("placeholder"), # type: ignore[arg-type] + min_values=payload.get("min_values", 1), + max_values=payload.get("max_values", 1), + is_disabled=payload.get("disabled", False), + default_values=[ + self._deserialize_select_default_value(default_value) + for default_value in payload.get("default_values", []) + ], + ) + + def _deserialize_role_select_menu( + self, payload: data_binding.JSONObject + ) -> component_models.RoleSelectMenuComponent: + return component_models.RoleSelectMenuComponent( + type=component_models.ComponentType(payload["type"]), + custom_id=payload["custom_id"], + placeholder=payload.get("placeholder"), # type: ignore[arg-type] + min_values=payload.get("min_values", 1), + max_values=payload.get("max_values", 1), + is_disabled=payload.get("disabled", False), + default_values=[ + self._deserialize_select_default_value(default_value) + for default_value in payload.get("default_values", []) + ], + ) + + def _deserialize_mentionable_select_menu( + self, payload: data_binding.JSONObject + ) -> component_models.MentionableSelectMenuComponent: + return component_models.MentionableSelectMenuComponent( + type=component_models.ComponentType(payload["type"]), + custom_id=payload["custom_id"], + placeholder=payload.get("placeholder"), # type: ignore[arg-type] + min_values=payload.get("min_values", 1), + max_values=payload.get("max_values", 1), + is_disabled=payload.get("disabled", False), + default_values=[ + self._deserialize_select_default_value(default_value) + for default_value in payload.get("default_values", []) + ], + ) + + def _deserialize_channel_select_menu( + self, payload: data_binding.JSONObject + ) -> component_models.ChannelSelectMenuComponent: + channel_types: typing.List[typing.Union[int, channel_models.ChannelType]] = [] + if "channel_types" in payload: + for channel_type in payload["channel_types"]: + channel_types.append(channel_models.ChannelType(channel_type)) + + return component_models.ChannelSelectMenuComponent( + type=component_models.ComponentType(payload["type"]), + custom_id=payload["custom_id"], + channel_types=channel_types, + placeholder=payload.get("placeholder"), # type: ignore[arg-type] min_values=payload.get("min_values", 1), max_values=payload.get("max_values", 1), is_disabled=payload.get("disabled", False), + default_values=[ + self._deserialize_select_default_value(default_value) + for default_value in payload.get("default_values", []) + ], ) def _deserialize_text_select_menu( @@ -2911,24 +2978,6 @@ def _deserialize_text_select_menu( is_disabled=payload.get("disabled", False), ) - def _deserialize_channel_select_menu( - self, payload: data_binding.JSONObject - ) -> component_models.ChannelSelectMenuComponent: - channel_types: typing.List[typing.Union[int, channel_models.ChannelType]] = [] - if "channel_types" in payload: - for channel_type in payload["channel_types"]: - channel_types.append(channel_models.ChannelType(channel_type)) - - return component_models.ChannelSelectMenuComponent( - type=component_models.ComponentType(payload["type"]), - custom_id=payload["custom_id"], - channel_types=channel_types, - placeholder=payload.get("placeholder"), - min_values=payload.get("min_values", 1), - max_values=payload.get("max_values", 1), - is_disabled=payload.get("disabled", False), - ) - def _deserialize_text_input(self, payload: data_binding.JSONObject) -> component_models.TextInputComponent: return component_models.TextInputComponent( type=component_models.ComponentType(payload["type"]), custom_id=payload["custom_id"], value=payload["value"] diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py index 4ac7df6096..bfeaf74720 100644 --- a/hikari/impl/special_endpoints.py +++ b/hikari/impl/special_endpoints.py @@ -41,11 +41,16 @@ "SelectMenuBuilder", "SelectOptionBuilder", "ChannelSelectMenuBuilder", + "UserSelectMenuBuilder", + "RoleSelectMenuBuilder", + "MentionableSelectMenuBuilder", "TextSelectMenuBuilder", "TextInputBuilder", "InteractionModalBuilder", "MessageActionRowBuilder", "ModalActionRowBuilder", + "SelectDefaultBuilder", + "AutoPopulatedSelectMenuBuilder", ) import asyncio @@ -68,6 +73,7 @@ from hikari.interactions import base_interactions from hikari.internal import attrs_extensions from hikari.internal import data_binding +from hikari.internal import deprecation from hikari.internal import mentions from hikari.internal import routes from hikari.internal import time @@ -1659,6 +1665,41 @@ def build(self) -> typing.MutableMapping[str, typing.Any]: return data +@attrs_extensions.with_copy +@attrs.define(kw_only=True, weakref_slot=False) +class SelectDefaultBuilder(special_endpoints.SelectDefaultBuilder[component_models.DefaultT]): + """Builder class for select defaults.""" + + _id: snowflakes.Snowflake = attrs.field(alias="id") + _type: component_models.DefaultT = attrs.field(alias="type") + + def __init__(self, id: snowflakes.Snowflakeish, *, type: component_models.DefaultT) -> None: + self._id = snowflakes.Snowflake(id) + self._type = type + + @property + def id(self) -> snowflakes.Snowflake: + return self._id + + @property + def type(self) -> component_models.DefaultT: + return self._type + + def set_id(self, id: snowflakes.Snowflakeish, /) -> Self: + self._id = snowflakes.Snowflake(id) + return self + + def set_type(self, type: component_models.DefaultT, /) -> Self: + self._type = type + return self + + def build(self) -> typing.MutableMapping[str, typing.Any]: + data = data_binding.JSONObjectBuilder() + data["id"] = self._id + data["type"] = self._type.value + return data + + @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) class SelectMenuBuilder(special_endpoints.SelectMenuBuilder): @@ -1827,7 +1868,52 @@ def build(self) -> typing.MutableMapping[str, typing.Any]: @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) -class ChannelSelectMenuBuilder(SelectMenuBuilder, special_endpoints.ChannelSelectMenuBuilder): +class AutoPopulatedSelectMenuBuilder( + SelectMenuBuilder, special_endpoints.AutoPopulatedSelectMenuBuilder[component_models.DefaultT] +): + """Builder class for auto-populated select menus.""" + + _default_values: undefined.UndefinedOr[ + typing.List[special_endpoints.SelectDefaultBuilder[component_models.DefaultT]] + ] = attrs.field(default=undefined.UNDEFINED, alias="default_values") + + @property + def default_values( + self, + ) -> undefined.UndefinedOr[typing.Sequence[special_endpoints.SelectDefaultBuilder[component_models.DefaultT]]]: + return self._default_values + + def add_raw_default_value( + self, value: special_endpoints.SelectDefaultBuilder[component_models.DefaultT], / + ) -> Self: + if self._default_values is undefined.UNDEFINED: + self._default_values = [] + + self._default_values.append(value) + return self + + def add_default_value(self, id: snowflakes.Snowflakeish, *, type: component_models.DefaultT) -> Self: + if self._default_values is undefined.UNDEFINED: + self._default_values = [] + + self._default_values.append(SelectDefaultBuilder(id=snowflakes.Snowflake(id), type=type)) + return self + + def build(self) -> typing.MutableMapping[str, typing.Any]: + data = super().build() + + if self._default_values is not undefined.UNDEFINED: + data["default_values"] = [value.build() for value in self._default_values] + + return data + + +@attrs_extensions.with_copy +@attrs.define(kw_only=True, weakref_slot=False) +class ChannelSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[component_models.SelectDefaultType.CHANNEL]], + special_endpoints.ChannelSelectMenuBuilder, +): """Builder class for channel select menus.""" _channel_types: typing.Sequence[channels.ChannelType] = attrs.field(alias="channel_types", factory=list) @@ -1849,6 +1935,71 @@ def build(self) -> typing.MutableMapping[str, typing.Any]: data["channel_types"] = self._channel_types return data + def add_default_value( + self, + id: snowflakes.Snowflakeish, + *, + type: typing.Literal[component_models.SelectDefaultType.CHANNEL] = component_models.SelectDefaultType.CHANNEL, + ) -> Self: + return super().add_default_value(id=id, type=type) + + +@attrs_extensions.with_copy +@attrs.define(kw_only=True, weakref_slot=False) +class UserSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[component_models.SelectDefaultType.USER]], + special_endpoints.UserSelectMenuBuilder, +): + """Builder class for user select menus.""" + + _type: typing.Literal[component_models.ComponentType.USER_SELECT_MENU] = attrs.field( + default=component_models.ComponentType.USER_SELECT_MENU, init=False + ) + + def add_default_value( + self, + id: snowflakes.Snowflakeish, + *, + type: typing.Literal[component_models.SelectDefaultType.USER] = component_models.SelectDefaultType.USER, + ) -> Self: + return super().add_default_value(id=id, type=type) + + +@attrs_extensions.with_copy +@attrs.define(kw_only=True, weakref_slot=False) +class RoleSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[typing.Literal[component_models.SelectDefaultType.ROLE]], + special_endpoints.RoleSelectMenuBuilder, +): + """Builder class for role select menus.""" + + _type: typing.Literal[component_models.ComponentType.ROLE_SELECT_MENU] = attrs.field( + default=component_models.ComponentType.ROLE_SELECT_MENU, init=False + ) + + def add_default_value( + self, + id: snowflakes.Snowflakeish, + *, + type: typing.Literal[component_models.SelectDefaultType.ROLE] = component_models.SelectDefaultType.ROLE, + ) -> Self: + return super().add_default_value(id=id, type=type) + + +@attrs_extensions.with_copy +@attrs.define(kw_only=True, weakref_slot=False) +class MentionableSelectMenuBuilder( + AutoPopulatedSelectMenuBuilder[ + typing.Literal[component_models.SelectDefaultType.ROLE, component_models.SelectDefaultType.USER] + ], + special_endpoints.MentionableSelectMenuBuilder, +): + """Builder class for mentionable select menus.""" + + _type: typing.Literal[component_models.ComponentType.MENTIONABLE_SELECT_MENU] = attrs.field( + default=component_models.ComponentType.MENTIONABLE_SELECT_MENU, init=False + ) + @attrs_extensions.with_copy @attrs.define(kw_only=True, weakref_slot=False) @@ -2017,6 +2168,11 @@ def add_select_menu( max_values: int = 1, is_disabled: bool = False, ) -> Self: + deprecation.warn_deprecated( + f"{type(self).__name__}.add_select_menu()", + removal_version="2.0.0.dev126", + additional_info="Use the non-generic add_*_menu methods instead.", + ) return self.add_component( SelectMenuBuilder( type=type_, @@ -2028,6 +2184,102 @@ def add_select_menu( ) ) + def add_user_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[ + special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.USER]] + ] + ] = undefined.UNDEFINED, + ) -> Self: + _default_values: undefined.UndefinedOr[ + typing.List[special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.USER]]] + ] = (list(default_values) if default_values is not undefined.UNDEFINED else undefined.UNDEFINED) + + return self.add_component( + UserSelectMenuBuilder( + custom_id=custom_id, + placeholder=placeholder, + min_values=min_values, + max_values=max_values, + is_disabled=is_disabled, + default_values=_default_values, + ) + ) + + def add_mentionable_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[ + special_endpoints.SelectDefaultBuilder[ + typing.Literal[component_models.SelectDefaultType.USER, component_models.SelectDefaultType.ROLE] + ] + ] + ] = undefined.UNDEFINED, + ) -> Self: + _default_values: undefined.UndefinedOr[ + typing.List[ + special_endpoints.SelectDefaultBuilder[ + typing.Literal[component_models.SelectDefaultType.USER, component_models.SelectDefaultType.ROLE] + ] + ] + ] = (list(default_values) if default_values is not undefined.UNDEFINED else undefined.UNDEFINED) + + return self.add_component( + MentionableSelectMenuBuilder( + custom_id=custom_id, + placeholder=placeholder, + min_values=min_values, + max_values=max_values, + is_disabled=is_disabled, + default_values=_default_values, + ) + ) + + def add_role_menu( + self, + custom_id: str, + /, + *, + placeholder: undefined.UndefinedOr[str] = undefined.UNDEFINED, + min_values: int = 0, + max_values: int = 1, + is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[ + special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.ROLE]] + ] + ] = undefined.UNDEFINED, + ) -> Self: + _default_values: undefined.UndefinedOr[ + typing.List[special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.ROLE]]] + ] = (list(default_values) if default_values is not undefined.UNDEFINED else undefined.UNDEFINED) + + return self.add_component( + RoleSelectMenuBuilder( + custom_id=custom_id, + placeholder=placeholder, + min_values=min_values, + max_values=max_values, + is_disabled=is_disabled, + default_values=_default_values, + ) + ) + def add_channel_menu( self, custom_id: str, @@ -2038,7 +2290,17 @@ def add_channel_menu( min_values: int = 0, max_values: int = 1, is_disabled: bool = False, + default_values: undefined.UndefinedOr[ + typing.Sequence[ + special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.CHANNEL]] + ] + ] = undefined.UNDEFINED, ) -> Self: + _default_values: undefined.UndefinedOr[ + typing.List[ + special_endpoints.SelectDefaultBuilder[typing.Literal[component_models.SelectDefaultType.CHANNEL]] + ] + ] = (list(default_values) if default_values is not undefined.UNDEFINED else undefined.UNDEFINED) return self.add_component( ChannelSelectMenuBuilder( custom_id=custom_id, @@ -2047,6 +2309,7 @@ def add_channel_menu( min_values=min_values, max_values=max_values, is_disabled=is_disabled, + default_values=_default_values, ) ) diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index 35d07ed180..5be200d426 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -5467,9 +5467,9 @@ def test__deserialize_text_select_menu_partial(self, entity_factory_impl): [ (2, "_deserialize_button", "_message_component_type_mapping"), (3, "_deserialize_text_select_menu", "_message_component_type_mapping"), - (5, "_deserialize_select_menu", "_message_component_type_mapping"), - (6, "_deserialize_select_menu", "_message_component_type_mapping"), - (7, "_deserialize_select_menu", "_message_component_type_mapping"), + (5, "_deserialize_user_select_menu", "_message_component_type_mapping"), + (6, "_deserialize_role_select_menu", "_message_component_type_mapping"), + (7, "_deserialize_mentionable_select_menu", "_message_component_type_mapping"), (8, "_deserialize_channel_select_menu", "_message_component_type_mapping"), (4, "_deserialize_text_input", "_modal_component_type_mapping"), ],