This commit is contained in:
ada-dmitry
2024-08-24 15:45:54 +03:00
commit ab9d8386fb
1777 changed files with 358417 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
"""
Contains classes for states and state groups.
"""
from telebot import types
class State:
"""
Class representing a state.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init__(self) -> None:
self.name: str = None
self.group: StatesGroup = None
def __str__(self) -> str:
return f"<{self.name}>"
class StatesGroup:
"""
Class representing common states.
.. code-block:: python3
class MyStates(StatesGroup):
my_state = State() # returns my_state:State string.
"""
def __init_subclass__(cls) -> None:
state_list = []
for name, value in cls.__dict__.items():
if (
not name.startswith("__")
and not callable(value)
and isinstance(value, State)
):
# change value of that variable
value.name = ":".join((cls.__name__, name))
value.group = cls
state_list.append(value)
cls._state_list = state_list
@classmethod
def state_list(self):
return self._state_list
def resolve_context(message, bot_id: int) -> tuple:
# chat_id, user_id, business_connection_id, bot_id, message_thread_id
# message, edited_message, channel_post, edited_channel_post, business_message, edited_business_message
if isinstance(message, types.Message):
return (
message.chat.id,
message.from_user.id,
message.business_connection_id,
bot_id,
message.message_thread_id if message.is_topic_message else None,
)
elif isinstance(message, types.CallbackQuery): # callback_query
return (
message.message.chat.id,
message.from_user.id,
message.message.business_connection_id,
bot_id,
(
message.message.message_thread_id
if message.message.is_topic_message
else None
),
)
elif isinstance(message, types.BusinessConnection): # business_connection
return (message.user_chat_id, message.user.id, message.id, bot_id, None)
elif isinstance(
message, types.BusinessMessagesDeleted
): # deleted_business_messages
return (
message.chat.id,
message.chat.id,
message.business_connection_id,
bot_id,
None,
)
elif isinstance(message, types.MessageReactionUpdated): # message_reaction
return (message.chat.id, message.user.id, None, bot_id, None)
elif isinstance(
message, types.MessageReactionCountUpdated
): # message_reaction_count
return (message.chat.id, None, None, bot_id, None)
elif isinstance(message, types.InlineQuery): # inline_query
return (None, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.ChosenInlineResult): # chosen_inline_result
return (None, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.ShippingQuery): # shipping_query
return (None, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.PreCheckoutQuery): # pre_checkout_query
return (None, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.PollAnswer): # poll_answer
return (None, message.user.id, None, bot_id, None)
elif isinstance(message, types.ChatMemberUpdated): # chat_member # my_chat_member
return (message.chat.id, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.ChatJoinRequest): # chat_join_request
return (message.chat.id, message.from_user.id, None, bot_id, None)
elif isinstance(message, types.ChatBoostRemoved): # removed_chat_boost
return (
message.chat.id,
message.source.user.id if message.source else None,
None,
bot_id,
None,
)
elif isinstance(message, types.ChatBoostUpdated): # chat_boost
return (
message.chat.id,
message.boost.source.user.id if message.boost.source else None,
None,
bot_id,
None,
)
else:
pass # not yet supported :(
@@ -0,0 +1,7 @@
from .context import StateContext
from .middleware import StateMiddleware
__all__ = [
"StateContext",
"StateMiddleware",
]
+153
View File
@@ -0,0 +1,153 @@
from telebot.states import State, StatesGroup
from telebot.types import CallbackQuery, Message
from telebot.async_telebot import AsyncTeleBot
from telebot.states import resolve_context
from typing import Union
class StateContext:
"""
Class representing a state context.
Passed through a middleware to provide easy way to set states.
.. code-block:: python3
@bot.message_handler(commands=['start'])
async def start_ex(message: types.Message, state_context: StateContext):
await state_context.set(MyStates.name)
await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
# also, state_context.data(), .add_data(), .reset_data(), .delete() methods available.
"""
def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None:
self.message: Union[Message, CallbackQuery] = message
self.bot: AsyncTeleBot = bot
self.bot_id = self.bot.bot_id
async def set(self, state: Union[State, str]) -> bool:
"""
Set state for current user.
:param state: State object or state name.
:type state: Union[State, str]
.. code-block:: python3
@bot.message_handler(commands=['start'])
async def start_ex(message: types.Message, state_context: StateContext):
await state_context.set(MyStates.name)
await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
if isinstance(state, State):
state = state.name
return await self.bot.set_state(
chat_id=chat_id,
user_id=user_id,
state=state,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
)
async def get(self) -> str:
"""
Get current state for current user.
:return: Current state name.
:rtype: str
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
return await self.bot.get_state(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
)
async def delete(self) -> bool:
"""
Deletes state and data for current user.
.. warning::
This method deletes state and associated data for current user.
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
return await self.bot.delete_state(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
)
async def reset_data(self) -> bool:
"""
Reset data for current user.
State will not be changed.
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
return await self.bot.reset_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
)
def data(self) -> dict:
"""
Get data for current user.
.. code-block:: python3
with state_context.data() as data:
print(data)
data['name'] = 'John'
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
return self.bot.retrieve_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
)
async def add_data(self, **kwargs) -> None:
"""
Add data for current user.
:param kwargs: Data to add.
:type kwargs: dict
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = (
resolve_context(self.message, self.bot.bot_id)
)
return await self.bot.add_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
**kwargs
)
@@ -0,0 +1,21 @@
from telebot.asyncio_handler_backends import BaseMiddleware
from telebot.async_telebot import AsyncTeleBot
from telebot.states.sync.context import StateContext
from telebot.util import update_types
from telebot import types
class StateMiddleware(BaseMiddleware):
def __init__(self, bot: AsyncTeleBot) -> None:
self.update_sensitive = False
self.update_types = update_types
self.bot: AsyncTeleBot = bot
async def pre_process(self, message, data):
state_context = StateContext(message, self.bot)
data["state_context"] = state_context
data["state"] = state_context # 2 ways to access state context
async def post_process(self, message, data, exception):
pass
+7
View File
@@ -0,0 +1,7 @@
from .context import StateContext
from .middleware import StateMiddleware
__all__ = [
'StateContext',
'StateMiddleware',
]
+143
View File
@@ -0,0 +1,143 @@
from telebot.states import State, StatesGroup
from telebot.types import CallbackQuery, Message
from telebot import TeleBot, types
from telebot.states import resolve_context
from typing import Union
class StateContext():
"""
Class representing a state context.
Passed through a middleware to provide easy way to set states.
.. code-block:: python3
@bot.message_handler(commands=['start'])
def start_ex(message: types.Message, state_context: StateContext):
state_context.set(MyStates.name)
bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
# also, state_context.data(), .add_data(), .reset_data(), .delete() methods available.
"""
def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None:
self.message: Union[Message, CallbackQuery] = message
self.bot: TeleBot = bot
self.bot_id = self.bot.bot_id
def set(self, state: Union[State, str]) -> bool:
"""
Set state for current user.
:param state: State object or state name.
:type state: Union[State, str]
.. code-block:: python3
@bot.message_handler(commands=['start'])
def start_ex(message: types.Message, state_context: StateContext):
state_context.set(MyStates.name)
bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id)
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
if isinstance(state, State):
state = state.name
return self.bot.set_state(
chat_id=chat_id,
user_id=user_id,
state=state,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id
)
def get(self) -> str:
"""
Get current state for current user.
:return: Current state name.
:rtype: str
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
return self.bot.get_state(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id
)
def delete(self) -> bool:
"""
Deletes state and data for current user.
.. warning::
This method deletes state and associated data for current user.
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
return self.bot.delete_state(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id
)
def reset_data(self) -> bool:
"""
Reset data for current user.
State will not be changed.
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
return self.bot.reset_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id
)
def data(self) -> dict:
"""
Get data for current user.
.. code-block:: python3
with state_context.data() as data:
print(data)
data['name'] = 'John'
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
return self.bot.retrieve_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id
)
def add_data(self, **kwargs) -> None:
"""
Add data for current user.
:param kwargs: Data to add.
:type kwargs: dict
"""
chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id)
return self.bot.add_data(
chat_id=chat_id,
user_id=user_id,
business_connection_id=business_connection_id,
bot_id=bot_id,
message_thread_id=message_thread_id,
**kwargs
)
+21
View File
@@ -0,0 +1,21 @@
from telebot.handler_backends import BaseMiddleware
from telebot import TeleBot
from telebot.states.sync.context import StateContext
from telebot.util import update_types
from telebot import types
class StateMiddleware(BaseMiddleware):
def __init__(self, bot: TeleBot) -> None:
self.update_sensitive = False
self.update_types = update_types
self.bot: TeleBot = bot
def pre_process(self, message, data):
state_context = StateContext(message, self.bot)
data['state_context'] = state_context
data['state'] = state_context # 2 ways to access state context
def post_process(self, message, data, exception):
pass