Source code for MateMenus.panels

from typing import Callable, Union, List

from telegram import InlineKeyboardMarkup, InlineKeyboardButton, Update
from telegram.ext import Handler, CallbackContext

from MateMenus.generics import GenericPanel, Button, MenuContext, get_back_button_handler
from MateWrapper.generics import TelegramEvent, Chain
from MateWrapper.globals import Globals
from MateWrapper.prompts import Prompt


[docs]class Panel(GenericPanel): """ A complex implementation of a Panel that automatically handles callbacks & states """
[docs] def __init__( self, prompt_text: Union[str, Callable[[TelegramEvent], str]], buttons: List[Union[Button, List[Button]]], back_to: str, extra_handlers: List[Handler] or None = None, ): """ :param Union[str, Callable[[TelegramEvent], str]] prompt_text: The text that will be sent when this panel is shown, supports all the same formatting options as the Panel object. Can be callable. :param List[Union[Button, List[Button]]] buttons: The list of Button objects (or of List[Button]) that will define the functionality of this panel. :param str back_to: The name of the Panel to go back to. If "__end__" the back button will close the menu. :param List[Handler] or None extra_handlers: A list of extra handlers that can do extra stuff, like read text inputs and stuff like that. """ self.buttons = buttons self.prompt_text = prompt_text self.back_to = back_to self.extra_handlers = extra_handlers
def _get_keyboard(self) -> InlineKeyboardMarkup: """ generates a keyboard object from the list of buttons """ keyboard_list: List[List[InlineKeyboardButton]] = [] for button in self.buttons: if type(button) == list: keyboard_list.append([b.get_keyboard_button() for b in button]) else: keyboard_list.append([button.get_keyboard_button()]) keyboard_list.append([Globals.BACK_BUTTON]) return InlineKeyboardMarkup(keyboard_list)
[docs] def set_prompt(self, current_state: object): """ Generates the Prompt, call this before compiling handlers """ self.prompt = Prompt( self.prompt_text, self._get_keyboard(), next_state=current_state, delete_last_message=True, use_markdown=True )
@staticmethod def _add_button_handler(context: MenuContext, current_handlers: List[Handler], button: Button): button.compile(context) handler = button.get_handler() if handler: current_handlers.append(handler) next_state_handler = button.get_next_state_handlers() if next_state_handler: context.current_menu.states[button.handle] = next_state_handler
[docs] def get_handlers(self, context: MenuContext) -> List[Handler]: """ returns a dictionary used to extend the main menu dictionary """ result: List[Handler] = [] # compile all button handlers and add them for element in self.buttons: if type(element) == list: for button in element: self._add_button_handler(context, result, button) else: self._add_button_handler(context, result, element) # add back button if back_to is defined if self.back_to: if self.back_to == Globals.CLOSE_MENU: result.append(Globals.END_HANDLER) else: result.append(get_back_button_handler(context.panels[self.back_to])) # add the extra handlers if there are any if self.extra_handlers: result.extend(self.extra_handlers) return result
[docs]class GOTO: """ Compiles to the prompt of the panel given as destination_panel, useful for Changing Panel in a custom Panel """ prompt: Prompt def __call__(self, update: Update, context: CallbackContext): return self.prompt(update, context)
[docs] def __init__(self, destination_panel: str): self.destination_panel = destination_panel
def compile(self, context: MenuContext): if self.destination_panel not in context.panels: raise ValueError(f"The specified panel '{self.destination_panel}' is not defined") self.prompt = context.panels[self.destination_panel].prompt
[docs]class CustomPanel(GenericPanel): """ An extremely simple implementation of a Panel, useful for building very dynamic apps """
[docs] def __init__(self, prompt: Prompt, handlers: List[Handler], auto_handle_state: bool = True): """ :param prompt: The prompt that will be shown by this panel :param handlers: The list of handlers tied to this panel :param auto_handle_state: Define if the prompt's state will be automatically handled by the wrapper. """ self.prompt = prompt self.handlers = handlers self.auto_handle_state = auto_handle_state
[docs] def set_prompt(self, current_state: object): if self.auto_handle_state: self.prompt.next_state = current_state
[docs] def get_handlers(self, context: MenuContext) -> List[Handler]: for handler in self.handlers: if type(handler.callback) == Chain: for func in handler.callback.functions: if hasattr(func, "compile"): func.compile(context) elif hasattr(handler.callback, "compile"): handler.callback.compile(context) return self.handlers