Skip to content

Commit

Permalink
Merge pull request #146 from YousefEZ/add-configurable-menu
Browse files Browse the repository at this point in the history
✨ Add configurable menu
  • Loading branch information
YousefEZ authored May 20, 2023
2 parents c47e1e5 + cd72c24 commit 16489e3
Show file tree
Hide file tree
Showing 16 changed files with 661 additions and 208 deletions.
30 changes: 22 additions & 8 deletions qalib/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

from qalib.renderer import Renderer
from qalib.translators import Callback, Message
from qalib.translators.deserializer import K_contra
from qalib.translators.deserializer import K_contra, EventCallback
from qalib.translators.menu import Menu


class QalibContext(discord.ext.commands.context.Context, Generic[K_contra]):
Expand Down Expand Up @@ -61,6 +62,7 @@ async def rendered_send(
identifier: K_contra,
callables: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallback] = None,
**kwargs,
) -> discord.message.Message:
"""Methods that is fires a message to the client and returns the message object. Doesn't save/keep track of the
Expand All @@ -70,11 +72,17 @@ async def rendered_send(
identifier (str): identifies the embed in the route file
callables (Optional[Dict[str, Callback]]) : functions that are hooked to components
keywords (Dict[str, Any]): keywords that are passed to the embed renderer to format the text
events (Optional[EventCallback]): callbacks that are called on the event
**kwargs: kwargs that are passed to the context's send method
Returns (discord.message.Message): Message object that got sent to the client.
"""
message = self._renderer.render(identifier, callables, keywords)
message = self._renderer.render(identifier, callables, keywords, events)

if isinstance(message, Menu):
message.front = 0 if "page" not in kwargs else kwargs["page"]
message = message.front

assert isinstance(message, Message)
return await self.send(**{**message.convert_to_context_message().dict(), **kwargs})

Expand All @@ -83,20 +91,26 @@ async def display(
key: K_contra,
callables: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallback] = None,
**kwargs,
) -> None:
"""this is the main function that we use to send one message, and one message only. However, edits to that
message can take place.
Args:
key (str): identifies the embed in the route file
callables: callable coroutines that are called when the user interacts with the message
keywords: keywords that are passed to the embed renderer to format the text
callables (Optional[Dict[str, Callback]]): callable coroutines that are called when the user interacts
keywords (Optional[Dict[str, Any]]: keywords that are passed to the embed renderer to format the text
events (Optional[EventCallback]): callbacks that are called on the event
**kwargs: kwargs that are passed to the context send method or the message edit method
Returns (discord.message.Message): Message object that got sent to the client.
"""
message = self._renderer.render(key, callables, keywords)
message = self._renderer.render(key, callables, keywords, events)
if isinstance(message, Menu):
message.front = 0 if "page" not in kwargs else kwargs["page"]
message = message.front

assert isinstance(message, Message)
if self._displayed:
await self._display(**{**message.convert_to_context_message().as_edit().dict(), **kwargs})
Expand All @@ -120,6 +134,7 @@ async def menu(
key: K_contra,
callbacks: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallback] = None,
**kwargs,
) -> None:
"""This method is used to create a menu for the user to select from.
Expand All @@ -128,9 +143,8 @@ async def menu(
key (K): identifies the menu in the template file
callbacks (Dict[str, Callback]): callbacks that are called when the user interacts with the menu
keywords (Dict[str, Any]): keywords that are passed to the embed renderer to format the text
events (Optional[EventCallback]): callbacks that are called on the event
**kwargs: kwargs that are passed to the context's send method
"""
warnings.warn("use rendered_send method instead", DeprecationWarning)
display = self._renderer.render(key, callbacks=callbacks, keywords=keywords)
assert isinstance(display, Message)
await self._display(**{**display.convert_to_context_message().dict(), **kwargs})
await self.rendered_send(key, callbacks, keywords, events, **kwargs)
21 changes: 18 additions & 3 deletions qalib/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from qalib.renderer import Renderer
from qalib.translators import Callback, Message
from qalib.translators.deserializer import K_contra
from qalib.translators.deserializer import K_contra, EventCallback
from qalib.translators.menu import Menu

if TYPE_CHECKING:
from discord.types.interactions import Interaction as InteractionPayload
Expand Down Expand Up @@ -60,6 +61,7 @@ async def rendered_send(
identifier: K_contra,
callables: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallback] = None,
**kwargs,
) -> None:
"""Methods that is fires a message to the client and returns the message object. Doesn't save/keep track of the
Expand All @@ -69,11 +71,17 @@ async def rendered_send(
identifier (str): identifies the embed in the route file
callables (Optional[Dict[str, Callback]]) : functions that are hooked to components
keywords (Dict[str, Any]): keywords that are passed to the embed renderer to format the text
events (Optional[EventCallback]): callbacks that are hooked to the event.
**kwargs: kwargs that are passed to the context's send method
Returns (discord.message.Message): Message object that got sent to the client.
"""
message = self._renderer.render(identifier, callables, keywords)
message = self._renderer.render(identifier, callables, keywords, events)

if isinstance(message, Menu):
message.front = 0 if "page" not in kwargs else kwargs["page"]
message = message.front

if isinstance(message, Message):
assert isinstance(self.response, InteractionResponse) # pyright: ignore [reportGeneralTypeIssues]
# pylint: disable= no-member
Expand All @@ -89,6 +97,7 @@ async def display(
key: K_contra,
callables: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallback] = None,
**kwargs,
) -> None:
"""this is the main function that we use to send one message, and one message only. However, edits to that
Expand All @@ -98,11 +107,17 @@ async def display(
key (K): identifies the message in the template file
callables: callable coroutines that are called when the user interacts with the message
keywords: keywords that are passed to the embed renderer to format the text
events (Optional[EventCallback]): callbacks that are called on the event.
**kwargs: kwargs that are passed to the context send method or the message edit method
Returns (discord.message.Message): Message object that got sent to the client.
"""
message = self._renderer.render(key, callables, keywords)
message = self._renderer.render(key, callables, keywords, events)

if isinstance(message, Menu):
message.front = 0 if "page" not in kwargs else kwargs["page"]
message = message.front

assert isinstance(message, Message)
if self._displayed:
await self._display(**{**message.convert_to_interaction_message().as_edit().dict(), **kwargs})
Expand Down
9 changes: 7 additions & 2 deletions qalib/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from qalib.template_engines.template_engine import TemplateEngine
from qalib.translators import Callback
from qalib.translators.deserializer import ReturnType, K_contra, Deserializer
from qalib.translators.deserializer import ReturnType, K_contra, Deserializer, EventCallbacks
from qalib.translators.factory import DeserializerFactory, TemplaterFactory
from qalib.translators.templater import Templater

Expand Down Expand Up @@ -53,13 +53,15 @@ def render(
key: K_contra,
callbacks: Optional[Dict[str, Callback]] = None,
keywords: Optional[Dict[str, Any]] = None,
events: Optional[EventCallbacks] = None
) -> ReturnType:
"""This method is used to render an embed and a view, and places it in a NamedTuple
Args:
key (K): key of the embed,
callbacks (Optional[Dict[str, Callable]]): callbacks that are attached to the components of the view,
keywords (Dict[str, Any]): keywords that are passed to the embed renderer to format the text,
events (Optional[EventCallbacks]): callbacks that are called on events
Returns (ReturnType): All possible deserialized types
"""
Expand All @@ -69,5 +71,8 @@ def render(
if keywords is None:
keywords = {}

if events is None:
events = {}

source = self._pre_template(keywords).template(self._template_engine, keywords)
return self._deserializer.deserialize(source, key, callbacks)
return self._deserializer.deserialize(source, key, callbacks, events)
19 changes: 15 additions & 4 deletions qalib/translators/deserializer.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from __future__ import annotations

from enum import Enum
from typing import Dict, Protocol, Optional, Literal, TypeVar, Union
from typing import Dict, Protocol, Optional, Literal, TypeVar, Union, Callable

from discord.ui import Modal

from qalib.translators import Callback, Message
from qalib.translators.menu import Menu, MenuEvents

Types = Literal["message", "menu", "modal", "expansive"]

ReturnType = Union[Message, Modal]
ReturnType = Union[Message, Modal, Menu]

K_contra = TypeVar("K_contra", bound=str, contravariant=True)
Events = MenuEvents
EventCallback = Callable[[Menu], None]
EventCallbacks = Dict[Events, EventCallback]


class ElementTypes(Enum):
Expand All @@ -34,13 +38,20 @@ class Deserializer(Protocol[K_contra]):
"""Protocol that represents the deserializer. It is meant to be placed into a Renderer, and is responsible for
deserializing the document into embeds and views."""

def deserialize(self, source: str, key: K_contra, callables: Dict[str, Callback]) -> ReturnType:
def deserialize(
self,
source: str,
key: K_contra,
callables: Dict[str, Callback],
events: EventCallbacks
) -> ReturnType:
"""This method is used to deserialize a document into an embed and a view.
Parameters:
source (str): document that is deserialized
key:
key (K_contra): key that is used to deserialize the document
callables (Dict[str, Callback]): callables that are used to deserialize the document
events (EventCallbacks): hooks that are called on events
Returns (ReturnType): All possible deserialized types
"""
Expand Down
Loading

0 comments on commit 16489e3

Please sign in to comment.