Skip to content

Commit 7e5a044

Browse files
Sync states v2 early version
1 parent fce1c3d commit 7e5a044

File tree

6 files changed

+223
-40
lines changed

6 files changed

+223
-40
lines changed

telebot/handler_backends.py

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
except:
1010
redis_installed = False
1111

12+
# backward compatibility
13+
from telebot.states import State, StatesGroup
1214

1315
class HandlerBackend(object):
1416
"""
@@ -160,45 +162,6 @@ def get_handlers(self, handler_group_id):
160162
return handlers
161163

162164

163-
class State:
164-
"""
165-
Class representing a state.
166-
167-
.. code-block:: python3
168-
169-
class MyStates(StatesGroup):
170-
my_state = State() # returns my_state:State string.
171-
"""
172-
def __init__(self) -> None:
173-
self.name = None
174-
def __str__(self) -> str:
175-
return self.name
176-
177-
178-
class StatesGroup:
179-
"""
180-
Class representing common states.
181-
182-
.. code-block:: python3
183-
184-
class MyStates(StatesGroup):
185-
my_state = State() # returns my_state:State string.
186-
"""
187-
def __init_subclass__(cls) -> None:
188-
state_list = []
189-
for name, value in cls.__dict__.items():
190-
if not name.startswith('__') and not callable(value) and isinstance(value, State):
191-
# change value of that variable
192-
value.name = ':'.join((cls.__name__, name))
193-
value.group = cls
194-
state_list.append(value)
195-
cls._state_list = state_list
196-
197-
@classmethod
198-
def state_list(self):
199-
return self._state_list
200-
201-
202165
class BaseMiddleware:
203166
"""
204167
Base class for middleware.
@@ -292,4 +255,4 @@ def start2(message):
292255
293256
"""
294257
def __init__(self) -> None:
295-
pass
258+
pass

telebot/states/__init__.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Contains classes for states and state groups.
3+
"""
4+
5+
6+
class State:
7+
"""
8+
Class representing a state.
9+
10+
.. code-block:: python3
11+
12+
class MyStates(StatesGroup):
13+
my_state = State() # returns my_state:State string.
14+
"""
15+
def __init__(self) -> None:
16+
self.name: str = None
17+
self.group: StatesGroup = None
18+
def __str__(self) -> str:
19+
return f"<{self.group.__name__}:{self.name}>"
20+
21+
22+
class StatesGroup:
23+
"""
24+
Class representing common states.
25+
26+
.. code-block:: python3
27+
28+
class MyStates(StatesGroup):
29+
my_state = State() # returns my_state:State string.
30+
"""
31+
def __init_subclass__(cls) -> None:
32+
state_list = []
33+
for name, value in cls.__dict__.items():
34+
if not name.startswith('__') and not callable(value) and isinstance(value, State):
35+
# change value of that variable
36+
value.name = ':'.join((cls.__name__, name))
37+
value.group = cls
38+
state_list.append(value)
39+
cls._state_list = state_list
40+
41+
@classmethod
42+
def state_list(self):
43+
return self._state_list

telebot/states/aio/__init__.py

Whitespace-only changes.

telebot/states/sync/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from .context import StateContext
2+
from .middleware import StateMiddleware
3+
4+
__all__ = [
5+
'StateContext',
6+
'StateMiddleware',
7+
]

telebot/states/sync/context.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
from telebot.states import State, StatesGroup
2+
from telebot.types import CallbackQuery, Message
3+
from telebot import TeleBot
4+
5+
from typing import Union
6+
7+
8+
class StateContext():
9+
"""
10+
Class representing a state context.
11+
12+
Passed through a middleware to provide easy way to set states.
13+
14+
.. code-block:: python3
15+
16+
@bot.message_handler(commands=['start'])
17+
def start_ex(message: types.Message, state_context: StateContext):
18+
state_context.set(MyStates.name)
19+
bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
20+
# also, state_context.data(), .add_data(), .reset_data(), .delete() methods available.
21+
"""
22+
23+
def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None:
24+
self.message: Union[Message, CallbackQuery] = message
25+
self.bot: TeleBot = bot
26+
self.bot_id = self.bot.bot_id
27+
28+
def _resolve_context(self) -> Union[Message, CallbackQuery]:
29+
chat_id = None
30+
user_id = None
31+
business_connection_id = self.message.business_connection_id
32+
bot_id = self.bot_id
33+
message_thread_id = None
34+
35+
if isinstance(self.message, Message):
36+
chat_id = self.message.chat.id
37+
user_id = self.message.from_user.id
38+
message_thread_id = self.message.message_thread_id if self.message.is_topic_message else None
39+
elif isinstance(self.message, CallbackQuery):
40+
chat_id = self.message.message.chat.id
41+
user_id = self.message.from_user.id
42+
message_thread_id = self.message.message.message_thread_id if self.message.message.is_topic_message else None
43+
44+
return chat_id, user_id, business_connection_id, bot_id, message_thread_id
45+
46+
def set(self, state: Union[State, str]) -> None:
47+
"""
48+
Set state for current user.
49+
50+
:param state: State object or state name.
51+
:type state: Union[State, str]
52+
53+
.. code-block:: python3
54+
55+
@bot.message_handler(commands=['start'])
56+
def start_ex(message: types.Message, state_context: StateContext):
57+
state_context.set(MyStates.name)
58+
bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
59+
"""
60+
61+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
62+
if isinstance(state, State):
63+
state = state.name
64+
return self.bot.set_state(
65+
chat_id=chat_id,
66+
user_id=user_id,
67+
state=state,
68+
business_connection_id=business_connection_id,
69+
bot_id=bot_id,
70+
message_thread_id=message_thread_id
71+
)
72+
73+
def get(self) -> str:
74+
"""
75+
Get current state for current user.
76+
77+
:return: Current state name.
78+
:rtype: str
79+
"""
80+
81+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
82+
return self.bot.get_state(
83+
chat_id=chat_id,
84+
user_id=user_id,
85+
business_connection_id=business_connection_id,
86+
bot_id=bot_id,
87+
message_thread_id=message_thread_id
88+
)
89+
90+
def reset_data(self) -> None:
91+
"""
92+
Reset data for current user.
93+
State will not be changed.
94+
"""
95+
96+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
97+
return self.bot.reset_data(
98+
chat_id=chat_id,
99+
user_id=user_id,
100+
business_connection_id=business_connection_id,
101+
bot_id=bot_id,
102+
message_thread_id=message_thread_id
103+
)
104+
105+
def delete(self) -> None:
106+
"""
107+
Deletes state and data for current user.
108+
"""
109+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
110+
return self.bot.delete_state(
111+
chat_id=chat_id,
112+
user_id=user_id,
113+
business_connection_id=business_connection_id,
114+
bot_id=bot_id,
115+
message_thread_id=message_thread_id
116+
)
117+
118+
def data(self) -> dict:
119+
"""
120+
Get data for current user.
121+
122+
.. code-block:: python3
123+
124+
with state_context.data() as data:
125+
print(data)
126+
"""
127+
128+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
129+
return self.bot.retrieve_data(
130+
chat_id=chat_id,
131+
user_id=user_id,
132+
business_connection_id=business_connection_id,
133+
bot_id=bot_id,
134+
message_thread_id=message_thread_id
135+
)
136+
137+
def add_data(self, **kwargs) -> None:
138+
"""
139+
Add data for current user.
140+
141+
:param kwargs: Data to add.
142+
:type kwargs: dict
143+
"""
144+
145+
chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context()
146+
return self.bot.add_data(
147+
chat_id=chat_id,
148+
user_id=user_id,
149+
business_connection_id=business_connection_id,
150+
bot_id=bot_id,
151+
message_thread_id=message_thread_id,
152+
**kwargs
153+
)

telebot/states/sync/middleware.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from telebot.handler_backends import BaseMiddleware
2+
from telebot import TeleBot
3+
from telebot.states.sync.context import StateContext
4+
5+
6+
class StateMiddleware(BaseMiddleware):
7+
8+
def __init__(self, bot: TeleBot) -> None:
9+
self.update_sensitive = False
10+
self.update_types = ['message', 'edited_message', 'callback_query'] #TODO: support other types
11+
self.bot: TeleBot = bot
12+
13+
def pre_process(self, message, data):
14+
data['state_context'] = StateContext(message, self.bot)
15+
16+
def post_process(self, message, data, exception):
17+
pass

0 commit comments

Comments
 (0)