1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Update of state handlers

No need to create state handlers
This commit is contained in:
_run 2021-10-01 15:56:54 +05:00
parent 4a6b5b3d28
commit 2e4280a947
4 changed files with 77 additions and 93 deletions

View File

@ -1,34 +1,48 @@
import telebot import telebot
from telebot.handler_backends import State from telebot import custom_filters
bot = telebot.TeleBot("") bot = telebot.TeleBot("")
@bot.message_handler(commands=['start']) @bot.message_handler(commands=['start'])
def start_ex(message): def start_ex(message):
bot.set_state(message.chat.id, 1) bot.set_state(message.chat.id, 1)
bot.send_message(message.chat.id, 'Hi, write me a name') bot.send_message(message.chat.id, 'Hi, write me a name')
@bot.state_handler(state=1)
def name_get(message, state:State): @bot.message_handler(state="*", commands='cancel')
def any_state(message):
bot.send_message(message.chat.id, "Your state was cancelled.")
bot.delete_state(message.chat.id)
@bot.message_handler(state=1)
def name_get(message):
bot.send_message(message.chat.id, f'Now write me a surname') bot.send_message(message.chat.id, f'Now write me a surname')
state.set(message.chat.id, 2) bot.set_state(message.chat.id, 2)
with state.retrieve_data(message.chat.id) as data: with bot.retrieve_data(message.chat.id) as data:
data['name'] = message.text data['name'] = message.text
@bot.state_handler(state=2) @bot.message_handler(state=2)
def ask_age(message, state:State): def ask_age(message):
bot.send_message(message.chat.id, "What is your age?") bot.send_message(message.chat.id, "What is your age?")
state.set(message.chat.id, 3) bot.set_state(message.chat.id, 3)
with state.retrieve_data(message.chat.id) as data: with bot.retrieve_data(message.chat.id) as data:
data['surname'] = message.text data['surname'] = message.text
@bot.state_handler(state=3) @bot.message_handler(state=3, is_digit=True)
def ready_for_answer(message, state: State): def ready_for_answer(message):
with state.retrieve_data(message.chat.id) as data: with bot.retrieve_data(message.chat.id) as data:
bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
state.finish(message.chat.id) bot.delete_state(message.chat.id)
bot.infinity_polling() @bot.message_handler(state=3, is_digit=False)
def age_incorrect(message):
bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
bot.add_custom_filter(custom_filters.StateFilter(bot))
bot.add_custom_filter(custom_filters.IsDigitFilter())
bot.infinity_polling(skip_pending=True)

View File

@ -499,7 +499,6 @@ class TeleBot:
def process_new_messages(self, new_messages): def process_new_messages(self, new_messages):
self._notify_next_handlers(new_messages) self._notify_next_handlers(new_messages)
self._notify_reply_handlers(new_messages) self._notify_reply_handlers(new_messages)
self._notify_state_handlers(new_messages)
self.__notify_update(new_messages) self.__notify_update(new_messages)
self._notify_command_handlers(self.message_handlers, new_messages) self._notify_command_handlers(self.message_handlers, new_messages)
@ -2386,6 +2385,9 @@ class TeleBot:
""" """
self.current_states.delete_state(chat_id) self.current_states.delete_state(chat_id)
def retrieve_data(self, chat_id):
return self.current_states.retrieve_data(chat_id)
def get_state(self, chat_id): def get_state(self, chat_id):
""" """
Get current state of a user. Get current state of a user.
@ -2394,6 +2396,14 @@ class TeleBot:
""" """
return self.current_states.current_state(chat_id) return self.current_states.current_state(chat_id)
def add_data(self, chat_id, **kwargs):
"""
Add data to states.
:param chat_id:
"""
for key, value in kwargs.items():
self.current_states._add_data(chat_id, key, value)
def register_next_step_handler_by_chat_id( def register_next_step_handler_by_chat_id(
self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None:
""" """
@ -2459,32 +2469,6 @@ class TeleBot:
new_messages.pop(i) # removing message that was detected with next_step_handler new_messages.pop(i) # removing message that was detected with next_step_handler
def _notify_state_handlers(self, new_messages):
"""
Description: TBD
:param new_messages:
:return:
"""
if not self.current_states: return
for i, message in enumerate(new_messages):
need_pop = False
user_state = self.current_states.current_state(message.from_user.id)
if user_state:
for handler in self.state_handlers:
if handler['filters']['state'] == user_state:
for message_filter, filter_value in handler['filters'].items():
if filter_value is None:
continue
if not self._test_filter(message_filter, filter_value, message):
return False
need_pop = True
state = self.current_states
self._exec_task(handler["function"], message, state)
if need_pop:
new_messages.pop(i) # removing message that was detected by states
@staticmethod @staticmethod
def _build_handler_dict(handler, **filters): def _build_handler_dict(handler, **filters):
""" """
@ -2548,7 +2532,7 @@ class TeleBot:
else: else:
self.default_middleware_handlers.append(handler) self.default_middleware_handlers.append(handler)
def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, state=None, **kwargs):
""" """
Message handler decorator. Message handler decorator.
This decorator can be used to decorate functions that must handle certain types of messages. This decorator can be used to decorate functions that must handle certain types of messages.
@ -2591,6 +2575,9 @@ class TeleBot:
if content_types is None: if content_types is None:
content_types = ["text"] content_types = ["text"]
if type(state) is not list and state is not None:
state = [state]
if isinstance(commands, str): if isinstance(commands, str):
logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.")
commands = [commands] commands = [commands]
@ -2605,6 +2592,7 @@ class TeleBot:
content_types=content_types, content_types=content_types,
commands=commands, commands=commands,
regexp=regexp, regexp=regexp,
state=state,
func=func, func=func,
**kwargs) **kwargs)
self.add_message_handler(handler_dict) self.add_message_handler(handler_dict)
@ -2620,7 +2608,7 @@ class TeleBot:
""" """
self.message_handlers.append(handler_dict) self.message_handlers.append(handler_dict)
def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, state=None, **kwargs):
""" """
Registers message handler. Registers message handler.
:param callback: function to be called :param callback: function to be called
@ -2644,6 +2632,7 @@ class TeleBot:
content_types=content_types, content_types=content_types,
commands=commands, commands=commands,
regexp=regexp, regexp=regexp,
state=state,
func=func, func=func,
**kwargs) **kwargs)
self.add_message_handler(handler_dict) self.add_message_handler(handler_dict)
@ -2721,54 +2710,6 @@ class TeleBot:
self.add_edited_message_handler(handler_dict) self.add_edited_message_handler(handler_dict)
def state_handler(self, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs):
"""
State handler for getting input from a user.
:param state: state of a user
:param content_types:
:param regexp:
:param func:
:param chat_types:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler,
state=state,
content_types=content_types,
regexp=regexp,
chat_types=chat_types,
func=func,
**kwargs)
self.add_state_handler(handler_dict)
return handler
return decorator
def add_state_handler(self, handler_dict):
"""
Adds the edit message handler
:param handler_dict:
:return:
"""
self.state_handlers.append(handler_dict)
def register_state_handler(self, callback, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs):
"""
Register a state handler.
:param callback: function to be called
:param state: state to be checked
:param content_types:
:param func:
"""
handler_dict = self._build_handler_dict(callback=callback,
state=state,
content_types=content_types,
regexp=regexp,
chat_types=chat_types,
func=func,
**kwargs)
self.add_state_handler(handler_dict)
def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs):
""" """
Channel post handler decorator Channel post handler decorator
@ -3251,8 +3192,6 @@ class TeleBot:
return filter_value(message) return filter_value(message)
elif self.custom_filters and message_filter in self.custom_filters: elif self.custom_filters and message_filter in self.custom_filters:
return self._check_filter(message_filter,filter_value,message) return self._check_filter(message_filter,filter_value,message)
elif message_filter == 'state':
return True
else: else:
return False return False

View File

@ -146,3 +146,30 @@ class IsAdminFilter(SimpleCustomFilter):
def check(self, message): def check(self, message):
return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator']
class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
"""
def __init__(self, bot):
self.bot = bot
key = 'state'
def check(self, message, text):
if self.bot.current_states.current_state(message.from_user.id) is False:return False
elif '*' in text:return True
return self.bot.current_states.current_state(message.from_user.id) in text
class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
"""
key = 'is_digit'
def check(self, message):
return message.text.isdigit()

View File

@ -179,6 +179,10 @@ class State:
""" """
self.add_state(chat_id,new_state) self.add_state(chat_id,new_state)
def _add_data(self, chat_id, key, value):
result = self._states[chat_id]['data'][key] = value
return result
def finish(self, chat_id): def finish(self, chat_id):
""" """
Finish(delete) state of a user. Finish(delete) state of a user.