Example #1
0
    def __init__(
        self,
        token: str,
        base_url: str = None,
        base_file_url: str = None,
        request: 'Request' = None,
        private_key: bytes = None,
        private_key_password: bytes = None,
        defaults: 'Defaults' = None,
        arbitrary_callback_data: Union[bool, int] = False,
    ):
        super().__init__(
            token=token,
            base_url=base_url,
            base_file_url=base_file_url,
            request=request,
            private_key=private_key,
            private_key_password=private_key_password,
            defaults=defaults,
        )

        # set up callback_data
        if not isinstance(arbitrary_callback_data, bool):
            maxsize = cast(int, arbitrary_callback_data)
            self.arbitrary_callback_data = True
        else:
            maxsize = 1024
            self.arbitrary_callback_data = arbitrary_callback_data
        self.callback_data_cache: CallbackDataCache = CallbackDataCache(
            bot=self, maxsize=maxsize)
    def test_init_and_access__persistent_data(self, bot):
        keyboard_data = _KeyboardData('123', 456, {'button': 678})
        persistent_data = ([keyboard_data.to_tuple()], {'id': '123'})
        cdc = CallbackDataCache(bot, persistent_data=persistent_data)

        assert cdc.maxsize == 1024
        assert dict(cdc._callback_queries) == {'id': '123'}
        assert list(cdc._keyboard_data.keys()) == ['123']
        assert cdc._keyboard_data['123'].keyboard_uuid == '123'
        assert cdc._keyboard_data['123'].access_time == 456
        assert cdc._keyboard_data['123'].button_data == {'button': 678}

        assert cdc.persistence_data == persistent_data
    def test_process_keyboard_full(self, bot):
        cdc = CallbackDataCache(bot, maxsize=1)
        changing_button_1 = InlineKeyboardButton('changing',
                                                 callback_data='some data 1')
        changing_button_2 = InlineKeyboardButton('changing',
                                                 callback_data='some data 2')
        non_changing_button = InlineKeyboardButton('non-changing',
                                                   url='https://ptb.org')
        reply_markup = InlineKeyboardMarkup.from_row(
            [non_changing_button, changing_button_1, changing_button_2])

        out1 = cdc.process_keyboard(reply_markup)
        assert len(cdc.persistence_data[0]) == 1
        out2 = cdc.process_keyboard(reply_markup)
        assert len(cdc.persistence_data[0]) == 1

        keyboard_1, button_1 = cdc.extract_uuids(
            out1.inline_keyboard[0][1].callback_data)
        keyboard_2, button_2 = cdc.extract_uuids(
            out2.inline_keyboard[0][2].callback_data)
        assert cdc.persistence_data[0][0][0] != keyboard_1
        assert cdc.persistence_data[0][0][0] == keyboard_2
 def test_init_maxsize(self, maxsize, bot):
     assert CallbackDataCache(bot).maxsize == 1024
     cdc = CallbackDataCache(bot, maxsize=maxsize)
     assert cdc.maxsize == maxsize
     assert cdc.bot is bot
def callback_data_cache(bot):
    return CallbackDataCache(bot)
Example #6
0
    def __init__(
        self,
        bot: 'Bot',
        update_queue: Queue,
        workers: int = 4,
        exception_event: Event = None,
        job_queue: 'JobQueue' = None,
        persistence: BasePersistence = None,
        use_context: bool = True,
        context_types: ContextTypes[CCT, UD, CD, BD] = None,
    ):
        self.bot = bot
        self.update_queue = update_queue
        self.job_queue = job_queue
        self.workers = workers
        self.use_context = use_context
        self.context_types = cast(ContextTypes[CCT, UD, CD, BD], context_types
                                  or ContextTypes())

        if not use_context:
            warnings.warn(
                'Old Handler API is deprecated - see https://git.io/fxJuV for details',
                TelegramDeprecationWarning,
                stacklevel=3,
            )

        if self.workers < 1:
            warnings.warn(
                'Asynchronous callbacks can not be processed without at least one worker thread.'
            )

        self.user_data: DefaultDict[int, UD] = defaultdict(
            self.context_types.user_data)
        self.chat_data: DefaultDict[int, CD] = defaultdict(
            self.context_types.chat_data)
        self.bot_data = self.context_types.bot_data()
        self.persistence: Optional[BasePersistence] = None
        self._update_persistence_lock = Lock()
        if persistence:
            if not isinstance(persistence, BasePersistence):
                raise TypeError(
                    "persistence must be based on telegram.ext.BasePersistence"
                )
            self.persistence = persistence
            self.persistence.set_bot(self.bot)
            if self.persistence.store_user_data:
                self.user_data = self.persistence.get_user_data()
                if not isinstance(self.user_data, defaultdict):
                    raise ValueError("user_data must be of type defaultdict")
            if self.persistence.store_chat_data:
                self.chat_data = self.persistence.get_chat_data()
                if not isinstance(self.chat_data, defaultdict):
                    raise ValueError("chat_data must be of type defaultdict")
            if self.persistence.store_bot_data:
                self.bot_data = self.persistence.get_bot_data()
                if not isinstance(self.bot_data, self.context_types.bot_data):
                    raise ValueError(
                        f"bot_data must be of type {self.context_types.bot_data.__name__}"
                    )
            if self.persistence.store_callback_data:
                self.bot = cast(telegram.ext.extbot.ExtBot, self.bot)
                persistent_data = self.persistence.get_callback_data()
                if persistent_data is not None:
                    if not isinstance(persistent_data,
                                      tuple) and len(persistent_data) != 2:
                        raise ValueError('callback_data must be a 2-tuple')
                    self.bot.callback_data_cache = CallbackDataCache(
                        self.bot,
                        self.bot.callback_data_cache.maxsize,
                        persistent_data=persistent_data,
                    )
        else:
            self.persistence = None

        self.handlers: Dict[int, List[Handler]] = {}
        """Dict[:obj:`int`, List[:class:`telegram.ext.Handler`]]: Holds the handlers per group."""
        self.groups: List[int] = []
        """List[:obj:`int`]: A list with all groups."""
        self.error_handlers: Dict[Callable, Union[bool, DefaultValue]] = {}
        """Dict[:obj:`callable`, :obj:`bool`]: A dict, where the keys are error handlers and the
        values indicate whether they are to be run asynchronously."""

        self.running = False
        """:obj:`bool`: Indicates if this dispatcher is running."""
        self.__stop_event = Event()
        self.__exception_event = exception_event or Event()
        self.__async_queue: Queue = Queue()
        self.__async_threads: Set[Thread] = set()

        # For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's
        # only one instance of Dispatcher, it will be possible to use the `run_async` decorator.
        with self.__singleton_lock:
            if self.__singleton_semaphore.acquire(blocking=False):  # pylint: disable=R1732
                self._set_singleton(self)
            else:
                self._set_singleton(None)