Source code for MateWrapper.prompts

import logging
from re import search, findall
from typing import Dict, Callable, Union

from telegram import InlineKeyboardMarkup, ParseMode

from MateWrapper.context import MATEVarGetter
from MateWrapper.generics import TelegramFunctionBlueprint, TelegramEvent


logging.basicConfig(
    level=logging.ERROR,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

log = logging.getLogger()
log.setLevel(logging.INFO)


FORMATTING_REGEX = r"\{([a-zA-Z_:.]+)\}"


# Formatting ----

def _format_message(message_text: str, variables: Dict[str, MATEVarGetter], event: TelegramEvent) -> str:
    result_message = message_text
    for var in variables:
        getter = variables[var]
        result_message = result_message.replace(f"{{{var}}}", str(getter.logic(getter, event)))
    return result_message


def _has_formatting(message_text: str) -> bool:
    return search(FORMATTING_REGEX, message_text) is not None


def _get_variable_getters(message_text: str) -> Dict[str, MATEVarGetter]:
    result: Dict[str, MATEVarGetter] = {}
    matches = findall(FORMATTING_REGEX, message_text)
    for variable in matches:
        if variable not in result:
            result[variable] = MATEVarGetter(variable)
    return result


def _autoformat_text(text_to_send: str, event: TelegramEvent):
    if _has_formatting(text_to_send):
        return _format_message(text_to_send, _get_variable_getters(text_to_send), event)
    return text_to_send


[docs]class Prompt(TelegramFunctionBlueprint): """ Class to handle sending prompts via Telegram. Automatically optimizes itself depending on the text & keyboard you pass in. """
[docs] def __init__( self, text: Union[str, Callable[[TelegramEvent], str]], keyboard: Union[InlineKeyboardMarkup, Callable[[TelegramEvent], InlineKeyboardMarkup]] = None, format_text: bool or None = None, next_state: object or None = None, delete_last_message: bool = False, use_markdown: bool = False, use_web_preview: bool = False ): """ :param Union[str, Callable[[TelegramEvent], str]] text: text of the message to send or the function used to generate it. If '{something}' is found in the text then the TelegramWrapper will try to format the text replacing all '{something}' instances with whatever context.chat_data['something'] contains see MATEVarHandler for more info on special characters in format string Can be callable, the logic defined above still applies, see the format_text parameter for more info. :param Union[InlineKeyboardMarkup, Callable[[TelegramEvent], InlineKeyboardMarkup]] keyboard: the inline keyboard to send or the function that will generate the inline keyboard to send. Leave as None to not send any keyboard. :param bool or None format_text: manually set if the text should be formatted, by default the wrapper will automatically determine if the prompt needs to be formatted. It is only taken into account if the text is of type 'callable', that is to say if the text is programmatically generated by a function and is not known beforehand by the program. :param object or None next_state: the return value of the function, used to change state in conversation handlers. Leave at None to not change state. :param bool delete_last_message: if true it will delete either the last message sent by the user or the keyboard that generated the update. :param bool use_markdown: tells telegram to format the message using markdown V2. By default it's false. :param bool use_web_preview: tells telegram to render the web preview of the first link found in the message if true. By default it's false. """ self.text = text self.keyboard = keyboard self.next_state = next_state self.delete_last_message = delete_last_message self.parse_mode = ParseMode.MARKDOWN_V2 if use_markdown else None self.web_preview = not use_web_preview if callable(text): if format_text is None: if callable(keyboard): self.behaviour = self._call_and_call_autoformat else: self.behaviour = self._call_and_send_autoformat else: if format_text: if callable(keyboard): self.behaviour = self._call_and_call_format else: self.behaviour = self._call_and_send_format else: if callable(keyboard): self.behaviour = self._call_and_call_noformat else: self.behaviour = self._call_and_send_noformat else: if _has_formatting(text): self.variables = _get_variable_getters(text) if callable(keyboard): self.behaviour = self._format_and_call else: self.behaviour = self._format_and_send else: if callable(keyboard): self.behaviour = self._send_and_call else: self.behaviour = self._send_and_send
# print(self.behaviour.__name__, type(keyboard)) def _format_and_call(self, text: str, keyboard_func: Callable[[TelegramEvent], InlineKeyboardMarkup], event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=_format_message(text, self.variables, event), reply_markup=keyboard_func(event), parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _format_and_send(self, text: str, keyboard: InlineKeyboardMarkup, event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=_format_message(text, self.variables, event), reply_markup=keyboard, parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) # No formatting behaviours ---- def _send_and_call(self, text: str, keyboard_func: Callable[[TelegramEvent], InlineKeyboardMarkup], event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=text, reply_markup=keyboard_func(event), parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _send_and_send(self, text: str, keyboard: InlineKeyboardMarkup, event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=text, reply_markup=keyboard, parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) # text is callable def _call_and_call_autoformat(self, text_func: Callable[[TelegramEvent], str], keyboard_func: Callable[[TelegramEvent], InlineKeyboardMarkup], event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=_autoformat_text(text_func(event), event), reply_markup=keyboard_func(event), parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _call_and_call_noformat(self, text_func: Callable[[TelegramEvent], str], keyboard_func: Callable[[TelegramEvent], InlineKeyboardMarkup], event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=text_func(event), reply_markup=keyboard_func(event), parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _call_and_call_format(self, text_func: Callable[[TelegramEvent], str], keyboard_func: Callable[[TelegramEvent], InlineKeyboardMarkup], event: TelegramEvent): text_to_send = text_func(event) event.context.bot.send_message( chat_id=event.chat_id, text=_format_message(text_to_send, _get_variable_getters(text_to_send), event), reply_markup=keyboard_func(event), parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _call_and_send_autoformat(self, text_func: Callable[[TelegramEvent], str], keyboard: InlineKeyboardMarkup, event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=_autoformat_text(text_func(event), event), reply_markup=keyboard, parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _call_and_send_noformat(self, text_func: Callable[[TelegramEvent], str], keyboard: InlineKeyboardMarkup, event: TelegramEvent): event.context.bot.send_message( chat_id=event.chat_id, text=text_func(event), reply_markup=keyboard, parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview ) def _call_and_send_format(self, text_func: Callable[[TelegramEvent], str], keyboard: InlineKeyboardMarkup, event: TelegramEvent): text_to_send = text_func(event) event.context.bot.send_message( chat_id=event.chat_id, text=_format_message(text_to_send, _get_variable_getters(text_to_send), event), reply_markup=keyboard, parse_mode=self.parse_mode, disable_web_page_preview=self.web_preview )
[docs] def logic(self, event: TelegramEvent): if self.delete_last_message: try: try: last_message_id = event.update.message.message_id except AttributeError: last_message_id = event.update.callback_query.message.message_id event.context.bot.delete_message( event.chat_id, last_message_id ) except Exception as e: log.error(f"error while trying to delete message: {e}, update: {event.update.to_dict()}") self.behaviour(self.text, self.keyboard, event) return self.next_state