Add support for Modal Interactions

This commit is contained in:
Josh
2022-02-20 19:57:44 +10:00
committed by GitHub
parent 964ca0a6f1
commit 19c6687b55
13 changed files with 679 additions and 17 deletions

View File

@ -29,6 +29,7 @@ from itertools import groupby
import traceback
import asyncio
import logging
import sys
import time
import os
@ -40,6 +41,7 @@ from ..components import (
Button as ButtonComponent,
SelectMenu as SelectComponent,
)
from ..utils import MISSING
__all__ = (
'View',
@ -50,7 +52,12 @@ if TYPE_CHECKING:
from ..interactions import Interaction
from ..message import Message
from ..types.components import Component as ComponentPayload
from ..types.interactions import ModalSubmitComponentInteractionData as ModalSubmitComponentInteractionDataPayload
from ..state import ConnectionState
from .modal import Modal
_log = logging.getLogger(__name__)
def _walk_all_components(components: List[Component]) -> Iterator[Component]:
@ -138,6 +145,7 @@ class View:
"""
__discord_ui_view__: ClassVar[bool] = True
__discord_ui_modal__: ClassVar[bool] = False
__view_children_items__: ClassVar[List[ItemCallbackType]] = []
def __init_subclass__(cls) -> None:
@ -152,16 +160,19 @@ class View:
cls.__view_children_items__ = children
def __init__(self, *, timeout: Optional[float] = 180.0):
self.timeout = timeout
self.children: List[Item] = []
def _init_children(self) -> List[Item]:
children = []
for func in self.__view_children_items__:
item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
item.callback = partial(func, self, item)
item.callback = partial(func, self, item) # type: ignore
item._view = self
setattr(self, func.__name__, item)
self.children.append(item)
children.append(item)
return children
def __init__(self, *, timeout: Optional[float] = 180.0):
self.timeout = timeout
self.children: List[Item] = self._init_children()
self.__weights = _ViewWeights(self.children)
loop = asyncio.get_running_loop()
self.id: str = os.urandom(16).hex()
@ -464,6 +475,8 @@ class ViewStore:
self._views: Dict[Tuple[int, Optional[int], str], Tuple[View, Item]] = {}
# message_id: View
self._synced_message_views: Dict[int, View] = {}
# custom_id: Modal
self._modals: Dict[str, Modal] = {}
self._state: ConnectionState = state
@property
@ -487,6 +500,10 @@ class ViewStore:
del self._views[k]
def add_view(self, view: View, message_id: Optional[int] = None):
if view.__discord_ui_modal__:
self._modals[view.custom_id] = view # type: ignore
return
self.__verify_integrity()
view._start_listening_from_store(self)
@ -498,6 +515,10 @@ class ViewStore:
self._synced_message_views[message_id] = view
def remove_view(self, view: View):
if view.__discord_ui_modal__:
self._modals.pop(view.custom_id, None) # type: ignore
return
for item in view.children:
if item.is_dispatchable():
self._views.pop((item.type.value, item.custom_id), None) # type: ignore
@ -507,7 +528,7 @@ class ViewStore:
del self._synced_message_views[key]
break
def dispatch(self, component_type: int, custom_id: str, interaction: Interaction):
def dispatch_view(self, component_type: int, custom_id: str, interaction: Interaction):
self.__verify_integrity()
message_id: Optional[int] = interaction.message and interaction.message.id
key = (component_type, message_id, custom_id)
@ -518,9 +539,18 @@ class ViewStore:
return
view, item = value
item.refresh_state(interaction)
item.refresh_state(interaction.data) # type: ignore
view._dispatch_item(item, interaction)
def dispatch_modal(self, custom_id: str, interaction: Interaction, components: List[ModalSubmitComponentInteractionDataPayload]):
modal = self._modals.get(custom_id)
if modal is None:
_log.debug("Modal interaction referencing unknown custom_id %s. Discarding", custom_id)
return
modal.refresh(components)
modal._dispatch_submit(interaction)
def is_message_tracked(self, message_id: int):
return message_id in self._synced_message_views