From b78f88a8330fd66e7223dfd966595df5382a35bd Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Sat, 23 Sep 2023 16:58:35 +0100 Subject: [PATCH 1/3] feat: add support select menu defaults --- interactions/models/discord/components.py | 142 +++++++++++++++++++++- 1 file changed, 137 insertions(+), 5 deletions(-) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index 632089b5e..696c26b71 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -1,15 +1,22 @@ import contextlib import uuid from abc import abstractmethod -from typing import Any, Dict, Iterator, List, Optional, Union +from typing import Any, Dict, Iterator, List, Optional, Union, TYPE_CHECKING +import attrs import discord_typings +import interactions.models.discord as d_models +from interactions.models.discord.snowflake import Snowflake from interactions.client.const import ACTION_ROW_MAX_ITEMS, MISSING from interactions.client.mixins.serialization import DictSerializationMixin +from interactions.models.discord.base import DiscordObject from interactions.models.discord.emoji import PartialEmoji, process_emoji from interactions.models.discord.enums import ButtonStyle, ChannelType, ComponentType +if TYPE_CHECKING: + import interactions.models.discord + __all__ = ( "BaseComponent", "InteractiveComponent", @@ -325,6 +332,87 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData: } +@attrs.define(eq=False, order=False, hash=False, slots=False) +class SelectDefaultValues(DictSerializationMixin): + id: Snowflake + """ID of a user, role, or channel""" + type: str + """Type of value that id represents. Either "user", "role", or "channel""" + + @classmethod + def from_object(cls, obj: DiscordObject) -> "SelectDefaultValues": + """Create a default value from a discord object.""" + match obj: + case d_models.User(): + return cls(id=obj.id, type="user") + case d_models.Member(): + return cls(id=obj.id, type="user") + case d_models.BaseChannel(): + return cls(id=obj.id, type="channel") + case d_models.Role(): + return cls(id=obj.id, type="role") + case _: + raise TypeError( + f"Cannot convert {obj} of type {type(obj)} to a SelectDefaultValues - Expected User, Channel, Member, or Role" + ) + + +class DefaultableSelectMenu(BaseSelectMenu): + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ] + ] | None = None + + def __init__( + self, + defaults: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ] + ] + | None = None, + **kwargs, + ) -> None: + super().__init__(**kwargs) + self.default_values = defaults + + def add_default_value( + self, + value: Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ) -> None: + if self.default_values is None: + self.default_values = [] + self.default_values.append(value) + + def to_dict(self) -> discord_typings.SelectMenuComponentData: + data = super().to_dict() + if self.default_values is not None: + data["default_values"] = [ # type: ignore # waiting on discord typings to update + value.to_dict() + if isinstance(value, SelectDefaultValues) + else SelectDefaultValues.from_object(value).to_dict() + for value in self.default_values + ] + + # Discord handles the type checking, no need to do it here + return data + + class StringSelectOption(BaseComponent): """ Represents a select option. @@ -461,7 +549,7 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData: } -class UserSelectMenu(BaseSelectMenu): +class UserSelectMenu(DefaultableSelectMenu): """ Represents a user select component. @@ -481,6 +569,16 @@ def __init__( min_values: int = 1, max_values: int = 1, custom_id: str | None = None, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, disabled: bool = False, ) -> None: super().__init__( @@ -489,12 +587,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.USER_SELECT -class RoleSelectMenu(BaseSelectMenu): +class RoleSelectMenu(DefaultableSelectMenu): """ Represents a user select component. @@ -515,6 +614,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -522,12 +631,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.ROLE_SELECT -class MentionableSelectMenu(BaseSelectMenu): +class MentionableSelectMenu(DefaultableSelectMenu): def __init__( self, *, @@ -536,6 +646,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -543,12 +663,13 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.type: ComponentType = ComponentType.MENTIONABLE_SELECT -class ChannelSelectMenu(BaseSelectMenu): +class ChannelSelectMenu(DefaultableSelectMenu): def __init__( self, *, @@ -558,6 +679,16 @@ def __init__( max_values: int = 1, custom_id: str | None = None, disabled: bool = False, + default_values: list[ + Union[ + "interactions.models.discord.BaseUser", + "interactions.models.discord.Role", + "interactions.models.discord.BaseChannel", + "interactions.models.discord.Member", + SelectDefaultValues, + ], + ] + | None = None, ) -> None: super().__init__( placeholder=placeholder, @@ -565,6 +696,7 @@ def __init__( max_values=max_values, custom_id=custom_id, disabled=disabled, + defaults=default_values, ) self.channel_types: list[ChannelType] | None = channel_types or [] From d8bee6f946b297c802b2f486b93ea95921ab8f8f Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Sat, 23 Sep 2023 17:02:26 +0100 Subject: [PATCH 2/3] feat: add new objects to local scope __all__ --- interactions/models/discord/components.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index 696c26b71..d7331a661 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -33,6 +33,8 @@ "spread_to_rows", "get_components_ids", "TYPE_COMPONENT_MAPPING", + "SelectDefaultValues", + "DefaultableSelectMenu", ) From feb3a6cbb0e2a0f4f217726004eee89558d3c72b Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Sun, 8 Oct 2023 09:48:19 +0100 Subject: [PATCH 3/3] fix: use discordobject as base for dfaultselectvalue --- interactions/models/discord/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index d7331a661..a5c023534 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -335,7 +335,7 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData: @attrs.define(eq=False, order=False, hash=False, slots=False) -class SelectDefaultValues(DictSerializationMixin): +class SelectDefaultValues(DiscordObject): id: Snowflake """ID of a user, role, or channel""" type: str