Skip to content

Commit d048a8c

Browse files
FayeDelEepyElvyraDamego
authored
feat: Implement barebones Select Menus for other supporting types (#836)
* feat: Implement barebones Select Menus for other supporting types with type-conversion support. * fix: Tweak SelectMenu option generation for other type of menus * docs: update FAQ and quickstart documentation links (#1038) * Docs: add back previous changes * docs: fix outdated links in the quickstart (#1037) Co-authored-by: Damego <damego.dev@gmail.com> * chore: Resolve merge conflicts. Co-authored-by: EdVraz <88881326+EdVraz@users.noreply.github.com> Co-authored-by: Damego <damego.dev@gmail.com>
1 parent c75bda4 commit d048a8c

File tree

4 files changed

+96
-25
lines changed

4 files changed

+96
-25
lines changed

interactions/api/gateway/client.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from aiohttp import ClientWebSocketResponse, WSMessage, WSMsgType
2525

2626
from ...base import __version__, get_logger
27-
from ...client.enums import InteractionType, OptionType
27+
from ...client.enums import ComponentType, InteractionType, OptionType
2828
from ...client.models import Option
2929
from ...utils.missing import MISSING
3030
from ..dispatch import Listener
@@ -379,7 +379,14 @@ def _dispatch_event(self, event: str, data: dict) -> None:
379379
_name = f"component_{_context.data.custom_id}"
380380

381381
if _context.data._json.get("values"):
382-
__args.append(_context.data.values)
382+
if _context.data.component_type.value not in {5, 6, 7, 8}:
383+
__args.append(_context.data.values)
384+
else:
385+
for value in _context.data._json.get("values"):
386+
_data = self.__select_option_type_context(
387+
_context, _context.data.component_type.value
388+
) # resolved.
389+
__args.append(_data[value])
383390

384391
self._dispatch.dispatch("on_component", _context)
385392
elif data["type"] == InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE:
@@ -842,6 +849,40 @@ def __option_type_context(self, context: "_Context", type: int) -> dict:
842849
}
843850
return _resolved
844851

852+
def __select_option_type_context(self, context: "_Context", type: int) -> dict:
853+
"""
854+
Looks up the type of select menu respective to the existing option types. This is applicable for non-string
855+
select menus.
856+
857+
:param context: The context to refer types from.
858+
:type context: object
859+
:param type: The option type.
860+
:type type: int
861+
:return: The select menu type context.
862+
:rtype: dict
863+
"""
864+
865+
_resolved = {}
866+
867+
if type == ComponentType.USER_SELECT.value:
868+
_resolved = (
869+
context.data.resolved.members if context.guild_id else context.data.resolved.users
870+
)
871+
elif type == ComponentType.CHANNEL_SELECT.value:
872+
_resolved = context.data.resolved.channels
873+
elif type == ComponentType.ROLE_SELECT.value:
874+
_resolved = context.data.resolved.roles
875+
elif type == ComponentType.MENTIONABLE_SELECT.value:
876+
_resolved = {
877+
**(
878+
context.data.resolved.members
879+
if context.guild_id
880+
else context.data.resolved.users
881+
),
882+
**context.data.resolved.roles,
883+
}
884+
return _resolved
885+
845886
async def _reconnect(self, to_resume: bool, code: Optional[int] = 1012) -> None:
846887
"""
847888
Restarts the client's connection and heartbeat with the Gateway.

interactions/client/bot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,8 @@ def event(
727727
728728
:param coro: The coroutine of the event.
729729
:type coro: Optional[Callable[..., Coroutine]]
730-
:param name(?): The name of the event. If not given, this defaults to the coroutine's name.
731-
:type name: Optional[str]
730+
:param name?: The name of the event. If not given, this defaults to the coroutine's name.
731+
:type name?: Optional[str]
732732
:return: A callable response.
733733
:rtype: Callable[..., Any]
734734
"""

interactions/client/enums.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,22 @@ class ComponentType(IntEnum):
120120
:ivar ACTION_ROW: 1
121121
:ivar BUTTON: 2
122122
:ivar SELECT: 3
123+
:ivar STRING_SELECT: 3
123124
:ivar INPUT_TEXT: 4
125+
:ivar USER_SELECT: 5
126+
:ivar ROLE_SELECT: 6
127+
:ivar MENTIONABLE_SELECT: 7
128+
:ivar CHANNEL_SELECT: 8
124129
"""
125130

126131
ACTION_ROW = 1
127132
BUTTON = 2
128-
SELECT = 3
133+
SELECT = STRING_SELECT = 3
129134
INPUT_TEXT = 4
135+
USER_SELECT = 5
136+
ROLE_SELECT = 6
137+
MENTIONABLE_SELECT = 7
138+
CHANNEL_SELECT = 8
130139

131140

132141
class ButtonStyle(IntEnum):

interactions/client/models/component.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,26 +77,31 @@ class SelectMenu(ComponentMixin):
7777
placeholder="Check out my options. :)",
7878
custom_id="menu_component",
7979
)
80-
:ivar ComponentType type: The type of select menu. Always defaults to ``3``.
80+
:ivar ComponentType type: The type of select menu. If not given, it defaults to ``ComponentType.SELECT`` (``STRING_SELECT``)
8181
:ivar str custom_id: The customized "ID" of the select menu.
82-
:ivar List[SelectOption] options: The list of select options in the select menu.
82+
:ivar Optional[List[SelectOption]] options: The list of select options in the select menu. This only applies to String-based selects.
8383
:ivar Optional[str] placeholder?: The placeholder of the select menu.
8484
:ivar Optional[int] min_values?: The minimum "options"/values to choose from the component.
8585
:ivar Optional[int] max_values?: The maximum "options"/values to choose from the component.
8686
:ivar Optional[bool] disabled?: Whether the select menu is unable to be used.
87+
:ivar Optional[List[int]] channel_types: Optional channel types to filter/whitelist. Only works with the CHANNEL_SELECT type.
8788
"""
8889

8990
type: ComponentType = field(converter=ComponentType, default=ComponentType.SELECT)
9091
custom_id: str = field()
91-
options: List[SelectOption] = field(converter=convert_list(SelectOption))
92+
options: Optional[List[SelectOption]] = field(
93+
converter=convert_list(SelectOption), default=None
94+
)
9295
placeholder: Optional[str] = field(default=None)
9396
min_values: Optional[int] = field(default=None)
9497
max_values: Optional[int] = field(default=None)
9598
disabled: Optional[bool] = field(default=None)
99+
channel_types: Optional[List[int]] = field(default=None)
96100

97101
def __attrs_post_init__(self) -> None:
98102
self._json.update({"type": self.type.value})
99-
self._json.update({"options": [option._json for option in self.options]})
103+
if self.options:
104+
self._json.update({"options": [option._json for option in self.options]})
100105

101106

102107
@define()
@@ -284,10 +289,14 @@ class ActionRow(ComponentMixin):
284289
def __attrs_post_init__(self) -> None:
285290
for component in self.components:
286291
if isinstance(component, SelectMenu):
287-
component._json["options"] = [
288-
option._json if isinstance(option, SelectOption) else option
289-
for option in component._json["options"]
290-
]
292+
component._json["options"] = (
293+
[
294+
option._json if isinstance(option, SelectOption) else option
295+
for option in component._json["options"]
296+
]
297+
if component._json.get("options")
298+
else []
299+
)
291300
self.components = (
292301
[Component(**component._json) for component in self.components]
293302
if self._json.get("components")
@@ -323,10 +332,14 @@ def __check_action_row():
323332
action_row if isinstance(action_row, list) else action_row.components
324333
):
325334
if isinstance(component, SelectMenu):
326-
component._json["options"] = [
327-
option if isinstance(option, dict) else option._json
328-
for option in component.options
329-
]
335+
component._json["options"] = (
336+
[
337+
option if isinstance(option, dict) else option._json
338+
for option in component.options
339+
]
340+
if component._json.get("options")
341+
else []
342+
)
330343

331344
_components.append(
332345
{
@@ -367,10 +380,14 @@ def __check_components():
367380
):
368381
for component in components:
369382
if isinstance(component, SelectMenu):
370-
component._json["options"] = [
371-
options if isinstance(options, dict) else options._json
372-
for options in component._json["options"]
373-
]
383+
component._json["options"] = (
384+
[
385+
options if isinstance(options, dict) else options._json
386+
for options in component._json["options"]
387+
]
388+
if component._json.get("options")
389+
else []
390+
)
374391

375392
_components = [
376393
{
@@ -397,10 +414,14 @@ def __check_components():
397414
return _components
398415
elif isinstance(components, SelectMenu):
399416
_components: List[dict] = [{"type": 1, "components": []}]
400-
components._json["options"] = [
401-
options if isinstance(options, dict) else options._json
402-
for options in components._json["options"]
403-
]
417+
components._json["options"] = (
418+
[
419+
options if isinstance(options, dict) else options._json
420+
for options in components._json["options"]
421+
]
422+
if components._json.get("options")
423+
else []
424+
)
404425

405426
_components[0]["components"] = (
406427
[components._json]

0 commit comments

Comments
 (0)