From 90b075da286fa42c6890f3d0b2c3049366e4ae47 Mon Sep 17 00:00:00 2001 From: DeltaXWizard <33706469+deltaxwizard@users.noreply.github.com> Date: Thu, 9 Jun 2022 18:24:45 -0400 Subject: [PATCH 1/4] feat: Implement barebones Select Menus for other supporting types with type-conversion support. --- interactions/api/gateway/client.py | 44 ++++++++++++++++++++++++- interactions/api/gateway/client.pyi | 1 + interactions/client/enums.py | 11 ++++++- interactions/client/models/component.py | 10 ++++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/interactions/api/gateway/client.py b/interactions/api/gateway/client.py index 075ece295..0a970623f 100644 --- a/interactions/api/gateway/client.py +++ b/interactions/api/gateway/client.py @@ -305,7 +305,14 @@ def _dispatch_event(self, event: str, data: dict) -> None: # sourcery no-metric _name = f"component_{_context.data.custom_id}" if _context.data._json.get("values"): - __args.append(_context.data.values) + if _context.data.component_type.value not in {5, 6, 7, 8}: + __args.append(_context.data.values) + else: + for value in _context.data._json.get("values"): + _data = self.__select_option_type_context( + _context, _context.data.component_type.value + ) # resolved. + __args.append(_data[value]) self._dispatch.dispatch("on_component", _context) elif data["type"] == InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE: @@ -544,6 +551,41 @@ def __option_type_context(self, context: object, type: int) -> dict: } return _resolved + def __select_option_type_context(self, context: object, type: int) -> dict: + """ + Looks up the type of select menu respective to the existing option types. + + :param context: The context to refer types from. + :type context: object + :param type: The option type. + :type type: int + :return: The select menu type context. + :rtype: dict + """ + + from interactions import ComponentType + + _resolved = {} + + if type == ComponentType.USER_SELECT.value: + _resolved = ( + context.data.resolved.members if context.guild_id else context.data.resolved.users + ) + elif type == ComponentType.CHANNEL_SELECT.value: + _resolved = context.data.resolved.channels + elif type == ComponentType.ROLE_SELECT.value: + _resolved = context.data.resolved.roles + elif type == ComponentType.MENTIONABLE_SELECT.value: + _resolved = { + **( + context.data.resolved.members + if context.guild_id + else context.data.resolved.users + ), + **context.data.resolved.roles, + } + return _resolved + async def restart(self): await self.__restart() diff --git a/interactions/api/gateway/client.pyi b/interactions/api/gateway/client.pyi index f03d73ec9..269cbf13b 100644 --- a/interactions/api/gateway/client.pyi +++ b/interactions/api/gateway/client.pyi @@ -61,6 +61,7 @@ class WebSocketClient: self, data: Union[dict, Option], _context: Optional[object] = MISSING ) -> Union[Tuple[str], dict]: ... def __option_type_context(self, context: object, type: int) -> dict: ... + def __select_option_type_context(self, context: object, type: int) -> dict: ... @property async def __receive_packet_stream(self) -> Optional[Dict[str, Any]]: ... async def _send_packet(self, data: Dict[str, Any]) -> None: ... diff --git a/interactions/client/enums.py b/interactions/client/enums.py index cf0af6d07..a1e57ce96 100644 --- a/interactions/client/enums.py +++ b/interactions/client/enums.py @@ -120,13 +120,22 @@ class ComponentType(IntEnum): :ivar ACTION_ROW: 1 :ivar BUTTON: 2 :ivar SELECT: 3 + :ivar STRING_SELECT: 3 :ivar INPUT_TEXT: 4 + :ivar USER_SELECT: 5 + :ivar ROLE_SELECT: 6 + :ivar MENTIONABLE_SELECT: 7 + :ivar CHANNEL_SELECT: 8 """ ACTION_ROW = 1 BUTTON = 2 - SELECT = 3 + SELECT = STRING_SELECT = 3 INPUT_TEXT = 4 + USER_SELECT = 5 + ROLE_SELECT = 6 + MENTIONABLE_SELECT = 7 + CHANNEL_SELECT = 8 class ButtonStyle(IntEnum): diff --git a/interactions/client/models/component.py b/interactions/client/models/component.py index d716b47bc..187783fc8 100644 --- a/interactions/client/models/component.py +++ b/interactions/client/models/component.py @@ -75,22 +75,26 @@ class SelectMenu(ComponentMixin): placeholder="Check out my options. :)", custom_id="menu_component", ) - :ivar ComponentType type: The type of select menu. Always defaults to ``3``. + :ivar ComponentType type: The type of select menu. :ivar str custom_id: The customized "ID" of the select menu. - :ivar List[SelectOption] options: The list of select options in the select menu. + :ivar Optional[List[SelectOption]] options: The list of select options in the select menu. This only applies to String-based selects. :ivar Optional[str] placeholder?: The placeholder of the select menu. :ivar Optional[int] min_values?: The minimum "options"/values to choose from the component. :ivar Optional[int] max_values?: The maximum "options"/values to choose from the component. :ivar Optional[bool] disabled?: Whether the select menu is unable to be used. + :ivar Optional[List[int]] channel_types: Optional channel types to filter/whitelist. Only works with the CHANNEL_SELECT type. """ type: ComponentType = field(converter=ComponentType) custom_id: str = field() - options: List[SelectOption] = field(converter=convert_list(SelectOption)) + options: Optional[List[SelectOption]] = field( + converter=convert_list(SelectOption), default=None + ) placeholder: Optional[str] = field(default=None) min_values: Optional[int] = field(default=None) max_values: Optional[int] = field(default=None) disabled: Optional[bool] = field(default=None) + channel_types: Optional[List[int]] = field(default=None) def __attrs_post_init__(self) -> None: self._json.update({"type": self.type.value}) From a1bebc1052ef79710aef99b1a1e1ea66188b16e9 Mon Sep 17 00:00:00 2001 From: DeltaXWizard <33706469+deltaxwizard@users.noreply.github.com> Date: Thu, 23 Jun 2022 08:44:22 -0400 Subject: [PATCH 2/4] fix: Tweak SelectMenu option generation for other type of menus --- interactions/client/models/component.py | 52 ++++++++++++++++--------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/interactions/client/models/component.py b/interactions/client/models/component.py index 187783fc8..afb1d90ac 100644 --- a/interactions/client/models/component.py +++ b/interactions/client/models/component.py @@ -75,7 +75,7 @@ class SelectMenu(ComponentMixin): placeholder="Check out my options. :)", custom_id="menu_component", ) - :ivar ComponentType type: The type of select menu. + :ivar ComponentType type: The type of select menu. If not given, it defaults to ``ComponentType.SELECT`` (``STRING_SELECT``) :ivar str custom_id: The customized "ID" of the select menu. :ivar Optional[List[SelectOption]] options: The list of select options in the select menu. This only applies to String-based selects. :ivar Optional[str] placeholder?: The placeholder of the select menu. @@ -85,7 +85,7 @@ class SelectMenu(ComponentMixin): :ivar Optional[List[int]] channel_types: Optional channel types to filter/whitelist. Only works with the CHANNEL_SELECT type. """ - type: ComponentType = field(converter=ComponentType) + type: ComponentType = field(converter=ComponentType, default=ComponentType.SELECT) custom_id: str = field() options: Optional[List[SelectOption]] = field( converter=convert_list(SelectOption), default=None @@ -286,10 +286,14 @@ class ActionRow(ComponentMixin): def __attrs_post_init__(self) -> None: for component in self.components: if isinstance(component, SelectMenu): - component._json["options"] = [ - option._json if isinstance(option, SelectOption) else option - for option in component._json["options"] - ] + component._json["options"] = ( + [ + option._json if isinstance(option, SelectOption) else option + for option in component._json["options"] + ] + if component._json.get("options") + else [] + ) self.components = ( [Component(**component._json) for component in self.components] if self._json.get("components") @@ -313,10 +317,14 @@ def __check_action_row(): action_row if isinstance(action_row, list) else action_row.components ): if isinstance(component, SelectMenu): - component._json["options"] = [ - option if isinstance(option, dict) else option._json - for option in component.options - ] + component._json["options"] = ( + [ + option if isinstance(option, dict) else option._json + for option in component.options + ] + if component._json.get("options") + else [] + ) _components.append( { @@ -357,10 +365,14 @@ def __check_components(): ): for component in components: if isinstance(component, SelectMenu): - component._json["options"] = [ - options if isinstance(options, dict) else options._json - for options in component._json["options"] - ] + component._json["options"] = ( + [ + options if isinstance(options, dict) else options._json + for options in component._json["options"] + ] + if component._json.get("options") + else [] + ) _components = [ { @@ -387,10 +399,14 @@ def __check_components(): return _components elif isinstance(components, SelectMenu): _components: List[dict] = [{"type": 1, "components": []}] - components._json["options"] = [ - options if isinstance(options, dict) else options._json - for options in components._json["options"] - ] + components._json["options"] = ( + [ + options if isinstance(options, dict) else options._json + for options in components._json["options"] + ] + if components._json.get("options") + else [] + ) _components[0]["components"] = ( [components._json] From 0c6f59b9a7dadeaace6c30ae98201ce64a31b0d1 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:49:14 +0200 Subject: [PATCH 3/4] 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 --- docs/faq.rst | 14 ++++++++------ docs/quickstart.rst | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index bb572d212..789b56a66 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -154,21 +154,22 @@ A list of all official extensions can be found `on our github page`_ Those are (at the moment): -- A `voice client`_ - which is still WIP, but it is able to listen for the ``VOICE_STATE_UPDATE`` event. -- An `autosharder`_ for automatic sharding +- `voice client`_ - which is still WIP, but it is able to listen for the ``VOICE_STATE_UPDATE`` event. +- `autosharder`_ for automatic sharding - `wait_for`_ allows listening and waiting for a specific event or a component inside a function - `files`_ for file sending with ctx - `Molter`_ for message commands - `enhanced`_ which enhances the DX in general -- A `paginator`_ for paginating embeds on messages using components -- `persistence`_ - for storing data inside your custom IDs (as an alternative to ``wait_for``) +- `paginator`_ for paginating embeds on messages using components +- `persistence`_ for storing data inside your custom IDs (as an alternative to ``wait_for``) +- `lavalink`_ for voice sending and listening ``VOICE_STATE_UPDATE`` event +- `fastapi`_ for building own API - And a `boilerplate`_ Below are a few unofficial exts (for the time being) which implement some functionality similar to what d.py had: - `checks and cooldowns`_ - `tasks`_ -- `get`_ for getting objects from the discord API (will be implemented into the core library at a later time) Usage examples can usually be found at the linked page @@ -318,4 +319,5 @@ Please join our `Discord Server`_ for any further support regarding our library .. _persistence: https://github.com/interactions-py/persistence .. _Molter: https://github.com/interactions-py/molter .. _boilerplate: https://github.com/interactions-py/boilerplate -.. _get: https://github.com/EdVraz/interactions-get +.. _lavalink: https://github.com/interactions-py/interactions-lavalink +.. _fastapi: https://github.com/interactions-py/interactions-fastapi diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 240700507..5756fa74b 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -686,6 +686,6 @@ Usage of ``@autodefer()``: await asyncio.sleep(5) await ctx.send("I'm awake now!") -.. _Client: https://interactionspy.rtfd.io/en/stable/client.html -.. _find these component types: https://interactionspy.readthedocs.io/en/stable/models.component.html +.. _Client: https://interactionspy.rtfd.io/en/latest/client.html +.. _find these component types: https://interactionspy.readthedocs.io/en/latest/models.component.html .. _discord applications page: https://discord.com/developers/applications From 87b52f47b8df3c78a4c3d4869c62fd314b16dab1 Mon Sep 17 00:00:00 2001 From: DeltaX <33706469+DeltaXWizard@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:31:22 -0400 Subject: [PATCH 4/4] chore: Resolve merge conflicts. --- interactions/api/gateway/client.py | 9 ++++----- interactions/client/bot.py | 4 ++-- interactions/client/models/component.py | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interactions/api/gateway/client.py b/interactions/api/gateway/client.py index d1e332dac..80f255da4 100644 --- a/interactions/api/gateway/client.py +++ b/interactions/api/gateway/client.py @@ -24,7 +24,7 @@ from aiohttp import ClientWebSocketResponse, WSMessage, WSMsgType from ...base import __version__, get_logger -from ...client.enums import InteractionType, OptionType +from ...client.enums import ComponentType, InteractionType, OptionType from ...client.models import Option from ...utils.missing import MISSING from ..dispatch import Listener @@ -843,9 +843,10 @@ def __option_type_context(self, context: "_Context", type: int) -> dict: } return _resolved - def __select_option_type_context(self, context: object, type: int) -> dict: + def __select_option_type_context(self, context: "_Context", type: int) -> dict: """ - Looks up the type of select menu respective to the existing option types. + Looks up the type of select menu respective to the existing option types. This is applicable for non-string + select menus. :param context: The context to refer types from. :type context: object @@ -855,8 +856,6 @@ def __select_option_type_context(self, context: object, type: int) -> dict: :rtype: dict """ - from interactions import ComponentType - _resolved = {} if type == ComponentType.USER_SELECT.value: diff --git a/interactions/client/bot.py b/interactions/client/bot.py index fa74e4bd8..9c1c85935 100644 --- a/interactions/client/bot.py +++ b/interactions/client/bot.py @@ -725,8 +725,8 @@ def event( :param coro: The coroutine of the event. :type coro: Optional[Callable[..., Coroutine]] - :param name(?): The name of the event. If not given, this defaults to the coroutine's name. - :type name: Optional[str] + :param name?: The name of the event. If not given, this defaults to the coroutine's name. + :type name?: Optional[str] :return: A callable response. :rtype: Callable[..., Any] """ diff --git a/interactions/client/models/component.py b/interactions/client/models/component.py index 1c74c1871..b8a3f0d60 100644 --- a/interactions/client/models/component.py +++ b/interactions/client/models/component.py @@ -100,7 +100,8 @@ class SelectMenu(ComponentMixin): def __attrs_post_init__(self) -> None: self._json.update({"type": self.type.value}) - self._json.update({"options": [option._json for option in self.options]}) + if self.options: + self._json.update({"options": [option._json for option in self.options]}) @define()