mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Merge pull request #1336 from coder2020official/master
File support for states
This commit is contained in:
commit
b979c2fa1f
@ -61,4 +61,8 @@ def age_incorrect(message):
|
|||||||
|
|
||||||
bot.add_custom_filter(custom_filters.StateFilter(bot))
|
bot.add_custom_filter(custom_filters.StateFilter(bot))
|
||||||
bot.add_custom_filter(custom_filters.IsDigitFilter())
|
bot.add_custom_filter(custom_filters.IsDigitFilter())
|
||||||
|
|
||||||
|
# set saving states into file.
|
||||||
|
bot.enable_saving_states() # you can delete this if you do not need to save states
|
||||||
|
|
||||||
bot.infinity_polling(skip_pending=True)
|
bot.infinity_polling(skip_pending=True)
|
@ -27,7 +27,7 @@ logger.addHandler(console_output_handler)
|
|||||||
logger.setLevel(logging.ERROR)
|
logger.setLevel(logging.ERROR)
|
||||||
|
|
||||||
from telebot import apihelper, util, types
|
from telebot import apihelper, util, types
|
||||||
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, State
|
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile
|
||||||
|
|
||||||
|
|
||||||
REPLY_MARKUP_TYPES = Union[
|
REPLY_MARKUP_TYPES = Union[
|
||||||
@ -188,7 +188,8 @@ class TeleBot:
|
|||||||
self.custom_filters = {}
|
self.custom_filters = {}
|
||||||
self.state_handlers = []
|
self.state_handlers = []
|
||||||
|
|
||||||
self.current_states = State()
|
self.current_states = StateMemory()
|
||||||
|
|
||||||
|
|
||||||
if apihelper.ENABLE_MIDDLEWARE:
|
if apihelper.ENABLE_MIDDLEWARE:
|
||||||
self.typed_middleware_handlers = {
|
self.typed_middleware_handlers = {
|
||||||
@ -237,6 +238,17 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay)
|
self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay)
|
||||||
|
|
||||||
|
def enable_saving_states(self, filename="./.state-save/states.pkl"):
|
||||||
|
"""
|
||||||
|
Enable saving states (by default saving disabled)
|
||||||
|
|
||||||
|
:param filename: Filename of saving file
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.current_states = StateFile(filename=filename)
|
||||||
|
self.current_states._create_dir()
|
||||||
|
|
||||||
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
|
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
|
||||||
"""
|
"""
|
||||||
Enable saving reply handlers (by default saving disable)
|
Enable saving reply handlers (by default saving disable)
|
||||||
@ -345,7 +357,7 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
return apihelper.delete_webhook(self.token, drop_pending_updates, timeout)
|
return apihelper.delete_webhook(self.token, drop_pending_updates, timeout)
|
||||||
|
|
||||||
def get_webhook_info(self, timeout=None):
|
def get_webhook_info(self, timeout: Optional[int]=None):
|
||||||
"""
|
"""
|
||||||
Use this method to get current webhook status. Requires no parameters.
|
Use this method to get current webhook status. Requires no parameters.
|
||||||
If the bot is using getUpdates, will return an object with the url field empty.
|
If the bot is using getUpdates, will return an object with the url field empty.
|
||||||
@ -2369,7 +2381,7 @@ class TeleBot:
|
|||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs)
|
self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs)
|
||||||
|
|
||||||
def set_state(self, chat_id, state):
|
def set_state(self, chat_id: int, state: Union[int, str]):
|
||||||
"""
|
"""
|
||||||
Sets a new state of a user.
|
Sets a new state of a user.
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
@ -2377,7 +2389,7 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
self.current_states.add_state(chat_id, state)
|
self.current_states.add_state(chat_id, state)
|
||||||
|
|
||||||
def delete_state(self, chat_id):
|
def delete_state(self, chat_id: int):
|
||||||
"""
|
"""
|
||||||
Delete the current state of a user.
|
Delete the current state of a user.
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
@ -2385,10 +2397,10 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
self.current_states.delete_state(chat_id)
|
self.current_states.delete_state(chat_id)
|
||||||
|
|
||||||
def retrieve_data(self, chat_id):
|
def retrieve_data(self, chat_id: int):
|
||||||
return self.current_states.retrieve_data(chat_id)
|
return self.current_states.retrieve_data(chat_id)
|
||||||
|
|
||||||
def get_state(self, chat_id):
|
def get_state(self, chat_id: int):
|
||||||
"""
|
"""
|
||||||
Get current state of a user.
|
Get current state of a user.
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
@ -2396,7 +2408,7 @@ 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):
|
def add_data(self, chat_id: int, **kwargs):
|
||||||
"""
|
"""
|
||||||
Add data to states.
|
Add data to states.
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
|
@ -158,9 +158,9 @@ class StateFilter(AdvancedCustomFilter):
|
|||||||
key = 'state'
|
key = 'state'
|
||||||
|
|
||||||
def check(self, message, text):
|
def check(self, message, text):
|
||||||
if self.bot.current_states.current_state(message.from_user.id) is False:return False
|
if self.bot.current_states.current_state(message.from_user.id) is False: return False
|
||||||
elif text == '*':return True
|
elif text == '*': return True
|
||||||
elif type(text) is list:return self.bot.current_states.current_state(message.from_user.id) in text
|
elif type(text) is list: return self.bot.current_states.current_state(message.from_user.id) in text
|
||||||
return self.bot.current_states.current_state(message.from_user.id) == text
|
return self.bot.current_states.current_state(message.from_user.id) == text
|
||||||
|
|
||||||
class IsDigitFilter(SimpleCustomFilter):
|
class IsDigitFilter(SimpleCustomFilter):
|
||||||
|
@ -143,7 +143,7 @@ class RedisHandlerBackend(HandlerBackend):
|
|||||||
return handlers
|
return handlers
|
||||||
|
|
||||||
|
|
||||||
class State:
|
class StateMemory:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._states = {}
|
self._states = {}
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ class State:
|
|||||||
|
|
||||||
def delete_state(self, chat_id):
|
def delete_state(self, chat_id):
|
||||||
"""Delete a state"""
|
"""Delete a state"""
|
||||||
return self._states.pop(chat_id)
|
self._states.pop(chat_id)
|
||||||
|
|
||||||
def _get_data(self, chat_id):
|
def _get_data(self, chat_id):
|
||||||
return self._states[chat_id]['data']
|
return self._states[chat_id]['data']
|
||||||
@ -195,7 +195,7 @@ class State:
|
|||||||
Save input text.
|
Save input text.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
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
|
||||||
|
|
||||||
Also, at the end of your 'Form' you can get the name:
|
Also, at the end of your 'Form' you can get the name:
|
||||||
@ -203,11 +203,114 @@ class State:
|
|||||||
"""
|
"""
|
||||||
return StateContext(self, chat_id)
|
return StateContext(self, chat_id)
|
||||||
|
|
||||||
|
|
||||||
|
class StateFile:
|
||||||
|
"""
|
||||||
|
Class to save states in a file.
|
||||||
|
"""
|
||||||
|
def __init__(self, filename):
|
||||||
|
self.file_path = filename
|
||||||
|
|
||||||
|
def add_state(self, chat_id, state):
|
||||||
|
"""
|
||||||
|
Add a state.
|
||||||
|
:param chat_id:
|
||||||
|
:param state: new state
|
||||||
|
"""
|
||||||
|
states_data = self._read_data()
|
||||||
|
if chat_id in states_data:
|
||||||
|
states_data[chat_id]['state'] = state
|
||||||
|
return self._save_data(states_data)
|
||||||
|
else:
|
||||||
|
new_data = states_data[chat_id] = {'state': state,'data': {}}
|
||||||
|
return self._save_data(states_data)
|
||||||
|
|
||||||
|
|
||||||
|
def current_state(self, chat_id):
|
||||||
|
"""Current state."""
|
||||||
|
states_data = self._read_data()
|
||||||
|
if chat_id in states_data: return states_data[chat_id]['state']
|
||||||
|
else: return False
|
||||||
|
|
||||||
|
def delete_state(self, chat_id):
|
||||||
|
"""Delete a state"""
|
||||||
|
states_data = self._read_data()
|
||||||
|
states_data.pop(chat_id)
|
||||||
|
self._save_data(states_data)
|
||||||
|
|
||||||
|
def _read_data(self):
|
||||||
|
"""
|
||||||
|
Read the data from file.
|
||||||
|
"""
|
||||||
|
file = open(self.file_path, 'rb')
|
||||||
|
states_data = pickle.load(file)
|
||||||
|
file.close()
|
||||||
|
return states_data
|
||||||
|
|
||||||
|
def _create_dir(self):
|
||||||
|
"""
|
||||||
|
Create directory .save-handlers.
|
||||||
|
"""
|
||||||
|
dirs = self.file_path.rsplit('/', maxsplit=1)[0]
|
||||||
|
os.makedirs(dirs, exist_ok=True)
|
||||||
|
if not os.path.isfile(self.file_path):
|
||||||
|
with open(self.file_path,'wb') as file:
|
||||||
|
pickle.dump({}, file)
|
||||||
|
|
||||||
|
def _save_data(self, new_data):
|
||||||
|
"""
|
||||||
|
Save data after editing.
|
||||||
|
:param new_data:
|
||||||
|
"""
|
||||||
|
with open(self.file_path, 'wb+') as state_file:
|
||||||
|
pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_data(self, chat_id):
|
||||||
|
return self._read_data()[chat_id]['data']
|
||||||
|
|
||||||
|
def set(self, chat_id, new_state):
|
||||||
|
"""
|
||||||
|
Set a new state for a user.
|
||||||
|
:param chat_id:
|
||||||
|
:param new_state: new_state of a user
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.add_state(chat_id,new_state)
|
||||||
|
|
||||||
|
def _add_data(self, chat_id, key, value):
|
||||||
|
states_data = self._read_data()
|
||||||
|
result = states_data[chat_id]['data'][key] = value
|
||||||
|
self._save_data(result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def finish(self, chat_id):
|
||||||
|
"""
|
||||||
|
Finish(delete) state of a user.
|
||||||
|
:param chat_id:
|
||||||
|
"""
|
||||||
|
self.delete_state(chat_id)
|
||||||
|
|
||||||
|
def retrieve_data(self, chat_id):
|
||||||
|
"""
|
||||||
|
Save input text.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
with bot.retrieve_data(message.chat.id) as data:
|
||||||
|
data['name'] = message.text
|
||||||
|
|
||||||
|
Also, at the end of your 'Form' you can get the name:
|
||||||
|
data['name']
|
||||||
|
"""
|
||||||
|
return StateFileContext(self, chat_id)
|
||||||
|
|
||||||
|
|
||||||
class StateContext:
|
class StateContext:
|
||||||
"""
|
"""
|
||||||
Class for data.
|
Class for data.
|
||||||
"""
|
"""
|
||||||
def __init__(self , obj: State, chat_id) -> None:
|
def __init__(self , obj: StateMemory, chat_id) -> None:
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.chat_id = chat_id
|
self.chat_id = chat_id
|
||||||
self.data = obj._get_data(chat_id)
|
self.data = obj._get_data(chat_id)
|
||||||
@ -217,3 +320,23 @@ class StateContext:
|
|||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
class StateFileContext:
|
||||||
|
"""
|
||||||
|
Class for data.
|
||||||
|
"""
|
||||||
|
def __init__(self , obj: StateFile, chat_id) -> None:
|
||||||
|
self.obj = obj
|
||||||
|
self.chat_id = chat_id
|
||||||
|
self.data = self.obj._get_data(self.chat_id)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
old_data = self.obj._read_data()
|
||||||
|
for i in self.data:
|
||||||
|
old_data[self.chat_id]['data'][i] = self.data.get(i)
|
||||||
|
self.obj._save_data(old_data)
|
||||||
|
|
||||||
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user