Skip to content
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

feat: add support select menu defaults #1556

Merged
merged 3 commits into from
Nov 16, 2023
Merged
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
144 changes: 139 additions & 5 deletions interactions/models/discord/components.py
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -26,6 +33,8 @@
"spread_to_rows",
"get_components_ids",
"TYPE_COMPONENT_MAPPING",
"SelectDefaultValues",
"DefaultableSelectMenu",
)


Expand Down Expand Up @@ -325,6 +334,87 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData:
}


@attrs.define(eq=False, order=False, hash=False, slots=False)
LordOfPolls marked this conversation as resolved.
Show resolved Hide resolved
class SelectDefaultValues(DiscordObject):
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.
Expand Down Expand Up @@ -461,7 +551,7 @@ def to_dict(self) -> discord_typings.SelectMenuComponentData:
}


class UserSelectMenu(BaseSelectMenu):
class UserSelectMenu(DefaultableSelectMenu):
"""
Represents a user select component.

Expand All @@ -481,6 +571,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__(
Expand All @@ -489,12 +589,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.

Expand All @@ -515,19 +616,30 @@ 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,
min_values=min_values,
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,
*,
Expand All @@ -536,19 +648,30 @@ 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,
min_values=min_values,
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,
*,
Expand All @@ -558,13 +681,24 @@ 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,
min_values=min_values,
max_values=max_values,
custom_id=custom_id,
disabled=disabled,
defaults=default_values,
)

self.channel_types: list[ChannelType] | None = channel_types or []
Expand Down