From 286188f380e98028d03a8fdb543296cca09be480 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sun, 12 Apr 2020 22:41:32 +0500 Subject: [PATCH 1/5] Add Step/Reply Handler Backend Mechanism Implement Memory, File, Redis Backends --- requirements.txt | 1 + setup.py | 1 + telebot/__init__.py | 191 ++++++++++++++---------------------- telebot/handler_backends.py | 144 +++++++++++++++++++++++++++ 4 files changed, 217 insertions(+), 120 deletions(-) create mode 100644 telebot/handler_backends.py diff --git a/requirements.txt b/requirements.txt index 6e4ca40..f58156a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ pytest==3.0.2 requests==2.20.0 six==1.9.0 wheel==0.24.0 +redis==3.4.1 diff --git a/setup.py b/setup.py index 44a5cde..7c7c856 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ setup(name='pyTelegramBotAPI', install_requires=['requests', 'six'], extras_require={ 'json': 'ujson', + 'redis': 'redis>=3.4.1' }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/telebot/__init__.py b/telebot/__init__.py index 72f6c71..89b792a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2,8 +2,6 @@ from __future__ import print_function import logging -import os -import pickle import re import sys import threading @@ -23,6 +21,7 @@ logger.addHandler(console_output_handler) logger.setLevel(logging.ERROR) from telebot import apihelper, types, util +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend """ Module : telebot @@ -43,64 +42,6 @@ class Handler: return getattr(self, item) -class Saver: - """ - Class for saving (next step|reply) handlers - """ - - def __init__(self, handlers, filename, delay): - self.handlers = handlers - self.filename = filename - self.delay = delay - self.timer = threading.Timer(delay, self.save_handlers) - - def start_save_timer(self): - if not self.timer.is_alive(): - if self.delay <= 0: - self.save_handlers() - else: - self.timer = threading.Timer(self.delay, self.save_handlers) - self.timer.start() - - def save_handlers(self): - self.dump_handlers(self.handlers, self.filename) - - def load_handlers(self, filename, del_file_after_loading=True): - tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) - if tmp is not None: - self.handlers.update(tmp) - - @staticmethod - def dump_handlers(handlers, filename, file_mode="wb"): - dirs = filename.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - - with open(filename + ".tmp", file_mode) as file: - if (apihelper.CUSTOM_SERIALIZER is None): - pickle.dump(handlers, file) - else: - apihelper.CUSTOM_SERIALIZER.dump(handlers, file) - - if os.path.isfile(filename): - os.remove(filename) - - os.rename(filename + ".tmp", filename) - - @staticmethod - def return_load_handlers(filename, del_file_after_loading=True): - if os.path.isfile(filename) and os.path.getsize(filename) > 0: - with open(filename, "rb") as file: - if (apihelper.CUSTOM_SERIALIZER is None): - handlers = pickle.load(file) - else: - handlers = apihelper.CUSTOM_SERIALIZER.load(file) - - if del_file_after_loading: - os.remove(filename) - - return handlers - - class TeleBot: """ This is TeleBot Class Methods: @@ -141,7 +82,10 @@ class TeleBot: answerInlineQuery """ - def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): + def __init__( + self, token, threaded=True, skip_pending=False, num_threads=2, + next_step_backend=None, reply_backend=None + ): """ :param token: bot API token :return: Telebot object. @@ -155,14 +99,13 @@ class TeleBot: self.last_update_id = 0 self.exc_info = None - # key: message_id, value: handler list - self.reply_handlers = {} + self.next_step_backend = next_step_backend + if not self.next_step_backend: + self.next_step_backend = MemoryHandlerBackend() - # key: chat_id, value: handler list - self.next_step_handlers = {} - - self.next_step_saver = None - self.reply_saver = None + self.reply_backend = reply_backend + if not self.reply_backend: + self.reply_backend = MemoryHandlerBackend() self.message_handlers = [] self.edited_message_handlers = [] @@ -196,51 +139,89 @@ class TeleBot: def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): """ - Enable saving next step handlers (by default saving disable) + Enable saving next step handlers (by default saving disabled) + + This function explicitly assigns FileHandlerBackend (instead of Saver) just to keep backward + compatibility whose purpose was to enable file saving capability for handlers. And the same + implementation is now available with FileHandlerBackend + + Most probably this function should be deprecated in future major releases :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ - self.next_step_saver = Saver(self.next_step_handlers, filename, delay) + self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay) def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) + This function explicitly assigns FileHandlerBackend (instead of Saver) just to keep backward + compatibility whose purpose was to enable file saving capability for handlers. And the same + implementation is now available with FileHandlerBackend + + Most probably this function should be deprecated in future major releases + :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ - self.reply_saver = Saver(self.reply_handlers, filename, delay) + self.reply_backend = FileHandlerBackend(self.reply_backend.handlers, filename, delay) def disable_save_next_step_handlers(self): """ Disable saving next step handlers (by default saving disable) + + This function is left to keep backward compatibility whose purpose was to disable file saving capability + for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new next_step_backend backend + instead of FileHandlerBackend. + + Most probably this function should be deprecated in future major releases """ - self.next_step_saver = None + self.next_step_backend = MemoryHandlerBackend(self.next_step_backend.handlers) def disable_save_reply_handlers(self): """ Disable saving next step handlers (by default saving disable) + + This function is left to keep backward compatibility whose purpose was to disable file saving capability + for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new reply_backend backend + instead of FileHandlerBackend. + + Most probably this function should be deprecated in future major releases """ - self.reply_saver = None + self.reply_backend = MemoryHandlerBackend(self.reply_backend.handlers) def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): """ Load next step handlers from save file + This function is left to keep backward compatibility whose purpose was to load handlers from file with the + help of FileHandlerBackend and is only recommended to use if next_step_backend was assigned as + FileHandlerBackend before entering this function + + Most probably this function should be deprecated in future major releases + :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.next_step_saver.load_handlers(filename, del_file_after_loading) + self.next_step_backend: FileHandlerBackend + self.next_step_backend.load_handlers(filename, del_file_after_loading) def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): """ Load reply handlers from save file + This function is left to keep backward compatibility whose purpose was to load handlers from file with the + help of FileHandlerBackend and is only recommended to use if reply_backend was assigned as + FileHandlerBackend before entering this function + + Most probably this function should be deprecated in future major releases + :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.reply_saver.load_handlers(filename) + self.reply_backend: FileHandlerBackend + self.reply_backend.load_handlers(filename, del_file_after_loading) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) @@ -1399,12 +1380,7 @@ class TeleBot: :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. """ - if message_id in self.reply_handlers.keys(): - self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs)) - else: - self.reply_handlers[message_id] = [Handler(callback, *args, **kwargs)] - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) def _notify_reply_handlers(self, new_messages): """ @@ -1414,14 +1390,9 @@ class TeleBot: """ for message in new_messages: if hasattr(message, "reply_to_message") and message.reply_to_message is not None: - reply_msg_id = message.reply_to_message.message_id - if reply_msg_id in self.reply_handlers.keys(): - handlers = self.reply_handlers[reply_msg_id] - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - self.reply_handlers.pop(reply_msg_id) - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id) + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1448,13 +1419,7 @@ class TeleBot: :param args: Args to pass in callback func :param kwargs: Args to pass in callback func """ - if chat_id in self.next_step_handlers.keys(): - self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs)) - else: - self.next_step_handlers[chat_id] = [Handler(callback, *args, **kwargs)] - - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() + self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) def clear_step_handler(self, message): """ @@ -1471,10 +1436,7 @@ class TeleBot: :param chat_id: The chat for which we want to clear next step handlers """ - self.next_step_handlers[chat_id] = [] - - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() + self.next_step_backend.clear_handlers(chat_id) def clear_reply_handlers(self, message): """ @@ -1491,10 +1453,7 @@ class TeleBot: :param message_id: The message id for which we want to clear reply handlers """ - self.reply_handlers[message_id] = [] - - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + self.reply_backend.clear_handlers(message_id) def _notify_next_handlers(self, new_messages): """ @@ -1502,22 +1461,14 @@ class TeleBot: :param new_messages: :return: """ - i = 0 - while i < len(new_messages): - message = new_messages[i] - chat_id = message.chat.id - was_poped = False - if chat_id in self.next_step_handlers.keys(): - handlers = self.next_step_handlers.pop(chat_id, None) - if handlers: - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - new_messages.pop(i) # removing message that detects with next_step_handler - was_poped = True - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() - if not was_poped: - i += 1 + for i, message in enumerate(new_messages): + need_pop = False + handlers = self.next_step_backend.get_handlers(message.chat.id) + for handler in handlers: + need_pop = True + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + if need_pop: + new_messages.pop(i) # removing message that detects with next_step_handler @staticmethod def _build_handler_dict(handler, **filters): diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py new file mode 100644 index 0000000..97dd44f --- /dev/null +++ b/telebot/handler_backends.py @@ -0,0 +1,144 @@ +import os +import pickle +import threading + +from telebot import apihelper + + +class HandlerBackend: + """ + Class for saving (next step|reply) handlers + """ + handlers = {} + + def __init__(self, handlers=None): + if handlers: + self.handlers = handlers + + def register_handler(self, handler_group_id, handler): + raise NotImplementedError() + + def clear_handlers(self, handler_group_id): + raise NotImplementedError() + + def get_handlers(self, handler_group_id): + raise NotImplementedError() + + +class MemoryHandlerBackend(HandlerBackend): + def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + + def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, []) + + def get_handlers(self, handler_group_id): + return self.handlers.pop(handler_group_id, []) + + +class FileHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): + super().__init__(handlers) + self.filename = filename + self.delay = delay + self.timer = threading.Timer(delay, self.save_handlers) + + def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + + self.start_save_timer() + + def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, []) + + self.start_save_timer() + + def get_handlers(self, handler_group_id): + handlers = self.handlers.pop(handler_group_id, []) + + self.start_save_timer() + + return handlers + + def start_save_timer(self): + if not self.timer.is_alive(): + if self.delay <= 0: + self.save_handlers() + else: + self.timer = threading.Timer(self.delay, self.save_handlers) + self.timer.start() + + def save_handlers(self): + self.dump_handlers(self.handlers, self.filename) + + def load_handlers(self, filename, del_file_after_loading=True): + tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) + if tmp is not None: + self.handlers.update(tmp) + + @staticmethod + def dump_handlers(handlers, filename, file_mode="wb"): + dirs = filename.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + + with open(filename + ".tmp", file_mode) as file: + if (apihelper.CUSTOM_SERIALIZER is None): + pickle.dump(handlers, file) + else: + apihelper.CUSTOM_SERIALIZER.dump(handlers, file) + + if os.path.isfile(filename): + os.remove(filename) + + os.rename(filename + ".tmp", filename) + + @staticmethod + def return_load_handlers(filename, del_file_after_loading=True): + if os.path.isfile(filename) and os.path.getsize(filename) > 0: + with open(filename, "rb") as file: + if (apihelper.CUSTOM_SERIALIZER is None): + handlers = pickle.load(file) + else: + handlers = apihelper.CUSTOM_SERIALIZER.load(file) + + if del_file_after_loading: + os.remove(filename) + + return handlers + + +class RedisHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'): + super().__init__(handlers) + from redis import Redis + self.prefix = prefix + self.redis = Redis(host, port, db) + + def _key(self, handle_group_id): + return ':'.join((self.prefix, str(handle_group_id))) + + def register_handler(self, handler_group_id, handler): + handlers = [] + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + handlers.append(handler) + self.redis.set(self._key(handler_group_id), pickle.dumps(handlers)) + + def clear_handlers(self, handler_group_id): + self.redis.delete(self._key(handler_group_id)) + + def get_handlers(self, handler_group_id): + handlers = [] + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + self.clear_handlers(handler_group_id) + + return handlers From 003c5db37f0b5930f1fe8f042c0edd123ddbc2ff Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 01:45:19 +0500 Subject: [PATCH 2/5] Add filename checking --- telebot/handler_backends.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 97dd44f..cb68594 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -77,7 +77,9 @@ class FileHandlerBackend(HandlerBackend): def save_handlers(self): self.dump_handlers(self.handlers, self.filename) - def load_handlers(self, filename, del_file_after_loading=True): + def load_handlers(self, filename=None, del_file_after_loading=True): + if not filename: + filename = self.filename tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) if tmp is not None: self.handlers.update(tmp) From e7e7c5813318a2463c4aeb94a7699fe2fac7fb9a Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 01:45:52 +0500 Subject: [PATCH 3/5] Add Memory, File, Redis Backend tests --- tests/test_handler_backends.py | 261 +++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 tests/test_handler_backends.py diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py new file mode 100644 index 0000000..9f18114 --- /dev/null +++ b/tests/test_handler_backends.py @@ -0,0 +1,261 @@ +import os +import time + +import pytest + +import telebot +from telebot import types, MemoryHandlerBackend, FileHandlerBackend +from telebot.handler_backends import RedisHandlerBackend + + +@pytest.fixture() +def telegram_bot(): + return telebot.TeleBot('', threaded=False) + + +@pytest.fixture +def private_chat(): + return types.Chat(id=11, type='private') + + +@pytest.fixture +def user(): + return types.User(id=10, is_bot=False, first_name='Some User') + + +@pytest.fixture() +def message(user, private_chat): + params = {'text': '/start'} + return types.Message( + message_id=1, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string="" + ) + + +@pytest.fixture() +def reply_to_message(user, private_chat, message): + params = {'text': '/start'} + reply_message = types.Message( + message_id=2, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string="" + ) + reply_message.reply_to_message = message + return reply_message + + +@pytest.fixture() +def update_type(message): + edited_message = None + channel_post = None + edited_channel_post = None + inline_query = None + chosen_inline_result = None + callback_query = None + shipping_query = None + pre_checkout_query = None + poll = None + return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + + +@pytest.fixture() +def reply_to_message_update_type(reply_to_message): + edited_message = None + channel_post = None + edited_channel_post = None + inline_query = None + chosen_inline_result = None + callback_query = None + shipping_query = None + pre_checkout_query = None + poll = None + return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post, + inline_query, + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + + +def next_handler(message): + message.text = 'entered next_handler' + + +def test_memory_handler_backend_default_backend(telegram_bot): + assert telegram_bot.reply_backend.__class__ == MemoryHandlerBackend + assert telegram_bot.next_step_backend.__class__ == MemoryHandlerBackend + + +def test_memory_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + +def test_memory_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + +def test_memory_handler_backend_register_reply_handler(telegram_bot, private_chat, update_type, + reply_to_message_update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1 + + telegram_bot.process_new_updates([reply_to_message_update_type]) + assert reply_to_message_update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.reply_backend.handlers + + +def test_memory_handler_backend_clear_reply_handler(telegram_bot, private_chat, update_type, + reply_to_message_update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1 + + telegram_bot.clear_reply_handlers_by_message_id(update_type.message.message_id) + + assert update_type.message.message_id not in telegram_bot.reply_backend.handlers + + telegram_bot.process_new_updates([reply_to_message_update_type]) + assert reply_to_message_update_type.message.text == 'entered start' + + +def test_file_handler_backend_register_next_step_handler(private_chat, update_type): + telegram_bot = telebot.TeleBot( + token='', + threaded=False, + next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=1) + ) + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + time.sleep(2) + + assert os.path.exists(telegram_bot.next_step_backend.filename) + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.next_step_backend.handlers = {} + + telegram_bot.next_step_backend.load_handlers() + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + time.sleep(2) + if os.path.exists(telegram_bot.next_step_backend.filename): + os.remove(telegram_bot.next_step_backend.filename) + + +def test_file_handler_backend_clear_next_step_handler(private_chat, update_type): + telegram_bot = telebot.TeleBot( + token='', + threaded=False, + next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=1) + ) + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + time.sleep(2) + + assert os.path.exists(telegram_bot.next_step_backend.filename) + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + time.sleep(2) + + telegram_bot.next_step_backend.load_handlers() + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + time.sleep(2) + if os.path.exists(telegram_bot.next_step_backend.filename): + os.remove(telegram_bot.next_step_backend.filename) + + +def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1') + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + +def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2') + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' From 3aec66bc0dfbdd1aafe5616b0ba68f3a1492cd72 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 03:17:13 +0500 Subject: [PATCH 4/5] Remove class static variable --- telebot/handler_backends.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index cb68594..cb6c688 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -9,11 +9,10 @@ class HandlerBackend: """ Class for saving (next step|reply) handlers """ - handlers = {} - def __init__(self, handlers=None): - if handlers: - self.handlers = handlers + if handlers is None: + handlers = {} + self.handlers = handlers def register_handler(self, handler_group_id, handler): raise NotImplementedError() From 0881e343819877ba79ad5dcf75452003cb6a9cda Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 03:21:10 +0500 Subject: [PATCH 5/5] Refactor tests --- tests/test_handler_backends.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 9f18114..b16642e 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -1,3 +1,7 @@ +import sys + +sys.path.append('../') + import os import time @@ -155,12 +159,8 @@ def test_memory_handler_backend_clear_reply_handler(telegram_bot, private_chat, assert reply_to_message_update_type.message.text == 'entered start' -def test_file_handler_backend_register_next_step_handler(private_chat, update_type): - telegram_bot = telebot.TeleBot( - token='', - threaded=False, - next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=1) - ) +def test_file_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=0.1) @telegram_bot.message_handler(commands=['start']) def start(message): @@ -170,7 +170,7 @@ def test_file_handler_backend_register_next_step_handler(private_chat, update_ty telegram_bot.process_new_updates([update_type]) assert update_type.message.text == 'entered start' - time.sleep(2) + time.sleep(0.2) assert os.path.exists(telegram_bot.next_step_backend.filename) @@ -187,17 +187,13 @@ def test_file_handler_backend_register_next_step_handler(private_chat, update_ty assert private_chat.id not in telegram_bot.next_step_backend.handlers - time.sleep(2) + time.sleep(0.2) if os.path.exists(telegram_bot.next_step_backend.filename): os.remove(telegram_bot.next_step_backend.filename) -def test_file_handler_backend_clear_next_step_handler(private_chat, update_type): - telegram_bot = telebot.TeleBot( - token='', - threaded=False, - next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=1) - ) +def test_file_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=0.1) @telegram_bot.message_handler(commands=['start']) def start(message): @@ -209,13 +205,13 @@ def test_file_handler_backend_clear_next_step_handler(private_chat, update_type) assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 - time.sleep(2) + time.sleep(0.2) assert os.path.exists(telegram_bot.next_step_backend.filename) telegram_bot.clear_step_handler_by_chat_id(private_chat.id) - time.sleep(2) + time.sleep(0.2) telegram_bot.next_step_backend.load_handlers() @@ -224,7 +220,7 @@ def test_file_handler_backend_clear_next_step_handler(private_chat, update_type) telegram_bot.process_new_updates([update_type]) assert update_type.message.text == 'entered start' - time.sleep(2) + time.sleep(0.2) if os.path.exists(telegram_bot.next_step_backend.filename): os.remove(telegram_bot.next_step_backend.filename)